Blame SOURCES/downstream-FIPS-with-PRNG-and-RADIUS-and-MD4.patch

a53771
From a7318c3cd6e1f58adb80493c05b59e6c180cd584 Mon Sep 17 00:00:00 2001
a53771
From: Julien Rische <jrische@redhat.com>
a53771
Date: Wed, 23 Feb 2022 17:34:33 +0100
a53771
Subject: [PATCH] [downstream] FIPS with PRNG and RADIUS and MD4
a53771
a53771
NB: Use openssl's PRNG in FIPS mode and taint within krad.
a53771
a53771
A lot of the FIPS error conditions from OpenSSL are incredibly
a53771
mysterious (at best, things return NULL unexpectedly; at worst,
a53771
internal assertions are tripped; most of the time, you just get
a53771
ENOMEM).  In order to cope with this, we need to have some level of
a53771
awareness of what we can and can't safely call.
a53771
a53771
This will slow down some calls slightly (FIPS_mode() takes multiple
a53771
locks), but not for any ciphers we care about - which is to say that
a53771
AES is fine.  Shame about SPAKE though.
a53771
a53771
post6 restores MD4 (and therefore keygen-only RC4).
a53771
a53771
post7 restores MD5 and adds radius_md5_fips_override.
a53771
a53771
post8 restores MD4/MD5 for OpenSSL 3.0
a53771
a53771
Use OpenSSL 3.0 library context to access MD4 and MD5 lazily from
a53771
legacy provider if RC4 encryption type is enabled, without affecting
a53771
global context.
a53771
a53771
Remove EVP_MD_CTX_FLAG_NON_FIPS_ALLOW flag since does not have any
a53771
effect anymore.
a53771
a53771
Last-updated: krb5-1.19
a53771
---
a53771
 doc/admin/conf_files/krb5_conf.rst            |  6 ++
a53771
 src/lib/crypto/krb/prng.c                     | 11 ++-
a53771
 .../crypto/openssl/enc_provider/camellia.c    |  6 ++
a53771
 src/lib/crypto/openssl/enc_provider/rc4.c     | 13 +++-
a53771
 .../crypto/openssl/hash_provider/hash_evp.c   | 85 ++++++++++++++++++-
a53771
 src/lib/crypto/openssl/hmac.c                 |  6 +-
a53771
 src/lib/krad/attr.c                           | 46 ++++++++---
a53771
 src/lib/krad/attrset.c                        |  5 +-
a53771
 src/lib/krad/internal.h                       | 28 ++++++-
a53771
 src/lib/krad/packet.c                         | 22 +++---
a53771
 src/lib/krad/remote.c                         | 10 ++-
a53771
 src/lib/krad/t_attr.c                         |  3 +-
a53771
 src/lib/krad/t_attrset.c                      |  4 +-
a53771
 src/plugins/preauth/spake/spake_client.c      |  6 ++
a53771
 src/plugins/preauth/spake/spake_kdc.c         |  6 ++
a53771
 15 files changed, 218 insertions(+), 35 deletions(-)
a53771
a53771
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
a53771
index 675175955..adba8238d 100644
a53771
--- a/doc/admin/conf_files/krb5_conf.rst
a53771
+++ b/doc/admin/conf_files/krb5_conf.rst
a53771
@@ -330,6 +330,12 @@ The libdefaults section may contain any of the following relations:
a53771
     qualification of shortnames, set this relation to the empty string
a53771
     with ``qualify_shortname = ""``.  (New in release 1.18.)
a53771
 
a53771
+**radius_md5_fips_override**
a53771
+    Downstream-only option to enable use of MD5 in RADIUS
a53771
+    communication (libkrad).  This allows for local (or protected
a53771
+    tunnel) communication with a RADIUS server that doesn't use krad
a53771
+    (e.g., freeradius) while in FIPS mode.
a53771
+
a53771
 **rdns**
a53771
     If this flag is true, reverse name lookup will be used in addition
a53771
     to forward name lookup to canonicalizing hostnames for use in
a53771
diff --git a/src/lib/crypto/krb/prng.c b/src/lib/crypto/krb/prng.c
a53771
index cb9ca9b98..f0e9984ca 100644
a53771
--- a/src/lib/crypto/krb/prng.c
a53771
+++ b/src/lib/crypto/krb/prng.c
a53771
@@ -26,6 +26,8 @@
a53771
 
