• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2016, 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.haxx.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 superceeds.
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 #include "curl_setup.h"
83 
84 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
85 
86 #ifdef USE_LIBPSL
87 # include <libpsl.h>
88 #endif
89 
90 #include "urldata.h"
91 #include "cookie.h"
92 #include "strtok.h"
93 #include "sendf.h"
94 #include "slist.h"
95 #include "share.h"
96 #include "strtoofft.h"
97 #include "strcase.h"
98 #include "curl_memrchr.h"
99 #include "inet_pton.h"
100 
101 /* The last 3 #include files should be in this order */
102 #include "curl_printf.h"
103 #include "curl_memory.h"
104 #include "memdebug.h"
105 
freecookie(struct Cookie * co)106 static void freecookie(struct Cookie *co)
107 {
108   free(co->expirestr);
109   free(co->domain);
110   free(co->path);
111   free(co->spath);
112   free(co->name);
113   free(co->value);
114   free(co->maxage);
115   free(co->version);
116   free(co);
117 }
118 
tailmatch(const char * cooke_domain,const char * hostname)119 static bool tailmatch(const char *cooke_domain, const char *hostname)
120 {
121   size_t cookie_domain_len = strlen(cooke_domain);
122   size_t hostname_len = strlen(hostname);
123 
124   if(hostname_len < cookie_domain_len)
125     return FALSE;
126 
127   if(!strcasecompare(cooke_domain, hostname+hostname_len-cookie_domain_len))
128     return FALSE;
129 
130   /* A lead char of cookie_domain is not '.'.
131      RFC6265 4.1.2.3. The Domain Attribute says:
132        For example, if the value of the Domain attribute is
133        "example.com", the user agent will include the cookie in the Cookie
134        header when making HTTP requests to example.com, www.example.com, and
135        www.corp.example.com.
136    */
137   if(hostname_len == cookie_domain_len)
138     return TRUE;
139   if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
140     return TRUE;
141   return FALSE;
142 }
143 
144 /*
145  * matching cookie path and url path
146  * RFC6265 5.1.4 Paths and Path-Match
147  */
pathmatch(const char * cookie_path,const char * request_uri)148 static bool pathmatch(const char *cookie_path, const char *request_uri)
149 {
150   size_t cookie_path_len;
151   size_t uri_path_len;
152   char *uri_path = NULL;
153   char *pos;
154   bool ret = FALSE;
155 
156   /* cookie_path must not have last '/' separator. ex: /sample */
157   cookie_path_len = strlen(cookie_path);
158   if(1 == cookie_path_len) {
159     /* cookie_path must be '/' */
160     return TRUE;
161   }
162 
163   uri_path = strdup(request_uri);
164   if(!uri_path)
165     return FALSE;
166   pos = strchr(uri_path, '?');
167   if(pos)
168     *pos = 0x0;
169 
170   /* #-fragments are already cut off! */
171   if(0 == strlen(uri_path) || uri_path[0] != '/') {
172     free(uri_path);
173     uri_path = strdup("/");
174     if(!uri_path)
175       return FALSE;
176   }
177 
178   /* here, RFC6265 5.1.4 says
179      4. Output the characters of the uri-path from the first character up
180         to, but not including, the right-most %x2F ("/").
181      but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
182      without redirect.
183      Ignore this algorithm because /hoge is uri path for this case
184      (uri path is not /).
185    */
186 
187   uri_path_len = strlen(uri_path);
188 
189   if(uri_path_len < cookie_path_len) {
190     ret = FALSE;
191     goto pathmatched;
192   }
193 
194   /* not using checkprefix() because matching should be case-sensitive */
195   if(strncmp(cookie_path, uri_path, cookie_path_len)) {
196     ret = FALSE;
197     goto pathmatched;
198   }
199 
200   /* The cookie-path and the uri-path are identical. */
201   if(cookie_path_len == uri_path_len) {
202     ret = TRUE;
203     goto pathmatched;
204   }
205 
206   /* here, cookie_path_len < url_path_len */
207   if(uri_path[cookie_path_len] == '/') {
208     ret = TRUE;
209     goto pathmatched;
210   }
211 
212   ret = FALSE;
213 
214 pathmatched:
215   free(uri_path);
216   return ret;
217 }
218 
219 /*
220  * cookie path sanitize
221  */
sanitize_cookie_path(const char * cookie_path)222 static char *sanitize_cookie_path(const char *cookie_path)
223 {
224   size_t len;
225   char *new_path = strdup(cookie_path);
226   if(!new_path)
227     return NULL;
228 
229   /* some stupid site sends path attribute with '"'. */
230   len = strlen(new_path);
231   if(new_path[0] == '\"') {
232     memmove((void *)new_path, (const void *)(new_path + 1), len);
233     len--;
234   }
235   if(len && (new_path[len - 1] == '\"')) {
236     new_path[len - 1] = 0x0;
237     len--;
238   }
239 
240   /* RFC6265 5.2.4 The Path Attribute */
241   if(new_path[0] != '/') {
242     /* Let cookie-path be the default-path. */
243     free(new_path);
244     new_path = strdup("/");
245     return new_path;
246   }
247 
248   /* convert /hoge/ to /hoge */
249   if(len && new_path[len - 1] == '/') {
250     new_path[len - 1] = 0x0;
251   }
252 
253   return new_path;
254 }
255 
256 /*
257  * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
258  *
259  * NOTE: OOM or cookie parsing failures are ignored.
260  */
Curl_cookie_loadfiles(struct Curl_easy * data)261 void Curl_cookie_loadfiles(struct Curl_easy *data)
262 {
263   struct curl_slist *list = data->change.cookielist;
264   if(list) {
265     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
266     while(list) {
267       struct CookieInfo *newcookies = Curl_cookie_init(data,
268                                         list->data,
269                                         data->cookies,
270                                         data->set.cookiesession);
271       if(!newcookies)
272         /* Failure may be due to OOM or a bad cookie; both are ignored
273          * but only the first should be
274          */
275         infof(data, "ignoring failed cookie_init for %s\n", list->data);
276       else
277         data->cookies = newcookies;
278       list = list->next;
279     }
280     curl_slist_free_all(data->change.cookielist); /* clean up list */
281     data->change.cookielist = NULL; /* don't do this again! */
282     Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
283   }
284 }
285 
286 /*
287  * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
288  * that will be freed before the allocated string is stored there.
289  *
290  * It is meant to easily replace strdup()
291  */
strstore(char ** str,const char * newstr)292 static void strstore(char **str, const char *newstr)
293 {
294   free(*str);
295   *str = strdup(newstr);
296 }
297 
298 /*
299  * remove_expired() removes expired cookies.
300  */
remove_expired(struct CookieInfo * cookies)301 static void remove_expired(struct CookieInfo *cookies)
302 {
303   struct Cookie *co, *nx, *pv;
304   curl_off_t now = (curl_off_t)time(NULL);
305 
306   co = cookies->cookies;
307   pv = NULL;
308   while(co) {
309     nx = co->next;
310     if(co->expires && co->expires < now) {
311       if(co == cookies->cookies) {
312         cookies->cookies = co->next;
313       }
314       else {
315         pv->next = co->next;
316       }
317       cookies->numcookies--;
318       freecookie(co);
319     }
320     else {
321       pv = co;
322     }
323     co = nx;
324   }
325 }
326 
327 /*
328  * Return true if the given string is an IP(v4|v6) address.
329  */
isip(const char * domain)330 static bool isip(const char *domain)
331 {
332   struct in_addr addr;
333 #ifdef ENABLE_IPV6
334   struct in6_addr addr6;
335 #endif
336 
337   if(Curl_inet_pton(AF_INET, domain, &addr)
338 #ifdef ENABLE_IPV6
339      || Curl_inet_pton(AF_INET6, domain, &addr6)
340 #endif
341     ) {
342     /* domain name given as IP address */
343     return TRUE;
344   }
345 
346   return FALSE;
347 }
348 
349 /****************************************************************************
350  *
351  * Curl_cookie_add()
352  *
353  * Add a single cookie line to the cookie keeping object.
354  *
355  * Be aware that sometimes we get an IP-only host name, and that might also be
356  * a numerical IPv6 address.
357  *
358  * Returns NULL on out of memory or invalid cookie. This is suboptimal,
359  * as they should be treated separately.
360  ***************************************************************************/
361 
362 struct Cookie *
Curl_cookie_add(struct Curl_easy * data,struct CookieInfo * c,bool httpheader,char * lineptr,const char * domain,const char * path)363 Curl_cookie_add(struct Curl_easy *data,
364                 /* The 'data' pointer here may be NULL at times, and thus
365                    must only be used very carefully for things that can deal
366                    with data being NULL. Such as infof() and similar */
367 
368                 struct CookieInfo *c,
369                 bool httpheader, /* TRUE if HTTP header-style line */
370                 char *lineptr,   /* first character of the line */
371                 const char *domain, /* default domain */
372                 const char *path)   /* full path used when this cookie is set,
373                                        used to get default path for the cookie
374                                        unless set */
375 {
376   struct Cookie *clist;
377   char name[MAX_NAME];
378   struct Cookie *co;
379   struct Cookie *lastc=NULL;
380   time_t now = time(NULL);
381   bool replace_old = FALSE;
382   bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
383 
384 #ifdef USE_LIBPSL
385   const psl_ctx_t *psl;
386 #endif
387 
388 #ifdef CURL_DISABLE_VERBOSE_STRINGS
389   (void)data;
390 #endif
391 
392   /* First, alloc and init a new struct for it */
393   co = calloc(1, sizeof(struct Cookie));
394   if(!co)
395     return NULL; /* bail out if we're this low on memory */
396 
397   if(httpheader) {
398     /* This line was read off a HTTP-header */
399     const char *ptr;
400     const char *semiptr;
401     char *what;
402 
403     what = malloc(MAX_COOKIE_LINE);
404     if(!what) {
405       free(co);
406       return NULL;
407     }
408 
409     semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
410 
411     while(*lineptr && ISBLANK(*lineptr))
412       lineptr++;
413 
414     ptr = lineptr;
415     do {
416       /* we have a <what>=<this> pair or a stand-alone word here */
417       name[0]=what[0]=0; /* init the buffers */
418       if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n=] =%"
419                      MAX_COOKIE_LINE_TXT "[^;\r\n]",
420                      name, what)) {
421         /* Use strstore() below to properly deal with received cookie
422            headers that have the same string property set more than once,
423            and then we use the last one. */
424         const char *whatptr;
425         bool done = FALSE;
426         bool sep;
427         size_t len=strlen(what);
428         size_t nlen = strlen(name);
429         const char *endofn = &ptr[ nlen ];
430 
431         /* name ends with a '=' ? */
432         sep = (*endofn == '=')?TRUE:FALSE;
433 
434         if(nlen) {
435           endofn--; /* move to the last character */
436           if(ISBLANK(*endofn)) {
437             /* skip trailing spaces in name */
438             while(*endofn && ISBLANK(*endofn) && nlen) {
439               endofn--;
440               nlen--;
441             }
442             name[nlen]=0; /* new end of name */
443           }
444         }
445 
446         /* Strip off trailing whitespace from the 'what' */
447         while(len && ISBLANK(what[len-1])) {
448           what[len-1]=0;
449           len--;
450         }
451 
452         /* Skip leading whitespace from the 'what' */
453         whatptr=what;
454         while(*whatptr && ISBLANK(*whatptr))
455           whatptr++;
456 
457         if(!co->name && sep) {
458           /* The very first name/value pair is the actual cookie name */
459           co->name = strdup(name);
460           co->value = strdup(whatptr);
461           if(!co->name || !co->value) {
462             badcookie = TRUE;
463             break;
464           }
465         }
466         else if(!len) {
467           /* this was a "<name>=" with no content, and we must allow
468              'secure' and 'httponly' specified this weirdly */
469           done = TRUE;
470           if(strcasecompare("secure", name))
471             co->secure = TRUE;
472           else if(strcasecompare("httponly", name))
473             co->httponly = TRUE;
474           else if(sep)
475             /* there was a '=' so we're not done parsing this field */
476             done = FALSE;
477         }
478         if(done)
479           ;
480         else if(strcasecompare("path", name)) {
481           strstore(&co->path, whatptr);
482           if(!co->path) {
483             badcookie = TRUE; /* out of memory bad */
484             break;
485           }
486           co->spath = sanitize_cookie_path(co->path);
487           if(!co->spath) {
488             badcookie = TRUE; /* out of memory bad */
489             break;
490           }
491         }
492         else if(strcasecompare("domain", name)) {
493           bool is_ip;
494           const char *dotp;
495 
496           /* Now, we make sure that our host is within the given domain,
497              or the given domain is not valid and thus cannot be set. */
498 
499           if('.' == whatptr[0])
500             whatptr++; /* ignore preceding dot */
501 
502           is_ip = isip(domain ? domain : whatptr);
503 
504           /* check for more dots */
505           dotp = strchr(whatptr, '.');
506           if(!dotp)
507             domain=":";
508 
509           if(!domain
510              || (is_ip && !strcmp(whatptr, domain))
511              || (!is_ip && tailmatch(whatptr, domain))) {
512             strstore(&co->domain, whatptr);
513             if(!co->domain) {
514               badcookie = TRUE;
515               break;
516             }
517             if(!is_ip)
518               co->tailmatch=TRUE; /* we always do that if the domain name was
519                                      given */
520           }
521           else {
522             /* we did not get a tailmatch and then the attempted set domain
523                is not a domain to which the current host belongs. Mark as
524                bad. */
525             badcookie=TRUE;
526             infof(data, "skipped cookie with bad tailmatch domain: %s\n",
527                   whatptr);
528           }
529         }
530         else if(strcasecompare("version", name)) {
531           strstore(&co->version, whatptr);
532           if(!co->version) {
533             badcookie = TRUE;
534             break;
535           }
536         }
537         else if(strcasecompare("max-age", name)) {
538           /* Defined in RFC2109:
539 
540              Optional.  The Max-Age attribute defines the lifetime of the
541              cookie, in seconds.  The delta-seconds value is a decimal non-
542              negative integer.  After delta-seconds seconds elapse, the
543              client should discard the cookie.  A value of zero means the
544              cookie should be discarded immediately.
545 
546           */
547           strstore(&co->maxage, whatptr);
548           if(!co->maxage) {
549             badcookie = TRUE;
550             break;
551           }
552         }
553         else if(strcasecompare("expires", name)) {
554           strstore(&co->expirestr, whatptr);
555           if(!co->expirestr) {
556             badcookie = TRUE;
557             break;
558           }
559         }
560         /*
561           else this is the second (or more) name we don't know
562           about! */
563       }
564       else {
565         /* this is an "illegal" <what>=<this> pair */
566       }
567 
568       if(!semiptr || !*semiptr) {
569         /* we already know there are no more cookies */
570         semiptr = NULL;
571         continue;
572       }
573 
574       ptr=semiptr+1;
575       while(*ptr && ISBLANK(*ptr))
576         ptr++;
577       semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
578 
579       if(!semiptr && *ptr)
580         /* There are no more semicolons, but there's a final name=value pair
581            coming up */
582         semiptr=strchr(ptr, '\0');
583     } while(semiptr);
584 
585     if(co->maxage) {
586       co->expires =
587         curlx_strtoofft((*co->maxage=='\"')?
588                         &co->maxage[1]:&co->maxage[0], NULL, 10);
589       if(CURL_OFF_T_MAX - now < co->expires)
590         /* avoid overflow */
591         co->expires = CURL_OFF_T_MAX;
592       else
593         co->expires += now;
594     }
595     else if(co->expirestr) {
596       /* Note that if the date couldn't get parsed for whatever reason,
597          the cookie will be treated as a session cookie */
598       co->expires = curl_getdate(co->expirestr, NULL);
599 
600       /* Session cookies have expires set to 0 so if we get that back
601          from the date parser let's add a second to make it a
602          non-session cookie */
603       if(co->expires == 0)
604         co->expires = 1;
605       else if(co->expires < 0)
606         co->expires = 0;
607     }
608 
609     if(!badcookie && !co->domain) {
610       if(domain) {
611         /* no domain was given in the header line, set the default */
612         co->domain=strdup(domain);
613         if(!co->domain)
614           badcookie = TRUE;
615       }
616     }
617 
618     if(!badcookie && !co->path && path) {
619       /* No path was given in the header line, set the default.
620          Note that the passed-in path to this function MAY have a '?' and
621          following part that MUST not be stored as part of the path. */
622       char *queryp = strchr(path, '?');
623 
624       /* queryp is where the interesting part of the path ends, so now we
625          want to the find the last */
626       char *endslash;
627       if(!queryp)
628         endslash = strrchr(path, '/');
629       else
630         endslash = memrchr(path, '/', (size_t)(queryp - path));
631       if(endslash) {
632         size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */
633         co->path=malloc(pathlen+1); /* one extra for the zero byte */
634         if(co->path) {
635           memcpy(co->path, path, pathlen);
636           co->path[pathlen]=0; /* zero terminate */
637           co->spath = sanitize_cookie_path(co->path);
638           if(!co->spath)
639             badcookie = TRUE; /* out of memory bad */
640         }
641         else
642           badcookie = TRUE;
643       }
644     }
645 
646     free(what);
647 
648     if(badcookie || !co->name) {
649       /* we didn't get a cookie name or a bad one,
650          this is an illegal line, bail out */
651       freecookie(co);
652       return NULL;
653     }
654 
655   }
656   else {
657     /* This line is NOT a HTTP header style line, we do offer support for
658        reading the odd netscape cookies-file format here */
659     char *ptr;
660     char *firstptr;
661     char *tok_buf=NULL;
662     int fields;
663 
664     /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
665        marked with httpOnly after the domain name are not accessible
666        from javascripts, but since curl does not operate at javascript
667        level, we include them anyway. In Firefox's cookie files, these
668        lines are preceded with #HttpOnly_ and then everything is
669        as usual, so we skip 10 characters of the line..
670     */
671     if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
672       lineptr += 10;
673       co->httponly = TRUE;
674     }
675 
676     if(lineptr[0]=='#') {
677       /* don't even try the comments */
678       free(co);
679       return NULL;
680     }
681     /* strip off the possible end-of-line characters */
682     ptr=strchr(lineptr, '\r');
683     if(ptr)
684       *ptr=0; /* clear it */
685     ptr=strchr(lineptr, '\n');
686     if(ptr)
687       *ptr=0; /* clear it */
688 
689     firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
690 
691     /* Now loop through the fields and init the struct we already have
692        allocated */
693     for(ptr=firstptr, fields=0; ptr && !badcookie;
694         ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
695       switch(fields) {
696       case 0:
697         if(ptr[0]=='.') /* skip preceding dots */
698           ptr++;
699         co->domain = strdup(ptr);
700         if(!co->domain)
701           badcookie = TRUE;
702         break;
703       case 1:
704         /* This field got its explanation on the 23rd of May 2001 by
705            Andr�s Garc�a:
706 
707            flag: A TRUE/FALSE value indicating if all machines within a given
708            domain can access the variable. This value is set automatically by
709            the browser, depending on the value you set for the domain.
710 
711            As far as I can see, it is set to true when the cookie says
712            .domain.com and to false when the domain is complete www.domain.com
713         */
714         co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
715         break;
716       case 2:
717         /* It turns out, that sometimes the file format allows the path
718            field to remain not filled in, we try to detect this and work
719            around it! Andr�s Garc�a made us aware of this... */
720         if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
721           /* only if the path doesn't look like a boolean option! */
722           co->path = strdup(ptr);
723           if(!co->path)
724             badcookie = TRUE;
725           else {
726             co->spath = sanitize_cookie_path(co->path);
727             if(!co->spath) {
728               badcookie = TRUE; /* out of memory bad */
729             }
730           }
731           break;
732         }
733         /* this doesn't look like a path, make one up! */
734         co->path = strdup("/");
735         if(!co->path)
736           badcookie = TRUE;
737         co->spath = strdup("/");
738         if(!co->spath)
739           badcookie = TRUE;
740         fields++; /* add a field and fall down to secure */
741         /* FALLTHROUGH */
742       case 3:
743         co->secure = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
744         break;
745       case 4:
746         co->expires = curlx_strtoofft(ptr, NULL, 10);
747         break;
748       case 5:
749         co->name = strdup(ptr);
750         if(!co->name)
751           badcookie = TRUE;
752         break;
753       case 6:
754         co->value = strdup(ptr);
755         if(!co->value)
756           badcookie = TRUE;
757         break;
758       }
759     }
760     if(6 == fields) {
761       /* we got a cookie with blank contents, fix it */
762       co->value = strdup("");
763       if(!co->value)
764         badcookie = TRUE;
765       else
766         fields++;
767     }
768 
769     if(!badcookie && (7 != fields))
770       /* we did not find the sufficient number of fields */
771       badcookie = TRUE;
772 
773     if(badcookie) {
774       freecookie(co);
775       return NULL;
776     }
777 
778   }
779 
780   if(!c->running &&    /* read from a file */
781      c->newsession &&  /* clean session cookies */
782      !co->expires) {   /* this is a session cookie since it doesn't expire! */
783     freecookie(co);
784     return NULL;
785   }
786 
787   co->livecookie = c->running;
788 
789   /* now, we have parsed the incoming line, we must now check if this
790      superceeds an already existing cookie, which it may if the previous have
791      the same domain and path as this */
792 
793   /* at first, remove expired cookies */
794   remove_expired(c);
795 
796 #ifdef USE_LIBPSL
797   /* Check if the domain is a Public Suffix and if yes, ignore the cookie.
798      This needs a libpsl compiled with builtin data. */
799   if(domain && co->domain && !isip(co->domain)) {
800     if(((psl = psl_builtin()) != NULL)
801         && !psl_is_cookie_domain_acceptable(psl, domain, co->domain)) {
802       infof(data,
803             "cookie '%s' dropped, domain '%s' must not set cookies for '%s'\n",
804             co->name, domain, co->domain);
805       freecookie(co);
806       return NULL;
807     }
808   }
809 #endif
810 
811   clist = c->cookies;
812   replace_old = FALSE;
813   while(clist) {
814     if(strcasecompare(clist->name, co->name)) {
815       /* the names are identical */
816 
817       if(clist->domain && co->domain) {
818         if(strcasecompare(clist->domain, co->domain) &&
819           (clist->tailmatch == co->tailmatch))
820           /* The domains are identical */
821           replace_old=TRUE;
822       }
823       else if(!clist->domain && !co->domain)
824         replace_old = TRUE;
825 
826       if(replace_old) {
827         /* the domains were identical */
828 
829         if(clist->spath && co->spath) {
830           if(strcasecompare(clist->spath, co->spath)) {
831             replace_old = TRUE;
832           }
833           else
834             replace_old = FALSE;
835         }
836         else if(!clist->spath && !co->spath)
837           replace_old = TRUE;
838         else
839           replace_old = FALSE;
840 
841       }
842 
843       if(replace_old && !co->livecookie && clist->livecookie) {
844         /* Both cookies matched fine, except that the already present
845            cookie is "live", which means it was set from a header, while
846            the new one isn't "live" and thus only read from a file. We let
847            live cookies stay alive */
848 
849         /* Free the newcomer and get out of here! */
850         freecookie(co);
851         return NULL;
852       }
853 
854       if(replace_old) {
855         co->next = clist->next; /* get the next-pointer first */
856 
857         /* then free all the old pointers */
858         free(clist->name);
859         free(clist->value);
860         free(clist->domain);
861         free(clist->path);
862         free(clist->spath);
863         free(clist->expirestr);
864         free(clist->version);
865         free(clist->maxage);
866 
867         *clist = *co;  /* then store all the new data */
868 
869         free(co);   /* free the newly alloced memory */
870         co = clist; /* point to the previous struct instead */
871 
872         /* We have replaced a cookie, now skip the rest of the list but
873            make sure the 'lastc' pointer is properly set */
874         do {
875           lastc = clist;
876           clist = clist->next;
877         } while(clist);
878         break;
879       }
880     }
881     lastc = clist;
882     clist = clist->next;
883   }
884 
885   if(c->running)
886     /* Only show this when NOT reading the cookies from a file */
887     infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
888           "expire %" CURL_FORMAT_CURL_OFF_T "\n",
889           replace_old?"Replaced":"Added", co->name, co->value,
890           co->domain, co->path, co->expires);
891 
892   if(!replace_old) {
893     /* then make the last item point on this new one */
894     if(lastc)
895       lastc->next = co;
896     else
897       c->cookies = co;
898     c->numcookies++; /* one more cookie in the jar */
899   }
900 
901   return co;
902 }
903 
904 /*
905  * get_line() makes sure to only return complete whole lines that fit in 'len'
906  * bytes and end with a newline.
907  */
get_line(char * buf,int len,FILE * input)908 static char *get_line(char *buf, int len, FILE *input)
909 {
910   bool partial = FALSE;
911   while(1) {
912     char *b = fgets(buf, len, input);
913     if(b) {
914       size_t rlen = strlen(b);
915       if(rlen && (b[rlen-1] == '\n')) {
916         if(partial) {
917           partial = FALSE;
918           continue;
919         }
920         return b;
921       }
922       else
923         /* read a partial, discard the next piece that ends with newline */
924         partial = TRUE;
925     }
926     else
927       break;
928   }
929   return NULL;
930 }
931 
932 
933 /*****************************************************************************
934  *
935  * Curl_cookie_init()
936  *
937  * Inits a cookie struct to read data from a local file. This is always
938  * called before any cookies are set. File may be NULL.
939  *
940  * If 'newsession' is TRUE, discard all "session cookies" on read from file.
941  *
942  * Returns NULL on out of memory. Invalid cookies are ignored.
943  ****************************************************************************/
Curl_cookie_init(struct Curl_easy * data,const char * file,struct CookieInfo * inc,bool newsession)944 struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
945                                     const char *file,
946                                     struct CookieInfo *inc,
947                                     bool newsession)
948 {
949   struct CookieInfo *c;
950   FILE *fp = NULL;
951   bool fromfile=TRUE;
952   char *line = NULL;
953 
954   if(NULL == inc) {
955     /* we didn't get a struct, create one */
956     c = calloc(1, sizeof(struct CookieInfo));
957     if(!c)
958       return NULL; /* failed to get memory */
959     c->filename = strdup(file?file:"none"); /* copy the name just in case */
960     if(!c->filename)
961       goto fail; /* failed to get memory */
962   }
963   else {
964     /* we got an already existing one, use that */
965     c = inc;
966   }
967   c->running = FALSE; /* this is not running, this is init */
968 
969   if(file && !strcmp(file, "-")) {
970     fp = stdin;
971     fromfile=FALSE;
972   }
973   else if(file && !*file) {
974     /* points to a "" string */
975     fp = NULL;
976   }
977   else
978     fp = file?fopen(file, FOPEN_READTEXT):NULL;
979 
980   c->newsession = newsession; /* new session? */
981 
982   if(fp) {
983     char *lineptr;
984     bool headerline;
985 
986     line = malloc(MAX_COOKIE_LINE);
987     if(!line)
988       goto fail;
989     while(get_line(line, MAX_COOKIE_LINE, fp)) {
990       if(checkprefix("Set-Cookie:", line)) {
991         /* This is a cookie line, get it! */
992         lineptr=&line[11];
993         headerline=TRUE;
994       }
995       else {
996         lineptr=line;
997         headerline=FALSE;
998       }
999       while(*lineptr && ISBLANK(*lineptr))
1000         lineptr++;
1001 
1002       Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
1003     }
1004     free(line); /* free the line buffer */
1005 
1006     if(fromfile)
1007       fclose(fp);
1008   }
1009 
1010   c->running = TRUE;          /* now, we're running */
1011 
1012   return c;
1013 
1014 fail:
1015   free(line);
1016   if(!inc)
1017     /* Only clean up if we allocated it here, as the original could still be in
1018      * use by a share handle */
1019     Curl_cookie_cleanup(c);
1020   if(fromfile && fp)
1021     fclose(fp);
1022   return NULL; /* out of memory */
1023 }
1024 
1025 /* sort this so that the longest path gets before the shorter path */
cookie_sort(const void * p1,const void * p2)1026 static int cookie_sort(const void *p1, const void *p2)
1027 {
1028   struct Cookie *c1 = *(struct Cookie **)p1;
1029   struct Cookie *c2 = *(struct Cookie **)p2;
1030   size_t l1, l2;
1031 
1032   /* 1 - compare cookie path lengths */
1033   l1 = c1->path ? strlen(c1->path) : 0;
1034   l2 = c2->path ? strlen(c2->path) : 0;
1035 
1036   if(l1 != l2)
1037     return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
1038 
1039   /* 2 - compare cookie domain lengths */
1040   l1 = c1->domain ? strlen(c1->domain) : 0;
1041   l2 = c2->domain ? strlen(c2->domain) : 0;
1042 
1043   if(l1 != l2)
1044     return (l2 > l1) ? 1 : -1 ;  /* avoid size_t <=> int conversions */
1045 
1046   /* 3 - compare cookie names */
1047   if(c1->name && c2->name)
1048     return strcmp(c1->name, c2->name);
1049 
1050   /* sorry, can't be more deterministic */
1051   return 0;
1052 }
1053 
1054 #define CLONE(field)                     \
1055   do {                                   \
1056     if(src->field) {                     \
1057       dup->field = strdup(src->field);   \
1058       if(!dup->field)                    \
1059         goto fail;                       \
1060     }                                    \
1061   } while(0)
1062 
dup_cookie(struct Cookie * src)1063 static struct Cookie *dup_cookie(struct Cookie *src)
1064 {
1065   struct Cookie *dup = calloc(sizeof(struct Cookie), 1);
1066   if(dup) {
1067     CLONE(expirestr);
1068     CLONE(domain);
1069     CLONE(path);
1070     CLONE(spath);
1071     CLONE(name);
1072     CLONE(value);
1073     CLONE(maxage);
1074     CLONE(version);
1075     dup->expires = src->expires;
1076     dup->tailmatch = src->tailmatch;
1077     dup->secure = src->secure;
1078     dup->livecookie = src->livecookie;
1079     dup->httponly = src->httponly;
1080   }
1081   return dup;
1082 
1083   fail:
1084   freecookie(dup);
1085   return NULL;
1086 }
1087 
1088 /*****************************************************************************
1089  *
1090  * Curl_cookie_getlist()
1091  *
1092  * For a given host and path, return a linked list of cookies that the
1093  * client should send to the server if used now. The secure boolean informs
1094  * the cookie if a secure connection is achieved or not.
1095  *
1096  * It shall only return cookies that haven't expired.
1097  *
1098  ****************************************************************************/
1099 
Curl_cookie_getlist(struct CookieInfo * c,const char * host,const char * path,bool secure)1100 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
1101                                    const char *host, const char *path,
1102                                    bool secure)
1103 {
1104   struct Cookie *newco;
1105   struct Cookie *co;
1106   time_t now = time(NULL);
1107   struct Cookie *mainco=NULL;
1108   size_t matches = 0;
1109   bool is_ip;
1110 
1111   if(!c || !c->cookies)
1112     return NULL; /* no cookie struct or no cookies in the struct */
1113 
1114   /* at first, remove expired cookies */
1115   remove_expired(c);
1116 
1117   /* check if host is an IP(v4|v6) address */
1118   is_ip = isip(host);
1119 
1120   co = c->cookies;
1121 
1122   while(co) {
1123     /* only process this cookie if it is not expired or had no expire
1124        date AND that if the cookie requires we're secure we must only
1125        continue if we are! */
1126     if((!co->expires || (co->expires > now)) &&
1127        (co->secure?secure:TRUE)) {
1128 
1129       /* now check if the domain is correct */
1130       if(!co->domain ||
1131          (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
1132          ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
1133         /* the right part of the host matches the domain stuff in the
1134            cookie data */
1135 
1136         /* now check the left part of the path with the cookies path
1137            requirement */
1138         if(!co->spath || pathmatch(co->spath, path) ) {
1139 
1140           /* and now, we know this is a match and we should create an
1141              entry for the return-linked-list */
1142 
1143           newco = dup_cookie(co);
1144           if(newco) {
1145             /* then modify our next */
1146             newco->next = mainco;
1147 
1148             /* point the main to us */
1149             mainco = newco;
1150 
1151             matches++;
1152           }
1153           else {
1154             fail:
1155             /* failure, clear up the allocated chain and return NULL */
1156             Curl_cookie_freelist(mainco);
1157             return NULL;
1158           }
1159         }
1160       }
1161     }
1162     co = co->next;
1163   }
1164 
1165   if(matches) {
1166     /* Now we need to make sure that if there is a name appearing more than
1167        once, the longest specified path version comes first. To make this
1168        the swiftest way, we just sort them all based on path length. */
1169     struct Cookie **array;
1170     size_t i;
1171 
1172     /* alloc an array and store all cookie pointers */
1173     array = malloc(sizeof(struct Cookie *) * matches);
1174     if(!array)
1175       goto fail;
1176 
1177     co = mainco;
1178 
1179     for(i=0; co; co = co->next)
1180       array[i++] = co;
1181 
1182     /* now sort the cookie pointers in path length order */
1183     qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
1184 
1185     /* remake the linked list order according to the new order */
1186 
1187     mainco = array[0]; /* start here */
1188     for(i=0; i<matches-1; i++)
1189       array[i]->next = array[i+1];
1190     array[matches-1]->next = NULL; /* terminate the list */
1191 
1192     free(array); /* remove the temporary data again */
1193   }
1194 
1195   return mainco; /* return the new list */
1196 }
1197 
1198 /*****************************************************************************
1199  *
1200  * Curl_cookie_clearall()
1201  *
1202  * Clear all existing cookies and reset the counter.
1203  *
1204  ****************************************************************************/
Curl_cookie_clearall(struct CookieInfo * cookies)1205 void Curl_cookie_clearall(struct CookieInfo *cookies)
1206 {
1207   if(cookies) {
1208     Curl_cookie_freelist(cookies->cookies);
1209     cookies->cookies = NULL;
1210     cookies->numcookies = 0;
1211   }
1212 }
1213 
1214 /*****************************************************************************
1215  *
1216  * Curl_cookie_freelist()
1217  *
1218  * Free a list of cookies previously returned by Curl_cookie_getlist();
1219  *
1220  ****************************************************************************/
1221 
Curl_cookie_freelist(struct Cookie * co)1222 void Curl_cookie_freelist(struct Cookie *co)
1223 {
1224   struct Cookie *next;
1225   while(co) {
1226     next = co->next;
1227     freecookie(co);
1228     co = next;
1229   }
1230 }
1231 
1232 
1233 /*****************************************************************************
1234  *
1235  * Curl_cookie_clearsess()
1236  *
1237  * Free all session cookies in the cookies list.
1238  *
1239  ****************************************************************************/
Curl_cookie_clearsess(struct CookieInfo * cookies)1240 void Curl_cookie_clearsess(struct CookieInfo *cookies)
1241 {
1242   struct Cookie *first, *curr, *next, *prev = NULL;
1243 
1244   if(!cookies || !cookies->cookies)
1245     return;
1246 
1247   first = curr = prev = cookies->cookies;
1248 
1249   for(; curr; curr = next) {
1250     next = curr->next;
1251     if(!curr->expires) {
1252       if(first == curr)
1253         first = next;
1254 
1255       if(prev == curr)
1256         prev = next;
1257       else
1258         prev->next = next;
1259 
1260       freecookie(curr);
1261       cookies->numcookies--;
1262     }
1263     else
1264       prev = curr;
1265   }
1266 
1267   cookies->cookies = first;
1268 }
1269 
1270 
1271 /*****************************************************************************
1272  *
1273  * Curl_cookie_cleanup()
1274  *
1275  * Free a "cookie object" previous created with Curl_cookie_init().
1276  *
1277  ****************************************************************************/
Curl_cookie_cleanup(struct CookieInfo * c)1278 void Curl_cookie_cleanup(struct CookieInfo *c)
1279 {
1280   if(c) {
1281     free(c->filename);
1282     Curl_cookie_freelist(c->cookies);
1283     free(c); /* free the base struct as well */
1284   }
1285 }
1286 
1287 /* get_netscape_format()
1288  *
1289  * Formats a string for Netscape output file, w/o a newline at the end.
1290  *
1291  * Function returns a char * to a formatted line. Has to be free()d
1292 */
get_netscape_format(const struct Cookie * co)1293 static char *get_netscape_format(const struct Cookie *co)
1294 {
1295   return aprintf(
1296     "%s"     /* httponly preamble */
1297     "%s%s\t" /* domain */
1298     "%s\t"   /* tailmatch */
1299     "%s\t"   /* path */
1300     "%s\t"   /* secure */
1301     "%" CURL_FORMAT_CURL_OFF_T "\t"   /* expires */
1302     "%s\t"   /* name */
1303     "%s",    /* value */
1304     co->httponly?"#HttpOnly_":"",
1305     /* Make sure all domains are prefixed with a dot if they allow
1306        tailmatching. This is Mozilla-style. */
1307     (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
1308     co->domain?co->domain:"unknown",
1309     co->tailmatch?"TRUE":"FALSE",
1310     co->path?co->path:"/",
1311     co->secure?"TRUE":"FALSE",
1312     co->expires,
1313     co->name,
1314     co->value?co->value:"");
1315 }
1316 
1317 /*
1318  * cookie_output()
1319  *
1320  * Writes all internally known cookies to the specified file. Specify
1321  * "-" as file name to write to stdout.
1322  *
1323  * The function returns non-zero on write failure.
1324  */
cookie_output(struct CookieInfo * c,const char * dumphere)1325 static int cookie_output(struct CookieInfo *c, const char *dumphere)
1326 {
1327   struct Cookie *co;
1328   FILE *out;
1329   bool use_stdout=FALSE;
1330   char *format_ptr;
1331 
1332   if((NULL == c) || (0 == c->numcookies))
1333     /* If there are no known cookies, we don't write or even create any
1334        destination file */
1335     return 0;
1336 
1337   /* at first, remove expired cookies */
1338   remove_expired(c);
1339 
1340   if(!strcmp("-", dumphere)) {
1341     /* use stdout */
1342     out = stdout;
1343     use_stdout=TRUE;
1344   }
1345   else {
1346     out = fopen(dumphere, FOPEN_WRITETEXT);
1347     if(!out)
1348       return 1; /* failure */
1349   }
1350 
1351   fputs("# Netscape HTTP Cookie File\n"
1352         "# https://curl.haxx.se/docs/http-cookies.html\n"
1353         "# This file was generated by libcurl! Edit at your own risk.\n\n",
1354         out);
1355 
1356   for(co = c->cookies; co; co = co->next) {
1357     if(!co->domain)
1358       continue;
1359     format_ptr = get_netscape_format(co);
1360     if(format_ptr == NULL) {
1361       fprintf(out, "#\n# Fatal libcurl error\n");
1362       if(!use_stdout)
1363         fclose(out);
1364       return 1;
1365     }
1366     fprintf(out, "%s\n", format_ptr);
1367     free(format_ptr);
1368   }
1369 
1370   if(!use_stdout)
1371     fclose(out);
1372 
1373   return 0;
1374 }
1375 
Curl_cookie_list(struct Curl_easy * data)1376 struct curl_slist *Curl_cookie_list(struct Curl_easy *data)
1377 {
1378   struct curl_slist *list = NULL;
1379   struct curl_slist *beg;
1380   struct Cookie *c;
1381   char *line;
1382 
1383   if((data->cookies == NULL) ||
1384       (data->cookies->numcookies == 0))
1385     return NULL;
1386 
1387   for(c = data->cookies->cookies; c; c = c->next) {
1388     if(!c->domain)
1389       continue;
1390     line = get_netscape_format(c);
1391     if(!line) {
1392       curl_slist_free_all(list);
1393       return NULL;
1394     }
1395     beg = Curl_slist_append_nodup(list, line);
1396     if(!beg) {
1397       free(line);
1398       curl_slist_free_all(list);
1399       return NULL;
1400     }
1401     list = beg;
1402   }
1403 
1404   return list;
1405 }
1406 
Curl_flush_cookies(struct Curl_easy * data,int cleanup)1407 void Curl_flush_cookies(struct Curl_easy *data, int cleanup)
1408 {
1409   if(data->set.str[STRING_COOKIEJAR]) {
1410     if(data->change.cookielist) {
1411       /* If there is a list of cookie files to read, do it first so that
1412          we have all the told files read before we write the new jar.
1413          Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
1414       Curl_cookie_loadfiles(data);
1415     }
1416 
1417     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1418 
1419     /* if we have a destination file for all the cookies to get dumped to */
1420     if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
1421       infof(data, "WARNING: failed to save cookies in %s\n",
1422             data->set.str[STRING_COOKIEJAR]);
1423   }
1424   else {
1425     if(cleanup && data->change.cookielist) {
1426       /* since nothing is written, we can just free the list of cookie file
1427          names */
1428       curl_slist_free_all(data->change.cookielist); /* clean up list */
1429       data->change.cookielist = NULL;
1430     }
1431     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1432   }
1433 
1434   if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
1435     Curl_cookie_cleanup(data->cookies);
1436   }
1437   Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1438 }
1439 
1440 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */
1441