1From 759088694e2ba68ddc5ffe042b071dadad6ff675 Mon Sep 17 00:00:00 2001 2From: Daniel Stenberg <daniel@haxx.se> 3Date: Wed, 25 May 2022 10:09:53 +0200 4Subject: [PATCH] fopen: add Curl_fopen() for better overwriting of files 5 6Bug: https://curl.se/docs/CVE-2022-32207.html 7CVE-2022-32207 8Reported-by: Harry Sintonen 9Closes #9050 10 11Upstream-Status: Backport [https://github.com/curl/curl/commit/20f9dd6bae50b] 12Signed-off-by: Robert Joslyn <robert.joslyn@redrectangle.org> 13--- 14 CMakeLists.txt | 1 + 15 configure.ac | 1 + 16 lib/Makefile.inc | 2 + 17 lib/cookie.c | 19 ++----- 18 lib/curl_config.h.cmake | 3 ++ 19 lib/fopen.c | 113 ++++++++++++++++++++++++++++++++++++++++ 20 lib/fopen.h | 30 +++++++++++ 21 7 files changed, 154 insertions(+), 15 deletions(-) 22 create mode 100644 lib/fopen.c 23 create mode 100644 lib/fopen.h 24 25diff --git a/CMakeLists.txt b/CMakeLists.txt 26index b77de6d..a0bfaad 100644 27--- a/CMakeLists.txt 28+++ b/CMakeLists.txt 29@@ -1027,6 +1027,7 @@ elseif(HAVE_LIBSOCKET) 30 set(CMAKE_REQUIRED_LIBRARIES socket) 31 endif() 32 33+check_symbol_exists(fchmod "${CURL_INCLUDES}" HAVE_FCHMOD) 34 check_symbol_exists(basename "${CURL_INCLUDES}" HAVE_BASENAME) 35 check_symbol_exists(socket "${CURL_INCLUDES}" HAVE_SOCKET) 36 check_symbol_exists(select "${CURL_INCLUDES}" HAVE_SELECT) 37diff --git a/configure.ac b/configure.ac 38index d431870..7433bb9 100644 39--- a/configure.ac 40+++ b/configure.ac 41@@ -3351,6 +3351,7 @@ AC_CHECK_DECLS([getpwuid_r], [], [AC_DEFINE(HAVE_DECL_GETPWUID_R_MISSING, 1, "Se 42 43 44 AC_CHECK_FUNCS([fnmatch \ 45+ fchmod \ 46 geteuid \ 47 getpass_r \ 48 getppid \ 49diff --git a/lib/Makefile.inc b/lib/Makefile.inc 50index e8f110f..5139b03 100644 51--- a/lib/Makefile.inc 52+++ b/lib/Makefile.inc 53@@ -133,6 +133,7 @@ LIB_CFILES = \ 54 escape.c \ 55 file.c \ 56 fileinfo.c \ 57+ fopen.c \ 58 formdata.c \ 59 ftp.c \ 60 ftplistparser.c \ 61@@ -263,6 +264,7 @@ LIB_HFILES = \ 62 escape.h \ 63 file.h \ 64 fileinfo.h \ 65+ fopen.h \ 66 formdata.h \ 67 ftp.h \ 68 ftplistparser.h \ 69diff --git a/lib/cookie.c b/lib/cookie.c 70index 8a6aa1a..cb0c03b 100644 71--- a/lib/cookie.c 72+++ b/lib/cookie.c 73@@ -96,8 +96,8 @@ Example set of cookies: 74 #include "curl_get_line.h" 75 #include "curl_memrchr.h" 76 #include "parsedate.h" 77-#include "rand.h" 78 #include "rename.h" 79+#include "fopen.h" 80 81 /* The last 3 #include files should be in this order */ 82 #include "curl_printf.h" 83@@ -1620,20 +1620,9 @@ static CURLcode cookie_output(struct Curl_easy *data, 84 use_stdout = TRUE; 85 } 86 else { 87- unsigned char randsuffix[9]; 88- 89- if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix))) 90- return 2; 91- 92- tempstore = aprintf("%s.%s.tmp", filename, randsuffix); 93- if(!tempstore) 94- return CURLE_OUT_OF_MEMORY; 95- 96- out = fopen(tempstore, FOPEN_WRITETEXT); 97- if(!out) { 98- error = CURLE_WRITE_ERROR; 99+ error = Curl_fopen(data, filename, &out, &tempstore); 100+ if(error) 101 goto error; 102- } 103 } 104 105 fputs("# Netscape HTTP Cookie File\n" 106@@ -1680,7 +1669,7 @@ static CURLcode cookie_output(struct Curl_easy *data, 107 if(!use_stdout) { 108 fclose(out); 109 out = NULL; 110- if(Curl_rename(tempstore, filename)) { 111+ if(tempstore && Curl_rename(tempstore, filename)) { 112 unlink(tempstore); 113 error = CURLE_WRITE_ERROR; 114 goto error; 115diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake 116index d2a0f43..c254359 100644 117--- a/lib/curl_config.h.cmake 118+++ b/lib/curl_config.h.cmake 119@@ -157,6 +157,9 @@ 120 /* Define to 1 if you have the <assert.h> header file. */ 121 #cmakedefine HAVE_ASSERT_H 1 122 123+/* Define to 1 if you have the `fchmod' function. */ 124+#cmakedefine HAVE_FCHMOD 1 125+ 126 /* Define to 1 if you have the `basename' function. */ 127 #cmakedefine HAVE_BASENAME 1 128 129diff --git a/lib/fopen.c b/lib/fopen.c 130new file mode 100644 131index 0000000..ad3691b 132--- /dev/null 133+++ b/lib/fopen.c 134@@ -0,0 +1,113 @@ 135+/*************************************************************************** 136+ * _ _ ____ _ 137+ * Project ___| | | | _ \| | 138+ * / __| | | | |_) | | 139+ * | (__| |_| | _ <| |___ 140+ * \___|\___/|_| \_\_____| 141+ * 142+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. 143+ * 144+ * This software is licensed as described in the file COPYING, which 145+ * you should have received as part of this distribution. The terms 146+ * are also available at https://curl.se/docs/copyright.html. 147+ * 148+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell 149+ * copies of the Software, and permit persons to whom the Software is 150+ * furnished to do so, under the terms of the COPYING file. 151+ * 152+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 153+ * KIND, either express or implied. 154+ * 155+ * SPDX-License-Identifier: curl 156+ * 157+ ***************************************************************************/ 158+ 159+#include "curl_setup.h" 160+ 161+#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \ 162+ !defined(CURL_DISABLE_HSTS) 163+ 164+#ifdef HAVE_FCNTL_H 165+#include <fcntl.h> 166+#endif 167+ 168+#include "urldata.h" 169+#include "rand.h" 170+#include "fopen.h" 171+/* The last 3 #include files should be in this order */ 172+#include "curl_printf.h" 173+#include "curl_memory.h" 174+#include "memdebug.h" 175+ 176+/* 177+ * Curl_fopen() opens a file for writing with a temp name, to be renamed 178+ * to the final name when completed. If there is an existing file using this 179+ * name at the time of the open, this function will clone the mode from that 180+ * file. if 'tempname' is non-NULL, it needs a rename after the file is 181+ * written. 182+ */ 183+CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, 184+ FILE **fh, char **tempname) 185+{ 186+ CURLcode result = CURLE_WRITE_ERROR; 187+ unsigned char randsuffix[9]; 188+ char *tempstore = NULL; 189+ struct_stat sb; 190+ int fd = -1; 191+ *tempname = NULL; 192+ 193+ if(stat(filename, &sb) == -1 || !S_ISREG(sb.st_mode)) { 194+ /* a non-regular file, fallback to direct fopen() */ 195+ *fh = fopen(filename, FOPEN_WRITETEXT); 196+ if(*fh) 197+ return CURLE_OK; 198+ goto fail; 199+ } 200+ 201+ result = Curl_rand_hex(data, randsuffix, sizeof(randsuffix)); 202+ if(result) 203+ goto fail; 204+ 205+ tempstore = aprintf("%s.%s.tmp", filename, randsuffix); 206+ if(!tempstore) { 207+ result = CURLE_OUT_OF_MEMORY; 208+ goto fail; 209+ } 210+ 211+ result = CURLE_WRITE_ERROR; 212+ fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600); 213+ if(fd == -1) 214+ goto fail; 215+ 216+#ifdef HAVE_FCHMOD 217+ { 218+ struct_stat nsb; 219+ if((fstat(fd, &nsb) != -1) && 220+ (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) { 221+ /* if the user and group are the same, clone the original mode */ 222+ if(fchmod(fd, sb.st_mode) == -1) 223+ goto fail; 224+ } 225+ } 226+#endif 227+ 228+ *fh = fdopen(fd, FOPEN_WRITETEXT); 229+ if(!*fh) 230+ goto fail; 231+ 232+ *tempname = tempstore; 233+ return CURLE_OK; 234+ 235+fail: 236+ if(fd != -1) { 237+ close(fd); 238+ unlink(tempstore); 239+ } 240+ 241+ free(tempstore); 242+ 243+ *tempname = NULL; 244+ return result; 245+} 246+ 247+#endif /* ! disabled */ 248diff --git a/lib/fopen.h b/lib/fopen.h 249new file mode 100644 250index 0000000..289e55f 251--- /dev/null 252+++ b/lib/fopen.h 253@@ -0,0 +1,30 @@ 254+#ifndef HEADER_CURL_FOPEN_H 255+#define HEADER_CURL_FOPEN_H 256+/*************************************************************************** 257+ * _ _ ____ _ 258+ * Project ___| | | | _ \| | 259+ * / __| | | | |_) | | 260+ * | (__| |_| | _ <| |___ 261+ * \___|\___/|_| \_\_____| 262+ * 263+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. 264+ * 265+ * This software is licensed as described in the file COPYING, which 266+ * you should have received as part of this distribution. The terms 267+ * are also available at https://curl.se/docs/copyright.html. 268+ * 269+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell 270+ * copies of the Software, and permit persons to whom the Software is 271+ * furnished to do so, under the terms of the COPYING file. 272+ * 273+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 274+ * KIND, either express or implied. 275+ * 276+ * SPDX-License-Identifier: curl 277+ * 278+ ***************************************************************************/ 279+ 280+CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, 281+ FILE **fh, char **tempname); 282+ 283+#endif 284