src: add `curlx_memzero()` to clear buffers securely
No binary (build failed)
—
(log yok)
| @@ -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) | |
| @@ -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) | |
| @@ -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) | |
| @@ -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 | |
| @@ -49,11 +49,6 @@ | |
| #include <inet.h> | |
| #endif | |
| - | #ifdef __DragonFly__ |
| - | /* Required for __DragonFly_version */ |
| - | #include <sys/param.h> |
| - | #endif |
| - | |
| #include "urldata.h" | |
| #include "curl_trc.h" | |
| #include "if2ip.h" | |
| @@ -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 | |
| @@ -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 <curl/curl.h> /* 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 <sys/param.h> /* 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 */ | |
| @@ -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 <sys/param.h> |
| # 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; | |
| @@ -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); |
| + | } |
| @@ -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 | |