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
This commit is contained in:
Daniel Stenberg 2022-05-25 10:09:53 +02:00
parent 46f8911d39
commit 20f9dd6bae
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
7 changed files with 154 additions and 15 deletions

View file

@ -1067,6 +1067,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)

View file

@ -3438,6 +3438,7 @@ AC_CHECK_DECLS([getpwuid_r], [], [AC_DEFINE(HAVE_DECL_GETPWUID_R_MISSING, 1, "Se
AC_CHECK_FUNCS([fnmatch \
fchmod \
geteuid \
getpass_r \
getppid \

View file

@ -137,6 +137,7 @@ LIB_CFILES = \
escape.c \
file.c \
fileinfo.c \
fopen.c \
formdata.c \
ftp.c \
ftplistparser.c \
@ -270,6 +271,7 @@ LIB_HFILES = \
escape.h \
file.h \
fileinfo.h \
fopen.h \
formdata.h \
ftp.h \
ftplistparser.h \

View file

@ -99,8 +99,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"
@ -1641,20 +1641,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"
@ -1701,7 +1690,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;

View file

@ -159,6 +159,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

113
lib/fopen.c Normal file
View file

@ -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 */

30
lib/fopen.h Normal file
View file

@ -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