1 /*
2  * Copyright (C) 2015-2018 Nikos Mavrogiannopoulos
3  * Copyright (C) 2015 Red Hat
4  *
5  * This file is part of ocserv.
6  *
7  * ocserv is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * ocserv is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <config.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <string.h>
26 
27 #ifdef ENABLE_COMPRESSION
28 # ifdef HAVE_LZ4
29 #  include <lz4.h>
30 # endif
31 # include "lzs.h"
32 #endif
33 
34 #include <nettle/base64.h>
35 #include <base64-helper.h>
36 #include <c-strcase.h>
37 #include <c-ctype.h>
38 
39 #include <vpn.h>
40 #include <worker.h>
41 
42 #define CS_AES128_GCM "OC-DTLS1_2-AES128-GCM"
43 #define CS_AES256_GCM "OC-DTLS1_2-AES256-GCM"
44 
45 struct known_urls_st {
46 	const char *url;
47 	unsigned url_size;
48 	unsigned partial_match;
49 	url_handler_fn get_handler;
50 	url_handler_fn post_handler;
51 };
52 
53 #define LL(x,y,z) {x, sizeof(x)-1, 0, y, z}
54 #define LL_DIR(x,y,z) {x, sizeof(x)-1, 1, y, z}
55 static const struct known_urls_st known_urls[] = {
56 	LL("/", get_auth_handler, post_auth_handler),
57 	LL("/auth", get_auth_handler, post_auth_handler),
58 	LL("/VPN", get_auth_handler, post_auth_handler),
59 	LL("/cert.pem", get_cert_handler, NULL),
60 	LL("/cert.cer", get_cert_der_handler, NULL),
61 	LL("/ca.pem", get_ca_handler, NULL),
62 	LL("/ca.cer", get_ca_der_handler, NULL),
63 #ifdef ANYCONNECT_CLIENT_COMPAT
64 	LL_DIR("/profiles", get_config_handler, NULL),
65 	LL("/1/index.html", get_empty_handler, NULL),
66 	LL("/1/Linux", get_empty_handler, NULL),
67 	LL("/1/Linux_64", get_empty_handler, NULL),
68 	LL("/1/Windows", get_empty_handler, NULL),
69 	LL("/1/Darwin_i386", get_empty_handler, NULL),
70 	LL("/1/binaries/vpndownloader.sh", get_dl_handler, NULL),
71 	LL("/1/VPNManifest.xml", get_string_handler, NULL),
72 	LL("/1/binaries/update.txt", get_string_handler, NULL),
73 
74 	LL("/+CSCOT+/", get_string_handler, NULL),
75 	LL("/logout", get_empty_handler, NULL),
76 #endif
77 	{NULL, 0, 0, NULL, NULL}
78 };
79 
80 /* In the following we use %NO_SESSION_HASH:%DISABLE_SAFE_RENEGOTIATION because certain
81  * versions of openssl send the extended master secret extension in this
82  * resumed session. Since the state of this extension is undefined
83  * (it's not a real session we are resuming), we explicitly disable this
84  * extension to avoid interop issues. Furthermore gnutls does seem to
85  * be sending the renegotiation extension which openssl doesn't like (see #193) */
86 
87 #if GNUTLS_VERSION_NUMBER >= 0x030400
88 # define WORKAROUND_STR "%NO_SESSION_HASH:%DISABLE_SAFE_RENEGOTIATION"
89 #else
90 # define WORKAROUND_STR "%DISABLE_SAFE_RENEGOTIATION"
91 #endif
92 
93 /* Consider switching to gperf when this table grows significantly.
94  * These tables are used for the custom DTLS cipher negotiation via
95  * HTTP headers (WTF), and the compression negotiation.
96  */
97 static const dtls_ciphersuite_st ciphersuites[] = {
98 	{
99 	 .oc_name = CS_AES128_GCM,
100 	 .gnutls_name =
101 	 "NONE:+VERS-DTLS1.2:+COMP-NULL:+AES-128-GCM:+AEAD:+RSA:+SIGN-ALL:"WORKAROUND_STR,
102 	 .gnutls_version = GNUTLS_DTLS1_2,
103 	 .gnutls_mac = GNUTLS_MAC_AEAD,
104 	 .gnutls_kx = GNUTLS_KX_RSA,
105 	 .gnutls_cipher = GNUTLS_CIPHER_AES_128_GCM,
106 	 .server_prio = 80},
107 	{
108 	 .oc_name = CS_AES256_GCM,
109 	 .gnutls_name =
110 	 "NONE:+VERS-DTLS1.2:+COMP-NULL:+AES-256-GCM:+AEAD:+RSA:+SIGN-ALL:"WORKAROUND_STR,
111 	 .gnutls_version = GNUTLS_DTLS1_2,
112 	 .gnutls_mac = GNUTLS_MAC_AEAD,
113 	 .gnutls_kx = GNUTLS_KX_RSA,
114 	 .gnutls_cipher = GNUTLS_CIPHER_AES_256_GCM,
115 	 .server_prio = 90,
116 	 },
117 	{
118 	 .oc_name = "AES256-SHA",
119 	 .gnutls_name =
120 	 "NONE:+VERS-DTLS0.9:+COMP-NULL:+AES-256-CBC:+SHA1:+RSA:"WORKAROUND_STR,
121 	 .gnutls_version = GNUTLS_DTLS0_9,
122 	 .gnutls_mac = GNUTLS_MAC_SHA1,
123 	 .gnutls_kx = GNUTLS_KX_RSA,
124 	 .gnutls_cipher = GNUTLS_CIPHER_AES_256_CBC,
125 	 .server_prio = 60,
126 	 },
127 	{
128 	 .oc_name = "AES128-SHA",
129 	 .gnutls_name =
130 	 "NONE:+VERS-DTLS0.9:+COMP-NULL:+AES-128-CBC:+SHA1:+RSA:"WORKAROUND_STR,
131 	 .gnutls_version = GNUTLS_DTLS0_9,
132 	 .gnutls_mac = GNUTLS_MAC_SHA1,
133 	 .gnutls_kx = GNUTLS_KX_RSA,
134 	 .gnutls_cipher = GNUTLS_CIPHER_AES_128_CBC,
135 	 .server_prio = 50,
136 	 },
137 	{
138 	 .oc_name = "DES-CBC3-SHA",
139 	 .gnutls_name =
140 	 "NONE:+VERS-DTLS0.9:+COMP-NULL:+3DES-CBC:+SHA1:+RSA:"WORKAROUND_STR,
141 	 .gnutls_version = GNUTLS_DTLS0_9,
142 	 .gnutls_mac = GNUTLS_MAC_SHA1,
143 	 .gnutls_kx = GNUTLS_KX_RSA,
144 	 .gnutls_cipher = GNUTLS_CIPHER_3DES_CBC,
145 	 .server_prio = 1,
146 	 }
147 };
148 
149 static const dtls_ciphersuite_st ciphersuites12[] = {
150 	{
151 	 .oc_name = "AES128-GCM-SHA256",
152 	 .gnutls_name =
153 	 "NONE:+VERS-DTLS1.2:+COMP-NULL:+AES-128-GCM:+AEAD:+RSA:+SIGN-ALL:"WORKAROUND_STR,
154 	 .gnutls_version = GNUTLS_DTLS1_2,
155 	 .gnutls_mac = GNUTLS_MAC_AEAD,
156 	 .gnutls_kx = GNUTLS_KX_RSA,
157 	 .gnutls_cipher = GNUTLS_CIPHER_AES_128_GCM,
158 	 .dtls12_mode = 1,
159 	 .server_prio = 50
160 	},
161 	{
162 	 .oc_name = "AES256-GCM-SHA384",
163 	 .gnutls_name =
164 	 "NONE:+VERS-DTLS1.2:+COMP-NULL:+AES-256-GCM:+AEAD:+RSA:+SIGN-ALL:"WORKAROUND_STR,
165 	 .gnutls_version = GNUTLS_DTLS1_2,
166 	 .gnutls_mac = GNUTLS_MAC_AEAD,
167 	 .gnutls_kx = GNUTLS_KX_RSA,
168 	 .gnutls_cipher = GNUTLS_CIPHER_AES_256_GCM,
169 	 .dtls12_mode = 1,
170 	 .server_prio = 90
171 	}
172 };
173 
174 #define STR_ST(x) {.data = (uint8_t*)x, .length = sizeof(x)-1}
175 static const str_st sensitve_http_headers[] = {
176 	STR_ST("Cookie"),
177 	STR_ST("X-DTLS-Master-Secret"),
178 	STR_ST("Authorization"),
179 	{NULL, 0}
180 };
181 
182 #ifdef HAVE_LZ4
183 /* Wrappers over LZ4 functions */
184 static
lz4_decompress(void * dst,int dstlen,const void * src,int srclen)185 int lz4_decompress(void *dst, int dstlen, const void *src, int srclen)
186 {
187 	return LZ4_decompress_safe(src, dst, srclen, dstlen);
188 }
189 
190 static
lz4_compress(void * dst,int dstlen,const void * src,int srclen)191 int lz4_compress(void *dst, int dstlen, const void *src, int srclen)
192 {
193 	/* we intentionally restrict output to srclen so that
194 	 * compression fails early for packets that expand. */
195 	return LZ4_compress_default(src, dst, srclen, srclen);
196 }
197 #endif
198 
199 #ifdef ENABLE_COMPRESSION
200 struct compression_method_st comp_methods[] = {
201 #ifdef HAVE_LZ4
202 	{
203 		.id = OC_COMP_LZ4,
204 		.name = "oc-lz4",
205 		.decompress = lz4_decompress,
206 		.compress = lz4_compress,
207 		.server_prio = 90,
208 	},
209 #endif
210 	{
211 		.id = OC_COMP_LZS,
212 		.name = "lzs",
213 		.decompress = (decompress_fn)lzs_decompress,
214 		.compress = (compress_fn)lzs_compress,
215 		.server_prio = 80,
216 	}
217 };
218 
switch_comp_priority(void * pool,const char * modstring)219 unsigned switch_comp_priority(void *pool, const char *modstring)
220 {
221 	unsigned i, ret;
222 	char *token, *str;
223 	const char *algo = NULL;
224 	long priority = -1;
225 
226 	str = talloc_strdup(pool, modstring);
227 	if (!str)
228 		return 0;
229 
230 	token = str;
231 	token = strtok(token, ":");
232 
233 	algo = token;
234 
235 	token = strtok(NULL, ":");
236 	if (token)
237 		priority = strtol(token, NULL, 10);
238 
239 	if (algo == NULL || priority <= 0) {
240 		ret = 0;
241 		goto finish;
242 	}
243 	for (i = 0;
244 	     i < sizeof(comp_methods) / sizeof(comp_methods[0]);
245 	     i++) {
246 		if (c_strcasecmp(algo, comp_methods[i].name) == 0) {
247 			comp_methods[i].server_prio = priority;
248 			ret = 1;
249 			goto finish;
250 		}
251 	}
252 
253 	ret = 0;
254 
255  finish:
256 	talloc_free(str);
257 	return ret;
258 }
259 #endif
260 
header_is_sensitive(str_st * header)261 static bool header_is_sensitive(str_st * header)
262 {
263 	size_t i;
264 	for (i = 0; sensitve_http_headers[i].length != 0; i++) {
265 		if ((header->length == sensitve_http_headers[i].length) &&
266 			(strncasecmp((char*)header->data, (char*)sensitve_http_headers[i].data, header->length) == 0))
267 			return true;
268 	}
269 	return false;
270 }
271 
272 static
header_value_check(struct worker_st * ws,struct http_req_st * req)273 void header_value_check(struct worker_st *ws, struct http_req_st *req)
274 {
275 	unsigned tmplen, i;
276 	int ret;
277 	size_t nlen, value_length;
278 	char *token, *value;
279 	char *str, *p;
280 	const dtls_ciphersuite_st *cand = NULL;
281 	const dtls_ciphersuite_st *saved_ciphersuite;
282 	const compression_method_st *comp_cand = NULL;
283 	const compression_method_st **selected_comp;
284 	int want_cipher;
285 	int want_mac;
286 
287 	if (req->value.length <= 0)
288 		return;
289 
290 	if (WSPCONFIG(ws)->debug < DEBUG_SENSITIVE && header_is_sensitive(&req->header))
291 		oclog(ws, LOG_HTTP_DEBUG, "HTTP processing: %.*s: (censored)", (int)req->header.length,
292 		      req->header.data);
293 	else
294 		oclog(ws, LOG_HTTP_DEBUG, "HTTP processing: %.*s: %.*s", (int)req->header.length,
295 		      req->header.data, (int)req->value.length, req->value.data);
296 
297 	value = talloc_size(ws, req->value.length + 1);
298 	if (value == NULL)
299 		return;
300 
301 	/* make sure the value is null terminated */
302 	value_length = req->value.length;
303 	memcpy(value, req->value.data, value_length);
304 	value[value_length] = 0;
305 
306 	switch (req->next_header) {
307 	case HEADER_MASTER_SECRET:
308 		if (req->use_psk || !WSCONFIG(ws)->dtls_legacy) /* ignored */
309 			break;
310 
311 		if (value_length < TLS_MASTER_SIZE * 2) {
312 			req->master_secret_set = 0;
313 			goto cleanup;
314 		}
315 
316 		tmplen = TLS_MASTER_SIZE * 2;
317 
318 		nlen = sizeof(req->master_secret);
319 		gnutls_hex2bin((void *)value, tmplen,
320 			       req->master_secret, &nlen);
321 
322 		req->master_secret_set = 1;
323 		break;
324 	case HEADER_HOSTNAME:
325 		if (value_length + 1 > MAX_HOSTNAME_SIZE) {
326 			req->hostname[0] = 0;
327 			goto cleanup;
328 		}
329 		memcpy(req->hostname, value, value_length);
330 		req->hostname[value_length] = 0;
331 
332 		/* check validity */
333 		if (!valid_hostname(req->hostname)) {
334 			oclog(ws, LOG_HTTP_DEBUG, "Skipping invalid hostname '%s'", req->hostname);
335 			req->hostname[0] = 0;
336 		}
337 
338 		break;
339 	case HEADER_DEVICE_TYPE:
340 		if (value_length + 1 > sizeof(req->devtype)) {
341 			req->devtype[0] = 0;
342 			goto cleanup;
343 		}
344 		memcpy(req->devtype, value, value_length);
345 		req->devtype[value_length] = 0;
346 
347 		oclog(ws, LOG_DEBUG,
348 		      "Device-type: '%s'", value);
349 		break;
350 	case HEADER_PLATFORM:
351 		if (value_length + 1 > sizeof(req->devplatform)) {
352 			req->devplatform[0] = 0;
353 			goto cleanup;
354 		}
355 		memcpy(req->devplatform, value, value_length);
356 		req->devplatform[value_length] = 0;
357 
358 		if (strncasecmp(value, "apple-ios", 9) == 0 ||
359 		    strncasecmp(value, "android", 7) == 0) {
360 
361 			if (strncasecmp(value, "apple-ios", 9) == 0)
362 				req->is_ios = 1;
363 
364 			oclog(ws, LOG_DEBUG,
365 			      "Platform: '%s' (mobile)", value);
366 			req->is_mobile = 1;
367 		} else {
368 			oclog(ws, LOG_DEBUG,
369 			      "Platform: '%s'", value);
370 		}
371 		break;
372 	case HEADER_SUPPORT_SPNEGO:
373 		/* Switch to GSSAPI if the client supports it, but only
374 		 * if we haven't already authenticated with a certificate */
375 		if (!((ws->selected_auth->type & AUTH_TYPE_CERTIFICATE) && ws->cert_auth_ok != 0)) {
376 			ws_switch_auth_to(ws, AUTH_TYPE_GSSAPI);
377 			req->spnego_set = 1;
378 		}
379 		break;
380 	case HEADER_AUTHORIZATION:
381 		if (req->authorization != NULL)
382 			talloc_free(req->authorization);
383 		req->authorization = value;
384 		req->authorization_size = value_length;
385 		value = NULL;
386 		break;
387 	case HEADER_USER_AGENT:
388 		if (value_length + 1 > MAX_AGENT_NAME) {
389 			memcpy(req->user_agent, value, MAX_AGENT_NAME-1);
390 			req->user_agent[MAX_AGENT_NAME-1] = 0;
391 		} else {
392 			memcpy(req->user_agent, value, value_length);
393 			req->user_agent[value_length] = 0;
394 		}
395 
396 		oclog(ws, LOG_DEBUG,
397 		      "User-agent: '%s'", req->user_agent);
398 
399 		if (strncasecmp(req->user_agent, "Open AnyConnect VPN Agent v", 27) == 0) {
400 			unsigned version = atoi(&req->user_agent[27]);
401 			if (version <= 3) {
402 				oclog(ws, LOG_DEBUG, "Detected OpenConnect v3 or older");
403 				req->user_agent_type = AGENT_OPENCONNECT_V3;
404 			} else {
405 				oclog(ws, LOG_DEBUG, "Detected OpenConnect v4 or newer");
406 				req->user_agent_type = AGENT_OPENCONNECT;
407 			}
408 		} else if (strncasecmp(req->user_agent, "Cisco AnyConnect VPN Agent for Apple", 36) == 0) {
409 			oclog(ws, LOG_DEBUG, "Detected Cisco AnyConnect on iOS");
410 			req->user_agent_type = AGENT_ANYCONNECT;
411 			req->is_ios = 1;
412 		} else if (strncasecmp(req->user_agent, "OpenConnect VPN Agent", 21) == 0) {
413 			oclog(ws, LOG_DEBUG, "Detected OpenConnect v4 or newer");
414 			req->user_agent_type = AGENT_OPENCONNECT;
415 		} else if (strncasecmp(req->user_agent, "Cisco AnyConnect", 16) == 0) {
416 			oclog(ws, LOG_DEBUG, "Detected Cisco AnyConnect");
417 			req->user_agent_type = AGENT_ANYCONNECT;
418 		} else if (strncasecmp(req->user_agent, "AnyConnect", 10) == 0) {
419 			oclog(ws, LOG_DEBUG, "Detected Cisco AnyConnect");
420 			req->user_agent_type = AGENT_ANYCONNECT;
421 		} else {
422 			oclog(ws, LOG_DEBUG, "Unknown client (%s)", req->user_agent);
423 		}
424 		break;
425 
426 	case HEADER_DTLS_CIPHERSUITE:
427 		str = (char *)value;
428 
429 		p = strstr(str, DTLS_PROTO_INDICATOR);
430 		if (p != NULL && (p[sizeof(DTLS_PROTO_INDICATOR)-1] == 0 || p[sizeof(DTLS_PROTO_INDICATOR)-1] == ':')) {
431 			/* OpenConnect DTLS setup was detected. */
432 			if (WSCONFIG(ws)->dtls_psk) {
433 				req->use_psk = 1;
434 				req->master_secret_set = 1; /* we don't need it */
435 				req->selected_ciphersuite = NULL;
436 				break;
437 			}
438 		}
439 
440 		if (req->use_psk || !WSCONFIG(ws)->dtls_legacy)
441 			break;
442 
443 		if (req->selected_ciphersuite) /* if set via HEADER_DTLS12_CIPHERSUITE */
444 			break;
445 
446 		if (ws->session != NULL) {
447 			want_mac = gnutls_mac_get(ws->session);
448 			want_cipher = gnutls_cipher_get(ws->session);
449 		} else {
450 			want_mac = -1;
451 			want_cipher = -1;
452 		}
453 
454 		while ((token = strtok(str, ":")) != NULL) {
455 			for (i = 0;
456 			     i < sizeof(ciphersuites) / sizeof(ciphersuites[0]);
457 			     i++) {
458 				if (strcmp(token, ciphersuites[i].oc_name) == 0) {
459 					if (cand == NULL ||
460 					    cand->server_prio < ciphersuites[i].server_prio ||
461 					    (want_cipher != -1 && want_cipher == ciphersuites[i].gnutls_cipher &&
462 					     want_mac == ciphersuites[i].gnutls_mac)) {
463 						cand =
464 						    &ciphersuites[i];
465 
466 						/* if our candidate matches the TLS session
467 						 * ciphersuite, we are finished */
468 						if (want_cipher != -1) {
469 							if (want_cipher == cand->gnutls_cipher &&
470 							    want_mac == cand->gnutls_mac)
471 							    goto ciphersuite_finish;
472 						}
473 					}
474 				}
475 			}
476 			str = NULL;
477 		}
478  ciphersuite_finish:
479 	        req->selected_ciphersuite = cand;
480 
481 		break;
482 	case HEADER_DTLS12_CIPHERSUITE:
483 		if (req->use_psk || !WSCONFIG(ws)->dtls_legacy)
484 			break;
485 
486 		/* in gnutls 3.6.0+ there is a regression which makes
487 		 * anyconnect's openssl fail: https://gitlab.com/gnutls/gnutls/merge_requests/868
488 		 */
489 #ifdef gnutls_check_version_numeric
490 		if (req->user_agent_type == AGENT_ANYCONNECT &&
491 		    (!gnutls_check_version_numeric(3,6,6) &&
492 		    (!gnutls_check_version_numeric(3,3,0) || gnutls_check_version_numeric(3,6,0)))) {
493 			break;
494 		}
495 #endif
496 
497 		str = (char *)value;
498 
499 		p = strstr(str, DTLS_PROTO_INDICATOR);
500 		if (p != NULL && (p[sizeof(DTLS_PROTO_INDICATOR)-1] == 0 || p[sizeof(DTLS_PROTO_INDICATOR)-1] == ':')) {
501 			/* OpenConnect DTLS setup was detected. */
502 			if (WSCONFIG(ws)->dtls_psk) {
503 				req->use_psk = 1;
504 				req->master_secret_set = 1; /* we don't need it */
505 				req->selected_ciphersuite = NULL;
506 				break;
507 			}
508 		}
509 
510 		saved_ciphersuite = req->selected_ciphersuite;
511 		req->selected_ciphersuite = NULL;
512 
513 		if (ws->session != NULL) {
514 			want_mac = gnutls_mac_get(ws->session);
515 			want_cipher = gnutls_cipher_get(ws->session);
516 		} else {
517 			want_mac = -1;
518 			want_cipher = -1;
519 		}
520 
521 		while ((token = strtok(str, ":")) != NULL) {
522 			for (i = 0;
523 			     i < sizeof(ciphersuites12) / sizeof(ciphersuites12[0]);
524 			     i++) {
525 				if (strcmp(token, ciphersuites12[i].oc_name) == 0) {
526 					if (cand == NULL ||
527 					    cand->server_prio < ciphersuites12[i].server_prio ||
528 					    (want_cipher != -1 && want_cipher == ciphersuites12[i].gnutls_cipher &&
529 					     want_mac == ciphersuites12[i].gnutls_mac)) {
530 						cand =
531 						    &ciphersuites12[i];
532 
533 						/* if our candidate matches the TLS session
534 						 * ciphersuite, we are finished */
535 						if (want_cipher != -1) {
536 							if (want_cipher == cand->gnutls_cipher &&
537 							    want_mac == cand->gnutls_mac)
538 							    goto ciphersuite12_finish;
539 						}
540 					}
541 				}
542 			}
543 			str = NULL;
544 		}
545  ciphersuite12_finish:
546 	        req->selected_ciphersuite = cand;
547 
548 	        if (req->selected_ciphersuite == NULL && saved_ciphersuite)
549 			req->selected_ciphersuite = saved_ciphersuite;
550 
551 		break;
552 #ifdef ENABLE_COMPRESSION
553 	case HEADER_DTLS_ENCODING:
554 	case HEADER_CSTP_ENCODING:
555 	        if (WSCONFIG(ws)->enable_compression == 0)
556 	        	break;
557 
558 		if (req->next_header == HEADER_DTLS_ENCODING)
559 			selected_comp = &ws->dtls_selected_comp;
560 		else
561 			selected_comp = &ws->cstp_selected_comp;
562 	        *selected_comp = NULL;
563 
564 		str = (char *)value;
565 		while ((token = strtok(str, ",")) != NULL) {
566 			for (i = 0;
567 			     i < sizeof(comp_methods) / sizeof(comp_methods[0]);
568 			     i++) {
569 				if (c_strcasecmp(token, comp_methods[i].name) == 0) {
570 					if (comp_cand == NULL ||
571 					    comp_cand->server_prio <
572 					    comp_methods[i].server_prio) {
573 						comp_cand =
574 						    &comp_methods[i];
575 					}
576 				}
577 			}
578 			str = NULL;
579 		}
580 	        *selected_comp = comp_cand;
581 		break;
582 #endif
583 
584 	case HEADER_CSTP_BASE_MTU:
585 		req->link_mtu = atoi((char *)value);
586 		break;
587 	case HEADER_CSTP_MTU:
588 		req->tunnel_mtu = atoi((char *)value);
589 		break;
590 	case HEADER_CSTP_ATYPE:
591 		if (memmem(value, value_length, "IPv4", 4) == NULL)
592 			req->no_ipv4 = 1;
593 		if (memmem(value, value_length, "IPv6", 4) == NULL)
594 			req->no_ipv6 = 1;
595 		break;
596 	case HEADER_FULL_IPV6:
597 		if (memmem(value, value_length, "true", 4) != NULL)
598 			ws->full_ipv6 = 1;
599 		break;
600 	case HEADER_COOKIE:
601 		/* don't bother parsing cookies if we are already authenticated */
602 		if (ws->auth_state > S_AUTH_COOKIE)
603 			break;
604 
605 		str = (char *)value;
606 		while ((token = strtok(str, ";")) != NULL) {
607 			p = token;
608 			while (c_isspace(*p)) {
609 				p++;
610 			}
611 			tmplen = strlen(p);
612 
613 			if (strncmp(p, "webvpn=", 7) == 0) {
614 				tmplen -= 7;
615 				p += 7;
616 
617 				while (tmplen > 1 && c_isspace(p[tmplen - 1])) {
618 					tmplen--;
619 				}
620 
621 				/* we allow for BASE64_DECODE_LENGTH reporting few bytes more
622 				 * than the expected */
623 				nlen = BASE64_DECODE_LENGTH(tmplen);
624 				if (nlen < sizeof(ws->cookie) || nlen > sizeof(ws->cookie)+8)
625 					return;
626 
627 				/* we assume that - should be build time optimized */
628 				if (sizeof(ws->buffer) < sizeof(ws->cookie)+8)
629 					abort();
630 
631 				ret =
632 				    oc_base64_decode((uint8_t*)p, tmplen,
633 						  ws->buffer, &nlen);
634 				if (ret == 0 || nlen != sizeof(ws->cookie)) {
635 					oclog(ws, LOG_INFO,
636 					      "could not decode cookie: %.*s",
637 					      tmplen, p);
638 					ws->cookie_set = 0;
639 				} else {
640 					memcpy(ws->cookie, ws->buffer, sizeof(ws->cookie));
641 					ws->auth_state = S_AUTH_COOKIE;
642 					ws->cookie_set = 1;
643 				}
644 			} else if (strncmp(p, "webvpncontext=", 14) == 0) {
645 				p += 14;
646 				tmplen -= 14;
647 
648 				while (tmplen > 1 && c_isspace(p[tmplen - 1])) {
649 					tmplen--;
650 				}
651 
652 				nlen = BASE64_DECODE_LENGTH(tmplen);
653 				ret =
654 				    oc_base64_decode((uint8_t*)p, tmplen,
655 						  ws->sid, &nlen);
656 				if (ret == 0 || nlen != sizeof(ws->sid)) {
657 					oclog(ws, LOG_SENSITIVE,
658 					      "could not decode sid: %.*s",
659 					      tmplen, p);
660 					ws->sid_set = 0;
661 				} else {
662 					ws->sid_set = 1;
663 					oclog(ws, LOG_SENSITIVE,
664 					      "received sid: %.*s", tmplen, p);
665 				}
666 			}
667 
668 			str = NULL;
669 		}
670 		break;
671 	}
672 
673  cleanup:
674 	talloc_free(value);
675 }
676 
http_get_url_handler(const char * url)677 url_handler_fn http_get_url_handler(const char *url)
678 {
679 	const struct known_urls_st *p;
680 	unsigned len = strlen(url);
681 
682 	p = known_urls;
683 	do {
684 		if (p->url != NULL) {
685 			if ((len == p->url_size && strcmp(p->url, url) == 0) ||
686 			    (len >= p->url_size
687 			     && strncmp(p->url, url, p->url_size) == 0
688 			     && (p->partial_match != 0
689 				 || url[p->url_size] == '/'
690 				 || url[p->url_size] == '?')))
691 				return p->get_handler;
692 		}
693 		p++;
694 	} while (p->url != NULL);
695 
696 	return NULL;
697 }
698 
http_post_url_handler(struct worker_st * ws,const char * url)699 url_handler_fn http_post_url_handler(struct worker_st *ws, const char *url)
700 {
701 	const struct known_urls_st *p;
702 	unsigned i;
703 
704 	p = known_urls;
705 	do {
706 		if (p->url != NULL && strcmp(p->url, url) == 0)
707 			return p->post_handler;
708 		p++;
709 	} while (p->url != NULL);
710 
711 	for (i=0;i<WSCONFIG(ws)->kkdcp_size;i++) {
712 		if (WSCONFIG(ws)->kkdcp[i].url && strcmp(WSCONFIG(ws)->kkdcp[i].url, url) == 0)
713 			return post_kkdcp_handler;
714 	}
715 
716 	return NULL;
717 }
718 
http_url_cb(http_parser * parser,const char * at,size_t length)719 int http_url_cb(http_parser * parser, const char *at, size_t length)
720 {
721 	struct worker_st *ws = parser->data;
722 	struct http_req_st *req = &ws->req;
723 
724 	if (length >= sizeof(req->url)) {
725 		req->url[0] = 0;
726 		return 1;
727 	}
728 
729 	memcpy(req->url, at, length);
730 	req->url[length] = 0;
731 
732 	return 0;
733 }
734 
http_header_field_cb(http_parser * parser,const char * at,size_t length)735 int http_header_field_cb(http_parser * parser, const char *at, size_t length)
736 {
737 	struct worker_st *ws = parser->data;
738 	struct http_req_st *req = &ws->req;
739 	int ret;
740 
741 	if (req->header_state != HTTP_HEADER_RECV) {
742 		/* handle value */
743 		if (req->header_state == HTTP_HEADER_VALUE_RECV)
744 			header_value_check(ws, req);
745 		req->header_state = HTTP_HEADER_RECV;
746 		str_reset(&req->header);
747 	}
748 
749 	ret = str_append_data(&req->header, at, length);
750 	if (ret < 0)
751 		return ret;
752 
753 	return 0;
754 }
755 
756 /* include hash table of headers */
757 #include "http-heads.h"
758 
header_check(struct http_req_st * req)759 static void header_check(struct http_req_st *req)
760 {
761 	const struct http_headers_st *p;
762 
763 	p = in_word_set((char *)req->header.data, req->header.length);
764 	if (p != NULL) {
765 		req->next_header = p->id;
766 		return;
767 	}
768 	req->next_header = 0;
769 }
770 
http_header_value_cb(http_parser * parser,const char * at,size_t length)771 int http_header_value_cb(http_parser * parser, const char *at, size_t length)
772 {
773 	struct worker_st *ws = parser->data;
774 	struct http_req_st *req = &ws->req;
775 	int ret;
776 
777 	if (req->header_state != HTTP_HEADER_VALUE_RECV) {
778 		/* handle header */
779 		header_check(req);
780 		req->header_state = HTTP_HEADER_VALUE_RECV;
781 		str_reset(&req->value);
782 	}
783 
784 	ret = str_append_data(&req->value, at, length);
785 	if (ret < 0)
786 		return ret;
787 
788 	return 0;
789 }
790 
http_header_complete_cb(http_parser * parser)791 int http_header_complete_cb(http_parser * parser)
792 {
793 	struct worker_st *ws = parser->data;
794 	struct http_req_st *req = &ws->req;
795 
796 	/* handle header value */
797 	header_value_check(ws, req);
798 
799 	if ((ws->selected_auth->type & AUTH_TYPE_GSSAPI) && ws->auth_state == S_AUTH_INACTIVE &&
800 	    req->spnego_set == 0) {
801 		/* client retried getting the form without the SPNEGO header, probably
802 		 * wants a fallback authentication method */
803 		if (ws_switch_auth_to_next(ws) == 0)
804 			oclog(ws, LOG_INFO, "no fallback from gssapi authentication");
805 	}
806 
807 	req->headers_complete = 1;
808 	return 0;
809 }
810 
http_message_complete_cb(http_parser * parser)811 int http_message_complete_cb(http_parser * parser)
812 {
813 	struct worker_st *ws = parser->data;
814 	struct http_req_st *req = &ws->req;
815 
816 	req->message_complete = 1;
817 	return 0;
818 }
819 
http_body_cb(http_parser * parser,const char * at,size_t length)820 int http_body_cb(http_parser * parser, const char *at, size_t length)
821 {
822 	struct worker_st *ws = parser->data;
823 	struct http_req_st *req = &ws->req;
824 	char *tmp;
825 
826 	tmp = talloc_realloc_size(ws, req->body, req->body_length + length + 1);
827 	if (tmp == NULL)
828 		return 1;
829 
830 	memcpy(&tmp[req->body_length], at, length);
831 	req->body_length += length;
832 	tmp[req->body_length] = 0;
833 
834 	req->body = tmp;
835 	return 0;
836 }
837 
http_req_init(worker_st * ws)838 void http_req_init(worker_st * ws)
839 {
840 	str_init(&ws->req.header, ws);
841 	str_init(&ws->req.value, ws);
842 }
843 
http_req_reset(worker_st * ws)844 void http_req_reset(worker_st * ws)
845 {
846 	ws->req.headers_complete = 0;
847 	ws->req.message_complete = 0;
848 	ws->req.body_length = 0;
849 	ws->req.spnego_set = 0;
850 	ws->req.url[0] = 0;
851 
852 	ws->req.header_state = HTTP_HEADER_INIT;
853 	str_reset(&ws->req.header);
854 	str_reset(&ws->req.value);
855 }
856 
http_req_deinit(worker_st * ws)857 void http_req_deinit(worker_st * ws)
858 {
859 	http_req_reset(ws);
860 	str_clear(&ws->req.header);
861 	str_clear(&ws->req.value);
862 	talloc_free(ws->req.body);
863 	ws->req.body = NULL;
864 }
865 
866 /* add_owasp_headers:
867  * @ws: an initialized worker structure
868  *
869  * This function adds the OWASP default headers
870  * There are security tools that flag the server as a security risk.
871  * These are added to help users comply with security best practices.
872  */
add_owasp_headers(worker_st * ws)873 int add_owasp_headers(worker_st * ws)
874 {
875 	if (cstp_puts(ws, "Strict-Transport-Security: max-age=31536000 ; includeSubDomains\r\n") < 0 ||
876 		cstp_puts(ws, "X-Frame-Options: deny\r\n") < 0 ||
877 		cstp_puts(ws, "X-Content-Type-Options: nosniff\r\n") < 0 ||
878 		cstp_puts(ws, "Content-Security-Policy: default-src \'none\'\r\n") < 0 ||
879 		cstp_puts(ws, "X-Permitted-Cross-Domain-Policies: none\r\n") < 0 ||
880 		cstp_puts(ws, "Referrer-Policy: no-referrer\r\n") < 0 ||
881 		cstp_puts(ws, "Clear-Site-Data: \"cache\",\"cookies\",\"storage\"\r\n") < 0 ||
882 		cstp_puts(ws, "Cross-Origin-Embedder-Policy: require-corp\r\n") < 0 ||
883 		cstp_puts(ws, "Cross-Origin-Opener-Policy: same-origin\r\n") < 0 ||
884 		cstp_puts(ws, "Cross-Origin-Resource-Policy: same-origin\r\n") < 0 ||
885 		cstp_puts(ws, "X-XSS-Protection: 0\r\n") < 0)
886 	{
887 		return -1;
888 	}
889 	return 0;
890 }