From 45a125c1d6aaa2352c5ec04eecba322930e6d169 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 17 Oct 2014 12:59:32 +0200 Subject: [PATCH] curl_easy_duphandle: CURLOPT_COPYPOSTFIELDS read out of bounds When duplicating a handle, the data to post was duplicated using strdup() when it could be binary and contain zeroes and it was not even zero terminated! This caused read out of bounds crashes/segfaults. Bug: http://curl.haxx.se/docs/adv_20141105.html CVE: CVE-2014-3707 Reported-By: Symeon Paraschoudis Upstream-commit: b3875606925536f82fc61f3114ac42f29eaf6945 Signed-off-by: Kamil Dudka --- lib/formdata.c | 50 ++++++++------------------------------------------ lib/strdup.c | 29 ++++++++++++++++++++++++++--- lib/strdup.h | 1 + lib/url.c | 19 +++++++++++++++---- lib/urldata.h | 11 +++++++++-- src/Makefile.in | 3 +-- src/Makefile.inc | 1 - 7 files changed, 60 insertions(+), 54 deletions(-) diff --git a/lib/formdata.c b/lib/formdata.c index 3260928..050f538 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -39,6 +39,7 @@ #include "strequal.h" #include "curl_memory.h" #include "sendf.h" +#include "strdup.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -216,46 +217,6 @@ static const char * ContentTypeForFilename (const char *filename, /*************************************************************************** * - * memdup() - * - * Copies the 'source' data to a newly allocated buffer buffer (that is - * returned). Uses buffer_length if not null, else uses strlen to determine - * the length of the buffer to be copied - * - * Returns the new pointer or NULL on failure. - * - ***************************************************************************/ -static char *memdup(const char *src, size_t buffer_length) -{ - size_t length; - bool add = FALSE; - char *buffer; - - if(buffer_length) - length = buffer_length; - else if(src) { - length = strlen(src); - add = TRUE; - } - else - /* no length and a NULL src pointer! */ - return strdup(""); - - buffer = malloc(length+add); - if(!buffer) - return NULL; /* fail */ - - memcpy(buffer, src, length); - - /* if length unknown do null termination */ - if(add) - buffer[length] = '\0'; - - return buffer; -} - -/*************************************************************************** - * * FormAdd() * * Stores a formpost parameter and builds the appropriate linked list. @@ -684,7 +645,10 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, app passed in a bad combo, so we better check for that first. */ if(form->name) /* copy name (without strdup; possibly contains null characters) */ - form->name = memdup(form->name, form->namelength); + form->name = Curl_memdup(form->name, form->namelength? + form->namelength: + strlen(form->name)+1); + if(!form->name) { return_value = CURL_FORMADD_MEMORY; break; @@ -695,7 +659,9 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER | HTTPPOST_CALLBACK)) ) { /* copy value (without strdup; possibly contains null characters) */ - form->value = memdup(form->value, form->contentslength); + form->value = Curl_memdup(form->value, form->contentslength? + form->contentslength: + strlen(form->value)+1); if(!form->value) { return_value = CURL_FORMADD_MEMORY; break; diff --git a/lib/strdup.c b/lib/strdup.c index 3b776b1..14f370f 100644 --- a/lib/strdup.c +++ b/lib/strdup.c @@ -19,12 +19,13 @@ * KIND, either express or implied. * ***************************************************************************/ -/* - * This file is 'mem-include-scan' clean. See test 1132. - */ #include "curl_setup.h" #include "strdup.h" +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" #ifndef HAVE_STRDUP char *curlx_strdup(const char *str) @@ -50,3 +51,25 @@ char *curlx_strdup(const char *str) } #endif + +/*************************************************************************** + * + * Curl_memdup(source, length) + * + * Copies the 'source' data to a newly allocated buffer (that is + * returned). Copies 'length' bytes. + * + * Returns the new pointer or NULL on failure. + * + ***************************************************************************/ +char *Curl_memdup(const char *src, size_t length) +{ + char *buffer = malloc(length); + if(!buffer) + return NULL; /* fail */ + + memcpy(buffer, src, length); + + /* if length unknown do null termination */ + return buffer; +} diff --git a/lib/strdup.h b/lib/strdup.h index 49af911..36cc430 100644 --- a/lib/strdup.h +++ b/lib/strdup.h @@ -26,5 +26,6 @@ #ifndef HAVE_STRDUP extern char *curlx_strdup(const char *str); #endif +char *Curl_memdup(const char *src, size_t buffer_length); #endif /* HEADER_CURL_STRDUP_H */ diff --git a/lib/url.c b/lib/url.c index b0aade1..0aa5a33 100644 --- a/lib/url.c +++ b/lib/url.c @@ -123,6 +123,7 @@ int curl_win32_idn_to_ascii(const char *in, char **out); #include "bundles.h" #include "conncache.h" #include "multihandle.h" +#include "strdup.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -344,14 +345,24 @@ CURLcode Curl_dupset(struct SessionHandle * dst, struct SessionHandle * src) memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); /* duplicate all strings */ - for(i=(enum dupstring)0; i< STRING_LAST; i++) { + for(i=(enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) { r = setstropt(&dst->set.str[i], src->set.str[i]); if(r != CURLE_OK) - break; + return r; } - /* If a failure occurred, freeing has to be performed externally. */ - return r; + /* duplicate memory areas pointed to */ + i = STRING_COPYPOSTFIELDS; + if(src->set.postfieldsize && src->set.str[i]) { + /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ + dst->set.str[i] = Curl_memdup(src->set.str[i], src->set.postfieldsize); + if(!dst->set.str[i]) + return CURLE_OUT_OF_MEMORY; + /* point to the new copy */ + dst->set.postfields = dst->set.str[i]; + } + + return CURLE_OK; } /* diff --git a/lib/urldata.h b/lib/urldata.h index 640cbb1..d03440b 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1337,7 +1337,6 @@ enum dupstring { STRING_KRB_LEVEL, /* krb security level */ STRING_NETRC_FILE, /* if not NULL, use this instead of trying to find $HOME/.netrc */ - STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */ STRING_PROXY, /* proxy to use */ STRING_SET_RANGE, /* range, if used */ STRING_SET_REFERER, /* custom string for the HTTP referer field */ @@ -1376,7 +1375,15 @@ enum dupstring { STRING_TLSAUTH_PASSWORD, /* TLS auth */ #endif - /* -- end of strings -- */ + /* -- end of zero-terminated strings -- */ + + STRING_LASTZEROTERMINATED, + + /* -- below this are pointers to binary data that cannot be strdup'ed. + Each such pointer must be added manually to Curl_dupset() --- */ + + STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */ + STRING_LAST /* not used, just an end-of-list marker */ }; diff --git a/src/Makefile.in b/src/Makefile.in index 86e3d3a..74e8b9e 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -111,7 +111,7 @@ am__objects_1 = curl-tool_binmode.$(OBJEXT) curl-tool_bname.$(OBJEXT) \ curl-tool_urlglob.$(OBJEXT) curl-tool_util.$(OBJEXT) \ curl-tool_vms.$(OBJEXT) curl-tool_writeenv.$(OBJEXT) \ curl-tool_writeout.$(OBJEXT) curl-tool_xattr.$(OBJEXT) -am__objects_2 = curl-strtoofft.$(OBJEXT) curl-strdup.$(OBJEXT) \ +am__objects_2 = curl-strtoofft.$(OBJEXT) \ curl-rawstr.$(OBJEXT) curl-nonblock.$(OBJEXT) am__objects_3 = am_curl_OBJECTS = $(am__objects_1) $(am__objects_2) $(am__objects_3) @@ -376,7 +376,6 @@ AM_CPPFLAGS = -I$(top_builddir)/include/curl -I$(top_builddir)/include \ # the official API, but we re-use the code here to avoid duplication. CURLX_ONES = \ ../lib/strtoofft.c \ - ../lib/strdup.c \ ../lib/rawstr.c \ ../lib/nonblock.c diff --git a/src/Makefile.inc b/src/Makefile.inc index 3f9044d..ea81000 100644 --- a/src/Makefile.inc +++ b/src/Makefile.inc @@ -11,7 +11,6 @@ # the official API, but we re-use the code here to avoid duplication. CURLX_ONES = \ ../lib/strtoofft.c \ - ../lib/strdup.c \ ../lib/rawstr.c \ ../lib/nonblock.c -- 2.1.0