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