From 8a57adb3b04134c495e28846a4f214f0f6f7da0e Mon Sep 17 00:00:00 2001 From: Arran Cudbard-Bell Date: Tue, 12 Aug 2025 20:17:44 -0600 Subject: [PATCH] Place all md4/md5 functions in a struct and swap the pointer where we're building with OpenSSL !fips This prevents potential skew during startup --- src/lib/util/md4.c | 91 +++++++++++++++++++++++++++++++--------------- src/lib/util/md4.h | 39 +++++++++++++------- src/lib/util/md5.c | 76 +++++++++++++++++++++++--------------- src/lib/util/md5.h | 38 +++++++++++++------ 4 files changed, 161 insertions(+), 83 deletions(-) diff --git a/src/lib/util/md4.c b/src/lib/util/md4.c index 8f0f3e17b6..774f74e3d4 100644 --- a/src/lib/util/md4.c +++ b/src/lib/util/md4.c @@ -26,6 +26,32 @@ typedef struct { fr_md4_ctx_t *md_ctx; } fr_md4_free_list_t; static _Thread_local fr_md4_free_list_t *md4_array; + +static void fr_md4_local_ctx_reset(fr_md4_ctx_t *ctx); +static void fr_md4_local_ctx_copy(fr_md4_ctx_t *dst, fr_md4_ctx_t const *src); +#ifdef HAVE_OPENSSL_EVP_H +static fr_md4_ctx_t *fr_md4_local_ctx_init(void); +#else +static fr_md4_ctx_t *fr_md4_local_ctx_alloc(void); +#endif +static void fr_md4_local_ctx_free(fr_md4_ctx_t **ctx); +static void fr_md4_local_update(fr_md4_ctx_t *ctx, uint8_t const *in, size_t inlen); +static void fr_md4_local_final(uint8_t out[static MD4_DIGEST_LENGTH], fr_md4_ctx_t *ctx); + +static fr_md4_funcs_t md4_local_funcs = { + .reset = fr_md4_local_ctx_reset, + .copy = fr_md4_local_ctx_copy, +#ifdef HAVE_OPENSSL_EVP_H + .alloc = fr_md4_local_ctx_init, +#else + .alloc = fr_md4_local_ctx_alloc, +#endif + .free = fr_md4_local_ctx_free, + .update = fr_md4_local_update, + .final = fr_md4_local_final +}; +fr_md4_funcs_t const *fr_md4_funcs = &md4_local_funcs; + /* * If we have OpenSSL's EVP API available, then build wrapper functions. * @@ -39,8 +65,6 @@ static _Thread_local fr_md4_free_list_t *md4_array; # include # include -static int have_openssl_md4 = -1; - /** @copydoc fr_md4_ctx_reset * */ @@ -111,6 +135,15 @@ static void fr_md4_openssl_final(uint8_t out[static MD4_DIGEST_LENGTH], fr_md4_c if (!fr_cond_assert(len == MD4_DIGEST_LENGTH)) return; } + +static fr_md4_funcs_t md4_openssl_funcs = { + .reset = fr_md4_openssl_ctx_reset, + .copy = fr_md4_openssl_ctx_copy, + .alloc = fr_md4_openssl_ctx_alloc, + .free = fr_md4_openssl_ctx_free, + .update = fr_md4_openssl_update, + .final = fr_md4_openssl_final +}; #endif /* @@ -312,33 +345,6 @@ static fr_md4_ctx_t *fr_md4_local_ctx_alloc(void) { fr_md4_ctx_local_t *ctx_local; -#ifdef HAVE_OPENSSL_EVP_H - if (unlikely(have_openssl_md4 == -1)) { - /* - * If we're not in FIPS mode, then swap out the - * md4 functions, and call the OpenSSL init - * function. - */ - if (!EVP_default_properties_is_fips_enabled(NULL)) { - have_openssl_md4 = 1; - - /* - * Swap out the functions pointers - * for the OpenSSL versions. - */ - fr_md4_ctx_reset = fr_md4_openssl_ctx_reset; - fr_md4_ctx_copy = fr_md4_openssl_ctx_copy; - fr_md4_ctx_alloc = fr_md4_openssl_ctx_alloc; - fr_md4_ctx_free = fr_md4_openssl_ctx_free; - fr_md4_update = fr_md4_openssl_update; - fr_md4_final = fr_md4_openssl_final; - - return fr_md4_ctx_alloc(); - } - - have_openssl_md4 = 0; - } -#endif ctx_local = talloc(NULL, fr_md4_ctx_local_t); if (unlikely(!ctx_local)) return NULL; fr_md4_local_ctx_reset(ctx_local); @@ -346,6 +352,33 @@ static fr_md4_ctx_t *fr_md4_local_ctx_alloc(void) return ctx_local; } +#ifdef HAVE_OPENSSL_EVP_H +/** Initialize whether or not we use the local allocator, or the OpenSSL one. + * + */ +static fr_md4_ctx_t *fr_md4_local_ctx_init(void) +{ + /* + * If we are in FIPS mode, then use the local allocator. + */ + if (!EVP_default_properties_is_fips_enabled(NULL)) { + /* + * OpenSSL isn't in FIPS mode. Swap out the functions + * pointers for the OpenSSL versions. + * + * We do this by swapping out a pointer to a structure + * containing the functions, as this prevents possible + * skew where some threads see a mixture of functions. + */ + fr_md4_funcs = &md4_openssl_funcs; + } else { + md4_local_funcs.alloc = fr_md4_local_ctx_alloc; /* Don't call this (init) function again */ + } + + return fr_md4_ctx_alloc(); +} +#endif + /** @copydoc fr_md4_ctx_free * */ diff --git a/src/lib/util/md4.h b/src/lib/util/md4.h index 8ae9010f53..94ed5698a6 100644 --- a/src/lib/util/md4.h +++ b/src/lib/util/md4.h @@ -29,20 +29,38 @@ typedef void fr_md4_ctx_t; /* md4.c */ +typedef void (*fr_md4_ctx_reset_t)(fr_md4_ctx_t *ctx); +typedef void (*fr_md4_ctx_copy_t)(fr_md4_ctx_t *dst, fr_md4_ctx_t const *src); +typedef fr_md4_ctx_t *(*fr_md4_ctx_alloc_t)(void); +typedef void (*fr_md4_ctx_free_t)(fr_md4_ctx_t **ctx); +typedef void (*fr_md4_update_t)(fr_md4_ctx_t *ctx, uint8_t const *in, size_t inlen); +typedef void (*fr_md4_final_t)(uint8_t out[static MD4_DIGEST_LENGTH], fr_md4_ctx_t *ctx); + +typedef struct { + fr_md4_ctx_reset_t reset; + fr_md4_ctx_copy_t copy; + fr_md4_ctx_alloc_t alloc; + fr_md4_ctx_free_t free; + fr_md4_update_t update; + fr_md4_final_t final; +} fr_md4_funcs_t; + +/** Swap a single pointer, so all functions get swapped as an atomic operation + */ +extern fr_md4_funcs_t const *fr_md4_funcs; + /** Reset the ctx to allow reuse * * @param[in] ctx To reuse. */ -typedef void (*fr_md4_ctx_reset_t)(fr_md4_ctx_t *ctx); -extern fr_md4_ctx_reset_t fr_md4_ctx_reset; +#define fr_md4_ctx_reset(_ctx) fr_md4_funcs->reset(_ctx) /** Copy the contents of a ctx * * @param[in] dst Where to copy the context to. * @param[in] src Where to copy the context from. */ -typedef void (*fr_md4_ctx_copy_t)(fr_md4_ctx_t *dst, fr_md4_ctx_t const *src); -extern fr_md4_ctx_copy_t fr_md4_ctx_copy; +#define fr_md4_ctx_copy(_dst, _src) fr_md4_funcs->copy(_dst, _src) /** Allocation function for MD4 digest context * @@ -50,17 +68,14 @@ extern fr_md4_ctx_copy_t fr_md4_ctx_copy; * - An MD4 ctx. * - NULL if out of memory. */ -typedef fr_md4_ctx_t *(*fr_md4_ctx_alloc_t)(void); -extern fr_md4_ctx_alloc_t fr_md4_ctx_alloc; +#define fr_md4_ctx_alloc() fr_md4_funcs->alloc() /** Free function for MD4 digest ctx * * @param[in] ctx MD4 ctx to free. If the shared ctx is passed in * then the ctx is reset but not freed. */ -typedef void (*fr_md4_ctx_free_t)(fr_md4_ctx_t **ctx); -extern fr_md4_ctx_free_t fr_md4_ctx_free; - +#define fr_md4_ctx_free(_ctx) fr_md4_funcs->free(_ctx) /** Ingest plaintext into the digest * @@ -68,16 +83,14 @@ extern fr_md4_ctx_free_t fr_md4_ctx_free; * @param[in] in Data to ingest. * @param[in] inlen Length of data to ingest. */ -typedef void (*fr_md4_update_t)(fr_md4_ctx_t *ctx, uint8_t const *in, size_t inlen); -extern fr_md4_update_t fr_md4_update; +#define fr_md4_update(_ctx, _in, _inlen) fr_md4_funcs->update(_ctx, _in, _inlen) /** Finalise the ctx, producing the digest * * @param[out] out The MD4 digest. * @param[in] ctx To finalise. */ -typedef void (*fr_md4_final_t)(uint8_t out[static MD4_DIGEST_LENGTH], fr_md4_ctx_t *ctx); -extern fr_md4_final_t fr_md4_final; +#define fr_md4_final(_out, _ctx) fr_md4_funcs->final(_out, _ctx) /** Perform a single digest operation on a single input buffer * diff --git a/src/lib/util/md5.c b/src/lib/util/md5.c index e8a0a56ea3..c8885e9123 100644 --- a/src/lib/util/md5.c +++ b/src/lib/util/md5.c @@ -27,6 +27,31 @@ typedef struct { } fr_md5_free_list_t; static _Thread_local fr_md5_free_list_t *md5_array; +static void fr_md5_local_ctx_reset(fr_md5_ctx_t *ctx); +static void fr_md5_local_ctx_copy(fr_md5_ctx_t *dst, fr_md5_ctx_t const *src); +#ifdef HAVE_OPENSSL_EVP_H +static fr_md5_ctx_t *fr_md5_local_ctx_init(void); +#else +static fr_md5_ctx_t *fr_md5_local_ctx_alloc(void); +#endif +static void fr_md5_local_ctx_free(fr_md5_ctx_t **ctx); +static void fr_md5_local_update(fr_md5_ctx_t *ctx, uint8_t const *in, size_t inlen); +static void fr_md5_local_final(uint8_t out[static MD5_DIGEST_LENGTH], fr_md5_ctx_t *ctx); + +static fr_md5_funcs_t md5_local_funcs = { + .reset = fr_md5_local_ctx_reset, + .copy = fr_md5_local_ctx_copy, +#ifdef HAVE_OPENSSL_EVP_H + .alloc = fr_md5_local_ctx_init, +#else + .alloc = fr_md5_local_ctx_alloc, +#endif + .free = fr_md5_local_ctx_free, + .update = fr_md5_local_update, + .final = fr_md5_local_final +}; +fr_md5_funcs_t const *fr_md5_funcs = &md5_local_funcs; + /* * If we have OpenSSL's EVP API available, then build wrapper functions. * @@ -110,6 +135,15 @@ static void fr_md5_openssl_final(uint8_t out[static MD5_DIGEST_LENGTH], fr_md5_c if (!fr_cond_assert(len == MD5_DIGEST_LENGTH)) return; } + +static fr_md5_funcs_t md5_openssl_funcs = { + .reset = fr_md5_openssl_ctx_reset, + .copy = fr_md5_openssl_ctx_copy, + .alloc = fr_md5_openssl_ctx_alloc, + .free = fr_md5_openssl_ctx_free, + .update = fr_md5_openssl_update, + .final = fr_md5_openssl_final +}; #endif # define MD5_BLOCK_LENGTH 64 @@ -321,23 +355,21 @@ static fr_md5_ctx_t *fr_md5_local_ctx_init(void) /* * If we are in FIPS mode, then use the local allocator. */ - if (EVP_default_properties_is_fips_enabled(NULL)) { - fr_md5_ctx_alloc = fr_md5_local_ctx_alloc; - return fr_md5_local_ctx_alloc(); + if (!EVP_default_properties_is_fips_enabled(NULL)) { + /* + * OpenSSL isn't in FIPS mode. Swap out the functions + * pointers for the OpenSSL versions. + * + * We do this by swapping out a pointer to a structure + * containing the functions, as this prevents possible + * skew where some threads see a mixture of functions. + */ + fr_md5_funcs = &md5_openssl_funcs; + } else { + md5_local_funcs.alloc = fr_md5_local_ctx_alloc; /* Don't call this (init) function again */ } - /* - * Otherwise OpenSSL isn't in FIPS mode. Swap out the - * functions pointers for the OpenSSL versions. - */ - fr_md5_ctx_reset = fr_md5_openssl_ctx_reset; - fr_md5_ctx_copy = fr_md5_openssl_ctx_copy; - fr_md5_ctx_alloc = fr_md5_openssl_ctx_alloc; - fr_md5_ctx_free = fr_md5_openssl_ctx_free; - fr_md5_update = fr_md5_openssl_update; - fr_md5_final = fr_md5_openssl_final; - - return fr_md5_openssl_ctx_alloc(); + return fr_md5_ctx_alloc(); } #endif @@ -432,20 +464,6 @@ static void fr_md5_local_final(uint8_t out[static MD5_DIGEST_LENGTH], fr_md5_ctx memset(ctx_local, 0, sizeof(*ctx_local)); /* in case it's sensitive */ } -/* - * Digest function pointers - */ -fr_md5_ctx_reset_t fr_md5_ctx_reset = fr_md5_local_ctx_reset; -fr_md5_ctx_copy_t fr_md5_ctx_copy = fr_md5_local_ctx_copy; -#ifdef HAVE_OPENSSL_EVP_H -fr_md5_ctx_alloc_t fr_md5_ctx_alloc = fr_md5_local_ctx_init; /* check what we should do */ -#else -fr_md5_ctx_alloc_t fr_md5_ctx_alloc = fr_md5_local_ctx_alloc; -#endif -fr_md5_ctx_free_t fr_md5_ctx_free = fr_md5_local_ctx_free; -fr_md5_update_t fr_md5_update = fr_md5_local_update; -fr_md5_final_t fr_md5_final = fr_md5_local_final; - /** Calculate the MD5 hash of the contents of a buffer * * @param[out] out Where to write the MD5 digest. Must be a minimum of MD5_DIGEST_LENGTH. diff --git a/src/lib/util/md5.h b/src/lib/util/md5.h index de408dca67..9337b7afab 100644 --- a/src/lib/util/md5.h +++ b/src/lib/util/md5.h @@ -29,20 +29,38 @@ typedef void fr_md5_ctx_t; /* md5.c */ +typedef void (*fr_md5_ctx_reset_t)(fr_md5_ctx_t *ctx); +typedef void (*fr_md5_ctx_copy_t)(fr_md5_ctx_t *dst, fr_md5_ctx_t const *src); +typedef fr_md5_ctx_t *(*fr_md5_ctx_alloc_t)(void); +typedef void (*fr_md5_ctx_free_t)(fr_md5_ctx_t **ctx); +typedef void (*fr_md5_update_t)(fr_md5_ctx_t *ctx, uint8_t const *in, size_t inlen); +typedef void (*fr_md5_final_t)(uint8_t out[static MD5_DIGEST_LENGTH], fr_md5_ctx_t *ctx); + +typedef struct { + fr_md5_ctx_reset_t reset; + fr_md5_ctx_copy_t copy; + fr_md5_ctx_alloc_t alloc; + fr_md5_ctx_free_t free; + fr_md5_update_t update; + fr_md5_final_t final; +} fr_md5_funcs_t; + +/** Swap a single pointer, so all functions get swapped as an atomic operation + */ +extern fr_md5_funcs_t const *fr_md5_funcs; + /** Reset the ctx to allow reuse * * @param[in] ctx To reuse. */ -typedef void (*fr_md5_ctx_reset_t)(fr_md5_ctx_t *ctx); -extern fr_md5_ctx_reset_t fr_md5_ctx_reset; +#define fr_md5_ctx_reset(_ctx) fr_md5_funcs->reset(_ctx) /** Copy the contents of a ctx * * @param[in] dst Where to copy the context to. * @param[in] src Where to copy the context from. */ -typedef void (*fr_md5_ctx_copy_t)(fr_md5_ctx_t *dst, fr_md5_ctx_t const *src); -extern fr_md5_ctx_copy_t fr_md5_ctx_copy; +#define fr_md5_ctx_copy(_dst, _src) fr_md5_funcs->copy(_dst, _src) /** Allocation function for MD5 digest context * @@ -50,16 +68,14 @@ extern fr_md5_ctx_copy_t fr_md5_ctx_copy; * - An MD5 ctx. * - NULL if out of memory. */ -typedef fr_md5_ctx_t *(*fr_md5_ctx_alloc_t)(void); -extern fr_md5_ctx_alloc_t fr_md5_ctx_alloc; +#define fr_md5_ctx_alloc() fr_md5_funcs->alloc() /** Free function for MD5 digest ctx * * @param[in] ctx MD5 ctx to free. If the shared ctx is passed in * then the ctx is reset but not freed. */ -typedef void (*fr_md5_ctx_free_t)(fr_md5_ctx_t **ctx); -extern fr_md5_ctx_free_t fr_md5_ctx_free; +#define fr_md5_ctx_free(_ctx) fr_md5_funcs->free(_ctx) /** Ingest plaintext into the digest * @@ -67,16 +83,14 @@ extern fr_md5_ctx_free_t fr_md5_ctx_free; * @param[in] in Data to ingest. * @param[in] inlen Length of data to ingest. */ -typedef void (*fr_md5_update_t)(fr_md5_ctx_t *ctx, uint8_t const *in, size_t inlen); -extern fr_md5_update_t fr_md5_update; +#define fr_md5_update(_ctx, _in, _inlen) fr_md5_funcs->update(_ctx, _in, _inlen) /** Finalise the ctx, producing the digest * * @param[out] out The MD5 digest. * @param[in] ctx To finalise. */ -typedef void (*fr_md5_final_t)(uint8_t out[static MD5_DIGEST_LENGTH], fr_md5_ctx_t *ctx); -extern fr_md5_final_t fr_md5_final; +#define fr_md5_final(_out, _ctx) fr_md5_funcs->final(_out, _ctx) /** Perform a single digest operation on a single input buffer * -- 2.47.2