Coverage Report

Created: 2025-10-10 06:19

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