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