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