CVE: CVE-2023-46219 Upstream-Status: Backport [ https://github.com/curl/curl/commit/73b65e94f3531179de45 ] Signed-off-by: Lee Chee Yang From 73b65e94f3531179de45c6f3c836a610e3d0a846 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 23 Nov 2023 08:23:17 +0100 Subject: [PATCH] fopen: create short(er) temporary file name Only using random letters in the name plus a ".tmp" extension. Not by appending characters to the final file name. Reported-by: Maksymilian Arciemowicz Closes #12388 --- lib/fopen.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/lib/fopen.c b/lib/fopen.c index 75b8a7aa534085..a73ac068ea3016 100644 --- a/lib/fopen.c +++ b/lib/fopen.c @@ -39,6 +39,51 @@ #include "curl_memory.h" #include "memdebug.h" +/* + The dirslash() function breaks a null-terminated pathname string into + directory and filename components then returns the directory component up + to, *AND INCLUDING*, a final '/'. If there is no directory in the path, + this instead returns a "" string. + + This function returns a pointer to malloc'ed memory. + + The input path to this function is expected to have a file name part. +*/ + +#ifdef _WIN32 +#define PATHSEP "\\" +#define IS_SEP(x) (((x) == '/') || ((x) == '\\')) +#elif defined(MSDOS) || defined(__EMX__) || defined(OS2) +#define PATHSEP "\\" +#define IS_SEP(x) ((x) == '\\') +#else +#define PATHSEP "/" +#define IS_SEP(x) ((x) == '/') +#endif + +static char *dirslash(const char *path) +{ + size_t n; + struct dynbuf out; + DEBUGASSERT(path); + Curl_dyn_init(&out, CURL_MAX_INPUT_LENGTH); + n = strlen(path); + if(n) { + /* find the rightmost path separator, if any */ + while(n && !IS_SEP(path[n-1])) + --n; + /* skip over all the path separators, if any */ + while(n && IS_SEP(path[n-1])) + --n; + } + if(Curl_dyn_addn(&out, path, n)) + return NULL; + /* if there was a directory, append a single trailing slash */ + if(n && Curl_dyn_addn(&out, PATHSEP, 1)) + return NULL; + return Curl_dyn_ptr(&out); +} + /* * Curl_fopen() opens a file for writing with a temp name, to be renamed * to the final name when completed. If there is an existing file using this @@ -50,25 +95,34 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, FILE **fh, char **tempname) { CURLcode result = CURLE_WRITE_ERROR; - unsigned char randsuffix[9]; + unsigned char randbuf[41]; char *tempstore = NULL; struct_stat sb; int fd = -1; + char *dir; *tempname = NULL; + dir = dirslash(filename); + if(!dir) + goto fail; + *fh = fopen(filename, FOPEN_WRITETEXT); if(!*fh) goto fail; - if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) + if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) { + free(dir); return CURLE_OK; + } fclose(*fh); *fh = NULL; - result = Curl_rand_alnum(data, randsuffix, sizeof(randsuffix)); + result = Curl_rand_alnum(data, randbuf, sizeof(randbuf)); if(result) goto fail; - tempstore = aprintf("%s.%s.tmp", filename, randsuffix); + /* The temp file name should not end up too long for the target file + system */ + tempstore = aprintf("%s%s.tmp", dir, randbuf); if(!tempstore) { result = CURLE_OUT_OF_MEMORY; goto fail; @@ -95,6 +149,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, if(!*fh) goto fail; + free(dir); *tempname = tempstore; return CURLE_OK; @@ -105,7 +160,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, } free(tempstore); - + free(dir); return result; }