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

d1ad9f
From b8e3a859f8904d395ea0e1a7d6c49a791029711c Mon Sep 17 00:00:00 2001
d1ad9f
From: Robbie Harwood <rharwood@redhat.com>
d1ad9f
Date: Fri, 9 Nov 2018 15:12:21 -0500
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
d1ad9f
post8 silences a static analyzer warning.
a53771
d1ad9f
post9 add missing includes for FIPS_mode() macro
a53771
d1ad9f
Last-updated: krb5-1.20.1
a53771
---
d1ad9f
 doc/admin/conf_files/krb5_conf.rst            |  6 +++
d1ad9f
 src/lib/crypto/krb/prng.c                     | 17 ++++++-
d1ad9f
 .../crypto/openssl/enc_provider/camellia.c    |  7 +++
d1ad9f
 src/lib/crypto/openssl/enc_provider/rc4.c     | 17 ++++++-
d1ad9f
 .../crypto/openssl/hash_provider/hash_evp.c   | 12 +++++
d1ad9f
 src/lib/crypto/openssl/hmac.c                 |  7 ++-
d1ad9f
 src/lib/krad/attr.c                           | 46 ++++++++++++++-----
a53771
 src/lib/krad/attrset.c                        |  5 +-
d1ad9f
 src/lib/krad/internal.h                       | 32 ++++++++++++-
d1ad9f
 src/lib/krad/packet.c                         | 22 +++++----
d1ad9f
 src/lib/krad/remote.c                         | 10 +++-
a53771
 src/lib/krad/t_attr.c                         |  3 +-
a53771
 src/lib/krad/t_attrset.c                      |  4 +-
d1ad9f
 src/plugins/preauth/spake/spake_client.c      | 10 ++++
d1ad9f
 src/plugins/preauth/spake/spake_kdc.c         | 10 ++++
d1ad9f
 15 files changed, 175 insertions(+), 33 deletions(-)
a53771
a53771
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
d1ad9f
index d5d6e06ebb..2a4962069f 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
d1ad9f
index d6b79e2dea..ae37c77518 100644
a53771
--- a/src/lib/crypto/krb/prng.c
a53771
+++ b/src/lib/crypto/krb/prng.c
d1ad9f
@@ -26,6 +26,14 @@
a53771
 
a53771
 #include "crypto_int.h"
a53771
 
a53771
+#include <openssl/rand.h>
a53771
+
d1ad9f
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
d1ad9f
+#include <openssl/fips.h>
d1ad9f
+#else
d1ad9f
+#include <openssl/crypto.h>
d1ad9f
+#endif
d1ad9f
+
a53771
 krb5_error_code KRB5_CALLCONV
a53771
 krb5_c_random_seed(krb5_context context, krb5_data *data)
