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