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