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