1
2 #include <libwebsockets.h>
3 #include "private-lib-core.h"
4
5 //#define LWS_COOKIE_DEBUG
6
7 #if defined(LWS_COOKIE_DEBUG)
8 #define lwsl_cookie lwsl_notice
9 #else
10 #define lwsl_cookie lwsl_debug
11 #endif
12
13 #define LWS_COOKIE_MAX_CACHE_NAME_LEN 128
14
15 #define lws_tolower(_c) (((_c) >= 'A' && (_c) <= 'Z') ? \
16 (char)((_c) + 'a' - 'A') : \
17 (char)(_c))
18
19 #define LWS_COOKIE_NSC_FORMAT "%.*s\t"\
20 "%s\t"\
21 "%.*s\t"\
22 "%s\t"\
23 "%llu\t"\
24 "%.*s\t"\
25 "%.*s"
26
27 static const char *const mon = "janfebmaraprnayjunjulaugsepoctnovdec";
28
29 enum lws_cookie_nsc_f {
30 LWSC_NSC_DOMAIN,
31 LWSC_NSC_HOSTONLY,
32 LWSC_NSC_PATH,
33 LWSC_NSC_SECURE,
34 LWSC_NSC_EXPIRES,
35 LWSC_NSC_NAME,
36 LWSC_NSC_VALUE,
37
38 LWSC_NSC_COUNT,
39 };
40
41 enum lws_cookie_elements {
42 CE_DOMAIN,
43 CE_PATH,
44 CE_EXPIRES,
45 CE_MAXAGE,
46 CE_NAME,
47 CE_VALUE,
48
49 CE_HOSTONLY, /* these are bool, NULL = 0, non-NULL = 1 */
50 CE_SECURE,
51
52 CE_COUNT
53 };
54
55 struct lws_cookie {
56 const char *f[CE_COUNT];
57 size_t l[CE_COUNT];
58
59 unsigned int httponly:1;
60 };
61
62 static int
lws_cookie_parse_date(const char * d,size_t len,time_t * t)63 lws_cookie_parse_date(const char *d, size_t len, time_t *t)
64 {
65 struct tm date;
66 int offset = 0, i;
67
68 memset(&date, 0, sizeof(date));
69
70 while (len) {
71 if (isalnum((int)*d)) {
72 offset++;
73 goto next;
74 }
75 switch (offset) {
76 case 2:
77 if (*d == ':' && len >= 6) {
78 date.tm_hour = atoi(d - 2);
79 if (date.tm_hour < 0 || date.tm_hour > 23)
80 return -1;
81 date.tm_min = atoi(d + 1);
82 if (date.tm_min < 0 || date.tm_min > 60)
83 return -1;
84 date.tm_sec = atoi(d + 4);
85 if (date.tm_sec < 0 || date.tm_sec > 61)
86 /* leap second */
87 return -1;
88
89 d += 6;
90 len -= 6;
91 offset = 0;
92 continue;
93 }
94
95 if (!date.tm_mday) {
96 date.tm_mday = atoi(d - 2);
97 if (date.tm_mday < 1 || date.tm_mday > 31)
98 return -1;
99 goto next2;
100 }
101
102 if (!date.tm_year) {
103 date.tm_year = atoi(d - 2);
104 if (date.tm_year < 0 || date.tm_year > 99)
105 return -1;
106 if (date.tm_year < 70)
107 date.tm_year += 100;
108 }
109 goto next2;
110
111 case 3:
112 for (i = 0; i < 36; i += 3) {
113 if (lws_tolower(*(d - 3)) == mon[i] &&
114 lws_tolower(*(d - 2)) == mon[i + 1] &&
115 lws_tolower(*(d - 1)) == mon[i + 2]) {
116 date.tm_mon = i / 3;
117 break;
118 }
119 }
120 goto next2;
121
122 case 4:
123 if (!date.tm_year) {
124 date.tm_year = atoi(d - 4);
125 if (date.tm_year < 1601)
126 return -1;
127 date.tm_year -= 1900;
128 }
129 goto next2;
130
131 default:
132 goto next2;
133 }
134
135 next2:
136 offset = 0;
137 next:
138 d++;
139 len--;
140 }
141
142 *t = mktime(&date);
143
144 if (*t < 0)
145 return -1;
146
147 return 0;
148 }
149
150 static void
lws_cookie_rm_sws(const char ** buf_p,size_t * len_p)151 lws_cookie_rm_sws(const char **buf_p, size_t *len_p)
152 {
153 const char *buf;
154 size_t len;
155
156 if (!buf_p || !*buf_p || !len_p || !*len_p) {
157 lwsl_err("%s: false parameter\n", __func__);
158 return;
159 }
160
161 buf = *buf_p;
162 len = *len_p;
163 while (buf[0] == ' ' && len > 0) {
164 buf++;
165 len--;
166 }
167 while (buf[len - 1] == ' ' && len > 0)
168 len--;
169
170 *buf_p = buf;
171 *len_p = len;
172 }
173
174 static int
is_iprefix(const char * h,size_t hl,const char * n,size_t nl)175 is_iprefix(const char *h, size_t hl, const char *n, size_t nl)
176 {
177 if (!h || !n || nl > hl)
178 return 0;
179
180 while (nl) {
181 nl--;
182 if (lws_tolower(h[nl]) != lws_tolower(n[nl]))
183 return 0;
184 }
185 return 1;
186 }
187
188 static int
lws_cookie_compile_cache_name(char * buf,size_t buf_len,struct lws_cookie * c)189 lws_cookie_compile_cache_name(char *buf, size_t buf_len, struct lws_cookie *c)
190 {
191 if (!buf || !c->f[CE_DOMAIN] || !c->f[CE_PATH] || !c->f[CE_NAME] ||
192 c->l[CE_DOMAIN] + c->l[CE_PATH] + c->l[CE_NAME] + 6 > buf_len)
193 return -1;
194
195 memcpy(buf, c->f[CE_DOMAIN], c->l[CE_DOMAIN]);
196 buf += c->l[CE_DOMAIN];
197 *buf++ = '|';
198
199 memcpy(buf, c->f[CE_PATH], c->l[CE_PATH]);
200 buf += c->l[CE_PATH];
201 *buf++ = '|';
202
203 memcpy(buf, c->f[CE_NAME], c->l[CE_NAME]);
204 buf += c->l[CE_NAME];
205 *buf = '\0';
206
207 return 0;
208 }
209
210 static int
lws_cookie_parse_nsc(struct lws_cookie * c,const char * b,size_t l)211 lws_cookie_parse_nsc(struct lws_cookie *c, const char *b, size_t l)
212 {
213 enum lws_cookie_nsc_f state = LWSC_NSC_DOMAIN;
214 size_t n = 0;
215
216 if (!c || !b || l < 13)
217 return -1;
218
219 memset(c, 0, sizeof(*c));
220 lwsl_cookie("%s: parsing (%.*s) \n", __func__, (int)l, b);
221
222 while (l) {
223 l--;
224 if (b[n] != '\t' && l) {
225 n++;
226 continue;
227 }
228 switch (state) {
229 case LWSC_NSC_DOMAIN:
230 c->f[CE_DOMAIN] = b;
231 c->l[CE_DOMAIN] = n;
232 break;
233 case LWSC_NSC_PATH:
234 c->f[CE_PATH] = b;
235 c->l[CE_PATH] = n;
236 break;
237 case LWSC_NSC_EXPIRES:
238 c->f[CE_EXPIRES] = b;
239 c->l[CE_EXPIRES] = n;
240 break;
241 case LWSC_NSC_NAME:
242 c->f[CE_NAME] = b;
243 c->l[CE_NAME] = n;
244 break;
245
246 case LWSC_NSC_HOSTONLY:
247 if (b[0] == 'T') {
248 c->f[CE_HOSTONLY] = b;
249 c->l[CE_HOSTONLY] = 1;
250 }
251 break;
252 case LWSC_NSC_SECURE:
253 if (b[0] == 'T') {
254 c->f[CE_SECURE] = b;
255 c->l[CE_SECURE] = 1;
256 }
257 break;
258
259 case LWSC_NSC_VALUE:
260 c->f[CE_VALUE] = b;
261 c->l[CE_VALUE] = n + 1;
262
263 for (n = 0; n < LWS_ARRAY_SIZE(c->f); n++)
264 lwsl_cookie("%s: %d: %.*s\n", __func__,
265 (int)n, (int)c->l[n], c->f[n]);
266
267 return 0;
268 default:
269 return -1;
270 }
271
272 b += n + 1;
273 n = 0;
274 state++;
275 }
276
277 return -1;
278 }
279
280 static int
lws_cookie_write_nsc(struct lws * wsi,struct lws_cookie * c)281 lws_cookie_write_nsc(struct lws *wsi, struct lws_cookie *c)
282 {
283 char cache_name[LWS_COOKIE_MAX_CACHE_NAME_LEN];
284 struct lws_cache_ttl_lru *l1;
285 struct client_info_stash *stash;
286 char *cookie_string = NULL, *dl;
287 /* 6 tabs + 20 for max time_t + 2 * TRUE/FALSE + null */
288 size_t size = 6 + 20 + 10 + 1;
289 time_t expires = 0;
290 int ret = 0;
291
292 if (!wsi || !c)
293 return -1;
294
295 l1 = wsi->a.context->l1;
296 if (!l1 || !wsi->a.context->nsc)
297 return -1;
298
299 stash = wsi->stash ? wsi->stash : lws_get_network_wsi(wsi)->stash;
300 if (!stash || !stash->cis[CIS_ADDRESS] ||
301 !stash->cis[CIS_PATH])
302 return -1;
303
304
305 if (!c->f[CE_NAME] || !c->f[CE_VALUE]) {
306 lwsl_err("%s: malformed c\n", __func__);
307
308 return -1;
309 }
310
311 if (!c->f[CE_EXPIRES]) {
312 /*
313 * Currently we just take the approach to reject session cookies
314 */
315 lwsl_warn("%s: reject session cookies\n", __func__);
316
317 return 0;
318 }
319
320 if (!c->f[CE_DOMAIN]) {
321 c->f[CE_HOSTONLY] = "T";
322 c->l[CE_HOSTONLY] = 1;
323 c->f[CE_DOMAIN] = stash->cis[CIS_ADDRESS];
324 c->l[CE_DOMAIN] = strlen(c->f[CE_DOMAIN]);
325 }
326
327 if (!c->f[CE_PATH]) {
328 c->f[CE_PATH] = stash->cis[CIS_PATH];
329 c->l[CE_PATH] = strlen(c->f[CE_PATH]);
330 dl = memchr(c->f[CE_PATH], '?', c->l[CE_PATH]);
331 if (dl)
332 c->l[CE_PATH] = (size_t)(dl - c->f[CE_PATH]);
333 }
334
335 if (lws_cookie_compile_cache_name(cache_name, sizeof(cache_name), c))
336 return -1;
337
338 if (c->f[CE_EXPIRES] &&
339 lws_cookie_parse_date(c->f[CE_EXPIRES], c->l[CE_EXPIRES], &expires)) {
340 lwsl_err("%s: can't parse date %.*s\n", __func__,
341 (int)c->l[CE_EXPIRES], c->f[CE_EXPIRES]);
342 return -1;
343 }
344
345 size += c->l[CE_NAME] + c->l[CE_VALUE] + c->l[CE_DOMAIN] + c->l[CE_PATH];
346 cookie_string = (char *)lws_malloc(size, __func__);
347 if (!cookie_string) {
348 lwsl_err("%s: OOM\n",__func__);
349
350 return -1;
351 }
352
353 lws_snprintf(cookie_string, size, LWS_COOKIE_NSC_FORMAT,
354 (int)c->l[CE_DOMAIN], c->f[CE_DOMAIN],
355 c->f[CE_HOSTONLY] ? "TRUE" : "FALSE",
356 (int)c->l[CE_PATH], c->f[CE_PATH],
357 c->f[CE_SECURE] ? "TRUE" : "FALSE",
358 (unsigned long long)expires,
359 (int)c->l[CE_NAME], c->f[CE_NAME],
360 (int)c->l[CE_VALUE], c->f[CE_VALUE]);
361
362 lwsl_cookie("%s: name %s\n", __func__, cache_name);
363 lwsl_cookie("%s: c %s\n", __func__, cookie_string);
364
365 if (lws_cache_write_through(l1, cache_name,
366 (const uint8_t *)cookie_string,
367 strlen(cookie_string),
368 (lws_usec_t)((unsigned long long)expires *
369 (lws_usec_t)LWS_US_PER_SEC), NULL)) {
370 ret = -1;
371 goto exit;
372 }
373
374 #if defined(LWS_COOKIE_DEBUG)
375 char *po;
376 if (lws_cache_item_get(l1, cache_name, (const void **)&po, &size) ||
377 size != strlen(cookie_string) || memcmp(po, cookie_string, size)) {
378 lwsl_err("%s: L1 '%s' missing\n", __func__, cache_name);
379 }
380
381 if (lws_cache_item_get(wsi->a.context->nsc, cache_name,
382 (const void **)&po, &size) ||
383 size != strlen(cookie_string) ||
384 memcmp(po, cookie_string, size)) {
385 lwsl_err("%s: NSC '%s' missing, size %llu, po %s\n", __func__,
386 cache_name, (unsigned long long)size, po);
387 }
388 #endif
389
390 exit:
391 lws_free(cookie_string);
392
393 return ret;
394 }
395
396 static int
lws_cookie_attach_cookies(struct lws * wsi,char * buf,char * end)397 lws_cookie_attach_cookies(struct lws *wsi, char *buf, char *end)
398 {
399 const char *domain, *path, *dl_domain, *dl_path, *po;
400 char cache_name[LWS_COOKIE_MAX_CACHE_NAME_LEN];
401 size_t domain_len, path_len, size, ret = 0;
402 struct lws_cache_ttl_lru *l1;
403 struct client_info_stash *stash;
404 lws_cache_results_t cr;
405 struct lws_cookie c;
406 int hostdomain = 1;
407 char *p, *p1;
408
409 if (!wsi)
410 return -1;
411
412 stash = wsi->stash ? wsi->stash : lws_get_network_wsi(wsi)->stash;
413 if (!stash || !stash->cis[CIS_ADDRESS] ||
414 !stash->cis[CIS_PATH])
415 return -1;
416
417 l1 = wsi->a.context->l1;
418 if (!l1 || !wsi->a.context->nsc){
419 lwsl_err("%s:no cookiejar\n", __func__);
420 return -1;
421 }
422
423 memset(&c, 0, sizeof(c));
424
425 domain = stash->cis[CIS_ADDRESS];
426 path = stash->cis[CIS_PATH];
427
428 if (!domain || !path)
429 return -1;
430
431 path_len = strlen(path);
432
433 /* remove query string if exist */
434 dl_path = memchr(path, '?', path_len);
435 if (dl_path)
436 path_len = lws_ptr_diff_size_t(dl_path, path);
437
438 /* remove last slash if exist */
439 if (path_len != 1 && path[path_len - 1] == '/')
440 path_len--;
441
442 if (!path_len)
443 return -1;
444
445 lwsl_cookie("%s: path %.*s len %d\n", __func__, (int)path_len, path, (int)path_len);
446
447 /* when dest buf is not provided, we only return size of cookie string */
448 if (!buf || !end)
449 p = NULL;
450 else
451 p = buf;
452
453 /* iterate through domain and path levels to find matching cookies */
454 dl_domain = domain;
455 while (dl_domain) {
456 domain_len = strlen(domain);
457 dl_domain = memchr(domain, '.', domain_len);
458 /* don't match top level domain */
459 if (!dl_domain)
460 break;
461
462 if (domain_len + path_len + 6 > sizeof(cache_name))
463 return -1;
464
465 /* compile key string "[domain]|[path]|*"" */
466 p1 = cache_name;
467 memcpy(p1, domain, domain_len);
468 p1 += domain_len;
469 *p1 = '|';
470 p1++;
471 memcpy(p1, path, path_len);
472 p1 += path_len;
473 *p1 = '|';
474 p1++;
475 *p1 = '*';
476 p1++;
477 *p1 = '\0';
478
479 lwsl_cookie("%s: looking for %s\n", __func__, cache_name);
480
481 if (!lws_cache_lookup(l1, cache_name,
482 (const void **)&cr.ptr, &cr.size)) {
483
484 while (!lws_cache_results_walk(&cr)) {
485 lwsl_cookie(" %s (%d)\n", (const char *)cr.tag,
486 (int)cr.payload_len);
487
488 if (lws_cache_item_get(l1, (const char *)cr.tag,
489 (const void **)&po, &size) ||
490 lws_cookie_parse_nsc(&c, po, size)) {
491 lwsl_err("%s: failed to get c '%s'\n",
492 __func__, cr.tag);
493 break;
494 }
495
496 if (c.f[CE_HOSTONLY] && !hostdomain){
497 lwsl_cookie("%s: not sending this\n",
498 __func__);
499 continue;
500 }
501
502 if (p) {
503 if (ret) {
504 *p = ';';
505 p++;
506 *p = ' ';
507 p++;
508 }
509
510 memcpy(p, c.f[CE_NAME], c.l[CE_NAME]);
511 p += c.l[CE_NAME];
512 *p = '=';
513 p++;
514 memcpy(p, c.f[CE_VALUE], c.l[CE_VALUE]);
515 p += c.l[CE_VALUE];
516 }
517
518 if (ret)
519 ret += 2;
520 ret += c.l[CE_NAME] + 1 + c.l[CE_VALUE];
521
522 }
523 }
524
525 domain = dl_domain + 1;
526 hostdomain = 0;
527 }
528
529 lwsl_notice("%s: c len (%d)\n", __func__, (int)ret);
530
531 return (int)ret;
532 }
533
534 static struct {
535 const char *const name;
536 uint8_t len;
537 } cft[] = {
538 { "domain=", 7 },
539 { "path=", 5 },
540 { "expires=", 8 },
541 { "max-age=", 8 },
542 { "httponly", 8 },
543 { "secure", 6 }
544 };
545
546 int
lws_parse_set_cookie(struct lws * wsi)547 lws_parse_set_cookie(struct lws *wsi)
548 {
549 char *tk_head, *tk_end, *buf_head, *buf_end, *cookiep, *dl;
550 struct lws_cache_ttl_lru *l1;
551 struct lws_cookie c;
552 size_t fl;
553 int f, n;
554
555 if (!wsi)
556 return -1;
557
558 l1 = wsi->a.context->l1;
559 if (!l1)
560 return -1;
561
562 f = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_SET_COOKIE];
563
564 while (f) {
565 cookiep = wsi->http.ah->data + wsi->http.ah->frags[f].offset;
566 fl = wsi->http.ah->frags[f].len;
567 f = wsi->http.ah->frags[f].nfrag;
568
569 if (!cookiep || !fl)
570 continue;
571
572 #if defined(LWS_COOKIE_DEBUG)
573 lwsl_notice("%s:parsing: %.*s\n", __func__, (int)fl, cookiep);
574 #endif
575
576 buf_head = cookiep;
577 buf_end = cookiep + fl - 1;
578 memset(&c, 0, sizeof(struct lws_cookie));
579
580 do {
581 tk_head = buf_head;
582 tk_end = memchr(buf_head, ';',
583 (size_t)(buf_end - buf_head + 1));
584 if (!tk_end) {
585 tk_end = buf_end;
586 buf_head = buf_end;
587 } else {
588 buf_head = tk_end + 1;
589 tk_end--;
590 }
591
592 if (c.f[CE_NAME])
593 goto parse_av;
594
595 /*
596 * find name value, remove leading trailing
597 * WS and DQ for value
598 */
599
600 dl = memchr(tk_head, '=', lws_ptr_diff_size_t(tk_end,
601 tk_head + 1));
602 if (!dl || dl == tk_head)
603 return -1;
604
605 c.f[CE_NAME] = tk_head;
606 c.l[CE_NAME] = lws_ptr_diff_size_t(dl, tk_head);
607 lws_cookie_rm_sws(&c.f[CE_NAME], &c.l[CE_NAME]);
608
609 if (!c.l[CE_NAME])
610 return -1;
611
612 lwsl_cookie("%s: c name l %d v:%.*s\n", __func__,
613 (int)c.l[CE_NAME],
614 (int)c.l[CE_NAME], c.f[CE_NAME]);
615 c.f[CE_VALUE] = dl + 1;
616 c.l[CE_VALUE] = lws_ptr_diff_size_t(tk_end,
617 c.f[CE_VALUE]) + 1;
618
619 lws_cookie_rm_sws(&c.f[CE_VALUE], &c.l[CE_VALUE]);
620 if (c.l[CE_VALUE] >= 2 && c.f[CE_VALUE][0] == '\"') {
621 c.f[CE_VALUE]++;
622 c.l[CE_VALUE] -= 2;
623 }
624 lwsl_cookie("%s: c value l %d v:%.*s\n", __func__,
625 (int)c.l[CE_VALUE], (int)c.l[CE_VALUE],
626 c.f[CE_VALUE]);
627 continue;
628
629 parse_av:
630 while (*tk_head == ' ') {
631 if (tk_head == tk_end)
632 return -1;
633
634 tk_head++;
635 }
636
637 for (n = 0; n < (int)LWS_ARRAY_SIZE(cft); n++) {
638 if (lws_tolower(*tk_head) != cft[n].name[0])
639 continue;
640
641 if (!is_iprefix(tk_head,
642 lws_ptr_diff_size_t(tk_end,
643 tk_head) + 1,
644 cft[n].name, cft[n].len))
645 continue;
646
647 if (n == 4 || n == 5) {
648 c.f[n] = "T";
649 c.l[n] = 1;
650 break;
651 }
652
653 c.f[n] = tk_head + cft[n].len;
654 c.l[n] = lws_ptr_diff_size_t(tk_end, c.f[n]) + 1;
655 lws_cookie_rm_sws(&c.f[n], &c.l[n]);
656
657 if (n == CE_DOMAIN && c.l[0] &&
658 c.f[n][0] == '.'){
659 c.f[n]++;
660 c.l[n]--;
661 }
662
663 lwsl_cookie("%s: %s l %d v:%.*s\n", __func__,
664 cft[n].name, (int)c.l[n],
665 (int)c.l[n], c.f[n]);
666 break;
667 }
668
669 } while (tk_end != buf_end);
670
671 if (lws_cookie_write_nsc(wsi, &c))
672 lwsl_err("%s:failed to write nsc\n", __func__);
673 }
674
675 return 0;
676 }
677
678 int
lws_cookie_send_cookies(struct lws * wsi,char ** pp,char * end)679 lws_cookie_send_cookies(struct lws *wsi, char **pp, char *end)
680 {
681 char *p;
682 int size;
683
684 if (!wsi || !pp || !(*pp) || !end)
685 return -1;
686
687 size = lws_cookie_attach_cookies(wsi, NULL, NULL);
688
689 if (!size)
690 return 0;
691 if (size < 0) {
692 lwsl_err("%s:failed to get cookie string size\n", __func__);
693 return -1;
694 }
695
696 lwsl_notice("%s: size %d\n", __func__, size);
697
698 #if defined(LWS_COOKIE_DEBUG)
699 char *p_dbg = *pp;
700 #endif
701
702 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COOKIE, NULL, size,
703 (unsigned char **)pp, (unsigned char *)end))
704 return -1;
705
706 #if defined(LWS_COOKIE_DEBUG)
707 lwsl_notice("%s: dummy copy (%.*s) \n", __func__, (int)(*pp - p_dbg), p_dbg);
708 #endif
709
710
711 #ifdef LWS_WITH_HTTP2
712 if (lws_wsi_is_h2(wsi))
713 p = *pp - size;
714 else
715 #endif
716 p = *pp - size - 2;
717
718 if (lws_cookie_attach_cookies(wsi, p, p + size) <= 0) {
719 lwsl_err("%s:failed to attach cookies\n", __func__);
720 return -1;
721 }
722
723 #if defined(LWS_COOKIE_DEBUG)
724 lwsl_notice("%s: real copy (%.*s) total len %d\n", __func__, (int)(*pp - p_dbg), p_dbg, (int)(*pp - p_dbg));
725 lwsl_hexdump_notice(p_dbg, (size_t)(*pp - p_dbg));
726 #endif
727
728 return 0;
729 }
730