| CVE: CVE-2023-46219 |
| Upstream-Status: Backport [ https://github.com/curl/curl/commit/73b65e94f3531179de45 ] |
| Signed-off-by: Lee Chee Yang <chee.yang.lee@intel.com> |
| |
| From 73b65e94f3531179de45c6f3c836a610e3d0a846 Mon Sep 17 00:00:00 2001 |
| From: Daniel Stenberg <daniel@haxx.se> |
| 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; |
| } |
| |