1 From eac573ea9c368f5e3c07de4d5ec5c5d0f84a021a Mon Sep 17 00:00:00 2001 |
|
2 From: Tim Ruehsen <[email protected]> |
|
3 Date: Tue, 19 Aug 2014 21:01:28 +0200 |
|
4 Subject: [PATCH 1/2] cookies: only use full host matches for hosts used as IP |
|
5 address |
|
6 |
|
7 By not detecting and rejecting domain names for partial literal IP |
|
8 addresses properly when parsing received HTTP cookies, libcurl can be |
|
9 fooled to both send cookies to wrong sites and to allow arbitrary sites |
|
10 to set cookies for others. |
|
11 |
|
12 Bug: http://curl.haxx.se/docs/adv_20140910.html |
|
13 --- |
|
14 lib/cookie.c | 50 ++++++++++++++++++++++++++++++++++++++---------- |
|
15 tests/data/test1105 | 3 +-- |
|
16 tests/data/test31 | 55 +++++++++++++++++++++++++++-------------------------- |
|
17 tests/data/test8 | 3 ++- |
|
18 4 files changed, 71 insertions(+), 40 deletions(-) |
|
19 |
|
20 This problem has been fixed upstream in curl version 7.38.0 |
|
21 |
|
22 --- lib/cookie.c.orig 2014-09-04 10:25:26.404578422 -0700 |
|
23 +++ lib/cookie.c 2014-09-04 10:40:04.769726955 -0700 |
|
24 @@ -97,6 +97,7 @@ |
|
25 #include "strtoofft.h" |
|
26 #include "rawstr.h" |
|
27 #include "curl_memrchr.h" |
|
28 +#include "inet_pton.h" |
|
29 |
|
30 /* The last #include file should be: */ |
|
31 #include "memdebug.h" |
|
32 @@ -181,6 +182,27 @@ |
|
33 *str = strdup(newstr); |
|
34 } |
|
35 |
|
36 +/* |
|
37 + * Return true if the given string is an IP(v4|v6) address. |
|
38 + */ |
|
39 +static bool isip(const char *domain) |
|
40 +{ |
|
41 + struct in_addr addr; |
|
42 +#ifdef ENABLE_IPV6 |
|
43 + struct in6_addr addr6; |
|
44 +#endif |
|
45 + |
|
46 + if(Curl_inet_pton(AF_INET, domain, &addr) |
|
47 +#ifdef ENABLE_IPV6 |
|
48 + || Curl_inet_pton(AF_INET6, domain, &addr6) |
|
49 +#endif |
|
50 + ) { |
|
51 + /* domain name given as IP address */ |
|
52 + return TRUE; |
|
53 + } |
|
54 + |
|
55 + return FALSE; |
|
56 +} |
|
57 |
|
58 /**************************************************************************** |
|
59 * |
|
60 @@ -280,6 +302,8 @@ |
|
61 } |
|
62 } |
|
63 else if(Curl_raw_equal("domain", name)) { |
|
64 + bool is_ip; |
|
65 + |
|
66 /* note that this name may or may not have a preceeding dot, but |
|
67 we don't care about that, we treat the names the same anyway */ |
|
68 |
|
69 @@ -321,18 +345,19 @@ |
|
70 if('.' == whatptr[0]) |
|
71 whatptr++; /* ignore preceeding dot */ |
|
72 |
|
73 - if(!domain || tailmatch(whatptr, domain)) { |
|
74 - const char *tailptr=whatptr; |
|
75 - if(tailptr[0] == '.') |
|
76 - tailptr++; |
|
77 - strstore(&co->domain, tailptr); /* don't prefix w/dots |
|
78 - internally */ |
|
79 + is_ip = isip(domain ? domain : whatptr); |
|
80 + |
|
81 + if(!domain |
|
82 + || (is_ip && !strcmp(whatptr, domain)) |
|
83 + || (!is_ip && tailmatch(whatptr, domain))) { |
|
84 + strstore(&co->domain, whatptr); |
|
85 if(!co->domain) { |
|
86 badcookie = TRUE; |
|
87 break; |
|
88 } |
|
89 - co->tailmatch=TRUE; /* we always do that if the domain name was |
|
90 - given */ |
|
91 + if(!is_ip) |
|
92 + co->tailmatch=TRUE; /* we always do that if the domain name |
|
93 + was given */ |
|
94 } |
|
95 else { |
|
96 /* we did not get a tailmatch and then the attempted set domain |
|
97 @@ -821,10 +846,14 @@ |
|
98 time_t now = time(NULL); |
|
99 struct Cookie *mainco=NULL; |
|
100 size_t matches = 0; |
|
101 + bool is_ip; |
|
102 |
|
103 if(!c || !c->cookies) |
|
104 return NULL; /* no cookie struct or no cookies in the struct */ |
|
105 |
|
106 + /* check if host is an IP(v4|v6) address */ |
|
107 + is_ip = isip(host); |
|
108 + |
|
109 co = c->cookies; |
|
110 |
|
111 while(co) { |
|
112 @@ -836,8 +865,8 @@ |
|
113 |
|
114 /* now check if the domain is correct */ |
|
115 if(!co->domain || |
|
116 - (co->tailmatch && tailmatch(co->domain, host)) || |
|
117 - (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) { |
|
118 + (co->tailmatch && !is_ip && tailmatch(co->domain, host)) || |
|
119 + ((!co->tailmatch || is_ip) && Curl_raw_equal(host, co->domain)) ) { |
|
120 /* the right part of the host matches the domain stuff in the |
|
121 cookie data */ |
|
122 |
|
123 --- tests/data/test1105.orig 2014-09-04 10:27:20.052223915 -0700 |
|
124 +++ tests/data/test1105 2014-09-04 10:41:18.310085197 -0700 |
|
125 @@ -56,8 +56,7 @@ |
|
126 # This file was generated by libcurl! Edit at your own risk. |
|
127 |
|
128 127.0.0.1 FALSE /we/want/ FALSE 0 foobar name |
|
129 -.127.0.0.1 TRUE "/silly/" FALSE 0 mismatch this |
|
130 -.0.0.1 TRUE / FALSE 0 partmatch present |
|
131 +127.0.0.1 FALSE "/silly/" FALSE 0 mismatch this |
|
132 </file> |
|
133 </verify> |
|
134 </testcase> |
|
135 --- tests/data/test31.orig 2014-09-04 10:27:10.395450839 -0700 |
|
136 +++ tests/data/test31 2014-09-04 11:27:38.685969246 -0700 |
|
137 @@ -18,6 +18,30 @@ |
|
138 Funny-head: yesyes |
|
139 Set-Cookie: foobar=name; domain=anything.com; path=/ ; secure |
|
140 Set-Cookie:ismatch=this ; domain=127.0.0.1; path=/silly/ |
|
141 +Set-Cookie: overwrite=this ; domain=127.0.0.1; path=/overwrite/ |
|
142 +Set-Cookie: overwrite=this2 ; domain=127.0.0.1; path=/overwrite |
|
143 +Set-Cookie: sec1value=secure1 ; domain=127.0.0.1; path=/secure1/ ; secure |
|
144 +Set-Cookie: sec2value=secure2 ; domain=127.0.0.1; path=/secure2/ ; secure= |
|
145 +Set-Cookie: sec3value=secure3 ; domain=127.0.0.1; path=/secure3/ ; secure= |
|
146 +Set-Cookie: sec4value=secure4 ; secure=; domain=127.0.0.1; path=/secure4/ ; |
|
147 +Set-Cookie: sec5value=secure5 ; secure; domain=127.0.0.1; path=/secure5/ ; |
|
148 +Set-Cookie: sec6value=secure6 ; secure ; domain=127.0.0.1; path=/secure6/ ; |
|
149 +Set-Cookie: sec7value=secure7 ; secure ; domain=127.0.0.1; path=/secure7/ ; |
|
150 +Set-Cookie: sec8value=secure8 ; secure= ; domain=127.0.0.1; path=/secure8/ ; |
|
151 +Set-Cookie: secure=very1 ; secure=; domain=127.0.0.1; path=/secure9/; |
|
152 +Set-Cookie: httpo1=value1 ; domain=127.0.0.1; path=/p1/; httponly |
|
153 +Set-Cookie: httpo2=value2 ; domain=127.0.0.1; path=/p2/; httponly= |
|
154 +Set-Cookie: httpo3=value3 ; httponly; domain=127.0.0.1; path=/p3/; |
|
155 +Set-Cookie: httpo4=value4 ; httponly=; domain=127.0.0.1; path=/p4/; |
|
156 +Set-Cookie: httponly=myvalue1 ; domain=127.0.0.1; path=/p4/; httponly |
|
157 +Set-Cookie: httpandsec=myvalue2 ; domain=127.0.0.1; path=/p4/; httponly; secure |
|
158 +Set-Cookie: httpandsec2=myvalue3; domain=127.0.0.1; path=/p4/; httponly=; secure |
|
159 +Set-Cookie: httpandsec3=myvalue4 ; domain=127.0.0.1; path=/p4/; httponly; secure= |
|
160 +Set-Cookie: httpandsec4=myvalue5 ; domain=127.0.0.1; path=/p4/; httponly=; secure= |
|
161 +Set-Cookie: httpandsec5=myvalue6 ; domain=127.0.0.1; path=/p4/; secure; httponly= |
|
162 +Set-Cookie: httpandsec6=myvalue7 ; domain=127.0.0.1; path=/p4/; secure=; httponly= |
|
163 +Set-Cookie: httpandsec7=myvalue8 ; domain=127.0.0.1; path=/p4/; secure; httponly |
|
164 +Set-Cookie: httpandsec8=myvalue9; domain=127.0.0.1; path=/p4/; secure=; httponly |
|
165 Set-Cookie: partmatch=present; domain=127.0.0.1 ; path=/; |
|
166 Set-Cookie:eat=this; domain=moo.foo.moo; |
|
167 Set-Cookie: eat=this-too; domain=.foo.moo; |
|
168 @@ -27,7 +51,8 @@ |
|
169 Set-Cookie: test=yes; domain=foo.com; expires=Sat Feb 2 11:56:27 GMT 2030 |
|
170 Set-Cookie: test2=yes; domain=se; expires=Sat Feb 2 11:56:27 GMT 2030 |
|
171 Set-Cookie: magic=yessir; path=/silly/; HttpOnly |
|
172 -Set-Cookie: blexp=yesyes; domain=.0.0.1; domain=.0.0.1; expiry=totally bad; |
|
173 +Set-Cookie: blexp=yesyes; domain=127.0.0.1; domain=127.0.0.1; expiry=totally bad; |
|
174 +Set-Cookie: partialip=nono; domain=.0.0.1; |
|
175 |
|
176 boo |
|
177 </data> |
|
178 @@ -50,6 +75,9 @@ |
|
179 <command> |
|
180 http://%HOSTIP:%HTTPPORT/we/want/31 -b none -c log/jar31.txt |
|
181 </command> |
|
182 +<precheck> |
|
183 +perl -e 'if ("%HOSTIP" !~ /127\.0\.0\.1$/) {print "Test only works for HOSTIP 127.0.0.1"; exit(1)}' |
|
184 +</precheck> |
|
185 </client> |
|
186 |
|
187 # Verify data after the test has been "shot" |
|
188 @@ -68,11 +96,35 @@ |
|
189 # http://curl.haxx.se/rfc/cookie_spec.html |
|
190 # This file was generated by libcurl! Edit at your own risk. |
|
191 |
|
192 -.127.0.0.1 TRUE /silly/ FALSE 0 ismatch this |
|
193 -.127.0.0.1 TRUE / FALSE 0 partmatch present |
|
194 +127.0.0.1 FALSE /silly/ FALSE 0 ismatch this |
|
195 +127.0.0.1 FALSE /overwrite/ FALSE 0 overwrite this |
|
196 +127.0.0.1 FALSE /overwrite FALSE 0 overwrite this2 |
|
197 +127.0.0.1 FALSE /secure1/ TRUE 0 sec1value secure1 |
|
198 +127.0.0.1 FALSE /secure2/ FALSE 0 sec2value secure2 |
|
199 +127.0.0.1 FALSE /secure3/ FALSE 0 sec3value secure3 |
|
200 +127.0.0.1 FALSE /secure4/ FALSE 0 sec4value secure4 |
|
201 +127.0.0.1 FALSE /secure5/ TRUE 0 sec5value secure5 |
|
202 +127.0.0.1 FALSE /secure6/ FALSE 0 sec6value secure6 |
|
203 +127.0.0.1 FALSE /secure7/ FALSE 0 sec7value secure7 |
|
204 +127.0.0.1 FALSE /secure8/ FALSE 0 sec8value secure8 |
|
205 +127.0.0.1 FALSE /secure9/ FALSE 0 secure very1 |
|
206 +#HttpOnly_127.0.0.1 FALSE /p1/ FALSE 0 httpo1 value1 |
|
207 +127.0.0.1 FALSE /p2/ FALSE 0 httpo2 value2 |
|
208 +#HttpOnly_127.0.0.1 FALSE /p3/ FALSE 0 httpo3 value3 |
|
209 +127.0.0.1 FALSE /p4/ FALSE 0 httpo4 value4 |
|
210 +#HttpOnly_127.0.0.1 FALSE /p4/ FALSE 0 httponly myvalue1 |
|
211 +#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec myvalue2 |
|
212 +127.0.0.1 FALSE /p4/ TRUE 0 httpandsec2 myvalue3 |
|
213 +#HttpOnly_127.0.0.1 FALSE /p4/ FALSE 0 httpandsec3 myvalue4 |
|
214 +127.0.0.1 FALSE /p4/ FALSE 0 httpandsec4 myvalue5 |
|
215 +127.0.0.1 FALSE /p4/ TRUE 0 httpandsec5 myvalue6 |
|
216 +127.0.0.1 FALSE /p4/ FALSE 0 httpandsec6 myvalue7 |
|
217 +#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec7 myvalue8 |
|
218 +#HttpOnly_127.0.0.1 FALSE /p4/ FALSE 0 httpandsec8 myvalue9 |
|
219 +127.0.0.1 FALSE / FALSE 0 partmatch present |
|
220 127.0.0.1 FALSE /we/want/ FALSE 2054030187 nodomain value |
|
221 #HttpOnly_127.0.0.1 FALSE /silly/ FALSE 0 magic yessir |
|
222 -.0.0.1 TRUE /we/want/ FALSE 0 blexp yesyes |
|
223 +127.0.0.1 FALSE /we/want/ FALSE 0 blexp yesyes |
|
224 </file> |
|
225 </verify> |
|
226 </testcase> |
|
227 --- tests/data/test8.orig 2014-09-04 10:27:04.885231628 -0700 |
|
228 +++ tests/data/test8 2014-09-04 10:41:44.133914601 -0700 |
|
229 @@ -35,16 +35,20 @@ |
|
230 Server: test-server/fake |
|
231 Content-Type: text/html |
|
232 Funny-head: yesyes |
|
233 -Set-Cookie: foobar=name; domain=127.0.0.1; path=/; |
|
234 -Set-Cookie: mismatch=this; domain=127.0.0.1; path="/silly/"; |
|
235 +Set-Cookie: foobar=name; domain=%HOSTIP; path=/; |
|
236 +Set-Cookie: mismatch=this; domain=%HOSTIP; path="/silly/"; |
|
237 Set-Cookie: partmatch=present; domain=.0.0.1; path=/w; |
|
238 Set-Cookie: duplicate=test; domain=.0.0.1; domain=.0.0.1; path=/donkey; |
|
239 Set-Cookie: cookie=yes; path=/we; |
|
240 Set-Cookie: cookie=perhaps; path=/we/want; |
|
241 Set-Cookie: nocookie=yes; path=/WE; |
|
242 -Set-Cookie: blexp=yesyes; domain=.0.0.1; domain=.0.0.1; expiry=totally bad; |
|
243 +Set-Cookie: blexp=yesyes; domain=%HOSTIP; domain=%HOSTIP; expiry=totally bad; |
|
244 +Set-Cookie: partialip=nono; domain=.0.0.1; |
|
245 |
|
246 </file> |
|
247 +<precheck> |
|
248 +perl -e 'if ("%HOSTIP" !~ /\.0\.0\.1$/) {print "Test only works for HOSTIPs ending with .0.0.1"; exit(1)}' |
|
249 +</precheck> |
|
250 </client> |
|
251 |
|
252 # Verify data after the test has been "shot" |
|
253 @@ -56,7 +60,7 @@ |
|
254 GET /we/want/8 HTTP/1.1 |
|
255 Host: %HOSTIP:%HTTPPORT |
|
256 Accept: */* |
|
257 -Cookie: cookie=perhaps; cookie=yes; partmatch=present; foobar=name; blexp=yesyes |
|
258 +Cookie: cookie=perhaps; cookie=yes; foobar=name; blexp=yesyes |
|
259 |
|
260 </protocol> |
|
261 </verify> |
|