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