diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 3cda27766a..e0250f5624 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -36,7 +36,7 @@ permissions: {} # or runtime: # # - 10.7 Lion (2011) - GSS (build-time, deprecated MIT Kerberos shim) -# - 10.9 Mavericks (2013) - LDAP (build-time, deprecated), OCSP (runtime) +# - 10.9 Mavericks (2013) - LDAP (build-time, deprecated), memset_s(), OCSP (runtime) # - 10.11 El Capitan (2015) - connectx() (runtime) # - 10.12 Sierra (2016) - clock_gettime() (build-time, runtime) # - 10.14 Mojave (2018) - SecTrustEvaluateWithError() (runtime) diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index 8ecd206186..e69ea5f608 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -65,6 +65,16 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD") set(HAVE_EVENTFD 1) endif() +if(ANDROID AND ANDROID_PLATFORM_LEVEL GREATER_EQUAL 34) + set(HAVE_MEMSET_EXPLICIT 1) +endif() +if((APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.9) OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR # v6+ + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") # v11.2+ + set(HAVE_MEMSET_S 1) +elseif(NOT APPLE) + set(HAVE_MEMSET_S 0) +endif() set(HAVE_FCNTL 1) set(HAVE_FCNTL_H 1) set(HAVE_FCNTL_O_NONBLOCK 1) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0573d2f143..89d7f8a932 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1672,6 +1672,11 @@ if(NOT WIN32) check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP) check_symbol_exists("stricmp" "string.h" HAVE_STRICMP) check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) + + check_symbol_exists("memset_s" "string.h" HAVE_MEMSET_S) + if(NOT HAVE_MEMSET_S) + check_function_exists("memset_explicit" HAVE_MEMSET_EXPLICIT) + endif() endif() if(AMIGA) diff --git a/configure.ac b/configure.ac index d06024bc10..3e8569de3e 100644 --- a/configure.ac +++ b/configure.ac @@ -4152,6 +4152,11 @@ if test "$curl_cv_native_windows" != "yes"; then CURL_CHECK_FUNC_STRCASECMP CURL_CHECK_FUNC_STRCMPI CURL_CHECK_FUNC_STRICMP + + CURL_CHECK_FUNC_MEMSET_S + if test "$curl_cv_func_memset_s" = "no"; then + AC_CHECK_FUNCS([memset_explicit]) + fi fi if test -z "$ssl_backends"; then diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 1e244671f3..0edd0efe74 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -49,11 +49,6 @@ #include #endif -#ifdef __DragonFly__ -/* Required for __DragonFly_version */ -#include -#endif - #include "urldata.h" #include "curl_trc.h" #include "if2ip.h" diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index 41b0ddf073..31e94d0691 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -234,6 +234,12 @@ /* Define to 1 if you have the `opendir' function. */ #cmakedefine HAVE_OPENDIR 1 +/* Define to 1 if you have the memset_explicit (C23) function. */ +#cmakedefine HAVE_MEMSET_EXPLICIT 1 + +/* Define to 1 if you have the memset_s (C11) function. */ +#cmakedefine HAVE_MEMSET_S 1 + /* Define to 1 if you have the fcntl function. */ #cmakedefine HAVE_FCNTL 1 diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 9329f5605f..d4b805f9e2 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -1329,6 +1329,20 @@ extern curl_calloc_callback Curl_ccalloc; (ptr) = NULL; \ } while(0) +/* Same as curlx_safefree() but zeroes memory before freeing */ +#define curlx_safefreezero(ptr, size) \ + do { \ + curlx_freezero(ptr, size); \ + (ptr) = NULL; \ + } while(0) + +/* Same as curlx_safefreezero() but determines length with strlen() */ +#define curlx_safefreezeroz(ptr) \ + do { \ + curlx_freezeroz(ptr); \ + (ptr) = NULL; \ + } while(0) + #include /* for CURL_EXTERN, curl_socket_t, mprintf.h */ #ifdef DEBUGBUILD @@ -1608,4 +1622,42 @@ typedef struct sockaddr_un { #define NOVERBOSE(x) x #endif +/* For FreeBSD it is included from curl/curl.h */ +#if defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) +#include /* for __DragonFly_version, OpenBSD, + __NetBSD_Version__ */ +#endif + +#ifndef _CURL_LOCAL_MEMZERO /* to be removed after a couple of releases */ +#ifdef _WIN32 +#if defined(_MSC_VER) && defined(NTDDI_VERSION) && \ + (NTDDI_VERSION >= 0x0A000010) /* MS SDK 10.0.26100.0+ */ +#pragma comment(lib, "volatileaccessu.lib") +#define curlx_memzero(buf, size) SecureZeroMemory2(buf, size) +#else +#define curlx_memzero(buf, size) SecureZeroMemory(buf, size) +#endif +#elif defined(HAVE_MEMSET_S) +#define curlx_memzero(buf, size) (void)memset_s(buf, size, 0, size) +#elif defined(HAVE_MEMSET_EXPLICIT) +#define curlx_memzero(buf, size) (void)memset_explicit(buf, 0, size) +#elif defined(__CYGWIN__) || defined(__NEWLIB__) || \ + (defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) || \ + (defined(__DragonFly__) && __DragonFly_version >= 500600 /* v5.6+ */) || \ + (defined(__FreeBSD__) && __FreeBSD_version >= 1100037 /* v11.0+ */) || \ + (defined(__OpenBSD__) && OpenBSD >= 201405 /* v5.5+ */) +#define curlx_memzero(buf, size) explicit_bzero(buf, size) +#elif defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 /* v7.2+ */ +#define curlx_memzero(buf, size) (void)explicit_memset(buf, 0, size) +#endif +#endif /* !_CURL_LOCAL_MEMZERO */ + +#ifndef curlx_memzero +#define USE_CURLX_MEMZERO +void curlx_memzero(void *buf, size_t size); +#endif +void curlx_freezero(void *buf, size_t size); +void curlx_freezeroz(void *buf); + #endif /* HEADER_CURL_SETUP_H */ diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index f8f2053b5d..7b851788ad 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -54,7 +54,6 @@ * NetBSD 10.99.11 development. * It is safe to apply the workaround even if the bug is not present, as * the workaround reduces performance slightly. */ -# include # if __NetBSD_Version__ < 904000000 || \ (__NetBSD_Version__ >= 999000000 && \ __NetBSD_Version__ < 1000000000) || \ @@ -173,7 +172,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; if(result == CURLE_OK) memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE); - explicit_memset(tmp_digest, 0, sizeof(tmp_digest)); + curlx_memzero(tmp_digest, sizeof(tmp_digest)); #else /* !NEED_NETBSD_SHA512_256_WORKAROUND */ result = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; diff --git a/lib/curlx/strdup.c b/lib/curlx/strdup.c index 3c967dbe0a..8e788ea34c 100644 --- a/lib/curlx/strdup.c +++ b/lib/curlx/strdup.c @@ -94,3 +94,32 @@ void *curlx_memdup0(const char *src, size_t length) buf[length] = 0; return buf; } + +#ifdef USE_CURLX_MEMZERO +static void *(* const volatile p_curlx_memset)(void *buf, int val, + size_t size) = memset; + +/* Local fallback in case there is no system function to securely zero a memory + buffer. */ +void curlx_memzero(void *buf, size_t size) +{ + if(buf) + p_curlx_memset(buf, 0, size); +} +#endif + +/* Free 'buf' after zeroing its content. */ +void curlx_freezero(void *buf, size_t size) +{ + if(buf) + curlx_memzero(buf, size); + curlx_free(buf); +} + +/* Free 'buf' after zeroing its content, where 'buf' is null-terminated. */ +void curlx_freezeroz(void *buf) +{ + if(buf) + curlx_memzero(buf, strlen(buf)); + curlx_free(buf); +} diff --git a/m4/curl-functions.m4 b/m4/curl-functions.m4 index 9d80f2f538..999f96add0 100644 --- a/m4/curl-functions.m4 +++ b/m4/curl-functions.m4 @@ -4111,7 +4111,6 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ test "$tst_allow_strerror_r" = "unknown"; then AC_MSG_WARN([cannot determine strerror_r() style: edit lib/curl_config.h manually.]) fi - ]) @@ -4199,6 +4198,92 @@ AC_DEFUN([CURL_CHECK_FUNC_STRICMP], [ fi ]) + +dnl CURL_CHECK_FUNC_MEMSET_S +dnl ------------------------------------------------- +dnl Verify if memset_s is available, prototyped, and +dnl can be compiled. If all of these are true, and +dnl usage has not been previously disallowed with +dnl shell variable curl_disallow_memset_s, then +dnl HAVE_MEMSET_S will be defined. + +AC_DEFUN([CURL_CHECK_FUNC_MEMSET_S], [ + AC_REQUIRE([CURL_INCLUDES_STRING]) + + tst_links_memset_s="unknown" + tst_proto_memset_s="unknown" + tst_compi_memset_s="unknown" + tst_allow_memset_s="unknown" + + AC_MSG_CHECKING([if memset_s can be linked]) + AC_LINK_IFELSE([ + AC_LANG_FUNC_LINK_TRY([memset_s]) + ],[ + AC_MSG_RESULT([yes]) + tst_links_memset_s="yes" + ],[ + AC_MSG_RESULT([no]) + tst_links_memset_s="no" + ]) + + if test "$tst_links_memset_s" = "yes"; then + AC_MSG_CHECKING([if memset_s is prototyped]) + AC_EGREP_CPP([memset_s],[ + $curl_includes_string + ],[ + AC_MSG_RESULT([yes]) + tst_proto_memset_s="yes" + ],[ + AC_MSG_RESULT([no]) + tst_proto_memset_s="no" + ]) + fi + + if test "$tst_proto_memset_s" = "yes"; then + AC_MSG_CHECKING([if memset_s is compilable]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ + $curl_includes_string + ]],[[ + char buf[2]; + if(memset_s(buf, sizeof(buf), 0, sizeof(buf)) != 0) + return 1; + ]]) + ],[ + AC_MSG_RESULT([yes]) + tst_compi_memset_s="yes" + ],[ + AC_MSG_RESULT([no]) + tst_compi_memset_s="no" + ]) + fi + + if test "$tst_compi_memset_s" = "yes"; then + AC_MSG_CHECKING([if memset_s usage allowed]) + if test "x$curl_disallow_memset_s" != "xyes"; then + AC_MSG_RESULT([yes]) + tst_allow_memset_s="yes" + else + AC_MSG_RESULT([no]) + tst_allow_memset_s="no" + fi + fi + + AC_MSG_CHECKING([if memset_s might be used]) + if test "$tst_links_memset_s" = "yes" && + test "$tst_proto_memset_s" = "yes" && + test "$tst_compi_memset_s" = "yes" && + test "$tst_allow_memset_s" = "yes"; then + AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(HAVE_MEMSET_S, 1, + [Define to 1 if you have the memset_s function.]) + curl_cv_func_memset_s="yes" + else + AC_MSG_RESULT([no]) + curl_cv_func_memset_s="no" + fi +]) + dnl CURL_RUN_IFELSE dnl ------------------------------------------------- dnl Wrapper macro to use instead of AC_RUN_IFELSE. It