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