diff options
Diffstat (limited to 'Utilities/cmcurl/lib/cookie.c')
-rw-r--r-- | Utilities/cmcurl/lib/cookie.c | 204 |
1 files changed, 128 insertions, 76 deletions
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index 0c2d49b..8eaedee 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c @@ -18,6 +18,8 @@ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * + * SPDX-License-Identifier: curl + * ***************************************************************************/ /*** @@ -33,8 +35,9 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, called before any cookies are set. struct Cookie *Curl_cookie_add(struct Curl_easy *data, - struct CookieInfo *c, bool httpheader, char *lineptr, - const char *domain, const char *path); + struct CookieInfo *c, bool httpheader, bool noexpire, + char *lineptr, const char *domain, const char *path, + bool secure); The 'lineptr' parameter is a full "Set-cookie:" line as received from a server. @@ -96,8 +99,8 @@ Example set of cookies: #include "curl_get_line.h" #include "curl_memrchr.h" #include "parsedate.h" -#include "rand.h" #include "rename.h" +#include "fopen.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -439,6 +442,29 @@ static bool bad_domain(const char *domain) } /* + RFC 6265 section 4.1.1 says a server should accept this range: + + cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E + + But Firefox and Chrome as of June 2022 accept space, comma and double-quotes + fine. The prime reason for filtering out control bytes is that some HTTP + servers return 400 for requests that contain such. +*/ +static int invalid_octets(const char *p) +{ + /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */ + static const char badoctets[] = { + "\x01\x02\x03\x04\x05\x06\x07\x08\x0a" + "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" + "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f" + }; + size_t len; + /* scan for all the octets that are *not* in cookie-octet */ + len = strcspn(p, badoctets); + return (p[len] != '\0'); +} + +/* * Curl_cookie_add * * Add a single cookie line to the cookie keeping object. Be aware that @@ -468,6 +494,8 @@ Curl_cookie_add(struct Curl_easy *data, struct Cookie *clist; struct Cookie *co; struct Cookie *lastc = NULL; + struct Cookie *replace_co = NULL; + struct Cookie *replace_clist = NULL; time_t now = time(NULL); bool replace_old = FALSE; bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */ @@ -477,6 +505,10 @@ Curl_cookie_add(struct Curl_easy *data, (void)data; #endif + DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */ + if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT) + return NULL; + /* First, alloc and init a new struct for it */ co = calloc(1, sizeof(struct Cookie)); if(!co) @@ -505,7 +537,7 @@ Curl_cookie_add(struct Curl_easy *data, do { /* we have a <what>=<this> pair or a stand-alone word here */ name[0] = what[0] = 0; /* init the buffers */ - if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n=] =%" + if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\t\r\n=] =%" MAX_NAME_TXT "[^;\r\n]", name, what)) { /* @@ -559,6 +591,13 @@ Curl_cookie_add(struct Curl_easy *data, while(*whatptr && ISBLANK(*whatptr)) whatptr++; + /* Reject cookies with a TAB inside the content */ + if(strchr(whatptr, '\t')) { + freecookie(co); + infof(data, "cookie contains TAB, dropping"); + return NULL; + } + /* * Check if we have a reserved prefix set before anything else, as we * otherwise have to test for the prefix in both the cookie name and @@ -586,6 +625,11 @@ Curl_cookie_add(struct Curl_easy *data, badcookie = TRUE; break; } + if(invalid_octets(whatptr) || invalid_octets(name)) { + infof(data, "invalid octets in name/value, cookie dropped"); + badcookie = TRUE; + break; + } } else if(!len) { /* @@ -628,7 +672,7 @@ Curl_cookie_add(struct Curl_easy *data, break; } } - else if(strcasecompare("domain", name)) { + else if(strcasecompare("domain", name) && whatptr[0]) { bool is_ip; /* @@ -816,7 +860,7 @@ Curl_cookie_add(struct Curl_easy *data, freecookie(co); return NULL; } - + data->req.setcookies++; } else { /* @@ -1020,12 +1064,53 @@ Curl_cookie_add(struct Curl_easy *data, } #endif + /* A non-secure cookie may not overlay an existing secure cookie. */ myhash = cookiehash(co->domain); clist = c->cookies[myhash]; - replace_old = FALSE; while(clist) { if(strcasecompare(clist->name, co->name)) { /* the names are identical */ + bool matching_domains = FALSE; + + if(clist->domain && co->domain) { + if(strcasecompare(clist->domain, co->domain)) + /* The domains are identical */ + matching_domains = TRUE; + } + else if(!clist->domain && !co->domain) + matching_domains = TRUE; + + if(matching_domains && /* the domains were identical */ + clist->spath && co->spath && /* both have paths */ + clist->secure && !co->secure && !secure) { + size_t cllen; + const char *sep; + + /* + * A non-secure cookie may not overlay an existing secure cookie. + * For an existing cookie "a" with path "/login", refuse a new + * cookie "a" with for example path "/login/en", while the path + * "/loginhelper" is ok. + */ + + sep = strchr(clist->spath + 1, '/'); + + if(sep) + cllen = sep - clist->spath; + else + cllen = strlen(clist->spath); + + if(strncasecompare(clist->spath, co->spath, cllen)) { + infof(data, "cookie '%s' for domain '%s' dropped, would " + "overlay an existing cookie", co->name, co->domain); + freecookie(co); + return NULL; + } + } + } + + if(!replace_co && strcasecompare(clist->name, co->name)) { + /* the names are identical */ if(clist->domain && co->domain) { if(strcasecompare(clist->domain, co->domain) && @@ -1040,30 +1125,7 @@ Curl_cookie_add(struct Curl_easy *data, /* the domains were identical */ if(clist->spath && co->spath) { - if(clist->secure && !co->secure && !secure) { - size_t cllen; - const char *sep; - - /* - * A non-secure cookie may not overlay an existing secure cookie. - * For an existing cookie "a" with path "/login", refuse a new - * cookie "a" with for example path "/login/en", while the path - * "/loginhelper" is ok. - */ - - sep = strchr(clist->spath + 1, '/'); - - if(sep) - cllen = sep - clist->spath; - else - cllen = strlen(clist->spath); - - if(strncasecompare(clist->spath, co->spath, cllen)) { - freecookie(co); - return NULL; - } - } - else if(strcasecompare(clist->spath, co->spath)) + if(strcasecompare(clist->spath, co->spath)) replace_old = TRUE; else replace_old = FALSE; @@ -1085,42 +1147,37 @@ Curl_cookie_add(struct Curl_easy *data, freecookie(co); return NULL; } - if(replace_old) { - co->next = clist->next; /* get the next-pointer first */ - - /* when replacing, creationtime is kept from old */ - co->creationtime = clist->creationtime; - - /* then free all the old pointers */ - free(clist->name); - free(clist->value); - free(clist->domain); - free(clist->path); - free(clist->spath); - free(clist->expirestr); - free(clist->version); - free(clist->maxage); - - *clist = *co; /* then store all the new data */ - - free(co); /* free the newly allocated memory */ - co = clist; /* point to the previous struct instead */ - - /* - * We have replaced a cookie, now skip the rest of the list but make - * sure the 'lastc' pointer is properly set - */ - do { - lastc = clist; - clist = clist->next; - } while(clist); - break; + replace_co = co; + replace_clist = clist; } } lastc = clist; clist = clist->next; } + if(replace_co) { + co = replace_co; + clist = replace_clist; + co->next = clist->next; /* get the next-pointer first */ + + /* when replacing, creationtime is kept from old */ + co->creationtime = clist->creationtime; + + /* then free all the old pointers */ + free(clist->name); + free(clist->value); + free(clist->domain); + free(clist->path); + free(clist->spath); + free(clist->expirestr); + free(clist->version); + free(clist->maxage); + + *clist = *co; /* then store all the new data */ + + free(co); /* free the newly allocated memory */ + co = clist; + } if(c->running) /* Only show this when NOT reading the cookies from a file */ @@ -1357,7 +1414,8 @@ static struct Cookie *dup_cookie(struct Cookie *src) * * It shall only return cookies that haven't expired. */ -struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, +struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, + struct CookieInfo *c, const char *host, const char *path, bool secure) { @@ -1412,6 +1470,11 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, mainco = newco; matches++; + if(matches >= MAX_COOKIE_SEND_AMOUNT) { + infof(data, "Included max number of cookies (%zu) in request!", + matches); + break; + } } else goto fail; @@ -1613,20 +1676,9 @@ static CURLcode cookie_output(struct Curl_easy *data, use_stdout = TRUE; } else { - unsigned char randsuffix[9]; - - if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix))) - return 2; - - tempstore = aprintf("%s.%s.tmp", filename, randsuffix); - if(!tempstore) - return CURLE_OUT_OF_MEMORY; - - out = fopen(tempstore, FOPEN_WRITETEXT); - if(!out) { - error = CURLE_WRITE_ERROR; + error = Curl_fopen(data, filename, &out, &tempstore); + if(error) goto error; - } } fputs("# Netscape HTTP Cookie File\n" @@ -1673,7 +1725,7 @@ static CURLcode cookie_output(struct Curl_easy *data, if(!use_stdout) { fclose(out); out = NULL; - if(Curl_rename(tempstore, filename)) { + if(tempstore && Curl_rename(tempstore, filename)) { unlink(tempstore); error = CURLE_WRITE_ERROR; goto error; |