| From 759088694e2ba68ddc5ffe042b071dadad6ff675 Mon Sep 17 00:00:00 2001 |
| From: Daniel Stenberg <daniel@haxx.se> |
| Date: Wed, 25 May 2022 10:09:53 +0200 |
| Subject: [PATCH] fopen: add Curl_fopen() for better overwriting of files |
| |
| Bug: https://curl.se/docs/CVE-2022-32207.html |
| CVE-2022-32207 |
| Reported-by: Harry Sintonen |
| Closes #9050 |
| |
| Upstream-Status: Backport [https://github.com/curl/curl/commit/20f9dd6bae50b] |
| Signed-off-by: Robert Joslyn <robert.joslyn@redrectangle.org> |
| --- |
| CMakeLists.txt | 1 + |
| configure.ac | 1 + |
| lib/Makefile.inc | 2 + |
| lib/cookie.c | 19 ++----- |
| lib/curl_config.h.cmake | 3 ++ |
| lib/fopen.c | 113 ++++++++++++++++++++++++++++++++++++++++ |
| lib/fopen.h | 30 +++++++++++ |
| 7 files changed, 154 insertions(+), 15 deletions(-) |
| create mode 100644 lib/fopen.c |
| create mode 100644 lib/fopen.h |
| |
| diff --git a/CMakeLists.txt b/CMakeLists.txt |
| index b77de6d..a0bfaad 100644 |
| --- a/CMakeLists.txt |
| +++ b/CMakeLists.txt |
| @@ -1027,6 +1027,7 @@ elseif(HAVE_LIBSOCKET) |
| set(CMAKE_REQUIRED_LIBRARIES socket) |
| endif() |
| |
| +check_symbol_exists(fchmod "${CURL_INCLUDES}" HAVE_FCHMOD) |
| check_symbol_exists(basename "${CURL_INCLUDES}" HAVE_BASENAME) |
| check_symbol_exists(socket "${CURL_INCLUDES}" HAVE_SOCKET) |
| check_symbol_exists(select "${CURL_INCLUDES}" HAVE_SELECT) |
| diff --git a/configure.ac b/configure.ac |
| index d431870..7433bb9 100644 |
| --- a/configure.ac |
| +++ b/configure.ac |
| @@ -3351,6 +3351,7 @@ AC_CHECK_DECLS([getpwuid_r], [], [AC_DEFINE(HAVE_DECL_GETPWUID_R_MISSING, 1, "Se |
| |
| |
| AC_CHECK_FUNCS([fnmatch \ |
| + fchmod \ |
| geteuid \ |
| getpass_r \ |
| getppid \ |
| diff --git a/lib/Makefile.inc b/lib/Makefile.inc |
| index e8f110f..5139b03 100644 |
| --- a/lib/Makefile.inc |
| +++ b/lib/Makefile.inc |
| @@ -133,6 +133,7 @@ LIB_CFILES = \ |
| escape.c \ |
| file.c \ |
| fileinfo.c \ |
| + fopen.c \ |
| formdata.c \ |
| ftp.c \ |
| ftplistparser.c \ |
| @@ -263,6 +264,7 @@ LIB_HFILES = \ |
| escape.h \ |
| file.h \ |
| fileinfo.h \ |
| + fopen.h \ |
| formdata.h \ |
| ftp.h \ |
| ftplistparser.h \ |
| diff --git a/lib/cookie.c b/lib/cookie.c |
| index 8a6aa1a..cb0c03b 100644 |
| --- a/lib/cookie.c |
| +++ b/lib/cookie.c |
| @@ -96,8 +96,8 @@ Example set of cookies: |
| #include "curl_get_line.h" |
| #include "curl_memrchr.h" |
| #include "parsedate.h" |
| -#include "rand.h" |
| #include "rename.h" |
| +#include "fopen.h" |
| |
| /* The last 3 #include files should be in this order */ |
| #include "curl_printf.h" |
| @@ -1620,20 +1620,9 @@ static CURLcode cookie_output(struct Curl_easy *data, |
| use_stdout = TRUE; |
| } |
| else { |
| - unsigned char randsuffix[9]; |
| - |
| - if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix))) |
| - return 2; |
| - |
| - tempstore = aprintf("%s.%s.tmp", filename, randsuffix); |
| - if(!tempstore) |
| - return CURLE_OUT_OF_MEMORY; |
| - |
| - out = fopen(tempstore, FOPEN_WRITETEXT); |
| - if(!out) { |
| - error = CURLE_WRITE_ERROR; |
| + error = Curl_fopen(data, filename, &out, &tempstore); |
| + if(error) |
| goto error; |
| - } |
| } |
| |
| fputs("# Netscape HTTP Cookie File\n" |
| @@ -1680,7 +1669,7 @@ static CURLcode cookie_output(struct Curl_easy *data, |
| if(!use_stdout) { |
| fclose(out); |
| out = NULL; |
| - if(Curl_rename(tempstore, filename)) { |
| + if(tempstore && Curl_rename(tempstore, filename)) { |
| unlink(tempstore); |
| error = CURLE_WRITE_ERROR; |
| goto error; |
| diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake |
| index d2a0f43..c254359 100644 |
| --- a/lib/curl_config.h.cmake |
| +++ b/lib/curl_config.h.cmake |
| @@ -157,6 +157,9 @@ |
| /* Define to 1 if you have the <assert.h> header file. */ |
| #cmakedefine HAVE_ASSERT_H 1 |
| |
| +/* Define to 1 if you have the `fchmod' function. */ |
| +#cmakedefine HAVE_FCHMOD 1 |
| + |
| /* Define to 1 if you have the `basename' function. */ |
| #cmakedefine HAVE_BASENAME 1 |
| |
| diff --git a/lib/fopen.c b/lib/fopen.c |
| new file mode 100644 |
| index 0000000..ad3691b |
| --- /dev/null |
| +++ b/lib/fopen.c |
| @@ -0,0 +1,113 @@ |
| +/*************************************************************************** |
| + * _ _ ____ _ |
| + * Project ___| | | | _ \| | |
| + * / __| | | | |_) | | |
| + * | (__| |_| | _ <| |___ |
| + * \___|\___/|_| \_\_____| |
| + * |
| + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. |
| + * |
| + * This software is licensed as described in the file COPYING, which |
| + * you should have received as part of this distribution. The terms |
| + * are also available at https://curl.se/docs/copyright.html. |
| + * |
| + * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| + * copies of the Software, and permit persons to whom the Software is |
| + * furnished to do so, under the terms of the COPYING file. |
| + * |
| + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| + * KIND, either express or implied. |
| + * |
| + * SPDX-License-Identifier: curl |
| + * |
| + ***************************************************************************/ |
| + |
| +#include "curl_setup.h" |
| + |
| +#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \ |
| + !defined(CURL_DISABLE_HSTS) |
| + |
| +#ifdef HAVE_FCNTL_H |
| +#include <fcntl.h> |
| +#endif |
| + |
| +#include "urldata.h" |
| +#include "rand.h" |
| +#include "fopen.h" |
| +/* The last 3 #include files should be in this order */ |
| +#include "curl_printf.h" |
| +#include "curl_memory.h" |
| +#include "memdebug.h" |
| + |
| +/* |
| + * 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 |
| + * name at the time of the open, this function will clone the mode from that |
| + * file. if 'tempname' is non-NULL, it needs a rename after the file is |
| + * written. |
| + */ |
| +CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, |
| + FILE **fh, char **tempname) |
| +{ |
| + CURLcode result = CURLE_WRITE_ERROR; |
| + unsigned char randsuffix[9]; |
| + char *tempstore = NULL; |
| + struct_stat sb; |
| + int fd = -1; |
| + *tempname = NULL; |
| + |
| + if(stat(filename, &sb) == -1 || !S_ISREG(sb.st_mode)) { |
| + /* a non-regular file, fallback to direct fopen() */ |
| + *fh = fopen(filename, FOPEN_WRITETEXT); |
| + if(*fh) |
| + return CURLE_OK; |
| + goto fail; |
| + } |
| + |
| + result = Curl_rand_hex(data, randsuffix, sizeof(randsuffix)); |
| + if(result) |
| + goto fail; |
| + |
| + tempstore = aprintf("%s.%s.tmp", filename, randsuffix); |
| + if(!tempstore) { |
| + result = CURLE_OUT_OF_MEMORY; |
| + goto fail; |
| + } |
| + |
| + result = CURLE_WRITE_ERROR; |
| + fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600); |
| + if(fd == -1) |
| + goto fail; |
| + |
| +#ifdef HAVE_FCHMOD |
| + { |
| + struct_stat nsb; |
| + if((fstat(fd, &nsb) != -1) && |
| + (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) { |
| + /* if the user and group are the same, clone the original mode */ |
| + if(fchmod(fd, sb.st_mode) == -1) |
| + goto fail; |
| + } |
| + } |
| +#endif |
| + |
| + *fh = fdopen(fd, FOPEN_WRITETEXT); |
| + if(!*fh) |
| + goto fail; |
| + |
| + *tempname = tempstore; |
| + return CURLE_OK; |
| + |
| +fail: |
| + if(fd != -1) { |
| + close(fd); |
| + unlink(tempstore); |
| + } |
| + |
| + free(tempstore); |
| + |
| + *tempname = NULL; |
| + return result; |
| +} |
| + |
| +#endif /* ! disabled */ |
| diff --git a/lib/fopen.h b/lib/fopen.h |
| new file mode 100644 |
| index 0000000..289e55f |
| --- /dev/null |
| +++ b/lib/fopen.h |
| @@ -0,0 +1,30 @@ |
| +#ifndef HEADER_CURL_FOPEN_H |
| +#define HEADER_CURL_FOPEN_H |
| +/*************************************************************************** |
| + * _ _ ____ _ |
| + * Project ___| | | | _ \| | |
| + * / __| | | | |_) | | |
| + * | (__| |_| | _ <| |___ |
| + * \___|\___/|_| \_\_____| |
| + * |
| + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. |
| + * |
| + * This software is licensed as described in the file COPYING, which |
| + * you should have received as part of this distribution. The terms |
| + * are also available at https://curl.se/docs/copyright.html. |
| + * |
| + * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| + * copies of the Software, and permit persons to whom the Software is |
| + * furnished to do so, under the terms of the COPYING file. |
| + * |
| + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| + * KIND, either express or implied. |
| + * |
| + * SPDX-License-Identifier: curl |
| + * |
| + ***************************************************************************/ |
| + |
| +CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, |
| + FILE **fh, char **tempname); |
| + |
| +#endif |