1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 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.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 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25/*** 26 27 28RECEIVING COOKIE INFORMATION 29============================ 30 31struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, 32 const char *file, struct CookieInfo *inc, bool newsession); 33 34 Inits a cookie struct to store data in a local file. This is always 35 called before any cookies are set. 36 37struct Cookie *Curl_cookie_add(struct Curl_easy *data, 38 struct CookieInfo *c, bool httpheader, bool noexpire, 39 char *lineptr, const char *domain, const char *path, 40 bool secure); 41 42 The 'lineptr' parameter is a full "Set-cookie:" line as 43 received from a server. 44 45 The function need to replace previously stored lines that this new 46 line supersedes. 47 48 It may remove lines that are expired. 49 50 It should return an indication of success/error. 51 52 53SENDING COOKIE INFORMATION 54========================== 55 56struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie, 57 char *host, char *path, bool secure); 58 59 For a given host and path, return a linked list of cookies that 60 the client should send to the server if used now. The secure 61 boolean informs the cookie if a secure connection is achieved or 62 not. 63 64 It shall only return cookies that haven't expired. 65 66 67Example set of cookies: 68 69 Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure 70 Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 71 domain=.fidelity.com; path=/ftgw; secure 72 Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 73 domain=.fidelity.com; path=/; secure 74 Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 75 domain=.fidelity.com; path=/; secure 76 Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 77 domain=.fidelity.com; path=/; secure 78 Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 79 domain=.fidelity.com; path=/; secure 80 Set-cookie: 81 Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday, 82 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure 83****/ 84 85 86#include "curl_setup.h" 87 88#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) 89 90#include "urldata.h" 91#include "cookie.h" 92#include "psl.h" 93#include "strtok.h" 94#include "sendf.h" 95#include "slist.h" 96#include "share.h" 97#include "strtoofft.h" 98#include "strcase.h" 99#include "curl_get_line.h" 100#include "curl_memrchr.h" 101#include "parsedate.h" 102#include "rename.h" 103#include "fopen.h" 104#include "strdup.h" 105 106/* The last 3 #include files should be in this order */ 107#include "curl_printf.h" 108#include "curl_memory.h" 109#include "memdebug.h" 110 111static void strstore(char **str, const char *newstr, size_t len); 112 113static void freecookie(struct Cookie *co) 114{ 115 free(co->domain); 116 free(co->path); 117 free(co->spath); 118 free(co->name); 119 free(co->value); 120 free(co); 121} 122 123static bool cookie_tailmatch(const char *cookie_domain, 124 size_t cookie_domain_len, 125 const char *hostname) 126{ 127 size_t hostname_len = strlen(hostname); 128 129 if(hostname_len < cookie_domain_len) 130 return FALSE; 131 132 if(!strncasecompare(cookie_domain, 133 hostname + hostname_len-cookie_domain_len, 134 cookie_domain_len)) 135 return FALSE; 136 137 /* 138 * A lead char of cookie_domain is not '.'. 139 * RFC6265 4.1.2.3. The Domain Attribute says: 140 * For example, if the value of the Domain attribute is 141 * "example.com", the user agent will include the cookie in the Cookie 142 * header when making HTTP requests to example.com, www.example.com, and 143 * www.corp.example.com. 144 */ 145 if(hostname_len == cookie_domain_len) 146 return TRUE; 147 if('.' == *(hostname + hostname_len - cookie_domain_len - 1)) 148 return TRUE; 149 return FALSE; 150} 151 152/* 153 * matching cookie path and url path 154 * RFC6265 5.1.4 Paths and Path-Match 155 */ 156static bool pathmatch(const char *cookie_path, const char *request_uri) 157{ 158 size_t cookie_path_len; 159 size_t uri_path_len; 160 char *uri_path = NULL; 161 char *pos; 162 bool ret = FALSE; 163 164 /* cookie_path must not have last '/' separator. ex: /sample */ 165 cookie_path_len = strlen(cookie_path); 166 if(1 == cookie_path_len) { 167 /* cookie_path must be '/' */ 168 return TRUE; 169 } 170 171 uri_path = strdup(request_uri); 172 if(!uri_path) 173 return FALSE; 174 pos = strchr(uri_path, '?'); 175 if(pos) 176 *pos = 0x0; 177 178 /* #-fragments are already cut off! */ 179 if(0 == strlen(uri_path) || uri_path[0] != '/') { 180 strstore(&uri_path, "/", 1); 181 if(!uri_path) 182 return FALSE; 183 } 184 185 /* 186 * here, RFC6265 5.1.4 says 187 * 4. Output the characters of the uri-path from the first character up 188 * to, but not including, the right-most %x2F ("/"). 189 * but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site 190 * without redirect. 191 * Ignore this algorithm because /hoge is uri path for this case 192 * (uri path is not /). 193 */ 194 195 uri_path_len = strlen(uri_path); 196 197 if(uri_path_len < cookie_path_len) { 198 ret = FALSE; 199 goto pathmatched; 200 } 201 202 /* not using checkprefix() because matching should be case-sensitive */ 203 if(strncmp(cookie_path, uri_path, cookie_path_len)) { 204 ret = FALSE; 205 goto pathmatched; 206 } 207 208 /* The cookie-path and the uri-path are identical. */ 209 if(cookie_path_len == uri_path_len) { 210 ret = TRUE; 211 goto pathmatched; 212 } 213 214 /* here, cookie_path_len < uri_path_len */ 215 if(uri_path[cookie_path_len] == '/') { 216 ret = TRUE; 217 goto pathmatched; 218 } 219 220 ret = FALSE; 221 222pathmatched: 223 free(uri_path); 224 return ret; 225} 226 227/* 228 * Return the top-level domain, for optimal hashing. 229 */ 230static const char *get_top_domain(const char * const domain, size_t *outlen) 231{ 232 size_t len = 0; 233 const char *first = NULL, *last; 234 235 if(domain) { 236 len = strlen(domain); 237 last = memrchr(domain, '.', len); 238 if(last) { 239 first = memrchr(domain, '.', (last - domain)); 240 if(first) 241 len -= (++first - domain); 242 } 243 } 244 245 if(outlen) 246 *outlen = len; 247 248 return first? first: domain; 249} 250 251/* Avoid C1001, an "internal error" with MSVC14 */ 252#if defined(_MSC_VER) && (_MSC_VER == 1900) 253#pragma optimize("", off) 254#endif 255 256/* 257 * A case-insensitive hash for the cookie domains. 258 */ 259static size_t cookie_hash_domain(const char *domain, const size_t len) 260{ 261 const char *end = domain + len; 262 size_t h = 5381; 263 264 while(domain < end) { 265 h += h << 5; 266 h ^= Curl_raw_toupper(*domain++); 267 } 268 269 return (h % COOKIE_HASH_SIZE); 270} 271 272#if defined(_MSC_VER) && (_MSC_VER == 1900) 273#pragma optimize("", on) 274#endif 275 276/* 277 * Hash this domain. 278 */ 279static size_t cookiehash(const char * const domain) 280{ 281 const char *top; 282 size_t len; 283 284 if(!domain || Curl_host_is_ipnum(domain)) 285 return 0; 286 287 top = get_top_domain(domain, &len); 288 return cookie_hash_domain(top, len); 289} 290 291/* 292 * cookie path sanitize 293 */ 294static char *sanitize_cookie_path(const char *cookie_path) 295{ 296 size_t len; 297 char *new_path = strdup(cookie_path); 298 if(!new_path) 299 return NULL; 300 301 /* some stupid site sends path attribute with '"'. */ 302 len = strlen(new_path); 303 if(new_path[0] == '\"') { 304 memmove(new_path, new_path + 1, len); 305 len--; 306 } 307 if(len && (new_path[len - 1] == '\"')) { 308 new_path[--len] = 0x0; 309 } 310 311 /* RFC6265 5.2.4 The Path Attribute */ 312 if(new_path[0] != '/') { 313 /* Let cookie-path be the default-path. */ 314 strstore(&new_path, "/", 1); 315 return new_path; 316 } 317 318 /* convert /hoge/ to /hoge */ 319 if(len && new_path[len - 1] == '/') { 320 new_path[len - 1] = 0x0; 321 } 322 323 return new_path; 324} 325 326/* 327 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE). 328 * 329 * NOTE: OOM or cookie parsing failures are ignored. 330 */ 331void Curl_cookie_loadfiles(struct Curl_easy *data) 332{ 333 struct curl_slist *list = data->state.cookielist; 334 if(list) { 335 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 336 while(list) { 337 struct CookieInfo *newcookies = 338 Curl_cookie_init(data, list->data, data->cookies, 339 data->set.cookiesession); 340 if(!newcookies) 341 /* 342 * Failure may be due to OOM or a bad cookie; both are ignored 343 * but only the first should be 344 */ 345 infof(data, "ignoring failed cookie_init for %s", list->data); 346 else 347 data->cookies = newcookies; 348 list = list->next; 349 } 350 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); 351 } 352} 353 354/* 355 * strstore 356 * 357 * A thin wrapper around strdup which ensures that any memory allocated at 358 * *str will be freed before the string allocated by strdup is stored there. 359 * The intended usecase is repeated assignments to the same variable during 360 * parsing in a last-wins scenario. The caller is responsible for checking 361 * for OOM errors. 362 */ 363static void strstore(char **str, const char *newstr, size_t len) 364{ 365 DEBUGASSERT(newstr); 366 DEBUGASSERT(str); 367 free(*str); 368 *str = Curl_memdup0(newstr, len); 369} 370 371/* 372 * remove_expired 373 * 374 * Remove expired cookies from the hash by inspecting the expires timestamp on 375 * each cookie in the hash, freeing and deleting any where the timestamp is in 376 * the past. If the cookiejar has recorded the next timestamp at which one or 377 * more cookies expire, then processing will exit early in case this timestamp 378 * is in the future. 379 */ 380static void remove_expired(struct CookieInfo *cookies) 381{ 382 struct Cookie *co, *nx; 383 curl_off_t now = (curl_off_t)time(NULL); 384 unsigned int i; 385 386 /* 387 * If the earliest expiration timestamp in the jar is in the future we can 388 * skip scanning the whole jar and instead exit early as there won't be any 389 * cookies to evict. If we need to evict however, reset the next_expiration 390 * counter in order to track the next one. In case the recorded first 391 * expiration is the max offset, then perform the safe fallback of checking 392 * all cookies. 393 */ 394 if(now < cookies->next_expiration && 395 cookies->next_expiration != CURL_OFF_T_MAX) 396 return; 397 else 398 cookies->next_expiration = CURL_OFF_T_MAX; 399 400 for(i = 0; i < COOKIE_HASH_SIZE; i++) { 401 struct Cookie *pv = NULL; 402 co = cookies->cookies[i]; 403 while(co) { 404 nx = co->next; 405 if(co->expires && co->expires < now) { 406 if(!pv) { 407 cookies->cookies[i] = co->next; 408 } 409 else { 410 pv->next = co->next; 411 } 412 cookies->numcookies--; 413 freecookie(co); 414 } 415 else { 416 /* 417 * If this cookie has an expiration timestamp earlier than what we've 418 * seen so far then record it for the next round of expirations. 419 */ 420 if(co->expires && co->expires < cookies->next_expiration) 421 cookies->next_expiration = co->expires; 422 pv = co; 423 } 424 co = nx; 425 } 426 } 427} 428 429/* Make sure domain contains a dot or is localhost. */ 430static bool bad_domain(const char *domain, size_t len) 431{ 432 if((len == 9) && strncasecompare(domain, "localhost", 9)) 433 return FALSE; 434 else { 435 /* there must be a dot present, but that dot must not be a trailing dot */ 436 char *dot = memchr(domain, '.', len); 437 if(dot) { 438 size_t i = dot - domain; 439 if((len - i) > 1) 440 /* the dot is not the last byte */ 441 return FALSE; 442 } 443 } 444 return TRUE; 445} 446 447/* 448 RFC 6265 section 4.1.1 says a server should accept this range: 449 450 cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E 451 452 But Firefox and Chrome as of June 2022 accept space, comma and double-quotes 453 fine. The prime reason for filtering out control bytes is that some HTTP 454 servers return 400 for requests that contain such. 455*/ 456static int invalid_octets(const char *p) 457{ 458 /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */ 459 static const char badoctets[] = { 460 "\x01\x02\x03\x04\x05\x06\x07\x08\x0a" 461 "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" 462 "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f" 463 }; 464 size_t len; 465 /* scan for all the octets that are *not* in cookie-octet */ 466 len = strcspn(p, badoctets); 467 return (p[len] != '\0'); 468} 469 470/* 471 * Curl_cookie_add 472 * 473 * Add a single cookie line to the cookie keeping object. Be aware that 474 * sometimes we get an IP-only host name, and that might also be a numerical 475 * IPv6 address. 476 * 477 * Returns NULL on out of memory or invalid cookie. This is suboptimal, 478 * as they should be treated separately. 479 */ 480struct Cookie * 481Curl_cookie_add(struct Curl_easy *data, 482 struct CookieInfo *c, 483 bool httpheader, /* TRUE if HTTP header-style line */ 484 bool noexpire, /* if TRUE, skip remove_expired() */ 485 const char *lineptr, /* first character of the line */ 486 const char *domain, /* default domain */ 487 const char *path, /* full path used when this cookie is set, 488 used to get default path for the cookie 489 unless set */ 490 bool secure) /* TRUE if connection is over secure origin */ 491{ 492 struct Cookie *clist; 493 struct Cookie *co; 494 struct Cookie *lastc = NULL; 495 struct Cookie *replace_co = NULL; 496 struct Cookie *replace_clist = NULL; 497 time_t now = time(NULL); 498 bool replace_old = FALSE; 499 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */ 500 size_t myhash; 501 502 DEBUGASSERT(data); 503 DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */ 504 if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT) 505 return NULL; 506 507 /* First, alloc and init a new struct for it */ 508 co = calloc(1, sizeof(struct Cookie)); 509 if(!co) 510 return NULL; /* bail out if we're this low on memory */ 511 512 if(httpheader) { 513 /* This line was read off an HTTP-header */ 514 const char *ptr; 515 516 size_t linelength = strlen(lineptr); 517 if(linelength > MAX_COOKIE_LINE) { 518 /* discard overly long lines at once */ 519 free(co); 520 return NULL; 521 } 522 523 ptr = lineptr; 524 do { 525 size_t vlen; 526 size_t nlen; 527 528 while(*ptr && ISBLANK(*ptr)) 529 ptr++; 530 531 /* we have a <name>=<value> pair or a stand-alone word here */ 532 nlen = strcspn(ptr, ";\t\r\n="); 533 if(nlen) { 534 bool done = FALSE; 535 bool sep = FALSE; 536 const char *namep = ptr; 537 const char *valuep; 538 539 ptr += nlen; 540 541 /* trim trailing spaces and tabs after name */ 542 while(nlen && ISBLANK(namep[nlen - 1])) 543 nlen--; 544 545 if(*ptr == '=') { 546 vlen = strcspn(++ptr, ";\r\n"); 547 valuep = ptr; 548 sep = TRUE; 549 ptr = &valuep[vlen]; 550 551 /* Strip off trailing whitespace from the value */ 552 while(vlen && ISBLANK(valuep[vlen-1])) 553 vlen--; 554 555 /* Skip leading whitespace from the value */ 556 while(vlen && ISBLANK(*valuep)) { 557 valuep++; 558 vlen--; 559 } 560 561 /* Reject cookies with a TAB inside the value */ 562 if(memchr(valuep, '\t', vlen)) { 563 freecookie(co); 564 infof(data, "cookie contains TAB, dropping"); 565 return NULL; 566 } 567 } 568 else { 569 valuep = NULL; 570 vlen = 0; 571 } 572 573 /* 574 * Check for too long individual name or contents, or too long 575 * combination of name + contents. Chrome and Firefox support 4095 or 576 * 4096 bytes combo 577 */ 578 if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) || 579 ((nlen + vlen) > MAX_NAME)) { 580 freecookie(co); 581 infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", 582 nlen, vlen); 583 return NULL; 584 } 585 586 /* 587 * Check if we have a reserved prefix set before anything else, as we 588 * otherwise have to test for the prefix in both the cookie name and 589 * "the rest". Prefixes must start with '__' and end with a '-', so 590 * only test for names where that can possibly be true. 591 */ 592 if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') { 593 if(strncasecompare("__Secure-", namep, 9)) 594 co->prefix |= COOKIE_PREFIX__SECURE; 595 else if(strncasecompare("__Host-", namep, 7)) 596 co->prefix |= COOKIE_PREFIX__HOST; 597 } 598 599 /* 600 * Use strstore() below to properly deal with received cookie 601 * headers that have the same string property set more than once, 602 * and then we use the last one. 603 */ 604 605 if(!co->name) { 606 /* The very first name/value pair is the actual cookie name */ 607 if(!sep) { 608 /* Bad name/value pair. */ 609 badcookie = TRUE; 610 break; 611 } 612 strstore(&co->name, namep, nlen); 613 strstore(&co->value, valuep, vlen); 614 done = TRUE; 615 if(!co->name || !co->value) { 616 badcookie = TRUE; 617 break; 618 } 619 if(invalid_octets(co->value) || invalid_octets(co->name)) { 620 infof(data, "invalid octets in name/value, cookie dropped"); 621 badcookie = TRUE; 622 break; 623 } 624 } 625 else if(!vlen) { 626 /* 627 * this was a "<name>=" with no content, and we must allow 628 * 'secure' and 'httponly' specified this weirdly 629 */ 630 done = TRUE; 631 /* 632 * secure cookies are only allowed to be set when the connection is 633 * using a secure protocol, or when the cookie is being set by 634 * reading from file 635 */ 636 if((nlen == 6) && strncasecompare("secure", namep, 6)) { 637 if(secure || !c->running) { 638 co->secure = TRUE; 639 } 640 else { 641 badcookie = TRUE; 642 break; 643 } 644 } 645 else if((nlen == 8) && strncasecompare("httponly", namep, 8)) 646 co->httponly = TRUE; 647 else if(sep) 648 /* there was a '=' so we're not done parsing this field */ 649 done = FALSE; 650 } 651 if(done) 652 ; 653 else if((nlen == 4) && strncasecompare("path", namep, 4)) { 654 strstore(&co->path, valuep, vlen); 655 if(!co->path) { 656 badcookie = TRUE; /* out of memory bad */ 657 break; 658 } 659 free(co->spath); /* if this is set again */ 660 co->spath = sanitize_cookie_path(co->path); 661 if(!co->spath) { 662 badcookie = TRUE; /* out of memory bad */ 663 break; 664 } 665 } 666 else if((nlen == 6) && 667 strncasecompare("domain", namep, 6) && vlen) { 668 bool is_ip; 669 670 /* 671 * Now, we make sure that our host is within the given domain, or 672 * the given domain is not valid and thus cannot be set. 673 */ 674 675 if('.' == valuep[0]) { 676 valuep++; /* ignore preceding dot */ 677 vlen--; 678 } 679 680#ifndef USE_LIBPSL 681 /* 682 * Without PSL we don't know when the incoming cookie is set on a 683 * TLD or otherwise "protected" suffix. To reduce risk, we require a 684 * dot OR the exact host name being "localhost". 685 */ 686 if(bad_domain(valuep, vlen)) 687 domain = ":"; 688#endif 689 690 is_ip = Curl_host_is_ipnum(domain ? domain : valuep); 691 692 if(!domain 693 || (is_ip && !strncmp(valuep, domain, vlen) && 694 (vlen == strlen(domain))) 695 || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) { 696 strstore(&co->domain, valuep, vlen); 697 if(!co->domain) { 698 badcookie = TRUE; 699 break; 700 } 701 if(!is_ip) 702 co->tailmatch = TRUE; /* we always do that if the domain name was 703 given */ 704 } 705 else { 706 /* 707 * We did not get a tailmatch and then the attempted set domain is 708 * not a domain to which the current host belongs. Mark as bad. 709 */ 710 badcookie = TRUE; 711 infof(data, "skipped cookie with bad tailmatch domain: %s", 712 valuep); 713 } 714 } 715 else if((nlen == 7) && strncasecompare("version", namep, 7)) { 716 /* just ignore */ 717 } 718 else if((nlen == 7) && strncasecompare("max-age", namep, 7)) { 719 /* 720 * Defined in RFC2109: 721 * 722 * Optional. The Max-Age attribute defines the lifetime of the 723 * cookie, in seconds. The delta-seconds value is a decimal non- 724 * negative integer. After delta-seconds seconds elapse, the 725 * client should discard the cookie. A value of zero means the 726 * cookie should be discarded immediately. 727 */ 728 CURLofft offt; 729 const char *maxage = valuep; 730 offt = curlx_strtoofft((*maxage == '\"')? 731 &maxage[1]:&maxage[0], NULL, 10, 732 &co->expires); 733 switch(offt) { 734 case CURL_OFFT_FLOW: 735 /* overflow, used max value */ 736 co->expires = CURL_OFF_T_MAX; 737 break; 738 case CURL_OFFT_INVAL: 739 /* negative or otherwise bad, expire */ 740 co->expires = 1; 741 break; 742 case CURL_OFFT_OK: 743 if(!co->expires) 744 /* already expired */ 745 co->expires = 1; 746 else if(CURL_OFF_T_MAX - now < co->expires) 747 /* would overflow */ 748 co->expires = CURL_OFF_T_MAX; 749 else 750 co->expires += now; 751 break; 752 } 753 } 754 else if((nlen == 7) && strncasecompare("expires", namep, 7)) { 755 char date[128]; 756 if(!co->expires && (vlen < sizeof(date))) { 757 /* copy the date so that it can be null terminated */ 758 memcpy(date, valuep, vlen); 759 date[vlen] = 0; 760 /* 761 * Let max-age have priority. 762 * 763 * If the date cannot get parsed for whatever reason, the cookie 764 * will be treated as a session cookie 765 */ 766 co->expires = Curl_getdate_capped(date); 767 768 /* 769 * Session cookies have expires set to 0 so if we get that back 770 * from the date parser let's add a second to make it a 771 * non-session cookie 772 */ 773 if(co->expires == 0) 774 co->expires = 1; 775 else if(co->expires < 0) 776 co->expires = 0; 777 } 778 } 779 780 /* 781 * Else, this is the second (or more) name we don't know about! 782 */ 783 } 784 else { 785 /* this is an "illegal" <what>=<this> pair */ 786 } 787 788 while(*ptr && ISBLANK(*ptr)) 789 ptr++; 790 if(*ptr == ';') 791 ptr++; 792 else 793 break; 794 } while(1); 795 796 if(!badcookie && !co->domain) { 797 if(domain) { 798 /* no domain was given in the header line, set the default */ 799 co->domain = strdup(domain); 800 if(!co->domain) 801 badcookie = TRUE; 802 } 803 } 804 805 if(!badcookie && !co->path && path) { 806 /* 807 * No path was given in the header line, set the default. Note that the 808 * passed-in path to this function MAY have a '?' and following part that 809 * MUST NOT be stored as part of the path. 810 */ 811 char *queryp = strchr(path, '?'); 812 813 /* 814 * queryp is where the interesting part of the path ends, so now we 815 * want to the find the last 816 */ 817 char *endslash; 818 if(!queryp) 819 endslash = strrchr(path, '/'); 820 else 821 endslash = memrchr(path, '/', (queryp - path)); 822 if(endslash) { 823 size_t pathlen = (endslash-path + 1); /* include end slash */ 824 co->path = Curl_memdup0(path, pathlen); 825 if(co->path) { 826 co->spath = sanitize_cookie_path(co->path); 827 if(!co->spath) 828 badcookie = TRUE; /* out of memory bad */ 829 } 830 else 831 badcookie = TRUE; 832 } 833 } 834 835 /* 836 * If we didn't get a cookie name, or a bad one, the this is an illegal 837 * line so bail out. 838 */ 839 if(badcookie || !co->name) { 840 freecookie(co); 841 return NULL; 842 } 843 data->req.setcookies++; 844 } 845 else { 846 /* 847 * This line is NOT an HTTP header style line, we do offer support for 848 * reading the odd netscape cookies-file format here 849 */ 850 char *ptr; 851 char *firstptr; 852 char *tok_buf = NULL; 853 int fields; 854 855 /* 856 * IE introduced HTTP-only cookies to prevent XSS attacks. Cookies marked 857 * with httpOnly after the domain name are not accessible from javascripts, 858 * but since curl does not operate at javascript level, we include them 859 * anyway. In Firefox's cookie files, these lines are preceded with 860 * #HttpOnly_ and then everything is as usual, so we skip 10 characters of 861 * the line.. 862 */ 863 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) { 864 lineptr += 10; 865 co->httponly = TRUE; 866 } 867 868 if(lineptr[0]=='#') { 869 /* don't even try the comments */ 870 free(co); 871 return NULL; 872 } 873 /* strip off the possible end-of-line characters */ 874 ptr = strchr(lineptr, '\r'); 875 if(ptr) 876 *ptr = 0; /* clear it */ 877 ptr = strchr(lineptr, '\n'); 878 if(ptr) 879 *ptr = 0; /* clear it */ 880 881 firstptr = strtok_r((char *)lineptr, "\t", &tok_buf); /* tokenize on TAB */ 882 883 /* 884 * Now loop through the fields and init the struct we already have 885 * allocated 886 */ 887 for(ptr = firstptr, fields = 0; ptr && !badcookie; 888 ptr = strtok_r(NULL, "\t", &tok_buf), fields++) { 889 switch(fields) { 890 case 0: 891 if(ptr[0]=='.') /* skip preceding dots */ 892 ptr++; 893 co->domain = strdup(ptr); 894 if(!co->domain) 895 badcookie = TRUE; 896 break; 897 case 1: 898 /* 899 * flag: A TRUE/FALSE value indicating if all machines within a given 900 * domain can access the variable. Set TRUE when the cookie says 901 * .domain.com and to false when the domain is complete www.domain.com 902 */ 903 co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE; 904 break; 905 case 2: 906 /* The file format allows the path field to remain not filled in */ 907 if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) { 908 /* only if the path doesn't look like a boolean option! */ 909 co->path = strdup(ptr); 910 if(!co->path) 911 badcookie = TRUE; 912 else { 913 co->spath = sanitize_cookie_path(co->path); 914 if(!co->spath) { 915 badcookie = TRUE; /* out of memory bad */ 916 } 917 } 918 break; 919 } 920 /* this doesn't look like a path, make one up! */ 921 co->path = strdup("/"); 922 if(!co->path) 923 badcookie = TRUE; 924 co->spath = strdup("/"); 925 if(!co->spath) 926 badcookie = TRUE; 927 fields++; /* add a field and fall down to secure */ 928 FALLTHROUGH(); 929 case 3: 930 co->secure = FALSE; 931 if(strcasecompare(ptr, "TRUE")) { 932 if(secure || c->running) 933 co->secure = TRUE; 934 else 935 badcookie = TRUE; 936 } 937 break; 938 case 4: 939 if(curlx_strtoofft(ptr, NULL, 10, &co->expires)) 940 badcookie = TRUE; 941 break; 942 case 5: 943 co->name = strdup(ptr); 944 if(!co->name) 945 badcookie = TRUE; 946 else { 947 /* For Netscape file format cookies we check prefix on the name */ 948 if(strncasecompare("__Secure-", co->name, 9)) 949 co->prefix |= COOKIE_PREFIX__SECURE; 950 else if(strncasecompare("__Host-", co->name, 7)) 951 co->prefix |= COOKIE_PREFIX__HOST; 952 } 953 break; 954 case 6: 955 co->value = strdup(ptr); 956 if(!co->value) 957 badcookie = TRUE; 958 break; 959 } 960 } 961 if(6 == fields) { 962 /* we got a cookie with blank contents, fix it */ 963 co->value = strdup(""); 964 if(!co->value) 965 badcookie = TRUE; 966 else 967 fields++; 968 } 969 970 if(!badcookie && (7 != fields)) 971 /* we did not find the sufficient number of fields */ 972 badcookie = TRUE; 973 974 if(badcookie) { 975 freecookie(co); 976 return NULL; 977 } 978 979 } 980 981 if(co->prefix & COOKIE_PREFIX__SECURE) { 982 /* The __Secure- prefix only requires that the cookie be set secure */ 983 if(!co->secure) { 984 freecookie(co); 985 return NULL; 986 } 987 } 988 if(co->prefix & COOKIE_PREFIX__HOST) { 989 /* 990 * The __Host- prefix requires the cookie to be secure, have a "/" path 991 * and not have a domain set. 992 */ 993 if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch) 994 ; 995 else { 996 freecookie(co); 997 return NULL; 998 } 999 } 1000 1001 if(!c->running && /* read from a file */ 1002 c->newsession && /* clean session cookies */ 1003 !co->expires) { /* this is a session cookie since it doesn't expire! */ 1004 freecookie(co); 1005 return NULL; 1006 } 1007 1008 co->livecookie = c->running; 1009 co->creationtime = ++c->lastct; 1010 1011 /* 1012 * Now we have parsed the incoming line, we must now check if this supersedes 1013 * an already existing cookie, which it may if the previous have the same 1014 * domain and path as this. 1015 */ 1016 1017 /* at first, remove expired cookies */ 1018 if(!noexpire) 1019 remove_expired(c); 1020 1021#ifdef USE_LIBPSL 1022 /* 1023 * Check if the domain is a Public Suffix and if yes, ignore the cookie. We 1024 * must also check that the data handle isn't NULL since the psl code will 1025 * dereference it. 1026 */ 1027 if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) { 1028 bool acceptable = FALSE; 1029 char lcase[256]; 1030 char lcookie[256]; 1031 size_t dlen = strlen(domain); 1032 size_t clen = strlen(co->domain); 1033 if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) { 1034 const psl_ctx_t *psl = Curl_psl_use(data); 1035 if(psl) { 1036 /* the PSL check requires lowercase domain name and pattern */ 1037 Curl_strntolower(lcase, domain, dlen + 1); 1038 Curl_strntolower(lcookie, co->domain, clen + 1); 1039 acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); 1040 Curl_psl_release(data); 1041 } 1042 else 1043 acceptable = !bad_domain(domain, strlen(domain)); 1044 } 1045 1046 if(!acceptable) { 1047 infof(data, "cookie '%s' dropped, domain '%s' must not " 1048 "set cookies for '%s'", co->name, domain, co->domain); 1049 freecookie(co); 1050 return NULL; 1051 } 1052 } 1053#endif 1054 1055 /* A non-secure cookie may not overlay an existing secure cookie. */ 1056 myhash = cookiehash(co->domain); 1057 clist = c->cookies[myhash]; 1058 while(clist) { 1059 if(strcasecompare(clist->name, co->name)) { 1060 /* the names are identical */ 1061 bool matching_domains = FALSE; 1062 1063 if(clist->domain && co->domain) { 1064 if(strcasecompare(clist->domain, co->domain)) 1065 /* The domains are identical */ 1066 matching_domains = TRUE; 1067 } 1068 else if(!clist->domain && !co->domain) 1069 matching_domains = TRUE; 1070 1071 if(matching_domains && /* the domains were identical */ 1072 clist->spath && co->spath && /* both have paths */ 1073 clist->secure && !co->secure && !secure) { 1074 size_t cllen; 1075 const char *sep; 1076 1077 /* 1078 * A non-secure cookie may not overlay an existing secure cookie. 1079 * For an existing cookie "a" with path "/login", refuse a new 1080 * cookie "a" with for example path "/login/en", while the path 1081 * "/loginhelper" is ok. 1082 */ 1083 1084 sep = strchr(clist->spath + 1, '/'); 1085 1086 if(sep) 1087 cllen = sep - clist->spath; 1088 else 1089 cllen = strlen(clist->spath); 1090 1091 if(strncasecompare(clist->spath, co->spath, cllen)) { 1092 infof(data, "cookie '%s' for domain '%s' dropped, would " 1093 "overlay an existing cookie", co->name, co->domain); 1094 freecookie(co); 1095 return NULL; 1096 } 1097 } 1098 } 1099 1100 if(!replace_co && strcasecompare(clist->name, co->name)) { 1101 /* the names are identical */ 1102 1103 if(clist->domain && co->domain) { 1104 if(strcasecompare(clist->domain, co->domain) && 1105 (clist->tailmatch == co->tailmatch)) 1106 /* The domains are identical */ 1107 replace_old = TRUE; 1108 } 1109 else if(!clist->domain && !co->domain) 1110 replace_old = TRUE; 1111 1112 if(replace_old) { 1113 /* the domains were identical */ 1114 1115 if(clist->spath && co->spath && 1116 !strcasecompare(clist->spath, co->spath)) 1117 replace_old = FALSE; 1118 else if(!clist->spath != !co->spath) 1119 replace_old = FALSE; 1120 } 1121 1122 if(replace_old && !co->livecookie && clist->livecookie) { 1123 /* 1124 * Both cookies matched fine, except that the already present cookie is 1125 * "live", which means it was set from a header, while the new one was 1126 * read from a file and thus isn't "live". "live" cookies are preferred 1127 * so the new cookie is freed. 1128 */ 1129 freecookie(co); 1130 return NULL; 1131 } 1132 if(replace_old) { 1133 replace_co = co; 1134 replace_clist = clist; 1135 } 1136 } 1137 lastc = clist; 1138 clist = clist->next; 1139 } 1140 if(replace_co) { 1141 co = replace_co; 1142 clist = replace_clist; 1143 co->next = clist->next; /* get the next-pointer first */ 1144 1145 /* when replacing, creationtime is kept from old */ 1146 co->creationtime = clist->creationtime; 1147 1148 /* then free all the old pointers */ 1149 free(clist->name); 1150 free(clist->value); 1151 free(clist->domain); 1152 free(clist->path); 1153 free(clist->spath); 1154 1155 *clist = *co; /* then store all the new data */ 1156 1157 free(co); /* free the newly allocated memory */ 1158 co = clist; 1159 } 1160 1161 if(c->running) 1162 /* Only show this when NOT reading the cookies from a file */ 1163 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, " 1164 "expire %" CURL_FORMAT_CURL_OFF_T, 1165 replace_old?"Replaced":"Added", co->name, co->value, 1166 co->domain, co->path, co->expires); 1167 1168 if(!replace_old) { 1169 /* then make the last item point on this new one */ 1170 if(lastc) 1171 lastc->next = co; 1172 else 1173 c->cookies[myhash] = co; 1174 c->numcookies++; /* one more cookie in the jar */ 1175 } 1176 1177 /* 1178 * Now that we've added a new cookie to the jar, update the expiration 1179 * tracker in case it is the next one to expire. 1180 */ 1181 if(co->expires && (co->expires < c->next_expiration)) 1182 c->next_expiration = co->expires; 1183 1184 return co; 1185} 1186 1187 1188/* 1189 * Curl_cookie_init() 1190 * 1191 * Inits a cookie struct to read data from a local file. This is always 1192 * called before any cookies are set. File may be NULL in which case only the 1193 * struct is initialized. Is file is "-" then STDIN is read. 1194 * 1195 * If 'newsession' is TRUE, discard all "session cookies" on read from file. 1196 * 1197 * Note that 'data' might be called as NULL pointer. If data is NULL, 'file' 1198 * will be ignored. 1199 * 1200 * Returns NULL on out of memory. Invalid cookies are ignored. 1201 */ 1202struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, 1203 const char *file, 1204 struct CookieInfo *inc, 1205 bool newsession) 1206{ 1207 struct CookieInfo *c; 1208 char *line = NULL; 1209 FILE *handle = NULL; 1210 1211 if(!inc) { 1212 /* we didn't get a struct, create one */ 1213 c = calloc(1, sizeof(struct CookieInfo)); 1214 if(!c) 1215 return NULL; /* failed to get memory */ 1216 /* 1217 * Initialize the next_expiration time to signal that we don't have enough 1218 * information yet. 1219 */ 1220 c->next_expiration = CURL_OFF_T_MAX; 1221 } 1222 else { 1223 /* we got an already existing one, use that */ 1224 c = inc; 1225 } 1226 c->newsession = newsession; /* new session? */ 1227 1228 if(data) { 1229 FILE *fp = NULL; 1230 if(file && *file) { 1231 if(!strcmp(file, "-")) 1232 fp = stdin; 1233 else { 1234 fp = fopen(file, "rb"); 1235 if(!fp) 1236 infof(data, "WARNING: failed to open cookie file \"%s\"", file); 1237 else 1238 handle = fp; 1239 } 1240 } 1241 1242 c->running = FALSE; /* this is not running, this is init */ 1243 if(fp) { 1244 1245 line = malloc(MAX_COOKIE_LINE); 1246 if(!line) 1247 goto fail; 1248 while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) { 1249 char *lineptr = line; 1250 bool headerline = FALSE; 1251 if(checkprefix("Set-Cookie:", line)) { 1252 /* This is a cookie line, get it! */ 1253 lineptr = &line[11]; 1254 headerline = TRUE; 1255 while(*lineptr && ISBLANK(*lineptr)) 1256 lineptr++; 1257 } 1258 1259 Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE); 1260 } 1261 free(line); /* free the line buffer */ 1262 1263 /* 1264 * Remove expired cookies from the hash. We must make sure to run this 1265 * after reading the file, and not on every cookie. 1266 */ 1267 remove_expired(c); 1268 1269 if(handle) 1270 fclose(handle); 1271 } 1272 data->state.cookie_engine = TRUE; 1273 } 1274 c->running = TRUE; /* now, we're running */ 1275 1276 return c; 1277 1278fail: 1279 free(line); 1280 /* 1281 * Only clean up if we allocated it here, as the original could still be in 1282 * use by a share handle. 1283 */ 1284 if(!inc) 1285 Curl_cookie_cleanup(c); 1286 if(handle) 1287 fclose(handle); 1288 return NULL; /* out of memory */ 1289} 1290 1291/* 1292 * cookie_sort 1293 * 1294 * Helper function to sort cookies such that the longest path gets before the 1295 * shorter path. Path, domain and name lengths are considered in that order, 1296 * with the creationtime as the tiebreaker. The creationtime is guaranteed to 1297 * be unique per cookie, so we know we will get an ordering at that point. 1298 */ 1299static int cookie_sort(const void *p1, const void *p2) 1300{ 1301 struct Cookie *c1 = *(struct Cookie **)p1; 1302 struct Cookie *c2 = *(struct Cookie **)p2; 1303 size_t l1, l2; 1304 1305 /* 1 - compare cookie path lengths */ 1306 l1 = c1->path ? strlen(c1->path) : 0; 1307 l2 = c2->path ? strlen(c2->path) : 0; 1308 1309 if(l1 != l2) 1310 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ 1311 1312 /* 2 - compare cookie domain lengths */ 1313 l1 = c1->domain ? strlen(c1->domain) : 0; 1314 l2 = c2->domain ? strlen(c2->domain) : 0; 1315 1316 if(l1 != l2) 1317 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ 1318 1319 /* 3 - compare cookie name lengths */ 1320 l1 = c1->name ? strlen(c1->name) : 0; 1321 l2 = c2->name ? strlen(c2->name) : 0; 1322 1323 if(l1 != l2) 1324 return (l2 > l1) ? 1 : -1; 1325 1326 /* 4 - compare cookie creation time */ 1327 return (c2->creationtime > c1->creationtime) ? 1 : -1; 1328} 1329 1330/* 1331 * cookie_sort_ct 1332 * 1333 * Helper function to sort cookies according to creation time. 1334 */ 1335static int cookie_sort_ct(const void *p1, const void *p2) 1336{ 1337 struct Cookie *c1 = *(struct Cookie **)p1; 1338 struct Cookie *c2 = *(struct Cookie **)p2; 1339 1340 return (c2->creationtime > c1->creationtime) ? 1 : -1; 1341} 1342 1343#define CLONE(field) \ 1344 do { \ 1345 if(src->field) { \ 1346 d->field = strdup(src->field); \ 1347 if(!d->field) \ 1348 goto fail; \ 1349 } \ 1350 } while(0) 1351 1352static struct Cookie *dup_cookie(struct Cookie *src) 1353{ 1354 struct Cookie *d = calloc(1, sizeof(struct Cookie)); 1355 if(d) { 1356 CLONE(domain); 1357 CLONE(path); 1358 CLONE(spath); 1359 CLONE(name); 1360 CLONE(value); 1361 d->expires = src->expires; 1362 d->tailmatch = src->tailmatch; 1363 d->secure = src->secure; 1364 d->livecookie = src->livecookie; 1365 d->httponly = src->httponly; 1366 d->creationtime = src->creationtime; 1367 } 1368 return d; 1369 1370fail: 1371 freecookie(d); 1372 return NULL; 1373} 1374 1375/* 1376 * Curl_cookie_getlist 1377 * 1378 * For a given host and path, return a linked list of cookies that the client 1379 * should send to the server if used now. The secure boolean informs the cookie 1380 * if a secure connection is achieved or not. 1381 * 1382 * It shall only return cookies that haven't expired. 1383 */ 1384struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, 1385 struct CookieInfo *c, 1386 const char *host, const char *path, 1387 bool secure) 1388{ 1389 struct Cookie *newco; 1390 struct Cookie *co; 1391 struct Cookie *mainco = NULL; 1392 size_t matches = 0; 1393 bool is_ip; 1394 const size_t myhash = cookiehash(host); 1395 1396 if(!c || !c->cookies[myhash]) 1397 return NULL; /* no cookie struct or no cookies in the struct */ 1398 1399 /* at first, remove expired cookies */ 1400 remove_expired(c); 1401 1402 /* check if host is an IP(v4|v6) address */ 1403 is_ip = Curl_host_is_ipnum(host); 1404 1405 co = c->cookies[myhash]; 1406 1407 while(co) { 1408 /* if the cookie requires we're secure we must only continue if we are! */ 1409 if(co->secure?secure:TRUE) { 1410 1411 /* now check if the domain is correct */ 1412 if(!co->domain || 1413 (co->tailmatch && !is_ip && 1414 cookie_tailmatch(co->domain, strlen(co->domain), host)) || 1415 ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) { 1416 /* 1417 * the right part of the host matches the domain stuff in the 1418 * cookie data 1419 */ 1420 1421 /* 1422 * now check the left part of the path with the cookies path 1423 * requirement 1424 */ 1425 if(!co->spath || pathmatch(co->spath, path) ) { 1426 1427 /* 1428 * and now, we know this is a match and we should create an 1429 * entry for the return-linked-list 1430 */ 1431 1432 newco = dup_cookie(co); 1433 if(newco) { 1434 /* then modify our next */ 1435 newco->next = mainco; 1436 1437 /* point the main to us */ 1438 mainco = newco; 1439 1440 matches++; 1441 if(matches >= MAX_COOKIE_SEND_AMOUNT) { 1442 infof(data, "Included max number of cookies (%zu) in request!", 1443 matches); 1444 break; 1445 } 1446 } 1447 else 1448 goto fail; 1449 } 1450 } 1451 } 1452 co = co->next; 1453 } 1454 1455 if(matches) { 1456 /* 1457 * Now we need to make sure that if there is a name appearing more than 1458 * once, the longest specified path version comes first. To make this 1459 * the swiftest way, we just sort them all based on path length. 1460 */ 1461 struct Cookie **array; 1462 size_t i; 1463 1464 /* alloc an array and store all cookie pointers */ 1465 array = malloc(sizeof(struct Cookie *) * matches); 1466 if(!array) 1467 goto fail; 1468 1469 co = mainco; 1470 1471 for(i = 0; co; co = co->next) 1472 array[i++] = co; 1473 1474 /* now sort the cookie pointers in path length order */ 1475 qsort(array, matches, sizeof(struct Cookie *), cookie_sort); 1476 1477 /* remake the linked list order according to the new order */ 1478 1479 mainco = array[0]; /* start here */ 1480 for(i = 0; i<matches-1; i++) 1481 array[i]->next = array[i + 1]; 1482 array[matches-1]->next = NULL; /* terminate the list */ 1483 1484 free(array); /* remove the temporary data again */ 1485 } 1486 1487 return mainco; /* return the new list */ 1488 1489fail: 1490 /* failure, clear up the allocated chain and return NULL */ 1491 Curl_cookie_freelist(mainco); 1492 return NULL; 1493} 1494 1495/* 1496 * Curl_cookie_clearall 1497 * 1498 * Clear all existing cookies and reset the counter. 1499 */ 1500void Curl_cookie_clearall(struct CookieInfo *cookies) 1501{ 1502 if(cookies) { 1503 unsigned int i; 1504 for(i = 0; i < COOKIE_HASH_SIZE; i++) { 1505 Curl_cookie_freelist(cookies->cookies[i]); 1506 cookies->cookies[i] = NULL; 1507 } 1508 cookies->numcookies = 0; 1509 } 1510} 1511 1512/* 1513 * Curl_cookie_freelist 1514 * 1515 * Free a list of cookies previously returned by Curl_cookie_getlist(); 1516 */ 1517void Curl_cookie_freelist(struct Cookie *co) 1518{ 1519 struct Cookie *next; 1520 while(co) { 1521 next = co->next; 1522 freecookie(co); 1523 co = next; 1524 } 1525} 1526 1527/* 1528 * Curl_cookie_clearsess 1529 * 1530 * Free all session cookies in the cookies list. 1531 */ 1532void Curl_cookie_clearsess(struct CookieInfo *cookies) 1533{ 1534 struct Cookie *first, *curr, *next, *prev = NULL; 1535 unsigned int i; 1536 1537 if(!cookies) 1538 return; 1539 1540 for(i = 0; i < COOKIE_HASH_SIZE; i++) { 1541 if(!cookies->cookies[i]) 1542 continue; 1543 1544 first = curr = prev = cookies->cookies[i]; 1545 1546 for(; curr; curr = next) { 1547 next = curr->next; 1548 if(!curr->expires) { 1549 if(first == curr) 1550 first = next; 1551 1552 if(prev == curr) 1553 prev = next; 1554 else 1555 prev->next = next; 1556 1557 freecookie(curr); 1558 cookies->numcookies--; 1559 } 1560 else 1561 prev = curr; 1562 } 1563 1564 cookies->cookies[i] = first; 1565 } 1566} 1567 1568/* 1569 * Curl_cookie_cleanup() 1570 * 1571 * Free a "cookie object" previous created with Curl_cookie_init(). 1572 */ 1573void Curl_cookie_cleanup(struct CookieInfo *c) 1574{ 1575 if(c) { 1576 unsigned int i; 1577 for(i = 0; i < COOKIE_HASH_SIZE; i++) 1578 Curl_cookie_freelist(c->cookies[i]); 1579 free(c); /* free the base struct as well */ 1580 } 1581} 1582 1583/* 1584 * get_netscape_format() 1585 * 1586 * Formats a string for Netscape output file, w/o a newline at the end. 1587 * Function returns a char * to a formatted line. The caller is responsible 1588 * for freeing the returned pointer. 1589 */ 1590static char *get_netscape_format(const struct Cookie *co) 1591{ 1592 return aprintf( 1593 "%s" /* httponly preamble */ 1594 "%s%s\t" /* domain */ 1595 "%s\t" /* tailmatch */ 1596 "%s\t" /* path */ 1597 "%s\t" /* secure */ 1598 "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */ 1599 "%s\t" /* name */ 1600 "%s", /* value */ 1601 co->httponly?"#HttpOnly_":"", 1602 /* 1603 * Make sure all domains are prefixed with a dot if they allow 1604 * tailmatching. This is Mozilla-style. 1605 */ 1606 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"", 1607 co->domain?co->domain:"unknown", 1608 co->tailmatch?"TRUE":"FALSE", 1609 co->path?co->path:"/", 1610 co->secure?"TRUE":"FALSE", 1611 co->expires, 1612 co->name, 1613 co->value?co->value:""); 1614} 1615 1616/* 1617 * cookie_output() 1618 * 1619 * Writes all internally known cookies to the specified file. Specify 1620 * "-" as file name to write to stdout. 1621 * 1622 * The function returns non-zero on write failure. 1623 */ 1624static CURLcode cookie_output(struct Curl_easy *data, 1625 struct CookieInfo *c, const char *filename) 1626{ 1627 struct Cookie *co; 1628 FILE *out = NULL; 1629 bool use_stdout = FALSE; 1630 char *tempstore = NULL; 1631 CURLcode error = CURLE_OK; 1632 1633 if(!c) 1634 /* no cookie engine alive */ 1635 return CURLE_OK; 1636 1637 /* at first, remove expired cookies */ 1638 remove_expired(c); 1639 1640 if(!strcmp("-", filename)) { 1641 /* use stdout */ 1642 out = stdout; 1643 use_stdout = TRUE; 1644 } 1645 else { 1646 error = Curl_fopen(data, filename, &out, &tempstore); 1647 if(error) 1648 goto error; 1649 } 1650 1651 fputs("# Netscape HTTP Cookie File\n" 1652 "# https://curl.se/docs/http-cookies.html\n" 1653 "# This file was generated by libcurl! Edit at your own risk.\n\n", 1654 out); 1655 1656 if(c->numcookies) { 1657 unsigned int i; 1658 size_t nvalid = 0; 1659 struct Cookie **array; 1660 1661 array = calloc(1, sizeof(struct Cookie *) * c->numcookies); 1662 if(!array) { 1663 error = CURLE_OUT_OF_MEMORY; 1664 goto error; 1665 } 1666 1667 /* only sort the cookies with a domain property */ 1668 for(i = 0; i < COOKIE_HASH_SIZE; i++) { 1669 for(co = c->cookies[i]; co; co = co->next) { 1670 if(!co->domain) 1671 continue; 1672 array[nvalid++] = co; 1673 } 1674 } 1675 1676 qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct); 1677 1678 for(i = 0; i < nvalid; i++) { 1679 char *format_ptr = get_netscape_format(array[i]); 1680 if(!format_ptr) { 1681 free(array); 1682 error = CURLE_OUT_OF_MEMORY; 1683 goto error; 1684 } 1685 fprintf(out, "%s\n", format_ptr); 1686 free(format_ptr); 1687 } 1688 1689 free(array); 1690 } 1691 1692 if(!use_stdout) { 1693 fclose(out); 1694 out = NULL; 1695 if(tempstore && Curl_rename(tempstore, filename)) { 1696 unlink(tempstore); 1697 error = CURLE_WRITE_ERROR; 1698 goto error; 1699 } 1700 } 1701 1702 /* 1703 * If we reach here we have successfully written a cookie file so there is 1704 * no need to inspect the error, any error case should have jumped into the 1705 * error block below. 1706 */ 1707 free(tempstore); 1708 return CURLE_OK; 1709 1710error: 1711 if(out && !use_stdout) 1712 fclose(out); 1713 free(tempstore); 1714 return error; 1715} 1716 1717static struct curl_slist *cookie_list(struct Curl_easy *data) 1718{ 1719 struct curl_slist *list = NULL; 1720 struct curl_slist *beg; 1721 struct Cookie *c; 1722 char *line; 1723 unsigned int i; 1724 1725 if(!data->cookies || (data->cookies->numcookies == 0)) 1726 return NULL; 1727 1728 for(i = 0; i < COOKIE_HASH_SIZE; i++) { 1729 for(c = data->cookies->cookies[i]; c; c = c->next) { 1730 if(!c->domain) 1731 continue; 1732 line = get_netscape_format(c); 1733 if(!line) { 1734 curl_slist_free_all(list); 1735 return NULL; 1736 } 1737 beg = Curl_slist_append_nodup(list, line); 1738 if(!beg) { 1739 free(line); 1740 curl_slist_free_all(list); 1741 return NULL; 1742 } 1743 list = beg; 1744 } 1745 } 1746 1747 return list; 1748} 1749 1750struct curl_slist *Curl_cookie_list(struct Curl_easy *data) 1751{ 1752 struct curl_slist *list; 1753 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 1754 list = cookie_list(data); 1755 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); 1756 return list; 1757} 1758 1759void Curl_flush_cookies(struct Curl_easy *data, bool cleanup) 1760{ 1761 CURLcode res; 1762 1763 if(data->set.str[STRING_COOKIEJAR]) { 1764 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 1765 1766 /* if we have a destination file for all the cookies to get dumped to */ 1767 res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]); 1768 if(res) 1769 infof(data, "WARNING: failed to save cookies in %s: %s", 1770 data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res)); 1771 } 1772 else { 1773 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 1774 } 1775 1776 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) { 1777 Curl_cookie_cleanup(data->cookies); 1778 data->cookies = NULL; 1779 } 1780 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); 1781} 1782 1783#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */ 1784