a53771
 #include "crypto_int.h"
a53771
 
a53771
+#include <openssl/rand.h>
a53771
+
a53771
 krb5_error_code KRB5_CALLCONV
a53771
 krb5_c_random_seed(krb5_context context, krb5_data *data)
a53771
 {
a53771
@@ -99,9 +101,16 @@ krb5_boolean
a53771
 k5_get_os_entropy(unsigned char *buf, size_t len, int strong)
a53771
 {
a53771
     const char *device;
a53771
-#if defined(__linux__) && defined(SYS_getrandom)
a53771
     int r;
a53771
 
a53771
+    /* A wild FIPS mode appeared! */
a53771
+    if (FIPS_mode()) {
a53771
+        /* The return codes on this API are not good */
a53771
+        r = RAND_bytes(buf, len);
a53771
+        return r == 1;
a53771
+    }
a53771
+
a53771
+#if defined(__linux__) && defined(SYS_getrandom)
a53771
     while (len > 0) {
a53771
         /*
a53771
          * Pull from the /dev/urandom pool, but require it to have been seeded.
a53771
diff --git a/src/lib/crypto/openssl/enc_provider/camellia.c b/src/lib/crypto/openssl/enc_provider/camellia.c
a53771
index 2da691329..f79679a0b 100644
a53771
--- a/src/lib/crypto/openssl/enc_provider/camellia.c
a53771
+++ b/src/lib/crypto/openssl/enc_provider/camellia.c
a53771
@@ -304,6 +304,9 @@ krb5int_camellia_cbc_mac(krb5_key key, const krb5_crypto_iov *data,
a53771
     unsigned char blockY[CAMELLIA_BLOCK_SIZE], blockB[CAMELLIA_BLOCK_SIZE];
a53771
     struct iov_cursor cursor;
a53771
 
a53771
+    if (FIPS_mode())
a53771
+        return KRB5_CRYPTO_INTERNAL;
a53771
+
a53771
     if (output->length < CAMELLIA_BLOCK_SIZE)
a53771
         return KRB5_BAD_MSIZE;
a53771
 
a53771
@@ -331,6 +334,9 @@ static krb5_error_code
a53771
 krb5int_camellia_init_state (const krb5_keyblock *key, krb5_keyusage usage,
a53771
                              krb5_data *state)
a53771
 {
a53771
+    if (FIPS_mode())
a53771
+        return KRB5_CRYPTO_INTERNAL;
a53771
+
a53771
     state->length = 16;
a53771
     state->data = (void *) malloc(16);
a53771
     if (state->data == NULL)
a53771
diff --git a/src/lib/crypto/openssl/enc_provider/rc4.c b/src/lib/crypto/openssl/enc_provider/rc4.c
a53771
index bc87c6f42..9bf407899 100644
a53771
--- a/src/lib/crypto/openssl/enc_provider/rc4.c
a53771
+++ b/src/lib/crypto/openssl/enc_provider/rc4.c
a53771
@@ -66,6 +66,9 @@ k5_arcfour_docrypt(krb5_key key, const krb5_data *state, krb5_crypto_iov *data,
a53771
     EVP_CIPHER_CTX *ctx = NULL;
a53771
     struct arcfour_state *arcstate;
a53771
 
a53771
+    if (FIPS_mode())
a53771
+        return KRB5_CRYPTO_INTERNAL;
a53771
+
a53771
     arcstate = (state != NULL) ? (void *)state->data : NULL;
a53771
     if (arcstate != NULL) {
a53771
         ctx = arcstate->ctx;
a53771
@@ -113,7 +116,12 @@ k5_arcfour_docrypt(krb5_key key, const krb5_data *state, krb5_crypto_iov *data,
a53771
 static void
a53771
 k5_arcfour_free_state(krb5_data *state)
a53771
 {
a53771
-    struct arcfour_state *arcstate = (void *)state->data;
a53771
+    struct arcfour_state *arcstate;
a53771
+
a53771
+    if (FIPS_mode())
a53771
+        return;
a53771
+
a53771
+    arcstate = (void *) state->data;
a53771
 
a53771
     EVP_CIPHER_CTX_free(arcstate->ctx);
a53771
     free(arcstate);
a53771
@@ -125,6 +133,9 @@ k5_arcfour_init_state(const krb5_keyblock *key,
a53771
 {
a53771
     struct arcfour_state *arcstate;
a53771
 
a53771
+    if (FIPS_mode())
a53771
+        return KRB5_CRYPTO_INTERNAL;
a53771
+
a53771
     /*
a53771
      * The cipher state here is a saved pointer to a struct arcfour_state
a53771
      * object, rather than a flat byte array as in most enc providers.  The
a53771
diff --git a/src/lib/crypto/openssl/hash_provider/hash_evp.c b/src/lib/crypto/openssl/hash_provider/hash_evp.c
a53771
index 1e0fb8fc3..4b8e1a6b2 100644
a53771
--- a/src/lib/crypto/openssl/hash_provider/hash_evp.c
a53771
+++ b/src/lib/crypto/openssl/hash_provider/hash_evp.c
a53771
@@ -32,6 +32,50 @@
a53771
 
a53771
 #include "crypto_int.h"
a53771
 #include <openssl/evp.h>
a53771
+#include <openssl/provider.h>
a53771
+#include <threads.h>
a53771
+
a53771
+typedef struct ossl_lib_md_context {
a53771
+    OSSL_LIB_CTX *libctx;
a53771
+    OSSL_PROVIDER *legacy_provider;
a53771
+    EVP_MD *md;
a53771
+} ossl_md_context_t;
a53771
+
a53771
+static thread_local ossl_md_context_t *ossl_md_ctx = NULL;
a53771
+
a53771
+static krb5_error_code
a53771
+init_ossl_md_ctx(ossl_md_context_t *ctx, const char *algo)
a53771
+{
a53771
+    ctx->libctx = OSSL_LIB_CTX_new();
a53771
+    if (!ctx->libctx)
a53771
+        return KRB5_CRYPTO_INTERNAL;
a53771
+
a53771
+    /*
a53771
+     * Load both legacy and default provider as both may be needed.
a53771
+     * If they fail keep going and an error will be raised when we try to
a53771
+     * fetch the cipher later.
a53771
+     */
a53771
+    ctx->legacy_provider = OSSL_PROVIDER_load(ctx->libctx, "legacy");
a53771
+
a53771
+    ctx->md = EVP_MD_fetch(ctx->libctx, algo, NULL);
a53771
+    if (!ctx->md)
a53771
+        return KRB5_CRYPTO_INTERNAL;
a53771
+
a53771
+    return 0;
a53771
+}
a53771
+
a53771
+static void
a53771
+deinit_ossl_ctx(ossl_md_context_t *ctx)
a53771
+{
a53771
+    if (ctx->md)
a53771
+        EVP_MD_free(ctx->md);
a53771
+
a53771
+    if (ctx->legacy_provider)
a53771
+        OSSL_PROVIDER_unload(ctx->legacy_provider);
a53771
+
a53771
+    if (ctx->libctx)
a53771
+        OSSL_LIB_CTX_free(ctx->libctx);
a53771
+}
a53771
 
a53771
 static krb5_error_code
a53771
 hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
a53771
@@ -61,16 +104,53 @@ hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
a53771
     return ok ? 0 : KRB5_CRYPTO_INTERNAL;
a53771
 }
a53771
 
a53771
+static krb5_error_code
a53771
+hash_legacy_evp(const char *algo, const krb5_crypto_iov *data, size_t num_data,
a53771
+                krb5_data *output)
a53771
+{
a53771
+    krb5_error_code err;
a53771
+
a53771
+    if (!ossl_md_ctx) {
a53771
+        ossl_md_ctx = malloc(sizeof(ossl_md_context_t));
a53771
+        if (!ossl_md_ctx)
a53771
+            return ENOMEM;
a53771
+
a53771
+        err = init_ossl_md_ctx(ossl_md_ctx, algo);
a53771
+        if (err) {
a53771
+            deinit_ossl_ctx(ossl_md_ctx);
a53771
+            free(ossl_md_ctx);
a53771
+            ossl_md_ctx = NULL;
a53771
+            goto end;
a53771
+        }
a53771
+    }
a53771
+
a53771
+    err = hash_evp(ossl_md_ctx->md, data, num_data, output);
a53771
+
a53771
+end:
a53771
+    return err;
a53771
+}
a53771
+
a53771
 static krb5_error_code
a53771
 hash_md4(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
a53771
 {
a53771
-    return hash_evp(EVP_md4(), data, num_data, output);
a53771
+    /*
a53771
+     * MD4 is needed in FIPS mode to perform key generation for RC4 keys used
a53771
+     * by IPA.  These keys are only used along a (separately) secured channel
a53771
+     * for legacy reasons when performing trusts to Active Directory.
a53771
+     */
a53771
+    return FIPS_mode() ? hash_legacy_evp("MD4", data, num_data, output)
a53771
+                       : hash_evp(EVP_md4(), data, num_data, output);
a53771
 }
a53771
 
a53771
 static krb5_error_code
a53771
 hash_md5(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
a53771
 {
a53771
-    return hash_evp(EVP_md5(), data, num_data, output);
a53771
+    /*
a53771
+     * MD5 is needed in FIPS mode for communication with RADIUS servers.  This
a53771
+     * is gated in libkrad by libdefaults->radius_md5_fips_override.
a53771
+     */
a53771
+    return FIPS_mode() ? hash_legacy_evp("MD5", data, num_data, output)
a53771
+                       : hash_evp(EVP_md5(), data, num_data, output);
a53771
 }
a53771
 
a53771
 static krb5_error_code
a53771
diff --git a/src/lib/crypto/openssl/hmac.c b/src/lib/crypto/openssl/hmac.c
a53771
index 7dc59dcc0..769a50c00 100644
a53771
--- a/src/lib/crypto/openssl/hmac.c
a53771
+++ b/src/lib/crypto/openssl/hmac.c
a53771
@@ -103,7 +103,11 @@ map_digest(const struct krb5_hash_provider *hash)
a53771
         return EVP_sha256();
a53771
     else if (!strncmp(hash->hash_name, "SHA-384",7))
a53771
         return EVP_sha384();
a53771
-    else if (!strncmp(hash->hash_name, "MD5", 3))
a53771
+
a53771
+    if (FIPS_mode())
a53771
+        return NULL;
a53771
+
a53771
+    if (!strncmp(hash->hash_name, "MD5", 3))
a53771
         return EVP_md5();
a53771
     else if (!strncmp(hash->hash_name, "MD4", 3))
a53771
         return EVP_md4();
a53771
diff --git a/src/lib/krad/attr.c b/src/lib/krad/attr.c
a53771
index 9c13d9d75..42d354a3b 100644
a53771
--- a/src/lib/krad/attr.c
a53771
+++ b/src/lib/krad/attr.c
a53771
@@ -38,7 +38,8 @@
a53771
 typedef krb5_error_code
a53771
 (*attribute_transform_fn)(krb5_context ctx, const char *secret,
a53771
                           const unsigned char *auth, const krb5_data *in,
a53771
-                          unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
a53771
+                          unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
a53771
+                          krb5_boolean *is_fips);
a53771
 
a53771
 typedef struct {
a53771
     const char *name;
a53771
@@ -51,12 +52,14 @@ typedef struct {
a53771
 static krb5_error_code
a53771
 user_password_encode(krb5_context ctx, const char *secret,
a53771
                      const unsigned char *auth, const krb5_data *in,
a53771
-                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
a53771
+                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
a53771
+                     krb5_boolean *is_fips);
a53771
 
a53771
 static krb5_error_code
a53771
 user_password_decode(krb5_context ctx, const char *secret,
a53771
                      const unsigned char *auth, const krb5_data *in,
a53771
-                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
a53771
+                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
a53771
+                     krb5_boolean *ignored);
a53771
 
a53771
 static const attribute_record attributes[UCHAR_MAX] = {
a53771
     {"User-Name", 1, MAX_ATTRSIZE, NULL, NULL},
a53771
@@ -128,7 +131,8 @@ static const attribute_record attributes[UCHAR_MAX] = {
a53771
 static krb5_error_code
a53771
 user_password_encode(krb5_context ctx, const char *secret,
a53771
                      const unsigned char *auth, const krb5_data *in,
a53771
-                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
a53771
+                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
a53771
+                     krb5_boolean *is_fips)
a53771
 {
a53771
     const unsigned char *indx;
a53771
     krb5_error_code retval;
a53771
@@ -154,8 +158,15 @@ user_password_encode(krb5_context ctx, const char *secret,
a53771
     for (blck = 0, indx = auth; blck * BLOCKSIZE < len; blck++) {
a53771
         memcpy(tmp.data + seclen, indx, BLOCKSIZE);
a53771
 
a53771
-        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
a53771
-                                      &sum);
a53771
+        if (kr_use_fips(ctx)) {
a53771
+            /* Skip encryption here.  Taint so that we won't pass it out of
a53771
+             * the machine by accident. */
a53771
+            *is_fips = TRUE;
a53771
+            sum.contents = calloc(1, BLOCKSIZE);
a53771
+        } else {
a53771
+            retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
a53771
+                                          &sum);
a53771
+        }
a53771
         if (retval != 0) {
a53771
             zap(tmp.data, tmp.length);
a53771
             zap(outbuf, len);
a53771
@@ -180,7 +191,8 @@ user_password_encode(krb5_context ctx, const char *secret,
a53771
 static krb5_error_code
a53771
 user_password_decode(krb5_context ctx, const char *secret,
a53771
                      const unsigned char *auth, const krb5_data *in,
a53771
-                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
a53771
+                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
a53771
+                     krb5_boolean *is_fips)
a53771
 {
a53771
     const unsigned char *indx;
a53771
     krb5_error_code retval;
a53771
@@ -204,8 +216,15 @@ user_password_decode(krb5_context ctx, const char *secret,
a53771
     for (blck = 0, indx = auth; blck * BLOCKSIZE < in->length; blck++) {
a53771
         memcpy(tmp.data + seclen, indx, BLOCKSIZE);
a53771
 
a53771
-        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
a53771
-                                      &tmp, &sum);
a53771
+        if (kr_use_fips(ctx)) {
a53771
+            /* Skip encryption here.  Taint so that we won't pass it out of
a53771
+             * the machine by accident. */
a53771
+            *is_fips = TRUE;
a53771
+            sum.contents = calloc(1, BLOCKSIZE);
a53771
+        } else {
a53771
+            retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
a53771
+                                          &tmp, &sum);
a53771
+        }
a53771
         if (retval != 0) {
a53771
             zap(tmp.data, tmp.length);
a53771
             zap(outbuf, in->length);
a53771
@@ -248,7 +267,7 @@ krb5_error_code
a53771
 kr_attr_encode(krb5_context ctx, const char *secret,
a53771
                const unsigned char *auth, krad_attr type,
a53771
                const krb5_data *in, unsigned char outbuf[MAX_ATTRSIZE],
a53771
-               size_t *outlen)
a53771
+               size_t *outlen, krb5_boolean *is_fips)
a53771
 {
a53771
     krb5_error_code retval;
a53771
 
a53771
@@ -265,7 +284,8 @@ kr_attr_encode(krb5_context ctx, const char *secret,
a53771
         return 0;
a53771
     }
a53771
 
a53771
-    return attributes[type - 1].encode(ctx, secret, auth, in, outbuf, outlen);
a53771
+    return attributes[type - 1].encode(ctx, secret, auth, in, outbuf, outlen,
a53771
+                                       is_fips);
a53771
 }
a53771
 
a53771
 krb5_error_code
a53771
@@ -274,6 +294,7 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
a53771
                unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
a53771
 {
a53771
     krb5_error_code retval;
a53771
+    krb5_boolean ignored;
a53771
 
a53771
     retval = kr_attr_valid(type, in);
a53771
     if (retval != 0)
a53771
@@ -288,7 +309,8 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
a53771
         return 0;
a53771
     }
a53771
 
a53771
-    return attributes[type - 1].decode(ctx, secret, auth, in, outbuf, outlen);
a53771
+    return attributes[type - 1].decode(ctx, secret, auth, in, outbuf, outlen,
a53771
+                                       &ignored);
a53771
 }
a53771
 
a53771
 krad_attr
a53771
diff --git a/src/lib/krad/attrset.c b/src/lib/krad/attrset.c
a53771
index 03c613716..d89982a13 100644
a53771
--- a/src/lib/krad/attrset.c
a53771
+++ b/src/lib/krad/attrset.c
a53771
@@ -167,7 +167,8 @@ krad_attrset_copy(const krad_attrset *set, krad_attrset **copy)
a53771
 krb5_error_code
a53771
 kr_attrset_encode(const krad_attrset *set, const char *secret,
a53771
                   const unsigned char *auth,
a53771
-                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen)
a53771
+                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen,
a53771
+                  krb5_boolean *is_fips)
a53771
 {
a53771
     unsigned char buffer[MAX_ATTRSIZE];
a53771
     krb5_error_code retval;
a53771
@@ -181,7 +182,7 @@ kr_attrset_encode(const krad_attrset *set, const char *secret,
a53771
 
a53771
     K5_TAILQ_FOREACH(a, &set->list, list) {
a53771
         retval = kr_attr_encode(set->ctx, secret, auth, a->type, &a->attr,
a53771
-                                buffer, &attrlen);
a53771
+                                buffer, &attrlen, is_fips);
a53771
         if (retval != 0)
a53771
             return retval;
a53771
 
a53771
diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h
a53771
index 0143d155a..223ffd730 100644
a53771
--- a/src/lib/krad/internal.h
a53771
+++ b/src/lib/krad/internal.h
a53771
@@ -39,6 +39,8 @@
a53771
 #include <sys/socket.h>
a53771
 #include <netdb.h>
a53771
 
a53771
+#include <openssl/crypto.h>
a53771
+
a53771
 #ifndef UCHAR_MAX
a53771
 #define UCHAR_MAX 255
a53771
 #endif
a53771
@@ -49,6 +51,13 @@
a53771
 
a53771
 typedef struct krad_remote_st krad_remote;
a53771
 
a53771
+struct krad_packet_st {
a53771
+    char buffer[KRAD_PACKET_SIZE_MAX];
a53771
+    krad_attrset *attrset;
a53771
+    krb5_data pkt;
a53771
+    krb5_boolean is_fips;
a53771
+};
a53771
+
a53771
 /* Validate constraints of an attribute. */
a53771
 krb5_error_code
a53771
 kr_attr_valid(krad_attr type, const krb5_data *data);
a53771
@@ -57,7 +66,8 @@ kr_attr_valid(krad_attr type, const krb5_data *data);
a53771
 krb5_error_code
a53771
 kr_attr_encode(krb5_context ctx, const char *secret, const unsigned char *auth,
a53771
                krad_attr type, const krb5_data *in,
a53771
-               unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
a53771
+               unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
a53771
+               krb5_boolean *is_fips);
a53771
 
a53771
 /* Decode an attribute. */
a53771
 krb5_error_code
a53771
@@ -69,7 +79,8 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
a53771
 krb5_error_code
a53771
 kr_attrset_encode(const krad_attrset *set, const char *secret,
a53771
                   const unsigned char *auth,
a53771
-                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen);
a53771
+                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen,
a53771
+                  krb5_boolean *is_fips);
a53771
 
a53771
 /* Decode attributes from a buffer. */
a53771
 krb5_error_code
a53771
@@ -152,4 +163,17 @@ gai_error_code(int err)
a53771
     }
a53771
 }
a53771
 
a53771
+static inline krb5_boolean
a53771
+kr_use_fips(krb5_context ctx)
a53771
+{
a53771
+    int val = 0;
a53771
+
a53771
+    if (!FIPS_mode())
a53771
+        return 0;
a53771
+
a53771
+    profile_get_boolean(ctx->profile, "libdefaults",
a53771
+                        "radius_md5_fips_override", NULL, 0, &val;;
a53771
+    return !val;
a53771
+}
a53771
+
a53771
 #endif /* INTERNAL_H_ */
a53771
diff --git a/src/lib/krad/packet.c b/src/lib/krad/packet.c
a53771
index c597174b6..fc2d24800 100644
a53771
--- a/src/lib/krad/packet.c
a53771
+++ b/src/lib/krad/packet.c
a53771
@@ -53,12 +53,6 @@ typedef unsigned char uchar;
a53771
 #define pkt_auth(p) ((uchar *)offset(&(p)->pkt, OFFSET_AUTH))
a53771
 #define pkt_attr(p) ((unsigned char *)offset(&(p)->pkt, OFFSET_ATTR))
a53771
 
a53771
-struct krad_packet_st {
a53771
-    char buffer[KRAD_PACKET_SIZE_MAX];
a53771
-    krad_attrset *attrset;
a53771
-    krb5_data pkt;
a53771
-};
a53771
-
a53771
 typedef struct {
a53771
     uchar x[(UCHAR_MAX + 1) / 8];
a53771
 } idmap;
a53771
@@ -187,8 +181,14 @@ auth_generate_response(krb5_context ctx, const char *secret,
a53771
     memcpy(data.data + response->pkt.length, secret, strlen(secret));
a53771
 
a53771
     /* Hash it. */
a53771
-    retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
a53771
-                                  &hash);
a53771
+    if (kr_use_fips(ctx)) {
a53771
+        /* This checksum does very little security-wise anyway, so don't
a53771
+         * taint. */
a53771
+        hash.contents = calloc(1, AUTH_FIELD_SIZE);
a53771
+    } else {
a53771
+        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
a53771
+                                      &hash);
a53771
+    }
a53771
     free(data.data);
a53771
     if (retval != 0)
a53771
         return retval;
a53771
@@ -276,7 +276,7 @@ krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
a53771
 
a53771
     /* Encode the attributes. */
a53771
     retval = kr_attrset_encode(set, secret, pkt_auth(pkt), pkt_attr(pkt),
a53771
-                               &attrset_len);
a53771
+                               &attrset_len, &pkt->is_fips);
a53771
     if (retval != 0)
a53771
         goto error;
a53771
 
a53771
@@ -314,7 +314,7 @@ krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
a53771
 
a53771
     /* Encode the attributes. */
a53771
     retval = kr_attrset_encode(set, secret, pkt_auth(request), pkt_attr(pkt),
a53771
-                               &attrset_len);
a53771
+                               &attrset_len, &pkt->is_fips);
a53771
     if (retval != 0)
a53771
         goto error;
a53771
 
a53771
@@ -451,6 +451,8 @@ krad_packet_decode_response(krb5_context ctx, const char *secret,
a53771
 const krb5_data *
a53771
 krad_packet_encode(const krad_packet *pkt)
a53771
 {
a53771
+    if (pkt->is_fips)
a53771
+        return NULL;
a53771
     return &pkt->pkt;
a53771
 }
a53771
 
a53771
diff --git a/src/lib/krad/remote.c b/src/lib/krad/remote.c
a53771
index c96a9b4ee..eca432424 100644
a53771
--- a/src/lib/krad/remote.c
a53771
+++ b/src/lib/krad/remote.c
a53771
@@ -263,7 +263,7 @@ on_io_write(krad_remote *rr)
a53771
     request *r;
a53771
 
a53771
     K5_TAILQ_FOREACH(r, &rr->list, list) {
a53771
-        tmp = krad_packet_encode(r->request);
a53771
+        tmp = &r->request->pkt;
a53771
 
a53771
         /* If the packet has already been sent, do nothing. */
a53771
         if (r->sent == tmp->length)
a53771
@@ -359,7 +359,7 @@ on_io_read(krad_remote *rr)
a53771
     if (req != NULL) {
a53771
         K5_TAILQ_FOREACH(r, &rr->list, list) {
a53771
             if (r->request == req &&
a53771
-                r->sent == krad_packet_encode(req)->length) {
a53771
+                r->sent == req->pkt.length) {
a53771
                 request_finish(r, 0, rsp);
a53771
                 break;
a53771
             }
a53771
@@ -455,6 +455,12 @@ kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
a53771
                                      (krad_packet_iter_cb)iterator, &r, &tmp);
a53771
     if (retval != 0)
a53771
         goto error;
a53771
+    else if (tmp->is_fips && rr->info->ai_family != AF_LOCAL &&
a53771
+        rr->info->ai_family != AF_UNIX) {
a53771
+        /* This would expose cleartext passwords, so abort. */
a53771
+        retval = ESOCKTNOSUPPORT;
a53771
+        goto error;
a53771
+    }
a53771
 
a53771
     K5_TAILQ_FOREACH(r, &rr->list, list) {
a53771
         if (r->request == tmp) {
a53771
diff --git a/src/lib/krad/t_attr.c b/src/lib/krad/t_attr.c
a53771
index eb2a780c8..4d285ad9d 100644
a53771
--- a/src/lib/krad/t_attr.c
a53771
+++ b/src/lib/krad/t_attr.c
a53771
@@ -50,6 +50,7 @@ main()
a53771
     const char *tmp;
a53771
     krb5_data in;
a53771
     size_t len;
a53771
+    krb5_boolean is_fips = FALSE;
a53771
 
a53771
     noerror(krb5_init_context(&ctx));
a53771
 
a53771
@@ -73,7 +74,7 @@ main()
a53771
     in = string2data((char *)decoded);
a53771
     retval = kr_attr_encode(ctx, secret, auth,
a53771
                             krad_attr_name2num("User-Password"),
a53771
-                            &in, outbuf, &len;;
a53771
+                            &in, outbuf, &len, &is_fips);
a53771
     insist(retval == 0);
a53771
     insist(len == sizeof(encoded));
a53771
     insist(memcmp(outbuf, encoded, len) == 0);
a53771
diff --git a/src/lib/krad/t_attrset.c b/src/lib/krad/t_attrset.c
a53771
index 7928335ca..0f9576253 100644
a53771
--- a/src/lib/krad/t_attrset.c
a53771
+++ b/src/lib/krad/t_attrset.c
a53771
@@ -49,6 +49,7 @@ main()
a53771
     krb5_context ctx;
a53771
     size_t len = 0, encode_len;
a53771
     krb5_data tmp;
a53771
+    krb5_boolean is_fips = FALSE;
a53771
 
a53771
     noerror(krb5_init_context(&ctx));
a53771
     noerror(krad_attrset_new(ctx, &set);;
a53771
@@ -62,7 +63,8 @@ main()
a53771
     noerror(krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp));
a53771
 
a53771
     /* Encode attrset. */
a53771
-    noerror(kr_attrset_encode(set, "foo", auth, buffer, &encode_len));
a53771
+    noerror(kr_attrset_encode(set, "foo", auth, buffer, &encode_len,
a53771
+                              &is_fips));
a53771
     krad_attrset_free(set);
a53771
 
a53771
     /* Manually encode User-Name. */
a53771
diff --git a/src/plugins/preauth/spake/spake_client.c b/src/plugins/preauth/spake/spake_client.c
a53771
index 00734a13b..a3ce22b70 100644
a53771
--- a/src/plugins/preauth/spake/spake_client.c
a53771
+++ b/src/plugins/preauth/spake/spake_client.c
a53771
@@ -38,6 +38,8 @@
a53771
 #include "groups.h"
a53771
 #include <krb5/clpreauth_plugin.h>
a53771
 
a53771
+#include <openssl/crypto.h>
a53771
+
a53771
 typedef struct reqstate_st {
a53771
     krb5_pa_spake *msg;         /* set in prep_questions, used in process */
a53771
     krb5_keyblock *initial_key;
a53771
@@ -375,6 +377,10 @@ clpreauth_spake_initvt(krb5_context context, int maj_ver, int min_ver,
a53771
 
a53771
     if (maj_ver != 1)
a53771
         return KRB5_PLUGIN_VER_NOTSUPP;
a53771
+
a53771
+    if (FIPS_mode())
a53771
+        return KRB5_CRYPTO_INTERNAL;
a53771
+
a53771
     vt = (krb5_clpreauth_vtable)vtable;
a53771
     vt->name = "spake";
a53771
     vt->pa_type_list = pa_types;
a53771
diff --git a/src/plugins/preauth/spake/spake_kdc.c b/src/plugins/preauth/spake/spake_kdc.c
a53771
index 88c964ce1..c7df0392f 100644
a53771
--- a/src/plugins/preauth/spake/spake_kdc.c
a53771
+++ b/src/plugins/preauth/spake/spake_kdc.c
a53771
@@ -41,6 +41,8 @@
a53771
 
a53771
 #include <krb5/kdcpreauth_plugin.h>
a53771
 
a53771
+#include <openssl/crypto.h>
a53771
+
a53771
 /*
a53771
  * The SPAKE kdcpreauth module uses a secure cookie containing the following
a53771
  * concatenated fields (all integer fields are big-endian):
a53771
@@ -571,6 +573,10 @@ kdcpreauth_spake_initvt(krb5_context context, int maj_ver, int min_ver,
a53771
 
a53771
     if (maj_ver != 1)
a53771
         return KRB5_PLUGIN_VER_NOTSUPP;
a53771
+
a53771
+    if (FIPS_mode())
a53771
+        return KRB5_CRYPTO_INTERNAL;
a53771
+
a53771
     vt = (krb5_kdcpreauth_vtable)vtable;
a53771
     vt->name = "spake";
a53771
     vt->pa_type_list = pa_types;