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