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-02 16:10:55.940825864 -0700 |
|
23 +++ lib/cookie.c 2014-09-02 16:32:39.899617696 -0700 |
|
24 @@ -94,6 +94,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 @@ -178,6 +179,28 @@ |
|
33 } |
|
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 * Curl_cookie_add() |
|
61 @@ -290,6 +313,8 @@ |
|
62 } |
|
63 } |
|
64 else if(Curl_raw_equal("domain", name)) { |
|
65 + bool is_ip; |
|
66 + |
|
67 /* note that this name may or may not have a preceding dot, but |
|
68 we don't care about that, we treat the names the same anyway */ |
|
69 |
|
70 @@ -333,18 +358,19 @@ |
|
71 if('.' == whatptr[0]) |
|
72 whatptr++; /* ignore preceding dot */ |
|
73 |
|
74 - if(!domain || tailmatch(whatptr, domain)) { |
|
75 - const char *tailptr=whatptr; |
|
76 - if(tailptr[0] == '.') |
|
77 - tailptr++; |
|
78 - strstore(&co->domain, tailptr); /* don't prefix w/dots |
|
79 - internally */ |
|
80 + is_ip = isip(domain ? domain : whatptr); |
|
81 + |
|
82 + if(!domain |
|
83 + || (is_ip && !strcmp(whatptr, domain)) |
|
84 + || (!is_ip && tailmatch(whatptr, domain))) { |
|
85 + strstore(&co->domain, whatptr); |
|
86 if(!co->domain) { |
|
87 badcookie = TRUE; |
|
88 break; |
|
89 } |
|
90 - co->tailmatch=TRUE; /* we always do that if the domain name was |
|
91 - given */ |
|
92 + if(!is_ip) |
|
93 + co->tailmatch=TRUE; /* we always do that if the domain name was |
|
94 + given */ |
|
95 } |
|
96 else { |
|
97 /* we did not get a tailmatch and then the attempted set domain |
|
98 @@ -819,10 +845,14 @@ |
|
99 time_t now = time(NULL); |
|
100 struct Cookie *mainco=NULL; |
|
101 size_t matches = 0; |
|
102 + bool is_ip; |
|
103 |
|
104 if(!c || !c->cookies) |
|
105 return NULL; /* no cookie struct or no cookies in the struct */ |
|
106 |
|
107 + /* check if host is an IP(v4|v6) address */ |
|
108 + is_ip = isip(host); |
|
109 + |
|
110 co = c->cookies; |
|
111 |
|
112 while(co) { |
|
113 @@ -834,8 +864,8 @@ |
|
114 |
|
115 /* now check if the domain is correct */ |
|
116 if(!co->domain || |
|
117 - (co->tailmatch && tailmatch(co->domain, host)) || |
|
118 - (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) { |
|
119 + (co->tailmatch && !is_ip && tailmatch(co->domain, host)) || |
|
120 + ((!co->tailmatch || is_ip) && Curl_raw_equal(host, co->domain)) ) { |
|
121 /* the right part of the host matches the domain stuff in the |
|
122 cookie data */ |
|
123 |
|
124 --- tests/data/test1105.orig 2014-09-02 16:11:45.732615643 -0700 |
|
125 +++ tests/data/test1105 2014-09-02 16:33:42.523906352 -0700 |
|
126 @@ -56,8 +56,7 @@ |
|
127 # This file was generated by libcurl! Edit at your own risk. |
|
128 |
|
129 127.0.0.1 FALSE /we/want/ FALSE 0 foobar name |
|
130 -.127.0.0.1 TRUE "/silly/" FALSE 0 mismatch this |
|
131 -.0.0.1 TRUE / FALSE 0 partmatch present |
|
132 +127.0.0.1 FALSE "/silly/" FALSE 0 mismatch this |
|
133 </file> |
|
134 </verify> |
|
135 </testcase> |
|
136 --- tests/data/test31.orig 2014-09-02 16:11:56.912528200 -0700 |
|
137 +++ tests/data/test31 2014-09-04 06:13:16.741533782 -0700 |
|
138 @@ -18,27 +18,29 @@ |
|
139 Funny-head: yesyes |
|
140 Set-Cookie: foobar=name; domain=anything.com; path=/ ; secure |
|
141 Set-Cookie:ismatch=this ; domain=127.0.0.1; path=/silly/ |
|
142 +Set-Cookie: overwrite=this ; domain=127.0.0.1; path=/overwrite/ |
|
143 +Set-Cookie: overwrite=this2 ; domain=127.0.0.1; path=/overwrite |
|
144 Set-Cookie: sec1value=secure1 ; domain=127.0.0.1; path=/secure1/ ; secure |
|
145 Set-Cookie: sec2value=secure2 ; domain=127.0.0.1; path=/secure2/ ; secure= |
|
146 Set-Cookie: sec3value=secure3 ; domain=127.0.0.1; path=/secure3/ ; secure= |
|
147 -Set-Cookie: sec4value=secure4 ; secure=; domain=127.0.0.1; path=/secure4/ ; |
|
148 -Set-Cookie: sec5value=secure5 ; secure; domain=127.0.0.1; path=/secure5/ ; |
|
149 -Set-Cookie: sec6value=secure6 ; secure ; domain=127.0.0.1; path=/secure6/ ; |
|
150 -Set-Cookie: sec7value=secure7 ; secure ; domain=127.0.0.1; path=/secure7/ ; |
|
151 -Set-Cookie: sec8value=secure8 ; secure= ; domain=127.0.0.1; path=/secure8/ ; |
|
152 -Set-Cookie: secure=very1 ; secure=; domain=127.0.0.1; path=/secure9/; |
|
153 -Set-Cookie: httpo1=value1 ; domain=127.0.0.1; path=/p1/; httponly |
|
154 -Set-Cookie: httpo2=value2 ; domain=127.0.0.1; path=/p2/; httponly= |
|
155 -Set-Cookie: httpo3=value3 ; httponly; domain=127.0.0.1; path=/p3/; |
|
156 -Set-Cookie: httpo4=value4 ; httponly=; domain=127.0.0.1; path=/p4/; |
|
157 -Set-Cookie: httponly=myvalue1 ; domain=127.0.0.1; path=/p4/; httponly |
|
158 -Set-Cookie: httpandsec=myvalue2 ; domain=127.0.0.1; path=/p4/; httponly; secure |
|
159 -Set-Cookie: httpandsec2=myvalue3; domain=127.0.0.1; path=/p4/; httponly=; secure |
|
160 -Set-Cookie: httpandsec3=myvalue4 ; domain=127.0.0.1; path=/p4/; httponly; secure= |
|
161 -Set-Cookie: httpandsec4=myvalue5 ; domain=127.0.0.1; path=/p4/; httponly=; secure= |
|
162 -Set-Cookie: httpandsec5=myvalue6 ; domain=127.0.0.1; path=/p4/; secure; httponly= |
|
163 -Set-Cookie: httpandsec6=myvalue7 ; domain=127.0.0.1; path=/p4/; secure=; httponly= |
|
164 -Set-Cookie: httpandsec7=myvalue8 ; domain=127.0.0.1; path=/p4/; secure; httponly |
|
165 +Set-Cookie: sec4value=secure4 ; secure=; domain=127.0.0.1; path=/secure4/ ; |
|
166 +Set-Cookie: sec5value=secure5 ; secure; domain=127.0.0.1; path=/secure5/ ; |
|
167 +Set-Cookie: sec6value=secure6 ; secure ; domain=127.0.0.1; path=/secure6/ ; |
|
168 +Set-Cookie: sec7value=secure7 ; secure ; domain=127.0.0.1; path=/secure7/ ; |
|
169 +Set-Cookie: sec8value=secure8 ; secure= ; domain=127.0.0.1; path=/secure8/ ; |
|
170 +Set-Cookie: secure=very1 ; secure=; domain=127.0.0.1; path=/secure9/; |
|
171 +Set-Cookie: httpo1=value1 ; domain=127.0.0.1; path=/p1/; httponly |
|
172 +Set-Cookie: httpo2=value2 ; domain=127.0.0.1; path=/p2/; httponly= |
|
173 +Set-Cookie: httpo3=value3 ; httponly; domain=127.0.0.1; path=/p3/; |
|
174 +Set-Cookie: httpo4=value4 ; httponly=; domain=127.0.0.1; path=/p4/; |
|
175 +Set-Cookie: httponly=myvalue1 ; domain=127.0.0.1; path=/p4/; httponly |
|
176 +Set-Cookie: httpandsec=myvalue2 ; domain=127.0.0.1; path=/p4/; httponly; secure |
|
177 +Set-Cookie: httpandsec2=myvalue3; domain=127.0.0.1; path=/p4/; httponly=; secure |
|
178 +Set-Cookie: httpandsec3=myvalue4 ; domain=127.0.0.1; path=/p4/; httponly; secure= |
|
179 +Set-Cookie: httpandsec4=myvalue5 ; domain=127.0.0.1; path=/p4/; httponly=; secure= |
|
180 +Set-Cookie: httpandsec5=myvalue6 ; domain=127.0.0.1; path=/p4/; secure; httponly= |
|
181 +Set-Cookie: httpandsec6=myvalue7 ; domain=127.0.0.1; path=/p4/; secure=; httponly= |
|
182 +Set-Cookie: httpandsec7=myvalue8 ; domain=127.0.0.1; path=/p4/; secure; httponly |
|
183 Set-Cookie: httpandsec8=myvalue9; domain=127.0.0.1; path=/p4/; secure=; httponly |
|
184 Set-Cookie: partmatch=present; domain=127.0.0.1 ; path=/; |
|
185 Set-Cookie:eat=this; domain=moo.foo.moo; |
|
186 @@ -49,7 +51,8 @@ |
|
187 Set-Cookie: test=yes; domain=foo.com; expires=Sat Feb 2 11:56:27 GMT 2030 |
|
188 Set-Cookie: test2=yes; domain=se; expires=Sat Feb 2 11:56:27 GMT 2030 |
|
189 Set-Cookie: magic=yessir; path=/silly/; HttpOnly |
|
190 -Set-Cookie: blexp=yesyes; domain=.0.0.1; domain=.0.0.1; expiry=totally bad; |
|
191 +Set-Cookie: blexp=yesyes; domain=127.0.0.1; domain=127.0.0.1; expiry=totally bad; |
|
192 +Set-Cookie: partialip=nono; domain=.0.0.1; |
|
193 |
|
194 boo |
|
195 </data> |
|
196 @@ -72,6 +75,9 @@ |
|
197 <command> |
|
198 http://%HOSTIP:%HTTPPORT/we/want/31 -b none -c log/jar31.txt |
|
199 </command> |
|
200 +<precheck> |
|
201 +perl -e 'if ("%HOSTIP" !~ /127\.0\.0\.1$/) {print "Test only works for HOSTIP 127.0.0.1"; exit(1)}' |
|
202 +</precheck> |
|
203 </client> |
|
204 |
|
205 # Verify data after the test has been "shot" |
|
206 @@ -90,33 +96,35 @@ |
|
207 # http://curl.haxx.se/rfc/cookie_spec.html |
|
208 # This file was generated by libcurl! Edit at your own risk. |
|
209 |
|
210 -.127.0.0.1 TRUE /silly/ FALSE 0 ismatch this |
|
211 -.127.0.0.1 TRUE /secure1/ TRUE 0 sec1value secure1 |
|
212 -.127.0.0.1 TRUE /secure2/ TRUE 0 sec2value secure2 |
|
213 -.127.0.0.1 TRUE /secure3/ TRUE 0 sec3value secure3 |
|
214 -.127.0.0.1 TRUE /secure4/ TRUE 0 sec4value secure4 |
|
215 -.127.0.0.1 TRUE /secure5/ TRUE 0 sec5value secure5 |
|
216 -.127.0.0.1 TRUE /secure6/ TRUE 0 sec6value secure6 |
|
217 -.127.0.0.1 TRUE /secure7/ TRUE 0 sec7value secure7 |
|
218 -.127.0.0.1 TRUE /secure8/ TRUE 0 sec8value secure8 |
|
219 -.127.0.0.1 TRUE /secure9/ TRUE 0 secure very1 |
|
220 -#HttpOnly_.127.0.0.1 TRUE /p1/ FALSE 0 httpo1 value1 |
|
221 -#HttpOnly_.127.0.0.1 TRUE /p2/ FALSE 0 httpo2 value2 |
|
222 -#HttpOnly_.127.0.0.1 TRUE /p3/ FALSE 0 httpo3 value3 |
|
223 -#HttpOnly_.127.0.0.1 TRUE /p4/ FALSE 0 httpo4 value4 |
|
224 -#HttpOnly_.127.0.0.1 TRUE /p4/ FALSE 0 httponly myvalue1 |
|
225 -#HttpOnly_.127.0.0.1 TRUE /p4/ TRUE 0 httpandsec myvalue2 |
|
226 -#HttpOnly_.127.0.0.1 TRUE /p4/ TRUE 0 httpandsec2 myvalue3 |
|
227 -#HttpOnly_.127.0.0.1 TRUE /p4/ TRUE 0 httpandsec3 myvalue4 |
|
228 -#HttpOnly_.127.0.0.1 TRUE /p4/ TRUE 0 httpandsec4 myvalue5 |
|
229 -#HttpOnly_.127.0.0.1 TRUE /p4/ TRUE 0 httpandsec5 myvalue6 |
|
230 -#HttpOnly_.127.0.0.1 TRUE /p4/ TRUE 0 httpandsec6 myvalue7 |
|
231 -#HttpOnly_.127.0.0.1 TRUE /p4/ TRUE 0 httpandsec7 myvalue8 |
|
232 -#HttpOnly_.127.0.0.1 TRUE /p4/ TRUE 0 httpandsec8 myvalue9 |
|
233 -.127.0.0.1 TRUE / FALSE 0 partmatch present |
|
234 +127.0.0.1 FALSE /silly/ FALSE 0 ismatch this |
|
235 +127.0.0.1 FALSE /overwrite/ FALSE 0 overwrite this |
|
236 +127.0.0.1 FALSE /overwrite FALSE 0 overwrite this2 |
|
237 +127.0.0.1 FALSE /secure1/ TRUE 0 sec1value secure1 |
|
238 +127.0.0.1 FALSE /secure2/ TRUE 0 sec2value secure2 |
|
239 +127.0.0.1 FALSE /secure3/ TRUE 0 sec3value secure3 |
|
240 +127.0.0.1 FALSE /secure4/ TRUE 0 sec4value secure4 |
|
241 +127.0.0.1 FALSE /secure5/ TRUE 0 sec5value secure5 |
|
242 +127.0.0.1 FALSE /secure6/ TRUE 0 sec6value secure6 |
|
243 +127.0.0.1 FALSE /secure7/ TRUE 0 sec7value secure7 |
|
244 +127.0.0.1 FALSE /secure8/ TRUE 0 sec8value secure8 |
|
245 +127.0.0.1 FALSE /secure9/ TRUE 0 secure very1 |
|
246 +#HttpOnly_127.0.0.1 FALSE /p1/ FALSE 0 httpo1 value1 |
|
247 +#HttpOnly_127.0.0.1 FALSE /p2/ FALSE 0 httpo2 value2 |
|
248 +#HttpOnly_127.0.0.1 FALSE /p3/ FALSE 0 httpo3 value3 |
|
249 +#HttpOnly_127.0.0.1 FALSE /p4/ FALSE 0 httpo4 value4 |
|
250 +#HttpOnly_127.0.0.1 FALSE /p4/ FALSE 0 httponly myvalue1 |
|
251 +#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec myvalue2 |
|
252 +#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec2 myvalue3 |
|
253 +#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec3 myvalue4 |
|
254 +#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec4 myvalue5 |
|
255 +#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec5 myvalue6 |
|
256 +#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec6 myvalue7 |
|
257 +#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec7 myvalue8 |
|
258 +#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec8 myvalue9 |
|
259 +127.0.0.1 FALSE / FALSE 0 partmatch present |
|
260 127.0.0.1 FALSE /we/want/ FALSE 2054030187 nodomain value |
|
261 #HttpOnly_127.0.0.1 FALSE /silly/ FALSE 0 magic yessir |
|
262 -.0.0.1 TRUE /we/want/ FALSE 0 blexp yesyes |
|
263 +127.0.0.1 FALSE /we/want/ FALSE 0 blexp yesyes |
|
264 </file> |
|
265 </verify> |
|
266 </testcase> |
|
267 --- tests/data/test8.orig 2014-09-02 16:13:07.812626284 -0700 |
|
268 +++ tests/data/test8 2014-09-02 17:24:33.131678950 -0700 |
|
269 @@ -35,16 +35,20 @@ |
|
270 Server: test-server/fake |
|
271 Content-Type: text/html |
|
272 Funny-head: yesyes |
|
273 -Set-Cookie: foobar=name; domain=127.0.0.1; path=/; |
|
274 -Set-Cookie: mismatch=this; domain=127.0.0.1; path="/silly/"; |
|
275 +Set-Cookie: foobar=name; domain=%HOSTIP; path=/; |
|
276 +Set-Cookie: mismatch=this; domain=%HOSTIP; path="/silly/"; |
|
277 Set-Cookie: partmatch=present; domain=.0.0.1; path=/w; |
|
278 Set-Cookie: duplicate=test; domain=.0.0.1; domain=.0.0.1; path=/donkey; |
|
279 Set-Cookie: cookie=yes; path=/we; |
|
280 Set-Cookie: cookie=perhaps; path=/we/want; |
|
281 Set-Cookie: nocookie=yes; path=/WE; |
|
282 -Set-Cookie: blexp=yesyes; domain=.0.0.1; domain=.0.0.1; expiry=totally bad; |
|
283 +Set-Cookie: blexp=yesyes; domain=%HOSTIP; domain=%HOSTIP; expiry=totally bad; |
|
284 +Set-Cookie: partialip=nono; domain=.0.0.1; |
|
285 |
|
286 </file> |
|
287 +<precheck> |
|
288 +perl -e 'if ("%HOSTIP" !~ /\.0\.0\.1$/) {print "Test only works for HOSTIPs ending with .0.0.1"; exit(1)}' |
|
289 +</precheck> |
|
290 </client> |
|
291 |
|
292 # Verify data after the test has been "shot" |
|
293 @@ -56,7 +60,7 @@ |
|
294 GET /we/want/8 HTTP/1.1 |
|
295 Host: %HOSTIP:%HTTPPORT |
|
296 Accept: */* |
|
297 -Cookie: cookie=perhaps; cookie=yes; partmatch=present; foobar=name; blexp=yesyes |
|
298 +Cookie: cookie=perhaps; cookie=yes; foobar=name; blexp=yesyes |
|
299 |
|
300 </protocol> |
|
301 </verify> |
|