From: Arran Cudbard-Bell Date: Tue, 8 Jan 2019 02:53:15 +0000 (+0800) Subject: Allow local md5 functions to be selected at runtime depending on OpenSSL's FIPS setting X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a94f9dfd512d7a9f872022d77761d9251614cf98;p=thirdparty%2Ffreeradius-server.git Allow local md5 functions to be selected at runtime depending on OpenSSL's FIPS setting --- diff --git a/src/lib/util/hmac_md5.c b/src/lib/util/hmac_md5.c index d8a46eed10e..24bf1cc89e6 100644 --- a/src/lib/util/hmac_md5.c +++ b/src/lib/util/hmac_md5.c @@ -89,19 +89,19 @@ void fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t in void fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen, uint8_t const *key, size_t key_len) { - FR_MD5_CTX ctx; - uint8_t k_ipad[65]; /* inner padding - key XORd with ipad */ - uint8_t k_opad[65]; /* outer padding - key XORd with opad */ - uint8_t tk[16]; + fr_md5_ctx_t *ctx; + uint8_t k_ipad[65]; /* inner padding - key XORd with ipad */ + uint8_t k_opad[65]; /* outer padding - key XORd with opad */ + uint8_t tk[16]; int i; + ctx = fr_md5_ctx_alloc(true); + /* if key is longer than 64 bytes reset it to key=MD5(key) */ if (key_len > 64) { - FR_MD5_CTX tctx; - - fr_md5_init(&tctx); - fr_md5_update(&tctx, key, key_len); - fr_md5_final(tk, &tctx); + fr_md5_update(ctx, key, key_len); + fr_md5_final(tk, ctx); + fr_md5_ctx_reset(ctx); key = tk; key_len = 16; @@ -120,10 +120,10 @@ void fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t in */ /* start out by storing key in pads */ - memset( k_ipad, 0, sizeof(k_ipad)); - memset( k_opad, 0, sizeof(k_opad)); - memcpy( k_ipad, key, key_len); - memcpy( k_opad, key, key_len); + memset(k_ipad, 0, sizeof(k_ipad)); + memset(k_opad, 0, sizeof(k_opad)); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); /* XOR key with ipad and opad values */ for (i = 0; i < 64; i++) { @@ -133,20 +133,20 @@ void fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t in /* * perform inner MD5 */ - fr_md5_init(&ctx); /* init ctx for 1st - * pass */ - fr_md5_update(&ctx, k_ipad, 64); /* start with inner pad */ - fr_md5_update(&ctx, in, inlen); /* then in of datagram */ - fr_md5_final(digest, &ctx); /* finish up 1st pass */ + fr_md5_update(ctx, k_ipad, 64); /* start with inner pad */ + fr_md5_update(ctx, in, inlen); /* then in of datagram */ + fr_md5_final(digest, ctx); /* finish up 1st pass */ + + /* * perform outer MD5 */ - fr_md5_init(&ctx); /* init ctx for 2nd - * pass */ - fr_md5_update(&ctx, k_opad, 64); /* start with outer pad */ - fr_md5_update(&ctx, digest, 16); /* then results of 1st - * hash */ - fr_md5_final(digest, &ctx); /* finish up 2nd pass */ + fr_md5_ctx_reset(ctx); + fr_md5_update(ctx, k_opad, 64); /* start with outer pad */ + fr_md5_update(ctx, digest, 16); /* then results of 1st hash */ + fr_md5_final(digest, ctx); /* finish up 2nd pass */ + + fr_md5_ctx_free(&ctx); } #endif /* HAVE_OPENSSL_EVP_H */ diff --git a/src/lib/util/md5.c b/src/lib/util/md5.c index 8b50ccc7d75..007299800a9 100644 --- a/src/lib/util/md5.c +++ b/src/lib/util/md5.c @@ -6,28 +6,143 @@ */ RCSID("$Id$") +#include +#include +#include +#include +#include + /* * FORCE MD5 TO USE OUR MD5 HEADER FILE! * If we don't do this, it might pick up the systems broken MD5. */ #include "md5.h" -/** Calculate the MD5 hash of the contents of a buffer +fr_thread_local_setup(fr_md5_ctx_t *, md5_ctx) + +/* + * If we have OpenSSL's EVP API available, then build wrapper functions. * - * @param[out] out Where to write the MD5 digest. Must be a minimum of MD5_DIGEST_LENGTH. - * @param[in] in Data to hash. - * @param[in] inlen Length of the data. + * We always need to build the local MD5 functions as OpenSSL could + * be operating in FIPS mode where MD5 digest functions are unavailable. */ -void fr_md5_calc(uint8_t out[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen) +#ifdef HAVE_OPENSSL_EVP_H +# include +# include + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +# define EVP_MD_CTX_new EVP_MD_CTX_create +# define EVP_MD_CTX_free EVP_MD_CTX_destroy +# define EVP_MD_CTX_reset EVP_MD_CTX_cleanup +#endif + +static int have_openssl_md5 = -1; + +static void _md5_ctx_openssl_free_on_exit(void *arg) +{ + EVP_MD_CTX_free(arg); +} + +/** @copydoc fr_md5_ctx_reset + * + */ +static void fr_md5_openssl_ctx_reset(fr_md5_ctx_t *ctx) { - FR_MD5_CTX ctx; + EVP_MD_CTX *md_ctx = ctx; - fr_md5_init(&ctx); - fr_md5_update(&ctx, in, inlen); - fr_md5_final(out, &ctx); + EVP_MD_CTX_reset(md_ctx); + EVP_DigestInit_ex(md_ctx, EVP_md5(), NULL); } -#ifndef HAVE_OPENSSL_EVP_H +/** @copydoc fr_md5_ctx_copy + * + */ +static void fr_md5_openssl_ctx_copy(fr_md5_ctx_t *dst, fr_md5_ctx_t const *src) +{ + EVP_MD_CTX_copy_ex(dst, src); +} + +/** @copydoc fr_md5_ctx_alloc + * + */ +static fr_md5_ctx_t *fr_md5_openssl_ctx_alloc(bool thread_local) +{ + EVP_MD_CTX *md_ctx; + + /* + * Use the thread local ctx to avoid heap allocations. + */ + if (thread_local) { + if (unlikely(!md5_ctx)) { + md_ctx = EVP_MD_CTX_new(); + if (unlikely(!md_ctx)) { + oom: + fr_strerror_printf("Out of memory"); + return NULL; + } + fr_thread_local_set_destructor(md5_ctx, _md5_ctx_openssl_free_on_exit, md_ctx); + EVP_DigestInit_ex(md_ctx, EVP_md5(), NULL); + } else { + md_ctx = md5_ctx; + } + /* + * If the MD5 ctx might be used across a yield point + * shared should be set to false, and new contexts + * should be allocated. + */ + } else { + md_ctx = EVP_MD_CTX_new(); + if (unlikely(!md_ctx)) goto oom; + EVP_DigestInit_ex(md_ctx, EVP_md5(), NULL); + } + + return md_ctx; +} + +/** @copydoc fr_md5_ctx_free + * + */ +static void fr_md5_openssl_ctx_free(fr_md5_ctx_t **ctx) +{ + if (md5_ctx && (md5_ctx == *ctx)) { + fr_md5_openssl_ctx_reset(*ctx); + *ctx = NULL; + return; + } + + EVP_MD_CTX_free(*ctx); + *ctx = NULL; +} + +/** @copydoc fr_md5_update + * + */ +static void fr_md5_openssl_update(fr_md5_ctx_t *ctx, uint8_t const *in, size_t inlen) +{ + EVP_DigestUpdate(ctx, in, inlen); +} + +/** @copydoc fr_md5_final + * + */ +static void fr_md5_openssl_final(uint8_t out[static MD5_DIGEST_LENGTH], fr_md5_ctx_t *ctx) +{ + unsigned int len; + + EVP_DigestFinal(ctx, out, &len); + + if (!fr_cond_assert(len == MD5_DIGEST_LENGTH)) return; +} +#endif + +# define MD5_BLOCK_LENGTH 64 +typedef struct { + uint32_t state[4]; //!< State. + uint32_t count[2]; //!< Number of bits, mod 2^64. + uint8_t buffer[MD5_BLOCK_LENGTH]; //!< Input buffer. +} fr_md5_ctx_local_t; + + /* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was @@ -68,96 +183,6 @@ static const uint8_t PADDING[MD5_BLOCK_LENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -/** Initialise a new MD5 context - * - * Set bit count to 0 and buffer to mysterious initialization constants. - * - * @param[out] ctx to initialise. - */ -void fr_md5_init(FR_MD5_CTX *ctx) -{ - ctx->count[0] = 0; - ctx->count[1] = 0; - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xefcdab89; - ctx->state[2] = 0x98badcfe; - ctx->state[3] = 0x10325476; -} - -/** Feed additional data into the MD5 hashing function - * - * @param[in,out] ctx to update. - * @param[in] in Data to hash. - * @param[in] inlen Length of the data. - */ -void fr_md5_update(FR_MD5_CTX *ctx, uint8_t const *in, size_t inlen) -{ - size_t have, need; - - /* Check how many bytes we already have and how many more we need. */ - have = (size_t)((ctx->count[0] >> 3) & (MD5_BLOCK_LENGTH - 1)); - need = MD5_BLOCK_LENGTH - have; - - /* Update bitcount */ -/* ctx->count += (uint64_t)inlen << 3;*/ - if ((ctx->count[0] += ((uint32_t)inlen << 3)) < (uint32_t)inlen) { - /* Overflowed ctx->count[0] */ - ctx->count[1]++; - } - ctx->count[1] += ((uint32_t)inlen >> 29); - - if (inlen >= need) { - if (have != 0) { - memcpy(ctx->buffer + have, in, need); - fr_md5_transform(ctx->state, ctx->buffer); - in += need; - inlen -= need; - have = 0; - } - - /* Process data in MD5_BLOCK_LENGTH-byte chunks. */ - while (inlen >= MD5_BLOCK_LENGTH) { - fr_md5_transform(ctx->state, in); - in += MD5_BLOCK_LENGTH; - inlen -= MD5_BLOCK_LENGTH; - } - } - - /* Handle any remaining bytes of data. */ - if (inlen != 0) memcpy(ctx->buffer + have, in, inlen); -} - -/** Finalise the MD5 context and write out the hash - * - * Final wrapup - pad to 64-byte boundary with the bit pattern 1 0* - * (64-bit count of bits processed, MSB-first). - * - * @param[out] out Where to write the MD5 digest. Minimum length of MD5_DIGEST_LENGTH. - * @param[in,out] ctx to finalise. - */ -void fr_md5_final(uint8_t out[static MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx) -{ - uint8_t count[8]; - size_t padlen; - int i; - - /* Convert count to 8 bytes in little endian order. */ - PUT_64BIT_LE(count, ctx->count); - - /* Pad out to 56 mod 64. */ - padlen = MD5_BLOCK_LENGTH - - ((ctx->count[0] >> 3) & (MD5_BLOCK_LENGTH - 1)); - if (padlen < 1 + 8) - padlen += MD5_BLOCK_LENGTH; - fr_md5_update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ - fr_md5_update(ctx, count, 8); - - if (out != NULL) { - for (i = 0; i < 4; i++) - PUT_32BIT_LE(out + i * 4, ctx->state[i]); - } - memset(ctx, 0, sizeof(*ctx)); /* in case it's sensitive */ -} /* The four core functions - F1 is optimized somewhat */ #define F1(x, y, z) (z ^ (x & (y ^ z))) @@ -177,7 +202,7 @@ void fr_md5_final(uint8_t out[static MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx) * @param[in] state 16 bytes of data to feed into the hashing function. * @param[in,out] block MD5 digest block to update. */ -void fr_md5_transform(uint32_t state[static 4], uint8_t const block[static MD5_BLOCK_LENGTH]) +static void fr_md5_local_transform(uint32_t state[static 4], uint8_t const block[static MD5_BLOCK_LENGTH]) { uint32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4]; @@ -267,4 +292,206 @@ void fr_md5_transform(uint32_t state[static 4], uint8_t const block[static MD5_B state[2] += c; state[3] += d; } + +/** @copydoc fr_md5_ctx_reset + * + */ +static void fr_md5_local_ctx_reset(fr_md5_ctx_t *ctx) +{ + fr_md5_ctx_local_t *ctx_local = talloc_get_type_abort(ctx, fr_md5_ctx_local_t); + + ctx_local->count[0] = 0; + ctx_local->count[1] = 0; + ctx_local->state[0] = 0x67452301; + ctx_local->state[1] = 0xefcdab89; + ctx_local->state[2] = 0x98badcfe; + ctx_local->state[3] = 0x10325476; +} + +/** @copydoc fr_md5_ctx_copy + * + */ +static void fr_md5_local_ctx_copy(fr_md5_ctx_t *dst, fr_md5_ctx_t const *src) +{ + fr_md5_ctx_local_t const *ctx_local_src = talloc_get_type_abort_const(src, fr_md5_ctx_local_t); + fr_md5_ctx_local_t *ctx_local_dst = talloc_get_type_abort(dst, fr_md5_ctx_local_t); + + memcpy(ctx_local_dst, ctx_local_src, sizeof(*ctx_local_dst)); +} + +static void _md5_ctx_local_free_on_exit(void *arg) +{ + talloc_free(arg); +} + +/** @copydoc fr_md5_ctx_alloc + * + */ +static fr_md5_ctx_t *fr_md5_local_ctx_alloc(bool thread_local) +{ + fr_md5_ctx_local_t *ctx_local; + +#ifdef HAVE_OPENSSL_EVP_H + if (unlikely(have_openssl_md5 == -1)) { + /* + * If we're not in FIPS mode, then swap out the + * md5 functions, and call the OpenSSL init + * function. + */ + if (FIPS_mode() == 0) { + have_openssl_md5 = 1; + + /* + * 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_ctx_alloc(thread_local); + } + + have_openssl_md5 = 0; + } #endif + + /* + * Use the thread local ctx to avoid heap allocations. + */ + if (thread_local) { + if (unlikely(!md5_ctx)) { + ctx_local = talloc(NULL, fr_md5_ctx_local_t); + if (unlikely(!ctx_local)) return NULL; + fr_md5_local_ctx_reset(ctx_local); + fr_thread_local_set_destructor(md5_ctx, _md5_ctx_local_free_on_exit, ctx_local); + } else { + ctx_local = md5_ctx; + } + /* + * If the MD5 ctx might be used across a yield point + * shared should be set to false, and new contexts + * should be allocated. + */ + } else { + ctx_local = talloc(NULL, fr_md5_ctx_local_t); + if (unlikely(!ctx_local)) return NULL; + fr_md5_local_ctx_reset(ctx_local); + } + + return ctx_local; +} + +/** @copydoc fr_md5_ctx_free + * + */ +static void fr_md5_local_ctx_free(fr_md5_ctx_t **ctx) +{ + if (md5_ctx && (md5_ctx == *ctx)) { + fr_md5_local_ctx_reset(*ctx); + *ctx = NULL; + return; /* Don't free the thread_local ctx */ + } + + talloc_free(*ctx); + *ctx = NULL; +} + +/** @copydoc fr_md5_update + * + */ +static void fr_md5_local_update(fr_md5_ctx_t *ctx, uint8_t const *in, size_t inlen) +{ + fr_md5_ctx_local_t *ctx_local = talloc_get_type_abort(ctx, fr_md5_ctx_local_t); + + size_t have, need; + + /* Check how many bytes we already have and how many more we need. */ + have = (size_t)((ctx_local->count[0] >> 3) & (MD5_BLOCK_LENGTH - 1)); + need = MD5_BLOCK_LENGTH - have; + + /* Update bitcount */ +/* ctx_local->count += (uint64_t)inlen << 3;*/ + if ((ctx_local->count[0] += ((uint32_t)inlen << 3)) < (uint32_t)inlen) { + /* Overflowed ctx_local->count[0] */ + ctx_local->count[1]++; + } + ctx_local->count[1] += ((uint32_t)inlen >> 29); + + if (inlen >= need) { + if (have != 0) { + memcpy(ctx_local->buffer + have, in, need); + fr_md5_local_transform(ctx_local->state, ctx_local->buffer); + in += need; + inlen -= need; + have = 0; + } + + /* Process data in MD5_BLOCK_LENGTH-byte chunks. */ + while (inlen >= MD5_BLOCK_LENGTH) { + fr_md5_local_transform(ctx_local->state, in); + in += MD5_BLOCK_LENGTH; + inlen -= MD5_BLOCK_LENGTH; + } + } + + /* Handle any remaining bytes of data. */ + if (inlen != 0) memcpy(ctx_local->buffer + have, in, inlen); +} + +/** @copydoc fr_md5_final + * + */ +static void fr_md5_local_final(uint8_t out[static MD5_DIGEST_LENGTH], fr_md5_ctx_t *ctx) +{ + fr_md5_ctx_local_t *ctx_local = talloc_get_type_abort(ctx, fr_md5_ctx_local_t); + uint8_t count[8]; + size_t padlen; + int i; + + /* Convert count to 8 bytes in little endian order. */ + PUT_64BIT_LE(count, ctx_local->count); + + /* Pad out to 56 mod 64. */ + padlen = MD5_BLOCK_LENGTH - + ((ctx_local->count[0] >> 3) & (MD5_BLOCK_LENGTH - 1)); + if (padlen < 1 + 8) + padlen += MD5_BLOCK_LENGTH; + fr_md5_update(ctx_local, PADDING, padlen - 8); /* padlen - 8 <= 64 */ + fr_md5_update(ctx_local, count, 8); + + if (out != NULL) { + for (i = 0; i < 4; i++) + PUT_32BIT_LE(out + i * 4, ctx_local->state[i]); + } + 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; +fr_md5_ctx_alloc_t fr_md5_ctx_alloc = fr_md5_local_ctx_alloc; +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. + * @param[in] in Data to hash. + * @param[in] inlen Length of the data. + */ +void fr_md5_calc(uint8_t out[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen) +{ + fr_md5_ctx_t *ctx; + + ctx = fr_md5_ctx_alloc(true); + fr_md5_update(ctx, in, inlen); + fr_md5_final(out, ctx); + fr_md5_ctx_free(&ctx); +} diff --git a/src/lib/util/md5.h b/src/lib/util/md5.h index 62ba1340a25..46bdfeee856 100644 --- a/src/lib/util/md5.h +++ b/src/lib/util/md5.h @@ -4,6 +4,7 @@ * @note license is LGPL, but largely derived from a public domain source. * * @file src/lib/util/md5.h + * @brief Structures and declarations for md5. */ RCSIDH(md5_h, "$Id$") @@ -14,72 +15,78 @@ extern "C" { #include #include -#ifdef HAVE_INTTYPES_H -# include -#endif +#include +#include +#include +#include +#include -#ifdef HAVE_SYS_TYPES_H -# include +#ifndef MD5_DIGEST_LENGTH +# define MD5_DIGEST_LENGTH 16 #endif -#ifdef HAVE_STDINT_H -# include -#endif +typedef void fr_md5_ctx_t; -# include +/* md5.c */ -#ifdef HAVE_OPENSSL_EVP_H -# include -#endif +/** 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; -#ifndef MD5_DIGEST_LENGTH -# define MD5_DIGEST_LENGTH 16 -#endif +/** 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; -#ifndef HAVE_OPENSSL_EVP_H -/* - * The MD5 code used here and in md5.c was originally retrieved from: - * http://www.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/crypto/md5.h?rev=1.1 +/** Allocation function for MD5 digest context * - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. + * @param[in] shared Whether we allocate a new context or use the thread local context. + * @return + * - An MD5 ctx. + * - NULL if out of memory. + */ +typedef fr_md5_ctx_t *(*fr_md5_ctx_alloc_t)(bool shared); +extern fr_md5_ctx_alloc_t fr_md5_ctx_alloc; + +/** Free function for MD5 digest ctx * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. + * @param[in] ctx MD5 ctx to free. If the shared ctx is passed in + * then the ctx is reset but not freed. */ -# define MD5_BLOCK_LENGTH 64 -typedef struct { - uint32_t state[4]; //!< State. - uint32_t count[2]; //!< Number of bits, mod 2^64. - uint8_t buffer[MD5_BLOCK_LENGTH]; //!< Input buffer. -} FR_MD5_CTX; +typedef void (*fr_md5_ctx_free_t)(fr_md5_ctx_t **ctx); +extern fr_md5_ctx_free_t fr_md5_ctx_free; -void fr_md5_init(FR_MD5_CTX *ctx); -void fr_md5_update(FR_MD5_CTX *ctx, uint8_t const *in, size_t inlen); -void fr_md5_final(uint8_t out[static MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx); -void fr_md5_transform(uint32_t state[static 4], uint8_t const block[static MD5_BLOCK_LENGTH]); -# define fr_md5_copy(_out, _in) memcpy(_out, _in, sizeof(*_out)) -#else /* HAVE_OPENSSL_EVP_H */ -USES_APPLE_DEPRECATED_API -#include -# define FR_MD5_CTX MD5_CTX -# define fr_md5_init MD5_Init -# define fr_md5_update MD5_Update -# define fr_md5_final MD5_Final -# define fr_md5_transform MD5_Transform -# define fr_md5_copy(_out, _in) memcpy(_out, _in, sizeof(*_out)) -#endif +/** Ingest plaintext into the digest + * + * @param[in] ctx To ingest data into. + * @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; -/* hmac.c */ -void fr_hmac_md5(uint8_t digest[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen, - uint8_t const *key, size_t key_len); -/* md5.c */ -void fr_md5_calc(uint8_t out[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t 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; + +/** Perform a single digest operation on a single input buffer + * + */ +void fr_md5_calc(uint8_t out[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen); +/* hmac.c */ +void fr_hmac_md5(uint8_t digest[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen, + uint8_t const *key, size_t key_len); #ifdef __cplusplus } #endif diff --git a/src/modules/rlm_cram/rlm_cram.c b/src/modules/rlm_cram/rlm_cram.c index a3c5292cb35..e71bb0fca83 100644 --- a/src/modules/rlm_cram/rlm_cram.c +++ b/src/modules/rlm_cram/rlm_cram.c @@ -74,33 +74,37 @@ fr_dict_attr_autoload_t rlm_cram_dict_attr[] = { static void calc_apop_digest(uint8_t *buffer, uint8_t const *challenge, size_t challen, char const *password) { - FR_MD5_CTX context; + fr_md5_ctx_t *md5_ctx; - fr_md5_init(&context); - fr_md5_update(&context, challenge, challen); - fr_md5_update(&context, (uint8_t const *) password, strlen(password)); - fr_md5_final(buffer, &context); + md5_ctx = fr_md5_ctx_alloc(true); + fr_md5_update(md5_ctx, challenge, challen); + fr_md5_update(md5_ctx, (uint8_t const *) password, strlen(password)); + fr_md5_final(buffer, md5_ctx); + fr_md5_ctx_free(&md5_ctx); } - static void calc_md5_digest(uint8_t *buffer, uint8_t const *challenge, size_t challen, char const *password) { - uint8_t buf[1024]; - int i; - FR_MD5_CTX context; + uint8_t buf[1024]; + int i; + fr_md5_ctx_t *md5_ctx; memset(buf, 0, 1024); memset(buf, 0x36, 64); - for(i=0; i<64 && password[i]; i++) buf[i]^=password[i]; - memcpy(buf+64, challenge, challen); - fr_md5_init(&context); - fr_md5_update(&context, buf, 64+challen); + for (i = 0; i < 64 && password[i]; i++) buf[i] ^= password[i]; + memcpy(buf + 64, challenge, challen); + + md5_ctx = fr_md5_ctx_alloc(true); + + fr_md5_update(md5_ctx, buf, 64 + challen); memset(buf, 0x5c, 64); - for(i=0; i<64 && password[i]; i++) buf[i]^=password[i]; - fr_md5_final(buf+64,&context); - fr_md5_init(&context); - fr_md5_update(&context,buf,64+16); - fr_md5_final(buffer,&context); + for (i = 0; i < 64 && password[i]; i++) buf[i] ^= password[i]; + fr_md5_final(buf + 64, md5_ctx); + + fr_md5_ctx_reset(md5_ctx); + fr_md5_update(md5_ctx, buf, 64 + 16); + fr_md5_final(buffer, md5_ctx); + fr_md5_ctx_free(&md5_ctx); } static void calc_md4_digest(uint8_t *buffer, uint8_t const *challenge, size_t challen, char const *password) diff --git a/src/modules/rlm_pap/rlm_pap.c b/src/modules/rlm_pap/rlm_pap.c index c5c0b33f19a..67c0edaef50 100644 --- a/src/modules/rlm_pap/rlm_pap.c +++ b/src/modules/rlm_pap/rlm_pap.c @@ -682,7 +682,7 @@ static rlm_rcode_t CC_HINT(nonnull) pap_auth_md5(rlm_pap_t const *inst, REQUEST static rlm_rcode_t CC_HINT(nonnull) pap_auth_smd5(rlm_pap_t const *inst, REQUEST *request, VALUE_PAIR *vp) { - FR_MD5_CTX md5_context; + fr_md5_ctx_t *md5_ctx; uint8_t digest[128]; RDEBUG("Comparing with \"known-good\" SMD5-Password"); @@ -693,10 +693,11 @@ static rlm_rcode_t CC_HINT(nonnull) pap_auth_smd5(rlm_pap_t const *inst, REQUEST return RLM_MODULE_INVALID; } - fr_md5_init(&md5_context); - fr_md5_update(&md5_context, request->password->vp_octets, request->password->vp_length); - fr_md5_update(&md5_context, vp->vp_octets + MD5_DIGEST_LENGTH, vp->vp_length - MD5_DIGEST_LENGTH); - fr_md5_final(digest, &md5_context); + md5_ctx = fr_md5_ctx_alloc(true); + fr_md5_update(md5_ctx, request->password->vp_octets, request->password->vp_length); + fr_md5_update(md5_ctx, vp->vp_octets + MD5_DIGEST_LENGTH, vp->vp_length - MD5_DIGEST_LENGTH); + fr_md5_final(digest, md5_ctx); + fr_md5_ctx_free(&md5_ctx); /* * Compare only the MD5 hash results, not the salt. diff --git a/src/protocols/radius/base.c b/src/protocols/radius/base.c index 2268ab5dfa3..03e2c7bcd8f 100644 --- a/src/protocols/radius/base.c +++ b/src/protocols/radius/base.c @@ -234,13 +234,14 @@ size_t fr_radius_attr_len(VALUE_PAIR const *vp) */ void fr_radius_ascend_secret(uint8_t *digest, uint8_t const *vector, char const *secret, uint8_t const *value) { - FR_MD5_CTX context; - int i; + fr_md5_ctx_t *md5_ctx; + int i; - fr_md5_init(&context); - fr_md5_update(&context, vector, AUTH_VECTOR_LEN); - fr_md5_update(&context, (uint8_t const *) secret, talloc_array_length(secret) - 1); - fr_md5_final(digest, &context); + md5_ctx = fr_md5_ctx_alloc(true); + fr_md5_update(md5_ctx, vector, AUTH_VECTOR_LEN); + fr_md5_update(md5_ctx, (uint8_t const *) secret, talloc_array_length(secret) - 1); + fr_md5_final(digest, md5_ctx); + fr_md5_ctx_free(&md5_ctx); for (i = 0; i < AUTH_VECTOR_LEN; i++ ) digest[i] ^= value[i]; } @@ -334,7 +335,6 @@ int fr_radius_sign(uint8_t *packet, uint8_t const *original, { uint8_t *msg, *end; size_t packet_len = (packet[2] << 8) | packet[3]; - FR_MD5_CTX context; /* * No real limit on secret length, this is just @@ -466,10 +466,15 @@ int fr_radius_sign(uint8_t *packet, uint8_t const *original, /* * Request / Response Authenticator = MD5(packet + secret) */ - fr_md5_init(&context); - fr_md5_update(&context, packet, packet_len); - fr_md5_update(&context, secret, secret_len); - fr_md5_final(packet + 4, &context); + { + fr_md5_ctx_t *md5_ctx; + + md5_ctx = fr_md5_ctx_alloc(true); + fr_md5_update(md5_ctx, packet, packet_len); + fr_md5_update(md5_ctx, secret, secret_len); + fr_md5_final(packet + 4, md5_ctx); + fr_md5_ctx_free(&md5_ctx); + } return 0; } diff --git a/src/protocols/radius/decode.c b/src/protocols/radius/decode.c index 14383da7d93..9efa0657cd2 100644 --- a/src/protocols/radius/decode.c +++ b/src/protocols/radius/decode.c @@ -59,7 +59,7 @@ static void memcpy_bounded(void * restrict dst, const void * restrict src, size_ ssize_t fr_radius_decode_tunnel_password(uint8_t *passwd, size_t *pwlen, char const *secret, uint8_t const *vector, bool tunnel_password_zeros) { - FR_MD5_CTX context, old; + fr_md5_ctx_t *md5_ctx, *md5_ctx_old; uint8_t digest[AUTH_VECTOR_LEN]; int secretlen; size_t i, n, encrypted_len, embedded_len; @@ -97,17 +97,19 @@ ssize_t fr_radius_decode_tunnel_password(uint8_t *passwd, size_t *pwlen, */ secretlen = talloc_array_length(secret) - 1; - fr_md5_init(&context); - fr_md5_update(&context, (uint8_t const *) secret, secretlen); - fr_md5_copy(&old, &context); /* save intermediate work */ + md5_ctx = fr_md5_ctx_alloc(false); + md5_ctx_old = fr_md5_ctx_alloc(true); + + fr_md5_update(md5_ctx, (uint8_t const *) secret, secretlen); + fr_md5_ctx_copy(md5_ctx_old, md5_ctx); /* save intermediate work */ /* * Set up the initial key: * * b(1) = MD5(secret + vector + salt) */ - fr_md5_update(&context, vector, AUTH_VECTOR_LEN); - fr_md5_update(&context, passwd, 2); + fr_md5_update(md5_ctx, vector, AUTH_VECTOR_LEN); + fr_md5_update(md5_ctx, passwd, 2); embedded_len = 0; for (n = 0; n < encrypted_len; n += AUTH_PASS_LEN) { @@ -124,8 +126,8 @@ ssize_t fr_radius_decode_tunnel_password(uint8_t *passwd, size_t *pwlen, if (n == 0) { base = 1; - fr_md5_final(digest, &context); - fr_md5_copy(&context, &old); + fr_md5_final(digest, md5_ctx); + fr_md5_ctx_copy(md5_ctx, md5_ctx_old); /* * A quick check: decrypt the first octet @@ -136,18 +138,20 @@ ssize_t fr_radius_decode_tunnel_password(uint8_t *passwd, size_t *pwlen, if (embedded_len > encrypted_len) { fr_strerror_printf("Tunnel Password is too long for the attribute " "(shared secret is probably incorrect!)"); + fr_md5_ctx_free(&md5_ctx); + fr_md5_ctx_free(&md5_ctx_old); return -1; } - fr_md5_update(&context, passwd + 2, block_len); + fr_md5_update(md5_ctx, passwd + 2, block_len); } else { base = 0; - fr_md5_final(digest, &context); + fr_md5_final(digest, md5_ctx); - fr_md5_copy(&context, &old); - fr_md5_update(&context, passwd + n + 2, block_len); + fr_md5_ctx_copy(md5_ctx, md5_ctx_old); + fr_md5_update(md5_ctx, passwd + n + 2, block_len); } for (i = base; i < block_len; i++) { @@ -155,6 +159,9 @@ ssize_t fr_radius_decode_tunnel_password(uint8_t *passwd, size_t *pwlen, } } + fr_md5_ctx_free(&md5_ctx); + fr_md5_ctx_free(&md5_ctx_old); + /* * Check trailing bytes */ @@ -179,7 +186,7 @@ ssize_t fr_radius_decode_tunnel_password(uint8_t *passwd, size_t *pwlen, */ ssize_t fr_radius_decode_password(char *passwd, size_t pwlen, char const *secret, uint8_t const *vector) { - FR_MD5_CTX context, old; + fr_md5_ctx_t *md5_ctx, *md5_ctx_old; uint8_t digest[AUTH_VECTOR_LEN]; int i; size_t n, secretlen; @@ -201,34 +208,39 @@ ssize_t fr_radius_decode_password(char *passwd, size_t pwlen, char const *secret */ secretlen = talloc_array_length(secret) - 1; - fr_md5_init(&context); - fr_md5_update(&context, (uint8_t const *) secret, secretlen); - fr_md5_copy(&old, &context); /* save intermediate work */ + md5_ctx = fr_md5_ctx_alloc(false); + md5_ctx_old = fr_md5_ctx_alloc(true); + + fr_md5_update(md5_ctx, (uint8_t const *) secret, secretlen); + fr_md5_ctx_copy(md5_ctx_old, md5_ctx); /* save intermediate work */ /* * The inverse of the code above. */ for (n = 0; n < pwlen; n += AUTH_PASS_LEN) { if (n == 0) { - fr_md5_update(&context, vector, AUTH_VECTOR_LEN); - fr_md5_final(digest, &context); + fr_md5_update(md5_ctx, vector, AUTH_VECTOR_LEN); + fr_md5_final(digest, md5_ctx); - fr_md5_copy(&context, &old); + fr_md5_ctx_copy(md5_ctx, md5_ctx_old); if (pwlen > AUTH_PASS_LEN) { - fr_md5_update(&context, (uint8_t *) passwd, AUTH_PASS_LEN); + fr_md5_update(md5_ctx, (uint8_t *) passwd, AUTH_PASS_LEN); } } else { - fr_md5_final(digest, &context); + fr_md5_final(digest, md5_ctx); - fr_md5_copy(&context, &old); + fr_md5_ctx_copy(md5_ctx, md5_ctx_old); if (pwlen > (n + AUTH_PASS_LEN)) { - fr_md5_update(&context, (uint8_t *) passwd + n, AUTH_PASS_LEN); + fr_md5_update(md5_ctx, (uint8_t *) passwd + n, AUTH_PASS_LEN); } } for (i = 0; i < AUTH_PASS_LEN; i++) passwd[i + n] ^= digest[i]; } + fr_md5_ctx_free(&md5_ctx); + fr_md5_ctx_free(&md5_ctx_old); + done: passwd[pwlen] = '\0'; return strlen(passwd); diff --git a/src/protocols/radius/encode.c b/src/protocols/radius/encode.c index ccfc3283e3d..d61061ddf08 100644 --- a/src/protocols/radius/encode.c +++ b/src/protocols/radius/encode.c @@ -219,7 +219,7 @@ int fr_radius_encode_tunnel_password(char *passwd, size_t *pwlen, char const *se */ int fr_radius_encode_password(char *passwd, size_t *pwlen, char const *secret, uint8_t const *vector) { - FR_MD5_CTX context, old; + fr_md5_ctx_t *md5_ctx, *md5_ctx_old; uint8_t digest[AUTH_VECTOR_LEN]; int i, n, secretlen; int len; @@ -250,9 +250,11 @@ int fr_radius_encode_password(char *passwd, size_t *pwlen, char const *secret, u */ secretlen = talloc_array_length(secret) - 1; - fr_md5_init(&context); - fr_md5_update(&context, (uint8_t const *) secret, secretlen); - fr_md5_copy(&old, &context); /* save intermediate work */ + md5_ctx = fr_md5_ctx_alloc(false); + md5_ctx_old = fr_md5_ctx_alloc(true); + + fr_md5_update(md5_ctx, (uint8_t const *) secret, secretlen); + fr_md5_ctx_copy(md5_ctx_old, md5_ctx); /* save intermediate work */ /* * Encrypt it in place. Don't bother checking @@ -260,24 +262,27 @@ int fr_radius_encode_password(char *passwd, size_t *pwlen, char const *secret, u */ for (n = 0; n < len; n += AUTH_PASS_LEN) { if (n == 0) { - fr_md5_update(&context, vector, AUTH_PASS_LEN); - fr_md5_final(digest, &context); + fr_md5_update(md5_ctx, vector, AUTH_PASS_LEN); + fr_md5_final(digest, md5_ctx); } else { - fr_md5_copy(&context, &old); - fr_md5_update(&context, (uint8_t *) passwd + n - AUTH_PASS_LEN, AUTH_PASS_LEN); - fr_md5_final(digest, &context); + fr_md5_ctx_copy(md5_ctx, md5_ctx_old); + fr_md5_update(md5_ctx, (uint8_t *) passwd + n - AUTH_PASS_LEN, AUTH_PASS_LEN); + fr_md5_final(digest, md5_ctx); } for (i = 0; i < AUTH_PASS_LEN; i++) passwd[i + n] ^= digest[i]; } + fr_md5_ctx_free(&md5_ctx); + fr_md5_ctx_free(&md5_ctx_old); + return 0; } static void encode_password(uint8_t *out, ssize_t *outlen, uint8_t const *input, size_t inlen, char const *secret, uint8_t const *vector) { - FR_MD5_CTX context, old; + fr_md5_ctx_t *md5_ctx, *md5_ctx_old; uint8_t digest[AUTH_VECTOR_LEN]; uint8_t passwd[MAX_PASS_LEN]; size_t i, n; @@ -300,25 +305,30 @@ static void encode_password(uint8_t *out, ssize_t *outlen, uint8_t const *input, } *outlen = len; - fr_md5_init(&context); - fr_md5_update(&context, (uint8_t const *) secret, talloc_array_length(secret) - 1); - fr_md5_copy(&old, &context); + md5_ctx = fr_md5_ctx_alloc(false); + md5_ctx_old = fr_md5_ctx_alloc(true); + + fr_md5_update(md5_ctx, (uint8_t const *) secret, talloc_array_length(secret) - 1); + fr_md5_ctx_copy(md5_ctx_old, md5_ctx); /* * Do first pass. */ - fr_md5_update(&context, vector, AUTH_PASS_LEN); + fr_md5_update(md5_ctx, vector, AUTH_PASS_LEN); for (n = 0; n < len; n += AUTH_PASS_LEN) { if (n > 0) { - fr_md5_copy(&context, &old); - fr_md5_update(&context, passwd + n - AUTH_PASS_LEN, AUTH_PASS_LEN); + fr_md5_ctx_copy(md5_ctx, md5_ctx_old); + fr_md5_update(md5_ctx, passwd + n - AUTH_PASS_LEN, AUTH_PASS_LEN); } - fr_md5_final(digest, &context); + fr_md5_final(digest, md5_ctx); for (i = 0; i < AUTH_PASS_LEN; i++) passwd[i + n] ^= digest[i]; } + fr_md5_ctx_free(&md5_ctx); + fr_md5_ctx_free(&md5_ctx_old); + memcpy(out, passwd, len); } @@ -327,7 +337,7 @@ static void encode_tunnel_password(uint8_t *out, ssize_t *outlen, uint8_t const *input, size_t inlen, size_t freespace, char const *secret, uint8_t const *vector) { - FR_MD5_CTX context, old; + fr_md5_ctx_t *md5_ctx, *md5_ctx_old; uint8_t digest[AUTH_VECTOR_LEN]; size_t i, n; size_t encrypted_len; @@ -386,21 +396,23 @@ static void encode_tunnel_password(uint8_t *out, ssize_t *outlen, out[1] = fr_rand(); out[2] = inlen; /* length of the password string */ - fr_md5_init(&context); - fr_md5_update(&context, (uint8_t const *) secret, talloc_array_length(secret) - 1); - fr_md5_copy(&old, &context); + md5_ctx = fr_md5_ctx_alloc(false); + md5_ctx_old = fr_md5_ctx_alloc(true); + + fr_md5_update(md5_ctx, (uint8_t const *) secret, talloc_array_length(secret) - 1); + fr_md5_ctx_copy(md5_ctx_old, md5_ctx); - fr_md5_update(&context, vector, AUTH_VECTOR_LEN); - fr_md5_update(&context, &out[0], 2); + fr_md5_update(md5_ctx, vector, AUTH_VECTOR_LEN); + fr_md5_update(md5_ctx, &out[0], 2); for (n = 0; n < encrypted_len; n += AUTH_PASS_LEN) { size_t block_len; if (n > 0) { - fr_md5_copy(&context, &old); - fr_md5_update(&context, out + 2 + n - AUTH_PASS_LEN, AUTH_PASS_LEN); + fr_md5_ctx_copy(md5_ctx, md5_ctx_old); + fr_md5_update(md5_ctx, out + 2 + n - AUTH_PASS_LEN, AUTH_PASS_LEN); } - fr_md5_final(digest, &context); + fr_md5_final(digest, md5_ctx); if ((2 + n + AUTH_PASS_LEN) < freespace) { block_len = AUTH_PASS_LEN; @@ -410,6 +422,9 @@ static void encode_tunnel_password(uint8_t *out, ssize_t *outlen, for (i = 0; i < block_len; i++) out[i + 2 + n] ^= digest[i]; } + + fr_md5_ctx_free(&md5_ctx); + fr_md5_ctx_free(&md5_ctx_old); } static ssize_t encode_tlv_hdr_internal(uint8_t *out, size_t outlen, diff --git a/src/tests/util/radius1_test.c b/src/tests/util/radius1_test.c index 0f958590ddb..c4a2dd670ca 100644 --- a/src/tests/util/radius1_test.c +++ b/src/tests/util/radius1_test.c @@ -114,7 +114,7 @@ static int test_decode(UNUSED void const *instance, REQUEST *request, uint8_t *c static ssize_t test_encode(UNUSED void const *instance, REQUEST *request, uint8_t *buffer, size_t buffer_len) { - FR_MD5_CTX context; + fr_md5_ctx_t *md5_ctx; fr_radius_packet_ctx_t const *pc = talloc_get_type_abort_const(request->async->listen->app_instance, fr_radius_packet_ctx_t); @@ -128,10 +128,11 @@ static ssize_t test_encode(UNUSED void const *instance, REQUEST *request, uint8_ memcpy(buffer + 4, pc->vector, 16); - fr_md5_init(&context); - fr_md5_update(&context, buffer, 20); - fr_md5_update(&context, (uint8_t const *) secret, strlen(secret)); - fr_md5_final(buffer + 4, &context); + md5_ctx = fr_md5_ctx_alloc(true); + fr_md5_update(md5_ctx, buffer, 20); + fr_md5_update(md5_ctx, (uint8_t const *) secret, strlen(secret)); + fr_md5_final(buffer + 4, md5_ctx); + fr_md5_ctx_free(&md5_ctx); return 20; } diff --git a/src/tests/util/radius_schedule_test.c b/src/tests/util/radius_schedule_test.c index 487ded66143..aeae6c9a26a 100644 --- a/src/tests/util/radius_schedule_test.c +++ b/src/tests/util/radius_schedule_test.c @@ -83,7 +83,7 @@ static int test_decode(void const *instance, REQUEST *request, uint8_t *const da static ssize_t test_encode(void const *instance, REQUEST *request, uint8_t *buffer, size_t buffer_len) { - FR_MD5_CTX context; + fr_md5_ctx_t *md5_ctx; fr_listen_test_t const *pc = instance; MPRINT1("\t\tENCODE >>> request %"PRIu64"- data %p %p room %zd\n", request->number, pc, buffer, buffer_len); @@ -95,10 +95,11 @@ static ssize_t test_encode(void const *instance, REQUEST *request, uint8_t *buff memcpy(buffer + 4, tpc.vector, 16); - fr_md5_init(&context); - fr_md5_update(&context, buffer, 20); - fr_md5_update(&context, (uint8_t const *) secret, strlen(secret)); - fr_md5_final(buffer + 4, &context); + md5_ctx = fr_md5_ctx_alloc(true); + fr_md5_update(md5_ctx, buffer, 20); + fr_md5_update(md5_ctx, (uint8_t const *) secret, strlen(secret)); + fr_md5_final(buffer + 4, md5_ctx); + fr_md5_ctx_free(&md5_ctx); return 20; }