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