a53771
 {
d1ad9f
@@ -96,9 +104,16 @@ cleanup:
d1ad9f
 static krb5_boolean
d1ad9f
 get_os_entropy(unsigned char *buf, size_t len)
a53771
 {
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
d1ad9f
index 01920e6ce1..3dd3b0624f 100644
a53771
--- a/src/lib/crypto/openssl/enc_provider/camellia.c
a53771
+++ b/src/lib/crypto/openssl/enc_provider/camellia.c
d1ad9f
@@ -32,6 +32,7 @@
d1ad9f
 #include <openssl/camellia.h>
d1ad9f
 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
d1ad9f
 #include <openssl/core_names.h>
d1ad9f
+#include <openssl/fips.h>
d1ad9f
 #else
d1ad9f
 #include <openssl/modes.h>
d1ad9f
 #endif
d1ad9f
@@ -387,6 +388,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
 
d1ad9f
@@ -418,6 +422,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
d1ad9f
index 448d563348..6a83f10d27 100644
a53771
--- a/src/lib/crypto/openssl/enc_provider/rc4.c
a53771
+++ b/src/lib/crypto/openssl/enc_provider/rc4.c
d1ad9f
@@ -38,6 +38,10 @@
d1ad9f
 
d1ad9f
 #include <openssl/evp.h>
d1ad9f
 
d1ad9f
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
d1ad9f
+#include <openssl/fips.h>
d1ad9f
+#endif
d1ad9f
+
d1ad9f
 /*
d1ad9f
  * The loopback field is a pointer to the structure.  If the application copies
d1ad9f
  * the state (not a valid operation, but one which happens to works with some
d1ad9f
@@ -69,6 +73,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;
d1ad9f
@@ -116,7 +123,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);
d1ad9f
@@ -128,6 +140,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
d1ad9f
index f2fbffdb29..11659908bb 100644
a53771
--- a/src/lib/crypto/openssl/hash_provider/hash_evp.c
a53771
+++ b/src/lib/crypto/openssl/hash_provider/hash_evp.c
d1ad9f
@@ -60,6 +60,11 @@ hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
d1ad9f
     if (ctx == NULL)
d1ad9f
         return ENOMEM;
a53771
 
d1ad9f
+    if (type == EVP_md4() || type == EVP_md5()) {
d1ad9f
+        /* See comments below in hash_md4() and hash_md5(). */
d1ad9f
+        EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
38a7f7
+    }
38a7f7
+
d1ad9f
     ok = EVP_DigestInit_ex(ctx, type, NULL);
d1ad9f
     for (i = 0; i < num_data; i++) {
d1ad9f
         if (!SIGN_IOV(&data[i]))
d1ad9f
@@ -78,6 +83,11 @@ hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
a53771
 static krb5_error_code
a53771
 hash_md4(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
a53771
 {
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
+     */
d1ad9f
     return hash_evp(EVP_md4(), data, num_data, output);
a53771
 }
a53771
 
d1ad9f
@@ -90,6 +100,8 @@ const struct krb5_hash_provider krb5int_hash_md4 = {
a53771
 static krb5_error_code
a53771
 hash_md5(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
a53771
 {
d1ad9f
+    /* MD5 is needed in FIPS mode for communication with RADIUS servers.  This
d1ad9f
+     * is gated in libkrad by libdefaults->radius_md5_fips_override. */
d1ad9f
     return hash_evp(EVP_md5(), data, num_data, output);
a53771
 }
a53771
 
a53771
diff --git a/src/lib/crypto/openssl/hmac.c b/src/lib/crypto/openssl/hmac.c
d1ad9f
index bf12b8d6a0..25a419d73a 100644
a53771
--- a/src/lib/crypto/openssl/hmac.c
a53771
+++ b/src/lib/crypto/openssl/hmac.c
d1ad9f
@@ -59,6 +59,7 @@
d1ad9f
 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
d1ad9f
 #include <openssl/params.h>
d1ad9f
 #include <openssl/core_names.h>
d1ad9f
+#include <openssl/fips.h>
d1ad9f
 #else
d1ad9f
 #include <openssl/hmac.h>
d1ad9f
 #endif
d1ad9f
@@ -111,7 +112,11 @@ map_digest(const struct krb5_hash_provider *hash)
a53771
         return EVP_sha256();
d1ad9f
     else if (hash == &krb5int_hash_sha384)
a53771
         return EVP_sha384();
d1ad9f
-    else if (hash == &krb5int_hash_md5)
a53771
+
a53771
+    if (FIPS_mode())
a53771
+        return NULL;
a53771
+
d1ad9f
+    if (hash == &krb5int_hash_md5)
a53771
         return EVP_md5();
d1ad9f
     else if (hash == &krb5int_hash_md4)
a53771
         return EVP_md4();
a53771
diff --git a/src/lib/krad/attr.c b/src/lib/krad/attr.c
d1ad9f
index 9c13d9d755..42d354a3b5 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
d1ad9f
index f309f1581c..6ec031e320 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
d1ad9f
index 7619563fc5..a17b6f39b1 100644
a53771
--- a/src/lib/krad/internal.h
a53771
+++ b/src/lib/krad/internal.h
d1ad9f
@@ -39,6 +39,12 @@
a53771
 #include <sys/socket.h>
a53771
 #include <netdb.h>
a53771
 
a53771
+#include <openssl/crypto.h>
a53771
+
d1ad9f
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
d1ad9f
+#include <openssl/fips.h>
d1ad9f
+#endif
d1ad9f
+
a53771
 #ifndef UCHAR_MAX
a53771
 #define UCHAR_MAX 255
a53771
 #endif
d1ad9f
@@ -49,6 +55,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);
d1ad9f
@@ -57,7 +70,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
d1ad9f
@@ -69,7 +83,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
d1ad9f
@@ -156,4 +171,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
+
d1ad9f
+    (void)profile_get_boolean(ctx->profile, "libdefaults",
d1ad9f
+                              "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
d1ad9f
index c597174b65..fc2d248001 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
d1ad9f
index 06ae751bc8..929f1cef67 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
             }
d1ad9f
@@ -460,6 +460,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
d1ad9f
index eb2a780c89..4d285ad9de 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
d1ad9f
index 7928335ca4..0f95762534 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
d1ad9f
index 00734a13b5..13c699071f 100644
a53771
--- a/src/plugins/preauth/spake/spake_client.c
a53771
+++ b/src/plugins/preauth/spake/spake_client.c
d1ad9f
@@ -38,6 +38,12 @@
a53771
 #include "groups.h"
a53771
 #include <krb5/clpreauth_plugin.h>
a53771
 
a53771
+#include <openssl/crypto.h>
a53771
+
d1ad9f
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
d1ad9f
+#include <openssl/fips.h>
d1ad9f
+#endif
d1ad9f
+
a53771
 typedef struct reqstate_st {
a53771
     krb5_pa_spake *msg;         /* set in prep_questions, used in process */
a53771
     krb5_keyblock *initial_key;
d1ad9f
@@ -375,6 +381,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
d1ad9f
index 1a772d450f..3394f8a58e 100644
a53771
--- a/src/plugins/preauth/spake/spake_kdc.c
a53771
+++ b/src/plugins/preauth/spake/spake_kdc.c
d1ad9f
@@ -41,6 +41,12 @@
a53771
 
a53771
 #include <krb5/kdcpreauth_plugin.h>
a53771
 
a53771
+#include <openssl/crypto.h>
a53771
+
d1ad9f
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
d1ad9f
+#include <openssl/fips.h>
d1ad9f
+#endif
d1ad9f
+
a53771
 /*
a53771
  * The SPAKE kdcpreauth module uses a secure cookie containing the following
a53771
  * concatenated fields (all integer fields are big-endian):
d1ad9f
@@ -551,6 +557,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;
38a7f7
-- 
d1ad9f
2.38.1
38a7f7