2012/8/7 Peter Dunkley <peter.dunkley(a)crocodile-rcs.com>om>:
Hi Inaki,
1) A callback (i.e. a script "route[X]"
in kamailio.cfg) that is called
when a new WebSocket connection is requested by a client, by providing
access to the HTTP GET request (so the admin, based on local policy can
inspect the HTTP request and allow/dissalow the connection after checking
the Cookie header, the Origin header, the Host header or whatever).
All new WebSocket connections are handled by an
event_route[xhttp:request]. This allows any inspection of the headers you
want, and you can perform HTTP digest authentication using this
event_route.
Note that, even if RFC 6455 gives the door open to HTTP basic/digest
authentication, currently no one browser with WebSocket support reacts
on a 401/407 for the WS GET.
I strongly know that WWW people does not like authentication
mechanisms other than those based on HTML forms ;)
Once you are happy that the connection should be
allowed you
call the ws_handle_handshake() function to do some further checks,
generate the handshake response, and upgrade the connection in Kamailio
core. So, for example:
event_route[xhttp:request] {
set_reply_close();
set_reply_no_connect();
if ($Rp != 80 && $Rp != 443) {
xlog("L_WARN", "HTTP request received on $Rp\n");
xhttp_reply("403", "Forbidden", "",
"");
exit;
}
xlog("L_DBG", "HTTP Request Received\n");
if ($hdr(Upgrade)=~"websocket"
&& $hdr(Connection)=~"Upgrade"
&& $rm=~"GET") {
But the Sec-WebSocket-Accept header is generated (and added to the 101
response) by the WS module itself, am I right?
xlog("L_DBG",
"WebSocket\n");
xlog("L_DBG", " Host: $hdr(Host)\n");
xlog("L_DBG", " Origin: $hdr(Origin)\n");
if ($hdr(Host) == $null || !is_myself($hdr(Host))) {
xlog("L_WARN", "Bad host $hdr(Host)\n");
xhttp_reply("403", "Forbidden", "",
"");
exit;
}
# Optional... validate Origin
# Optional... perform HTTP authentication
# ws_handle_handshake() exits (no further configuration file
# processing of the request) when complete.
ws_handle_handshake();
}
xhttp_reply("404", "Not found", "", "");
}
2) A mechanism
to associate custom attributes for each WebSocket
connection
(i.e. an authorized user identifier, which has been authorized by means of
the previous HTTP GET request inspection in step 1).
You can do this by creating a hash table using the htable module
(
http://kamailio.org/docs/modules/stable/modules_k/htable.html). This
table can be filled in as part of the event_route[xhttp:request] above.
If required ws_handle_handshake() could easily be updated to return
success/failure instead of exiting on completion to help with this (but
that change may not be needed).
The hash table should be indexed on the source address and port of the
connection.
Ok.
3) A mechanism
to retrieve, in the kamailio.cfg script, the attributes of
the WebSocket connection from which a SIP request arrives to Kamailio
(i.e.
useful to enforce that the From URI matches the connection user identifier
retrieved in any custom way in step 1).
This information can be put in the hash table and retrieved on demand.
You can always get the source address and port (which the hash table would
be indexed on) of a request in the Kamailio configuration.
Sure, ok.
4) A mechanism
to close an existing WebSocket connection from the
kamailio.cfg script (i.e: during the WebSocket handshake we decided,
somehow, that the user identifier is "alice", but at some point we receive
an INVITE from that WS connection with From user "bob", so it could be an
attacker and we want to close the connection).
I believe that calling the existing "set_reply_close()" function and then
sending a SIP reply to the INVITE will kill the connection. Adding an API
to explicitly kill the WebSocket connection immediately wouldn't be hard -
but shouldn't be needed.
5) A callback that is called when a previously
accepted WebSocket
connection is closed (by passing as argument the terminator of the
connection: the client or kamailio.cfg).
There is no support for this at the moment, but it would be relatively
easy to add an event_route[] to the WebSocket module (probably just a few
lines in ws_conn.c:wsconn_close_now() or ws_conn.c:wsconn_rm()). If the
event_route[] makes sure appropriate pseudo-vars (containing information
like source address and port of the terminated connection and so on) are
provided then this can be used to clean-up the hash-table (otherwise
entries will just have to time-out).
Makes sense :)
Said that, WWW
people WON'T like the idea of providing a SIP username and
SIP password in the JavaScript code retrieved by the user when visiting
the
web page, and for sure most of WWW people will prefer to
validate/authenticate/authorize the WebSocket connections by inspecting
the
HTTP GET request (Origin, Cookie, some custom API_KEY in the request-URI
of
the GET method or whatever).
Yes I thought that. That is one of the reasons I decided to use the xhttp
module to handle the WebSocket handshake instead of coding it all in the
module.
It seems a good decision :)
Thanks a lot for your explanations, and congratulations for your work.
--
Iñaki Baz Castillo
<ibc(a)aliax.net>