Coverage Report

Created: 2023-06-07 07:02

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