1 /*
2  * Copyright (C) 2012-2013 Crocodile RCS Ltd
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * Kamailio is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version
10  *
11  * Kamailio is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * Exception: permission to copy, modify, propagate, and distribute a work
21  * formed by combining OpenSSL toolkit software and the code in this file,
22  * such as linking with software components and libraries released under
23  * OpenSSL project license.
24  *
25  */
26 
27 #include <openssl/sha.h>
28 
29 #include "../../core/basex.h"
30 #include "../../core/data_lump_rpl.h"
31 #include "../../core/dprint.h"
32 #include "../../core/locking.h"
33 #include "../../core/str.h"
34 #include "../../core/tcp_conn.h"
35 #include "../../core/counters.h"
36 #include "../../core/strutils.h"
37 #include "../../core/mem/mem.h"
38 #include "../../core/parser/msg_parser.h"
39 #include "../sl/sl.h"
40 #include "../tls/tls_cfg.h"
41 #include "ws_conn.h"
42 #include "ws_handshake.h"
43 #include "websocket.h"
44 #include "config.h"
45 
46 #define WS_VERSION (13)
47 
48 int ws_sub_protocols = DEFAULT_SUB_PROTOCOLS;
49 int ws_cors_mode = CORS_MODE_NONE;
50 
51 stat_var *ws_failed_handshakes;
52 stat_var *ws_successful_handshakes;
53 stat_var *ws_sip_successful_handshakes;
54 stat_var *ws_msrp_successful_handshakes;
55 
56 static str str_sip = str_init("sip");
57 static str str_msrp = str_init("msrp");
58 static str str_upgrade = str_init("upgrade");
59 static str str_websocket = str_init("websocket");
60 static str str_ws_guid = str_init("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
61 
62 /* HTTP headers */
63 static str str_hdr_connection = str_init("Connection");
64 static str str_hdr_upgrade = str_init("Upgrade");
65 static str str_hdr_sec_websocket_accept = str_init("Sec-WebSocket-Accept");
66 static str str_hdr_sec_websocket_key = str_init("Sec-WebSocket-Key");
67 static str str_hdr_sec_websocket_protocol = str_init("Sec-WebSocket-Protocol");
68 static str str_hdr_sec_websocket_version = str_init("Sec-WebSocket-Version");
69 static str str_hdr_origin = str_init("Origin");
70 static str str_hdr_access_control_allow_origin =
71 		str_init("Access-Control-Allow-Origin");
72 #define CONNECTION (1 << 0)
73 #define UPGRADE (1 << 1)
74 #define SEC_WEBSOCKET_ACCEPT (1 << 2)
75 #define SEC_WEBSOCKET_KEY (1 << 3)
76 #define SEC_WEBSOCKET_PROTOCOL (1 << 4)
77 #define SEC_WEBSOCKET_VERSION (1 << 5)
78 #define ORIGIN (1 << 6)
79 
80 #define REQUIRED_HEADERS                                               \
81 	(CONNECTION | UPGRADE | SEC_WEBSOCKET_KEY | SEC_WEBSOCKET_PROTOCOL \
82 			| SEC_WEBSOCKET_VERSION)
83 
84 /* HTTP status text */
85 static str str_status_switching_protocols = str_init("Switching Protocols");
86 static str str_status_bad_request = str_init("Bad Request");
87 static str str_status_upgrade_required = str_init("Upgrade Required");
88 static str str_status_internal_server_error = str_init("Internal Server Error");
89 static str str_status_service_unavailable = str_init("Service Unavailable");
90 
91 #define HDR_BUF_LEN (512)
92 static char headers_buf[HDR_BUF_LEN];
93 
94 static char key_buf[base64_enc_len(SHA_DIGEST_LENGTH)];
95 
ws_send_reply(sip_msg_t * msg,int code,str * reason,str * hdrs)96 static int ws_send_reply(sip_msg_t *msg, int code, str *reason, str *hdrs)
97 {
98 	if(hdrs && hdrs->len > 0) {
99 		if(add_lump_rpl(msg, hdrs->s, hdrs->len, LUMP_RPL_HDR) == 0) {
100 			LM_ERR("inserting extra-headers lump\n");
101 			update_stat(ws_failed_handshakes, 1);
102 			return -1;
103 		}
104 	}
105 
106 	if(ws_slb.freply(msg, code, reason) < 0) {
107 		LM_ERR("sending reply\n");
108 		update_stat(ws_failed_handshakes, 1);
109 		return -1;
110 	}
111 
112 	update_stat(
113 			code == 101 ? ws_successful_handshakes : ws_failed_handshakes, 1);
114 
115 	return 0;
116 }
117 
ws_handle_handshake(struct sip_msg * msg)118 int ws_handle_handshake(struct sip_msg *msg)
119 {
120 	str key = {0, 0}, headers = {0, 0}, reply_key = {0, 0}, origin = {0, 0};
121 	unsigned char sha1[SHA_DIGEST_LENGTH];
122 	unsigned int hdr_flags = 0, sub_protocol = 0;
123 	int version = 0;
124 	struct hdr_field *hdr = msg->headers;
125 	struct tcp_connection *con;
126 	ws_connection_t *wsc;
127 
128 	/* Make sure that the connection is closed after the response _and_
129 	   the existing connection (from the request) is reused for the
130 	   response.  The close flag will be unset later if the handshake is
131 	   successful. */
132 	msg->rpl_send_flags.f |= SND_F_CON_CLOSE;
133 	msg->rpl_send_flags.f |= SND_F_FORCE_CON_REUSE;
134 
135 	if(cfg_get(websocket, ws_cfg, enabled) == 0) {
136 		LM_INFO("disabled: bouncing handshake\n");
137 		ws_send_reply(msg, 503, &str_status_service_unavailable, NULL);
138 		return 0;
139 	}
140 
141 	/* Retrieve TCP/TLS connection */
142 	if((con = tcpconn_get(msg->rcv.proto_reserved1, 0, 0, 0, 0)) == NULL) {
143 		LM_ERR("retrieving connection\n");
144 		ws_send_reply(msg, 500, &str_status_internal_server_error, NULL);
145 		return 0;
146 	}
147 
148 	if(con->type != PROTO_TCP && con->type != PROTO_TLS) {
149 		LM_ERR("unsupported transport: %d", con->type);
150 		goto end;
151 	}
152 
153 	if(parse_headers(msg, HDR_EOH_F, 0) < 0) {
154 		LM_ERR("error parsing headers\n");
155 		ws_send_reply(msg, 500, &str_status_internal_server_error, NULL);
156 		goto end;
157 	}
158 
159 	/* Process HTTP headers */
160 	while(hdr != NULL) {
161 		/* Decode and validate Connection */
162 		if(cmp_hdrname_strzn(
163 				   &hdr->name, str_hdr_connection.s, str_hdr_connection.len)
164 				== 0) {
165 			strlower(&hdr->body);
166 			if(str_search(&hdr->body, &str_upgrade) != NULL) {
167 				LM_DBG("found %.*s: %.*s\n",
168 
169 						hdr->name.len, hdr->name.s, hdr->body.len, hdr->body.s);
170 				hdr_flags |= CONNECTION;
171 			}
172 		}
173 		/* Decode and validate Upgrade */
174 		else if(cmp_hdrname_strzn(
175 						&hdr->name, str_hdr_upgrade.s, str_hdr_upgrade.len)
176 				== 0) {
177 			strlower(&hdr->body);
178 			if(str_search(&hdr->body, &str_websocket) != NULL) {
179 				LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s,
180 						hdr->body.len, hdr->body.s);
181 				hdr_flags |= UPGRADE;
182 			}
183 		}
184 		/* Decode and validate Sec-WebSocket-Key */
185 		else if(cmp_hdrname_strzn(&hdr->name, str_hdr_sec_websocket_key.s,
186 						str_hdr_sec_websocket_key.len)
187 				== 0) {
188 			if(hdr_flags & SEC_WEBSOCKET_KEY) {
189 				LM_WARN("%.*s found multiple times\n", hdr->name.len,
190 						hdr->name.s);
191 				ws_send_reply(msg, 400, &str_status_bad_request, NULL);
192 				goto end;
193 			}
194 
195 			LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s,
196 					hdr->body.len, hdr->body.s);
197 			key = hdr->body;
198 			hdr_flags |= SEC_WEBSOCKET_KEY;
199 		}
200 		/* Decode and validate Sec-WebSocket-Protocol */
201 		else if(cmp_hdrname_strzn(&hdr->name, str_hdr_sec_websocket_protocol.s,
202 						str_hdr_sec_websocket_protocol.len)
203 				== 0) {
204 			strlower(&hdr->body);
205 			if(str_search(&hdr->body, &str_sip) != NULL) {
206 				LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s,
207 						hdr->body.len, hdr->body.s);
208 				hdr_flags |= SEC_WEBSOCKET_PROTOCOL;
209 				sub_protocol |= SUB_PROTOCOL_SIP;
210 			}
211 			if(str_search(&hdr->body, &str_msrp) != NULL) {
212 				LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s,
213 						hdr->body.len, hdr->body.s);
214 				hdr_flags |= SEC_WEBSOCKET_PROTOCOL;
215 				sub_protocol |= SUB_PROTOCOL_MSRP;
216 			}
217 		}
218 		/* Decode and validate Sec-WebSocket-Version */
219 		else if(cmp_hdrname_strzn(&hdr->name, str_hdr_sec_websocket_version.s,
220 						str_hdr_sec_websocket_version.len)
221 				== 0) {
222 			if(hdr_flags & SEC_WEBSOCKET_VERSION) {
223 				LM_WARN("%.*s found multiple times\n", hdr->name.len,
224 						hdr->name.s);
225 				ws_send_reply(msg, 400, &str_status_bad_request, NULL);
226 				goto end;
227 			}
228 
229 			str2sint(&hdr->body, &version);
230 
231 			if(version != WS_VERSION) {
232 				LM_WARN("Unsupported protocol version %.*s\n", hdr->body.len,
233 						hdr->body.s);
234 				headers.s = headers_buf;
235 				headers.len = snprintf(headers.s, HDR_BUF_LEN, "%.*s: %d\r\n",
236 						str_hdr_sec_websocket_version.len,
237 						str_hdr_sec_websocket_version.s, WS_VERSION);
238 				ws_send_reply(msg, 426, &str_status_upgrade_required, &headers);
239 				goto end;
240 			}
241 
242 			LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s,
243 					hdr->body.len, hdr->body.s);
244 			hdr_flags |= SEC_WEBSOCKET_VERSION;
245 		}
246 		/* Decode Origin */
247 		else if(cmp_hdrname_strzn(
248 						&hdr->name, str_hdr_origin.s, str_hdr_origin.len)
249 				== 0) {
250 			if(hdr_flags & ORIGIN) {
251 				LM_WARN("%.*s found multiple times\n", hdr->name.len,
252 						hdr->name.s);
253 				ws_send_reply(msg, 400, &str_status_bad_request, NULL);
254 				goto end;
255 			}
256 
257 			LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s,
258 					hdr->body.len, hdr->body.s);
259 			origin = hdr->body;
260 			hdr_flags |= ORIGIN;
261 		}
262 
263 		hdr = hdr->next;
264 	}
265 
266 	/* Final check that all required headers/values were found */
267 	sub_protocol &= ws_sub_protocols;
268 	if((hdr_flags & REQUIRED_HEADERS) != REQUIRED_HEADERS
269 			|| sub_protocol == 0) {
270 
271 		LM_WARN("required headers not present\n");
272 		headers.s = headers_buf;
273 		headers.len = 0;
274 
275 		if(ws_sub_protocols & SUB_PROTOCOL_SIP)
276 			headers.len += snprintf(headers.s + headers.len,
277 					HDR_BUF_LEN - headers.len, "%.*s: %.*s\r\n",
278 					str_hdr_sec_websocket_protocol.len,
279 					str_hdr_sec_websocket_protocol.s, str_sip.len, str_sip.s);
280 
281 		if(ws_sub_protocols & SUB_PROTOCOL_MSRP)
282 			headers.len += snprintf(headers.s + headers.len,
283 					HDR_BUF_LEN - headers.len, "%.*s: %.*s\r\n",
284 					str_hdr_sec_websocket_protocol.len,
285 					str_hdr_sec_websocket_protocol.s, str_msrp.len, str_msrp.s);
286 
287 		headers.len +=
288 				snprintf(headers.s + headers.len, HDR_BUF_LEN - headers.len,
289 						"%.*s: %d\r\n", str_hdr_sec_websocket_version.len,
290 						str_hdr_sec_websocket_version.s, WS_VERSION);
291 		ws_send_reply(msg, 400, &str_status_bad_request, &headers);
292 		goto end;
293 	}
294 
295 	/* Construct reply_key */
296 	reply_key.s =
297 			(char *)pkg_malloc((key.len + str_ws_guid.len) * sizeof(char));
298 	if(reply_key.s == NULL) {
299 		LM_ERR("allocating pkg memory\n");
300 		ws_send_reply(msg, 500, &str_status_internal_server_error, NULL);
301 		goto end;
302 	}
303 	memcpy(reply_key.s, key.s, key.len);
304 	memcpy(reply_key.s + key.len, str_ws_guid.s, str_ws_guid.len);
305 	reply_key.len = key.len + str_ws_guid.len;
306 	SHA1((const unsigned char *)reply_key.s, reply_key.len, sha1);
307 	pkg_free(reply_key.s);
308 	reply_key.s = key_buf;
309 	reply_key.len = base64_enc(sha1, SHA_DIGEST_LENGTH,
310 			(unsigned char *)reply_key.s, base64_enc_len(SHA_DIGEST_LENGTH));
311 
312 	/* Add the connection to the WebSocket connection table */
313 	wsconn_add(&msg->rcv, sub_protocol);
314 
315 	/* Make sure Kamailio core sends future messages on this connection
316 	   directly to this module */
317 	if(con->type == PROTO_TLS)
318 		con->type = con->rcv.proto = PROTO_WSS;
319 	else
320 		con->type = con->rcv.proto = PROTO_WS;
321 
322 	/* Now Kamailio is ready to receive WebSocket frames build and send a
323 	   101 reply */
324 	headers.s = headers_buf;
325 	headers.len = 0;
326 
327 	if(ws_cors_mode == CORS_MODE_ANY)
328 		headers.len +=
329 				snprintf(headers.s + headers.len, HDR_BUF_LEN - headers.len,
330 						"%.*s: *\r\n", str_hdr_access_control_allow_origin.len,
331 						str_hdr_access_control_allow_origin.s);
332 	else if(ws_cors_mode == CORS_MODE_ORIGIN && origin.len > 0)
333 		headers.len += snprintf(headers.s + headers.len,
334 				HDR_BUF_LEN - headers.len, "%.*s: %.*s\r\n",
335 				str_hdr_access_control_allow_origin.len,
336 				str_hdr_access_control_allow_origin.s, origin.len, origin.s);
337 
338 	if(sub_protocol & SUB_PROTOCOL_SIP)
339 		headers.len += snprintf(headers.s + headers.len,
340 				HDR_BUF_LEN - headers.len, "%.*s: %.*s\r\n",
341 				str_hdr_sec_websocket_protocol.len,
342 				str_hdr_sec_websocket_protocol.s, str_sip.len, str_sip.s);
343 	else if(sub_protocol & SUB_PROTOCOL_MSRP)
344 		headers.len += snprintf(headers.s + headers.len,
345 				HDR_BUF_LEN - headers.len, "%.*s: %.*s\r\n",
346 				str_hdr_sec_websocket_protocol.len,
347 				str_hdr_sec_websocket_protocol.s, str_msrp.len, str_msrp.s);
348 
349 	headers.len += snprintf(headers.s + headers.len, HDR_BUF_LEN - headers.len,
350 			"%.*s: %.*s\r\n"
351 			"%.*s: %.*s\r\n"
352 			"%.*s: %.*s\r\n",
353 			str_hdr_upgrade.len, str_hdr_upgrade.s, str_websocket.len,
354 			str_websocket.s, str_hdr_connection.len, str_hdr_connection.s,
355 			str_upgrade.len, str_upgrade.s, str_hdr_sec_websocket_accept.len,
356 			str_hdr_sec_websocket_accept.s, reply_key.len, reply_key.s);
357 	msg->rpl_send_flags.f &= ~SND_F_CON_CLOSE;
358 	if(ws_send_reply(msg, 101, &str_status_switching_protocols, &headers) < 0) {
359 		if((wsc = wsconn_get(msg->rcv.proto_reserved1)) != NULL) {
360 			wsconn_rm(wsc, WSCONN_EVENTROUTE_NO);
361 			wsconn_put(wsc);
362 		}
363 		goto end;
364 	} else {
365 		if(sub_protocol & SUB_PROTOCOL_SIP)
366 			update_stat(ws_sip_successful_handshakes, 1);
367 		else if(sub_protocol & SUB_PROTOCOL_MSRP)
368 			update_stat(ws_msrp_successful_handshakes, 1);
369 	}
370 
371 	tcpconn_put(con);
372 	return 1;
373 end:
374 	if(con)
375 		tcpconn_put(con);
376 	return 0;
377 }
378 
w_ws_handle_handshake(sip_msg_t * msg,char * p1,char * p2)379 int w_ws_handle_handshake(sip_msg_t *msg, char *p1, char *p2)
380 {
381 	return ws_handle_handshake(msg);
382 }
383 
ws_rpc_disable(rpc_t * rpc,void * ctx)384 void ws_rpc_disable(rpc_t *rpc, void *ctx)
385 {
386 	cfg_get(websocket, ws_cfg, enabled) = 0;
387 	LM_WARN("disabling websockets - new connections will be dropped\n");
388 	return;
389 }
390 
ws_rpc_enable(rpc_t * rpc,void * ctx)391 void ws_rpc_enable(rpc_t *rpc, void *ctx)
392 {
393 	cfg_get(websocket, ws_cfg, enabled) = 1;
394 	LM_WARN("enabling websockets\n");
395 	return;
396 }
397