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