Skip to content

Commit

Permalink
Use OpenSSL 3.0 library context for MD4/MD5 when in FIPS mode
Browse files Browse the repository at this point in the history
When the system is in FIPS mode, certain algorithms are removed from the
system default provider, including MD5 and MD4. These won't be available
even if the default and legacy providers are enabled on the default
context.
To bypass this limitation, we need to create a lib context that will hold both default
and legacy providers. Since the OSSL_LIB_CTX from OpenSSL 3.0 is not
thread-safe, we define it as thread-local.

While this was previously solved by just resorting in internal
implementations, using the libctx instead brings the benefit of
FreeRADIUS being able to work on FIPS systems without the need of
recompiling the package, as well as performance improvements from the
OpenSSL implementations.

Signed-off-by: Antonio Torres <[email protected]>
  • Loading branch information
antoniotorresm committed Jul 18, 2022
1 parent e6d3bfc commit cd1ff66
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 41 deletions.
24 changes: 16 additions & 8 deletions src/include/md4.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ RCSIDH(md4_h, "$Id$")

#include <string.h>

#ifdef WITH_FIPS
#undef HAVE_OPENSSL_MD4_H
#endif

#ifdef HAVE_OPENSSL_MD4_H
# include <openssl/md4.h>
#endif
Expand Down Expand Up @@ -87,6 +83,10 @@ USES_APPLE_DEPRECATED_API
# define fr_md4_destroy(_x)
#else
#include <openssl/evp.h>
#include <openssl/provider.h>
#include <freeradius-devel/threads.h>

#include <freeradius-devel/openssl3.h>

/*
* Wrappers for OpenSSL3, so we don't have to butcher the rest of
Expand All @@ -101,11 +101,20 @@ typedef struct FR_MD4_CTX {
static inline void fr_md4_init(FR_MD4_CTX *ctx)
{
ctx->ctx = EVP_MD_CTX_new();
// ctx->md = EVP_MD_fetch(NULL, "MD4", "provider=legacy");
ctx->md = EVP_md4();
if (EVP_default_properties_is_fips_enabled(NULL)) {
OSSL_FIPS_LIBCTX *fips_libctx = fr_thread_local_init(fips_ossl_libctx, _fips_ossl_libctx_free);
if (!fips_libctx) {
fips_libctx = _fips_ossl_libctx_create();
fr_thread_local_set(fips_ossl_libctx, fips_libctx);
}
if (!fips_libctx->md4)
fips_libctx->md4 = EVP_MD_fetch(fips_libctx->libctx, "MD4", NULL);
ctx->md = fips_libctx->md4;
} else {
ctx->md = EVP_md4();
}
ctx->len = MD4_DIGEST_LENGTH;

EVP_MD_CTX_set_flags(ctx->ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
EVP_DigestInit_ex(ctx->ctx, ctx->md, NULL);
}

Expand All @@ -122,7 +131,6 @@ static inline void fr_md4_final(uint8_t out[MD4_DIGEST_LENGTH], FR_MD4_CTX *ctx)
static inline void fr_md4_destroy(FR_MD4_CTX *ctx)
{
EVP_MD_CTX_destroy(ctx->ctx);
// EVP_MD_free(ctx->md);
}

#endif /* OPENSSL3 */
Expand Down
67 changes: 49 additions & 18 deletions src/include/md5.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ RCSIDH(md5_h, "$Id$")

# include <string.h>

#ifdef WITH_FIPS
#undef HAVE_OPENSSL_MD5_H
#endif

#ifdef HAVE_OPENSSL_MD5_H
# include <openssl/md5.h>
#endif
Expand Down Expand Up @@ -86,25 +82,60 @@ USES_APPLE_DEPRECATED_API
# define fr_md5_destroy(_x)
#else
#include <openssl/evp.h>
#include <openssl/provider.h>

#include <freeradius-devel/openssl3.h>

/*
* Wrappers for OpenSSL3, so we don't have to butcher the rest of
* the code too much.
*/
typedef EVP_MD_CTX* FR_MD5_CTX;

