1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 * 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 Andrs Garca: 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! Andrs Garca 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 ****************************************************************************/ 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 */ 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 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 ****************************************************************************/ 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 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 ****************************************************************************/ 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 ****************************************************************************/ 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 */ 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 */ 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 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 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