diff options
author | Armin Kuster <akuster@mvista.com> | 2016-02-05 08:57:11 -0800 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-02-07 22:55:24 +0000 |
commit | f4341a9b6fef02285f368e43585e5cd0452fe728 (patch) | |
tree | 4f34c144b6215b5884b93c11c08bd995b4791943 /meta/recipes-support/curl | |
parent | 35f4306ed4c505c8cc4d756e209dfd4eb0fbe424 (diff) | |
download | poky-f4341a9b6fef02285f368e43585e5cd0452fe728.tar.gz |
curl: Security fix CVE-2016-0754
CVE-2016-0754 curl: remote file name path traversal in curl tool for Windows
(From OE-Core rev: b2c9b48dea2fd968c307a809ff95f2e686435222)
Signed-off-by: Armin Kuster <akuster@mvista.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-support/curl')
-rw-r--r-- | meta/recipes-support/curl/curl/CVE-2016-0754.patch | 417 | ||||
-rw-r--r-- | meta/recipes-support/curl/curl_7.44.0.bb | 3 |
2 files changed, 419 insertions, 1 deletions
diff --git a/meta/recipes-support/curl/curl/CVE-2016-0754.patch b/meta/recipes-support/curl/curl/CVE-2016-0754.patch new file mode 100644 index 0000000000..f0402de081 --- /dev/null +++ b/meta/recipes-support/curl/curl/CVE-2016-0754.patch | |||
@@ -0,0 +1,417 @@ | |||
1 | From b1bb4ca6d8777683b6a549fb61dba36759da26f4 Mon Sep 17 00:00:00 2001 | ||
2 | From: Ray Satiro <raysatiro@yahoo.com> | ||
3 | Date: Tue, 26 Jan 2016 23:23:15 +0100 | ||
4 | Subject: [PATCH] curl: avoid local drive traversal when saving file (Windows) | ||
5 | |||
6 | curl does not sanitize colons in a remote file name that is used as the | ||
7 | local file name. This may lead to a vulnerability on systems where the | ||
8 | colon is a special path character. Currently Windows/DOS is the only OS | ||
9 | where this vulnerability applies. | ||
10 | |||
11 | CVE-2016-0754 | ||
12 | |||
13 | Bug: http://curl.haxx.se/docs/adv_20160127B.html | ||
14 | |||
15 | Upstream-Status: Backport | ||
16 | http://curl.haxx.se/CVE-2016-0754.patch | ||
17 | |||
18 | CVE: CVE-2016-0754 | ||
19 | Signed-off-by: Armin Kuster <akuster@mvista.com> | ||
20 | |||
21 | --- | ||
22 | src/tool_cb_hdr.c | 40 ++++++------ | ||
23 | src/tool_doswin.c | 174 ++++++++++++++++++++++++++++++++++++++++++++--------- | ||
24 | src/tool_doswin.h | 2 +- | ||
25 | src/tool_operate.c | 29 ++++++--- | ||
26 | 4 files changed, 187 insertions(+), 58 deletions(-) | ||
27 | |||
28 | diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c | ||
29 | index fd208e8..0fca39f 100644 | ||
30 | --- a/src/tool_cb_hdr.c | ||
31 | +++ b/src/tool_cb_hdr.c | ||
32 | @@ -26,10 +26,11 @@ | ||
33 | #define ENABLE_CURLX_PRINTF | ||
34 | /* use our own printf() functions */ | ||
35 | #include "curlx.h" | ||
36 | |||
37 | #include "tool_cfgable.h" | ||
38 | +#include "tool_doswin.h" | ||
39 | #include "tool_msgs.h" | ||
40 | #include "tool_cb_hdr.h" | ||
41 | |||
42 | #include "memdebug.h" /* keep this as LAST include */ | ||
43 | |||
44 | @@ -112,22 +113,28 @@ size_t tool_header_cb(void *ptr, size_t size, size_t nmemb, void *userdata) | ||
45 | /* this expression below typecasts 'cb' only to avoid | ||
46 | warning: signed and unsigned type in conditional expression | ||
47 | */ | ||
48 | len = (ssize_t)cb - (p - str); | ||
49 | filename = parse_filename(p, len); | ||
50 | - if(filename) { | ||
51 | - outs->filename = filename; | ||
52 | - outs->alloc_filename = TRUE; | ||
53 | - outs->is_cd_filename = TRUE; | ||
54 | - outs->s_isreg = TRUE; | ||
55 | - outs->fopened = FALSE; | ||
56 | - outs->stream = NULL; | ||
57 | - hdrcbdata->honor_cd_filename = FALSE; | ||
58 | - break; | ||
59 | - } | ||
60 | - else | ||
61 | + if(!filename) | ||
62 | + return failure; | ||
63 | + | ||
64 | +#if defined(MSDOS) || defined(WIN32) | ||
65 | + if(sanitize_file_name(&filename)) { | ||
66 | + free(filename); | ||
67 | return failure; | ||
68 | + } | ||
69 | +#endif /* MSDOS || WIN32 */ | ||
70 | + | ||
71 | + outs->filename = filename; | ||
72 | + outs->alloc_filename = TRUE; | ||
73 | + outs->is_cd_filename = TRUE; | ||
74 | + outs->s_isreg = TRUE; | ||
75 | + outs->fopened = FALSE; | ||
76 | + outs->stream = NULL; | ||
77 | + hdrcbdata->honor_cd_filename = FALSE; | ||
78 | + break; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | return cb; | ||
83 | } | ||
84 | @@ -179,19 +186,16 @@ static char *parse_filename(const char *ptr, size_t len) | ||
85 | return NULL; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | /* scan for the end letter and stop there */ | ||
90 | - q = p; | ||
91 | - while(*q) { | ||
92 | - if(q[1] && (q[0] == '\\')) | ||
93 | - q++; | ||
94 | - else if(q[0] == stop) | ||
95 | + for(q = p; *q; ++q) { | ||
96 | + if(*q == stop) { | ||
97 | + *q = '\0'; | ||
98 | break; | ||
99 | - q++; | ||
100 | + } | ||
101 | } | ||
102 | - *q = '\0'; | ||
103 | |||
104 | /* make sure the file name doesn't end in \r or \n */ | ||
105 | q = strchr(p, '\r'); | ||
106 | if(q) | ||
107 | *q = '\0'; | ||
108 | diff --git a/src/tool_doswin.c b/src/tool_doswin.c | ||
109 | index dd6e8bb..9c6a7a3 100644 | ||
110 | --- a/src/tool_doswin.c | ||
111 | +++ b/src/tool_doswin.c | ||
112 | @@ -83,46 +83,110 @@ __pragma(warning(pop)) | ||
113 | # define _use_lfn(f) ALWAYS_FALSE /* long file names never available */ | ||
114 | #elif defined(__DJGPP__) | ||
115 | # include <fcntl.h> /* _use_lfn(f) prototype */ | ||
116 | #endif | ||
117 | |||
118 | -static const char *msdosify (const char *file_name); | ||
119 | -static char *rename_if_dos_device_name (char *file_name); | ||
120 | +static char *msdosify(const char *file_name); | ||
121 | +static char *rename_if_dos_device_name(const char *file_name); | ||
122 | |||
123 | -/* | ||
124 | - * sanitize_dos_name: returns a newly allocated string holding a | ||
125 | - * valid file name which will be a transformation of given argument | ||
126 | - * in case this wasn't already a valid file name. | ||
127 | - * | ||
128 | - * This function takes ownership of given argument, free'ing it before | ||
129 | - * returning. Caller is responsible of free'ing returned string. Upon | ||
130 | - * out of memory condition function returns NULL. | ||
131 | - */ | ||
132 | |||
133 | -char *sanitize_dos_name(char *file_name) | ||
134 | +/* | ||
135 | +Sanitize *file_name. | ||
136 | +Success: (CURLE_OK) *file_name points to a sanitized version of the original. | ||
137 | + This function takes ownership of the original *file_name and frees it. | ||
138 | +Failure: (!= CURLE_OK) *file_name is unchanged. | ||
139 | +*/ | ||
140 | +CURLcode sanitize_file_name(char **file_name) | ||
141 | { | ||
142 | - char new_name[PATH_MAX]; | ||
143 | + size_t len; | ||
144 | + char *p, *sanitized; | ||
145 | + | ||
146 | + /* Calculate the maximum length of a filename. | ||
147 | + FILENAME_MAX is often the same as PATH_MAX, in other words it does not | ||
148 | + discount the path information. PATH_MAX size is calculated based on: | ||
149 | + <drive-letter><colon><path-sep><max-filename-len><NULL> */ | ||
150 | + const size_t max_filename_len = PATH_MAX - 3 - 1; | ||
151 | + | ||
152 | + if(!file_name || !*file_name) | ||
153 | + return CURLE_BAD_FUNCTION_ARGUMENT; | ||
154 | + | ||
155 | + len = strlen(*file_name); | ||
156 | + | ||
157 | + if(len >= max_filename_len) | ||
158 | + len = max_filename_len - 1; | ||
159 | |||
160 | - if(!file_name) | ||
161 | - return NULL; | ||
162 | + sanitized = malloc(len + 1); | ||
163 | |||
164 | - if(strlen(file_name) >= PATH_MAX) | ||
165 | - file_name[PATH_MAX-1] = '\0'; /* truncate it */ | ||
166 | + if(!sanitized) | ||
167 | + return CURLE_OUT_OF_MEMORY; | ||
168 | |||
169 | - strcpy(new_name, msdosify(file_name)); | ||
170 | + strncpy(sanitized, *file_name, len); | ||
171 | + sanitized[len] = '\0'; | ||
172 | |||
173 | - Curl_safefree(file_name); | ||
174 | + for(p = sanitized; *p; ++p ) { | ||
175 | + const char *banned; | ||
176 | + if(1 <= *p && *p <= 31) { | ||
177 | + *p = '_'; | ||
178 | + continue; | ||
179 | + } | ||
180 | + for(banned = "|<>/\\\":?*"; *banned; ++banned) { | ||
181 | + if(*p == *banned) { | ||
182 | + *p = '_'; | ||
183 | + break; | ||
184 | + } | ||
185 | + } | ||
186 | + } | ||
187 | |||
188 | - return strdup(rename_if_dos_device_name(new_name)); | ||
189 | +#ifdef MSDOS | ||
190 | + /* msdosify checks for more banned characters for MSDOS, however it allows | ||
191 | + for some path information to pass through. since we are sanitizing only a | ||
192 | + filename and cannot allow a path it's important this call be done in | ||
193 | + addition to and not instead of the banned character check above. */ | ||
194 | + p = msdosify(sanitized); | ||
195 | + if(!p) { | ||
196 | + free(sanitized); | ||
197 | + return CURLE_BAD_FUNCTION_ARGUMENT; | ||
198 | + } | ||
199 | + sanitized = p; | ||
200 | + len = strlen(sanitized); | ||
201 | +#endif | ||
202 | + | ||
203 | + p = rename_if_dos_device_name(sanitized); | ||
204 | + if(!p) { | ||
205 | + free(sanitized); | ||
206 | + return CURLE_BAD_FUNCTION_ARGUMENT; | ||
207 | + } | ||
208 | + sanitized = p; | ||
209 | + len = strlen(sanitized); | ||
210 | + | ||
211 | + /* dos_device_name rename will rename a device name, possibly changing the | ||
212 | + length. If the length is too long now we can't truncate it because we | ||
213 | + could end up with a device name. In practice this shouldn't be a problem | ||
214 | + because device names are short, but you never know. */ | ||
215 | + if(len >= max_filename_len) { | ||
216 | + free(sanitized); | ||
217 | + return CURLE_BAD_FUNCTION_ARGUMENT; | ||
218 | + } | ||
219 | + | ||
220 | + *file_name = sanitized; | ||
221 | + return CURLE_OK; | ||
222 | } | ||
223 | |||
224 | -/* The following functions are taken with modification from the DJGPP | ||
225 | - * port of tar 1.12. They use algorithms originally from DJTAR. */ | ||
226 | +/* The functions msdosify, rename_if_dos_device_name and __crt0_glob_function | ||
227 | + * were taken with modification from the DJGPP port of tar 1.12. They use | ||
228 | + * algorithms originally from DJTAR. | ||
229 | + */ | ||
230 | |||
231 | -static const char *msdosify (const char *file_name) | ||
232 | +/* | ||
233 | +Extra sanitization MSDOS for file_name. | ||
234 | +Returns a copy of file_name that is sanitized by MSDOS standards. | ||
235 | +Warning: path information may pass through. For sanitizing a filename use | ||
236 | +sanitize_file_name which calls this function after sanitizing path info. | ||
237 | +*/ | ||
238 | +static char *msdosify(const char *file_name) | ||
239 | { | ||
240 | - static char dos_name[PATH_MAX]; | ||
241 | + char dos_name[PATH_MAX]; | ||
242 | static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */ | ||
243 | "|<>\\\":?*"; /* illegal in DOS & W95 */ | ||
244 | static const char *illegal_chars_w95 = &illegal_chars_dos[8]; | ||
245 | int idx, dot_idx; | ||
246 | const char *s = file_name; | ||
247 | @@ -199,39 +263,89 @@ static const char *msdosify (const char *file_name) | ||
248 | else | ||
249 | idx++; | ||
250 | } | ||
251 | |||
252 | *d = '\0'; | ||
253 | - return dos_name; | ||
254 | + return strdup(dos_name); | ||
255 | } | ||
256 | |||
257 | -static char *rename_if_dos_device_name (char *file_name) | ||
258 | +/* | ||
259 | +Rename file_name if it's a representation of a device name. | ||
260 | +Returns a copy of file_name, and the copy will have contents different from the | ||
261 | +original if a device name was found. | ||
262 | +*/ | ||
263 | +static char *rename_if_dos_device_name(const char *file_name) | ||
264 | { | ||
265 | /* We could have a file whose name is a device on MS-DOS. Trying to | ||
266 | * retrieve such a file would fail at best and wedge us at worst. We need | ||
267 | * to rename such files. */ | ||
268 | - char *base; | ||
269 | + char *p, *base; | ||
270 | struct_stat st_buf; | ||
271 | char fname[PATH_MAX]; | ||
272 | |||
273 | strncpy(fname, file_name, PATH_MAX-1); | ||
274 | fname[PATH_MAX-1] = '\0'; | ||
275 | base = basename(fname); | ||
276 | if(((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) { | ||
277 | size_t blen = strlen(base); | ||
278 | |||
279 | - if(strlen(fname) >= PATH_MAX-1) { | ||
280 | + if(strlen(fname) == PATH_MAX-1) { | ||
281 | /* Make room for the '_' */ | ||
282 | blen--; | ||
283 | base[blen] = '\0'; | ||
284 | } | ||
285 | /* Prepend a '_'. */ | ||
286 | memmove(base + 1, base, blen + 1); | ||
287 | base[0] = '_'; | ||
288 | - strcpy(file_name, fname); | ||
289 | } | ||
290 | - return file_name; | ||
291 | + | ||
292 | + /* The above stat check does not identify devices for me in Windows 7. For | ||
293 | + example a stat on COM1 returns a regular file S_IFREG. According to MSDN | ||
294 | + stat doc that is the correct behavior, so I assume the above code is | ||
295 | + legacy, maybe MSDOS or DJGPP specific? */ | ||
296 | + | ||
297 | + /* Rename devices. | ||
298 | + Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS */ | ||
299 | + for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) { | ||
300 | + size_t p_len; | ||
301 | + int x = (curl_strnequal(p, "CON", 3) || | ||
302 | + curl_strnequal(p, "PRN", 3) || | ||
303 | + curl_strnequal(p, "AUX", 3) || | ||
304 | + curl_strnequal(p, "NUL", 3)) ? 3 : | ||
305 | + (curl_strnequal(p, "CLOCK$", 6)) ? 6 : | ||
306 | + (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ? | ||
307 | + (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0; | ||
308 | + | ||
309 | + if(!x) | ||
310 | + continue; | ||
311 | + | ||
312 | + /* the devices may be accessible with an extension or ADS, for | ||
313 | + example CON.AIR and CON:AIR both access console */ | ||
314 | + if(p[x] == '.' || p[x] == ':') { | ||
315 | + p[x] = '_'; | ||
316 | + continue; | ||
317 | + } | ||
318 | + else if(p[x]) /* no match */ | ||
319 | + continue; | ||
320 | + | ||
321 | + p_len = strlen(p); | ||
322 | + | ||
323 | + if(strlen(fname) == PATH_MAX-1) { | ||
324 | + /* Make room for the '_' */ | ||
325 | + p_len--; | ||
326 | + p[p_len] = '\0'; | ||
327 | + } | ||
328 | + /* Prepend a '_'. */ | ||
329 | + memmove(p + 1, p, p_len + 1); | ||
330 | + p[0] = '_'; | ||
331 | + | ||
332 | + /* if fname was just modified then the basename pointer must be updated */ | ||
333 | + if(p == fname) | ||
334 | + base = basename(fname); | ||
335 | + } | ||
336 | + | ||
337 | + return strdup(fname); | ||
338 | } | ||
339 | |||
340 | #if defined(MSDOS) && (defined(__DJGPP__) || defined(__GO32__)) | ||
341 | |||
342 | /* | ||
343 | diff --git a/src/tool_doswin.h b/src/tool_doswin.h | ||
344 | index cd216db..fc83f16 100644 | ||
345 | --- a/src/tool_doswin.h | ||
346 | +++ b/src/tool_doswin.h | ||
347 | @@ -23,11 +23,11 @@ | ||
348 | ***************************************************************************/ | ||
349 | #include "tool_setup.h" | ||
350 | |||
351 | #if defined(MSDOS) || defined(WIN32) | ||
352 | |||
353 | -char *sanitize_dos_name(char *file_name); | ||
354 | +CURLcode sanitize_file_name(char **filename); | ||
355 | |||
356 | #if defined(MSDOS) && (defined(__DJGPP__) || defined(__GO32__)) | ||
357 | |||
358 | char **__crt0_glob_function(char *arg); | ||
359 | |||
360 | diff --git a/src/tool_operate.c b/src/tool_operate.c | ||
361 | index 30d60cb..272ebd4 100644 | ||
362 | --- a/src/tool_operate.c | ||
363 | +++ b/src/tool_operate.c | ||
364 | @@ -541,30 +541,41 @@ static CURLcode operate_do(struct GlobalConfig *global, | ||
365 | if(!outfile) { | ||
366 | /* extract the file name from the URL */ | ||
367 | result = get_url_file_name(&outfile, this_url); | ||
368 | if(result) | ||
369 | goto show_error; | ||
370 | + | ||
371 | +#if defined(MSDOS) || defined(WIN32) | ||
372 | + result = sanitize_file_name(&outfile); | ||
373 | + if(result) { | ||
374 | + Curl_safefree(outfile); | ||
375 | + goto show_error; | ||
376 | + } | ||
377 | +#endif /* MSDOS || WIN32 */ | ||
378 | + | ||
379 | if(!*outfile && !config->content_disposition) { | ||
380 | helpf(global->errors, "Remote file name has no length!\n"); | ||
381 | result = CURLE_WRITE_ERROR; | ||
382 | goto quit_urls; | ||
383 | } | ||
384 | -#if defined(MSDOS) || defined(WIN32) | ||
385 | - /* For DOS and WIN32, we do some major replacing of | ||
386 | - bad characters in the file name before using it */ | ||
387 | - outfile = sanitize_dos_name(outfile); | ||
388 | - if(!outfile) { | ||
389 | - result = CURLE_OUT_OF_MEMORY; | ||
390 | - goto show_error; | ||
391 | - } | ||
392 | -#endif /* MSDOS || WIN32 */ | ||
393 | } | ||
394 | else if(urls) { | ||
395 | /* fill '#1' ... '#9' terms from URL pattern */ | ||
396 | char *storefile = outfile; | ||
397 | result = glob_match_url(&outfile, storefile, urls); | ||
398 | Curl_safefree(storefile); | ||
399 | + | ||
400 | +#if defined(MSDOS) || defined(WIN32) | ||
401 | + if(!result) { | ||
402 | + result = sanitize_file_name(&outfile); | ||
403 | + if(result) { | ||
404 | + Curl_safefree(outfile); | ||
405 | + goto show_error; | ||
406 | + } | ||
407 | + } | ||
408 | +#endif /* MSDOS || WIN32 */ | ||
409 | + | ||
410 | if(result) { | ||
411 | /* bad globbing */ | ||
412 | warnf(config->global, "bad output glob!\n"); | ||
413 | goto quit_urls; | ||
414 | } | ||
415 | -- | ||
416 | 2.7.0 | ||
417 | |||
diff --git a/meta/recipes-support/curl/curl_7.44.0.bb b/meta/recipes-support/curl/curl_7.44.0.bb index f6d350e86a..852c4dd472 100644 --- a/meta/recipes-support/curl/curl_7.44.0.bb +++ b/meta/recipes-support/curl/curl_7.44.0.bb | |||
@@ -12,7 +12,8 @@ SRC_URI = "http://curl.haxx.se/download/curl-${PV}.tar.bz2 \ | |||
12 | # curl likes to set -g0 in CFLAGS, so we stop it | 12 | # curl likes to set -g0 in CFLAGS, so we stop it |
13 | # from mucking around with debug options | 13 | # from mucking around with debug options |
14 | # | 14 | # |
15 | SRC_URI += " file://configure_ac.patch" | 15 | SRC_URI += " file://configure_ac.patch \ |
16 | file://CVE-2016-0754.patch" | ||
16 | 17 | ||
17 | SRC_URI[md5sum] = "6b952ca00e5473b16a11f05f06aa8dae" | 18 | SRC_URI[md5sum] = "6b952ca00e5473b16a11f05f06aa8dae" |
18 | SRC_URI[sha256sum] = "1e2541bae6582bb697c0fbae49e1d3e6fad5d05d5aa80dbd6f072e0a44341814" | 19 | SRC_URI[sha256sum] = "1e2541bae6582bb697c0fbae49e1d3e6fad5d05d5aa80dbd6f072e0a44341814" |