1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 /***
26
27
28 RECEIVING COOKIE INFORMATION
29 ============================
30
31 Curl_cookie_init()
32
33 Inits a cookie struct to store data in a local file. This is always
34 called before any cookies are set.
35
36 Curl_cookie_add()
37
38 Adds a cookie to the in-memory cookie jar.
39
40
41 SENDING COOKIE INFORMATION
42 ==========================
43
44 Curl_cookie_getlist()
45
46 For a given host and path, return a linked list of cookies that
47 the client should send to the server if used now. The secure
48 boolean informs the cookie if a secure connection is achieved or
49 not.
50
51 It shall only return cookies that have not expired.
52
53 Example set of cookies:
54
55 Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
56 Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
57 domain=.fidelity.com; path=/ftgw; secure
58 Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
59 domain=.fidelity.com; path=/; secure
60 Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
61 domain=.fidelity.com; path=/; secure
62 Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
63 domain=.fidelity.com; path=/; secure
64 Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
65 domain=.fidelity.com; path=/; secure
66 Set-cookie:
67 Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
68 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
69 ****/
70
71
72 #include "curl_setup.h"
73
74 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
75
76 #include "urldata.h"
77 #include "cookie.h"
78 #include "psl.h"
79 #include "strtok.h"
80 #include "sendf.h"
81 #include "slist.h"
82 #include "share.h"
83 #include "strtoofft.h"
84 #include "strcase.h"
85 #include "curl_get_line.h"
86 #include "curl_memrchr.h"
87 #include "parsedate.h"
88 #include "rename.h"
89 #include "fopen.h"
90 #include "strdup.h"
91 #include "llist.h"
92
93 /* The last 3 #include files should be in this order */
94 #include "curl_printf.h"
95 #include "curl_memory.h"
96 #include "memdebug.h"
97
98 static void strstore(char **str, const char *newstr, size_t len);
99
100 /* number of seconds in 400 days */
101 #define COOKIES_MAXAGE (400*24*3600)
102
103 /* Make sure cookies never expire further away in time than 400 days into the
104 future. (from RFC6265bis draft-19)
105
106 For the sake of easier testing, align the capped time to an even 60 second
107 boundary.
108 */
cap_expires(time_t now,struct Cookie * co)109 static void cap_expires(time_t now, struct Cookie *co)
110 {
111 if((TIME_T_MAX - COOKIES_MAXAGE - 30) > now) {
112 timediff_t cap = now + COOKIES_MAXAGE;
113 if(co->expires > cap) {
114 cap += 30;
115 co->expires = (cap/60)*60;
116 }
117 }
118 }
119
freecookie(struct Cookie * co)120 static void freecookie(struct Cookie *co)
121 {
122 free(co->domain);
123 free(co->path);
124 free(co->spath);
125 free(co->name);
126 free(co->value);
127 free(co);
128 }
129
cookie_tailmatch(const char * cookie_domain,size_t cookie_domain_len,const char * hostname)130 static bool cookie_tailmatch(const char *cookie_domain,
131 size_t cookie_domain_len,
132 const char *hostname)
133 {
134 size_t hostname_len = strlen(hostname);
135
136 if(hostname_len < cookie_domain_len)
137 return FALSE;
138
139 if(!strncasecompare(cookie_domain,
140 hostname + hostname_len-cookie_domain_len,
141 cookie_domain_len))
142 return FALSE;
143
144 /*
145 * A lead char of cookie_domain is not '.'.
146 * RFC6265 4.1.2.3. The Domain Attribute says:
147 * For example, if the value of the Domain attribute is
148 * "example.com", the user agent will include the cookie in the Cookie
149 * header when making HTTP requests to example.com, www.example.com, and
150 * www.corp.example.com.
151 */
152 if(hostname_len == cookie_domain_len)
153 return TRUE;
154 if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
155 return TRUE;
156 return FALSE;
157 }
158
159 /*
160 * matching cookie path and URL path
161 * RFC6265 5.1.4 Paths and Path-Match
162 */
pathmatch(const char * cookie_path,const char * request_uri)163 static bool pathmatch(const char *cookie_path, const char *request_uri)
164 {
165 size_t cookie_path_len;
166 size_t uri_path_len;
167 char *uri_path = NULL;
168 char *pos;
169 bool ret = FALSE;
170
171 /* cookie_path must not have last '/' separator. ex: /sample */
172 cookie_path_len = strlen(cookie_path);
173 if(1 == cookie_path_len) {
174 /* cookie_path must be '/' */
175 return TRUE;
176 }
177
178 uri_path = strdup(request_uri);
179 if(!uri_path)
180 return FALSE;
181 pos = strchr(uri_path, '?');
182 if(pos)
183 *pos = 0x0;
184
185 /* #-fragments are already cut off! */
186 if(0 == strlen(uri_path) || uri_path[0] != '/') {
187 strstore(&uri_path, "/", 1);
188 if(!uri_path)
189 return FALSE;
190 }
191
192 /*
193 * here, RFC6265 5.1.4 says
194 * 4. Output the characters of the uri-path from the first character up
195 * to, but not including, the right-most %x2F ("/").
196 * but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
197 * without redirect.
198 * Ignore this algorithm because /hoge is uri path for this case
199 * (uri path is not /).
200 */
201
202 uri_path_len = strlen(uri_path);
203
204 if(uri_path_len < cookie_path_len) {
205 ret = FALSE;
206 goto pathmatched;
207 }
208
209 /* not using checkprefix() because matching should be case-sensitive */
210 if(strncmp(cookie_path, uri_path, cookie_path_len)) {
211 ret = FALSE;
212 goto pathmatched;
213 }
214
215 /* The cookie-path and the uri-path are identical. */
216 if(cookie_path_len == uri_path_len) {
217 ret = TRUE;
218 goto pathmatched;
219 }
220
221 /* here, cookie_path_len < uri_path_len */
222 if(uri_path[cookie_path_len] == '/') {
223 ret = TRUE;
224 goto pathmatched;
225 }
226
227 ret = FALSE;
228
229 pathmatched:
230 free(uri_path);
231 return ret;
232 }
233
234 /*
235 * Return the top-level domain, for optimal hashing.
236 */
get_top_domain(const char * const domain,size_t * outlen)237 static const char *get_top_domain(const char * const domain, size_t *outlen)
238 {
239 size_t len = 0;
240 const char *first = NULL, *last;
241
242 if(domain) {
243 len = strlen(domain);
244 last = memrchr(domain, '.', len);
245 if(last) {
246 first = memrchr(domain, '.', (last - domain));
247 if(first)
248 len -= (++first - domain);
249 }
250 }
251
252 if(outlen)
253 *outlen = len;
254
255 return first ? first : domain;
256 }
257
258 /* Avoid C1001, an "internal error" with MSVC14 */
259 #if defined(_MSC_VER) && (_MSC_VER == 1900)
260 #pragma optimize("", off)
261 #endif
262
263 /*
264 * A case-insensitive hash for the cookie domains.
265 */
cookie_hash_domain(const char * domain,const size_t len)266 static size_t cookie_hash_domain(const char *domain, const size_t len)
267 {
268 const char *end = domain + len;
269 size_t h = 5381;
270
271 while(domain < end) {
272 size_t j = (size_t)Curl_raw_toupper(*domain++);
273 h += h << 5;
274 h ^= j;
275 }
276
277 return (h % COOKIE_HASH_SIZE);
278 }
279
280 #if defined(_MSC_VER) && (_MSC_VER == 1900)
281 #pragma optimize("", on)
282 #endif
283
284 /*
285 * Hash this domain.
286 */
cookiehash(const char * const domain)287 static size_t cookiehash(const char * const domain)
288 {
289 const char *top;
290 size_t len;
291
292 if(!domain || Curl_host_is_ipnum(domain))
293 return 0;
294
295 top = get_top_domain(domain, &len);
296 return cookie_hash_domain(top, len);
297 }
298
299 /*
300 * cookie path sanitize
301 */
sanitize_cookie_path(const char * cookie_path)302 static char *sanitize_cookie_path(const char *cookie_path)
303 {
304 size_t len;
305 char *new_path = strdup(cookie_path);
306 if(!new_path)
307 return NULL;
308
309 /* some stupid site sends path attribute with '"'. */
310 len = strlen(new_path);
311 if(new_path[0] == '\"') {
312 memmove(new_path, new_path + 1, len);
313 len--;
314 }
315 if(len && (new_path[len - 1] == '\"')) {
316 new_path[--len] = 0x0;
317 }
318
319 /* RFC6265 5.2.4 The Path Attribute */
320 if(new_path[0] != '/') {
321 /* Let cookie-path be the default-path. */
322 strstore(&new_path, "/", 1);
323 return new_path;
324 }
325
326 /* convert /hoge/ to /hoge */
327 if(len && new_path[len - 1] == '/') {
328 new_path[len - 1] = 0x0;
329 }
330
331 return new_path;
332 }
333
334 /*
335 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
336 *
337 * NOTE: OOM or cookie parsing failures are ignored.
338 */
Curl_cookie_loadfiles(struct Curl_easy * data)339 void Curl_cookie_loadfiles(struct Curl_easy *data)
340 {
341 struct curl_slist *list = data->state.cookielist;
342 if(list) {
343 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
344 while(list) {
345 struct CookieInfo *ci =
346 Curl_cookie_init(data, list->data, data->cookies,
347 data->set.cookiesession);
348 if(!ci)
349 /*
350 * Failure may be due to OOM or a bad cookie; both are ignored
351 * but only the first should be
352 */
353 infof(data, "ignoring failed cookie_init for %s", list->data);
354 else
355 data->cookies = ci;
356 list = list->next;
357 }
358 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
359 }
360 }
361
362 /*
363 * strstore
364 *
365 * A thin wrapper around strdup which ensures that any memory allocated at
366 * *str will be freed before the string allocated by strdup is stored there.
367 * The intended usecase is repeated assignments to the same variable during
368 * parsing in a last-wins scenario. The caller is responsible for checking
369 * for OOM errors.
370 */
strstore(char ** str,const char * newstr,size_t len)371 static void strstore(char **str, const char *newstr, size_t len)
372 {
373 DEBUGASSERT(newstr);
374 DEBUGASSERT(str);
375 free(*str);
376 *str = Curl_memdup0(newstr, len);
377 }
378
379 /*
380 * remove_expired
381 *
382 * Remove expired cookies from the hash by inspecting the expires timestamp on
383 * each cookie in the hash, freeing and deleting any where the timestamp is in
384 * the past. If the cookiejar has recorded the next timestamp at which one or
385 * more cookies expire, then processing will exit early in case this timestamp
386 * is in the future.
387 */
remove_expired(struct CookieInfo * ci)388 static void remove_expired(struct CookieInfo *ci)
389 {
390 struct Cookie *co;
391 curl_off_t now = (curl_off_t)time(NULL);
392 unsigned int i;
393
394 /*
395 * If the earliest expiration timestamp in the jar is in the future we can
396 * skip scanning the whole jar and instead exit early as there will not be
397 * any cookies to evict. If we need to evict however, reset the
398 * next_expiration counter in order to track the next one. In case the
399 * recorded first expiration is the max offset, then perform the safe
400 * fallback of checking all cookies.
401 */
402 if(now < ci->next_expiration &&
403 ci->next_expiration != CURL_OFF_T_MAX)
404 return;
405 else
406 ci->next_expiration = CURL_OFF_T_MAX;
407
408 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
409 struct Curl_llist_node *n;
410 struct Curl_llist_node *e = NULL;
411
412 for(n = Curl_llist_head(&ci->cookielist[i]); n; n = e) {
413 co = Curl_node_elem(n);
414 e = Curl_node_next(n);
415 if(co->expires && co->expires < now) {
416 Curl_node_remove(n);
417 freecookie(co);
418 ci->numcookies--;
419 }
420 else {
421 /*
422 * If this cookie has an expiration timestamp earlier than what we
423 * have seen so far then record it for the next round of expirations.
424 */
425 if(co->expires && co->expires < ci->next_expiration)
426 ci->next_expiration = co->expires;
427 }
428 }
429 }
430 }
431
432 #ifndef USE_LIBPSL
433 /* Make sure domain contains a dot or is localhost. */
bad_domain(const char * domain,size_t len)434 static bool bad_domain(const char *domain, size_t len)
435 {
436 if((len == 9) && strncasecompare(domain, "localhost", 9))
437 return FALSE;
438 else {
439 /* there must be a dot present, but that dot must not be a trailing dot */
440 char *dot = memchr(domain, '.', len);
441 if(dot) {
442 size_t i = dot - domain;
443 if((len - i) > 1)
444 /* the dot is not the last byte */
445 return FALSE;
446 }
447 }
448 return TRUE;
449 }
450 #endif
451
452 /*
453 RFC 6265 section 4.1.1 says a server should accept this range:
454
455 cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
456
457 But Firefox and Chrome as of June 2022 accept space, comma and double-quotes
458 fine. The prime reason for filtering out control bytes is that some HTTP
459 servers return 400 for requests that contain such.
460 */
invalid_octets(const char * p)461 static bool invalid_octets(const char *p)
462 {
463 /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */
464 static const char badoctets[] = {
465 "\x01\x02\x03\x04\x05\x06\x07\x08\x0a"
466 "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
467 "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f"
468 };
469 size_t len;
470 /* scan for all the octets that are *not* in cookie-octet */
471 len = strcspn(p, badoctets);
472 return p[len] != '\0';
473 }
474
475 #define CERR_OK 0
476 #define CERR_TOO_LONG 1 /* input line too long */
477 #define CERR_TAB 2 /* in a wrong place */
478 #define CERR_TOO_BIG 3 /* name/value too large */
479 #define CERR_BAD 4 /* deemed incorrect */
480 #define CERR_NO_SEP 5 /* semicolon problem */
481 #define CERR_NO_NAME_VALUE 6 /* name or value problem */
482 #define CERR_INVALID_OCTET 7 /* bad content */
483 #define CERR_BAD_SECURE 8 /* secure in a bad place */
484 #define CERR_OUT_OF_MEMORY 9
485 #define CERR_NO_TAILMATCH 10
486 #define CERR_COMMENT 11 /* a commented line */
487 #define CERR_RANGE 12 /* expire range problem */
488 #define CERR_FIELDS 13 /* incomplete netscape line */
489 #define CERR_PSL 14 /* a public suffix */
490 #define CERR_LIVE_WINS 15
491
492 /* The maximum length we accept a date string for the 'expire' keyword. The
493 standard date formats are within the 30 bytes range. This adds an extra
494 margin just to make sure it realistically works with what is used out
495 there.
496 */
497 #define MAX_DATE_LENGTH 80
498
499 static int
parse_cookie_header(struct Curl_easy * data,struct Cookie * co,struct CookieInfo * ci,const char * ptr,const char * domain,const char * path,bool secure)500 parse_cookie_header(struct Curl_easy *data,
501 struct Cookie *co,
502 struct CookieInfo *ci,
503 const char *ptr,
504 const char *domain, /* default domain */
505 const char *path, /* full path used when this cookie is
506 set, used to get default path for
507 the cookie unless set */
508 bool secure) /* TRUE if connection is over secure
509 origin */
510 {
511 /* This line was read off an HTTP-header */
512 time_t now;
513 size_t linelength = strlen(ptr);
514 if(linelength > MAX_COOKIE_LINE)
515 /* discard overly long lines at once */
516 return CERR_TOO_LONG;
517
518 now = time(NULL);
519 do {
520 size_t vlen;
521 size_t nlen;
522
523 while(*ptr && ISBLANK(*ptr))
524 ptr++;
525
526 /* we have a <name>=<value> pair or a stand-alone word here */
527 nlen = strcspn(ptr, ";\t\r\n=");
528 if(nlen) {
529 bool done = FALSE;
530 bool sep = FALSE;
531 const char *namep = ptr;
532 const char *valuep;
533
534 ptr += nlen;
535
536 /* trim trailing spaces and tabs after name */
537 while(nlen && ISBLANK(namep[nlen - 1]))
538 nlen--;
539
540 if(*ptr == '=') {
541 vlen = strcspn(++ptr, ";\r\n");
542 valuep = ptr;
543 sep = TRUE;
544 ptr = &valuep[vlen];
545
546 /* Strip off trailing whitespace from the value */
547 while(vlen && ISBLANK(valuep[vlen-1]))
548 vlen--;
549
550 /* Skip leading whitespace from the value */
551 while(vlen && ISBLANK(*valuep)) {
552 valuep++;
553 vlen--;
554 }
555
556 /* Reject cookies with a TAB inside the value */
557 if(memchr(valuep, '\t', vlen)) {
558 infof(data, "cookie contains TAB, dropping");
559 return CERR_TAB;
560 }
561 }
562 else {
563 valuep = NULL;
564 vlen = 0;
565 }
566
567 /*
568 * Check for too long individual name or contents, or too long
569 * combination of name + contents. Chrome and Firefox support 4095 or
570 * 4096 bytes combo
571 */
572 if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) ||
573 ((nlen + vlen) > MAX_NAME)) {
574 infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
575 nlen, vlen);
576 return CERR_TOO_BIG;
577 }
578
579 /*
580 * Check if we have a reserved prefix set before anything else, as we
581 * otherwise have to test for the prefix in both the cookie name and
582 * "the rest". Prefixes must start with '__' and end with a '-', so
583 * only test for names where that can possibly be true.
584 */
585 if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') {
586 if(strncasecompare("__Secure-", namep, 9))
587 co->prefix_secure = TRUE;
588 else if(strncasecompare("__Host-", namep, 7))
589 co->prefix_host = TRUE;
590 }
591
592 /*
593 * Use strstore() below to properly deal with received cookie
594 * headers that have the same string property set more than once,
595 * and then we use the last one.
596 */
597
598 if(!co->name) {
599 /* The very first name/value pair is the actual cookie name */
600 if(!sep)
601 /* Bad name/value pair. */
602 return CERR_NO_SEP;
603
604 strstore(&co->name, namep, nlen);
605 strstore(&co->value, valuep, vlen);
606 done = TRUE;
607 if(!co->name || !co->value)
608 return CERR_NO_NAME_VALUE;
609
610 if(invalid_octets(co->value) || invalid_octets(co->name)) {
611 infof(data, "invalid octets in name/value, cookie dropped");
612 return CERR_INVALID_OCTET;
613 }
614 }
615 else if(!vlen) {
616 /*
617 * this was a "<name>=" with no content, and we must allow
618 * 'secure' and 'httponly' specified this weirdly
619 */
620 done = TRUE;
621 /*
622 * secure cookies are only allowed to be set when the connection is
623 * using a secure protocol, or when the cookie is being set by
624 * reading from file
625 */
626 if((nlen == 6) && strncasecompare("secure", namep, 6)) {
627 if(secure || !ci->running) {
628 co->secure = TRUE;
629 }
630 else {
631 return CERR_BAD_SECURE;
632 }
633 }
634 else if((nlen == 8) && strncasecompare("httponly", namep, 8))
635 co->httponly = TRUE;
636 else if(sep)
637 /* there was a '=' so we are not done parsing this field */
638 done = FALSE;
639 }
640 if(done)
641 ;
642 else if((nlen == 4) && strncasecompare("path", namep, 4)) {
643 strstore(&co->path, valuep, vlen);
644 if(!co->path)
645 return CERR_OUT_OF_MEMORY;
646 free(co->spath); /* if this is set again */
647 co->spath = sanitize_cookie_path(co->path);
648 if(!co->spath)
649 return CERR_OUT_OF_MEMORY;
650 }
651 else if((nlen == 6) &&
652 strncasecompare("domain", namep, 6) && vlen) {
653 bool is_ip;
654
655 /*
656 * Now, we make sure that our host is within the given domain, or
657 * the given domain is not valid and thus cannot be set.
658 */
659
660 if('.' == valuep[0]) {
661 valuep++; /* ignore preceding dot */
662 vlen--;
663 }
664
665 #ifndef USE_LIBPSL
666 /*
667 * Without PSL we do not know when the incoming cookie is set on a
668 * TLD or otherwise "protected" suffix. To reduce risk, we require a
669 * dot OR the exact hostname being "localhost".
670 */
671 if(bad_domain(valuep, vlen))
672 domain = ":";
673 #endif
674
675 is_ip = Curl_host_is_ipnum(domain ? domain : valuep);
676
677 if(!domain
678 || (is_ip && !strncmp(valuep, domain, vlen) &&
679 (vlen == strlen(domain)))
680 || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) {
681 strstore(&co->domain, valuep, vlen);
682 if(!co->domain)
683 return CERR_OUT_OF_MEMORY;
684
685 if(!is_ip)
686 co->tailmatch = TRUE; /* we always do that if the domain name was
687 given */
688 }
689 else {
690 /*
691 * We did not get a tailmatch and then the attempted set domain is
692 * not a domain to which the current host belongs. Mark as bad.
693 */
694 infof(data, "skipped cookie with bad tailmatch domain: %s",
695 valuep);
696 return CERR_NO_TAILMATCH;
697 }
698 }
699 else if((nlen == 7) && strncasecompare("version", namep, 7)) {
700 /* just ignore */
701 }
702 else if((nlen == 7) && strncasecompare("max-age", namep, 7)) {
703 /*
704 * Defined in RFC2109:
705 *
706 * Optional. The Max-Age attribute defines the lifetime of the
707 * cookie, in seconds. The delta-seconds value is a decimal non-
708 * negative integer. After delta-seconds seconds elapse, the
709 * client should discard the cookie. A value of zero means the
710 * cookie should be discarded immediately.
711 */
712 CURLofft offt;
713 const char *maxage = valuep;
714 offt = curlx_strtoofft((*maxage == '\"') ?
715 &maxage[1] : &maxage[0], NULL, 10,
716 &co->expires);
717 switch(offt) {
718 case CURL_OFFT_FLOW:
719 /* overflow, used max value */
720 co->expires = CURL_OFF_T_MAX;
721 break;
722 case CURL_OFFT_INVAL:
723 /* negative or otherwise bad, expire */
724 co->expires = 1;
725 break;
726 case CURL_OFFT_OK:
727 if(!co->expires)
728 /* already expired */
729 co->expires = 1;
730 else if(CURL_OFF_T_MAX - now < co->expires)
731 /* would overflow */
732 co->expires = CURL_OFF_T_MAX;
733 else
734 co->expires += now;
735 break;
736 }
737 cap_expires(now, co);
738 }
739 else if((nlen == 7) && strncasecompare("expires", namep, 7)) {
740 if(!co->expires && (vlen < MAX_DATE_LENGTH)) {
741 /*
742 * Let max-age have priority.
743 *
744 * If the date cannot get parsed for whatever reason, the cookie
745 * will be treated as a session cookie
746 */
747 char dbuf[MAX_DATE_LENGTH + 1];
748 memcpy(dbuf, valuep, vlen);
749 dbuf[vlen] = 0;
750 co->expires = Curl_getdate_capped(dbuf);
751
752 /*
753 * Session cookies have expires set to 0 so if we get that back
754 * from the date parser let's add a second to make it a
755 * non-session cookie
756 */
757 if(co->expires == 0)
758 co->expires = 1;
759 else if(co->expires < 0)
760 co->expires = 0;
761 cap_expires(now, co);
762 }
763 }
764
765 /*
766 * Else, this is the second (or more) name we do not know about!
767 */
768 }
769 else {
770 /* this is an "illegal" <what>=<this> pair */
771 }
772
773 while(*ptr && ISBLANK(*ptr))
774 ptr++;
775 if(*ptr == ';')
776 ptr++;
777 else
778 break;
779 } while(1);
780
781 if(!co->domain && domain) {
782 /* no domain was given in the header line, set the default */
783 co->domain = strdup(domain);
784 if(!co->domain)
785 return CERR_OUT_OF_MEMORY;
786 }
787
788 if(!co->path && path) {
789 /*
790 * No path was given in the header line, set the default. Note that the
791 * passed-in path to this function MAY have a '?' and following part that
792 * MUST NOT be stored as part of the path.
793 */
794 char *queryp = strchr(path, '?');
795
796 /*
797 * queryp is where the interesting part of the path ends, so now we
798 * want to the find the last
799 */
800 char *endslash;
801 if(!queryp)
802 endslash = strrchr(path, '/');
803 else
804 endslash = memrchr(path, '/', (queryp - path));
805 if(endslash) {
806 size_t pathlen = (endslash-path + 1); /* include end slash */
807 co->path = Curl_memdup0(path, pathlen);
808 if(co->path) {
809 co->spath = sanitize_cookie_path(co->path);
810 if(!co->spath)
811 return CERR_OUT_OF_MEMORY;
812 }
813 else
814 return CERR_OUT_OF_MEMORY;
815 }
816 }
817
818 /*
819 * If we did not get a cookie name, or a bad one, the this is an illegal
820 * line so bail out.
821 */
822 if(!co->name)
823 return CERR_BAD;
824
825 data->req.setcookies++;
826 return CERR_OK;
827 }
828
829 static int
parse_netscape(struct Cookie * co,struct CookieInfo * ci,const char * lineptr,bool secure)830 parse_netscape(struct Cookie *co,
831 struct CookieInfo *ci,
832 const char *lineptr,
833 bool secure) /* TRUE if connection is over secure
834 origin */
835 {
836 /*
837 * This line is NOT an HTTP header style line, we do offer support for
838 * reading the odd netscape cookies-file format here
839 */
840 const char *ptr, *next;
841 int fields;
842 size_t len;
843
844 /*
845 * In 2008, Internet Explorer introduced HTTP-only cookies to prevent XSS
846 * attacks. Cookies marked httpOnly are not accessible to JavaScript. In
847 * Firefox's cookie files, they are prefixed #HttpOnly_ and the rest
848 * remains as usual, so we skip 10 characters of the line.
849 */
850 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
851 lineptr += 10;
852 co->httponly = TRUE;
853 }
854
855 if(lineptr[0]=='#')
856 /* do not even try the comments */
857 return CERR_COMMENT;
858
859 /*
860 * Now loop through the fields and init the struct we already have
861 * allocated
862 */
863 fields = 0;
864 for(next = lineptr; next; fields++) {
865 ptr = next;
866 len = strcspn(ptr, "\t\r\n");
867 next = (ptr[len] == '\t' ? &ptr[len + 1] : NULL);
868 switch(fields) {
869 case 0:
870 if(ptr[0]=='.') { /* skip preceding dots */
871 ptr++;
872 len--;
873 }
874 co->domain = Curl_memdup0(ptr, len);
875 if(!co->domain)
876 return CERR_OUT_OF_MEMORY;
877 break;
878 case 1:
879 /*
880 * flag: A TRUE/FALSE value indicating if all machines within a given
881 * domain can access the variable. Set TRUE when the cookie says
882 * .domain.com and to false when the domain is complete www.domain.com
883 */
884 co->tailmatch = !!strncasecompare(ptr, "TRUE", len);
885 break;
886 case 2:
887 /* The file format allows the path field to remain not filled in */
888 if(strncmp("TRUE", ptr, len) && strncmp("FALSE", ptr, len)) {
889 /* only if the path does not look like a boolean option! */
890 co->path = Curl_memdup0(ptr, len);
891 if(!co->path)
892 return CERR_OUT_OF_MEMORY;
893 else {
894 co->spath = sanitize_cookie_path(co->path);
895 if(!co->spath)
896 return CERR_OUT_OF_MEMORY;
897 }
898 break;
899 }
900 /* this does not look like a path, make one up! */
901 co->path = strdup("/");
902 if(!co->path)
903 return CERR_OUT_OF_MEMORY;
904 co->spath = strdup("/");
905 if(!co->spath)
906 return CERR_OUT_OF_MEMORY;
907 fields++; /* add a field and fall down to secure */
908 FALLTHROUGH();
909 case 3:
910 co->secure = FALSE;
911 if(strncasecompare(ptr, "TRUE", len)) {
912 if(secure || ci->running)
913 co->secure = TRUE;
914 else
915 return CERR_BAD_SECURE;
916 }
917 break;
918 case 4:
919 {
920 char *endp;
921 const char *p;
922 /* make sure curlx_strtoofft won't read past the current field */
923 for(p = ptr; p < &ptr[len] && ISDIGIT(*p); ++p)
924 ;
925 if(p == ptr || p != &ptr[len] ||
926 curlx_strtoofft(ptr, &endp, 10, &co->expires) || endp != &ptr[len])
927 return CERR_RANGE;
928 }
929 break;
930 case 5:
931 co->name = Curl_memdup0(ptr, len);
932 if(!co->name)
933 return CERR_OUT_OF_MEMORY;
934 else {
935 /* For Netscape file format cookies we check prefix on the name */
936 if(strncasecompare("__Secure-", co->name, 9))
937 co->prefix_secure = TRUE;
938 else if(strncasecompare("__Host-", co->name, 7))
939 co->prefix_host = TRUE;
940 }
941 break;
942 case 6:
943 co->value = Curl_memdup0(ptr, len);
944 if(!co->value)
945 return CERR_OUT_OF_MEMORY;
946 break;
947 }
948 }
949 if(6 == fields) {
950 /* we got a cookie with blank contents, fix it */
951 co->value = strdup("");
952 if(!co->value)
953 return CERR_OUT_OF_MEMORY;
954 else
955 fields++;
956 }
957
958 if(7 != fields)
959 /* we did not find the sufficient number of fields */
960 return CERR_FIELDS;
961
962 return CERR_OK;
963 }
964
965 static int
is_public_suffix(struct Curl_easy * data,struct Cookie * co,const char * domain)966 is_public_suffix(struct Curl_easy *data,
967 struct Cookie *co,
968 const char *domain)
969 {
970 #ifdef USE_LIBPSL
971 /*
972 * Check if the domain is a Public Suffix and if yes, ignore the cookie. We
973 * must also check that the data handle is not NULL since the psl code will
974 * dereference it.
975 */
976 DEBUGF(infof(data, "PSL check set-cookie '%s' for domain=%s in %s",
977 co->name, co->domain, domain));
978 if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) {
979 bool acceptable = FALSE;
980 char lcase[256];
981 char lcookie[256];
982 size_t dlen = strlen(domain);
983 size_t clen = strlen(co->domain);
984 if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) {
985 const psl_ctx_t *psl = Curl_psl_use(data);
986 if(psl) {
987 /* the PSL check requires lowercase domain name and pattern */
988 Curl_strntolower(lcase, domain, dlen + 1);
989 Curl_strntolower(lcookie, co->domain, clen + 1);
990 acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie);
991 Curl_psl_release(data);
992 }
993 else
994 infof(data, "libpsl problem, rejecting cookie for satety");
995 }
996
997 if(!acceptable) {
998 infof(data, "cookie '%s' dropped, domain '%s' must not "
999 "set cookies for '%s'", co->name, domain, co->domain);
1000 return CERR_PSL;
1001 }
1002 }
1003 #else
1004 (void)data;
1005 (void)co;
1006 (void)domain;
1007 DEBUGF(infof(data, "NO PSL to check set-cookie '%s' for domain=%s in %s",
1008 co->name, co->domain, domain));
1009 #endif
1010 return CERR_OK;
1011 }
1012
1013 static int
replace_existing(struct Curl_easy * data,struct Cookie * co,struct CookieInfo * ci,bool secure,bool * replacep)1014 replace_existing(struct Curl_easy *data,
1015 struct Cookie *co,
1016 struct CookieInfo *ci,
1017 bool secure,
1018 bool *replacep)
1019 {
1020 bool replace_old = FALSE;
1021 struct Curl_llist_node *replace_n = NULL;
1022 struct Curl_llist_node *n;
1023 size_t myhash = cookiehash(co->domain);
1024 for(n = Curl_llist_head(&ci->cookielist[myhash]); n; n = Curl_node_next(n)) {
1025 struct Cookie *clist = Curl_node_elem(n);
1026 if(!strcmp(clist->name, co->name)) {
1027 /* the names are identical */
1028 bool matching_domains = FALSE;
1029
1030 if(clist->domain && co->domain) {
1031 if(strcasecompare(clist->domain, co->domain))
1032 /* The domains are identical */
1033 matching_domains = TRUE;
1034 }
1035 else if(!clist->domain && !co->domain)
1036 matching_domains = TRUE;
1037
1038 if(matching_domains && /* the domains were identical */
1039 clist->spath && co->spath && /* both have paths */
1040 clist->secure && !co->secure && !secure) {
1041 size_t cllen;
1042 const char *sep;
1043
1044 /*
1045 * A non-secure cookie may not overlay an existing secure cookie.
1046 * For an existing cookie "a" with path "/login", refuse a new
1047 * cookie "a" with for example path "/login/en", while the path
1048 * "/loginhelper" is ok.
1049 */
1050
1051 sep = strchr(clist->spath + 1, '/');
1052
1053 if(sep)
1054 cllen = sep - clist->spath;
1055 else
1056 cllen = strlen(clist->spath);
1057
1058 if(strncasecompare(clist->spath, co->spath, cllen)) {
1059 infof(data, "cookie '%s' for domain '%s' dropped, would "
1060 "overlay an existing cookie", co->name, co->domain);
1061 return CERR_BAD_SECURE;
1062 }
1063 }
1064 }
1065
1066 if(!replace_n && !strcmp(clist->name, co->name)) {
1067 /* the names are identical */
1068
1069 if(clist->domain && co->domain) {
1070 if(strcasecompare(clist->domain, co->domain) &&
1071 (clist->tailmatch == co->tailmatch))
1072 /* The domains are identical */
1073 replace_old = TRUE;
1074 }
1075 else if(!clist->domain && !co->domain)
1076 replace_old = TRUE;
1077
1078 if(replace_old) {
1079 /* the domains were identical */
1080
1081 if(clist->spath && co->spath &&
1082 !strcasecompare(clist->spath, co->spath))
1083 replace_old = FALSE;
1084 else if(!clist->spath != !co->spath)
1085 replace_old = FALSE;
1086 }
1087
1088 if(replace_old && !co->livecookie && clist->livecookie) {
1089 /*
1090 * Both cookies matched fine, except that the already present cookie
1091 * is "live", which means it was set from a header, while the new one
1092 * was read from a file and thus is not "live". "live" cookies are
1093 * preferred so the new cookie is freed.
1094 */
1095 return CERR_LIVE_WINS;
1096 }
1097 if(replace_old)
1098 replace_n = n;
1099 }
1100 }
1101 if(replace_n) {
1102 struct Cookie *repl = Curl_node_elem(replace_n);
1103
1104 /* when replacing, creationtime is kept from old */
1105 co->creationtime = repl->creationtime;
1106
1107 /* unlink the old */
1108 Curl_node_remove(replace_n);
1109
1110 /* free the old cookie */
1111 freecookie(repl);
1112 }
1113 *replacep = replace_old;
1114 return CERR_OK;
1115 }
1116
1117 /*
1118 * Curl_cookie_add
1119 *
1120 * Add a single cookie line to the cookie keeping object. Be aware that
1121 * sometimes we get an IP-only hostname, and that might also be a numerical
1122 * IPv6 address.
1123 *
1124 * Returns NULL on out of memory or invalid cookie. This is suboptimal,
1125 * as they should be treated separately.
1126 */
1127 struct Cookie *
Curl_cookie_add(struct Curl_easy * data,struct CookieInfo * ci,bool httpheader,bool noexpire,const char * lineptr,const char * domain,const char * path,bool secure)1128 Curl_cookie_add(struct Curl_easy *data,
1129 struct CookieInfo *ci,
1130 bool httpheader, /* TRUE if HTTP header-style line */
1131 bool noexpire, /* if TRUE, skip remove_expired() */
1132 const char *lineptr, /* first character of the line */
1133 const char *domain, /* default domain */
1134 const char *path, /* full path used when this cookie is set,
1135 used to get default path for the cookie
1136 unless set */
1137 bool secure) /* TRUE if connection is over secure origin */
1138 {
1139 struct Cookie *co;
1140 size_t myhash;
1141 int rc;
1142 bool replaces = FALSE;
1143
1144 DEBUGASSERT(data);
1145 DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */
1146 if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT)
1147 return NULL;
1148
1149 /* First, alloc and init a new struct for it */
1150 co = calloc(1, sizeof(struct Cookie));
1151 if(!co)
1152 return NULL; /* bail out if we are this low on memory */
1153
1154 if(httpheader)
1155 rc = parse_cookie_header(data, co, ci, lineptr, domain, path, secure);
1156 else
1157 rc = parse_netscape(co, ci, lineptr, secure);
1158
1159 if(rc)
1160 goto fail;
1161
1162 if(co->prefix_secure && !co->secure)
1163 /* The __Secure- prefix only requires that the cookie be set secure */
1164 goto fail;
1165
1166 if(co->prefix_host) {
1167 /*
1168 * The __Host- prefix requires the cookie to be secure, have a "/" path
1169 * and not have a domain set.
1170 */
1171 if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch)
1172 ;
1173 else
1174 goto fail;
1175 }
1176
1177 if(!ci->running && /* read from a file */
1178 ci->newsession && /* clean session cookies */
1179 !co->expires) /* this is a session cookie since it does not expire */
1180 goto fail;
1181
1182 co->livecookie = ci->running;
1183 co->creationtime = ++ci->lastct;
1184
1185 /*
1186 * Now we have parsed the incoming line, we must now check if this supersedes
1187 * an already existing cookie, which it may if the previous have the same
1188 * domain and path as this.
1189 */
1190
1191 /* remove expired cookies */
1192 if(!noexpire)
1193 remove_expired(ci);
1194
1195 if(is_public_suffix(data, co, domain))
1196 goto fail;
1197
1198 if(replace_existing(data, co, ci, secure, &replaces))
1199 goto fail;
1200
1201 /* add this cookie to the list */
1202 myhash = cookiehash(co->domain);
1203 Curl_llist_append(&ci->cookielist[myhash], co, &co->node);
1204
1205 if(ci->running)
1206 /* Only show this when NOT reading the cookies from a file */
1207 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
1208 "expire %" FMT_OFF_T,
1209 replaces ? "Replaced":"Added", co->name, co->value,
1210 co->domain, co->path, co->expires);
1211
1212 if(!replaces)
1213 ci->numcookies++; /* one more cookie in the jar */
1214
1215 /*
1216 * Now that we have added a new cookie to the jar, update the expiration
1217 * tracker in case it is the next one to expire.
1218 */
1219 if(co->expires && (co->expires < ci->next_expiration))
1220 ci->next_expiration = co->expires;
1221
1222 return co;
1223 fail:
1224 freecookie(co);
1225 return NULL;
1226 }
1227
1228
1229 /*
1230 * Curl_cookie_init()
1231 *
1232 * Inits a cookie struct to read data from a local file. This is always
1233 * called before any cookies are set. File may be NULL in which case only the
1234 * struct is initialized. Is file is "-" then STDIN is read.
1235 *
1236 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
1237 *
1238 * Note that 'data' might be called as NULL pointer. If data is NULL, 'file'
1239 * will be ignored.
1240 *
1241 * Returns NULL on out of memory. Invalid cookies are ignored.
1242 */
Curl_cookie_init(struct Curl_easy * data,const char * file,struct CookieInfo * ci,bool newsession)1243 struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
1244 const char *file,
1245 struct CookieInfo *ci,
1246 bool newsession)
1247 {
1248 FILE *handle = NULL;
1249
1250 if(!ci) {
1251 int i;
1252
1253 /* we did not get a struct, create one */
1254 ci = calloc(1, sizeof(struct CookieInfo));
1255 if(!ci)
1256 return NULL; /* failed to get memory */
1257
1258 /* This does not use the destructor callback since we want to add
1259 and remove to lists while keeping the cookie struct intact */
1260 for(i = 0; i < COOKIE_HASH_SIZE; i++)
1261 Curl_llist_init(&ci->cookielist[i], NULL);
1262 /*
1263 * Initialize the next_expiration time to signal that we do not have enough
1264 * information yet.
1265 */
1266 ci->next_expiration = CURL_OFF_T_MAX;
1267 }
1268 ci->newsession = newsession; /* new session? */
1269
1270 if(data) {
1271 FILE *fp = NULL;
1272 if(file && *file) {
1273 if(!strcmp(file, "-"))
1274 fp = stdin;
1275 else {
1276 fp = fopen(file, "rb");
1277 if(!fp)
1278 infof(data, "WARNING: failed to open cookie file \"%s\"", file);
1279 else
1280 handle = fp;
1281 }
1282 }
1283
1284 ci->running = FALSE; /* this is not running, this is init */
1285 if(fp) {
1286 struct dynbuf buf;
1287 Curl_dyn_init(&buf, MAX_COOKIE_LINE);
1288 while(Curl_get_line(&buf, fp)) {
1289 char *lineptr = Curl_dyn_ptr(&buf);
1290 bool headerline = FALSE;
1291 if(checkprefix("Set-Cookie:", lineptr)) {
1292 /* This is a cookie line, get it! */
1293 lineptr += 11;
1294 headerline = TRUE;
1295 while(*lineptr && ISBLANK(*lineptr))
1296 lineptr++;
1297 }
1298
1299 Curl_cookie_add(data, ci, headerline, TRUE, lineptr, NULL, NULL, TRUE);
1300 }
1301 Curl_dyn_free(&buf); /* free the line buffer */
1302
1303 /*
1304 * Remove expired cookies from the hash. We must make sure to run this
1305 * after reading the file, and not on every cookie.
1306 */
1307 remove_expired(ci);
1308
1309 if(handle)
1310 fclose(handle);
1311 }
1312 data->state.cookie_engine = TRUE;
1313 }
1314 ci->running = TRUE; /* now, we are running */
1315
1316 return ci;
1317 }
1318
1319 /*
1320 * cookie_sort
1321 *
1322 * Helper function to sort cookies such that the longest path gets before the
1323 * shorter path. Path, domain and name lengths are considered in that order,
1324 * with the creationtime as the tiebreaker. The creationtime is guaranteed to
1325 * be unique per cookie, so we know we will get an ordering at that point.
1326 */
cookie_sort(const void * p1,const void * p2)1327 static int cookie_sort(const void *p1, const void *p2)
1328 {
1329 struct Cookie *c1 = *(struct Cookie **)p1;
1330 struct Cookie *c2 = *(struct Cookie **)p2;
1331 size_t l1, l2;
1332
1333 /* 1 - compare cookie path lengths */
1334 l1 = c1->path ? strlen(c1->path) : 0;
1335 l2 = c2->path ? strlen(c2->path) : 0;
1336
1337 if(l1 != l2)
1338 return (l2 > l1) ? 1 : -1; /* avoid size_t <=> int conversions */
1339
1340 /* 2 - compare cookie domain lengths */
1341 l1 = c1->domain ? strlen(c1->domain) : 0;
1342 l2 = c2->domain ? strlen(c2->domain) : 0;
1343
1344 if(l1 != l2)
1345 return (l2 > l1) ? 1 : -1; /* avoid size_t <=> int conversions */
1346
1347 /* 3 - compare cookie name lengths */
1348 l1 = c1->name ? strlen(c1->name) : 0;
1349 l2 = c2->name ? strlen(c2->name) : 0;
1350
1351 if(l1 != l2)
1352 return (l2 > l1) ? 1 : -1;
1353
1354 /* 4 - compare cookie creation time */
1355 return (c2->creationtime > c1->creationtime) ? 1 : -1;
1356 }
1357
1358 /*
1359 * cookie_sort_ct
1360 *
1361 * Helper function to sort cookies according to creation time.
1362 */
cookie_sort_ct(const void * p1,const void * p2)1363 static int cookie_sort_ct(const void *p1, const void *p2)
1364 {
1365 struct Cookie *c1 = *(struct Cookie **)p1;
1366 struct Cookie *c2 = *(struct Cookie **)p2;
1367
1368 return (c2->creationtime > c1->creationtime) ? 1 : -1;
1369 }
1370
1371 /*
1372 * Curl_cookie_getlist
1373 *
1374 * For a given host and path, return a linked list of cookies that the client
1375 * should send to the server if used now. The secure boolean informs the cookie
1376 * if a secure connection is achieved or not.
1377 *
1378 * It shall only return cookies that have not expired.
1379 *
1380 * Returns 0 when there is a list returned. Otherwise non-zero.
1381 */
Curl_cookie_getlist(struct Curl_easy * data,struct CookieInfo * ci,const char * host,const char * path,bool secure,struct Curl_llist * list)1382 int Curl_cookie_getlist(struct Curl_easy *data,
1383 struct CookieInfo *ci,
1384 const char *host, const char *path,
1385 bool secure,
1386 struct Curl_llist *list)
1387 {
1388 size_t matches = 0;
1389 bool is_ip;
1390 const size_t myhash = cookiehash(host);
1391 struct Curl_llist_node *n;
1392
1393 Curl_llist_init(list, NULL);
1394
1395 if(!ci || !Curl_llist_count(&ci->cookielist[myhash]))
1396 return 1; /* no cookie struct or no cookies in the struct */
1397
1398 /* at first, remove expired cookies */
1399 remove_expired(ci);
1400
1401 /* check if host is an IP(v4|v6) address */
1402 is_ip = Curl_host_is_ipnum(host);
1403
1404 for(n = Curl_llist_head(&ci->cookielist[myhash]);
1405 n; n = Curl_node_next(n)) {
1406 struct Cookie *co = Curl_node_elem(n);
1407
1408 /* if the cookie requires we are secure we must only continue if we are! */
1409 if(co->secure ? secure : TRUE) {
1410
1411 /* now check if the domain is correct */
1412 if(!co->domain ||
1413 (co->tailmatch && !is_ip &&
1414 cookie_tailmatch(co->domain, strlen(co->domain), host)) ||
1415 ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
1416 /*
1417 * the right part of the host matches the domain stuff in the
1418 * cookie data
1419 */
1420
1421 /*
1422 * now check the left part of the path with the cookies path
1423 * requirement
1424 */
1425 if(!co->spath || pathmatch(co->spath, path) ) {
1426
1427 /*
1428 * This is a match and we add it to the return-linked-list
1429 */
1430 Curl_llist_append(list, co, &co->getnode);
1431 matches++;
1432 if(matches >= MAX_COOKIE_SEND_AMOUNT) {
1433 infof(data, "Included max number of cookies (%zu) in request!",
1434 matches);
1435 break;
1436 }
1437 }
1438 }
1439 }
1440 }
1441
1442 if(matches) {
1443 /*
1444 * Now we need to make sure that if there is a name appearing more than
1445 * once, the longest specified path version comes first. To make this
1446 * the swiftest way, we just sort them all based on path length.
1447 */
1448 struct Cookie **array;
1449 size_t i;
1450
1451 /* alloc an array and store all cookie pointers */
1452 array = malloc(sizeof(struct Cookie *) * matches);
1453 if(!array)
1454 goto fail;
1455
1456 n = Curl_llist_head(list);
1457
1458 for(i = 0; n; n = Curl_node_next(n))
1459 array[i++] = Curl_node_elem(n);
1460
1461 /* now sort the cookie pointers in path length order */
1462 qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
1463
1464 /* remake the linked list order according to the new order */
1465 Curl_llist_destroy(list, NULL);
1466
1467 for(i = 0; i < matches; i++)
1468 Curl_llist_append(list, array[i], &array[i]->getnode);
1469
1470 free(array); /* remove the temporary data again */
1471 }
1472
1473 return 0; /* success */
1474
1475 fail:
1476 /* failure, clear up the allocated chain and return NULL */
1477 Curl_llist_destroy(list, NULL);
1478 return 2; /* error */
1479 }
1480
1481 /*
1482 * Curl_cookie_clearall
1483 *
1484 * Clear all existing cookies and reset the counter.
1485 */
Curl_cookie_clearall(struct CookieInfo * ci)1486 void Curl_cookie_clearall(struct CookieInfo *ci)
1487 {
1488 if(ci) {
1489 unsigned int i;
1490 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
1491 struct Curl_llist_node *n;
1492 for(n = Curl_llist_head(&ci->cookielist[i]); n;) {
1493 struct Cookie *c = Curl_node_elem(n);
1494 struct Curl_llist_node *e = Curl_node_next(n);
1495 Curl_node_remove(n);
1496 freecookie(c);
1497 n = e;
1498 }
1499 }
1500 ci->numcookies = 0;
1501 }
1502 }
1503
1504 /*
1505 * Curl_cookie_clearsess
1506 *
1507 * Free all session cookies in the cookies list.
1508 */
Curl_cookie_clearsess(struct CookieInfo * ci)1509 void Curl_cookie_clearsess(struct CookieInfo *ci)
1510 {
1511 unsigned int i;
1512
1513 if(!ci)
1514 return;
1515
1516 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
1517 struct Curl_llist_node *n = Curl_llist_head(&ci->cookielist[i]);
1518 struct Curl_llist_node *e = NULL;
1519
1520 for(; n; n = e) {
1521 struct Cookie *curr = Curl_node_elem(n);
1522 e = Curl_node_next(n); /* in case the node is removed, get it early */
1523 if(!curr->expires) {
1524 Curl_node_remove(n);
1525 freecookie(curr);
1526 ci->numcookies--;
1527 }
1528 }
1529 }
1530 }
1531
1532 /*
1533 * Curl_cookie_cleanup()
1534 *
1535 * Free a "cookie object" previous created with Curl_cookie_init().
1536 */
Curl_cookie_cleanup(struct CookieInfo * ci)1537 void Curl_cookie_cleanup(struct CookieInfo *ci)
1538 {
1539 if(ci) {
1540 Curl_cookie_clearall(ci);
1541 free(ci); /* free the base struct as well */
1542 }
1543 }
1544
1545 /*
1546 * get_netscape_format()
1547 *
1548 * Formats a string for Netscape output file, w/o a newline at the end.
1549 * Function returns a char * to a formatted line. The caller is responsible
1550 * for freeing the returned pointer.
1551 */
get_netscape_format(const struct Cookie * co)1552 static char *get_netscape_format(const struct Cookie *co)
1553 {
1554 return aprintf(
1555 "%s" /* httponly preamble */
1556 "%s%s\t" /* domain */
1557 "%s\t" /* tailmatch */
1558 "%s\t" /* path */
1559 "%s\t" /* secure */
1560 "%" FMT_OFF_T "\t" /* expires */
1561 "%s\t" /* name */
1562 "%s", /* value */
1563 co->httponly ? "#HttpOnly_" : "",
1564 /*
1565 * Make sure all domains are prefixed with a dot if they allow
1566 * tailmatching. This is Mozilla-style.
1567 */
1568 (co->tailmatch && co->domain && co->domain[0] != '.') ? "." : "",
1569 co->domain ? co->domain : "unknown",
1570 co->tailmatch ? "TRUE" : "FALSE",
1571 co->path ? co->path : "/",
1572 co->secure ? "TRUE" : "FALSE",
1573 co->expires,
1574 co->name,
1575 co->value ? co->value : "");
1576 }
1577
1578 /*
1579 * cookie_output()
1580 *
1581 * Writes all internally known cookies to the specified file. Specify
1582 * "-" as filename to write to stdout.
1583 *
1584 * The function returns non-zero on write failure.
1585 */
cookie_output(struct Curl_easy * data,struct CookieInfo * ci,const char * filename)1586 static CURLcode cookie_output(struct Curl_easy *data,
1587 struct CookieInfo *ci,
1588 const char *filename)
1589 {
1590 FILE *out = NULL;
1591 bool use_stdout = FALSE;
1592 char *tempstore = NULL;
1593 CURLcode error = CURLE_OK;
1594
1595 if(!ci)
1596 /* no cookie engine alive */
1597 return CURLE_OK;
1598
1599 /* at first, remove expired cookies */
1600 remove_expired(ci);
1601
1602 if(!strcmp("-", filename)) {
1603 /* use stdout */
1604 out = stdout;
1605 use_stdout = TRUE;
1606 }
1607 else {
1608 error = Curl_fopen(data, filename, &out, &tempstore);
1609 if(error)
1610 goto error;
1611 }
1612
1613 fputs("# Netscape HTTP Cookie File\n"
1614 "# https://curl.se/docs/http-cookies.html\n"
1615 "# This file was generated by libcurl! Edit at your own risk.\n\n",
1616 out);
1617
1618 if(ci->numcookies) {
1619 unsigned int i;
1620 size_t nvalid = 0;
1621 struct Cookie **array;
1622 struct Curl_llist_node *n;
1623
1624 array = calloc(1, sizeof(struct Cookie *) * ci->numcookies);
1625 if(!array) {
1626 error = CURLE_OUT_OF_MEMORY;
1627 goto error;
1628 }
1629
1630 /* only sort the cookies with a domain property */
1631 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
1632 for(n = Curl_llist_head(&ci->cookielist[i]); n;
1633 n = Curl_node_next(n)) {
1634 struct Cookie *co = Curl_node_elem(n);
1635 if(!co->domain)
1636 continue;
1637 array[nvalid++] = co;
1638 }
1639 }
1640
1641 qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct);
1642
1643 for(i = 0; i < nvalid; i++) {
1644 char *format_ptr = get_netscape_format(array[i]);
1645 if(!format_ptr) {
1646 free(array);
1647 error = CURLE_OUT_OF_MEMORY;
1648 goto error;
1649 }
1650 fprintf(out, "%s\n", format_ptr);
1651 free(format_ptr);
1652 }
1653
1654 free(array);
1655 }
1656
1657 if(!use_stdout) {
1658 fclose(out);
1659 out = NULL;
1660 if(tempstore && Curl_rename(tempstore, filename)) {
1661 unlink(tempstore);
1662 error = CURLE_WRITE_ERROR;
1663 goto error;
1664 }
1665 }
1666
1667 /*
1668 * If we reach here we have successfully written a cookie file so there is
1669 * no need to inspect the error, any error case should have jumped into the
1670 * error block below.
1671 */
1672 free(tempstore);
1673 return CURLE_OK;
1674
1675 error:
1676 if(out && !use_stdout)
1677 fclose(out);
1678 free(tempstore);
1679 return error;
1680 }
1681
cookie_list(struct Curl_easy * data)1682 static struct curl_slist *cookie_list(struct Curl_easy *data)
1683 {
1684 struct curl_slist *list = NULL;
1685 struct curl_slist *beg;
1686 unsigned int i;
1687 struct Curl_llist_node *n;
1688
1689 if(!data->cookies || (data->cookies->numcookies == 0))
1690 return NULL;
1691
1692 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
1693 for(n = Curl_llist_head(&data->cookies->cookielist[i]); n;
1694 n = Curl_node_next(n)) {
1695 struct Cookie *c = Curl_node_elem(n);
1696 char *line;
1697 if(!c->domain)
1698 continue;
1699 line = get_netscape_format(c);
1700 if(!line) {
1701 curl_slist_free_all(list);
1702 return NULL;
1703 }
1704 beg = Curl_slist_append_nodup(list, line);
1705 if(!beg) {
1706 free(line);
1707 curl_slist_free_all(list);
1708 return NULL;
1709 }
1710 list = beg;
1711 }
1712 }
1713
1714 return list;
1715 }
1716
Curl_cookie_list(struct Curl_easy * data)1717 struct curl_slist *Curl_cookie_list(struct Curl_easy *data)
1718 {
1719 struct curl_slist *list;
1720 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1721 list = cookie_list(data);
1722 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1723 return list;
1724 }
1725
Curl_flush_cookies(struct Curl_easy * data,bool cleanup)1726 void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
1727 {
1728 CURLcode res;
1729
1730 if(data->set.str[STRING_COOKIEJAR]) {
1731 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1732
1733 /* if we have a destination file for all the cookies to get dumped to */
1734 res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]);
1735 if(res)
1736 infof(data, "WARNING: failed to save cookies in %s: %s",
1737 data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res));
1738 }
1739 else {
1740 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1741 }
1742
1743 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
1744 Curl_cookie_cleanup(data->cookies);
1745 data->cookies = NULL;
1746 }
1747 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1748 }
1749
1750 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */
1751