From 36b47377c2d1a8d141d1ef810102748f27384f5c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 25 May 2022 10:09:53 +0200 Subject: [PATCH 1/3] 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-commit: 20f9dd6bae50b7223171b17ba7798946e74f877f Signed-off-by: Kamil Dudka --- CMakeLists.txt | 1 + configure.ac | 1 + lib/Makefile.inc | 2 + lib/cookie.c | 17 ++---- lib/curl_config.h.cmake | 3 ++ lib/fopen.c | 113 ++++++++++++++++++++++++++++++++++++++++ lib/fopen.h | 30 +++++++++++ 7 files changed, 154 insertions(+), 13 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 @@ -982,6 +982,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 @@ -4516,6 +4516,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 @@ -130,6 +130,7 @@ LIB_CFILES = \ escape.c \ file.c \ fileinfo.c \ + fopen.c \ formdata.c \ ftp.c \ ftplistparser.c \ @@ -261,6 +262,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 @@ -97,8 +97,8 @@ Example set of cookies: #include "curl_memrchr.h" #include "inet_pton.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" @@ -1537,17 +1537,8 @@ static int 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 1; - - out = fopen(tempstore, FOPEN_WRITETEXT); - if(!out) + error = Curl_fopen(data, filename, &out, &tempstore); + if(error) goto error; } @@ -1594,7 +1585,7 @@ static int 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); 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 @@ -148,6 +148,9 @@ /* Define to 1 if you have the 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, , 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(USE_HSTS) + +#ifdef HAVE_FCNTL_H +#include +#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, , 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 -- 2.35.3 From bd7af48238b058e9b46fdf2e1333b355920c341c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 25 May 2022 10:09:53 +0200 Subject: [PATCH 2/3] altsvc: use Curl_fopen() Upstream-commit: fab970a5d19c1faa2052239ec1e2602b892cbeb2 Signed-off-by: Kamil Dudka --- lib/altsvc.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/lib/altsvc.c b/lib/altsvc.c index 242733b..4dc4078 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -34,7 +34,7 @@ #include "parsedate.h" #include "sendf.h" #include "warnless.h" -#include "rand.h" +#include "fopen.h" #include "rename.h" /* The last 3 #include files should be in this order */ @@ -329,8 +329,7 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data, struct Curl_llist_element *n; CURLcode result = CURLE_OK; FILE *out; - char *tempstore; - unsigned char randsuffix[9]; + char *tempstore = NULL; if(!altsvc) /* no cache activated */ @@ -344,17 +343,8 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data, /* marked as read-only, no file or zero length file name */ return CURLE_OK; - if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix))) - return CURLE_FAILED_INIT; - - tempstore = aprintf("%s.%s.tmp", file, randsuffix); - if(!tempstore) - return CURLE_OUT_OF_MEMORY; - - out = fopen(tempstore, FOPEN_WRITETEXT); - if(!out) - result = CURLE_WRITE_ERROR; - else { + result = Curl_fopen(data, file, &out, &tempstore); + if(!result) { fputs("# Your alt-svc cache. https://curl.se/docs/alt-svc.html\n" "# This file was generated by libcurl! Edit at your own risk.\n", out); @@ -366,10 +356,10 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data, break; } fclose(out); - if(!result && Curl_rename(tempstore, file)) + if(!result && tempstore && Curl_rename(tempstore, file)) result = CURLE_WRITE_ERROR; - if(result) + if(result && tempstore) unlink(tempstore); } free(tempstore); -- 2.35.3 From 2011622a36fa715f38277422241e77e25dfdf0d0 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 25 May 2022 10:09:54 +0200 Subject: [PATCH 3/3] hsts: use Curl_fopen() Upstream-commit: d64115d7bb8ae4c136b620912da523c063f1d2ee Signed-off-by: Kamil Dudka --- lib/hsts.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/lib/hsts.c b/lib/hsts.c index b9fa6f7..9d54c82 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -35,7 +35,7 @@ #include "sendf.h" #include "strtoofft.h" #include "parsedate.h" -#include "rand.h" +#include "fopen.h" #include "rename.h" /* The last 3 #include files should be in this order */ @@ -316,8 +316,7 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, struct Curl_llist_element *n; CURLcode result = CURLE_OK; FILE *out; - char *tempstore; - unsigned char randsuffix[9]; + char *tempstore = NULL; if(!h) /* no cache activated */ @@ -331,17 +330,8 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, /* marked as read-only, no file or zero length file name */ goto skipsave; - if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix))) - return CURLE_FAILED_INIT; - - tempstore = aprintf("%s.%s.tmp", file, randsuffix); - if(!tempstore) - return CURLE_OUT_OF_MEMORY; - - out = fopen(tempstore, FOPEN_WRITETEXT); - if(!out) - result = CURLE_WRITE_ERROR; - else { + result = Curl_fopen(data, file, &out, &tempstore); + if(!result) { fputs("# Your HSTS cache. https://curl.se/docs/hsts.html\n" "# This file was generated by libcurl! Edit at your own risk.\n", out); @@ -353,10 +343,10 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, break; } fclose(out); - if(!result && Curl_rename(tempstore, file)) + if(!result && tempstore && Curl_rename(tempstore, file)) result = CURLE_WRITE_ERROR; - if(result) + if(result && tempstore) unlink(tempstore); } free(tempstore); -- 2.35.3