diff options
Diffstat (limited to 'Utilities/cmcurl/lib/cookie.c')
-rw-r--r-- | Utilities/cmcurl/lib/cookie.c | 246 |
1 files changed, 121 insertions, 125 deletions
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index c457b2d..0c6e0f7 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c @@ -101,13 +101,14 @@ Example set of cookies: #include "parsedate.h" #include "rename.h" #include "fopen.h" +#include "strdup.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" -static void strstore(char **str, const char *newstr); +static void strstore(char **str, const char *newstr, size_t len); static void freecookie(struct Cookie *co) { @@ -122,15 +123,17 @@ static void freecookie(struct Cookie *co) free(co); } -static bool tailmatch(const char *cooke_domain, const char *hostname) +static bool tailmatch(const char *cookie_domain, size_t cookie_domain_len, + const char *hostname) { - size_t cookie_domain_len = strlen(cooke_domain); size_t hostname_len = strlen(hostname); if(hostname_len < cookie_domain_len) return FALSE; - if(!strcasecompare(cooke_domain, hostname + hostname_len-cookie_domain_len)) + if(!strncasecompare(cookie_domain, + hostname + hostname_len-cookie_domain_len, + cookie_domain_len)) return FALSE; /* @@ -176,7 +179,7 @@ static bool pathmatch(const char *cookie_path, const char *request_uri) /* #-fragments are already cut off! */ if(0 == strlen(uri_path) || uri_path[0] != '/') { - strstore(&uri_path, "/"); + strstore(&uri_path, "/", 1); if(!uri_path) return FALSE; } @@ -310,7 +313,7 @@ static char *sanitize_cookie_path(const char *cookie_path) /* RFC6265 5.2.4 The Path Attribute */ if(new_path[0] != '/') { /* Let cookie-path be the default-path. */ - strstore(&new_path, "/"); + strstore(&new_path, "/", 1); return new_path; } @@ -333,10 +336,9 @@ void Curl_cookie_loadfiles(struct Curl_easy *data) if(list) { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); while(list) { - struct CookieInfo *newcookies = Curl_cookie_init(data, - list->data, - data->cookies, - data->set.cookiesession); + struct CookieInfo *newcookies = + Curl_cookie_init(data, list->data, data->cookies, + data->set.cookiesession); if(!newcookies) /* * Failure may be due to OOM or a bad cookie; both are ignored @@ -360,10 +362,14 @@ void Curl_cookie_loadfiles(struct Curl_easy *data) * parsing in a last-wins scenario. The caller is responsible for checking * for OOM errors. */ -static void strstore(char **str, const char *newstr) +static void strstore(char **str, const char *newstr, size_t len) { + DEBUGASSERT(newstr); + DEBUGASSERT(str); free(*str); - *str = strdup(newstr); + *str = Curl_memdup(newstr, len + 1); + if(*str) + (*str)[len] = 0; } /* @@ -425,15 +431,19 @@ static void remove_expired(struct CookieInfo *cookies) } /* Make sure domain contains a dot or is localhost. */ -static bool bad_domain(const char *domain) +static bool bad_domain(const char *domain, size_t len) { - if(strcasecompare(domain, "localhost")) + if((len == 9) && strncasecompare(domain, "localhost", 9)) return FALSE; else { /* there must be a dot present, but that dot must not be a trailing dot */ - char *dot = strchr(domain, '.'); - if(dot) - return dot[1] ? FALSE : TRUE; + char *dot = memchr(domain, '.', len); + if(dot) { + size_t i = dot - domain; + if((len - i) > 1) + /* the dot is not the last byte */ + return FALSE; + } } return TRUE; } @@ -513,10 +523,9 @@ Curl_cookie_add(struct Curl_easy *data, if(httpheader) { /* This line was read off an HTTP-header */ - char name[MAX_NAME]; - char what[MAX_NAME]; + const char *namep; + const char *valuep; const char *ptr; - const char *semiptr; size_t linelength = strlen(lineptr); if(linelength > MAX_COOKIE_LINE) { @@ -525,73 +534,65 @@ Curl_cookie_add(struct Curl_easy *data, return NULL; } - semiptr = strchr(lineptr, ';'); /* first, find a semicolon */ - - while(*lineptr && ISBLANK(*lineptr)) - lineptr++; - ptr = lineptr; 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 "[^;\t\r\n=] =%" - MAX_NAME_TXT "[^;\r\n]", - name, what)) { - /* - * Use strstore() below to properly deal with received cookie - * headers that have the same string property set more than once, - * and then we use the last one. - */ - const char *whatptr; + size_t vlen; + size_t nlen; + + while(*ptr && ISBLANK(*ptr)) + ptr++; + + /* we have a <name>=<value> pair or a stand-alone word here */ + nlen = strcspn(ptr, ";\t\r\n="); + if(nlen) { bool done = FALSE; - bool sep; - size_t len = strlen(what); - size_t nlen = strlen(name); - const char *endofn = &ptr[ nlen ]; + bool sep = FALSE; - /* - * Check for too long individual name or contents, or too long - * combination of name + contents. Chrome and Firefox support 4095 or - * 4096 bytes combo - */ - if(nlen >= (MAX_NAME-1) || len >= (MAX_NAME-1) || - ((nlen + len) > MAX_NAME)) { - freecookie(co); - infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", - nlen, len); - return NULL; - } + namep = ptr; + ptr += nlen; - /* name ends with a '=' ? */ - sep = (*endofn == '=')?TRUE:FALSE; + /* trim trailing spaces and tabs after name */ + while(nlen && ISBLANK(namep[nlen - 1])) + nlen--; - if(nlen) { - endofn--; /* move to the last character */ - if(ISBLANK(*endofn)) { - /* skip trailing spaces in name */ - while(*endofn && ISBLANK(*endofn) && nlen) { - endofn--; - nlen--; - } - name[nlen] = 0; /* new end of name */ + if(*ptr == '=') { + vlen = strcspn(++ptr, ";\r\n"); + valuep = ptr; + sep = TRUE; + ptr = &valuep[vlen]; + + /* Strip off trailing whitespace from the value */ + while(vlen && ISBLANK(valuep[vlen-1])) + vlen--; + + /* Skip leading whitespace from the value */ + while(vlen && ISBLANK(*valuep)) { + valuep++; + vlen--; } - } - /* Strip off trailing whitespace from the 'what' */ - while(len && ISBLANK(what[len-1])) { - what[len-1] = 0; - len--; + /* Reject cookies with a TAB inside the value */ + if(memchr(valuep, '\t', vlen)) { + freecookie(co); + infof(data, "cookie contains TAB, dropping"); + return NULL; + } + } + else { + valuep = NULL; + vlen = 0; } - /* Skip leading whitespace from the 'what' */ - whatptr = what; - while(*whatptr && ISBLANK(*whatptr)) - whatptr++; - - /* Reject cookies with a TAB inside the content */ - if(strchr(whatptr, '\t')) { + /* + * Check for too long individual name or contents, or too long + * combination of name + contents. Chrome and Firefox support 4095 or + * 4096 bytes combo + */ + if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) || + ((nlen + vlen) > MAX_NAME)) { freecookie(co); - infof(data, "cookie contains TAB, dropping"); + infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", + nlen, vlen); return NULL; } @@ -601,13 +602,19 @@ Curl_cookie_add(struct Curl_easy *data, * "the rest". Prefixes must start with '__' and end with a '-', so * only test for names where that can possibly be true. */ - if(nlen > 3 && name[0] == '_' && name[1] == '_') { - if(strncasecompare("__Secure-", name, 9)) + if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') { + if(strncasecompare("__Secure-", namep, 9)) co->prefix |= COOKIE_PREFIX__SECURE; - else if(strncasecompare("__Host-", name, 7)) + else if(strncasecompare("__Host-", namep, 7)) co->prefix |= COOKIE_PREFIX__HOST; } + /* + * Use strstore() below to properly deal with received cookie + * headers that have the same string property set more than once, + * and then we use the last one. + */ + if(!co->name) { /* The very first name/value pair is the actual cookie name */ if(!sep) { @@ -615,20 +622,20 @@ Curl_cookie_add(struct Curl_easy *data, badcookie = TRUE; break; } - co->name = strdup(name); - co->value = strdup(whatptr); + strstore(&co->name, namep, nlen); + strstore(&co->value, valuep, vlen); done = TRUE; if(!co->name || !co->value) { badcookie = TRUE; break; } - if(invalid_octets(whatptr) || invalid_octets(name)) { + if(invalid_octets(co->value) || invalid_octets(co->name)) { infof(data, "invalid octets in name/value, cookie dropped"); badcookie = TRUE; break; } } - else if(!len) { + else if(!vlen) { /* * this was a "<name>=" with no content, and we must allow * 'secure' and 'httponly' specified this weirdly @@ -639,7 +646,7 @@ Curl_cookie_add(struct Curl_easy *data, * using a secure protocol, or when the cookie is being set by * reading from file */ - if(strcasecompare("secure", name)) { + if((nlen == 6) && strncasecompare("secure", namep, 6)) { if(secure || !c->running) { co->secure = TRUE; } @@ -648,7 +655,7 @@ Curl_cookie_add(struct Curl_easy *data, break; } } - else if(strcasecompare("httponly", name)) + else if((nlen == 8) && strncasecompare("httponly", namep, 8)) co->httponly = TRUE; else if(sep) /* there was a '=' so we're not done parsing this field */ @@ -656,8 +663,8 @@ Curl_cookie_add(struct Curl_easy *data, } if(done) ; - else if(strcasecompare("path", name)) { - strstore(&co->path, whatptr); + else if((nlen == 4) && strncasecompare("path", namep, 4)) { + strstore(&co->path, valuep, vlen); if(!co->path) { badcookie = TRUE; /* out of memory bad */ break; @@ -669,7 +676,8 @@ Curl_cookie_add(struct Curl_easy *data, break; } } - else if(strcasecompare("domain", name) && whatptr[0]) { + else if((nlen == 6) && + strncasecompare("domain", namep, 6) && vlen) { bool is_ip; /* @@ -677,8 +685,10 @@ Curl_cookie_add(struct Curl_easy *data, * the given domain is not valid and thus cannot be set. */ - if('.' == whatptr[0]) - whatptr++; /* ignore preceding dot */ + if('.' == valuep[0]) { + valuep++; /* ignore preceding dot */ + vlen--; + } #ifndef USE_LIBPSL /* @@ -686,16 +696,17 @@ Curl_cookie_add(struct Curl_easy *data, * TLD or otherwise "protected" suffix. To reduce risk, we require a * dot OR the exact host name being "localhost". */ - if(bad_domain(whatptr)) + if(bad_domain(valuep, vlen)) domain = ":"; #endif - is_ip = Curl_host_is_ipnum(domain ? domain : whatptr); + is_ip = Curl_host_is_ipnum(domain ? domain : valuep); if(!domain - || (is_ip && !strcmp(whatptr, domain)) - || (!is_ip && tailmatch(whatptr, domain))) { - strstore(&co->domain, whatptr); + || (is_ip && !strncmp(valuep, domain, vlen) && + (vlen == strlen(domain))) + || (!is_ip && tailmatch(valuep, vlen, domain))) { + strstore(&co->domain, valuep, vlen); if(!co->domain) { badcookie = TRUE; break; @@ -711,17 +722,17 @@ Curl_cookie_add(struct Curl_easy *data, */ badcookie = TRUE; infof(data, "skipped cookie with bad tailmatch domain: %s", - whatptr); + valuep); } } - else if(strcasecompare("version", name)) { - strstore(&co->version, whatptr); + else if((nlen == 7) && strncasecompare("version", namep, 7)) { + strstore(&co->version, valuep, vlen); if(!co->version) { badcookie = TRUE; break; } } - else if(strcasecompare("max-age", name)) { + else if((nlen == 7) && strncasecompare("max-age", namep, 7)) { /* * Defined in RFC2109: * @@ -731,14 +742,14 @@ Curl_cookie_add(struct Curl_easy *data, * client should discard the cookie. A value of zero means the * cookie should be discarded immediately. */ - strstore(&co->maxage, whatptr); + strstore(&co->maxage, valuep, vlen); if(!co->maxage) { badcookie = TRUE; break; } } - else if(strcasecompare("expires", name)) { - strstore(&co->expirestr, whatptr); + else if((nlen == 7) && strncasecompare("expires", namep, 7)) { + strstore(&co->expirestr, valuep, vlen); if(!co->expirestr) { badcookie = TRUE; break; @@ -753,24 +764,13 @@ Curl_cookie_add(struct Curl_easy *data, /* this is an "illegal" <what>=<this> pair */ } - if(!semiptr || !*semiptr) { - /* we already know there are no more cookies */ - semiptr = NULL; - continue; - } - - ptr = semiptr + 1; while(*ptr && ISBLANK(*ptr)) ptr++; - semiptr = strchr(ptr, ';'); /* now, find the next semicolon */ - - if(!semiptr && *ptr) - /* - * There are no more semicolons, but there's a final name=value pair - * coming up - */ - semiptr = strchr(ptr, '\0'); - } while(semiptr); + if(*ptr == ';') + ptr++; + else + break; + } while(1); if(co->maxage) { CURLofft offt; @@ -1057,7 +1057,7 @@ Curl_cookie_add(struct Curl_easy *data, Curl_psl_release(data); } else - acceptable = !bad_domain(domain); + acceptable = !bad_domain(domain, strlen(domain)); if(!acceptable) { infof(data, "cookie '%s' dropped, domain '%s' must not " @@ -1447,7 +1447,8 @@ struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, /* now check if the domain is correct */ if(!co->domain || - (co->tailmatch && !is_ip && tailmatch(co->domain, host)) || + (co->tailmatch && !is_ip && + tailmatch(co->domain, co->domain? strlen(co->domain):0, host)) || ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) { /* * the right part of the host matches the domain stuff in the @@ -1798,11 +1799,6 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup) CURLcode res; if(data->set.str[STRING_COOKIEJAR]) { - /* If there is a list of cookie files to read, do it first so that - we have all the told files read before we write the new jar. - Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */ - Curl_cookie_loadfiles(data); - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); /* if we have a destination file for all the cookies to get dumped to */ |