Backported of: From 3782dfda5fc4f45a19b1ce1b01ecf7206a3d304a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg <daniel@haxx.se> Date: Wed, 25 May 2022 10:09:53 +0200 Subject: [PATCH 1/3] fopen: add Curl_fopen() for better overwriting of files --- lib/Makefile.inc | 4 +- lib/altsvc.c | 22 +++------- lib/cookie.c | 16 ++----- lib/fopen.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++ lib/fopen.h | 28 +++++++++++++ lib/hsts.c | 22 +++------- 6 files changed, 152 insertions(+), 46 deletions(-) create mode 100644 lib/fopen.c create mode 100644 lib/fopen.h diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 6d35704..7dac605 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -50,7 +50,7 @@ LIB_CFILES = altsvc.c amigaos.c asyn-ares.c asyn-thread.c base64.c \ curl_gethostname.c curl_gssapi.c curl_memrchr.c curl_multibyte.c \ curl_ntlm_core.c curl_ntlm_wb.c curl_path.c curl_range.c curl_rtmp.c \ curl_sasl.c curl_sspi.c curl_threads.c dict.c dotdot.c easy.c escape.c \ - file.c fileinfo.c formdata.c ftp.c url.c ftplistparser.c getenv.c getinfo.c \ + file.c fileinfo.c fopen.c formdata.c ftp.c url.c ftplistparser.c getenv.c getinfo.c \ gopher.c hash.c hmac.c hostasyn.c hostcheck.c hostip.c hostip4.c hostip6.c \ hostsyn.c http.c http2.c http_chunks.c http_digest.c http_negotiate.c \ http_ntlm.c http_proxy.c idn_win32.c if2ip.c imap.c inet_ntop.c inet_pton.c \ @@ -70,7 +70,7 @@ LIB_HFILES = altsvc.h amigaos.h arpa_telnet.h asyn.h conncache.h connect.h \ curl_memrchr.h curl_multibyte.h curl_ntlm_core.h curl_ntlm_wb.h curl_path.h \ curl_printf.h curl_range.h curl_rtmp.h curl_sasl.h curl_krb5.h curl_setup.h \ curl_setup_once.h curl_sha256.h curl_sspi.h curl_threads.h curlx.h dict.h \ - dotdot.h easyif.h escape.h file.h fileinfo.h formdata.h ftp.h url.h \ + dotdot.h easyif.h escape.h file.h fileinfo.h fopen.h formdata.h ftp.h url.h \ ftplistparser.h getinfo.h gopher.h hash.h hostcheck.h hostip.h http.h \ http2.h http_chunks.h http_digest.h http_negotiate.h http_ntlm.h \ http_proxy.h if2ip.h imap.h inet_ntop.h inet_pton.h llist.h memdebug.h \ diff --git a/lib/altsvc.c b/lib/altsvc.c index 4ab77fd..97249b2 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); diff --git a/lib/cookie.c b/lib/cookie.c index 1d1bf9b..2dc6314 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -99,6 +99,7 @@ Example set of cookies: #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" @@ -1534,17 +1535,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; } @@ -1591,7 +1583,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/fopen.c b/lib/fopen.c new file mode 100644 index 0000000..92dc31d --- /dev/null +++ b/lib/fopen.c @@ -0,0 +1,106 @@ +/*************************************************************************** + * _ _ ____ _ + * 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. + * + ***************************************************************************/ + +#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, nsb; + 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; + + 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; + } + + *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..1020f3c --- /dev/null +++ b/lib/fopen.h @@ -0,0 +1,28 @@ +#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. + * + ***************************************************************************/ + +CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, + FILE **fh, char **tempname); + +#endif diff --git a/lib/hsts.c b/lib/hsts.c index 6f77128..00bd943 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 */ @@ -318,8 +318,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 */ @@ -333,17 +332,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); @@ -355,10 +345,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.25.1