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