Coverage Report

Created: 2024-02-25 06:14

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