diff options
-rw-r--r-- | lib/cookie.c | 143 | ||||
-rw-r--r-- | lib/cookie.h | 3 | ||||
-rw-r--r-- | tests/data/test31 | 3 | ||||
-rw-r--r-- | tests/data/test62 | 7 |
4 files changed, 134 insertions, 22 deletions
diff --git a/lib/cookie.c b/lib/cookie.c index 1ac97fa66..fc918136a 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -106,6 +106,8 @@ static void freecookie(struct Cookie *co) free(co->domain); if(co->path) free(co->path); + if(co->spath) + free(co->spath); if(co->name) free(co->name); if(co->value) @@ -143,32 +145,114 @@ static bool tailmatch(const char *cooke_domain, const char *hostname) return FALSE; } -static bool pathmatch(const char* cookie_path, const char* url_path) +/* + * matching cookie path and url path + * RFC6265 5.1.4 Paths and Path-Match + */ +static bool pathmatch(const char* cookie_path, const char* request_uri) { - size_t cookie_path_len = strlen(cookie_path); - size_t url_path_len = strlen(url_path); + size_t cookie_path_len; + size_t uri_path_len; + char* uri_path = NULL; + char* pos; + bool ret = FALSE; + + /* cookie_path must not have last '/' separator. ex: /sample */ + cookie_path_len = strlen(cookie_path); + if(1 == cookie_path_len) { + /* cookie_path must be '/' */ + return TRUE; + } - if(url_path_len < cookie_path_len) + uri_path = strdup(request_uri); + if(!uri_path) return FALSE; + pos = strchr(uri_path, '?'); + if(pos) + *pos = 0x0; + + /* #-fragments are already cut off! */ + if(0 == strlen(uri_path) || uri_path[0] != '/') { + free(uri_path); + uri_path = strdup("/"); + if(!uri_path) + return FALSE; + } + + /* here, RFC6265 5.1.4 says + 4. Output the characters of the uri-path from the first character up + to, but not including, the right-most %x2F ("/"). + but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site + without redirect. + Ignore this algorithm because /hoge is uri path for this case + (uri path is not /). + */ + + uri_path_len = strlen(uri_path); + + if(uri_path_len < cookie_path_len) { + ret = FALSE; + goto pathmatched; + } /* not using checkprefix() because matching should be case-sensitive */ - if(strncmp(cookie_path, url_path, cookie_path_len)) - return FALSE; + if(strncmp(cookie_path, uri_path, cookie_path_len)) { + ret = FALSE; + goto pathmatched; + } - /* it is true if cookie_path and url_path are the same */ - if(cookie_path_len == url_path_len) - return TRUE; + /* The cookie-path and the uri-path are identical. */ + if(cookie_path_len == uri_path_len) { + ret = TRUE; + goto pathmatched; + } /* here, cookie_path_len < url_path_len */ + if(uri_path[cookie_path_len] == '/') { + ret = TRUE; + goto pathmatched; + } - /* it is false if cookie path is /example and url path is /examples */ - if(cookie_path[cookie_path_len - 1] != '/') { - if(url_path[cookie_path_len] != '/') { - return FALSE; - } + ret = FALSE; + +pathmatched: + free(uri_path); + return ret; +} + +/* + * cookie path sanitize + */ +static char *sanitize_cookie_path(const char *cookie_path) +{ + size_t len; + char *new_path = strdup(cookie_path); + if(!new_path) + return NULL; + + /* some stupid site sends path attribute with '"'. */ + if(new_path[0] == '\"') { + memmove((void *)new_path, (const void *)(new_path + 1), strlen(new_path)); + } + if(new_path[strlen(new_path) - 1] == '\"') { + new_path[strlen(new_path) - 1] = 0x0; + } + + /* RFC6265 5.2.4 The Path Attribute */ + if(new_path[0] != '/') { + /* Let cookie-path be the default-path. */ + free(new_path); + new_path = strdup("/"); + return new_path; + } + + /* convert /hoge/ to /hoge */ + len = strlen(new_path); + if(1 < len && new_path[len - 1] == '/') { + new_path[len - 1] = 0x0; } - /* matching! */ - return TRUE; + + return new_path; } /* @@ -319,6 +403,11 @@ Curl_cookie_add(struct SessionHandle *data, badcookie = TRUE; /* out of memory bad */ break; } + co->spath = sanitize_cookie_path(co->path); + if(!co->spath) { + badcookie = TRUE; /* out of memory bad */ + break; + } } else if(Curl_raw_equal("domain", name)) { /* Now, we make sure that our host is within the given domain, @@ -454,6 +543,9 @@ Curl_cookie_add(struct SessionHandle *data, if(co->path) { memcpy(co->path, path, pathlen); co->path[pathlen]=0; /* zero terminate */ + co->spath = sanitize_cookie_path(co->path); + if(!co->spath) + badcookie = TRUE; /* out of memory bad */ } else badcookie = TRUE; @@ -539,12 +631,21 @@ Curl_cookie_add(struct SessionHandle *data, co->path = strdup(ptr); if(!co->path) badcookie = TRUE; + else { + co->spath = sanitize_cookie_path(co->path); + if(!co->spath) { + badcookie = TRUE; /* out of memory bad */ + } + } break; } /* this doesn't look like a path, make one up! */ co->path = strdup("/"); if(!co->path) badcookie = TRUE; + co->spath = strdup("/"); + if(!co->spath) + badcookie = TRUE; fields++; /* add a field and fall down to secure */ /* FALLTHROUGH */ case 3: @@ -615,14 +716,14 @@ Curl_cookie_add(struct SessionHandle *data, if(replace_old) { /* the domains were identical */ - if(clist->path && co->path) { - if(Curl_raw_equal(clist->path, co->path)) { + if(clist->spath && co->spath) { + if(Curl_raw_equal(clist->spath, co->spath)) { replace_old = TRUE; } else replace_old = FALSE; } - else if(!clist->path && !co->path) + else if(!clist->spath && !co->spath) replace_old = TRUE; else replace_old = FALSE; @@ -651,6 +752,8 @@ Curl_cookie_add(struct SessionHandle *data, free(clist->domain); if(clist->path) free(clist->path); + if(clist->spath) + free(clist->spath); if(clist->expirestr) free(clist->expirestr); @@ -845,7 +948,7 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, /* now check the left part of the path with the cookies path requirement */ - if(!co->path || pathmatch(co->path, path) ) { + if(!co->spath || pathmatch(co->spath, path) ) { /* and now, we know this is a match and we should create an entry for the return-linked-list */ diff --git a/lib/cookie.h b/lib/cookie.h index d3b63f780..bd890827c 100644 --- a/lib/cookie.h +++ b/lib/cookie.h @@ -29,7 +29,8 @@ struct Cookie { struct Cookie *next; /* next in the chain */ char *name; /* <this> = value */ char *value; /* name = <this> */ - char *path; /* path = <this> */ + char *path; /* path = <this> which is in Set-Cookie: */ + char *spath; /* sanitized cookie path */ char *domain; /* domain = <this> */ curl_off_t expires; /* expires = <this> */ char *expirestr; /* the plain text version */ diff --git a/tests/data/test31 b/tests/data/test31 index b1171d81d..38af83bb6 100644 --- a/tests/data/test31 +++ b/tests/data/test31 @@ -18,6 +18,8 @@ Content-Type: text/html Funny-head: yesyes
Set-Cookie: foobar=name; domain=anything.com; path=/ ; secure
Set-Cookie:ismatch=this ; domain=127.0.0.1; path=/silly/
+Set-Cookie: overwrite=this ; domain=127.0.0.1; path=/overwrite/
+Set-Cookie: overwrite=this2 ; domain=127.0.0.1; path=/overwrite
Set-Cookie: sec1value=secure1 ; domain=127.0.0.1; path=/secure1/ ; secure
Set-Cookie: sec2value=secure2 ; domain=127.0.0.1; path=/secure2/ ; secure=
Set-Cookie: sec3value=secure3 ; domain=127.0.0.1; path=/secure3/ ; secure=
@@ -94,6 +96,7 @@ Accept: */* # This file was generated by libcurl! Edit at your own risk. .127.0.0.1 TRUE /silly/ FALSE 0 ismatch this +.127.0.0.1 TRUE /overwrite FALSE 0 overwrite this2 .127.0.0.1 TRUE /secure1/ TRUE 0 sec1value secure1 .127.0.0.1 TRUE /secure2/ TRUE 0 sec2value secure2 .127.0.0.1 TRUE /secure3/ TRUE 0 sec3value secure3 diff --git a/tests/data/test62 b/tests/data/test62 index 19886066f..2e5d1db03 100644 --- a/tests/data/test62 +++ b/tests/data/test62 @@ -29,7 +29,7 @@ http HTTP, send cookies when using custom Host: </name> <command> -http://%HOSTIP:%HTTPPORT/we/want/62 -b log/jar62.txt -H "Host: www.host.foo.com" +http://%HOSTIP:%HTTPPORT/we/want/62 http://%HOSTIP:%HTTPPORT/we/want?hoge=fuga -b log/jar62.txt -H "Host: www.host.foo.com" </command> <file name="log/jar62.txt"> # Netscape HTTP Cookie File @@ -55,6 +55,11 @@ Accept: */* Cookie: test2=yes; test=yes
Host: www.host.foo.com
+GET /we/want?hoge=fuga HTTP/1.1
+Accept: */*
+Cookie: test2=yes; test=yes
+Host: www.host.foo.com
+
</protocol> </verify> </testcase> |