# define fr_md5_init(_ctx) \
do { \
*_ctx = EVP_MD_CTX_new(); \
EVP_MD_CTX_set_flags(*_ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); \
EVP_DigestInit_ex(*_ctx, EVP_md5(), NULL); \
} while (0)
# define fr_md5_update(_ctx, _str, _len) \
EVP_DigestUpdate(*_ctx, _str, _len)
# define fr_md5_final(_out, _ctx) \
EVP_DigestFinal_ex(*_ctx, _out, NULL)
# define fr_md5_destroy(_ctx) EVP_MD_CTX_destroy(*_ctx)
# define fr_md5_copy(_dst, _src) EVP_MD_CTX_copy_ex(_dst, _src)
typedef struct FR_MD5_CTX {
EVP_MD_CTX *ctx;
EVP_MD const *md;
unsigned int len;
} FR_MD5_CTX;

static inline void fr_md5_init(FR_MD5_CTX *ctx)
{
ctx->ctx = EVP_MD_CTX_new();
if (EVP_default_properties_is_fips_enabled(NULL)) {
OSSL_FIPS_LIBCTX *fips_libctx = fr_thread_local_init(fips_ossl_libctx, _fips_ossl_libctx_free);
if (!fips_libctx) {
fips_libctx = _fips_ossl_libctx_create();
fr_thread_local_set(fips_ossl_libctx, fips_libctx);
}
if (!fips_libctx->md5)
fips_libctx->md5 = EVP_MD_fetch(fips_libctx->libctx, "MD5", NULL);
ctx->md = fips_libctx->md5;
} else {
ctx->md = EVP_md5();
}
ctx->len = MD5_DIGEST_LENGTH;

EVP_DigestInit_ex(ctx->ctx, ctx->md, NULL);
}

static inline void fr_md5_update(FR_MD5_CTX *ctx, uint8_t const *in, size_t inlen)
{
EVP_DigestUpdate(ctx->ctx, in, inlen);
}

