Coverage Report

Created: 2024-05-04 12:45

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