diff --git a/ChangeLog.d/rng-cloning.txt b/ChangeLog.d/rng-cloning.txt index fc7d4db7f5..6f68d26bb8 100644 --- a/ChangeLog.d/rng-cloning.txt +++ b/ChangeLog.d/rng-cloning.txt @@ -6,6 +6,10 @@ Features prediction resistance in the PSA random generator. Security + * If an application called psa_crypto_init() then fork() and continued to + use cryptography APIs (possibly indirectly, e.g. for TLS), the random + generator states were duplicated. Fix this by forcing a RNG reseed in + the child process. CVE-2026-25835 * Applications running in environments where the application state is cloned (for example due to resuming a frozen system state multiple times, or due to cloning a virtual machine image) should arrange to diff --git a/library/psa_crypto_random.c b/library/psa_crypto_random.c index f5eb658ab6..b0e4c859a7 100644 --- a/library/psa_crypto_random.c +++ b/library/psa_crypto_random.c @@ -13,11 +13,17 @@ #include "psa_crypto_core.h" #include "psa_crypto_random.h" #include "psa_crypto_random_impl.h" +#include "threading_internal.h" #if defined(MBEDTLS_PSA_INJECT_ENTROPY) #include "entropy_poll.h" #endif +#if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE) +/* For getpid(), for fork protection */ +#include +#endif + void psa_random_internal_init(mbedtls_psa_random_context_t *rng) { /* Set default configuration if @@ -53,6 +59,9 @@ psa_status_t psa_random_internal_seed(mbedtls_psa_random_context_t *rng) const unsigned char drbg_seed[] = "PSA"; int ret = mbedtls_psa_drbg_seed(&rng->drbg, &rng->entropy, drbg_seed, sizeof(drbg_seed) - 1); +#if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE) + rng->pid = getpid(); +#endif return mbedtls_to_psa_error(ret); } @@ -60,6 +69,27 @@ psa_status_t psa_random_internal_generate( mbedtls_psa_random_context_t *rng, uint8_t *output, size_t output_size) { +#if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE) + intmax_t pid = getpid(); + if (pid != rng->pid) { + /* This is a (grand...)child of the original process, but + * we inherited the RNG state from our parent. We must reseed! */ +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex); +#endif /* defined(MBEDTLS_THREADING_C) */ + int ret = mbedtls_psa_drbg_reseed(&rng->drbg, NULL, 0); + if (ret == 0) { + rng->pid = pid; + } +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex); +#endif /* defined(MBEDTLS_THREADING_C) */ + if (ret != 0) { + return mbedtls_to_psa_error(ret); + } + } +#endif /* MBEDTLS_PLATFORM_IS_UNIXLIKE */ + while (output_size > 0) { size_t request_size = (output_size > MBEDTLS_PSA_RANDOM_MAX_REQUEST ? diff --git a/library/psa_crypto_random_impl.h b/library/psa_crypto_random_impl.h index 3ed85e5c4e..68bb3a39f8 100644 --- a/library/psa_crypto_random_impl.h +++ b/library/psa_crypto_random_impl.h @@ -70,6 +70,23 @@ typedef struct { void (* entropy_free)(mbedtls_entropy_context *ctx); mbedtls_entropy_context entropy; mbedtls_psa_drbg_context_t drbg; +#if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE) + /* Fork protection: normally pid = getpid(). If the value changes, + * we are in a (grand)*child of the original process, so reseed + * the RNG to ensure that the child and the original process have + * distinct RNG states. See psa_random_internal_generate(). + * + * The type is intmax_t, not pid_t, for portability reasons: + * pid_t is defined in `unistd.h`, but on some platforms, it may + * only be defined if a certain compatibility level is requested + * by defining a macro such as _POSIX_C_SOURCE or _XOPEN_SOURCE. + * The macro needs to be defined before any system header, which + * may be hard to do in some C files that include this header + * (e.g. test suites). So we sidestep this complication, at the + * cost of possibly a few more instructions to compare pid values. + */ + intmax_t pid; +#endif } mbedtls_psa_random_context_t; /** Initialize the PSA DRBG.