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