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 }