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