static inline void fr_md5_final(uint8_t out[MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx)
{
EVP_DigestFinal_ex(ctx->ctx, out, &(ctx->len));
}

static inline void fr_md5_destroy(FR_MD5_CTX *ctx)
{
EVP_MD_CTX_destroy(ctx->ctx);
}

static inline void fr_md5_copy(FR_MD5_CTX *dst, FR_MD5_CTX *src)
{
// other fields, too
EVP_MD_CTX_copy_ex(dst->ctx, src->ctx);
}
#endif /* OPENSSL3 */
#endif /* HAVE_OPENSSL_MD5_H */

Expand Down
79 changes: 78 additions & 1 deletion src/include/openssl3.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,29 @@ RCSIDH(openssl3_h, "$Id$")
* fill the code with ifdef's, so we define some horrific
* wrappers here.
*
* For FIPS environments, we need to define an OSSL_LIB_CTX that will
* hold both default and legacy providers, which will allow us to access MD5
* and MD4 under these systems.
*
* This file should be included AFTER all OpenSSL header files.
*/
#ifdef HAVE_OPENSSL_SSL_H
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <string.h>
#include <openssl/evp.h>
#include <openssl/core_names.h>
#include <openssl/provider.h>
#include <freeradius-devel/threads.h>

typedef struct {
OSSL_LIB_CTX *libctx;
OSSL_PROVIDER *default_provider;
OSSL_PROVIDER *legacy_provider;
EVP_MD *md5;
EVP_MD *md4;
} OSSL_FIPS_LIBCTX;

fr_thread_local_setup(OSSL_FIPS_LIBCTX *, fips_ossl_libctx) /* macro */

typedef struct {
EVP_MAC *mac;
Expand All @@ -53,14 +69,75 @@ static inline HMAC3_CTX *HMAC3_CTX_new(void)
return h;
}

static inline void _fips_ossl_libctx_free(void *arg) {
OSSL_FIPS_LIBCTX *ctx = arg;
if (ctx->legacy_provider) {
OSSL_PROVIDER_unload(ctx->legacy_provider);
ctx->legacy_provider = NULL;
}
if (ctx->default_provider) {
OSSL_PROVIDER_unload(ctx->default_provider);
ctx->default_provider = NULL;
}
if (ctx->libctx) {
OSSL_LIB_CTX_free(ctx->libctx);
ctx->libctx = NULL;
}
if (ctx->md5) {
EVP_MD_free(ctx->md5);
ctx->md5 = NULL;
}
if (ctx->md4) {
EVP_MD_free(ctx->md4);
ctx->md4 = NULL;
}
free(arg);
fips_ossl_libctx = NULL;
}

static inline OSSL_FIPS_LIBCTX *_fips_ossl_libctx_create() {
OSSL_FIPS_LIBCTX *ret = calloc(1, sizeof(*ret));
ret->libctx = OSSL_LIB_CTX_new();
ret->default_provider = OSSL_PROVIDER_load(ret->libctx, "default");
if (!ret->default_provider) {
fprintf(stderr, "Failed loading OpenSSL default provider.");
return NULL;
}
ret->legacy_provider = OSSL_PROVIDER_load(ret->libctx, "legacy");
if (!ret->legacy_provider) {
fprintf(stderr, "Failed loading OpenSSL legacy provider.");
return NULL;
}
ret->md5 = EVP_MD_fetch(ret->libctx, "MD5", NULL);
if (!ret->md5) {
fprintf(stderr, "Failed loading OpenSSL MD5 function.");
return NULL;
}
ret->md4 = EVP_MD_fetch(ret->libctx, "MD4", NULL);
if (!ret->md4) {
fprintf(stderr, "Failed loading OpenSSL MD4 function.");
return NULL;
}
return ret;
}

#define HMAC_Init_ex(_ctx, _key, _keylen, _md, _engine) HMAC3_Init_ex(_ctx, _key, _keylen, _md, _engine)
static inline int HMAC3_Init_ex(HMAC3_CTX *ctx, const unsigned char *key, unsigned int keylen, const EVP_MD *md, UNUSED void *engine)
{
OSSL_PARAM params[2], *p = params;
char const *name;
char *unconst;

ctx->mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
OSSL_LIB_CTX *libctx = NULL;
if (EVP_default_properties_is_fips_enabled(NULL)) {
OSSL_FIPS_LIBCTX *fips_libctx = fr_thread_local_init(fips_ossl_libctx, _fips_ossl_libctx_free);
if (!fips_libctx) {
fips_libctx = _fips_ossl_libctx_create();
fr_thread_local_set(fips_ossl_libctx, fips_libctx);
}
libctx = fips_libctx->libctx;
}
ctx->mac = EVP_MAC_fetch(libctx, "HMAC", NULL);
if (!ctx->mac) return 0;

ctx->ctx = EVP_MAC_CTX_new(ctx->mac);
Expand Down
32 changes: 30 additions & 2 deletions src/lib/hmacmd5.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@ RCSID("$Id$")

#include <freeradius-devel/libradius.h>
#include <freeradius-devel/md5.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/provider.h>
#endif
#include <freeradius-devel/openssl3.h>

#if defined(HAVE_OPENSSL_EVP_H) && !defined(WITH_FIPS)
#if defined(HAVE_OPENSSL_EVP_H)

/** Calculate HMAC using OpenSSL's MD5 implementation
*
* @param digest Caller digest to be filled in.
Expand All @@ -57,7 +61,31 @@ void fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *text, size_t
HMAC_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
#endif /* EVP_MD_CTX_FLAG_NON_FIPS_ALLOW */

HMAC_Init_ex(ctx, key, key_len, EVP_md5(), NULL);
EVP_MD const *md5 = NULL;

#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/*
* If we are using OpenSSL >= 3.0 and FIPS mode is
* enabled, we need to load the default provider in a
* standalone context in order to access MD5.
*/
if (EVP_default_properties_is_fips_enabled(NULL)) {
OSSL_FIPS_LIBCTX *fips_libctx = fr_thread_local_init(fips_ossl_libctx, _fips_ossl_libctx_free);
if (!fips_libctx) {
fips_libctx = _fips_ossl_libctx_create();
fr_thread_local_set(fips_ossl_libctx, fips_libctx);
}
if (!fips_libctx->md5)
fips_libctx->md5 = EVP_MD_fetch(fips_libctx->libctx, "MD5", NULL);
md5 = fips_libctx->md5;
} else {
md5 = EVP_md5();
}
#else
md5 = EVP_md5();
#endif

HMAC_Init_ex(ctx, key, key_len, md5, NULL);
HMAC_Update(ctx, text, text_len);
HMAC_Final(ctx, digest, &len);
HMAC_CTX_free(ctx);
Expand Down
Loading

0 comments on commit cd1ff66

Please sign in to comment.