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 size_t j = (size_t)Curl_raw_toupper(*domain++);
266 h += h << 5;
267 h ^= j;
268 }
269
270 return (h % COOKIE_HASH_SIZE);
271 }
272
273 #if defined(_MSC_VER) && (_MSC_VER == 1900)
274 #pragma optimize("", on)
275 #endif
276
277 /*
278 * Hash this domain.
279 */
cookiehash(const char * const domain)280 static size_t cookiehash(const char * const domain)
281 {
282 const char *top;
283 size_t len;
284
285 if(!domain || Curl_host_is_ipnum(domain))
286 return 0;
287
288 top = get_top_domain(domain, &len);
289 return cookie_hash_domain(top, len);
290 }
291
292 /*
293 * cookie path sanitize
294 */
sanitize_cookie_path(const char * cookie_path)295 static char *sanitize_cookie_path(const char *cookie_path)
296 {
297 size_t len;
298 char *new_path = strdup(cookie_path);
299 if(!new_path)
300 return NULL;
301
302 /* some stupid site sends path attribute with '"'. */
303 len = strlen(new_path);
304 if(new_path[0] == '\"') {
305 memmove(new_path, new_path + 1, len);
306 len--;
307 }
308 if(len && (new_path[len - 1] == '\"')) {
309 new_path[--len] = 0x0;
310 }
311
312 /* RFC6265 5.2.4 The Path Attribute */
313 if(new_path[0] != '/') {
314 /* Let cookie-path be the default-path. */
315 strstore(&new_path, "/", 1);
316 return new_path;
317 }
318
319 /* convert /hoge/ to /hoge */
320 if(len && new_path[len - 1] == '/') {
321 new_path[len - 1] = 0x0;
322 }
323
324 return new_path;
325 }
326
327 /*
328 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
329 *
330 * NOTE: OOM or cookie parsing failures are ignored.
331 */
Curl_cookie_loadfiles(struct Curl_easy * data)332 void Curl_cookie_loadfiles(struct Curl_easy *data)
333 {
334 struct curl_slist *list = data->state.cookielist;
335 if(list) {
336 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
337 while(list) {
338 struct CookieInfo *newcookies =
339 Curl_cookie_init(data, list->data, data->cookies,
340 data->set.cookiesession);
341 if(!newcookies)
342 /*
343 * Failure may be due to OOM or a bad cookie; both are ignored
344 * but only the first should be
345 */
346 infof(data, "ignoring failed cookie_init for %s", list->data);
347 else
348 data->cookies = newcookies;
349 list = list->next;
350 }
351 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
352 }
353 }
354
355 /*
356 * strstore
357 *
358 * A thin wrapper around strdup which ensures that any memory allocated at
359 * *str will be freed before the string allocated by strdup is stored there.
360 * The intended usecase is repeated assignments to the same variable during
361 * parsing in a last-wins scenario. The caller is responsible for checking
362 * for OOM errors.
363 */
strstore(char ** str,const char * newstr,size_t len)364 static void strstore(char **str, const char *newstr, size_t len)
365 {
366 DEBUGASSERT(newstr);
367 DEBUGASSERT(str);
368 free(*str);
369 *str = Curl_memdup0(newstr, len);
370 }
371
372 /*
373 * remove_expired
374 *
375 * Remove expired cookies from the hash by inspecting the expires timestamp on
376 * each cookie in the hash, freeing and deleting any where the timestamp is in
377 * the past. If the cookiejar has recorded the next timestamp at which one or
378 * more cookies expire, then processing will exit early in case this timestamp
379 * is in the future.
380 */
remove_expired(struct CookieInfo * cookies)381 static void remove_expired(struct CookieInfo *cookies)
382 {
383 struct Cookie *co, *nx;
384 curl_off_t now = (curl_off_t)time(NULL);
385 unsigned int i;
386
387 /*
388 * If the earliest expiration timestamp in the jar is in the future we can
389 * skip scanning the whole jar and instead exit early as there won't be any
390 * cookies to evict. If we need to evict however, reset the next_expiration
391 * counter in order to track the next one. In case the recorded first
392 * expiration is the max offset, then perform the safe fallback of checking
393 * all cookies.
394 */
395 if(now < cookies->next_expiration &&
396 cookies->next_expiration != CURL_OFF_T_MAX)
397 return;
398 else
399 cookies->next_expiration = CURL_OFF_T_MAX;
400
401 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
402 struct Cookie *pv = NULL;
403 co = cookies->cookies[i];
404 while(co) {
405 nx = co->next;
406 if(co->expires && co->expires < now) {
407 if(!pv) {
408 cookies->cookies[i] = co->next;
409 }
410 else {
411 pv->next = co->next;
412 }
413 cookies->numcookies--;
414 freecookie(co);
415 }
416 else {
417 /*
418 * If this cookie has an expiration timestamp earlier than what we've
419 * seen so far then record it for the next round of expirations.
420 */
421 if(co->expires && co->expires < cookies->next_expiration)
422 cookies->next_expiration = co->expires;
423 pv = co;
424 }
425 co = nx;
426 }
427 }
428 }
429
430 #ifndef USE_LIBPSL
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 #endif
449
450 /*
451 RFC 6265 section 4.1.1 says a server should accept this range:
452
453 cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
454
455 But Firefox and Chrome as of June 2022 accept space, comma and double-quotes
456 fine. The prime reason for filtering out control bytes is that some HTTP
457 servers return 400 for requests that contain such.
458 */
invalid_octets(const char * p)459 static int invalid_octets(const char *p)
460 {
461 /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */
462 static const char badoctets[] = {
463 "\x01\x02\x03\x04\x05\x06\x07\x08\x0a"
464 "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
465 "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f"
466 };
467 size_t len;
468 /* scan for all the octets that are *not* in cookie-octet */
469 len = strcspn(p, badoctets);
470 return (p[len] != '\0');
471 }
472
473 /*
474 * Curl_cookie_add
475 *
476 * Add a single cookie line to the cookie keeping object. Be aware that
477 * sometimes we get an IP-only host name, and that might also be a numerical
478 * IPv6 address.
479 *
480 * Returns NULL on out of memory or invalid cookie. This is suboptimal,
481 * as they should be treated separately.
482 */
483 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)484 Curl_cookie_add(struct Curl_easy *data,
485 struct CookieInfo *c,
486 bool httpheader, /* TRUE if HTTP header-style line */
487 bool noexpire, /* if TRUE, skip remove_expired() */
488 const char *lineptr, /* first character of the line */
489 const char *domain, /* default domain */
490 const char *path, /* full path used when this cookie is set,
491 used to get default path for the cookie
492 unless set */
493 bool secure) /* TRUE if connection is over secure origin */
494 {
495 struct Cookie *clist;
496 struct Cookie *co;
497 struct Cookie *lastc = NULL;
498 struct Cookie *replace_co = NULL;
499 struct Cookie *replace_clist = NULL;
500 time_t now = time(NULL);
501 bool replace_old = FALSE;
502 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
503 size_t myhash;
504
505 DEBUGASSERT(data);
506 DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */
507 if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT)
508 return NULL;
509
510 /* First, alloc and init a new struct for it */
511 co = calloc(1, sizeof(struct Cookie));
512 if(!co)
513 return NULL; /* bail out if we're this low on memory */
514
515 if(httpheader) {
516 /* This line was read off an HTTP-header */
517 const char *ptr;
518
519 size_t linelength = strlen(lineptr);
520 if(linelength > MAX_COOKIE_LINE) {
521 /* discard overly long lines at once */
522 free(co);
523 return NULL;
524 }
525
526 ptr = lineptr;
527 do {
528 size_t vlen;
529 size_t nlen;
530
531 while(*ptr && ISBLANK(*ptr))
532 ptr++;
533
534 /* we have a <name>=<value> pair or a stand-alone word here */
535 nlen = strcspn(ptr, ";\t\r\n=");
536 if(nlen) {
537 bool done = FALSE;
538 bool sep = FALSE;
539 const char *namep = ptr;
540 const char *valuep;
541
542 ptr += nlen;
543
544 /* trim trailing spaces and tabs after name */
545 while(nlen && ISBLANK(namep[nlen - 1]))
546 nlen--;
547
548 if(*ptr == '=') {
549 vlen = strcspn(++ptr, ";\r\n");
550 valuep = ptr;
551 sep = TRUE;
552 ptr = &valuep[vlen];
553
554 /* Strip off trailing whitespace from the value */
555 while(vlen && ISBLANK(valuep[vlen-1]))
556 vlen--;
557
558 /* Skip leading whitespace from the value */
559 while(vlen && ISBLANK(*valuep)) {
560 valuep++;
561 vlen--;
562 }
563
564 /* Reject cookies with a TAB inside the value */
565 if(memchr(valuep, '\t', vlen)) {
566 freecookie(co);
567 infof(data, "cookie contains TAB, dropping");
568 return NULL;
569 }
570 }
571 else {
572 valuep = NULL;
573 vlen = 0;
574 }
575
576 /*
577 * Check for too long individual name or contents, or too long
578 * combination of name + contents. Chrome and Firefox support 4095 or
579 * 4096 bytes combo
580 */
581 if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) ||
582 ((nlen + vlen) > MAX_NAME)) {
583 freecookie(co);
584 infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
585 nlen, vlen);
586 return NULL;
587 }
588
589 /*
590 * Check if we have a reserved prefix set before anything else, as we
591 * otherwise have to test for the prefix in both the cookie name and
592 * "the rest". Prefixes must start with '__' and end with a '-', so
593 * only test for names where that can possibly be true.
594 */
595 if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') {
596 if(strncasecompare("__Secure-", namep, 9))
597 co->prefix |= COOKIE_PREFIX__SECURE;
598 else if(strncasecompare("__Host-", namep, 7))
599 co->prefix |= COOKIE_PREFIX__HOST;
600 }
601
602 /*
603 * Use strstore() below to properly deal with received cookie
604 * headers that have the same string property set more than once,
605 * and then we use the last one.
606 */
607
608 if(!co->name) {
609 /* The very first name/value pair is the actual cookie name */
610 if(!sep) {
611 /* Bad name/value pair. */
612 badcookie = TRUE;
613 break;
614 }
615 strstore(&co->name, namep, nlen);
616 strstore(&co->value, valuep, vlen);
617 done = TRUE;
618 if(!co->name || !co->value) {
619 badcookie = TRUE;
620 break;
621 }
622 if(invalid_octets(co->value) || invalid_octets(co->name)) {
623 infof(data, "invalid octets in name/value, cookie dropped");
624 badcookie = TRUE;
625 break;
626 }
627 }
628 else if(!vlen) {
629 /*
630 * this was a "<name>=" with no content, and we must allow
631 * 'secure' and 'httponly' specified this weirdly
632 */
633 done = TRUE;
634 /*
635 * secure cookies are only allowed to be set when the connection is
636 * using a secure protocol, or when the cookie is being set by
637 * reading from file
638 */
639 if((nlen == 6) && strncasecompare("secure", namep, 6)) {
640 if(secure || !c->running) {
641 co->secure = TRUE;
642 }
643 else {
644 badcookie = TRUE;
645 break;
646 }
647 }
648 else if((nlen == 8) && strncasecompare("httponly", namep, 8))
649 co->httponly = TRUE;
650 else if(sep)
651 /* there was a '=' so we're not done parsing this field */
652 done = FALSE;
653 }
654 if(done)
655 ;
656 else if((nlen == 4) && strncasecompare("path", namep, 4)) {
657 strstore(&co->path, valuep, vlen);
658 if(!co->path) {
659 badcookie = TRUE; /* out of memory bad */
660 break;
661 }
662 free(co->spath); /* if this is set again */
663 co->spath = sanitize_cookie_path(co->path);
664 if(!co->spath) {
665 badcookie = TRUE; /* out of memory bad */
666 break;
667 }
668 }
669 else if((nlen == 6) &&
670 strncasecompare("domain", namep, 6) && vlen) {
671 bool is_ip;
672
673 /*
674 * Now, we make sure that our host is within the given domain, or
675 * the given domain is not valid and thus cannot be set.
676 */
677
678 if('.' == valuep[0]) {
679 valuep++; /* ignore preceding dot */
680 vlen--;
681 }
682
683 #ifndef USE_LIBPSL
684 /*
685 * Without PSL we don't know when the incoming cookie is set on a
686 * TLD or otherwise "protected" suffix. To reduce risk, we require a
687 * dot OR the exact host name being "localhost".
688 */
689 if(bad_domain(valuep, vlen))
690 domain = ":";
691 #endif
692
693 is_ip = Curl_host_is_ipnum(domain ? domain : valuep);
694
695 if(!domain
696 || (is_ip && !strncmp(valuep, domain, vlen) &&
697 (vlen == strlen(domain)))
698 || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) {
699 strstore(&co->domain, valuep, vlen);
700 if(!co->domain) {
701 badcookie = TRUE;
702 break;
703 }
704 if(!is_ip)
705 co->tailmatch = TRUE; /* we always do that if the domain name was
706 given */
707 }
708 else {
709 /*
710 * We did not get a tailmatch and then the attempted set domain is
711 * not a domain to which the current host belongs. Mark as bad.
712 */
713 badcookie = TRUE;
714 infof(data, "skipped cookie with bad tailmatch domain: %s",
715 valuep);
716 }
717 }
718 else if((nlen == 7) && strncasecompare("version", namep, 7)) {
719 /* just ignore */
720 }
721 else if((nlen == 7) && strncasecompare("max-age", namep, 7)) {
722 /*
723 * Defined in RFC2109:
724 *
725 * Optional. The Max-Age attribute defines the lifetime of the
726 * cookie, in seconds. The delta-seconds value is a decimal non-
727 * negative integer. After delta-seconds seconds elapse, the
728 * client should discard the cookie. A value of zero means the
729 * cookie should be discarded immediately.
730 */
731 CURLofft offt;
732 const char *maxage = valuep;
733 offt = curlx_strtoofft((*maxage == '\"')?
734 &maxage[1]:&maxage[0], NULL, 10,
735 &co->expires);
736 switch(offt) {
737 case CURL_OFFT_FLOW:
738 /* overflow, used max value */
739 co->expires = CURL_OFF_T_MAX;
740 break;
741 case CURL_OFFT_INVAL:
742 /* negative or otherwise bad, expire */
743 co->expires = 1;
744 break;
745 case CURL_OFFT_OK:
746 if(!co->expires)
747 /* already expired */
748 co->expires = 1;
749 else if(CURL_OFF_T_MAX - now < co->expires)
750 /* would overflow */
751 co->expires = CURL_OFF_T_MAX;
752 else
753 co->expires += now;
754 break;
755 }
756 }
757 else if((nlen == 7) && strncasecompare("expires", namep, 7)) {
758 char date[128];
759 if(!co->expires && (vlen < sizeof(date))) {
760 /* copy the date so that it can be null terminated */
761 memcpy(date, valuep, vlen);
762 date[vlen] = 0;
763 /*
764 * Let max-age have priority.
765 *
766 * If the date cannot get parsed for whatever reason, the cookie
767 * will be treated as a session cookie
768 */
769 co->expires = Curl_getdate_capped(date);
770
771 /*
772 * Session cookies have expires set to 0 so if we get that back
773 * from the date parser let's add a second to make it a
774 * non-session cookie
775 */
776 if(co->expires == 0)
777 co->expires = 1;
778 else if(co->expires < 0)
779 co->expires = 0;
780 }
781 }
782
783 /*
784 * Else, this is the second (or more) name we don't know about!
785 */
786 }
787 else {
788 /* this is an "illegal" <what>=<this> pair */
789 }
790
791 while(*ptr && ISBLANK(*ptr))
792 ptr++;
793 if(*ptr == ';')
794 ptr++;
795 else
796 break;
797 } while(1);
798
799 if(!badcookie && !co->domain) {
800 if(domain) {
801 /* no domain was given in the header line, set the default */
802 co->domain = strdup(domain);
803 if(!co->domain)
804 badcookie = TRUE;
805 }
806 }
807
808 if(!badcookie && !co->path && path) {
809 /*
810 * No path was given in the header line, set the default. Note that the
811 * passed-in path to this function MAY have a '?' and following part that
812 * MUST NOT be stored as part of the path.
813 */
814 char *queryp = strchr(path, '?');
815
816 /*
817 * queryp is where the interesting part of the path ends, so now we
818 * want to the find the last
819 */
820 char *endslash;
821 if(!queryp)
822 endslash = strrchr(path, '/');
823 else
824 endslash = memrchr(path, '/', (queryp - path));
825 if(endslash) {
826 size_t pathlen = (endslash-path + 1); /* include end slash */
827 co->path = Curl_memdup0(path, pathlen);
828 if(co->path) {
829 co->spath = sanitize_cookie_path(co->path);
830 if(!co->spath)
831 badcookie = TRUE; /* out of memory bad */
832 }
833 else
834 badcookie = TRUE;
835 }
836 }
837
838 /*
839 * If we didn't get a cookie name, or a bad one, the this is an illegal
840 * line so bail out.
841 */
842 if(badcookie || !co->name) {
843 freecookie(co);
844 return NULL;
845 }
846 data->req.setcookies++;
847 }
848 else {
849 /*
850 * This line is NOT an HTTP header style line, we do offer support for
851 * reading the odd netscape cookies-file format here
852 */
853 char *ptr;
854 char *firstptr;
855 char *tok_buf = NULL;
856 int fields;
857
858 /*
859 * IE introduced HTTP-only cookies to prevent XSS attacks. Cookies marked
860 * with httpOnly after the domain name are not accessible from javascripts,
861 * but since curl does not operate at javascript level, we include them
862 * anyway. In Firefox's cookie files, these lines are preceded with
863 * #HttpOnly_ and then everything is as usual, so we skip 10 characters of
864 * the line..
865 */
866 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
867 lineptr += 10;
868 co->httponly = TRUE;
869 }
870
871 if(lineptr[0]=='#') {
872 /* don't even try the comments */
873 free(co);
874 return NULL;
875 }
876 /* strip off the possible end-of-line characters */
877 ptr = strchr(lineptr, '\r');
878 if(ptr)
879 *ptr = 0; /* clear it */
880 ptr = strchr(lineptr, '\n');
881 if(ptr)
882 *ptr = 0; /* clear it */
883
884 firstptr = strtok_r((char *)lineptr, "\t", &tok_buf); /* tokenize on TAB */
885
886 /*
887 * Now loop through the fields and init the struct we already have
888 * allocated
889 */
890 fields = 0;
891 for(ptr = firstptr; 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 infof(data, "libpsl problem, rejecting cookie for satety");
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 FILE *handle = NULL;
1213
1214 if(!inc) {
1215 /* we didn't get a struct, create one */
1216 c = calloc(1, sizeof(struct CookieInfo));
1217 if(!c)
1218 return NULL; /* failed to get memory */
1219 /*
1220 * Initialize the next_expiration time to signal that we don't have enough
1221 * information yet.
1222 */
1223 c->next_expiration = CURL_OFF_T_MAX;
1224 }
1225 else {
1226 /* we got an already existing one, use that */
1227 c = inc;
1228 }
1229 c->newsession = newsession; /* new session? */
1230
1231 if(data) {
1232 FILE *fp = NULL;
1233 if(file && *file) {
1234 if(!strcmp(file, "-"))
1235 fp = stdin;
1236 else {
1237 fp = fopen(file, "rb");
1238 if(!fp)
1239 infof(data, "WARNING: failed to open cookie file \"%s\"", file);
1240 else
1241 handle = fp;
1242 }
1243 }
1244
1245 c->running = FALSE; /* this is not running, this is init */
1246 if(fp) {
1247 struct dynbuf buf;
1248 Curl_dyn_init(&buf, MAX_COOKIE_LINE);
1249 while(Curl_get_line(&buf, fp)) {
1250 char *lineptr = Curl_dyn_ptr(&buf);
1251 bool headerline = FALSE;
1252 if(checkprefix("Set-Cookie:", lineptr)) {
1253 /* This is a cookie line, get it! */
1254 lineptr += 11;
1255 headerline = TRUE;
1256 while(*lineptr && ISBLANK(*lineptr))
1257 lineptr++;
1258 }
1259
1260 Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE);
1261 }
1262 Curl_dyn_free(&buf); /* free the line buffer */
1263
1264 /*
1265 * Remove expired cookies from the hash. We must make sure to run this
1266 * after reading the file, and not on every cookie.
1267 */
1268 remove_expired(c);
1269
1270 if(handle)
1271 fclose(handle);
1272 }
1273 data->state.cookie_engine = TRUE;
1274 }
1275 c->running = TRUE; /* now, we're running */
1276
1277 return c;
1278 }
1279
1280 /*
1281 * cookie_sort
1282 *
1283 * Helper function to sort cookies such that the longest path gets before the
1284 * shorter path. Path, domain and name lengths are considered in that order,
1285 * with the creationtime as the tiebreaker. The creationtime is guaranteed to
1286 * be unique per cookie, so we know we will get an ordering at that point.
1287 */
cookie_sort(const void * p1,const void * p2)1288 static int cookie_sort(const void *p1, const void *p2)
1289 {
1290 struct Cookie *c1 = *(struct Cookie **)p1;
1291 struct Cookie *c2 = *(struct Cookie **)p2;
1292 size_t l1, l2;
1293
1294 /* 1 - compare cookie path lengths */
1295 l1 = c1->path ? strlen(c1->path) : 0;
1296 l2 = c2->path ? strlen(c2->path) : 0;
1297
1298 if(l1 != l2)
1299 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
1300
1301 /* 2 - compare cookie domain lengths */
1302 l1 = c1->domain ? strlen(c1->domain) : 0;
1303 l2 = c2->domain ? strlen(c2->domain) : 0;
1304
1305 if(l1 != l2)
1306 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
1307
1308 /* 3 - compare cookie name lengths */
1309 l1 = c1->name ? strlen(c1->name) : 0;
1310 l2 = c2->name ? strlen(c2->name) : 0;
1311
1312 if(l1 != l2)
1313 return (l2 > l1) ? 1 : -1;
1314
1315 /* 4 - compare cookie creation time */
1316 return (c2->creationtime > c1->creationtime) ? 1 : -1;
1317 }
1318
1319 /*
1320 * cookie_sort_ct
1321 *
1322 * Helper function to sort cookies according to creation time.
1323 */
cookie_sort_ct(const void * p1,const void * p2)1324 static int cookie_sort_ct(const void *p1, const void *p2)
1325 {
1326 struct Cookie *c1 = *(struct Cookie **)p1;
1327 struct Cookie *c2 = *(struct Cookie **)p2;
1328
1329 return (c2->creationtime > c1->creationtime) ? 1 : -1;
1330 }
1331
1332 #define CLONE(field) \
1333 do { \
1334 if(src->field) { \
1335 d->field = strdup(src->field); \
1336 if(!d->field) \
1337 goto fail; \
1338 } \
1339 } while(0)
1340
dup_cookie(struct Cookie * src)1341 static struct Cookie *dup_cookie(struct Cookie *src)
1342 {
1343 struct Cookie *d = calloc(1, sizeof(struct Cookie));
1344 if(d) {
1345 CLONE(domain);
1346 CLONE(path);
1347 CLONE(spath);
1348 CLONE(name);
1349 CLONE(value);
1350 d->expires = src->expires;
1351 d->tailmatch = src->tailmatch;
1352 d->secure = src->secure;
1353 d->livecookie = src->livecookie;
1354 d->httponly = src->httponly;
1355 d->creationtime = src->creationtime;
1356 }
1357 return d;
1358
1359 fail:
1360 freecookie(d);
1361 return NULL;
1362 }
1363
1364 /*
1365 * Curl_cookie_getlist
1366 *
1367 * For a given host and path, return a linked list of cookies that the client
1368 * should send to the server if used now. The secure boolean informs the cookie
1369 * if a secure connection is achieved or not.
1370 *
1371 * It shall only return cookies that haven't expired.
1372 */
Curl_cookie_getlist(struct Curl_easy * data,struct CookieInfo * c,const char * host,const char * path,bool secure)1373 struct Cookie *Curl_cookie_getlist(struct Curl_easy *data,
1374 struct CookieInfo *c,
1375 const char *host, const char *path,
1376 bool secure)
1377 {
1378 struct Cookie *newco;
1379 struct Cookie *co;
1380 struct Cookie *mainco = NULL;
1381 size_t matches = 0;
1382 bool is_ip;
1383 const size_t myhash = cookiehash(host);
1384
1385 if(!c || !c->cookies[myhash])
1386 return NULL; /* no cookie struct or no cookies in the struct */
1387
1388 /* at first, remove expired cookies */
1389 remove_expired(c);
1390
1391 /* check if host is an IP(v4|v6) address */
1392 is_ip = Curl_host_is_ipnum(host);
1393
1394 co = c->cookies[myhash];
1395
1396 while(co) {
1397 /* if the cookie requires we're secure we must only continue if we are! */
1398 if(co->secure?secure:TRUE) {
1399
1400 /* now check if the domain is correct */
1401 if(!co->domain ||
1402 (co->tailmatch && !is_ip &&
1403 cookie_tailmatch(co->domain, strlen(co->domain), host)) ||
1404 ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
1405 /*
1406 * the right part of the host matches the domain stuff in the
1407 * cookie data
1408 */
1409
1410 /*
1411 * now check the left part of the path with the cookies path
1412 * requirement
1413 */
1414 if(!co->spath || pathmatch(co->spath, path) ) {
1415
1416 /*
1417 * and now, we know this is a match and we should create an
1418 * entry for the return-linked-list
1419 */
1420
1421 newco = dup_cookie(co);
1422 if(newco) {
1423 /* then modify our next */
1424 newco->next = mainco;
1425
1426 /* point the main to us */
1427 mainco = newco;
1428
1429 matches++;
1430 if(matches >= MAX_COOKIE_SEND_AMOUNT) {
1431 infof(data, "Included max number of cookies (%zu) in request!",
1432 matches);
1433 break;
1434 }
1435 }
1436 else
1437 goto fail;
1438 }
1439 }
1440 }
1441 co = co->next;
1442 }
1443
1444 if(matches) {
1445 /*
1446 * Now we need to make sure that if there is a name appearing more than
1447 * once, the longest specified path version comes first. To make this
1448 * the swiftest way, we just sort them all based on path length.
1449 */
1450 struct Cookie **array;
1451 size_t i;
1452
1453 /* alloc an array and store all cookie pointers */
1454 array = malloc(sizeof(struct Cookie *) * matches);
1455 if(!array)
1456 goto fail;
1457
1458 co = mainco;
1459
1460 for(i = 0; co; co = co->next)
1461 array[i++] = co;
1462
1463 /* now sort the cookie pointers in path length order */
1464 qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
1465
1466 /* remake the linked list order according to the new order */
1467
1468 mainco = array[0]; /* start here */
1469 for(i = 0; i<matches-1; i++)
1470 array[i]->next = array[i + 1];
1471 array[matches-1]->next = NULL; /* terminate the list */
1472
1473 free(array); /* remove the temporary data again */
1474 }
1475
1476 return mainco; /* return the new list */
1477
1478 fail:
1479 /* failure, clear up the allocated chain and return NULL */
1480 Curl_cookie_freelist(mainco);
1481 return NULL;
1482 }
1483
1484 /*
1485 * Curl_cookie_clearall
1486 *
1487 * Clear all existing cookies and reset the counter.
1488 */
Curl_cookie_clearall(struct CookieInfo * cookies)1489 void Curl_cookie_clearall(struct CookieInfo *cookies)
1490 {
1491 if(cookies) {
1492 unsigned int i;
1493 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
1494 Curl_cookie_freelist(cookies->cookies[i]);
1495 cookies->cookies[i] = NULL;
1496 }
1497 cookies->numcookies = 0;
1498 }
1499 }
1500
1501 /*
1502 * Curl_cookie_freelist
1503 *
1504 * Free a list of cookies previously returned by Curl_cookie_getlist();
1505 */
Curl_cookie_freelist(struct Cookie * co)1506 void Curl_cookie_freelist(struct Cookie *co)
1507 {
1508 struct Cookie *next;
1509 while(co) {
1510 next = co->next;
1511 freecookie(co);
1512 co = next;
1513 }
1514 }
1515
1516 /*
1517 * Curl_cookie_clearsess
1518 *
1519 * Free all session cookies in the cookies list.
1520 */
Curl_cookie_clearsess(struct CookieInfo * cookies)1521 void Curl_cookie_clearsess(struct CookieInfo *cookies)
1522 {
1523 struct Cookie *first, *curr, *next, *prev = NULL;
1524 unsigned int i;
1525
1526 if(!cookies)
1527 return;
1528
1529 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
1530 if(!cookies->cookies[i])
1531 continue;
1532
1533 first = curr = prev = cookies->cookies[i];
1534
1535 for(; curr; curr = next) {
1536 next = curr->next;
1537 if(!curr->expires) {
1538 if(first == curr)
1539 first = next;
1540
1541 if(prev == curr)
1542 prev = next;
1543 else
1544 prev->next = next;
1545
1546 freecookie(curr);
1547 cookies->numcookies--;
1548 }
1549 else
1550 prev = curr;
1551 }
1552
1553 cookies->cookies[i] = first;
1554 }
1555 }
1556
1557 /*
1558 * Curl_cookie_cleanup()
1559 *
1560 * Free a "cookie object" previous created with Curl_cookie_init().
1561 */
Curl_cookie_cleanup(struct CookieInfo * c)1562 void Curl_cookie_cleanup(struct CookieInfo *c)
1563 {
1564 if(c) {
1565 unsigned int i;
1566 for(i = 0; i < COOKIE_HASH_SIZE; i++)
1567 Curl_cookie_freelist(c->cookies[i]);
1568 free(c); /* free the base struct as well */
1569 }
1570 }
1571
1572 /*
1573 * get_netscape_format()
1574 *
1575 * Formats a string for Netscape output file, w/o a newline at the end.
1576 * Function returns a char * to a formatted line. The caller is responsible
1577 * for freeing the returned pointer.
1578 */
get_netscape_format(const struct Cookie * co)1579 static char *get_netscape_format(const struct Cookie *co)
1580 {
1581 return aprintf(
1582 "%s" /* httponly preamble */
1583 "%s%s\t" /* domain */
1584 "%s\t" /* tailmatch */
1585 "%s\t" /* path */
1586 "%s\t" /* secure */
1587 "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */
1588 "%s\t" /* name */
1589 "%s", /* value */
1590 co->httponly?"#HttpOnly_":"",
1591 /*
1592 * Make sure all domains are prefixed with a dot if they allow
1593 * tailmatching. This is Mozilla-style.
1594 */
1595 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
1596 co->domain?co->domain:"unknown",
1597 co->tailmatch?"TRUE":"FALSE",
1598 co->path?co->path:"/",
1599 co->secure?"TRUE":"FALSE",
1600 co->expires,
1601 co->name,
1602 co->value?co->value:"");
1603 }
1604
1605 /*
1606 * cookie_output()
1607 *
1608 * Writes all internally known cookies to the specified file. Specify
1609 * "-" as file name to write to stdout.
1610 *
1611 * The function returns non-zero on write failure.
1612 */
cookie_output(struct Curl_easy * data,struct CookieInfo * c,const char * filename)1613 static CURLcode cookie_output(struct Curl_easy *data,
1614 struct CookieInfo *c, const char *filename)
1615 {
1616 struct Cookie *co;
1617 FILE *out = NULL;
1618 bool use_stdout = FALSE;
1619 char *tempstore = NULL;
1620 CURLcode error = CURLE_OK;
1621
1622 if(!c)
1623 /* no cookie engine alive */
1624 return CURLE_OK;
1625
1626 /* at first, remove expired cookies */
1627 remove_expired(c);
1628
1629 if(!strcmp("-", filename)) {
1630 /* use stdout */
1631 out = stdout;
1632 use_stdout = TRUE;
1633 }
1634 else {
1635 error = Curl_fopen(data, filename, &out, &tempstore);
1636 if(error)
1637 goto error;
1638 }
1639
1640 fputs("# Netscape HTTP Cookie File\n"
1641 "# https://curl.se/docs/http-cookies.html\n"
1642 "# This file was generated by libcurl! Edit at your own risk.\n\n",
1643 out);
1644
1645 if(c->numcookies) {
1646 unsigned int i;
1647 size_t nvalid = 0;
1648 struct Cookie **array;
1649
1650 array = calloc(1, sizeof(struct Cookie *) * c->numcookies);
1651 if(!array) {
1652 error = CURLE_OUT_OF_MEMORY;
1653 goto error;
1654 }
1655
1656 /* only sort the cookies with a domain property */
1657 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
1658 for(co = c->cookies[i]; co; co = co->next) {
1659 if(!co->domain)
1660 continue;
1661 array[nvalid++] = co;
1662 }
1663 }
1664
1665 qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct);
1666
1667 for(i = 0; i < nvalid; i++) {
1668 char *format_ptr = get_netscape_format(array[i]);
1669 if(!format_ptr) {
1670 free(array);
1671 error = CURLE_OUT_OF_MEMORY;
1672 goto error;
1673 }
1674 fprintf(out, "%s\n", format_ptr);
1675 free(format_ptr);
1676 }
1677
1678 free(array);
1679 }
1680
1681 if(!use_stdout) {
1682 fclose(out);
1683 out = NULL;
1684 if(tempstore && Curl_rename(tempstore, filename)) {
1685 unlink(tempstore);
1686 error = CURLE_WRITE_ERROR;
1687 goto error;
1688 }
1689 }
1690
1691 /*
1692 * If we reach here we have successfully written a cookie file so there is
1693 * no need to inspect the error, any error case should have jumped into the
1694 * error block below.
1695 */
1696 free(tempstore);
1697 return CURLE_OK;
1698
1699 error:
1700 if(out && !use_stdout)
1701 fclose(out);
1702 free(tempstore);
1703 return error;
1704 }
1705
cookie_list(struct Curl_easy * data)1706 static struct curl_slist *cookie_list(struct Curl_easy *data)
1707 {
1708 struct curl_slist *list = NULL;
1709 struct curl_slist *beg;
1710 struct Cookie *c;
1711 char *line;
1712 unsigned int i;
1713
1714 if(!data->cookies || (data->cookies->numcookies == 0))
1715 return NULL;
1716
1717 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
1718 for(c = data->cookies->cookies[i]; c; c = c->next) {
1719 if(!c->domain)
1720 continue;
1721 line = get_netscape_format(c);
1722 if(!line) {
1723 curl_slist_free_all(list);
1724 return NULL;
1725 }
1726 beg = Curl_slist_append_nodup(list, line);
1727 if(!beg) {
1728 free(line);
1729 curl_slist_free_all(list);
1730 return NULL;
1731 }
1732 list = beg;
1733 }
1734 }
1735
1736 return list;
1737 }
1738
Curl_cookie_list(struct Curl_easy * data)1739 struct curl_slist *Curl_cookie_list(struct Curl_easy *data)
1740 {
1741 struct curl_slist *list;
1742 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1743 list = cookie_list(data);
1744 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1745 return list;
1746 }
1747
Curl_flush_cookies(struct Curl_easy * data,bool cleanup)1748 void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
1749 {
1750 CURLcode res;
1751
1752 if(data->set.str[STRING_COOKIEJAR]) {
1753 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1754
1755 /* if we have a destination file for all the cookies to get dumped to */
1756 res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]);
1757 if(res)
1758 infof(data, "WARNING: failed to save cookies in %s: %s",
1759 data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res));
1760 }
1761 else {
1762 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1763 }
1764
1765 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
1766 Curl_cookie_cleanup(data->cookies);
1767 data->cookies = NULL;
1768 }
1769 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1770 }
1771
1772 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */
1773