]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
cookies: only use full host matches for hosts used as IP address
authorTim Ruehsen <tim.ruehsen@gmx.de>
Tue, 19 Aug 2014 19:01:28 +0000 (21:01 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 10 Sep 2014 05:32:36 +0000 (07:32 +0200)
By not detecting and rejecting domain names for partial literal IP
addresses properly when parsing received HTTP cookies, libcurl can be
fooled to both send cookies to wrong sites and to allow arbitrary sites
to set cookies for others.

CVE-2014-3613

Bug: http://curl.haxx.se/docs/adv_20140910A.html

lib/cookie.c
tests/data/test1105
tests/data/test31
tests/data/test8

index 0590643489ae0ad903ec4013c35d1b4fe8f7784c..46904ac57f7de74d2b8387bea59e219545d7264d 100644 (file)
@@ -95,6 +95,7 @@ Example set of cookies:
 #include "strtoofft.h"
 #include "rawstr.h"
 #include "curl_memrchr.h"
+#include "inet_pton.h"
 
 /* The last #include file should be: */
 #include "memdebug.h"
@@ -319,6 +320,28 @@ static void remove_expired(struct CookieInfo *cookies)
   }
 }
 
+/*
+ * Return true if the given string is an IP(v4|v6) address.
+ */
+static bool isip(const char *domain)
+{
+  struct in_addr addr;
+#ifdef ENABLE_IPV6
+  struct in6_addr addr6;
+#endif
+
+  if(Curl_inet_pton(AF_INET, domain, &addr)
+#ifdef ENABLE_IPV6
+     || Curl_inet_pton(AF_INET6, domain, &addr6)
+#endif
+    ) {
+    /* domain name given as IP address */
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
 /****************************************************************************
  *
  * Curl_cookie_add()
@@ -439,24 +462,27 @@ Curl_cookie_add(struct SessionHandle *data,
           }
         }
         else if(Curl_raw_equal("domain", name)) {
+          bool is_ip;
+
           /* Now, we make sure that our host is within the given domain,
              or the given domain is not valid and thus cannot be set. */
 
           if('.' == whatptr[0])
             whatptr++; /* ignore preceding dot */
 
-          if(!domain || tailmatch(whatptr, domain)) {
-            const char *tailptr=whatptr;
-            if(tailptr[0] == '.')
-              tailptr++;
-            strstore(&co->domain, tailptr); /* don't prefix w/dots
-                                               internally */
+          is_ip = isip(domain ? domain : whatptr);
+
+          if(!domain
+             || (is_ip && !strcmp(whatptr, domain))
+             || (!is_ip && tailmatch(whatptr, domain))) {
+            strstore(&co->domain, whatptr);
             if(!co->domain) {
               badcookie = TRUE;
               break;
             }
-            co->tailmatch=TRUE; /* we always do that if the domain name was
-                                   given */
+            if(!is_ip)
+              co->tailmatch=TRUE; /* we always do that if the domain name was
+                                     given */
           }
           else {
             /* we did not get a tailmatch and then the attempted set domain
@@ -968,6 +994,7 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
   time_t now = time(NULL);
   struct Cookie *mainco=NULL;
   size_t matches = 0;
+  bool is_ip;
 
   if(!c || !c->cookies)
     return NULL; /* no cookie struct or no cookies in the struct */
@@ -975,6 +1002,9 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
   /* at first, remove expired cookies */
   remove_expired(c);
 
+  /* check if host is an IP(v4|v6) address */
+  is_ip = isip(host);
+
   co = c->cookies;
 
   while(co) {
@@ -986,8 +1016,8 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
 
       /* now check if the domain is correct */
       if(!co->domain ||
-         (co->tailmatch && tailmatch(co->domain, host)) ||
-         (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) {
+         (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
+         ((!co->tailmatch || is_ip) && Curl_raw_equal(host, co->domain)) ) {
         /* the right part of the host matches the domain stuff in the
            cookie data */
 
index 25f194c15d0ebd98e1a6c7c47d984f02050259d5..95647753f1a6562419e1ccad52d70d79a668726d 100644 (file)
@@ -59,8 +59,7 @@ userid=myname&password=mypassword
 # This file was generated by libcurl! Edit at your own risk.
 
 127.0.0.1      FALSE   /we/want/       FALSE   0       foobar  name
-.127.0.0.1     TRUE    "/silly/"       FALSE   0       mismatch        this
-.0.0.1 TRUE    /       FALSE   0       partmatch       present
+127.0.0.1      FALSE   "/silly/"       FALSE   0       mismatch        this
 </file>
 </verify>
 </testcase>
index 38af83bb62bb03a2312b11fc4089114eeaeabacf..dfcac0458700948fb42e43cf36dda28babce737d 100644 (file)
@@ -51,7 +51,8 @@ Set-Cookie: novalue; domain=reallysilly
 Set-Cookie: test=yes; domain=foo.com; expires=Sat Feb 2 11:56:27 GMT 2030\r
 Set-Cookie: test2=yes; domain=se; expires=Sat Feb 2 11:56:27 GMT 2030\r
 Set-Cookie: magic=yessir; path=/silly/; HttpOnly\r
-Set-Cookie: blexp=yesyes; domain=.0.0.1; domain=.0.0.1; expiry=totally bad;\r
+Set-Cookie: blexp=yesyes; domain=127.0.0.1; domain=127.0.0.1; expiry=totally bad;\r
+Set-Cookie: partialip=nono; domain=.0.0.1;\r
 \r
 boo
 </data>
@@ -95,34 +96,34 @@ Accept: */*
 # http://curl.haxx.se/docs/http-cookies.html
 # 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
-.127.0.0.1     TRUE    /secure4/       TRUE    0       sec4value       secure4
-.127.0.0.1     TRUE    /secure5/       TRUE    0       sec5value       secure5
-.127.0.0.1     TRUE    /secure6/       TRUE    0       sec6value       secure6
-.127.0.0.1     TRUE    /secure7/       TRUE    0       sec7value       secure7
-.127.0.0.1     TRUE    /secure8/       TRUE    0       sec8value       secure8
-.127.0.0.1     TRUE    /secure9/       TRUE    0       secure  very1
-#HttpOnly_.127.0.0.1   TRUE    /p1/    FALSE   0       httpo1  value1
-#HttpOnly_.127.0.0.1   TRUE    /p2/    FALSE   0       httpo2  value2
-#HttpOnly_.127.0.0.1   TRUE    /p3/    FALSE   0       httpo3  value3
-#HttpOnly_.127.0.0.1   TRUE    /p4/    FALSE   0       httpo4  value4
-#HttpOnly_.127.0.0.1   TRUE    /p4/    FALSE   0       httponly        myvalue1
-#HttpOnly_.127.0.0.1   TRUE    /p4/    TRUE    0       httpandsec      myvalue2
-#HttpOnly_.127.0.0.1   TRUE    /p4/    TRUE    0       httpandsec2     myvalue3
-#HttpOnly_.127.0.0.1   TRUE    /p4/    TRUE    0       httpandsec3     myvalue4
-#HttpOnly_.127.0.0.1   TRUE    /p4/    TRUE    0       httpandsec4     myvalue5
-#HttpOnly_.127.0.0.1   TRUE    /p4/    TRUE    0       httpandsec5     myvalue6
-#HttpOnly_.127.0.0.1   TRUE    /p4/    TRUE    0       httpandsec6     myvalue7
-#HttpOnly_.127.0.0.1   TRUE    /p4/    TRUE    0       httpandsec7     myvalue8
-#HttpOnly_.127.0.0.1   TRUE    /p4/    TRUE    0       httpandsec8     myvalue9
-.127.0.0.1     TRUE    /       FALSE   0       partmatch       present
+127.0.0.1      FALSE   /silly/ FALSE   0       ismatch this
+127.0.0.1      FALSE   /overwrite      FALSE   0       overwrite       this2
+127.0.0.1      FALSE   /secure1/       TRUE    0       sec1value       secure1
+127.0.0.1      FALSE   /secure2/       TRUE    0       sec2value       secure2
+127.0.0.1      FALSE   /secure3/       TRUE    0       sec3value       secure3
+127.0.0.1      FALSE   /secure4/       TRUE    0       sec4value       secure4
+127.0.0.1      FALSE   /secure5/       TRUE    0       sec5value       secure5
+127.0.0.1      FALSE   /secure6/       TRUE    0       sec6value       secure6
+127.0.0.1      FALSE   /secure7/       TRUE    0       sec7value       secure7
+127.0.0.1      FALSE   /secure8/       TRUE    0       sec8value       secure8
+127.0.0.1      FALSE   /secure9/       TRUE    0       secure  very1
+#HttpOnly_127.0.0.1    FALSE   /p1/    FALSE   0       httpo1  value1
+#HttpOnly_127.0.0.1    FALSE   /p2/    FALSE   0       httpo2  value2
+#HttpOnly_127.0.0.1    FALSE   /p3/    FALSE   0       httpo3  value3
+#HttpOnly_127.0.0.1    FALSE   /p4/    FALSE   0       httpo4  value4
+#HttpOnly_127.0.0.1    FALSE   /p4/    FALSE   0       httponly        myvalue1
+#HttpOnly_127.0.0.1    FALSE   /p4/    TRUE    0       httpandsec      myvalue2
+#HttpOnly_127.0.0.1    FALSE   /p4/    TRUE    0       httpandsec2     myvalue3
+#HttpOnly_127.0.0.1    FALSE   /p4/    TRUE    0       httpandsec3     myvalue4
+#HttpOnly_127.0.0.1    FALSE   /p4/    TRUE    0       httpandsec4     myvalue5
+#HttpOnly_127.0.0.1    FALSE   /p4/    TRUE    0       httpandsec5     myvalue6
+#HttpOnly_127.0.0.1    FALSE   /p4/    TRUE    0       httpandsec6     myvalue7
+#HttpOnly_127.0.0.1    FALSE   /p4/    TRUE    0       httpandsec7     myvalue8
+#HttpOnly_127.0.0.1    FALSE   /p4/    TRUE    0       httpandsec8     myvalue9
+127.0.0.1      FALSE   /       FALSE   0       partmatch       present
 127.0.0.1      FALSE   /we/want/       FALSE   2054030187      nodomain        value
 #HttpOnly_127.0.0.1    FALSE   /silly/ FALSE   0       magic   yessir
-.0.0.1 TRUE    /we/want/       FALSE   0       blexp   yesyes
+127.0.0.1      FALSE   /we/want/       FALSE   0       blexp   yesyes
 </file>
 </verify>
 </testcase>
index 4d5454153dfa83f14e9283616babaf760c22d4aa..030fd55eb12480fb48e851851a205fc8c21af065 100644 (file)
@@ -42,7 +42,8 @@ Set-Cookie: duplicate=test; domain=.0.0.1; domain=.0.0.1; path=/donkey;
 Set-Cookie: cookie=yes; path=/we;
 Set-Cookie: cookie=perhaps; path=/we/want;
 Set-Cookie: nocookie=yes; path=/WE;
-Set-Cookie: blexp=yesyes; domain=.0.0.1; domain=.0.0.1; expiry=totally bad;
+Set-Cookie: blexp=yesyes; domain=%HOSTIP; domain=%HOSTIP; expiry=totally bad;
+Set-Cookie: partialip=nono; domain=.0.0.1;
 
 </file>
 <precheck>