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