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

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