diff --git a/SOURCES/Add-function-and-enctype-flag-for-deprecations.patch b/SOURCES/Add-function-and-enctype-flag-for-deprecations.patch
new file mode 100644
index 0000000..2a094e7
--- /dev/null
+++ b/SOURCES/Add-function-and-enctype-flag-for-deprecations.patch
@@ -0,0 +1,183 @@
+From ba1fd0a44c74089d42af244ff2b315baf506fd2f Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Tue, 15 Jan 2019 16:16:57 -0500
+Subject: [PATCH] Add function and enctype flag for deprecations
+
+krb5int_c_deprecated_enctype() checks for the ETYPE_DEPRECATED flag on
+enctypes.  All ENCTYPE_WEAK enctypes are currently deprecated; not all
+deprecated enctypes are considered weak.  Deprecations follow RFC 6649
+and RFC 8429.
+
+(cherry picked from commit 484a6e7712f9b66e782b2520f07b0883889e116f)
+---
+ src/include/k5-int.h               |  1 +
+ src/lib/crypto/krb/crypto_int.h    |  9 ++++++++-
+ src/lib/crypto/krb/enctype_util.c  |  7 +++++++
+ src/lib/crypto/krb/etypes.c        | 19 ++++++++++---------
+ src/lib/crypto/libk5crypto.exports |  1 +
+ src/lib/krb5_32.def                |  3 +++
+ 6 files changed, 30 insertions(+), 10 deletions(-)
+
+diff --git a/src/include/k5-int.h b/src/include/k5-int.h
+index 8f9329c59..255cee822 100644
+--- a/src/include/k5-int.h
++++ b/src/include/k5-int.h
+@@ -2077,6 +2077,7 @@ krb5_get_tgs_ktypes(krb5_context, krb5_const_principal, krb5_enctype **);
+ krb5_boolean krb5_is_permitted_enctype(krb5_context, krb5_enctype);
+ 
+ krb5_boolean KRB5_CALLCONV krb5int_c_weak_enctype(krb5_enctype);
++krb5_boolean KRB5_CALLCONV krb5int_c_deprecated_enctype(krb5_enctype);
+ krb5_error_code k5_enctype_to_ssf(krb5_enctype enctype, unsigned int *ssf_out);
+ 
+ krb5_error_code krb5_kdc_rep_decrypt_proc(krb5_context, const krb5_keyblock *,
+diff --git a/src/lib/crypto/krb/crypto_int.h b/src/lib/crypto/krb/crypto_int.h
+index e5099291e..6c1c77cac 100644
+--- a/src/lib/crypto/krb/crypto_int.h
++++ b/src/lib/crypto/krb/crypto_int.h
+@@ -114,7 +114,14 @@ struct krb5_keytypes {
+     unsigned int ssf;
+ };
+ 
+-#define ETYPE_WEAK 1
++/*
++ * "Weak" means the enctype is believed to be vulnerable to practical attacks,
++ * and will be disabled unless allow_weak_crypto is set to true.  "Deprecated"
++ * means the enctype has been deprecated by the IETF, and affects display and
++ * logging.
++ */
++#define ETYPE_WEAK (1 << 0)
++#define ETYPE_DEPRECATED (1 << 1)
+ 
+ extern const struct krb5_keytypes krb5int_enctypes_list[];
+ extern const int krb5int_enctypes_length;
+diff --git a/src/lib/crypto/krb/enctype_util.c b/src/lib/crypto/krb/enctype_util.c
+index b1b40e7ec..e394f4e19 100644
+--- a/src/lib/crypto/krb/enctype_util.c
++++ b/src/lib/crypto/krb/enctype_util.c
+@@ -51,6 +51,13 @@ krb5int_c_weak_enctype(krb5_enctype etype)
+     return (ktp != NULL && (ktp->flags & ETYPE_WEAK) != 0);
+ }
+ 
++krb5_boolean KRB5_CALLCONV
++krb5int_c_deprecated_enctype(krb5_enctype etype)
++{
++    const struct krb5_keytypes *ktp = find_enctype(etype);
++    return ktp != NULL && (ktp->flags & ETYPE_DEPRECATED) != 0;
++}
++
+ krb5_error_code KRB5_CALLCONV
+ krb5_c_enctype_compare(krb5_context context, krb5_enctype e1, krb5_enctype e2,
+                        krb5_boolean *similar)
+diff --git a/src/lib/crypto/krb/etypes.c b/src/lib/crypto/krb/etypes.c
+index 53d4a5c79..8f44c37e7 100644
+--- a/src/lib/crypto/krb/etypes.c
++++ b/src/lib/crypto/krb/etypes.c
+@@ -33,6 +33,7 @@
+    that the keytypes are all near each other.  I'd rather not make
+    that assumption. */
+ 
++/* Deprecations come from RFC 6649 and RFC 8249. */
+ const struct krb5_keytypes krb5int_enctypes_list[] = {
+     { ENCTYPE_DES_CBC_CRC,
+       "des-cbc-crc", { 0 }, "DES cbc mode with CRC-32",
+@@ -42,7 +43,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
+       krb5int_des_string_to_key, k5_rand2key_des,
+       krb5int_des_prf,
+       CKSUMTYPE_RSA_MD5_DES,
+-      ETYPE_WEAK, 56 },
++      ETYPE_WEAK | ETYPE_DEPRECATED, 56 },
+     { ENCTYPE_DES_CBC_MD4,
+       "des-cbc-md4", { 0 }, "DES cbc mode with RSA-MD4",
+       &krb5int_enc_des, &krb5int_hash_md4,
+@@ -51,7 +52,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
+       krb5int_des_string_to_key, k5_rand2key_des,
+       krb5int_des_prf,
+       CKSUMTYPE_RSA_MD4_DES,
+-      ETYPE_WEAK, 56 },
++      ETYPE_WEAK | ETYPE_DEPRECATED, 56 },
+     { ENCTYPE_DES_CBC_MD5,
+       "des-cbc-md5", { "des" }, "DES cbc mode with RSA-MD5",
+       &krb5int_enc_des, &krb5int_hash_md5,
+@@ -60,7 +61,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
+       krb5int_des_string_to_key, k5_rand2key_des,
+       krb5int_des_prf,
+       CKSUMTYPE_RSA_MD5_DES,
+-      ETYPE_WEAK, 56 },
++      ETYPE_WEAK | ETYPE_DEPRECATED, 56 },
+     { ENCTYPE_DES_CBC_RAW,
+       "des-cbc-raw", { 0 }, "DES cbc mode raw",
+       &krb5int_enc_des, NULL,
+@@ -69,7 +70,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
+       krb5int_des_string_to_key, k5_rand2key_des,
+       krb5int_des_prf,
+       0,
+-      ETYPE_WEAK, 56 },
++      ETYPE_WEAK | ETYPE_DEPRECATED, 56 },
+     { ENCTYPE_DES3_CBC_RAW,
+       "des3-cbc-raw", { 0 }, "Triple DES cbc mode raw",
+       &krb5int_enc_des3, NULL,
+@@ -78,7 +79,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
+       krb5int_dk_string_to_key, k5_rand2key_des3,
+       NULL, /*PRF*/
+       0,
+-      ETYPE_WEAK, 112 },
++      ETYPE_WEAK | ETYPE_DEPRECATED, 112 },
+ 
+     { ENCTYPE_DES3_CBC_SHA1,
+       "des3-cbc-sha1", { "des3-hmac-sha1", "des3-cbc-sha1-kd" },
+@@ -89,7 +90,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
+       krb5int_dk_string_to_key, k5_rand2key_des3,
+       krb5int_dk_prf,
+       CKSUMTYPE_HMAC_SHA1_DES3,
+-      0 /*flags*/, 112 },
++      ETYPE_DEPRECATED, 112 },
+ 
+     { ENCTYPE_DES_HMAC_SHA1,
+       "des-hmac-sha1", { 0 }, "DES with HMAC/sha1",
+@@ -99,7 +100,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
+       krb5int_dk_string_to_key, k5_rand2key_des,
+       NULL, /*PRF*/
+       0,
+-      ETYPE_WEAK, 56 },
++      ETYPE_WEAK | ETYPE_DEPRECATED, 56 },
+ 
+     /* rc4-hmac uses a 128-bit key, but due to weaknesses in the RC4 cipher, we
+      * consider its strength degraded and assign it an SSF value of 64. */
+@@ -113,7 +114,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
+       krb5int_arcfour_decrypt, krb5int_arcfour_string_to_key,
+       k5_rand2key_direct, krb5int_arcfour_prf,
+       CKSUMTYPE_HMAC_MD5_ARCFOUR,
+-      0 /*flags*/, 64 },
++      ETYPE_DEPRECATED, 64 },
+     { ENCTYPE_ARCFOUR_HMAC_EXP,
+       "arcfour-hmac-exp", { "rc4-hmac-exp", "arcfour-hmac-md5-exp" },
+       "Exportable ArcFour with HMAC/md5",
+@@ -124,7 +125,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
+       krb5int_arcfour_decrypt, krb5int_arcfour_string_to_key,
+       k5_rand2key_direct, krb5int_arcfour_prf,
+       CKSUMTYPE_HMAC_MD5_ARCFOUR,
+-      ETYPE_WEAK, 40
++      ETYPE_WEAK | ETYPE_DEPRECATED, 40
+     },
+ 
+     { ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+diff --git a/src/lib/crypto/libk5crypto.exports b/src/lib/crypto/libk5crypto.exports
+index 82eb5f30c..90afdf5f7 100644
+--- a/src/lib/crypto/libk5crypto.exports
++++ b/src/lib/crypto/libk5crypto.exports
+@@ -109,3 +109,4 @@ k5_allow_weak_pbkdf2iter
+ krb5_c_prfplus
+ krb5_c_derive_prfplus
+ k5_enctype_to_ssf
++krb5int_c_deprecated_enctype
+diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def
+index c35022931..e6a487593 100644
+--- a/src/lib/krb5_32.def
++++ b/src/lib/krb5_32.def
+@@ -487,3 +487,6 @@ EXPORTS
+ 	encode_krb5_pa_spake				@444 ; PRIVATE
+ 	decode_krb5_pa_spake				@445 ; PRIVATE
+ 	k5_free_pa_spake				@446 ; PRIVATE
++
++; new in 1.18
++	krb5int_c_deprecated_enctype			@450 ; PRIVATE
diff --git a/SOURCES/Add-soft-pkcs11-source-code.patch b/SOURCES/Add-soft-pkcs11-source-code.patch
new file mode 100644
index 0000000..25488b5
--- /dev/null
+++ b/SOURCES/Add-soft-pkcs11-source-code.patch
@@ -0,0 +1,2072 @@
+From c4626f17ece51265eaa5446e96104b115ecbe31c Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson@mit.edu>
+Date: Thu, 20 Jun 2019 10:45:18 -0400
+Subject: [PATCH] Add soft-pkcs11 source code
+
+soft-pkcs11 is no longer available upstream and is not generally
+packaged in distributions, making it inconvenient to use for tests.
+Import the 1.8 source code, detabified and with trailing whitespace
+removed but otherwise unmodified.
+
+(cherry picked from commit a4bc3e513a58b0d1292f3506ac3b35be8c178086)
+(cherry picked from commit a186597238ae40e167ce041857b5bd1f94ee2383)
+---
+ src/tests/softpkcs11/main.c | 2049 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 2049 insertions(+)
+ create mode 100644 src/tests/softpkcs11/main.c
+
+diff --git a/src/tests/softpkcs11/main.c b/src/tests/softpkcs11/main.c
+new file mode 100644
+index 000000000..2acec5169
+--- /dev/null
++++ b/src/tests/softpkcs11/main.c
+@@ -0,0 +1,2049 @@
++/*
++ * Copyright (c) 2004-2006, Stockholms universitet
++ * (Stockholm University, Stockholm Sweden)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of the university nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
++ * POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "locl.h"
++
++/* RCSID("$Id: main.c,v 1.24 2006/01/11 12:42:53 lha Exp $"); */
++
++#define OBJECT_ID_MASK          0xfff
++#define HANDLE_OBJECT_ID(h)     ((h) & OBJECT_ID_MASK)
++#define OBJECT_ID(obj)          HANDLE_OBJECT_ID((obj)->object_handle)
++
++struct st_attr {
++    CK_ATTRIBUTE attribute;
++    int secret;
++};
++
++struct st_object {
++    CK_OBJECT_HANDLE object_handle;
++    struct st_attr *attrs;
++    int num_attributes;
++    enum {
++        STO_T_CERTIFICATE,
++        STO_T_PRIVATE_KEY,
++        STO_T_PUBLIC_KEY
++    } type;
++    union {
++        X509 *cert;
++        EVP_PKEY *public_key;
++        struct {
++            const char *file;
++            EVP_PKEY *key;
++            X509 *cert;
++        } private_key;
++    } u;
++};
++
++static struct soft_token {
++    CK_VOID_PTR application;
++    CK_NOTIFY notify;
++    struct {
++        struct st_object **objs;
++        int num_objs;
++    } object;
++    struct {
++        int hardware_slot;
++        int app_error_fatal;
++        int login_done;
++    } flags;
++    int open_sessions;
++    struct session_state {
++        CK_SESSION_HANDLE session_handle;
++
++        struct {
++            CK_ATTRIBUTE *attributes;
++            CK_ULONG num_attributes;
++            int next_object;
++        } find;
++
++        int encrypt_object;
++        CK_MECHANISM_PTR encrypt_mechanism;
++        int decrypt_object;
++        CK_MECHANISM_PTR decrypt_mechanism;
++        int sign_object;
++        CK_MECHANISM_PTR sign_mechanism;
++        int verify_object;
++        CK_MECHANISM_PTR verify_mechanism;
++        int digest_object;
++    } state[10];
++#define MAX_NUM_SESSION (sizeof(soft_token.state)/sizeof(soft_token.state[0]))
++    FILE *logfile;
++} soft_token;
++
++static void
++application_error(const char *fmt, ...)
++{
++    va_list ap;
++    va_start(ap, fmt);
++    vprintf(fmt, ap);
++    va_end(ap);
++    if (soft_token.flags.app_error_fatal)
++        abort();
++}
++
++static void
++st_logf(const char *fmt, ...)
++{
++    va_list ap;
++    if (soft_token.logfile == NULL)
++        return;
++    va_start(ap, fmt);
++    vfprintf(soft_token.logfile, fmt, ap);
++    va_end(ap);
++    fflush(soft_token.logfile);
++}
++
++static void
++snprintf_fill(char *str, size_t size, char fillchar, const char *fmt, ...)
++{
++    int len;
++    va_list ap;
++    len = vsnprintf(str, size, fmt, ap);
++    va_end(ap);
++    if (len < 0 || len > size)
++        return;
++    while(len < size)
++        str[len++] = fillchar;
++}
++
++#ifndef TEST_APP
++#define printf error_use_st_logf
++#endif
++
++#define VERIFY_SESSION_HANDLE(s, state)                 \
++{                                                       \
++    CK_RV ret;                                          \
++    ret = verify_session_handle(s, state);              \
++    if (ret != CKR_OK) {                                \
++        /* return CKR_OK */;                            \
++    }                                                   \
++}
++
++static CK_RV
++verify_session_handle(CK_SESSION_HANDLE hSession,
++                      struct session_state **state)
++{
++    int i;
++
++    for (i = 0; i < MAX_NUM_SESSION; i++){
++        if (soft_token.state[i].session_handle == hSession)
++            break;
++    }
++    if (i == MAX_NUM_SESSION) {
++        application_error("use of invalid handle: 0x%08lx\n",
++                          (unsigned long)hSession);
++        return CKR_SESSION_HANDLE_INVALID;
++    }
++    if (state)
++        *state = &soft_token.state[i];
++    return CKR_OK;
++}
++
++static CK_RV
++object_handle_to_object(CK_OBJECT_HANDLE handle,
++                        struct st_object **object)
++{
++    int i = HANDLE_OBJECT_ID(handle);
++
++    *object = NULL;
++    if (i >= soft_token.object.num_objs)
++        return CKR_ARGUMENTS_BAD;
++    if (soft_token.object.objs[i] == NULL)
++        return CKR_ARGUMENTS_BAD;
++    if (soft_token.object.objs[i]->object_handle != handle)
++        return CKR_ARGUMENTS_BAD;
++    *object = soft_token.object.objs[i];
++    return CKR_OK;
++}
++
++static int
++attributes_match(const struct st_object *obj,
++                 const CK_ATTRIBUTE *attributes,
++                 CK_ULONG num_attributes)
++{
++    CK_ULONG i;
++    int j;
++    st_logf("attributes_match: %ld\n", (unsigned long)OBJECT_ID(obj));
++
++    for (i = 0; i < num_attributes; i++) {
++        int match = 0;
++        for (j = 0; j < obj->num_attributes; j++) {
++            if (attributes[i].type == obj->attrs[j].attribute.type &&
++                attributes[i].ulValueLen == obj->attrs[j].attribute.ulValueLen &&
++                memcmp(attributes[i].pValue, obj->attrs[j].attribute.pValue,
++                       attributes[i].ulValueLen) == 0) {
++                match = 1;
++                break;
++            }
++        }
++        if (match == 0) {
++            st_logf("type %d attribute have no match\n", attributes[i].type);
++            return 0;
++        }
++    }
++    st_logf("attribute matches\n");
++    return 1;
++}
++
++static void
++print_attributes(const CK_ATTRIBUTE *attributes,
++                 CK_ULONG num_attributes)
++{
++    CK_ULONG i;
++
++    st_logf("find objects: attrs: %lu\n", (unsigned long)num_attributes);
++
++    for (i = 0; i < num_attributes; i++) {
++        st_logf("  type: ");
++        switch (attributes[i].type) {
++        case CKA_TOKEN: {
++            CK_BBOOL *ck_true;
++            if (attributes[i].ulValueLen != sizeof(CK_BBOOL)) {
++                application_error("token attribute wrong length\n");
++                break;
++            }
++            ck_true = attributes[i].pValue;
++            st_logf("token: %s", *ck_true ? "TRUE" : "FALSE");
++            break;
++        }
++        case CKA_CLASS: {
++            CK_OBJECT_CLASS *class;
++            if (attributes[i].ulValueLen != sizeof(CK_ULONG)) {
++                application_error("class attribute wrong length\n");
++                break;
++            }
++            class = attributes[i].pValue;
++            st_logf("class ");
++            switch (*class) {
++            case CKO_CERTIFICATE:
++                st_logf("certificate");
++                break;
++            case CKO_PUBLIC_KEY:
++                st_logf("public key");
++                break;
++            case CKO_PRIVATE_KEY:
++                st_logf("private key");
++                break;
++            case CKO_SECRET_KEY:
++                st_logf("secret key");
++                break;
++            case CKO_DOMAIN_PARAMETERS:
++                st_logf("domain parameters");
++                break;
++            default:
++                st_logf("[class %lx]", (long unsigned)*class);
++                break;
++            }
++            break;
++        }
++        case CKA_PRIVATE:
++            st_logf("private");
++            break;
++        case CKA_LABEL:
++            st_logf("label");
++            break;
++        case CKA_APPLICATION:
++            st_logf("application");
++            break;
++        case CKA_VALUE:
++            st_logf("value");
++            break;
++        case CKA_ID:
++            st_logf("id");
++            break;
++        default:
++            st_logf("[unknown 0x%08lx]", (unsigned long)attributes[i].type);
++            break;
++        }
++        st_logf("\n");
++    }
++}
++
++static struct st_object *
++add_st_object(void)
++{
++    struct st_object *o, **objs;
++    int i;
++
++    o = malloc(sizeof(*o));
++    if (o == NULL)
++        return NULL;
++    memset(o, 0, sizeof(*o));
++    o->attrs = NULL;
++    o->num_attributes = 0;
++
++    for (i = 0; i < soft_token.object.num_objs; i++) {
++        if (soft_token.object.objs == NULL) {
++            soft_token.object.objs[i] = o;
++            break;
++        }
++    }
++    if (i == soft_token.object.num_objs) {
++        objs = realloc(soft_token.object.objs,
++                       (soft_token.object.num_objs + 1) * sizeof(soft_token.object.objs[0]));
++        if (objs == NULL) {
++            free(o);
++            return NULL;
++        }
++        soft_token.object.objs = objs;
++        soft_token.object.objs[soft_token.object.num_objs++] = o;
++    }
++    soft_token.object.objs[i]->object_handle =
++        (random() & (~OBJECT_ID_MASK)) | i;
++
++    return o;
++}
++
++static CK_RV
++add_object_attribute(struct st_object *o,
++                     int secret,
++                     CK_ATTRIBUTE_TYPE type,
++                     CK_VOID_PTR pValue,
++                     CK_ULONG ulValueLen)
++{
++    struct st_attr *a;
++    int i;
++
++    i = o->num_attributes;
++    a = realloc(o->attrs, (i + 1) * sizeof(o->attrs[0]));
++    if (a == NULL)
++        return CKR_DEVICE_MEMORY;
++    o->attrs = a;
++    o->attrs[i].secret = secret;
++    o->attrs[i].attribute.type = type;
++    o->attrs[i].attribute.pValue = malloc(ulValueLen);
++    if (o->attrs[i].attribute.pValue == NULL && ulValueLen != 0)
++        return CKR_DEVICE_MEMORY;
++    memcpy(o->attrs[i].attribute.pValue, pValue, ulValueLen);
++    o->attrs[i].attribute.ulValueLen = ulValueLen;
++    o->num_attributes++;
++
++    return CKR_OK;
++}
++
++static CK_RV
++add_pubkey_info(struct st_object *o, CK_KEY_TYPE key_type, EVP_PKEY *key)
++{
++    switch (key_type) {
++    case CKK_RSA: {
++        CK_BYTE *modulus = NULL;
++        size_t modulus_len = 0;
++        CK_ULONG modulus_bits = 0;
++        CK_BYTE *exponent = NULL;
++        size_t exponent_len = 0;
++
++        modulus_bits = BN_num_bits(key->pkey.rsa->n);
++
++        modulus_len = BN_num_bytes(key->pkey.rsa->n);
++        modulus = malloc(modulus_len);
++        BN_bn2bin(key->pkey.rsa->n, modulus);
++
++        exponent_len = BN_num_bytes(key->pkey.rsa->e);
++        exponent = malloc(exponent_len);
++        BN_bn2bin(key->pkey.rsa->e, exponent);
++
++        add_object_attribute(o, 0, CKA_MODULUS, modulus, modulus_len);
++        add_object_attribute(o, 0, CKA_MODULUS_BITS,
++                             &modulus_bits, sizeof(modulus_bits));
++        add_object_attribute(o, 0, CKA_PUBLIC_EXPONENT,
++                             exponent, exponent_len);
++
++        RSA_set_method(key->pkey.rsa, RSA_PKCS1_SSLeay());
++
++        free(modulus);
++        free(exponent);
++    }
++    default:
++        /* XXX */
++        break;
++    }
++    return CKR_OK;
++}
++
++
++static int
++pem_callback(char *buf, int num, int w, void *key)
++{
++    return -1;
++}
++
++
++static CK_RV
++add_certificate(char *label,
++                const char *cert_file,
++                const char *private_key_file,
++                char *id,
++                int anchor)
++{
++    struct st_object *o = NULL;
++    CK_BBOOL bool_true = CK_TRUE;
++    CK_BBOOL bool_false = CK_FALSE;
++    CK_OBJECT_CLASS c;
++    CK_CERTIFICATE_TYPE cert_type = CKC_X_509;
++    CK_KEY_TYPE key_type;
++    CK_MECHANISM_TYPE mech_type;
++    void *cert_data = NULL;
++    size_t cert_length;
++    void *subject_data = NULL;
++    size_t subject_length;
++    void *issuer_data = NULL;
++    size_t issuer_length;
++    void *serial_data = NULL;
++    size_t serial_length;
++    CK_RV ret = CKR_GENERAL_ERROR;
++    X509 *cert;
++    EVP_PKEY *public_key;
++
++    size_t id_len = strlen(id);
++
++    {
++        FILE *f;
++
++        f = fopen(cert_file, "r");
++        if (f == NULL) {
++            st_logf("failed to open file %s\n", cert_file);
++            return CKR_GENERAL_ERROR;
++        }
++
++        cert = PEM_read_X509(f, NULL, NULL, NULL);
++        fclose(f);
++        if (cert == NULL) {
++            st_logf("failed reading PEM cert\n");
++            return CKR_GENERAL_ERROR;
++        }
++
++        OPENSSL_ASN1_MALLOC_ENCODE(X509, cert_data, cert_length, cert, ret);
++        if (ret)
++            goto out;
++
++        OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, issuer_data, issuer_length,
++                                   X509_get_issuer_name(cert), ret);
++        if (ret)
++            goto out;
++
++        OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, subject_data, subject_length,
++                                   X509_get_subject_name(cert), ret);
++        if (ret)
++            goto out;
++
++        OPENSSL_ASN1_MALLOC_ENCODE(ASN1_INTEGER, serial_data, serial_length,
++                                   X509_get_serialNumber(cert), ret);
++        if (ret)
++            goto out;
++
++    }
++
++    st_logf("done parsing, adding to internal structure\n");
++
++    o = add_st_object();
++    if (o == NULL) {
++        ret = CKR_DEVICE_MEMORY;
++        goto out;
++    }
++    o->type = STO_T_CERTIFICATE;
++    o->u.cert = cert;
++    public_key = X509_get_pubkey(o->u.cert);
++
++    switch (EVP_PKEY_type(public_key->type)) {
++    case EVP_PKEY_RSA:
++        key_type = CKK_RSA;
++        break;
++    case EVP_PKEY_DSA:
++        key_type = CKK_DSA;
++        break;
++    default:
++        /* XXX */
++        break;
++    }
++
++    c = CKO_CERTIFICATE;
++    add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c));
++    add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true));
++    add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false));
++    add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false));
++    add_object_attribute(o, 0, CKA_LABEL, label, strlen(label));
++
++    add_object_attribute(o, 0, CKA_CERTIFICATE_TYPE, &cert_type, sizeof(cert_type));
++    add_object_attribute(o, 0, CKA_ID, id, id_len);
++
++    add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length);
++    add_object_attribute(o, 0, CKA_ISSUER, issuer_data, issuer_length);
++    add_object_attribute(o, 0, CKA_SERIAL_NUMBER, serial_data, serial_length);
++    add_object_attribute(o, 0, CKA_VALUE, cert_data, cert_length);
++    if (anchor)
++        add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true));
++    else
++        add_object_attribute(o, 0, CKA_TRUSTED, &bool_false, sizeof(bool_false));
++
++    st_logf("add cert ok: %lx\n", (unsigned long)OBJECT_ID(o));
++
++    o = add_st_object();
++    if (o == NULL) {
++        ret = CKR_DEVICE_MEMORY;
++        goto out;
++    }
++    o->type = STO_T_PUBLIC_KEY;
++    o->u.public_key = public_key;
++
++    c = CKO_PUBLIC_KEY;
++    add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c));
++    add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true));
++    add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false));
++    add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false));
++    add_object_attribute(o, 0, CKA_LABEL, label, strlen(label));
++
++    add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type));
++    add_object_attribute(o, 0, CKA_ID, id, id_len);
++    add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */
++    add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */
++    add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false));
++    add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false));
++    mech_type = CKM_RSA_X_509;
++    add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type));
++
++    add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length);
++    add_object_attribute(o, 0, CKA_ENCRYPT, &bool_true, sizeof(bool_true));
++    add_object_attribute(o, 0, CKA_VERIFY, &bool_true, sizeof(bool_true));
++    add_object_attribute(o, 0, CKA_VERIFY_RECOVER, &bool_false, sizeof(bool_false));
++    add_object_attribute(o, 0, CKA_WRAP, &bool_true, sizeof(bool_true));
++    add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true));
++
++    add_pubkey_info(o, key_type, public_key);
++
++    st_logf("add key ok: %lx\n", (unsigned long)OBJECT_ID(o));
++
++    if (private_key_file) {
++        CK_FLAGS flags;
++        FILE *f;
++
++        o = add_st_object();
++        if (o == NULL) {
++            ret = CKR_DEVICE_MEMORY;
++            goto out;
++        }
++        o->type = STO_T_PRIVATE_KEY;
++        o->u.private_key.file = strdup(private_key_file);
++        o->u.private_key.key = NULL;
++
++        o->u.private_key.cert = cert;
++
++        c = CKO_PRIVATE_KEY;
++        add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c));
++        add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true));
++        add_object_attribute(o, 0, CKA_PRIVATE, &bool_true, sizeof(bool_false));
++        add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false));
++        add_object_attribute(o, 0, CKA_LABEL, label, strlen(label));
++
++        add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type));
++        add_object_attribute(o, 0, CKA_ID, id, id_len);
++        add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */
++        add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */
++        add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false));
++        add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false));
++        mech_type = CKM_RSA_X_509;
++        add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type));
++
++        add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length);
++        add_object_attribute(o, 0, CKA_SENSITIVE, &bool_true, sizeof(bool_true));
++        add_object_attribute(o, 0, CKA_SECONDARY_AUTH, &bool_false, sizeof(bool_true));
++        flags = 0;
++        add_object_attribute(o, 0, CKA_AUTH_PIN_FLAGS, &flags, sizeof(flags));
++
++        add_object_attribute(o, 0, CKA_DECRYPT, &bool_true, sizeof(bool_true));
++        add_object_attribute(o, 0, CKA_SIGN, &bool_true, sizeof(bool_true));
++        add_object_attribute(o, 0, CKA_SIGN_RECOVER, &bool_false, sizeof(bool_false));
++        add_object_attribute(o, 0, CKA_UNWRAP, &bool_true, sizeof(bool_true));
++        add_object_attribute(o, 0, CKA_EXTRACTABLE, &bool_true, sizeof(bool_true));
++        add_object_attribute(o, 0, CKA_NEVER_EXTRACTABLE, &bool_false, sizeof(bool_false));
++
++        add_pubkey_info(o, key_type, public_key);
++
++        f = fopen(private_key_file, "r");
++        if (f == NULL) {
++            st_logf("failed to open private key\n");
++            return CKR_GENERAL_ERROR;
++        }
++
++        o->u.private_key.key = PEM_read_PrivateKey(f, NULL, pem_callback, NULL);
++        fclose(f);
++        if (o->u.private_key.key == NULL) {
++            st_logf("failed to read private key a startup\n");
++            /* don't bother with this failure for now,
++               fix it at C_Login time */;
++        } else {
++            /* XXX verify keytype */
++
++            if (key_type == CKK_RSA)
++                RSA_set_method(o->u.private_key.key->pkey.rsa,
++                               RSA_PKCS1_SSLeay());
++
++            if (X509_check_private_key(cert, o->u.private_key.key) != 1) {
++                EVP_PKEY_free(o->u.private_key.key);
++                o->u.private_key.key = NULL;
++                st_logf("private key doesn't verify\n");
++            } else {
++                st_logf("private key usable\n");
++                soft_token.flags.login_done = 1;
++            }
++        }
++    }
++
++    ret = CKR_OK;
++ out:
++    if (ret != CKR_OK) {
++        st_logf("something went wrong when adding cert!\n");
++
++        /* XXX wack o */;
++    }
++    free(cert_data);
++    free(serial_data);
++    free(issuer_data);
++    free(subject_data);
++
++    return ret;
++}
++
++static void
++find_object_final(struct session_state *state)
++{
++    if (state->find.attributes) {
++        CK_ULONG i;
++
++        for (i = 0; i < state->find.num_attributes; i++) {
++            if (state->find.attributes[i].pValue)
++                free(state->find.attributes[i].pValue);
++        }
++        free(state->find.attributes);
++        state->find.attributes = NULL;
++        state->find.num_attributes = 0;
++        state->find.next_object = -1;
++    }
++}
++
++static void
++reset_crypto_state(struct session_state *state)
++{
++    state->encrypt_object = -1;
++    if (state->encrypt_mechanism)
++        free(state->encrypt_mechanism);
++    state->encrypt_mechanism = NULL_PTR;
++    state->decrypt_object = -1;
++    if (state->decrypt_mechanism)
++        free(state->decrypt_mechanism);
++    state->decrypt_mechanism = NULL_PTR;
++    state->sign_object = -1;
++    if (state->sign_mechanism)
++        free(state->sign_mechanism);
++    state->sign_mechanism = NULL_PTR;
++    state->verify_object = -1;
++    if (state->verify_mechanism)
++        free(state->verify_mechanism);
++    state->verify_mechanism = NULL_PTR;
++    state->digest_object = -1;
++}
++
++static void
++close_session(struct session_state *state)
++{
++    if (state->find.attributes) {
++        application_error("application didn't do C_FindObjectsFinal\n");
++        find_object_final(state);
++    }
++
++    state->session_handle = CK_INVALID_HANDLE;
++    soft_token.application = NULL_PTR;
++    soft_token.notify = NULL_PTR;
++    reset_crypto_state(state);
++}
++
++static const char *
++has_session(void)
++{
++    return soft_token.open_sessions > 0 ? "yes" : "no";
++}
++
++static void
++read_conf_file(const char *fn)
++{
++    char buf[1024], *cert, *key, *id, *label, *s, *p;
++    int anchor;
++    FILE *f;
++
++    f = fopen(fn, "r");
++    if (f == NULL) {
++        st_logf("can't open configuration file %s\n", fn);
++        return;
++    }
++
++    while(fgets(buf, sizeof(buf), f) != NULL) {
++        buf[strcspn(buf, "\n")] = '\0';
++
++        anchor = 0;
++
++        st_logf("line: %s\n", buf);
++
++        p = buf;
++        while (isspace(*p))
++            p++;
++        if (*p == '#')
++            continue;
++        while (isspace(*p))
++            p++;
++
++        s = NULL;
++        id = strtok_r(p, "\t", &s);
++        if (id == NULL)
++            continue;
++        label = strtok_r(NULL, "\t", &s);
++        if (label == NULL)
++            continue;
++        cert = strtok_r(NULL, "\t", &s);
++        if (cert == NULL)
++            continue;
++        key = strtok_r(NULL, "\t", &s);
++
++        /* XXX */
++        if (strcmp(id, "anchor") == 0) {
++            id = "\x00\x00";
++            anchor = 1;
++        }
++
++        st_logf("adding: %s\n", label);
++
++        add_certificate(label, cert, key, id, anchor);
++    }
++}
++
++static CK_RV
++func_not_supported(void)
++{
++    st_logf("function not supported\n");
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++CK_RV
++C_Initialize(CK_VOID_PTR a)
++{
++    CK_C_INITIALIZE_ARGS_PTR args = a;
++    st_logf("Initialize\n");
++    int i;
++
++    OpenSSL_add_all_algorithms();
++    ERR_load_crypto_strings();
++
++    srandom(getpid() ^ time(NULL));
++
++    for (i = 0; i < MAX_NUM_SESSION; i++) {
++        soft_token.state[i].session_handle = CK_INVALID_HANDLE;
++        soft_token.state[i].find.attributes = NULL;
++        soft_token.state[i].find.num_attributes = 0;
++        soft_token.state[i].find.next_object = -1;
++        reset_crypto_state(&soft_token.state[i]);
++    }
++
++    soft_token.flags.hardware_slot = 1;
++    soft_token.flags.app_error_fatal = 0;
++    soft_token.flags.login_done = 0;
++
++    soft_token.object.objs = NULL;
++    soft_token.object.num_objs = 0;
++
++    soft_token.logfile = NULL;
++#if 0
++    soft_token.logfile = stdout;
++#endif
++#if 0
++    soft_token.logfile = fopen("/tmp/log-pkcs11.txt", "a");
++#endif
++
++    if (a != NULL_PTR) {
++        st_logf("\tCreateMutex:\t%p\n", args->CreateMutex);
++        st_logf("\tDestroyMutext\t%p\n", args->DestroyMutex);
++        st_logf("\tLockMutext\t%p\n", args->LockMutex);
++        st_logf("\tUnlockMutext\t%p\n", args->UnlockMutex);
++        st_logf("\tFlags\t%04x\n", (unsigned int)args->flags);
++    }
++
++    {
++        char *fn = NULL, *home = NULL;
++
++        if (getuid() == geteuid()) {
++            fn = getenv("SOFTPKCS11RC");
++            if (fn)
++                fn = strdup(fn);
++            home = getenv("HOME");
++        }
++        if (fn == NULL && home == NULL) {
++            struct passwd *pw = getpwuid(getuid());
++            if(pw != NULL)
++                home = pw->pw_dir;
++        }
++        if (fn == NULL) {
++            if (home)
++                asprintf(&fn, "%s/.soft-token.rc", home);
++            else
++                fn = strdup("/etc/soft-token.rc");
++        }
++
++        read_conf_file(fn);
++        free(fn);
++    }
++
++    return CKR_OK;
++}
++
++CK_RV
++C_Finalize(CK_VOID_PTR args)
++{
++    int i;
++
++    st_logf("Finalize\n");
++
++    for (i = 0; i < MAX_NUM_SESSION; i++) {
++        if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) {
++            application_error("application finalized without "
++                              "closing session\n");
++            close_session(&soft_token.state[i]);
++        }
++    }
++
++    return CKR_OK;
++}
++
++CK_RV
++C_GetInfo(CK_INFO_PTR args)
++{
++    st_logf("GetInfo\n");
++
++    memset(args, 17, sizeof(*args));
++    args->cryptokiVersion.major = 2;
++    args->cryptokiVersion.minor = 10;
++    snprintf_fill((char *)args->manufacturerID,
++                  sizeof(args->manufacturerID),
++                  ' ',
++                  "SoftToken");
++    snprintf_fill((char *)args->libraryDescription,
++                  sizeof(args->libraryDescription), ' ',
++                  "SoftToken");
++    args->libraryVersion.major = 1;
++    args->libraryVersion.minor = 8;
++
++    return CKR_OK;
++}
++
++extern CK_FUNCTION_LIST funcs;
++
++CK_RV
++C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList)
++{
++    *ppFunctionList = &funcs;
++    return CKR_OK;
++}
++
++CK_RV
++C_GetSlotList(CK_BBOOL tokenPresent,
++              CK_SLOT_ID_PTR pSlotList,
++              CK_ULONG_PTR   pulCount)
++{
++    st_logf("GetSlotList: %s\n",
++            tokenPresent ? "tokenPresent" : "token not Present");
++    if (pSlotList)
++        pSlotList[0] = 1;
++    *pulCount = 1;
++    return CKR_OK;
++}
++
++CK_RV
++C_GetSlotInfo(CK_SLOT_ID slotID,
++              CK_SLOT_INFO_PTR pInfo)
++{
++    st_logf("GetSlotInfo: slot: %d : %s\n", (int)slotID, has_session());
++
++    memset(pInfo, 18, sizeof(*pInfo));
++
++    if (slotID != 1)
++        return CKR_ARGUMENTS_BAD;
++
++    snprintf_fill((char *)pInfo->slotDescription,
++                  sizeof(pInfo->slotDescription),
++                  ' ',
++                  "SoftToken (slot)");
++    snprintf_fill((char *)pInfo->manufacturerID,
++                  sizeof(pInfo->manufacturerID),
++                  ' ',
++                  "SoftToken (slot)");
++    pInfo->flags = CKF_TOKEN_PRESENT;
++    if (soft_token.flags.hardware_slot)
++        pInfo->flags |= CKF_HW_SLOT;
++    pInfo->hardwareVersion.major = 1;
++    pInfo->hardwareVersion.minor = 0;
++    pInfo->firmwareVersion.major = 1;
++    pInfo->firmwareVersion.minor = 0;
++
++    return CKR_OK;
++}
++
++CK_RV
++C_GetTokenInfo(CK_SLOT_ID slotID,
++               CK_TOKEN_INFO_PTR pInfo)
++{
++    st_logf("GetTokenInfo: %s\n", has_session());
++
++    memset(pInfo, 19, sizeof(*pInfo));
++
++    snprintf_fill((char *)pInfo->label,
++                  sizeof(pInfo->label),
++                  ' ',
++                  "SoftToken (token)");
++    snprintf_fill((char *)pInfo->manufacturerID,
++                  sizeof(pInfo->manufacturerID),
++                  ' ',
++                  "SoftToken (token)");
++    snprintf_fill((char *)pInfo->model,
++                  sizeof(pInfo->model),
++                  ' ',
++                  "SoftToken (token)");
++    snprintf_fill((char *)pInfo->serialNumber,
++                  sizeof(pInfo->serialNumber),
++                  ' ',
++                  "4711");
++    pInfo->flags =
++        CKF_TOKEN_INITIALIZED |
++        CKF_USER_PIN_INITIALIZED;
++
++    if (soft_token.flags.login_done == 0)
++        pInfo->flags |= CKF_LOGIN_REQUIRED;
++
++    /* CFK_RNG |
++       CKF_RESTORE_KEY_NOT_NEEDED |
++    */
++    pInfo->ulMaxSessionCount = MAX_NUM_SESSION;
++    pInfo->ulSessionCount = soft_token.open_sessions;
++    pInfo->ulMaxRwSessionCount = MAX_NUM_SESSION;
++    pInfo->ulRwSessionCount = soft_token.open_sessions;
++    pInfo->ulMaxPinLen = 1024;
++    pInfo->ulMinPinLen = 0;
++    pInfo->ulTotalPublicMemory = 4711;
++    pInfo->ulFreePublicMemory = 4712;
++    pInfo->ulTotalPrivateMemory = 4713;
++    pInfo->ulFreePrivateMemory = 4714;
++    pInfo->hardwareVersion.major = 2;
++    pInfo->hardwareVersion.minor = 0;
++    pInfo->firmwareVersion.major = 2;
++    pInfo->firmwareVersion.minor = 0;
++
++    return CKR_OK;
++}
++
++CK_RV
++C_GetMechanismList(CK_SLOT_ID slotID,
++                   CK_MECHANISM_TYPE_PTR pMechanismList,
++                   CK_ULONG_PTR pulCount)
++{
++    st_logf("GetMechanismList\n");
++
++    *pulCount = 2;
++    if (pMechanismList == NULL_PTR)
++        return CKR_OK;
++    pMechanismList[0] = CKM_RSA_X_509;
++    pMechanismList[1] = CKM_RSA_PKCS;
++
++    return CKR_OK;
++}
++
++CK_RV
++C_GetMechanismInfo(CK_SLOT_ID slotID,
++                   CK_MECHANISM_TYPE type,
++                   CK_MECHANISM_INFO_PTR pInfo)
++{
++    st_logf("GetMechanismInfo: slot %d type: %d\n",
++            (int)slotID, (int)type);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++CK_RV
++C_InitToken(CK_SLOT_ID slotID,
++            CK_UTF8CHAR_PTR pPin,
++            CK_ULONG ulPinLen,
++            CK_UTF8CHAR_PTR pLabel)
++{
++    st_logf("InitToken: slot %d\n", (int)slotID);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++CK_RV
++C_OpenSession(CK_SLOT_ID slotID,
++              CK_FLAGS flags,
++              CK_VOID_PTR pApplication,
++              CK_NOTIFY Notify,
++              CK_SESSION_HANDLE_PTR phSession)
++{
++    int i;
++
++    st_logf("OpenSession: slot: %d\n", (int)slotID);
++
++    if (soft_token.open_sessions == MAX_NUM_SESSION)
++        return CKR_SESSION_COUNT;
++
++    soft_token.application = pApplication;
++    soft_token.notify = Notify;
++
++    for (i = 0; i < MAX_NUM_SESSION; i++)
++        if (soft_token.state[i].session_handle == CK_INVALID_HANDLE)
++            break;
++    if (i == MAX_NUM_SESSION)
++        abort();
++
++    soft_token.open_sessions++;
++
++    soft_token.state[i].session_handle =
++        (CK_SESSION_HANDLE)(random() & 0xfffff);
++    *phSession = soft_token.state[i].session_handle;
++
++    return CKR_OK;
++}
++
++CK_RV
++C_CloseSession(CK_SESSION_HANDLE hSession)
++{
++    struct session_state *state;
++    st_logf("CloseSession\n");
++
++    if (verify_session_handle(hSession, &state) != CKR_OK)
++        application_error("closed session not open");
++    else
++        close_session(state);
++
++    return CKR_OK;
++}
++
++CK_RV
++C_CloseAllSessions(CK_SLOT_ID slotID)
++{
++    int i;
++
++    st_logf("CloseAllSessions\n");
++
++    for (i = 0; i < MAX_NUM_SESSION; i++)
++        if (soft_token.state[i].session_handle != CK_INVALID_HANDLE)
++            close_session(&soft_token.state[i]);
++
++    return CKR_OK;
++}
++
++CK_RV
++C_GetSessionInfo(CK_SESSION_HANDLE hSession,
++                 CK_SESSION_INFO_PTR pInfo)
++{
++    st_logf("GetSessionInfo\n");
++
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++
++    memset(pInfo, 20, sizeof(*pInfo));
++
++    pInfo->slotID = 1;
++    if (soft_token.flags.login_done)
++        pInfo->state = CKS_RO_USER_FUNCTIONS;
++    else
++        pInfo->state = CKS_RO_PUBLIC_SESSION;
++    pInfo->flags = CKF_SERIAL_SESSION;
++    pInfo->ulDeviceError = 0;
++
++    return CKR_OK;
++}
++
++CK_RV
++C_Login(CK_SESSION_HANDLE hSession,
++        CK_USER_TYPE userType,
++        CK_UTF8CHAR_PTR pPin,
++        CK_ULONG ulPinLen)
++{
++    char *pin = NULL;
++    int i;
++
++    st_logf("Login\n");
++
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++
++    if (pPin != NULL_PTR) {
++        asprintf(&pin, "%.*s", (int)ulPinLen, pPin);
++        st_logf("type: %d password: %s\n", (int)userType, pin);
++    }
++
++    for (i = 0; i < soft_token.object.num_objs; i++) {
++        struct st_object *o = soft_token.object.objs[i];
++        FILE *f;
++
++        if (o->type != STO_T_PRIVATE_KEY)
++            continue;
++
++        if (o->u.private_key.key)
++            continue;
++
++        f = fopen(o->u.private_key.file, "r");
++        if (f == NULL) {
++            st_logf("can't open private file: %s\n", o->u.private_key.file);
++            continue;
++        }
++
++        o->u.private_key.key = PEM_read_PrivateKey(f, NULL, NULL, pin);
++        fclose(f);
++        if (o->u.private_key.key == NULL) {
++            st_logf("failed to read key: %s error: %s\n",
++                    o->u.private_key.file,
++                    ERR_error_string(ERR_get_error(), NULL));
++            /* just ignore failure */;
++            continue;
++        }
++
++        /* XXX check keytype */
++        RSA_set_method(o->u.private_key.key->pkey.rsa, RSA_PKCS1_SSLeay());
++
++        if (X509_check_private_key(o->u.private_key.cert, o->u.private_key.key) != 1) {
++            EVP_PKEY_free(o->u.private_key.key);
++            o->u.private_key.key = NULL;
++            st_logf("private key %s doesn't verify\n", o->u.private_key.file);
++            continue;
++        }
++
++        soft_token.flags.login_done = 1;
++    }
++    free(pin);
++
++    return soft_token.flags.login_done ? CKR_OK : CKR_PIN_INCORRECT;
++}
++
++CK_RV
++C_Logout(CK_SESSION_HANDLE hSession)
++{
++    st_logf("Logout\n");
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++CK_RV
++C_GetObjectSize(CK_SESSION_HANDLE hSession,
++                CK_OBJECT_HANDLE hObject,
++                CK_ULONG_PTR pulSize)
++{
++    st_logf("GetObjectSize\n");
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++CK_RV
++C_GetAttributeValue(CK_SESSION_HANDLE hSession,
++                    CK_OBJECT_HANDLE hObject,
++                    CK_ATTRIBUTE_PTR pTemplate,
++                    CK_ULONG ulCount)
++{
++    struct session_state *state;
++    struct st_object *obj;
++    CK_ULONG i;
++    CK_RV ret;
++    int j;
++
++    st_logf("GetAttributeValue: %lx\n",
++            (unsigned long)HANDLE_OBJECT_ID(hObject));
++    VERIFY_SESSION_HANDLE(hSession, &state);
++
++    if ((ret = object_handle_to_object(hObject, &obj)) != CKR_OK) {
++        st_logf("object not found: %lx\n",
++                (unsigned long)HANDLE_OBJECT_ID(hObject));
++        return ret;
++    }
++
++    for (i = 0; i < ulCount; i++) {
++        st_logf("       getting 0x%08lx\n", (unsigned long)pTemplate[i].type);
++        for (j = 0; j < obj->num_attributes; j++) {
++            if (obj->attrs[j].secret) {
++                pTemplate[i].ulValueLen = (CK_ULONG)-1;
++                break;
++            }
++            if (pTemplate[i].type == obj->attrs[j].attribute.type) {
++                if (pTemplate[i].pValue != NULL_PTR && obj->attrs[j].secret == 0) {
++                    if (pTemplate[i].ulValueLen >= obj->attrs[j].attribute.ulValueLen)
++                        memcpy(pTemplate[i].pValue, obj->attrs[j].attribute.pValue,
++                               obj->attrs[j].attribute.ulValueLen);
++                }
++                pTemplate[i].ulValueLen = obj->attrs[j].attribute.ulValueLen;
++                break;
++            }
++        }
++        if (j == obj->num_attributes) {
++            st_logf("key type: 0x%08lx not found\n", (unsigned long)pTemplate[i].type);
++            pTemplate[i].ulValueLen = (CK_ULONG)-1;
++        }
++
++    }
++    return CKR_OK;
++}
++
++CK_RV
++C_FindObjectsInit(CK_SESSION_HANDLE hSession,
++                  CK_ATTRIBUTE_PTR pTemplate,
++                  CK_ULONG ulCount)
++{
++    struct session_state *state;
++
++    st_logf("FindObjectsInit\n");
++
++    VERIFY_SESSION_HANDLE(hSession, &state);
++
++    if (state->find.next_object != -1) {
++        application_error("application didn't do C_FindObjectsFinal\n");
++        find_object_final(state);
++    }
++    if (ulCount) {
++        CK_ULONG i;
++        size_t len;
++
++        print_attributes(pTemplate, ulCount);
++
++        state->find.attributes =
++            calloc(1, ulCount * sizeof(state->find.attributes[0]));
++        if (state->find.attributes == NULL)
++            return CKR_DEVICE_MEMORY;
++        for (i = 0; i < ulCount; i++) {
++            state->find.attributes[i].pValue =
++                malloc(pTemplate[i].ulValueLen);
++            if (state->find.attributes[i].pValue == NULL) {
++                find_object_final(state);
++                return CKR_DEVICE_MEMORY;
++            }
++            memcpy(state->find.attributes[i].pValue,
++                   pTemplate[i].pValue, pTemplate[i].ulValueLen);
++            state->find.attributes[i].type = pTemplate[i].type;
++            state->find.attributes[i].ulValueLen = pTemplate[i].ulValueLen;
++        }
++        state->find.num_attributes = ulCount;
++        state->find.next_object = 0;
++    } else {
++        st_logf("find all objects\n");
++        state->find.attributes = NULL;
++        state->find.num_attributes = 0;
++        state->find.next_object = 0;
++    }
++
++    return CKR_OK;
++}
++
++CK_RV
++C_FindObjects(CK_SESSION_HANDLE hSession,
++              CK_OBJECT_HANDLE_PTR phObject,
++              CK_ULONG ulMaxObjectCount,
++              CK_ULONG_PTR pulObjectCount)
++{
++    struct session_state *state;
++    int i;
++
++    st_logf("FindObjects\n");
++
++    VERIFY_SESSION_HANDLE(hSession, &state);
++
++    if (state->find.next_object == -1) {
++        application_error("application didn't do C_FindObjectsInit\n");
++        return CKR_ARGUMENTS_BAD;
++    }
++    if (ulMaxObjectCount == 0) {
++        application_error("application asked for 0 objects\n");
++        return CKR_ARGUMENTS_BAD;
++    }
++    *pulObjectCount = 0;
++    for (i = state->find.next_object; i < soft_token.object.num_objs; i++) {
++        st_logf("FindObjects: %d\n", i);
++        state->find.next_object = i + 1;
++        if (attributes_match(soft_token.object.objs[i],
++                             state->find.attributes,
++                             state->find.num_attributes)) {
++            *phObject++ = soft_token.object.objs[i]->object_handle;
++            ulMaxObjectCount--;
++            (*pulObjectCount)++;
++            if (ulMaxObjectCount == 0)
++                break;
++        }
++    }
++    return CKR_OK;
++}
++
++CK_RV
++C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
++{
++    struct session_state *state;
++
++    st_logf("FindObjectsFinal\n");
++    VERIFY_SESSION_HANDLE(hSession, &state);
++    find_object_final(state);
++    return CKR_OK;
++}
++
++static CK_RV
++commonInit(CK_ATTRIBUTE *attr_match, int attr_match_len,
++           const CK_MECHANISM_TYPE *mechs, int mechs_len,
++           const CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey,
++           struct st_object **o)
++{
++    CK_RV ret;
++    int i;
++
++    *o = NULL;
++    if ((ret = object_handle_to_object(hKey, o)) != CKR_OK)
++        return ret;
++
++    ret = attributes_match(*o, attr_match, attr_match_len);
++    if (!ret) {
++        application_error("called commonInit on key that doesn't "
++                          "support required attr");
++        return CKR_ARGUMENTS_BAD;
++    }
++
++    for (i = 0; i < mechs_len; i++)
++        if (mechs[i] == pMechanism->mechanism)
++            break;
++    if (i == mechs_len) {
++        application_error("called mech (%08lx) not supported\n",
++                          pMechanism->mechanism);
++        return CKR_ARGUMENTS_BAD;
++    }
++    return CKR_OK;
++}
++
++
++static CK_RV
++dup_mechanism(CK_MECHANISM_PTR *dup, const CK_MECHANISM_PTR pMechanism)
++{
++    CK_MECHANISM_PTR p;
++
++    p = malloc(sizeof(*p));
++    if (p == NULL)
++        return CKR_DEVICE_MEMORY;
++
++    if (*dup)
++        free(*dup);
++    *dup = p;
++    memcpy(p, pMechanism, sizeof(*p));
++
++    return CKR_OK;
++}
++
++
++CK_RV
++C_EncryptInit(CK_SESSION_HANDLE hSession,
++              CK_MECHANISM_PTR pMechanism,
++              CK_OBJECT_HANDLE hKey)
++{
++    struct session_state *state;
++    CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 };
++    CK_BBOOL bool_true = CK_TRUE;
++    CK_ATTRIBUTE attr[] = {
++        { CKA_ENCRYPT, &bool_true, sizeof(bool_true) }
++    };
++    struct st_object *o;
++    CK_RV ret;
++
++    st_logf("EncryptInit\n");
++    VERIFY_SESSION_HANDLE(hSession, &state);
++
++    ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]),
++                     mechs, sizeof(mechs)/sizeof(mechs[0]),
++                     pMechanism, hKey, &o);
++    if (ret)
++        return ret;
++
++    ret = dup_mechanism(&state->encrypt_mechanism, pMechanism);
++    if (ret == CKR_OK)
++        state->encrypt_object = OBJECT_ID(o);
++
++    return ret;
++}
++
++CK_RV
++C_Encrypt(CK_SESSION_HANDLE hSession,
++          CK_BYTE_PTR pData,
++          CK_ULONG ulDataLen,
++          CK_BYTE_PTR pEncryptedData,
++          CK_ULONG_PTR pulEncryptedDataLen)
++{
++    struct session_state *state;
++    struct st_object *o;
++    void *buffer = NULL;
++    CK_RV ret;
++    RSA *rsa;
++    int padding, len, buffer_len, padding_len;
++
++    st_logf("Encrypt\n");
++
++    VERIFY_SESSION_HANDLE(hSession, &state);
++
++    if (state->encrypt_object == -1)
++        return CKR_ARGUMENTS_BAD;
++
++    o = soft_token.object.objs[state->encrypt_object];
++
++    if (o->u.public_key == NULL) {
++        st_logf("public key NULL\n");
++        return CKR_ARGUMENTS_BAD;
++    }
++
++    rsa = o->u.public_key->pkey.rsa;
++
++    if (rsa == NULL)
++        return CKR_ARGUMENTS_BAD;
++
++    RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */
++
++    buffer_len = RSA_size(rsa);
++
++    buffer = malloc(buffer_len);
++    if (buffer == NULL) {
++        ret = CKR_DEVICE_MEMORY;
++        goto out;
++    }
++
++    ret = CKR_OK;
++    switch(state->encrypt_mechanism->mechanism) {
++    case CKM_RSA_PKCS:
++        padding = RSA_PKCS1_PADDING;
++        padding_len = RSA_PKCS1_PADDING_SIZE;
++        break;
++    case CKM_RSA_X_509:
++        padding = RSA_NO_PADDING;
++        padding_len = 0;
++        break;
++    default:
++        ret = CKR_FUNCTION_NOT_SUPPORTED;
++        goto out;
++    }
++
++    if (buffer_len + padding_len < ulDataLen) {
++        ret = CKR_ARGUMENTS_BAD;
++        goto out;
++    }
++
++    if (pulEncryptedDataLen == NULL) {
++        st_logf("pulEncryptedDataLen NULL\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto out;
++    }
++
++    if (pData == NULL_PTR) {
++        st_logf("data NULL\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto out;
++    }
++
++    len = RSA_public_encrypt(ulDataLen, pData, buffer, rsa, padding);
++    if (len <= 0) {
++        ret = CKR_DEVICE_ERROR;
++        goto out;
++    }
++    if (len > buffer_len)
++        abort();
++
++    if (pEncryptedData != NULL_PTR)
++        memcpy(pEncryptedData, buffer, len);
++    *pulEncryptedDataLen = len;
++
++ out:
++    if (buffer) {
++        memset(buffer, 0, buffer_len);
++        free(buffer);
++    }
++    return ret;
++}
++
++CK_RV
++C_EncryptUpdate(CK_SESSION_HANDLE hSession,
++                CK_BYTE_PTR pPart,
++                CK_ULONG ulPartLen,
++                CK_BYTE_PTR pEncryptedPart,
++                CK_ULONG_PTR pulEncryptedPartLen)
++{
++    st_logf("EncryptUpdate\n");
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++
++CK_RV
++C_EncryptFinal(CK_SESSION_HANDLE hSession,
++               CK_BYTE_PTR pLastEncryptedPart,
++               CK_ULONG_PTR pulLastEncryptedPartLen)
++{
++    st_logf("EncryptFinal\n");
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++
++/* C_DecryptInit initializes a decryption operation. */
++CK_RV
++C_DecryptInit(CK_SESSION_HANDLE hSession,
++              CK_MECHANISM_PTR pMechanism,
++              CK_OBJECT_HANDLE hKey)
++{
++    struct session_state *state;
++    CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 };
++    CK_BBOOL bool_true = CK_TRUE;
++    CK_ATTRIBUTE attr[] = {
++        { CKA_DECRYPT, &bool_true, sizeof(bool_true) }
++    };
++    struct st_object *o;
++    CK_RV ret;
++
++    st_logf("DecryptInit\n");
++    VERIFY_SESSION_HANDLE(hSession, &state);
++
++    ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]),
++                     mechs, sizeof(mechs)/sizeof(mechs[0]),
++                     pMechanism, hKey, &o);
++    if (ret)
++        return ret;
++
++    ret = dup_mechanism(&state->decrypt_mechanism, pMechanism);
++    if (ret == CKR_OK)
++        state->decrypt_object = OBJECT_ID(o);
++
++    return CKR_OK;
++}
++
++
++CK_RV
++C_Decrypt(CK_SESSION_HANDLE hSession,
++          CK_BYTE_PTR       pEncryptedData,
++          CK_ULONG          ulEncryptedDataLen,
++          CK_BYTE_PTR       pData,
++          CK_ULONG_PTR      pulDataLen)
++{
++    struct session_state *state;
++    struct st_object *o;
++    void *buffer = NULL;
++    CK_RV ret;
++    RSA *rsa;
++    int padding, len, buffer_len, padding_len;
++
++    st_logf("Decrypt\n");
++
++    VERIFY_SESSION_HANDLE(hSession, &state);
++
++    if (state->decrypt_object == -1)
++        return CKR_ARGUMENTS_BAD;
++
++    o = soft_token.object.objs[state->decrypt_object];
++
++    if (o->u.private_key.key == NULL) {
++        st_logf("private key NULL\n");
++        return CKR_ARGUMENTS_BAD;
++    }
++
++    rsa = o->u.private_key.key->pkey.rsa;
++
++    if (rsa == NULL)
++        return CKR_ARGUMENTS_BAD;
++
++    RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */
++
++    buffer_len = RSA_size(rsa);
++
++    buffer = malloc(buffer_len);
++    if (buffer == NULL) {
++        ret = CKR_DEVICE_MEMORY;
++        goto out;
++    }
++
++    ret = CKR_OK;
++    switch(state->decrypt_mechanism->mechanism) {
++    case CKM_RSA_PKCS:
++        padding = RSA_PKCS1_PADDING;
++        padding_len = RSA_PKCS1_PADDING_SIZE;
++        break;
++    case CKM_RSA_X_509:
++        padding = RSA_NO_PADDING;
++        padding_len = 0;
++        break;
++    default:
++        ret = CKR_FUNCTION_NOT_SUPPORTED;
++        goto out;
++    }
++
++    if (buffer_len + padding_len < ulEncryptedDataLen) {
++        ret = CKR_ARGUMENTS_BAD;
++        goto out;
++    }
++
++    if (pulDataLen == NULL) {
++        st_logf("pulDataLen NULL\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto out;
++    }
++
++    if (pEncryptedData == NULL_PTR) {
++        st_logf("data NULL\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto out;
++    }
++
++    len = RSA_private_decrypt(ulEncryptedDataLen, pEncryptedData, buffer,
++                              rsa, padding);
++    if (len <= 0) {
++        ret = CKR_DEVICE_ERROR;
++        goto out;
++    }
++    if (len > buffer_len)
++        abort();
++
++    if (pData != NULL_PTR)
++        memcpy(pData, buffer, len);
++    *pulDataLen = len;
++
++ out:
++    if (buffer) {
++        memset(buffer, 0, buffer_len);
++        free(buffer);
++    }
++    return ret;
++}
++
++
++CK_RV
++C_DecryptUpdate(CK_SESSION_HANDLE hSession,
++                CK_BYTE_PTR pEncryptedPart,
++                CK_ULONG ulEncryptedPartLen,
++                CK_BYTE_PTR pPart,
++                CK_ULONG_PTR pulPartLen)
++
++{
++    st_logf("DecryptUpdate\n");
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++
++CK_RV
++C_DecryptFinal(CK_SESSION_HANDLE hSession,
++               CK_BYTE_PTR pLastPart,
++               CK_ULONG_PTR pulLastPartLen)
++{
++    st_logf("DecryptFinal\n");
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++CK_RV
++C_DigestInit(CK_SESSION_HANDLE hSession,
++             CK_MECHANISM_PTR pMechanism)
++{
++    st_logf("DigestInit\n");
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++CK_RV
++C_SignInit(CK_SESSION_HANDLE hSession,
++           CK_MECHANISM_PTR pMechanism,
++           CK_OBJECT_HANDLE hKey)
++{
++    struct session_state *state;
++    CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 };
++    CK_BBOOL bool_true = CK_TRUE;
++    CK_ATTRIBUTE attr[] = {
++        { CKA_SIGN, &bool_true, sizeof(bool_true) }
++    };
++    struct st_object *o;
++    CK_RV ret;
++
++    st_logf("SignInit\n");
++    VERIFY_SESSION_HANDLE(hSession, &state);
++
++    ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]),
++                     mechs, sizeof(mechs)/sizeof(mechs[0]),
++                     pMechanism, hKey, &o);
++    if (ret)
++        return ret;
++
++    ret = dup_mechanism(&state->sign_mechanism, pMechanism);
++    if (ret == CKR_OK)
++        state->sign_object = OBJECT_ID(o);
++
++    return CKR_OK;
++}
++
++CK_RV
++C_Sign(CK_SESSION_HANDLE hSession,
++       CK_BYTE_PTR pData,
++       CK_ULONG ulDataLen,
++       CK_BYTE_PTR pSignature,
++       CK_ULONG_PTR pulSignatureLen)
++{
++    struct session_state *state;
++    struct st_object *o;
++    void *buffer = NULL;
++    CK_RV ret;
++    RSA *rsa;
++    int padding, len, buffer_len, padding_len;
++
++    st_logf("Sign\n");
++    VERIFY_SESSION_HANDLE(hSession, &state);
++
++    if (state->sign_object == -1)
++        return CKR_ARGUMENTS_BAD;
++
++    o = soft_token.object.objs[state->sign_object];
++
++    if (o->u.private_key.key == NULL) {
++        st_logf("private key NULL\n");
++        return CKR_ARGUMENTS_BAD;
++    }
++
++    rsa = o->u.private_key.key->pkey.rsa;
++
++    if (rsa == NULL)
++        return CKR_ARGUMENTS_BAD;
++
++    RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */
++
++    buffer_len = RSA_size(rsa);
++
++    buffer = malloc(buffer_len);
++    if (buffer == NULL) {
++        ret = CKR_DEVICE_MEMORY;
++        goto out;
++    }
++
++    switch(state->sign_mechanism->mechanism) {
++    case CKM_RSA_PKCS:
++        padding = RSA_PKCS1_PADDING;
++        padding_len = RSA_PKCS1_PADDING_SIZE;
++        break;
++    case CKM_RSA_X_509:
++        padding = RSA_NO_PADDING;
++        padding_len = 0;
++        break;
++    default:
++        ret = CKR_FUNCTION_NOT_SUPPORTED;
++        goto out;
++    }
++
++    if (buffer_len < ulDataLen + padding_len) {
++        ret = CKR_ARGUMENTS_BAD;
++        goto out;
++    }
++
++    if (pulSignatureLen == NULL) {
++        st_logf("signature len NULL\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto out;
++    }
++
++    if (pData == NULL_PTR) {
++        st_logf("data NULL\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto out;
++    }
++
++    len = RSA_private_encrypt(ulDataLen, pData, buffer, rsa, padding);
++    st_logf("private encrypt done\n");
++    if (len <= 0) {
++        ret = CKR_DEVICE_ERROR;
++        goto out;
++    }
++    if (len > buffer_len)
++        abort();
++
++    if (pSignature != NULL_PTR)
++        memcpy(pSignature, buffer, len);
++    *pulSignatureLen = len;
++
++    ret = CKR_OK;
++
++ out:
++    if (buffer) {
++        memset(buffer, 0, buffer_len);
++        free(buffer);
++    }
++    return ret;
++}
++
++CK_RV
++C_SignUpdate(CK_SESSION_HANDLE hSession,
++             CK_BYTE_PTR pPart,
++             CK_ULONG ulPartLen)
++{
++    st_logf("SignUpdate\n");
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++
++CK_RV
++C_SignFinal(CK_SESSION_HANDLE hSession,
++            CK_BYTE_PTR pSignature,
++            CK_ULONG_PTR pulSignatureLen)
++{
++    st_logf("SignUpdate\n");
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++CK_RV
++C_VerifyInit(CK_SESSION_HANDLE hSession,
++             CK_MECHANISM_PTR pMechanism,
++             CK_OBJECT_HANDLE hKey)
++{
++    struct session_state *state;
++    CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 };
++    CK_BBOOL bool_true = CK_TRUE;
++    CK_ATTRIBUTE attr[] = {
++        { CKA_VERIFY, &bool_true, sizeof(bool_true) }
++    };
++    struct st_object *o;
++    CK_RV ret;
++
++    st_logf("VerifyInit\n");
++    VERIFY_SESSION_HANDLE(hSession, &state);
++
++    ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]),
++                     mechs, sizeof(mechs)/sizeof(mechs[0]),
++                     pMechanism, hKey, &o);
++    if (ret)
++        return ret;
++
++    ret = dup_mechanism(&state->verify_mechanism, pMechanism);
++    if (ret == CKR_OK)
++        state->verify_object = OBJECT_ID(o);
++
++    return ret;
++}
++
++CK_RV
++C_Verify(CK_SESSION_HANDLE hSession,
++         CK_BYTE_PTR pData,
++         CK_ULONG ulDataLen,
++         CK_BYTE_PTR pSignature,
++         CK_ULONG ulSignatureLen)
++{
++    struct session_state *state;
++    struct st_object *o;
++    void *buffer = NULL;
++    CK_RV ret;
++    RSA *rsa;
++    int padding, len, buffer_len;
++
++    st_logf("Verify\n");
++    VERIFY_SESSION_HANDLE(hSession, &state);
++
++    if (state->verify_object == -1)
++        return CKR_ARGUMENTS_BAD;
++
++    o = soft_token.object.objs[state->verify_object];
++
++    if (o->u.public_key == NULL) {
++        st_logf("public key NULL\n");
++        return CKR_ARGUMENTS_BAD;
++    }
++
++    rsa = o->u.public_key->pkey.rsa;
++
++    if (rsa == NULL)
++        return CKR_ARGUMENTS_BAD;
++
++    RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */
++
++    buffer_len = RSA_size(rsa);
++
++    buffer = malloc(buffer_len);
++    if (buffer == NULL) {
++        ret = CKR_DEVICE_MEMORY;
++        goto out;
++    }
++
++    ret = CKR_OK;
++    switch(state->verify_mechanism->mechanism) {
++    case CKM_RSA_PKCS:
++        padding = RSA_PKCS1_PADDING;
++        break;
++    case CKM_RSA_X_509:
++        padding = RSA_NO_PADDING;
++        break;
++    default:
++        ret = CKR_FUNCTION_NOT_SUPPORTED;
++        goto out;
++    }
++
++    if (buffer_len < ulDataLen) {
++        ret = CKR_ARGUMENTS_BAD;
++        goto out;
++    }
++
++    if (pSignature == NULL) {
++        st_logf("signature NULL\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto out;
++    }
++
++    if (pData == NULL_PTR) {
++        st_logf("data NULL\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto out;
++    }
++
++    len = RSA_public_decrypt(ulDataLen, pData, buffer, rsa, padding);
++    st_logf("private encrypt done\n");
++    if (len <= 0) {
++        ret = CKR_DEVICE_ERROR;
++        goto out;
++    }
++    if (len > buffer_len)
++        abort();
++
++    if (len != ulSignatureLen) {
++        ret = CKR_GENERAL_ERROR;
++        goto out;
++    }
++
++    if (memcmp(pSignature, buffer, len) != 0) {
++        ret = CKR_GENERAL_ERROR;
++        goto out;
++    }
++
++ out:
++    if (buffer) {
++        memset(buffer, 0, buffer_len);
++        free(buffer);
++    }
++    return ret;
++}
++
++
++CK_RV
++C_VerifyUpdate(CK_SESSION_HANDLE hSession,
++               CK_BYTE_PTR pPart,
++               CK_ULONG ulPartLen)
++{
++    st_logf("VerifyUpdate\n");
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++CK_RV
++C_VerifyFinal(CK_SESSION_HANDLE hSession,
++              CK_BYTE_PTR pSignature,
++              CK_ULONG ulSignatureLen)
++{
++    st_logf("VerifyFinal\n");
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++CK_RV
++C_GenerateRandom(CK_SESSION_HANDLE hSession,
++                 CK_BYTE_PTR RandomData,
++                 CK_ULONG ulRandomLen)
++{
++    st_logf("GenerateRandom\n");
++    VERIFY_SESSION_HANDLE(hSession, NULL);
++    return CKR_FUNCTION_NOT_SUPPORTED;
++}
++
++
++CK_FUNCTION_LIST funcs = {
++    { 2, 11 },
++    C_Initialize,
++    C_Finalize,
++    C_GetInfo,
++    C_GetFunctionList,
++    C_GetSlotList,
++    C_GetSlotInfo,
++    C_GetTokenInfo,
++    C_GetMechanismList,
++    C_GetMechanismInfo,
++    C_InitToken,
++    (void *)func_not_supported, /* C_InitPIN */
++    (void *)func_not_supported, /* C_SetPIN */
++    C_OpenSession,
++    C_CloseSession,
++    C_CloseAllSessions,
++    C_GetSessionInfo,
++    (void *)func_not_supported, /* C_GetOperationState */
++    (void *)func_not_supported, /* C_SetOperationState */
++    C_Login,
++    C_Logout,
++    (void *)func_not_supported, /* C_CreateObject */
++    (void *)func_not_supported, /* C_CopyObject */
++    (void *)func_not_supported, /* C_DestroyObject */
++    (void *)func_not_supported, /* C_GetObjectSize */
++    C_GetAttributeValue,
++    (void *)func_not_supported, /* C_SetAttributeValue */
++    C_FindObjectsInit,
++    C_FindObjects,
++    C_FindObjectsFinal,
++    C_EncryptInit,
++    C_Encrypt,
++    C_EncryptUpdate,
++    C_EncryptFinal,
++    C_DecryptInit,
++    C_Decrypt,
++    C_DecryptUpdate,
++    C_DecryptFinal,
++    C_DigestInit,
++    (void *)func_not_supported, /* C_Digest */
++    (void *)func_not_supported, /* C_DigestUpdate */
++    (void *)func_not_supported, /* C_DigestKey */
++    (void *)func_not_supported, /* C_DigestFinal */
++    C_SignInit,
++    C_Sign,
++    C_SignUpdate,
++    C_SignFinal,
++    (void *)func_not_supported, /* C_SignRecoverInit */
++    (void *)func_not_supported, /* C_SignRecover */
++    C_VerifyInit,
++    C_Verify,
++    C_VerifyUpdate,
++    C_VerifyFinal,
++    (void *)func_not_supported, /* C_VerifyRecoverInit */
++    (void *)func_not_supported, /* C_VerifyRecover */
++    (void *)func_not_supported, /* C_DigestEncryptUpdate */
++    (void *)func_not_supported, /* C_DecryptDigestUpdate */
++    (void *)func_not_supported, /* C_SignEncryptUpdate */
++    (void *)func_not_supported, /* C_DecryptVerifyUpdate */
++    (void *)func_not_supported, /* C_GenerateKey */
++    (void *)func_not_supported, /* C_GenerateKeyPair */
++    (void *)func_not_supported, /* C_WrapKey */
++    (void *)func_not_supported, /* C_UnwrapKey */
++    (void *)func_not_supported, /* C_DeriveKey */
++    (void *)func_not_supported, /* C_SeedRandom */
++    C_GenerateRandom,
++    (void *)func_not_supported, /* C_GetFunctionStatus */
++    (void *)func_not_supported, /* C_CancelFunction */
++    (void *)func_not_supported  /* C_WaitForSlotEvent */
++};
diff --git a/SOURCES/Add-tests-for-KCM-ccache-type.patch b/SOURCES/Add-tests-for-KCM-ccache-type.patch
index 8aa8f23..f572041 100644
--- a/SOURCES/Add-tests-for-KCM-ccache-type.patch
+++ b/SOURCES/Add-tests-for-KCM-ccache-type.patch
@@ -1,4 +1,4 @@
-From dd863eb4311310c23ee12961fa8e91703f29a6f5 Mon Sep 17 00:00:00 2001
+From 7ab0bbac058d2b82aa3432759c600b22012f8afe Mon Sep 17 00:00:00 2001
 From: Greg Hudson <ghudson@mit.edu>
 Date: Thu, 22 Nov 2018 00:27:35 -0500
 Subject: [PATCH] Add tests for KCM ccache type
diff --git a/SOURCES/Address-some-optimized-out-memset-calls.patch b/SOURCES/Address-some-optimized-out-memset-calls.patch
index 58b1b25..372e527 100644
--- a/SOURCES/Address-some-optimized-out-memset-calls.patch
+++ b/SOURCES/Address-some-optimized-out-memset-calls.patch
@@ -1,4 +1,4 @@
-From 54348bbfaec50bb72d1625c015f8e5c4cfa59e0d Mon Sep 17 00:00:00 2001
+From 722247aa6201d18a7ee69c4a9a05315226fe6383 Mon Sep 17 00:00:00 2001
 From: Greg Hudson <ghudson@mit.edu>
 Date: Sun, 30 Dec 2018 16:40:28 -0500
 Subject: [PATCH] Address some optimized-out memset() calls
diff --git a/SOURCES/Avoid-alignment-warnings-in-openssl-rc4.c.patch b/SOURCES/Avoid-alignment-warnings-in-openssl-rc4.c.patch
new file mode 100644
index 0000000..e684c88
--- /dev/null
+++ b/SOURCES/Avoid-alignment-warnings-in-openssl-rc4.c.patch
@@ -0,0 +1,63 @@
+From e22f3e2439903aa05321ca339be6a12067b2c4db Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Mon, 6 May 2019 15:14:49 -0400
+Subject: [PATCH] Avoid alignment warnings in openssl rc4.c
+
+Add a comment to k5_arcfour_init_state() explaining how we stretch the
+krb5_data cipher state contract.  Use void * casts when interpreting
+the data pointer to avoid alignment warnings.
+
+[ghudson@mit.edu: moved and expanded comment; rewrote commit message]
+
+(cherry picked from commit 1cd41d76c12fc1cea0a8bf0d6a40f34623c60d6d)
+---
+ src/lib/crypto/openssl/enc_provider/rc4.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/src/lib/crypto/openssl/enc_provider/rc4.c b/src/lib/crypto/openssl/enc_provider/rc4.c
+index 7f3c086ed..a65d57b7a 100644
+--- a/src/lib/crypto/openssl/enc_provider/rc4.c
++++ b/src/lib/crypto/openssl/enc_provider/rc4.c
+@@ -57,7 +57,7 @@ struct arcfour_state {
+ 
+ /* In-place IOV crypto */
+ static krb5_error_code
+-k5_arcfour_docrypt(krb5_key key,const krb5_data *state, krb5_crypto_iov *data,
++k5_arcfour_docrypt(krb5_key key, const krb5_data *state, krb5_crypto_iov *data,
+                    size_t num_data)
+ {
+     size_t i;
+@@ -66,7 +66,7 @@ k5_arcfour_docrypt(krb5_key key,const krb5_data *state, krb5_crypto_iov *data,
+     EVP_CIPHER_CTX *ctx = NULL;
+     struct arcfour_state *arcstate;
+ 
+-    arcstate = (state != NULL) ? (struct arcfour_state *) state->data : NULL;
++    arcstate = (state != NULL) ? (void *)state->data : NULL;
+     if (arcstate != NULL) {
+         ctx = arcstate->ctx;
+         if (arcstate->loopback != arcstate)
+@@ -113,7 +113,7 @@ k5_arcfour_docrypt(krb5_key key,const krb5_data *state, krb5_crypto_iov *data,
+ static void
+ k5_arcfour_free_state(krb5_data *state)
+ {
+-    struct arcfour_state *arcstate = (struct arcfour_state *) state->data;
++    struct arcfour_state *arcstate = (void *)state->data;
+ 
+     EVP_CIPHER_CTX_free(arcstate->ctx);
+     free(arcstate);
+@@ -125,6 +125,15 @@ k5_arcfour_init_state(const krb5_keyblock *key,
+ {
+     struct arcfour_state *arcstate;
+ 
++    /*
++     * The cipher state here is a saved pointer to a struct arcfour_state
++     * object, rather than a flat byte array as in most enc providers.  The
++     * object includes a loopback pointer to detect if if the caller made a
++     * copy of the krb5_data value or otherwise assumed it was a simple byte
++     * array.  When we cast the data pointer back, we need to go through void *
++     * to avoid increased alignment warnings.
++     */
++
+     /* Create a state structure with an uninitialized context. */
+     arcstate = calloc(1, sizeof(*arcstate));
+     if (arcstate == NULL)
diff --git a/SOURCES/Become-FIPS-aware-with-3DES.patch b/SOURCES/Become-FIPS-aware-with-3DES.patch
deleted file mode 100644
index 02ffa7b..0000000
--- a/SOURCES/Become-FIPS-aware-with-3DES.patch
+++ /dev/null
@@ -1,183 +0,0 @@
-From 0c361343839b2ff6f89f1ee9c1e01d4f05ab6ffe Mon Sep 17 00:00:00 2001
-From: Robbie Harwood <rharwood@redhat.com>
-Date: Fri, 9 Nov 2018 15:12:21 -0500
-Subject: [PATCH] Become FIPS-aware (with 3DES)
-
-A lot of the FIPS error conditions from OpenSSL are incredibly
-mysterious (at best, things return NULL unexpectedly; at worst,
-internal assertions are tripped; most of the time, you just get
-ENOMEM).  In order to cope with this, we need to have some level of
-awareness of what we can and can't safely call.
-
-This will slow down some calls slightly (FIPS_mode() takes multiple
-locks), but not for any crypto we care about - which is to say that
-AES is fine.
-
-(cherry picked from commit 9f5fbf191d74cae9b28d318fff4c80d3d3e49c86)
----
- src/lib/crypto/openssl/enc_provider/camellia.c  |  6 ++++++
- src/lib/crypto/openssl/enc_provider/des.c       |  9 +++++++++
- src/lib/crypto/openssl/enc_provider/des3.c      |  6 ++++++
- src/lib/crypto/openssl/enc_provider/rc4.c       | 13 ++++++++++++-
- src/lib/crypto/openssl/hash_provider/hash_evp.c |  4 ++++
- src/lib/crypto/openssl/hmac.c                   |  6 +++++-
- 6 files changed, 42 insertions(+), 2 deletions(-)
-
-diff --git a/src/lib/crypto/openssl/enc_provider/camellia.c b/src/lib/crypto/openssl/enc_provider/camellia.c
-index 2da691329..f79679a0b 100644
---- a/src/lib/crypto/openssl/enc_provider/camellia.c
-+++ b/src/lib/crypto/openssl/enc_provider/camellia.c
-@@ -304,6 +304,9 @@ krb5int_camellia_cbc_mac(krb5_key key, const krb5_crypto_iov *data,
-     unsigned char blockY[CAMELLIA_BLOCK_SIZE], blockB[CAMELLIA_BLOCK_SIZE];
-     struct iov_cursor cursor;
- 
-+    if (FIPS_mode())
-+        return KRB5_CRYPTO_INTERNAL;
-+
-     if (output->length < CAMELLIA_BLOCK_SIZE)
-         return KRB5_BAD_MSIZE;
- 
-@@ -331,6 +334,9 @@ static krb5_error_code
- krb5int_camellia_init_state (const krb5_keyblock *key, krb5_keyusage usage,
-                              krb5_data *state)
- {
-+    if (FIPS_mode())
-+        return KRB5_CRYPTO_INTERNAL;
-+
-     state->length = 16;
-     state->data = (void *) malloc(16);
-     if (state->data == NULL)
-diff --git a/src/lib/crypto/openssl/enc_provider/des.c b/src/lib/crypto/openssl/enc_provider/des.c
-index a662db512..7d17d287e 100644
---- a/src/lib/crypto/openssl/enc_provider/des.c
-+++ b/src/lib/crypto/openssl/enc_provider/des.c
-@@ -85,6 +85,9 @@ k5_des_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
-     EVP_CIPHER_CTX *ctx;
-     krb5_boolean empty;
- 
-+    if (FIPS_mode())
-+        return KRB5_CRYPTO_INTERNAL;
-+
-     ret = validate(key, ivec, data, num_data, &empty);
-     if (ret != 0 || empty)
-         return ret;
-@@ -133,6 +136,9 @@ k5_des_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
-     EVP_CIPHER_CTX *ctx;
-     krb5_boolean empty;
- 
-+    if (FIPS_mode())
-+        return KRB5_CRYPTO_INTERNAL;
-+
-     ret = validate(key, ivec, data, num_data, &empty);
-     if (ret != 0 || empty)
-         return ret;
-@@ -182,6 +188,9 @@ k5_des_cbc_mac(krb5_key key, const krb5_crypto_iov *data, size_t num_data,
-     DES_key_schedule sched;
-     krb5_boolean empty;
- 
-+    if (FIPS_mode())
-+        return KRB5_CRYPTO_INTERNAL;
-+
-     ret = validate(key, ivec, data, num_data, &empty);
-     if (ret != 0)
-         return ret;
-diff --git a/src/lib/crypto/openssl/enc_provider/des3.c b/src/lib/crypto/openssl/enc_provider/des3.c
-index 1c439c2cd..8be555a8d 100644
---- a/src/lib/crypto/openssl/enc_provider/des3.c
-+++ b/src/lib/crypto/openssl/enc_provider/des3.c
-@@ -84,6 +84,9 @@ k5_des3_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
-     EVP_CIPHER_CTX *ctx;
-     krb5_boolean empty;
- 
-+    if (FIPS_mode())
-+        return KRB5_CRYPTO_INTERNAL;
-+
-     ret = validate(key, ivec, data, num_data, &empty);
-     if (ret != 0 || empty)
-         return ret;
-@@ -133,6 +136,9 @@ k5_des3_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
-     EVP_CIPHER_CTX *ctx;
-     krb5_boolean empty;
- 
-+    if (FIPS_mode())
-+        return KRB5_CRYPTO_INTERNAL;
-+
-     ret = validate(key, ivec, data, num_data, &empty);
-     if (ret != 0 || empty)
-         return ret;
-diff --git a/src/lib/crypto/openssl/enc_provider/rc4.c b/src/lib/crypto/openssl/enc_provider/rc4.c
-index 7f3c086ed..a3f2a7442 100644
---- a/src/lib/crypto/openssl/enc_provider/rc4.c
-+++ b/src/lib/crypto/openssl/enc_provider/rc4.c
-@@ -66,6 +66,9 @@ k5_arcfour_docrypt(krb5_key key,const krb5_data *state, krb5_crypto_iov *data,
-     EVP_CIPHER_CTX *ctx = NULL;
-     struct arcfour_state *arcstate;
- 
-+    if (FIPS_mode())
-+        return KRB5_CRYPTO_INTERNAL;
-+
-     arcstate = (state != NULL) ? (struct arcfour_state *) state->data : NULL;
-     if (arcstate != NULL) {
-         ctx = arcstate->ctx;
-@@ -113,7 +116,12 @@ k5_arcfour_docrypt(krb5_key key,const krb5_data *state, krb5_crypto_iov *data,
- static void
- k5_arcfour_free_state(krb5_data *state)
- {
--    struct arcfour_state *arcstate = (struct arcfour_state *) state->data;
-+    struct arcfour_state *arcstate;
-+
-+    if (FIPS_mode())
-+        return;
-+
-+    arcstate = (struct arcfour_state *) state->data;
- 
-     EVP_CIPHER_CTX_free(arcstate->ctx);
-     free(arcstate);
-@@ -125,6 +133,9 @@ k5_arcfour_init_state(const krb5_keyblock *key,
- {
-     struct arcfour_state *arcstate;
- 
-+    if (FIPS_mode())
-+        return KRB5_CRYPTO_INTERNAL;
-+
-     /* Create a state structure with an uninitialized context. */
-     arcstate = calloc(1, sizeof(*arcstate));
-     if (arcstate == NULL)
-diff --git a/src/lib/crypto/openssl/hash_provider/hash_evp.c b/src/lib/crypto/openssl/hash_provider/hash_evp.c
-index 957ed8d9c..8c1fd7f59 100644
---- a/src/lib/crypto/openssl/hash_provider/hash_evp.c
-+++ b/src/lib/crypto/openssl/hash_provider/hash_evp.c
-@@ -64,12 +64,16 @@ hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
- static krb5_error_code
- hash_md4(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
- {
-+    if (FIPS_mode())
-+        return KRB5_CRYPTO_INTERNAL;
-     return hash_evp(EVP_md4(), data, num_data, output);
- }
- 
- static krb5_error_code
- hash_md5(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
- {
-+    if (FIPS_mode())
-+        return KRB5_CRYPTO_INTERNAL;
-     return hash_evp(EVP_md5(), data, num_data, output);
- }
- 
-diff --git a/src/lib/crypto/openssl/hmac.c b/src/lib/crypto/openssl/hmac.c
-index b2db6ec02..d94d9ac94 100644
---- a/src/lib/crypto/openssl/hmac.c
-+++ b/src/lib/crypto/openssl/hmac.c
-@@ -103,7 +103,11 @@ map_digest(const struct krb5_hash_provider *hash)
-         return EVP_sha256();
-     else if (!strncmp(hash->hash_name, "SHA-384",7))
-         return EVP_sha384();
--    else if (!strncmp(hash->hash_name, "MD5", 3))
-+
-+    if (FIPS_mode())
-+        return NULL;
-+
-+    if (!strncmp(hash->hash_name, "MD5", 3))
-         return EVP_md5();
-     else if (!strncmp(hash->hash_name, "MD4", 3))
-         return EVP_md4();
diff --git a/SOURCES/FIPS-aware-SPAKE-group-negotiation.patch b/SOURCES/FIPS-aware-SPAKE-group-negotiation.patch
deleted file mode 100644
index f7bb807..0000000
--- a/SOURCES/FIPS-aware-SPAKE-group-negotiation.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 5b4467a2c47e6de814e69ec3eb4c3e7a4632119c Mon Sep 17 00:00:00 2001
-From: Robbie Harwood <rharwood@redhat.com>
-Date: Mon, 1 Apr 2019 13:13:09 -0400
-Subject: [PATCH] FIPS-aware SPAKE group negotiation
-
-(cherry picked from commit 59269fca96168aa89dc32834d188a54eea8953ac)
----
- src/plugins/preauth/spake/groups.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
-diff --git a/src/plugins/preauth/spake/groups.c b/src/plugins/preauth/spake/groups.c
-index a195cc195..8a913cb5a 100644
---- a/src/plugins/preauth/spake/groups.c
-+++ b/src/plugins/preauth/spake/groups.c
-@@ -56,6 +56,8 @@
- #include "trace.h"
- #include "groups.h"
- 
-+#include <openssl/crypto.h>
-+
- #define DEFAULT_GROUPS_CLIENT "edwards25519"
- #define DEFAULT_GROUPS_KDC ""
- 
-@@ -102,6 +104,9 @@ find_gdef(int32_t group)
- {
-     size_t i;
- 
-+    if (group == builtin_edwards25519.reg->id && FIPS_mode())
-+        return NULL;
-+
-     for (i = 0; groupdefs[i] != NULL; i++) {
-         if (groupdefs[i]->reg->id == group)
-             return groupdefs[i];
-@@ -116,6 +121,9 @@ find_gnum(const char *name)
- {
-     size_t i;
- 
-+    if (strcasecmp(name, builtin_edwards25519.reg->name) == 0 && FIPS_mode())
-+        return 0;
-+
-     for (i = 0; groupdefs[i] != NULL; i++) {
-         if (strcasecmp(name, groupdefs[i]->reg->name) == 0)
-             return groupdefs[i]->reg->id;
diff --git a/SOURCES/Fix-Coverity-defects-in-soft-pkcs11-test-code.patch b/SOURCES/Fix-Coverity-defects-in-soft-pkcs11-test-code.patch
new file mode 100644
index 0000000..359335d
--- /dev/null
+++ b/SOURCES/Fix-Coverity-defects-in-soft-pkcs11-test-code.patch
@@ -0,0 +1,207 @@
+From 44b429df9ac4bb8ad84a090fee1bd70d83adcf23 Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson@mit.edu>
+Date: Sat, 20 Jul 2019 00:51:52 -0400
+Subject: [PATCH] Fix Coverity defects in soft-pkcs11 test code
+
+Nothing in the code removes objects from soft_token.object.obs, so
+simplify add_st_object() not to search for an empty slot.  Avoid using
+random() by using a counter for session handles and just the array
+slot number for object handles.  Add a helper get_rcfilename() to
+facilitate checking the result of asprintf().  Properly initialize ap
+in sprintf_fill().  Close the file handle in read_conf_file().
+
+(cherry picked from commit b4831515b2f3b6fd7d7fd4bff4558c10c710891d)
+(cherry picked from commit 28db01445d2807d51b5045c0a04d5e49905de504)
+---
+ src/tests/softpkcs11/main.c | 102 +++++++++++++++++++-----------------
+ 1 file changed, 53 insertions(+), 49 deletions(-)
+
+diff --git a/src/tests/softpkcs11/main.c b/src/tests/softpkcs11/main.c
+index 5255323d3..2d1448ca2 100644
+--- a/src/tests/softpkcs11/main.c
++++ b/src/tests/softpkcs11/main.c
+@@ -78,6 +78,7 @@ compat_rsa_get0_key(const RSA *rsa, const BIGNUM **n, const BIGNUM **e,
+                 (BL) = i2d_##T((S), &p);                \
+                 if ((BL) <= 0) {                        \
+                     free((B));                          \
++                    (B) = NULL;                         \
+                     (R) = EINVAL;                       \
+                 }                                       \
+             }                                           \
+@@ -149,6 +150,7 @@ static struct soft_token {
+     } state[10];
+ #define MAX_NUM_SESSION (sizeof(soft_token.state)/sizeof(soft_token.state[0]))
+     FILE *logfile;
++    CK_SESSION_HANDLE next_session_handle;
+ } soft_token;
+ 
+ static void
+@@ -179,6 +181,7 @@ snprintf_fill(char *str, int size, char fillchar, const char *fmt, ...)
+ {
+     int len;
+     va_list ap;
++    va_start(ap, fmt);
+     len = vsnprintf(str, size, fmt, ap);
+     va_end(ap);
+     if (len < 0 || len > size)
+@@ -344,7 +347,13 @@ static struct st_object *
+ add_st_object(void)
+ {
+     struct st_object *o, **objs;
+-    int i;
++
++    objs = realloc(soft_token.object.objs,
++                   (soft_token.object.num_objs + 1) *
++                   sizeof(soft_token.object.objs[0]));
++    if (objs == NULL)
++        return NULL;
++    soft_token.object.objs = objs;
+ 
+     o = malloc(sizeof(*o));
+     if (o == NULL)
+@@ -352,26 +361,9 @@ add_st_object(void)
+     memset(o, 0, sizeof(*o));
+     o->attrs = NULL;
+     o->num_attributes = 0;
++    o->object_handle = soft_token.object.num_objs;
+ 
+-    for (i = 0; i < soft_token.object.num_objs; i++) {
+-        if (soft_token.object.objs == NULL) {
+-            soft_token.object.objs[i] = o;
+-            break;
+-        }
+-    }
+-    if (i == soft_token.object.num_objs) {
+-        objs = realloc(soft_token.object.objs,
+-                       (soft_token.object.num_objs + 1) * sizeof(soft_token.object.objs[0]));
+-        if (objs == NULL) {
+-            free(o);
+-            return NULL;
+-        }
+-        soft_token.object.objs = objs;
+-        soft_token.object.objs[soft_token.object.num_objs++] = o;
+-    }
+-    soft_token.object.objs[i]->object_handle =
+-        (random() & (~OBJECT_ID_MASK)) | i;
+-
++    soft_token.object.objs[soft_token.object.num_objs++] = o;
+     return o;
+ }
+ 
+@@ -797,6 +789,8 @@ read_conf_file(const char *fn)
+ 
+         add_certificate(label, cert, key, id, anchor);
+     }
++
++    fclose(f);
+ }
+ 
+ static CK_RV
+@@ -806,19 +800,47 @@ func_not_supported(void)
+     return CKR_FUNCTION_NOT_SUPPORTED;
+ }
+ 
++static char *
++get_rcfilename()
++{
++    struct passwd *pw;
++    const char *home = NULL;
++    char *fn;
++
++    if (getuid() == geteuid()) {
++        fn = getenv("SOFTPKCS11RC");
++        if (fn != NULL)
++            return strdup(fn);
++
++        home = getenv("HOME");
++    }
++
++    if (home == NULL) {
++        pw = getpwuid(getuid());
++        if (pw != NULL)
++            home = pw->pw_dir;
++    }
++
++    if (home == NULL)
++        return strdup("/etc/soft-token.rc");
++
++    if (asprintf(&fn, "%s/.soft-token.rc", home) < 0)
++        return NULL;
++    return fn;
++}
++
+ CK_RV
+ C_Initialize(CK_VOID_PTR a)
+ {
+     CK_C_INITIALIZE_ARGS_PTR args = a;
+     size_t i;
++    char *fn;
+ 
+     st_logf("Initialize\n");
+ 
+     OpenSSL_add_all_algorithms();
+     ERR_load_crypto_strings();
+ 
+-    srandom(getpid() ^ time(NULL));
+-
+     for (i = 0; i < MAX_NUM_SESSION; i++) {
+         soft_token.state[i].session_handle = CK_INVALID_HANDLE;
+         soft_token.state[i].find.attributes = NULL;
+@@ -850,31 +872,13 @@ C_Initialize(CK_VOID_PTR a)
+         st_logf("\tFlags\t%04x\n", (unsigned int)args->flags);
+     }
+ 
+-    {
+-        char *fn = NULL, *home = NULL;
+-
+-        if (getuid() == geteuid()) {
+-            fn = getenv("SOFTPKCS11RC");
+-            if (fn)
+-                fn = strdup(fn);
+-            home = getenv("HOME");
+-        }
+-        if (fn == NULL && home == NULL) {
+-            struct passwd *pw = getpwuid(getuid());
+-            if(pw != NULL)
+-                home = pw->pw_dir;
+-        }
+-        if (fn == NULL) {
+-            if (home)
+-                asprintf(&fn, "%s/.soft-token.rc", home);
+-            else
+-                fn = strdup("/etc/soft-token.rc");
+-        }
+-
+-        read_conf_file(fn);
+-        free(fn);
+-    }
++    soft_token.next_session_handle = 0;
+ 
++    fn = get_rcfilename();
++    if (fn == NULL)
++        return CKR_DEVICE_MEMORY;
++    read_conf_file(fn);
++    free(fn);
+     return CKR_OK;
+ }
+ 
+@@ -1082,8 +1086,7 @@ C_OpenSession(CK_SLOT_ID slotID,
+ 
+     soft_token.open_sessions++;
+ 
+-    soft_token.state[i].session_handle =
+-        (CK_SESSION_HANDLE)(random() & 0xfffff);
++    soft_token.state[i].session_handle = soft_token.next_session_handle++;
+     *phSession = soft_token.state[i].session_handle;
+ 
+     return CKR_OK;
+@@ -1152,7 +1155,8 @@ C_Login(CK_SESSION_HANDLE hSession,
+     VERIFY_SESSION_HANDLE(hSession, NULL);
+ 
+     if (pPin != NULL_PTR) {
+-        asprintf(&pin, "%.*s", (int)ulPinLen, pPin);
++        if (asprintf(&pin, "%.*s", (int)ulPinLen, pPin) < 0)
++            return CKR_DEVICE_MEMORY;
+         st_logf("type: %d password: %s\n", (int)userType, pin);
+     }
+ 
diff --git a/SOURCES/Fix-KCM-client-time-offset-propagation.patch b/SOURCES/Fix-KCM-client-time-offset-propagation.patch
new file mode 100644
index 0000000..eedc4c0
--- /dev/null
+++ b/SOURCES/Fix-KCM-client-time-offset-propagation.patch
@@ -0,0 +1,33 @@
+From 7e4576cc62a16fa77030c42dcc43c61cdfa5b4e6 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Wed, 14 Aug 2019 13:52:27 -0400
+Subject: [PATCH] Fix KCM client time offset propagation
+
+An inverted status check in get_kdc_offset() would cause querying the
+offset time from the ccache to always fail (silently) on KCM.  Fix the
+status check so that KCM can properly handle desync.
+
+ticket: 8826 (new)
+tags: pullup
+target_version: 1.17-next
+target_verison: 1.16-next
+
+(cherry picked from commit 323abb6d1ebe5469d6c2167c29aa5d696d099b90)
+(cherry picked from commit 7e81b8077cf2cf186dadb96b064573f7c221fbf3)
+---
+ src/lib/krb5/ccache/cc_kcm.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/lib/krb5/ccache/cc_kcm.c b/src/lib/krb5/ccache/cc_kcm.c
+index 092ab7daf..fe93ca3dc 100644
+--- a/src/lib/krb5/ccache/cc_kcm.c
++++ b/src/lib/krb5/ccache/cc_kcm.c
+@@ -583,7 +583,7 @@ get_kdc_offset(krb5_context context, krb5_ccache cache)
+     if (cache_call(context, cache, &req, FALSE) != 0)
+         goto cleanup;
+     time_offset = k5_input_get_uint32_be(&req.reply);
+-    if (!req.reply.status)
++    if (req.reply.status)
+         goto cleanup;
+     context->os_context.time_offset = time_offset;
+     context->os_context.usec_offset = 0;
diff --git a/SOURCES/Fix-argument-order-on-strlcpy-in-enctype_name.patch b/SOURCES/Fix-argument-order-on-strlcpy-in-enctype_name.patch
new file mode 100644
index 0000000..99648bd
--- /dev/null
+++ b/SOURCES/Fix-argument-order-on-strlcpy-in-enctype_name.patch
@@ -0,0 +1,22 @@
+From 9a38af6aa136fdc92d5e0f1591c1647aec498f5a Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Tue, 17 Sep 2019 18:29:15 -0400
+Subject: [PATCH] Fix argument order on strlcpy() in enctype_name()
+
+---
+ src/kdc/kdc_util.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
+index 96c88edc1..6d1861a3b 100644
+--- a/src/kdc/kdc_util.c
++++ b/src/kdc/kdc_util.c
+@@ -1081,7 +1081,7 @@ enctype_name(krb5_enctype ktype, char *buf, size_t buflen)
+     else
+         return krb5_enctype_to_name(ktype, FALSE, buf, buflen);
+ 
+-    if (strlcpy(name, buf, buflen) >= buflen)
++    if (strlcpy(buf, name, buflen) >= buflen)
+         return ENOMEM;
+     return 0;
+ }
diff --git a/SOURCES/Fix-memory-leak-in-none-replay-cache-type.patch b/SOURCES/Fix-memory-leak-in-none-replay-cache-type.patch
index 22e5d59..166ef05 100644
--- a/SOURCES/Fix-memory-leak-in-none-replay-cache-type.patch
+++ b/SOURCES/Fix-memory-leak-in-none-replay-cache-type.patch
@@ -1,4 +1,4 @@
-From a904a5b85e8425823016b821153b37396edc2306 Mon Sep 17 00:00:00 2001
+From 74a3b8448949130d91417b261c3e1c316ffa8796 Mon Sep 17 00:00:00 2001
 From: Corene Casper <C.Casper@Dell.com>
 Date: Sat, 16 Feb 2019 00:49:26 -0500
 Subject: [PATCH] Fix memory leak in 'none' replay cache type
diff --git a/SOURCES/Fix-memory-leaks-in-soft-pkcs11-code.patch b/SOURCES/Fix-memory-leaks-in-soft-pkcs11-code.patch
new file mode 100644
index 0000000..01e41fc
--- /dev/null
+++ b/SOURCES/Fix-memory-leaks-in-soft-pkcs11-code.patch
@@ -0,0 +1,123 @@
+From 604f5dcbb018fca8ea27e00314ed615133b861e1 Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson@mit.edu>
+Date: Mon, 5 Aug 2019 01:53:51 -0400
+Subject: [PATCH] Fix memory leaks in soft-pkcs11 code
+
+Fix leaks detected by asan in t_pkinit.py.  Add a helper to free a
+struct st_object and free objects in C_Finalize().  Duplicate the X509
+cert in add_certificate() instead of creating aliases so it can be
+properly freed.  Start the session handle counter at 1 so that
+C_Finalize() won't confuse the first session handle with
+CK_INVALID_HANDLE (defined to 0 in pkinit.h) and will properly clean
+the session object.
+
+(cherry picked from commit 15bcaf8bcb4af25ff89820ad3bf23ad5a324e863)
+(cherry picked from commit 5cc80472e7a8b0fb3002f229ffb104dccf8bd120)
+---
+ src/tests/softpkcs11/main.c | 44 +++++++++++++++++++++++++++++++++----
+ 1 file changed, 40 insertions(+), 4 deletions(-)
+
+diff --git a/src/tests/softpkcs11/main.c b/src/tests/softpkcs11/main.c
+index 2d1448ca2..a4c3ae78e 100644
+--- a/src/tests/softpkcs11/main.c
++++ b/src/tests/softpkcs11/main.c
+@@ -109,7 +109,7 @@ struct st_object {
+         X509 *cert;
+         EVP_PKEY *public_key;
+         struct {
+-            const char *file;
++            char *file;
+             EVP_PKEY *key;
+             X509 *cert;
+         } private_key;
+@@ -343,6 +343,26 @@ print_attributes(const CK_ATTRIBUTE *attributes,
+     }
+ }
+ 
++static void
++free_st_object(struct st_object *o)
++{
++    int i;
++
++    for (i = 0; i < o->num_attributes; i++)
++        free(o->attrs[i].attribute.pValue);
++    free(o->attrs);
++    if (o->type == STO_T_CERTIFICATE) {
++        X509_free(o->u.cert);
++    } else if (o->type == STO_T_PRIVATE_KEY) {
++        free(o->u.private_key.file);
++        EVP_PKEY_free(o->u.private_key.key);
++        X509_free(o->u.private_key.cert);
++    } else if (o->type == STO_T_PUBLIC_KEY) {
++        EVP_PKEY_free(o->u.public_key);
++    }
++    free(o);
++}
++
+ static struct st_object *
+ add_st_object(void)
+ {
+@@ -518,7 +538,11 @@ add_certificate(char *label,
+         goto out;
+     }
+     o->type = STO_T_CERTIFICATE;
+-    o->u.cert = cert;
++    o->u.cert = X509_dup(cert);
++    if (o->u.cert == NULL) {
++        ret = CKR_DEVICE_MEMORY;
++        goto out;
++    }
+     public_key = X509_get_pubkey(o->u.cert);
+ 
+     switch (EVP_PKEY_base_id(public_key)) {
+@@ -602,7 +626,11 @@ add_certificate(char *label,
+         o->u.private_key.file = strdup(private_key_file);
+         o->u.private_key.key = NULL;
+ 
+-        o->u.private_key.cert = cert;
++        o->u.private_key.cert = X509_dup(cert);
++        if (o->u.private_key.cert == NULL) {
++            ret = CKR_DEVICE_MEMORY;
++            goto out;
++        }
+ 
+         c = CKO_PRIVATE_KEY;
+         add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c));
+@@ -676,6 +704,7 @@ add_certificate(char *label,
+     free(serial_data);
+     free(issuer_data);
+     free(subject_data);
++    X509_free(cert);
+ 
+     return ret;
+ }
+@@ -872,7 +901,7 @@ C_Initialize(CK_VOID_PTR a)
+         st_logf("\tFlags\t%04x\n", (unsigned int)args->flags);
+     }
+ 
+-    soft_token.next_session_handle = 0;
++    soft_token.next_session_handle = 1;
+ 
+     fn = get_rcfilename();
+     if (fn == NULL)
+@@ -886,6 +915,7 @@ CK_RV
+ C_Finalize(CK_VOID_PTR args)
+ {
+     size_t i;
++    int j;
+ 
+     st_logf("Finalize\n");
+ 
+@@ -897,6 +927,12 @@ C_Finalize(CK_VOID_PTR args)
+         }
+     }
+ 
++    for (j = 0; j < soft_token.object.num_objs; j++)
++        free_st_object(soft_token.object.objs[j]);
++    free(soft_token.object.objs);
++    soft_token.object.objs = NULL;
++    soft_token.object.num_objs = 0;
++
+     return CKR_OK;
+ }
+ 
diff --git a/SOURCES/In-FIPS-mode-add-plaintext-fallback-for-RC4-usages-a.patch b/SOURCES/In-FIPS-mode-add-plaintext-fallback-for-RC4-usages-a.patch
deleted file mode 100644
index b2a4f87..0000000
--- a/SOURCES/In-FIPS-mode-add-plaintext-fallback-for-RC4-usages-a.patch
+++ /dev/null
@@ -1,354 +0,0 @@
-From 088fd5a56e030739a31a43aee7335bc661a92b1c Mon Sep 17 00:00:00 2001
-From: Robbie Harwood <rharwood@redhat.com>
-Date: Tue, 31 Jul 2018 13:47:26 -0400
-Subject: [PATCH] In FIPS mode, add plaintext fallback for RC4 usages and taint
-
-(cherry picked from commit a327e3bf5b992ac829c7b2d3317fb7d93b1c88ef)
-(cherry picked from commit 2bd85da058d2d73eb2818a8e64656fec9b21b3c3)
----
- src/lib/krad/attr.c      | 45 +++++++++++++++++++++++++++++-----------
- src/lib/krad/attrset.c   |  5 +++--
- src/lib/krad/internal.h  | 13 ++++++++++--
- src/lib/krad/packet.c    | 22 +++++++++++---------
- src/lib/krad/remote.c    | 10 +++++++--
- src/lib/krad/t_attr.c    |  3 ++-
- src/lib/krad/t_attrset.c |  4 +++-
- 7 files changed, 72 insertions(+), 30 deletions(-)
-
-diff --git a/src/lib/krad/attr.c b/src/lib/krad/attr.c
-index 9c13d9d75..275327e67 100644
---- a/src/lib/krad/attr.c
-+++ b/src/lib/krad/attr.c
-@@ -30,6 +30,7 @@
- #include <k5-int.h>
- #include "internal.h"
- 
-+#include <openssl/crypto.h>
- #include <string.h>
- 
- /* RFC 2865 */
-@@ -38,7 +39,8 @@
- typedef krb5_error_code
- (*attribute_transform_fn)(krb5_context ctx, const char *secret,
-                           const unsigned char *auth, const krb5_data *in,
--                          unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
-+                          unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
-+                          krb5_boolean *is_fips);
- 
- typedef struct {
-     const char *name;
-@@ -51,12 +53,14 @@ typedef struct {
- static krb5_error_code
- user_password_encode(krb5_context ctx, const char *secret,
-                      const unsigned char *auth, const krb5_data *in,
--                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
-+                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
-+                     krb5_boolean *is_fips);
- 
- static krb5_error_code
- user_password_decode(krb5_context ctx, const char *secret,
-                      const unsigned char *auth, const krb5_data *in,
--                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
-+                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
-+                     krb5_boolean *ignored);
- 
- static const attribute_record attributes[UCHAR_MAX] = {
-     {"User-Name", 1, MAX_ATTRSIZE, NULL, NULL},
-@@ -128,7 +132,8 @@ static const attribute_record attributes[UCHAR_MAX] = {
- static krb5_error_code
- user_password_encode(krb5_context ctx, const char *secret,
-                      const unsigned char *auth, const krb5_data *in,
--                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
-+                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
-+                     krb5_boolean *is_fips)
- {
-     const unsigned char *indx;
-     krb5_error_code retval;
-@@ -154,8 +159,14 @@ user_password_encode(krb5_context ctx, const char *secret,
-     for (blck = 0, indx = auth; blck * BLOCKSIZE < len; blck++) {
-         memcpy(tmp.data + seclen, indx, BLOCKSIZE);
- 
--        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
--                                      &sum);
-+        if (FIPS_mode()) {
-+            /* Skip encryption here.  Taint so that we won't pass it out of
-+             * the machine by accident. */
-+            *is_fips = TRUE;
-+            sum.contents = calloc(1, BLOCKSIZE);
-+        } else
-+            retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
-+                                          &sum);
-         if (retval != 0) {
-             zap(tmp.data, tmp.length);
-             zap(outbuf, len);
-@@ -180,7 +191,8 @@ user_password_encode(krb5_context ctx, const char *secret,
- static krb5_error_code
- user_password_decode(krb5_context ctx, const char *secret,
-                      const unsigned char *auth, const krb5_data *in,
--                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
-+                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
-+                     krb5_boolean *is_fips)
- {
-     const unsigned char *indx;
-     krb5_error_code retval;
-@@ -204,8 +216,14 @@ user_password_decode(krb5_context ctx, const char *secret,
-     for (blck = 0, indx = auth; blck * BLOCKSIZE < in->length; blck++) {
-         memcpy(tmp.data + seclen, indx, BLOCKSIZE);
- 
--        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
--                                      &tmp, &sum);
-+        if (FIPS_mode()) {
-+            /* Skip encryption here.  Taint so that we won't pass it out of
-+             * the machine by accident. */
-+            *is_fips = TRUE;
-+            sum.contents = calloc(1, BLOCKSIZE);
-+        } else
-+            retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
-+                                          &tmp, &sum);
-         if (retval != 0) {
-             zap(tmp.data, tmp.length);
-             zap(outbuf, in->length);
-@@ -248,7 +266,7 @@ krb5_error_code
- kr_attr_encode(krb5_context ctx, const char *secret,
-                const unsigned char *auth, krad_attr type,
-                const krb5_data *in, unsigned char outbuf[MAX_ATTRSIZE],
--               size_t *outlen)
-+               size_t *outlen, krb5_boolean *is_fips)
- {
-     krb5_error_code retval;
- 
-@@ -265,7 +283,8 @@ kr_attr_encode(krb5_context ctx, const char *secret,
-         return 0;
-     }
- 
--    return attributes[type - 1].encode(ctx, secret, auth, in, outbuf, outlen);
-+    return attributes[type - 1].encode(ctx, secret, auth, in, outbuf, outlen,
-+                                       is_fips);
- }
- 
- krb5_error_code
-@@ -274,6 +293,7 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
-                unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
- {
-     krb5_error_code retval;
-+    krb5_boolean ignored;
- 
-     retval = kr_attr_valid(type, in);
-     if (retval != 0)
-@@ -288,7 +308,8 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
-         return 0;
-     }
- 
--    return attributes[type - 1].decode(ctx, secret, auth, in, outbuf, outlen);
-+    return attributes[type - 1].decode(ctx, secret, auth, in, outbuf, outlen,
-+                                       &ignored);
- }
- 
- krad_attr
-diff --git a/src/lib/krad/attrset.c b/src/lib/krad/attrset.c
-index 03c613716..d89982a13 100644
---- a/src/lib/krad/attrset.c
-+++ b/src/lib/krad/attrset.c
-@@ -167,7 +167,8 @@ krad_attrset_copy(const krad_attrset *set, krad_attrset **copy)
- krb5_error_code
- kr_attrset_encode(const krad_attrset *set, const char *secret,
-                   const unsigned char *auth,
--                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen)
-+                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen,
-+                  krb5_boolean *is_fips)
- {
-     unsigned char buffer[MAX_ATTRSIZE];
-     krb5_error_code retval;
-@@ -181,7 +182,7 @@ kr_attrset_encode(const krad_attrset *set, const char *secret,
- 
-     K5_TAILQ_FOREACH(a, &set->list, list) {
-         retval = kr_attr_encode(set->ctx, secret, auth, a->type, &a->attr,
--                                buffer, &attrlen);
-+                                buffer, &attrlen, is_fips);
-         if (retval != 0)
-             return retval;
- 
-diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h
-index 996a89372..a53ce31ce 100644
---- a/src/lib/krad/internal.h
-+++ b/src/lib/krad/internal.h
-@@ -49,6 +49,13 @@
- 
- typedef struct krad_remote_st krad_remote;
- 
-+struct krad_packet_st {
-+    char buffer[KRAD_PACKET_SIZE_MAX];
-+    krad_attrset *attrset;
-+    krb5_data pkt;
-+    krb5_boolean is_fips;
-+};
-+
- /* Validate constraints of an attribute. */
- krb5_error_code
- kr_attr_valid(krad_attr type, const krb5_data *data);
-@@ -57,7 +64,8 @@ kr_attr_valid(krad_attr type, const krb5_data *data);
- krb5_error_code
- kr_attr_encode(krb5_context ctx, const char *secret, const unsigned char *auth,
-                krad_attr type, const krb5_data *in,
--               unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
-+               unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
-+               krb5_boolean *is_fips);
- 
- /* Decode an attribute. */
- krb5_error_code
-@@ -69,7 +77,8 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
- krb5_error_code
- kr_attrset_encode(const krad_attrset *set, const char *secret,
-                   const unsigned char *auth,
--                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen);
-+                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen,
-+                  krb5_boolean *is_fips);
- 
- /* Decode attributes from a buffer. */
- krb5_error_code
-diff --git a/src/lib/krad/packet.c b/src/lib/krad/packet.c
-index c597174b6..794ac84c4 100644
---- a/src/lib/krad/packet.c
-+++ b/src/lib/krad/packet.c
-@@ -32,6 +32,7 @@
- #include <string.h>
- 
- #include <arpa/inet.h>
-+#include <openssl/crypto.h>
- 
- typedef unsigned char uchar;
- 
-@@ -53,12 +54,6 @@ typedef unsigned char uchar;
- #define pkt_auth(p) ((uchar *)offset(&(p)->pkt, OFFSET_AUTH))
- #define pkt_attr(p) ((unsigned char *)offset(&(p)->pkt, OFFSET_ATTR))
- 
--struct krad_packet_st {
--    char buffer[KRAD_PACKET_SIZE_MAX];
--    krad_attrset *attrset;
--    krb5_data pkt;
--};
--
- typedef struct {
-     uchar x[(UCHAR_MAX + 1) / 8];
- } idmap;
-@@ -187,8 +182,13 @@ auth_generate_response(krb5_context ctx, const char *secret,
-     memcpy(data.data + response->pkt.length, secret, strlen(secret));
- 
-     /* Hash it. */
--    retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
--                                  &hash);
-+    if (FIPS_mode()) {
-+        /* This checksum does very little security-wise anyway, so don't
-+         * taint. */
-+        hash.contents = calloc(1, AUTH_FIELD_SIZE);
-+    } else
-+        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
-+                                      &hash);
-     free(data.data);
-     if (retval != 0)
-         return retval;
-@@ -276,7 +276,7 @@ krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
- 
-     /* Encode the attributes. */
-     retval = kr_attrset_encode(set, secret, pkt_auth(pkt), pkt_attr(pkt),
--                               &attrset_len);
-+                               &attrset_len, &pkt->is_fips);
-     if (retval != 0)
-         goto error;
- 
-@@ -314,7 +314,7 @@ krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
- 
-     /* Encode the attributes. */
-     retval = kr_attrset_encode(set, secret, pkt_auth(request), pkt_attr(pkt),
--                               &attrset_len);
-+                               &attrset_len, &pkt->is_fips);
-     if (retval != 0)
-         goto error;
- 
-@@ -451,6 +451,8 @@ krad_packet_decode_response(krb5_context ctx, const char *secret,
- const krb5_data *
- krad_packet_encode(const krad_packet *pkt)
- {
-+    if (pkt->is_fips)
-+        return NULL;
-     return &pkt->pkt;
- }
- 
-diff --git a/src/lib/krad/remote.c b/src/lib/krad/remote.c
-index 437f7e91a..0f90443ce 100644
---- a/src/lib/krad/remote.c
-+++ b/src/lib/krad/remote.c
-@@ -263,7 +263,7 @@ on_io_write(krad_remote *rr)
-     request *r;
- 
-     K5_TAILQ_FOREACH(r, &rr->list, list) {
--        tmp = krad_packet_encode(r->request);
-+        tmp = &r->request->pkt;
- 
-         /* If the packet has already been sent, do nothing. */
-         if (r->sent == tmp->length)
-@@ -359,7 +359,7 @@ on_io_read(krad_remote *rr)
-     if (req != NULL) {
-         K5_TAILQ_FOREACH(r, &rr->list, list) {
-             if (r->request == req &&
--                r->sent == krad_packet_encode(req)->length) {
-+                r->sent == req->pkt.length) {
-                 request_finish(r, 0, rsp);
-                 break;
-             }
-@@ -455,6 +455,12 @@ kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
-                                      (krad_packet_iter_cb)iterator, &r, &tmp);
-     if (retval != 0)
-         goto error;
-+    else if (tmp->is_fips && rr->info->ai_family != AF_LOCAL &&
-+        rr->info->ai_family != AF_UNIX) {
-+        /* This would expose cleartext passwords, so abort. */
-+        retval = ESOCKTNOSUPPORT;
-+        goto error;
-+    }
- 
-     K5_TAILQ_FOREACH(r, &rr->list, list) {
-         if (r->request == tmp) {
-diff --git a/src/lib/krad/t_attr.c b/src/lib/krad/t_attr.c
-index eb2a780c8..4d285ad9d 100644
---- a/src/lib/krad/t_attr.c
-+++ b/src/lib/krad/t_attr.c
-@@ -50,6 +50,7 @@ main()
-     const char *tmp;
-     krb5_data in;
-     size_t len;
-+    krb5_boolean is_fips = FALSE;
- 
-     noerror(krb5_init_context(&ctx));
- 
-@@ -73,7 +74,7 @@ main()
-     in = string2data((char *)decoded);
-     retval = kr_attr_encode(ctx, secret, auth,
-                             krad_attr_name2num("User-Password"),
--                            &in, outbuf, &len);
-+                            &in, outbuf, &len, &is_fips);
-     insist(retval == 0);
-     insist(len == sizeof(encoded));
-     insist(memcmp(outbuf, encoded, len) == 0);
-diff --git a/src/lib/krad/t_attrset.c b/src/lib/krad/t_attrset.c
-index 7928335ca..0f9576253 100644
---- a/src/lib/krad/t_attrset.c
-+++ b/src/lib/krad/t_attrset.c
-@@ -49,6 +49,7 @@ main()
-     krb5_context ctx;
-     size_t len = 0, encode_len;
-     krb5_data tmp;
-+    krb5_boolean is_fips = FALSE;
- 
-     noerror(krb5_init_context(&ctx));
-     noerror(krad_attrset_new(ctx, &set));
-@@ -62,7 +63,8 @@ main()
-     noerror(krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp));
- 
-     /* Encode attrset. */
--    noerror(kr_attrset_encode(set, "foo", auth, buffer, &encode_len));
-+    noerror(kr_attrset_encode(set, "foo", auth, buffer, &encode_len,
-+                              &is_fips));
-     krad_attrset_free(set);
- 
-     /* Manually encode User-Name. */
diff --git a/SOURCES/In-kpropd-debug-log-proper-ticket-enctype-names.patch b/SOURCES/In-kpropd-debug-log-proper-ticket-enctype-names.patch
new file mode 100644
index 0000000..8a20e3d
--- /dev/null
+++ b/SOURCES/In-kpropd-debug-log-proper-ticket-enctype-names.patch
@@ -0,0 +1,28 @@
+From 30f112f8dc1c5241da5ba301cb45a06bb5bb4c01 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Tue, 15 Jan 2019 13:41:16 -0500
+Subject: [PATCH] In kpropd, debug-log proper ticket enctype names
+
+This change replaces the last call of krb5_enctype_to_string() in our
+sources with krb5_enctype_to_name(), ensuring that we log consistently
+to users using readily discoverable strings.
+
+(cherry picked from commit 30e12a2ecdf7e2a034a91626a03b5c9909e4c68d)
+---
+ src/kprop/kpropd.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/kprop/kpropd.c b/src/kprop/kpropd.c
+index 4cc035dc6..0c7bffa24 100644
+--- a/src/kprop/kpropd.c
++++ b/src/kprop/kpropd.c
+@@ -1279,7 +1279,8 @@ kerberos_authenticate(krb5_context context, int fd, krb5_principal *clientp,
+             exit(1);
+         }
+ 
+-        retval = krb5_enctype_to_string(*etype, etypebuf, sizeof(etypebuf));
++        retval = krb5_enctype_to_name(*etype, FALSE, etypebuf,
++                                      sizeof(etypebuf));
+         if (retval) {
+             com_err(progname, retval, _("while unparsing ticket etype"));
+             exit(1);
diff --git a/SOURCES/In-rd_req_dec-always-log-non-permitted-enctypes.patch b/SOURCES/In-rd_req_dec-always-log-non-permitted-enctypes.patch
new file mode 100644
index 0000000..6ca3da0
--- /dev/null
+++ b/SOURCES/In-rd_req_dec-always-log-non-permitted-enctypes.patch
@@ -0,0 +1,54 @@
+From 2b4521f3ba3dad064e3f64bfd56b88d5cb5d0955 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Mon, 14 Jan 2019 17:14:42 -0500
+Subject: [PATCH] In rd_req_dec, always log non-permitted enctypes
+
+The buffer specified in negotiate_etype() is too small for use with
+the AES enctypes when used with krb5_enctype_to_string(), so switch to
+using krb5_enctype_to_name().
+
+(cherry picked from commit bf75ebf583a51bf00005a96d17924818d19377be)
+---
+ src/lib/krb5/krb/rd_req_dec.c  | 5 ++---
+ src/tests/gssapi/t_enctypes.py | 5 +++--
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/lib/krb5/krb/rd_req_dec.c b/src/lib/krb5/krb/rd_req_dec.c
+index 4cd429a11..e75192fee 100644
+--- a/src/lib/krb5/krb/rd_req_dec.c
++++ b/src/lib/krb5/krb/rd_req_dec.c
+@@ -864,9 +864,8 @@ negotiate_etype(krb5_context context,
+         if (permitted == FALSE) {
+             char enctype_name[30];
+ 
+-            if (krb5_enctype_to_string(desired_etypes[i],
+-                                       enctype_name,
+-                                       sizeof(enctype_name)) == 0)
++            if (krb5_enctype_to_name(desired_etypes[i], FALSE, enctype_name,
++                                     sizeof(enctype_name)) == 0)
+                 k5_setmsg(context, KRB5_NOPERM_ETYPE,
+                           _("Encryption type %s not permitted"), enctype_name);
+             return KRB5_NOPERM_ETYPE;
+diff --git a/src/tests/gssapi/t_enctypes.py b/src/tests/gssapi/t_enctypes.py
+index ee43ff028..5d9f80e04 100755
+--- a/src/tests/gssapi/t_enctypes.py
++++ b/src/tests/gssapi/t_enctypes.py
+@@ -85,7 +85,8 @@ test('both aes128', 'aes128-cts', 'aes128-cts',
+ # If only the acceptor constrains the permitted session enctypes to
+ # aes128, subkey negotiation fails because the acceptor considers the
+ # aes256 session key to be non-permitted.
+-test_err('acc aes128', None, 'aes128-cts', 'Encryption type not permitted')
++test_err('acc aes128', None, 'aes128-cts',
++         'Encryption type aes256-cts-hmac-sha1-96 not permitted')
+ 
+ # If the initiator constrains the permitted session enctypes to des3,
+ # no acceptor subkey will be generated because we can't upgrade to a
+@@ -128,7 +129,7 @@ test('upgrade init des3+rc4', 'des3 rc4', None,
+ # is only for the sake of the kernel, since we could upgrade to an
+ # aes128 subkey, but it's the current semantics.)
+ test_err('upgrade acc aes128', None, 'aes128-cts',
+-         'Encryption type ArcFour with HMAC/md5 not permitted')
++         'Encryption type arcfour-hmac not permitted')
+ 
+ # If the acceptor permits rc4 but prefers aes128, it will negotiate an
+ # upgrade to aes128.
diff --git a/SOURCES/Make-etype-names-in-KDC-logs-human-readable.patch b/SOURCES/Make-etype-names-in-KDC-logs-human-readable.patch
new file mode 100644
index 0000000..00bb2db
--- /dev/null
+++ b/SOURCES/Make-etype-names-in-KDC-logs-human-readable.patch
@@ -0,0 +1,293 @@
+From f815140182976e882445a38ee5a0a77f56da7c8a Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Tue, 8 Jan 2019 17:42:35 -0500
+Subject: [PATCH] Make etype names in KDC logs human-readable
+
+Introduce enctype_name() as a wrapper over krb5_enctype_to_name for
+converting between registered constants and names.  Adjust signatures
+and rewrite ktypes2str() and rep_etypes2str() to operate on dynamic
+buffers.
+
+ticket: 8772 (new)
+(cherry picked from commit a649279727490687d54becad91fde8cf7429d951)
+---
+ src/kdc/kdc_log.c  |  42 +++++++--------
+ src/kdc/kdc_util.c | 125 +++++++++++++++++++++++----------------------
+ src/kdc/kdc_util.h |   6 +--
+ 3 files changed, 87 insertions(+), 86 deletions(-)
+
+diff --git a/src/kdc/kdc_log.c b/src/kdc/kdc_log.c
+index 4eec50373..b160ba21a 100644
+--- a/src/kdc/kdc_log.c
++++ b/src/kdc/kdc_log.c
+@@ -65,7 +65,7 @@ log_as_req(krb5_context context,
+ {
+     const char *fromstring = 0;
+     char fromstringbuf[70];
+-    char ktypestr[128];
++    char *ktypestr = NULL;
+     const char *cname2 = cname ? cname : "<unknown client>";
+     const char *sname2 = sname ? sname : "<unknown server>";
+ 
+@@ -74,26 +74,29 @@ log_as_req(krb5_context context,
+                            fromstringbuf, sizeof(fromstringbuf));
+     if (!fromstring)
+         fromstring = "<unknown>";
+-    ktypes2str(ktypestr, sizeof(ktypestr),
+-               request->nktypes, request->ktype);
++
++    ktypestr = ktypes2str(request->ktype, request->nktypes);
+ 
+     if (status == NULL) {
+         /* success */
+-        char rep_etypestr[128];
+-        rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), reply);
++        char *rep_etypestr = rep_etypes2str(reply);
+         krb5_klog_syslog(LOG_INFO, _("AS_REQ (%s) %s: ISSUE: authtime %u, %s, "
+                                      "%s for %s"),
+-                         ktypestr, fromstring, (unsigned int)authtime,
+-                         rep_etypestr, cname2, sname2);
++                         ktypestr ? ktypestr : "", fromstring,
++                         (unsigned int)authtime,
++                         rep_etypestr ? rep_etypestr : "", cname2, sname2);
++        free(rep_etypestr);
+     } else {
+         /* fail */
+         krb5_klog_syslog(LOG_INFO, _("AS_REQ (%s) %s: %s: %s for %s%s%s"),
+-                         ktypestr, fromstring, status,
+-                         cname2, sname2, emsg ? ", " : "", emsg ? emsg : "");
++                         ktypestr ? ktypestr : "", fromstring, status, cname2,
++                         sname2, emsg ? ", " : "", emsg ? emsg : "");
+     }
+     krb5_db_audit_as_req(context, request,
+                          local_addr->address, remote_addr->address,
+                          client, server, authtime, errcode);
++
++    free(ktypestr);
+ }
+ 
+ /*
+@@ -122,10 +125,9 @@ log_tgs_req(krb5_context ctx, const krb5_fulladdr *from,
+             unsigned int c_flags,
+             const char *status, krb5_error_code errcode, const char *emsg)
+ {
+-    char ktypestr[128];
++    char *ktypestr = NULL, *rep_etypestr = NULL;
+     const char *fromstring = 0;
+     char fromstringbuf[70];
+-    char rep_etypestr[128];
+     char *cname = NULL, *sname = NULL, *altcname = NULL;
+     char *logcname = NULL, *logsname = NULL, *logaltcname = NULL;
+ 
+@@ -134,11 +136,6 @@ log_tgs_req(krb5_context ctx, const krb5_fulladdr *from,
+                            fromstringbuf, sizeof(fromstringbuf));
+     if (!fromstring)
+         fromstring = "<unknown>";
+-    ktypes2str(ktypestr, sizeof(ktypestr), request->nktypes, request->ktype);
+-    if (!errcode)
+-        rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), reply);
+-    else
+-        rep_etypestr[0] = 0;
+ 
+     unparse_and_limit(ctx, cprinc, &cname);
+     logcname = (cname != NULL) ? cname : "<unknown client>";
+@@ -151,10 +148,14 @@ log_tgs_req(krb5_context ctx, const krb5_fulladdr *from,
+        name (useful), and doesn't log ktypestr (probably not
+        important).  */
+     if (errcode != KRB5KDC_ERR_SERVER_NOMATCH) {
++        ktypestr = ktypes2str(request->ktype, request->nktypes);
++        rep_etypestr = rep_etypes2str(reply);
+         krb5_klog_syslog(LOG_INFO, _("TGS_REQ (%s) %s: %s: authtime %u, %s%s "
+                                      "%s for %s%s%s"),
+-                         ktypestr, fromstring, status, (unsigned int)authtime,
+-                         rep_etypestr, !errcode ? "," : "", logcname, logsname,
++                         ktypestr ? ktypestr : "", fromstring, status,
++                         (unsigned int)authtime,
++                         rep_etypestr ? rep_etypestr : "",
++                         !errcode ? "," : "", logcname, logsname,
+                          errcode ? ", " : "", errcode ? emsg : "");
+         if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION))
+             krb5_klog_syslog(LOG_INFO,
+@@ -171,9 +172,8 @@ log_tgs_req(krb5_context ctx, const krb5_fulladdr *from,
+                          fromstring, status, (unsigned int)authtime,
+                          logcname, logsname, logaltcname);
+ 
+-    /* OpenSolaris: audit_krb5kdc_tgs_req(...)  or
+-       audit_krb5kdc_tgs_req_2ndtktmm(...) */
+-
++    free(rep_etypestr);
++    free(ktypestr);
+     krb5_free_unparsed_name(ctx, cname);
+     krb5_free_unparsed_name(ctx, sname);
+     krb5_free_unparsed_name(ctx, altcname);
+diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
+index 0155c28c6..f5c581c82 100644
+--- a/src/kdc/kdc_util.c
++++ b/src/kdc/kdc_util.c
+@@ -1043,84 +1043,87 @@ void limit_string(char *name)
+     return;
+ }
+ 
+-/*
+- * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301.
+- */
+-#define L10_2(x) ((int)(((x * 301) + 999) / 1000))
++/* Wrapper of krb5_enctype_to_name() to include the PKINIT types. */
++static krb5_error_code
++enctype_name(krb5_enctype ktype, char *buf, size_t buflen)
++{
++    char *name;
++
++    if (buflen == 0)
++        return EINVAL;
++    *buf = '\0'; /* ensure these are always valid C-strings */
++
++    /* rfc4556 recommends that clients wishing to indicate support for these
++     * pkinit algorithms include them in the etype field of the AS-REQ. */
++    if (ktype == ENCTYPE_DSA_SHA1_CMS)
++        name = "id-dsa-with-sha1-CmsOID";
++    else if (ktype == ENCTYPE_MD5_RSA_CMS)
++        name = "md5WithRSAEncryption-CmsOID";
++    else if (ktype == ENCTYPE_SHA1_RSA_CMS)
++        name = "sha-1WithRSAEncryption-CmsOID";
++    else if (ktype == ENCTYPE_RC2_CBC_ENV)
++        name = "rc2-cbc-EnvOID";
++    else if (ktype == ENCTYPE_RSA_ENV)
++        name = "rsaEncryption-EnvOID";
++    else if (ktype == ENCTYPE_RSA_ES_OAEP_ENV)
++        name = "id-RSAES-OAEP-EnvOID";
++    else if (ktype == ENCTYPE_DES3_CBC_ENV)
++        name = "des-ede3-cbc-EnvOID";
++    else
++        return krb5_enctype_to_name(ktype, FALSE, buf, buflen);
+ 
+-/*
+- * Max length of sprintf("%ld") for an int of type T; includes leading
+- * minus sign and terminating NUL.
+- */
+-#define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2)
++    if (strlcpy(name, buf, buflen) >= buflen)
++        return ENOMEM;
++    return 0;
++}
+ 
+-void
+-ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype)
++char *
++ktypes2str(krb5_enctype *ktype, int nktypes)
+ {
++    struct k5buf buf;
+     int i;
+-    char stmp[D_LEN(krb5_enctype) + 1];
+-    char *p;
++    char name[64];
+ 
+-    if (nktypes < 0
+-        || len < (sizeof(" etypes {...}") + D_LEN(int))) {
+-        *s = '\0';
+-        return;
+-    }
++    if (nktypes < 0)
++        return NULL;
+ 
+-    snprintf(s, len, "%d etypes {", nktypes);
++    k5_buf_init_dynamic(&buf);
++    k5_buf_add_fmt(&buf, "%d etypes {", nktypes);
+     for (i = 0; i < nktypes; i++) {
+-        snprintf(stmp, sizeof(stmp), "%s%ld", i ? " " : "", (long)ktype[i]);
+-        if (strlen(s) + strlen(stmp) + sizeof("}") > len)
+-            break;
+-        strlcat(s, stmp, len);
++        enctype_name(ktype[i], name, sizeof(name));
++        k5_buf_add_fmt(&buf, "%s%s(%ld)", i ? ", " : "", name, (long)ktype[i]);
+     }
+-    if (i < nktypes) {
+-        /*
+-         * We broke out of the loop. Try to truncate the list.
+-         */
+-        p = s + strlen(s);
+-        while (p - s + sizeof("...}") > len) {
+-            while (p > s && *p != ' ' && *p != '{')
+-                *p-- = '\0';
+-            if (p > s && *p == ' ') {
+-                *p-- = '\0';
+-                continue;
+-            }
+-        }
+-        strlcat(s, "...", len);
+-    }
+-    strlcat(s, "}", len);
+-    return;
++    k5_buf_add(&buf, "}");
++    return buf.data;
+ }
+ 
+-void
+-rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep)
++char *
++rep_etypes2str(krb5_kdc_rep *rep)
+ {
+-    char stmp[sizeof("ses=") + D_LEN(krb5_enctype)];
+-
+-    if (len < (3 * D_LEN(krb5_enctype)
+-               + sizeof("etypes {rep= tkt= ses=}"))) {
+-        *s = '\0';
+-        return;
+-    }
++    struct k5buf buf;
++    char name[64];
++    krb5_enctype etype;
+ 
+-    snprintf(s, len, "etypes {rep=%ld", (long)rep->enc_part.enctype);
++    k5_buf_init_dynamic(&buf);
++    k5_buf_add(&buf, "etypes {rep=");
++    enctype_name(rep->enc_part.enctype, name, sizeof(name));
++    k5_buf_add_fmt(&buf, "%s(%ld)", name, (long)rep->enc_part.enctype);
+ 
+     if (rep->ticket != NULL) {
+-        snprintf(stmp, sizeof(stmp),
+-                 " tkt=%ld", (long)rep->ticket->enc_part.enctype);
+-        strlcat(s, stmp, len);
++        etype = rep->ticket->enc_part.enctype;
++        enctype_name(etype, name, sizeof(name));
++        k5_buf_add_fmt(&buf, ", tkt=%s(%ld)", name, (long)etype);
+     }
+ 
+-    if (rep->ticket != NULL
+-        && rep->ticket->enc_part2 != NULL
+-        && rep->ticket->enc_part2->session != NULL) {
+-        snprintf(stmp, sizeof(stmp), " ses=%ld",
+-                 (long)rep->ticket->enc_part2->session->enctype);
+-        strlcat(s, stmp, len);
++    if (rep->ticket != NULL && rep->ticket->enc_part2 != NULL &&
++        rep->ticket->enc_part2->session != NULL) {
++        etype = rep->ticket->enc_part2->session->enctype;
++        enctype_name(etype, name, sizeof(name));
++        k5_buf_add_fmt(&buf, ", ses=%s(%ld)", name, (long)etype);
+     }
+-    strlcat(s, "}", len);
+-    return;
++
++    k5_buf_add(&buf, "}");
++    return buf.data;
+ }
+ 
+ static krb5_error_code
+diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h
+index 6ec645fc3..25077cbf5 100644
+--- a/src/kdc/kdc_util.h
++++ b/src/kdc/kdc_util.h
+@@ -110,11 +110,9 @@ select_session_keytype (kdc_realm_t *kdc_active_realm,
+ 
+ void limit_string (char *name);
+ 
+-void
+-ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype);
++char *ktypes2str(krb5_enctype *ktype, int nktypes);
+ 
+-void
+-rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep);
++char *rep_etypes2str(krb5_kdc_rep *rep);
+ 
+ /* authind.c */
+ krb5_boolean
diff --git a/SOURCES/Mark-deprecated-enctypes-when-used.patch b/SOURCES/Mark-deprecated-enctypes-when-used.patch
new file mode 100644
index 0000000..4ded20a
--- /dev/null
+++ b/SOURCES/Mark-deprecated-enctypes-when-used.patch
@@ -0,0 +1,250 @@
+From 6306a2a8697c94f968a02d66204f7d357aa0e7f6 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Thu, 10 Jan 2019 16:34:54 -0500
+Subject: [PATCH] Mark deprecated enctypes when used
+
+Preface ETYPE_DEPRECATED enctypes with "DEPRECATED:" in klist output,
+KDC logs, and kadmin interactions.  Also complain in krb5kdc when the
+stash file has a deprecated enctype or a deprecated enctype is
+requested with -k.
+
+ticket: 8773 (new)
+(cherry picked from commit 8d8e68283b599e680f9fe45eff8af397e827bd6c)
+---
+ src/clients/klist/klist.c      | 14 ++++++++++----
+ src/kadmin/cli/kadmin.c        |  6 +++++-
+ src/kdc/kdc_util.c             |  9 +++++++++
+ src/kdc/main.c                 | 19 +++++++++++++++++++
+ src/tests/gssapi/t_enctypes.py | 15 +++++++++------
+ src/tests/t_keyrollover.py     |  8 +++++---
+ src/tests/t_sesskeynego.py     |  4 ++--
+ 7 files changed, 59 insertions(+), 16 deletions(-)
+
+diff --git a/src/clients/klist/klist.c b/src/clients/klist/klist.c
+index 70adb54e8..8c307151a 100644
+--- a/src/clients/klist/klist.c
++++ b/src/clients/klist/klist.c
+@@ -571,11 +571,17 @@ static char *
+ etype_string(krb5_enctype enctype)
+ {
+     static char buf[100];
+-    krb5_error_code ret;
++    char *bp = buf;
++    size_t deplen, buflen = sizeof(buf);
+ 
+-    ret = krb5_enctype_to_name(enctype, FALSE, buf, sizeof(buf));
+-    if (ret)
+-        snprintf(buf, sizeof(buf), "etype %d", enctype);
++    if (krb5int_c_deprecated_enctype(enctype)) {
++        deplen = strlcpy(bp, "DEPRECATED:", buflen);
++        buflen -= deplen;
++        bp += deplen;
++    }
++
++    if (krb5_enctype_to_name(enctype, FALSE, bp, buflen))
++        snprintf(bp, buflen, "etype %d", enctype);
+     return buf;
+ }
+ 
+diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c
+index ed581ee79..cc74921bf 100644
+--- a/src/kadmin/cli/kadmin.c
++++ b/src/kadmin/cli/kadmin.c
+@@ -1451,12 +1451,16 @@ kadmin_getprinc(int argc, char *argv[])
+         for (i = 0; i < dprinc.n_key_data; i++) {
+             krb5_key_data *key_data = &dprinc.key_data[i];
+             char enctype[BUFSIZ], salttype[BUFSIZ];
++            char *deprecated = "";
+ 
+             if (krb5_enctype_to_name(key_data->key_data_type[0], FALSE,
+                                      enctype, sizeof(enctype)))
+                 snprintf(enctype, sizeof(enctype), _("<Encryption type 0x%x>"),
+                          key_data->key_data_type[0]);
+-            printf("Key: vno %d, %s", key_data->key_data_kvno, enctype);
++            if (krb5int_c_deprecated_enctype(key_data->key_data_type[0]))
++                deprecated = "DEPRECATED:";
++            printf("Key: vno %d, %s%s", key_data->key_data_kvno, deprecated,
++                   enctype);
+             if (key_data->key_data_ver > 1 &&
+                 key_data->key_data_type[1] != KRB5_KDB_SALTTYPE_NORMAL) {
+                 if (krb5_salttype_to_string(key_data->key_data_type[1],
+diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
+index f5c581c82..96c88edc1 100644
+--- a/src/kdc/kdc_util.c
++++ b/src/kdc/kdc_util.c
+@@ -1048,11 +1048,20 @@ static krb5_error_code
+ enctype_name(krb5_enctype ktype, char *buf, size_t buflen)
+ {
+     char *name;
++    size_t len;
+ 
+     if (buflen == 0)
+         return EINVAL;
+     *buf = '\0'; /* ensure these are always valid C-strings */
+ 
++    if (krb5int_c_deprecated_enctype(ktype)) {
++        len = strlcpy(buf, "DEPRECATED:", buflen);
++        if (len >= buflen)
++            return ENOMEM;
++        buflen -= len;
++        buf += len;
++    }
++
+     /* rfc4556 recommends that clients wishing to indicate support for these
+      * pkinit algorithms include them in the etype field of the AS-REQ. */
+     if (ktype == ENCTYPE_DSA_SHA1_CMS)
+diff --git a/src/kdc/main.c b/src/kdc/main.c
+index 663fd6303..60092a0df 100644
+--- a/src/kdc/main.c
++++ b/src/kdc/main.c
+@@ -210,12 +210,23 @@ init_realm(kdc_realm_t * rdp, krb5_pointer aprof, char *realm,
+     char                *svalue = NULL;
+     const char          *hierarchy[4];
+     krb5_kvno       mkvno = IGNORE_VNO;
++    char ename[32];
+ 
+     memset(rdp, 0, sizeof(kdc_realm_t));
+     if (!realm) {
+         kret = EINVAL;
+         goto whoops;
+     }
++
++    if (def_enctype != ENCTYPE_UNKNOWN &&
++        krb5int_c_deprecated_enctype(def_enctype)) {
++        if (krb5_enctype_to_name(def_enctype, FALSE, ename, sizeof(ename)))
++            ename[0] = '\0';
++        fprintf(stderr,
++                _("Requested master password enctype %s in %s is DEPRECATED!"),
++                ename, realm);
++    }
++
+     hierarchy[0] = KRB5_CONF_REALMS;
+     hierarchy[1] = realm;
+     hierarchy[3] = NULL;
+@@ -370,6 +381,14 @@ init_realm(kdc_realm_t * rdp, krb5_pointer aprof, char *realm,
+         goto whoops;
+     }
+ 
++    if (krb5int_c_deprecated_enctype(rdp->realm_mkey.enctype)) {
++        if (krb5_enctype_to_name(rdp->realm_mkey.enctype, FALSE, ename,
++                                 sizeof(ename)))
++            ename[0] = '\0';
++        fprintf(stderr, _("Stash file %s uses DEPRECATED enctype %s!"),
++                rdp->realm_stash, ename);
++    }
++
+     if ((kret = krb5_db_fetch_mkey_list(rdp->realm_context, rdp->realm_mprinc,
+                                         &rdp->realm_mkey))) {
+         kdc_err(rdp->realm_context, kret,
+diff --git a/src/tests/gssapi/t_enctypes.py b/src/tests/gssapi/t_enctypes.py
+index 5d9f80e04..ca3d32d21 100755
+--- a/src/tests/gssapi/t_enctypes.py
++++ b/src/tests/gssapi/t_enctypes.py
+@@ -9,8 +9,11 @@ from k5test import *
+ aes256 = 'aes256-cts-hmac-sha1-96'
+ aes128 = 'aes128-cts-hmac-sha1-96'
+ des3 = 'des3-cbc-sha1'
++d_des3 = 'DEPRECATED:des3-cbc-sha1'
+ des3raw = 'des3-cbc-raw'
++d_des3raw = 'DEPRECATED:des3-cbc-raw'
+ rc4 = 'arcfour-hmac'
++d_rc4 = 'DEPRECATED:arcfour-hmac'
+ 
+ # These tests make assumptions about the default enctype lists, so set
+ # them explicitly rather than relying on the library defaults.
+@@ -92,7 +95,7 @@ test_err('acc aes128', None, 'aes128-cts',
+ # no acceptor subkey will be generated because we can't upgrade to a
+ # CFX enctype.
+ test('init des3', 'des3', None,
+-     tktenc=aes256, tktsession=des3,
++     tktenc=aes256, tktsession=d_des3,
+      proto='rfc1964', isubkey=des3raw, asubkey=None)
+ 
+ # Force the ticket session key to be rc4, so we can test some subkey
+@@ -103,7 +106,7 @@ realm.run([kadminl, 'setstr', realm.host_princ, 'session_enctypes', 'rc4'])
+ # [aes256 aes128 des3] and the acceptor should upgrade to an aes256
+ # subkey.
+ test('upgrade noargs', None, None,
+-     tktenc=aes256, tktsession=rc4,
++     tktenc=aes256, tktsession=d_rc4,
+      proto='cfx', isubkey=rc4, asubkey=aes256)
+ 
+ # If the initiator won't permit rc4 as a session key, it won't be able
+@@ -113,14 +116,14 @@ test_err('upgrade init aes', 'aes', None, 'no support for encryption type')
+ # If the initiator permits rc4 but prefers aes128, it will send an
+ # upgrade list of [aes128] and the acceptor will upgrade to aes128.
+ test('upgrade init aes128+rc4', 'aes128-cts rc4', None,
+-     tktenc=aes256, tktsession=rc4,
++     tktenc=aes256, tktsession=d_rc4,
+      proto='cfx', isubkey=rc4, asubkey=aes128)
+ 
+ # If the initiator permits rc4 but prefers des3, it will send an
+ # upgrade list of [des3], but the acceptor won't generate a subkey
+ # because des3 isn't a CFX enctype.
+ test('upgrade init des3+rc4', 'des3 rc4', None,
+-     tktenc=aes256, tktsession=rc4,
++     tktenc=aes256, tktsession=d_rc4,
+      proto='rfc1964', isubkey=rc4, asubkey=None)
+ 
+ # If the acceptor permits only aes128, subkey negotiation will fail
+@@ -134,14 +137,14 @@ test_err('upgrade acc aes128', None, 'aes128-cts',
+ # If the acceptor permits rc4 but prefers aes128, it will negotiate an
+ # upgrade to aes128.
+ test('upgrade acc aes128 rc4', None, 'aes128-cts rc4',
+-     tktenc=aes256, tktsession=rc4,
++     tktenc=aes256, tktsession=d_rc4,
+      proto='cfx', isubkey=rc4, asubkey=aes128)
+ 
+ # In this test, the initiator and acceptor each prefer an AES enctype
+ # to rc4, but they can't agree on which one, so no subkey is
+ # generated.
+ test('upgrade mismatch', 'aes128-cts rc4', 'aes256-cts rc4',
+-     tktenc=aes256, tktsession=rc4,
++     tktenc=aes256, tktsession=d_rc4,
+      proto='rfc1964', isubkey=rc4, asubkey=None)
+ 
+ success('gss_krb5_set_allowable_enctypes tests')
+diff --git a/src/tests/t_keyrollover.py b/src/tests/t_keyrollover.py
+index 7c8d828f0..4af6804f2 100755
+--- a/src/tests/t_keyrollover.py
++++ b/src/tests/t_keyrollover.py
+@@ -22,8 +22,9 @@ realm.run([kvno, princ1])
+ realm.run([kadminl, 'purgekeys', realm.krbtgt_princ])
+ # Make sure an old TGT fails after purging old TGS key.
+ realm.run([kvno, princ2], expected_code=1)
+-msg = 'krbtgt/%s@%s\n\tEtype (skey, tkt): des-cbc-crc, des-cbc-crc' % \
+-    (realm.realm, realm.realm)
++ddes = "DEPRECATED:des-cbc-crc"
++msg = 'krbtgt/%s@%s\n\tEtype (skey, tkt): %s, %s' % \
++    (realm.realm, realm.realm, ddes, ddes)
+ realm.run([klist, '-e'], expected_msg=msg)
+ 
+ # Check that new key actually works.
+@@ -48,7 +49,8 @@ realm.run([kadminl, 'cpw', '-randkey', '-keepold', '-e', 'aes256-cts',
+            realm.krbtgt_princ])
+ realm.run([kadminl, 'modprinc', '-kvno', '1', realm.krbtgt_princ])
+ out = realm.run([kadminl, 'getprinc', realm.krbtgt_princ])
+-if 'vno 1, aes256' not in out or 'vno 1, des3' not in out:
++if 'vno 1, aes256-cts' not in out or \
++   'vno 1, DEPRECATED:des3-cbc-sha1' not in out:
+     fail('keyrollover: setup for TGS enctype test failed')
+ # Now present the DES3 ticket to the KDC and make sure it's rejected.
+ realm.run([kvno, realm.host_princ], expected_code=1)
+diff --git a/src/tests/t_sesskeynego.py b/src/tests/t_sesskeynego.py
+index 448092387..da02f224a 100755
+--- a/src/tests/t_sesskeynego.py
++++ b/src/tests/t_sesskeynego.py
+@@ -62,11 +62,11 @@ test_kvno(realm, 'aes128-cts-hmac-sha1-96', 'aes256-cts-hmac-sha1-96')
+ # 3b: Negotiate rc4-hmac session key when principal only has aes256 long-term.
+ realm.run([kadminl, 'setstr', 'server', 'session_enctypes',
+            'rc4-hmac,aes128-cts,aes256-cts'])
+-test_kvno(realm, 'arcfour-hmac', 'aes256-cts-hmac-sha1-96')
++test_kvno(realm, 'DEPRECATED:arcfour-hmac', 'aes256-cts-hmac-sha1-96')
+ 
+ # 3c: Test des-cbc-crc default assumption.
+ realm.run([kadminl, 'delstr', 'server', 'session_enctypes'])
+-test_kvno(realm, 'des-cbc-crc', 'aes256-cts-hmac-sha1-96')
++test_kvno(realm, 'DEPRECATED:des-cbc-crc', 'aes256-cts-hmac-sha1-96')
+ realm.stop()
+ 
+ # Last go: test that we can disable the des-cbc-crc assumption
diff --git a/SOURCES/Properly-size-ifdef-in-k5_cccol_lock.patch b/SOURCES/Properly-size-ifdef-in-k5_cccol_lock.patch
index 001ef5a..b1e4563 100644
--- a/SOURCES/Properly-size-ifdef-in-k5_cccol_lock.patch
+++ b/SOURCES/Properly-size-ifdef-in-k5_cccol_lock.patch
@@ -1,4 +1,4 @@
-From 11ebf658c737baddcd5ce91e018213f28c279737 Mon Sep 17 00:00:00 2001
+From bad6d4390e6e23099cc2295d94e2553575687561 Mon Sep 17 00:00:00 2001
 From: Robbie Harwood <rharwood@redhat.com>
 Date: Thu, 14 Feb 2019 11:50:35 -0500
 Subject: [PATCH] Properly size #ifdef in k5_cccol_lock()
diff --git a/SOURCES/Use-backported-version-of-OpenSSL-3-KDF-interface.patch b/SOURCES/Use-backported-version-of-OpenSSL-3-KDF-interface.patch
new file mode 100644
index 0000000..a95692c
--- /dev/null
+++ b/SOURCES/Use-backported-version-of-OpenSSL-3-KDF-interface.patch
@@ -0,0 +1,747 @@
+From 5e147f7f2924edfd278940dea8b1d8ed09d6872c Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Fri, 15 Nov 2019 20:05:16 +0000
+Subject: [PATCH] Use backported version of OpenSSL-3 KDF interface
+
+(cherry picked from commit 0e20daf7ccfe50518c89735c3dae2fde08d92325)
+---
+ src/configure.in                              |   4 +
+ src/lib/crypto/krb/derive.c                   | 346 +++++++++++++-----
+ .../preauth/pkinit/pkinit_crypto_openssl.c    | 257 +++++++------
+ 3 files changed, 423 insertions(+), 184 deletions(-)
+
+diff --git a/src/configure.in b/src/configure.in
+index 9f6b67b44..cf4b1139a 100644
+--- a/src/configure.in
++++ b/src/configure.in
+@@ -269,6 +269,10 @@ AC_SUBST(CRYPTO_IMPL)
+ AC_SUBST(CRYPTO_IMPL_CFLAGS)
+ AC_SUBST(CRYPTO_IMPL_LIBS)
+ 
++AC_CHECK_FUNCS(EVP_KDF_CTX_new_id EVP_KDF_ctrl EVP_KDF_derive,
++               AC_DEFINE(OSSL_KDFS, 1, [Define if using OpenSSL KDFs]),
++               AC_MSG_ERROR([backported OpenSSL KDFs not found]))
++
+ AC_ARG_WITH([prng-alg],
+ AC_HELP_STRING([--with-prng-alg=ALG], [use specified PRNG algorithm. @<:@fortuna@:>@]),
+ [PRNG_ALG=$withval
+diff --git a/src/lib/crypto/krb/derive.c b/src/lib/crypto/krb/derive.c
+index 6707a7308..915a173dd 100644
+--- a/src/lib/crypto/krb/derive.c
++++ b/src/lib/crypto/krb/derive.c
+@@ -27,6 +27,13 @@
+ 
+ #include "crypto_int.h"
+ 
++#ifdef OSSL_KDFS
++#include <openssl/evp.h>
++#include <openssl/kdf.h>
++#else
++#error "Refusing to build without OpenSSL KDFs!"
++#endif
++
+ static krb5_key
+ find_cached_dkey(struct derived_key *list, const krb5_data *constant)
+ {
+@@ -77,55 +84,193 @@ cleanup:
+     return ENOMEM;
+ }
+ 
++#ifdef OSSL_KDFS
+ static krb5_error_code
+-derive_random_rfc3961(const struct krb5_enc_provider *enc,
+-                      krb5_key inkey, krb5_data *outrnd,
+-                      const krb5_data *in_constant)
++openssl_kbdkf_counter_hmac(const struct krb5_hash_provider *hash,
++                           krb5_key inkey, krb5_data *outrnd,
++                           const krb5_data *label, const krb5_data *context)
+ {
+-    size_t blocksize, keybytes, n;
+-    krb5_error_code ret;
+-    krb5_data block = empty_data();
++    krb5_error_code ret = KRB5_CRYPTO_INTERNAL;
++    EVP_KDF_CTX *ctx = NULL;
++    const EVP_MD *digest;
++
++    if (!strcmp(hash->hash_name, "SHA1"))
++        digest = EVP_sha1();
++    else if (!strcmp(hash->hash_name, "SHA-256"))
++        digest = EVP_sha256();
++    else if (!strcmp(hash->hash_name, "SHA-384"))
++        digest = EVP_sha384();
++    else
++        goto done;
++
++    ctx = EVP_KDF_CTX_new_id(EVP_KDF_KB);
++    if (!ctx)
++        goto done;
++
++    if (EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_MD, digest) != 1 ||
++        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KB_MAC_TYPE,
++                     EVP_KDF_KB_MAC_TYPE_HMAC) != 1 ||
++        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KEY, inkey->keyblock.contents,
++                     inkey->keyblock.length) != 1 ||
++        (context->length > 0 &&
++         EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KB_INFO, context->data,
++                      context->length) != 1) ||
++        (label->length > 0 &&
++         EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SALT, label->data,
++                      label->length) != 1) ||
++        EVP_KDF_derive(ctx, (unsigned char *)outrnd->data,
++                       outrnd->length) != 1)
++        goto done;
++
++    ret = 0;
++done:
++    if (ret)
++        zap(outrnd->data, outrnd->length);
++    EVP_KDF_CTX_free(ctx);
++    return ret;
++}
+ 
+-    blocksize = enc->block_size;
+-    keybytes = enc->keybytes;
++static krb5_error_code
++openssl_kbkdf_feedback_cmac(const struct krb5_enc_provider *enc,
++                            krb5_key inkey, krb5_data *outrnd,
++                            const krb5_data *in_constant)
++{
++    krb5_error_code ret = KRB5_CRYPTO_INTERNAL;
++    EVP_KDF_CTX *ctx = NULL;
++    const EVP_CIPHER *cipher;
++    static unsigned char zeroes[16];
++
++    memset(zeroes, 0, sizeof(zeroes));
++
++    if (enc->keylength == 16)
++        cipher = EVP_camellia_128_cbc();
++    else if (enc->keylength == 32)
++        cipher = EVP_camellia_256_cbc();
++    else
++        goto done;
++
++    ctx = EVP_KDF_CTX_new_id(EVP_KDF_KB);
++    if (!ctx)
++        goto done;
++
++    if (EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KB_MODE,
++                     EVP_KDF_KB_MODE_FEEDBACK) != 1 ||
++        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KB_MAC_TYPE,
++                     EVP_KDF_KB_MAC_TYPE_CMAC) != 1 ||
++        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_CIPHER, cipher) != 1 ||
++        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KEY, inkey->keyblock.contents,
++                     inkey->keyblock.length) != 1 ||
++        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SALT, in_constant->data,
++                     in_constant->length) != 1 ||
++        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KB_SEED, zeroes,
++                     sizeof(zeroes)) != 1 ||
++        EVP_KDF_derive(ctx, (unsigned char *)outrnd->data,
++                       outrnd->length) != 1)
++        goto done;
++
++    ret = 0;
++done:
++    if (ret)
++        zap(outrnd->data, outrnd->length);
++    EVP_KDF_CTX_free(ctx);
++    return ret;
++}
+ 
+-    if (blocksize == 1)
+-        return KRB5_BAD_ENCTYPE;
+-    if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
++static krb5_error_code
++openssl_krb5kdf(const struct krb5_enc_provider *enc, krb5_key inkey,
++                krb5_data *outrnd, const krb5_data *in_constant)
++{
++    krb5_error_code ret = KRB5_CRYPTO_INTERNAL;
++    EVP_KDF_CTX *ctx = NULL;
++    const EVP_CIPHER *cipher;
++
++    if (inkey->keyblock.length != enc->keylength ||
++        outrnd->length != enc->keybytes) {
+         return KRB5_CRYPTO_INTERNAL;
++    }
+ 
+-    /* Allocate encryption data buffer. */
+-    ret = alloc_data(&block, blocksize);
++    if (enc->encrypt == krb5int_aes_encrypt && enc->keylength == 16)
++        cipher = EVP_aes_128_cbc();
++    else if (enc->encrypt == krb5int_aes_encrypt && enc->keylength == 32)
++        cipher = EVP_aes_256_cbc();
++    else if (enc->keylength == 24)
++        cipher = EVP_des_ede3_cbc();
++    else
++        goto done;
++
++    ctx = EVP_KDF_CTX_new_id(EVP_KDF_KRB5KDF);
++    if (ctx == NULL)
++        goto done;
++
++    if (EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_CIPHER, cipher) != 1 ||
++        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KEY, inkey->keyblock.contents,
++                     inkey->keyblock.length) != 1 ||
++        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KRB5KDF_CONSTANT,
++                     in_constant->data, in_constant->length) != 1 ||
++        EVP_KDF_derive(ctx, (unsigned char *)outrnd->data,
++                       outrnd->length) != 1)
++        goto done;
++
++    ret = 0;
++done:
+     if (ret)
+-        return ret;
++        zap(outrnd->data, outrnd->length);
++    EVP_KDF_CTX_free(ctx);
++    return ret;
++}
+ 
+-    /* Initialize the input block. */
+-    if (in_constant->length == blocksize) {
+-        memcpy(block.data, in_constant->data, blocksize);
+-    } else {
+-        krb5int_nfold(in_constant->length * 8,
+-                      (unsigned char *) in_constant->data,
+-                      blocksize * 8, (unsigned char *) block.data);
+-    }
++#else /* OSSL_KDFS */
+ 
+-    /* Loop encrypting the blocks until enough key bytes are generated. */
+-    n = 0;
+-    while (n < keybytes) {
+-        ret = encrypt_block(enc, inkey, &block);
+-        if (ret)
+-            goto cleanup;
++/*
++ * NIST SP800-108 KDF in counter mode (section 5.1).
++ * Parameters:
++ *   - HMAC (with hash as the hash provider) is the PRF.
++ *   - A block counter of four bytes is used.
++ *   - Four bytes are used to encode the output length in the PRF input.
++ *
++ * There are no uses requiring more than a single PRF invocation.
++ */
++static krb5_error_code
++builtin_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
++                               krb5_key inkey, krb5_data *outrnd,
++                               const krb5_data *label,
++                               const krb5_data *context)
++{
++    krb5_crypto_iov iov[5];
++    krb5_error_code ret;
++    krb5_data prf;
++    unsigned char ibuf[4], lbuf[4];
+ 
+-        if ((keybytes - n) <= blocksize) {
+-            memcpy(outrnd->data + n, block.data, (keybytes - n));
+-            break;
+-        }
++    if (hash == NULL || outrnd->length > hash->hashsize)
++        return KRB5_CRYPTO_INTERNAL;
+ 
+-        memcpy(outrnd->data + n, block.data, blocksize);
+-        n += blocksize;
+-    }
++    /* Allocate encryption data buffer. */
++    ret = alloc_data(&prf, hash->hashsize);
++    if (ret)
++        return ret;
+ 
+-cleanup:
+-    zapfree(block.data, blocksize);
++    /* [i]2: four-byte big-endian binary string giving the block counter (1) */
++    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
++    iov[0].data = make_data(ibuf, sizeof(ibuf));
++    store_32_be(1, ibuf);
++    /* Label */
++    iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
++    iov[1].data = *label;
++    /* 0x00: separator byte */
++    iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
++    iov[2].data = make_data("", 1);
++    /* Context */
++    iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
++    iov[3].data = *context;
++    /* [L]2: four-byte big-endian binary string giving the output length */
++    iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
++    iov[4].data = make_data(lbuf, sizeof(lbuf));
++    store_32_be(outrnd->length * 8, lbuf);
++
++    ret = krb5int_hmac(hash, inkey, iov, 5, &prf);
++    if (!ret)
++        memcpy(outrnd->data, prf.data, outrnd->length);
++    zapfree(prf.data, prf.length);
+     return ret;
+ }
+ 
+@@ -139,9 +284,9 @@ cleanup:
+  *   - Four bytes are used to encode the output length in the PRF input.
+  */
+ static krb5_error_code
+-derive_random_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc,
+-                                      krb5_key inkey, krb5_data *outrnd,
+-                                      const krb5_data *in_constant)
++builtin_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc,
++                                krb5_key inkey, krb5_data *outrnd,
++                                const krb5_data *in_constant)
+ {
+     size_t blocksize, keybytes, n;
+     krb5_crypto_iov iov[6];
+@@ -204,57 +349,95 @@ cleanup:
+     return ret;
+ }
+ 
+-/*
+- * NIST SP800-108 KDF in counter mode (section 5.1).
+- * Parameters:
+- *   - HMAC (with hash as the hash provider) is the PRF.
+- *   - A block counter of four bytes is used.
+- *   - Four bytes are used to encode the output length in the PRF input.
+- *
+- * There are no uses requiring more than a single PRF invocation.
+- */
+-krb5_error_code
+-k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
+-                          krb5_key inkey, krb5_data *outrnd,
+-                          const krb5_data *label, const krb5_data *context)
++static krb5_error_code
++builtin_derive_random_rfc3961(const struct krb5_enc_provider *enc,
++                              krb5_key inkey, krb5_data *outrnd,
++                              const krb5_data *in_constant)
+ {
+-    krb5_crypto_iov iov[5];
++    size_t blocksize, keybytes, n;
+     krb5_error_code ret;
+-    krb5_data prf;
+-    unsigned char ibuf[4], lbuf[4];
++    krb5_data block = empty_data();
+ 
+-    if (hash == NULL || outrnd->length > hash->hashsize)
++    blocksize = enc->block_size;
++    keybytes = enc->keybytes;
++
++    if (blocksize == 1)
++        return KRB5_BAD_ENCTYPE;
++    if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
+         return KRB5_CRYPTO_INTERNAL;
+ 
+     /* Allocate encryption data buffer. */
+-    ret = alloc_data(&prf, hash->hashsize);
++    ret = alloc_data(&block, blocksize);
+     if (ret)
+         return ret;
+ 
+-    /* [i]2: four-byte big-endian binary string giving the block counter (1) */
+-    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
+-    iov[0].data = make_data(ibuf, sizeof(ibuf));
+-    store_32_be(1, ibuf);
+-    /* Label */
+-    iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
+-    iov[1].data = *label;
+-    /* 0x00: separator byte */
+-    iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
+-    iov[2].data = make_data("", 1);
+-    /* Context */
+-    iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
+-    iov[3].data = *context;
+-    /* [L]2: four-byte big-endian binary string giving the output length */
+-    iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
+-    iov[4].data = make_data(lbuf, sizeof(lbuf));
+-    store_32_be(outrnd->length * 8, lbuf);
++    /* Initialize the input block. */
++    if (in_constant->length == blocksize) {
++        memcpy(block.data, in_constant->data, blocksize);
++    } else {
++        krb5int_nfold(in_constant->length * 8,
++                      (unsigned char *) in_constant->data,
++                      blocksize * 8, (unsigned char *) block.data);
++    }
+ 
+-    ret = krb5int_hmac(hash, inkey, iov, 5, &prf);
+-    if (!ret)
+-        memcpy(outrnd->data, prf.data, outrnd->length);
+-    zapfree(prf.data, prf.length);
++    /* Loop encrypting the blocks until enough key bytes are generated. */
++    n = 0;
++    while (n < keybytes) {
++        ret = encrypt_block(enc, inkey, &block);
++        if (ret)
++            goto cleanup;
++
++        if ((keybytes - n) <= blocksize) {
++            memcpy(outrnd->data + n, block.data, (keybytes - n));
++            break;
++        }
++
++        memcpy(outrnd->data + n, block.data, blocksize);
++        n += blocksize;
++    }
++
++cleanup:
++    zapfree(block.data, blocksize);
+     return ret;
+ }
++#endif /* OSSL_KDFS */
++
++krb5_error_code
++k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
++                          krb5_key inkey, krb5_data *outrnd,
++                          const krb5_data *label, const krb5_data *context)
++{
++#ifdef OSSL_KDFS
++    return openssl_kbdkf_counter_hmac(hash, inkey, outrnd, label, context);
++#else
++    return builtin_sp800_108_counter_hmac(hash, inkey, outrnd, label,
++                                          context);
++#endif
++}
++
++static krb5_error_code
++k5_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc,
++                           krb5_key inkey, krb5_data *outrnd,
++                           const krb5_data *in_constant)
++{
++#ifdef OSSL_KDFS
++    return openssl_kbkdf_feedback_cmac(enc, inkey, outrnd, in_constant);
++#else
++    return builtin_sp800_108_feedback_cmac(enc, inkey, outrnd, in_constant);
++#endif
++}
++
++static krb5_error_code
++k5_derive_random_rfc3961(const struct krb5_enc_provider *enc,
++                         krb5_key inkey, krb5_data *outrnd,
++                         const krb5_data *in_constant)
++{
++#ifdef OSSL_KDFS
++    return openssl_krb5kdf(enc, inkey, outrnd, in_constant);
++#else
++    return builtin_derive_random_rfc3961(enc, inkey, outrnd, in_constant);
++#endif
++}
+ 
+ krb5_error_code
+ krb5int_derive_random(const struct krb5_enc_provider *enc,
+@@ -266,10 +449,9 @@ krb5int_derive_random(const struct krb5_enc_provider *enc,
+ 
+     switch (alg) {
+     case DERIVE_RFC3961:
+-        return derive_random_rfc3961(enc, inkey, outrnd, in_constant);
++        return k5_derive_random_rfc3961(enc, inkey, outrnd, in_constant);
+     case DERIVE_SP800_108_CMAC:
+-        return derive_random_sp800_108_feedback_cmac(enc, inkey, outrnd,
+-                                                     in_constant);
++        return k5_sp800_108_feedback_cmac(enc, inkey, outrnd, in_constant);
+     case DERIVE_SP800_108_HMAC:
+         return k5_sp800_108_counter_hmac(hash, inkey, outrnd, in_constant,
+                                          &empty);
+diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+index 5ff81d8cf..8d2c230c8 100644
+--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
++++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+@@ -38,6 +38,13 @@
+ #include <dirent.h>
+ #include <arpa/inet.h>
+ 
++#ifdef OSSL_KDFS
++#include <openssl/evp.h>
++#include <openssl/kdf.h>
++#else
++#error "Refusing to build without OpenSSL KDFs!"
++#endif
++
+ static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context );
+ static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context );
+ 
+@@ -2460,11 +2467,51 @@ pkinit_alg_values(krb5_context context,
+     }
+ } /* pkinit_alg_values() */
+ 
++#ifdef OSSL_KDFS
++static krb5_error_code
++openssl_sskdf(krb5_context context, size_t hash_bytes, krb5_data *key,
++              krb5_data *info, char *out, size_t out_len)
++{
++    krb5_error_code ret = KRB5_CRYPTO_INTERNAL;
++    EVP_KDF_CTX *ctx = NULL;
++    const EVP_MD *digest;
++
++    /* RFC 8636 defines a SHA384 variant, but we don't use it. */
++    if (hash_bytes == 20) {
++        digest = EVP_sha1();
++    } else if (hash_bytes == 32) {
++        digest = EVP_sha256();
++    } else if (hash_bytes == 64) {
++        digest = EVP_sha512();
++    } else {
++        krb5_set_error_message(context, ret, "Bad hash type for SSKDF");
++        goto done;
++    }
+ 
+-/* pkinit_alg_agility_kdf() --
+- * This function generates a key using the KDF described in
+- * draft_ietf_krb_wg_pkinit_alg_agility-04.txt.  The algorithm is
+- * described as follows:
++    ctx = EVP_KDF_CTX_new_id(EVP_KDF_SS);
++    if (!ctx) {
++        oerr(context, ret, _("Failed to instantiate SSKDF"));
++        goto done;
++    }
++
++    if (EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_MD, digest) != 1 ||
++        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KEY, key->data,
++                     key->length) != 1 ||
++        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSKDF_INFO, info->data,
++                     info->length) != 1 ||
++        EVP_KDF_derive(ctx, (unsigned char *)out, out_len) != 1)
++        goto done;
++
++    ret = 0;
++done:
++    EVP_KDF_CTX_free(ctx);
++    return ret;
++}
++#else
++/*
++ * Generate a key using the KDF described in RFC 8636, also known as SSKDF
++ * (single-step kdf).  Our caller precomputes `reps`, but otherwise the
++ * algorithm is as follows:
+  *
+  *     1.  reps = keydatalen (K) / hash length (H)
+  *
+@@ -2478,95 +2525,16 @@ pkinit_alg_values(krb5_context context,
+  *
+  *     4.  Set key = Hash1 || Hash2 || ... so that length of key is K bytes.
+  */
+-krb5_error_code
+-pkinit_alg_agility_kdf(krb5_context context,
+-                       krb5_data *secret,
+-                       krb5_data *alg_oid,
+-                       krb5_const_principal party_u_info,
+-                       krb5_const_principal party_v_info,
+-                       krb5_enctype enctype,
+-                       krb5_data *as_req,
+-                       krb5_data *pk_as_rep,
+-                       krb5_keyblock *key_block)
++static krb5_error_code
++builtin_sskdf(krb5_context context, unsigned int reps, size_t hash_len,
++              const EVP_MD *(*EVP_func)(void), krb5_data *secret,
++              krb5_data *other_info, char *out, size_t out_len)
+ {
+     krb5_error_code retval = 0;
+ 
+-    unsigned int reps = 0;
+-    uint32_t counter = 1;       /* Does this type work on Windows? */
++    uint32_t counter = 1;
+     size_t offset = 0;
+-    size_t hash_len = 0;
+-    size_t rand_len = 0;
+-    size_t key_len = 0;
+-    krb5_data random_data;
+-    krb5_sp80056a_other_info other_info_fields;
+-    krb5_pkinit_supp_pub_info supp_pub_info_fields;
+-    krb5_data *other_info = NULL;
+-    krb5_data *supp_pub_info = NULL;
+-    krb5_algorithm_identifier alg_id;
+     EVP_MD_CTX *ctx = NULL;
+-    const EVP_MD *(*EVP_func)(void);
+-
+-    /* initialize random_data here to make clean-up safe */
+-    random_data.length = 0;
+-    random_data.data = NULL;
+-
+-    /* allocate and initialize the key block */
+-    key_block->magic = 0;
+-    key_block->enctype = enctype;
+-    if (0 != (retval = krb5_c_keylengths(context, enctype, &rand_len,
+-                                         &key_len)))
+-        goto cleanup;
+-
+-    random_data.length = rand_len;
+-    key_block->length = key_len;
+-
+-    if (NULL == (key_block->contents = malloc(key_block->length))) {
+-        retval = ENOMEM;
+-        goto cleanup;
+-    }
+-
+-    memset (key_block->contents, 0, key_block->length);
+-
+-    /* If this is anonymous pkinit, use the anonymous principle for party_u_info */
+-    if (party_u_info && krb5_principal_compare_any_realm(context, party_u_info,
+-                                                         krb5_anonymous_principal()))
+-        party_u_info = (krb5_principal)krb5_anonymous_principal();
+-
+-    if (0 != (retval = pkinit_alg_values(context, alg_oid, &hash_len, &EVP_func)))
+-        goto cleanup;
+-
+-    /* 1.  reps = keydatalen (K) / hash length (H) */
+-    reps = key_block->length/hash_len;
+-
+-    /* ... and round up, if necessary */
+-    if (key_block->length > (reps * hash_len))
+-        reps++;
+-
+-    /* Allocate enough space in the random data buffer to hash directly into
+-     * it, even if the last hash will make it bigger than the key length. */
+-    if (NULL == (random_data.data = malloc(reps * hash_len))) {
+-        retval = ENOMEM;
+-        goto cleanup;
+-    }
+-
+-    /* Encode the ASN.1 octet string for "SuppPubInfo" */
+-    supp_pub_info_fields.enctype = enctype;
+-    supp_pub_info_fields.as_req = *as_req;
+-    supp_pub_info_fields.pk_as_rep = *pk_as_rep;
+-    if (0 != ((retval = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields,
+-                                                         &supp_pub_info))))
+-        goto cleanup;
+-
+-    /* Now encode the ASN.1 octet string for "OtherInfo" */
+-    memset(&alg_id, 0, sizeof alg_id);
+-    alg_id.algorithm = *alg_oid; /*alias*/
+-
+-    other_info_fields.algorithm_identifier = alg_id;
+-    other_info_fields.party_u_info = (krb5_principal) party_u_info;
+-    other_info_fields.party_v_info = (krb5_principal) party_v_info;
+-    other_info_fields.supp_pub_info = *supp_pub_info;
+-    if (0 != (retval = encode_krb5_sp80056a_other_info(&other_info_fields, &other_info)))
+-        goto cleanup;
+ 
+     /* 2.  Initialize a 32-bit, big-endian bit string counter as 1.
+      * 3.  For i = 1 to reps by 1, do the following:
+@@ -2600,8 +2568,9 @@ pkinit_alg_agility_kdf(krb5_context context,
+             goto cleanup;
+         }
+ 
+-        /* 4.  Set key = Hash1 || Hash2 || ... so that length of key is K bytes. */
+-        if (!EVP_DigestFinal(ctx, (uint8_t *)random_data.data + offset, &s)) {
++        /* 4.  Set key = Hash1 || Hash2 || ... so that length of key is K
++         * bytes. */
++        if (!EVP_DigestFinal(ctx, (unsigned char *)out + offset, &s)) {
+             krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL,
+                                    "Call to OpenSSL EVP_DigestUpdate() returned an error.");
+             retval = KRB5_CRYPTO_INTERNAL;
+@@ -2613,26 +2582,110 @@ pkinit_alg_agility_kdf(krb5_context context,
+         EVP_MD_CTX_free(ctx);
+         ctx = NULL;
+     }
+-
+-    retval = krb5_c_random_to_key(context, enctype, &random_data,
+-                                  key_block);
+-
+ cleanup:
+     EVP_MD_CTX_free(ctx);
++    return retval;
++} /* builtin_sskdf() */
++#endif /* OSSL_KDFS */
+ 
+-    /* If this has been an error, free the allocated key_block, if any */
+-    if (retval) {
+-        krb5_free_keyblock_contents(context, key_block);
++/* id-pkinit-kdf family, as specified by RFC 8636. */
++krb5_error_code
++pkinit_alg_agility_kdf(krb5_context context, krb5_data *secret,
++                       krb5_data *alg_oid, krb5_const_principal party_u_info,
++                       krb5_const_principal party_v_info,
++                       krb5_enctype enctype, krb5_data *as_req,
++                       krb5_data *pk_as_rep, krb5_keyblock *key_block)
++{
++    krb5_error_code retval;
++    size_t hash_len = 0, rand_len = 0, key_len = 0;
++    const EVP_MD *(*EVP_func)(void);
++    krb5_sp80056a_other_info other_info_fields;
++    krb5_pkinit_supp_pub_info supp_pub_info_fields;
++    krb5_data *other_info = NULL, *supp_pub_info = NULL;
++    krb5_data random_data = empty_data();
++    krb5_algorithm_identifier alg_id;
++    unsigned int reps;
++
++    /* Allocate and initialize the key block. */
++    key_block->magic = 0;
++    key_block->enctype = enctype;
++
++    /* Use separate variables to avoid alignment restriction problems. */
++    retval = krb5_c_keylengths(context, enctype, &rand_len, &key_len);
++    if (retval)
++        goto cleanup;
++    random_data.length = rand_len;
++    key_block->length = key_len;
++
++    key_block->contents = k5calloc(key_block->length, 1, &retval);
++    if (key_block->contents == NULL)
++        goto cleanup;
++
++    /* If this is anonymous pkinit, use the anonymous principle for
++     * party_u_info. */
++    if (party_u_info &&
++        krb5_principal_compare_any_realm(context, party_u_info,
++                                         krb5_anonymous_principal())) {
++        party_u_info = (krb5_principal)krb5_anonymous_principal();
+     }
+ 
+-    /* free other allocated resources, either way */
+-    if (random_data.data)
+-        free(random_data.data);
++    retval = pkinit_alg_values(context, alg_oid, &hash_len, &EVP_func);
++    if (retval)
++        goto cleanup;
++
++    /* 1.  reps = keydatalen (K) / hash length (H) */
++    reps = key_block->length / hash_len;
++
++    /* ... and round up, if necessary. */
++    if (key_block->length > (reps * hash_len))
++        reps++;
++
++    /* Allocate enough space in the random data buffer to hash directly into
++     * it, even if the last hash will make it bigger than the key length. */
++    random_data.data = k5alloc(reps * hash_len, &retval);
++    if (random_data.data == NULL)
++        goto cleanup;
++
++    /* Encode the ASN.1 octet string for "SuppPubInfo". */
++    supp_pub_info_fields.enctype = enctype;
++    supp_pub_info_fields.as_req = *as_req;
++    supp_pub_info_fields.pk_as_rep = *pk_as_rep;
++    retval = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields,
++                                              &supp_pub_info);
++    if (retval)
++        goto cleanup;
++
++    /* Now encode the ASN.1 octet string for "OtherInfo". */
++    memset(&alg_id, 0, sizeof(alg_id));
++    alg_id.algorithm = *alg_oid;
++    other_info_fields.algorithm_identifier = alg_id;
++    other_info_fields.party_u_info = (krb5_principal)party_u_info;
++    other_info_fields.party_v_info = (krb5_principal)party_v_info;
++    other_info_fields.supp_pub_info = *supp_pub_info;
++    retval = encode_krb5_sp80056a_other_info(&other_info_fields, &other_info);
++    if (retval)
++        goto cleanup;
++
++#ifdef OSSL_KDFS
++    retval = openssl_sskdf(context, hash_len, secret, other_info,
++                           random_data.data, key_block->length);
++#else
++    retval = builtin_sskdf(context, reps, hash_len, EVP_func, secret,
++                           other_info, random_data.data, key_block->length);
++#endif
++    if (retval)
++        goto cleanup;
++
++    retval = krb5_c_random_to_key(context, enctype, &random_data, key_block);
++cleanup:
++    if (retval)
++        krb5_free_keyblock_contents(context, key_block);
++
++    zapfree(random_data.data, random_data.length);
+     krb5_free_data(context, other_info);
+     krb5_free_data(context, supp_pub_info);
+-
+     return retval;
+-} /*pkinit_alg_agility_kdf() */
++}
+ 
+ /* Call DH_compute_key() and ensure that we left-pad short results instead of
+  * leaving junk bytes at the end of the buffer. */
diff --git a/SOURCES/Use-imported-soft-pkcs11-for-tests.patch b/SOURCES/Use-imported-soft-pkcs11-for-tests.patch
new file mode 100644
index 0000000..0be2d97
--- /dev/null
+++ b/SOURCES/Use-imported-soft-pkcs11-for-tests.patch
@@ -0,0 +1,472 @@
+From 43f5837eecd5022c525efcfb3605af16958dc59a Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson@mit.edu>
+Date: Thu, 20 Jun 2019 13:41:57 -0400
+Subject: [PATCH] Use imported soft-pkcs11 for tests
+
+Update the soft-pkcs11 code for OpenSSL 1.1, fix some warnings,
+integrate it into the build system, and use it for the PKINIT tests.
+
+(cherry picked from commit e5ef7b69765353ea62ad8712a229ed4e90a8fe17)
+(cherry picked from commit 47e66724b9d5cfef84965d99c83d29e4739932e3)
+---
+ src/configure.in                        |   1 +
+ src/tests/Makefile.in                   |   2 +-
+ src/tests/softpkcs11/Makefile.in        |  21 ++++
+ src/tests/softpkcs11/deps               |   6 ++
+ src/tests/softpkcs11/main.c             | 124 +++++++++++++++++-------
+ src/tests/softpkcs11/softpkcs11.exports |  39 ++++++++
+ src/tests/t_pkinit.py                   |  18 +---
+ 7 files changed, 162 insertions(+), 49 deletions(-)
+ create mode 100644 src/tests/softpkcs11/Makefile.in
+ create mode 100644 src/tests/softpkcs11/deps
+ create mode 100644 src/tests/softpkcs11/softpkcs11.exports
+
+diff --git a/src/configure.in b/src/configure.in
+index 93aec682e..9f6b67b44 100644
+--- a/src/configure.in
++++ b/src/configure.in
+@@ -1086,6 +1086,7 @@ int i = 1;
+ fi
+ if test "$k5_cv_openssl_version_okay" = yes && (test "$enable_pkinit" = yes || test "$enable_pkinit" = try); then
+   K5_GEN_MAKEFILE(plugins/preauth/pkinit)
++  K5_GEN_MAKEFILE(tests/softpkcs11)
+   PKINIT=yes
+   AC_CHECK_LIB(crypto, CMS_get0_content, [AC_DEFINE([HAVE_OPENSSL_CMS], 1, [Define if OpenSSL supports cms.])])
+ elif test "$k5_cv_openssl_version_okay" = no && test "$enable_pkinit" = yes; then
+diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
+index e27617ee2..ab958eb4c 100644
+--- a/src/tests/Makefile.in
++++ b/src/tests/Makefile.in
+@@ -1,7 +1,7 @@
+ mydir=tests
+ BUILDTOP=$(REL)..
+ SUBDIRS = resolve asn.1 create hammer verify gssapi dejagnu shlib \
+-	gss-threads misc threads
++	gss-threads misc threads softpkcs11
+ 
+ RUN_DB_TEST = $(RUN_SETUP) KRB5_KDC_PROFILE=kdc.conf KRB5_CONFIG=krb5.conf \
+ 	LC_ALL=C $(VALGRIND)
+diff --git a/src/tests/softpkcs11/Makefile.in b/src/tests/softpkcs11/Makefile.in
+new file mode 100644
+index 000000000..e89678154
+--- /dev/null
++++ b/src/tests/softpkcs11/Makefile.in
+@@ -0,0 +1,21 @@
++mydir=tests$(S)softpkcs11
++BUILDTOP=$(REL)..$(S)..
++
++LOCALINCLUDES = -I$(top_srcdir)/plugins/preauth/pkinit
++
++LIBBASE=softpkcs11
++LIBMAJOR=0
++LIBMINOR=0
++
++SHLIB_EXPLIBS=$(SUPPORT_LIB) -lcrypto
++SHLIB_EXPDEPS=$(SUPPORT_DEPLIB)
++
++STLIBOBJS=main.o
++
++SRCS=$(srcdir)/main.c
++
++all-unix: all-libs
++clean-unix:: clean-libs clean-libobjs
++
++@libnover_frag@
++@libobj_frag@
+diff --git a/src/tests/softpkcs11/deps b/src/tests/softpkcs11/deps
+new file mode 100644
+index 000000000..1e82d9572
+--- /dev/null
++++ b/src/tests/softpkcs11/deps
+@@ -0,0 +1,6 @@
++#
++# Generated makefile dependencies follow.
++#
++main.so main.po $(OUTPRE)main.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
++  $(top_srcdir)/plugins/preauth/pkinit/pkcs11.h main.c
+diff --git a/src/tests/softpkcs11/main.c b/src/tests/softpkcs11/main.c
+index 2acec5169..5255323d3 100644
+--- a/src/tests/softpkcs11/main.c
++++ b/src/tests/softpkcs11/main.c
+@@ -1,3 +1,4 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+ /*
+  * Copyright (c) 2004-2006, Stockholms universitet
+  * (Stockholm University, Stockholm Sweden)
+@@ -31,7 +32,57 @@
+  * POSSIBILITY OF SUCH DAMAGE.
+  */
+ 
+-#include "locl.h"
++#include "k5-platform.h"
++
++#include <openssl/err.h>
++#include <openssl/evp.h>
++#include <openssl/pem.h>
++#include <openssl/rand.h>
++#include <openssl/x509.h>
++
++#include <ctype.h>
++#include <pwd.h>
++
++#include <pkcs11.h>
++
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
++#define EVP_PKEY_get0_RSA(key) ((key)->pkey.rsa)
++#define RSA_PKCS1_OpenSSL RSA_PKCS1_SSLeay
++#define RSA_get0_key compat_rsa_get0_key
++static void
++compat_rsa_get0_key(const RSA *rsa, const BIGNUM **n, const BIGNUM **e,
++                    const BIGNUM **d)
++{
++    if (n != NULL)
++        *n = rsa->n;
++    if (e != NULL)
++        *e = rsa->e;
++    if (d != NULL)
++        *d = rsa->d;
++}
++#endif
++
++#define OPENSSL_ASN1_MALLOC_ENCODE(T, B, BL, S, R)      \
++    {                                                   \
++        unsigned char *p;                               \
++        (BL) = i2d_##T((S), NULL);                      \
++        if ((BL) <= 0) {                                \
++            (R) = EINVAL;                               \
++        } else {                                        \
++            (B) = malloc((BL));                         \
++            if ((B) == NULL) {                          \
++                (R) = ENOMEM;                           \
++            } else {                                    \
++                p = (B);                                \
++                (R) = 0;                                \
++                (BL) = i2d_##T((S), &p);                \
++                if ((BL) <= 0) {                        \
++                    free((B));                          \
++                    (R) = EINVAL;                       \
++                }                                       \
++            }                                           \
++        }                                               \
++    }
+ 
+ /* RCSID("$Id: main.c,v 1.24 2006/01/11 12:42:53 lha Exp $"); */
+ 
+@@ -124,7 +175,7 @@ st_logf(const char *fmt, ...)
+ }
+ 
+ static void
+-snprintf_fill(char *str, size_t size, char fillchar, const char *fmt, ...)
++snprintf_fill(char *str, int size, char fillchar, const char *fmt, ...)
+ {
+     int len;
+     va_list ap;
+@@ -141,19 +192,19 @@ snprintf_fill(char *str, size_t size, char fillchar, const char *fmt, ...)
+ #endif
+ 
+ #define VERIFY_SESSION_HANDLE(s, state)                 \
+-{                                                       \
+-    CK_RV ret;                                          \
+-    ret = verify_session_handle(s, state);              \
+-    if (ret != CKR_OK) {                                \
+-        /* return CKR_OK */;                            \
+-    }                                                   \
+-}
++    {                                                   \
++        CK_RV vshret;                                   \
++        vshret = verify_session_handle(s, state);       \
++        if (vshret != CKR_OK) {                         \
++            /* return CKR_OK */;                        \
++        }                                               \
++    }
+ 
+ static CK_RV
+ verify_session_handle(CK_SESSION_HANDLE hSession,
+                       struct session_state **state)
+ {
+-    int i;
++    size_t i;
+ 
+     for (i = 0; i < MAX_NUM_SESSION; i++){
+         if (soft_token.state[i].session_handle == hSession)
+@@ -361,16 +412,20 @@ add_pubkey_info(struct st_object *o, CK_KEY_TYPE key_type, EVP_PKEY *key)
+         CK_ULONG modulus_bits = 0;
+         CK_BYTE *exponent = NULL;
+         size_t exponent_len = 0;
++        RSA *rsa;
++        const BIGNUM *n, *e;
+ 
+-        modulus_bits = BN_num_bits(key->pkey.rsa->n);
++        rsa = EVP_PKEY_get0_RSA(key);
++        RSA_get0_key(rsa, &n, &e, NULL);
++        modulus_bits = BN_num_bits(n);
+ 
+-        modulus_len = BN_num_bytes(key->pkey.rsa->n);
++        modulus_len = BN_num_bytes(n);
+         modulus = malloc(modulus_len);
+-        BN_bn2bin(key->pkey.rsa->n, modulus);
++        BN_bn2bin(n, modulus);
+ 
+-        exponent_len = BN_num_bytes(key->pkey.rsa->e);
++        exponent_len = BN_num_bytes(e);
+         exponent = malloc(exponent_len);
+-        BN_bn2bin(key->pkey.rsa->e, exponent);
++        BN_bn2bin(e, exponent);
+ 
+         add_object_attribute(o, 0, CKA_MODULUS, modulus, modulus_len);
+         add_object_attribute(o, 0, CKA_MODULUS_BITS,
+@@ -378,7 +433,7 @@ add_pubkey_info(struct st_object *o, CK_KEY_TYPE key_type, EVP_PKEY *key)
+         add_object_attribute(o, 0, CKA_PUBLIC_EXPONENT,
+                              exponent, exponent_len);
+ 
+-        RSA_set_method(key->pkey.rsa, RSA_PKCS1_SSLeay());
++        RSA_set_method(rsa, RSA_PKCS1_OpenSSL());
+ 
+         free(modulus);
+         free(exponent);
+@@ -474,7 +529,7 @@ add_certificate(char *label,
+     o->u.cert = cert;
+     public_key = X509_get_pubkey(o->u.cert);
+ 
+-    switch (EVP_PKEY_type(public_key->type)) {
++    switch (EVP_PKEY_base_id(public_key)) {
+     case EVP_PKEY_RSA:
+         key_type = CKK_RSA;
+         break;
+@@ -604,8 +659,8 @@ add_certificate(char *label,
+             /* XXX verify keytype */
+ 
+             if (key_type == CKK_RSA)
+-                RSA_set_method(o->u.private_key.key->pkey.rsa,
+-                               RSA_PKCS1_SSLeay());
++                RSA_set_method(EVP_PKEY_get0_RSA(o->u.private_key.key),
++                               RSA_PKCS1_OpenSSL());
+ 
+             if (X509_check_private_key(cert, o->u.private_key.key) != 1) {
+                 EVP_PKEY_free(o->u.private_key.key);
+@@ -755,8 +810,9 @@ CK_RV
+ C_Initialize(CK_VOID_PTR a)
+ {
+     CK_C_INITIALIZE_ARGS_PTR args = a;
++    size_t i;
++
+     st_logf("Initialize\n");
+-    int i;
+ 
+     OpenSSL_add_all_algorithms();
+     ERR_load_crypto_strings();
+@@ -825,7 +881,7 @@ C_Initialize(CK_VOID_PTR a)
+ CK_RV
+ C_Finalize(CK_VOID_PTR args)
+ {
+-    int i;
++    size_t i;
+ 
+     st_logf("Finalize\n");
+ 
+@@ -1008,7 +1064,7 @@ C_OpenSession(CK_SLOT_ID slotID,
+               CK_NOTIFY Notify,
+               CK_SESSION_HANDLE_PTR phSession)
+ {
+-    int i;
++    size_t i;
+ 
+     st_logf("OpenSession: slot: %d\n", (int)slotID);
+ 
+@@ -1050,7 +1106,7 @@ C_CloseSession(CK_SESSION_HANDLE hSession)
+ CK_RV
+ C_CloseAllSessions(CK_SLOT_ID slotID)
+ {
+-    int i;
++    size_t i;
+ 
+     st_logf("CloseAllSessions\n");
+ 
+@@ -1127,7 +1183,8 @@ C_Login(CK_SESSION_HANDLE hSession,
+         }
+ 
+         /* XXX check keytype */
+-        RSA_set_method(o->u.private_key.key->pkey.rsa, RSA_PKCS1_SSLeay());
++        RSA_set_method(EVP_PKEY_get0_RSA(o->u.private_key.key),
++                       RSA_PKCS1_OpenSSL());
+ 
+         if (X509_check_private_key(o->u.private_key.cert, o->u.private_key.key) != 1) {
+             EVP_PKEY_free(o->u.private_key.key);
+@@ -1226,7 +1283,6 @@ C_FindObjectsInit(CK_SESSION_HANDLE hSession,
+     }
+     if (ulCount) {
+         CK_ULONG i;
+-        size_t len;
+ 
+         print_attributes(pTemplate, ulCount);
+ 
+@@ -1415,7 +1471,7 @@ C_Encrypt(CK_SESSION_HANDLE hSession,
+         return CKR_ARGUMENTS_BAD;
+     }
+ 
+-    rsa = o->u.public_key->pkey.rsa;
++    rsa = EVP_PKEY_get0_RSA(o->u.public_key);
+ 
+     if (rsa == NULL)
+         return CKR_ARGUMENTS_BAD;
+@@ -1445,7 +1501,7 @@ C_Encrypt(CK_SESSION_HANDLE hSession,
+         goto out;
+     }
+ 
+-    if (buffer_len + padding_len < ulDataLen) {
++    if ((CK_ULONG)buffer_len + padding_len < ulDataLen) {
+         ret = CKR_ARGUMENTS_BAD;
+         goto out;
+     }
+@@ -1566,7 +1622,7 @@ C_Decrypt(CK_SESSION_HANDLE hSession,
+         return CKR_ARGUMENTS_BAD;
+     }
+ 
+-    rsa = o->u.private_key.key->pkey.rsa;
++    rsa = EVP_PKEY_get0_RSA(o->u.private_key.key);
+ 
+     if (rsa == NULL)
+         return CKR_ARGUMENTS_BAD;
+@@ -1596,7 +1652,7 @@ C_Decrypt(CK_SESSION_HANDLE hSession,
+         goto out;
+     }
+ 
+-    if (buffer_len + padding_len < ulEncryptedDataLen) {
++    if ((CK_ULONG)buffer_len + padding_len < ulEncryptedDataLen) {
+         ret = CKR_ARGUMENTS_BAD;
+         goto out;
+     }
+@@ -1725,7 +1781,7 @@ C_Sign(CK_SESSION_HANDLE hSession,
+         return CKR_ARGUMENTS_BAD;
+     }
+ 
+-    rsa = o->u.private_key.key->pkey.rsa;
++    rsa = EVP_PKEY_get0_RSA(o->u.private_key.key);
+ 
+     if (rsa == NULL)
+         return CKR_ARGUMENTS_BAD;
+@@ -1754,7 +1810,7 @@ C_Sign(CK_SESSION_HANDLE hSession,
+         goto out;
+     }
+ 
+-    if (buffer_len < ulDataLen + padding_len) {
++    if ((CK_ULONG)buffer_len < ulDataLen + padding_len) {
+         ret = CKR_ARGUMENTS_BAD;
+         goto out;
+     }
+@@ -1872,7 +1928,7 @@ C_Verify(CK_SESSION_HANDLE hSession,
+         return CKR_ARGUMENTS_BAD;
+     }
+ 
+-    rsa = o->u.public_key->pkey.rsa;
++    rsa = EVP_PKEY_get0_RSA(o->u.public_key);
+ 
+     if (rsa == NULL)
+         return CKR_ARGUMENTS_BAD;
+@@ -1900,7 +1956,7 @@ C_Verify(CK_SESSION_HANDLE hSession,
+         goto out;
+     }
+ 
+-    if (buffer_len < ulDataLen) {
++    if ((CK_ULONG)buffer_len < ulDataLen) {
+         ret = CKR_ARGUMENTS_BAD;
+         goto out;
+     }
+@@ -1926,7 +1982,7 @@ C_Verify(CK_SESSION_HANDLE hSession,
+     if (len > buffer_len)
+         abort();
+ 
+-    if (len != ulSignatureLen) {
++    if ((CK_ULONG)len != ulSignatureLen) {
+         ret = CKR_GENERAL_ERROR;
+         goto out;
+     }
+diff --git a/src/tests/softpkcs11/softpkcs11.exports b/src/tests/softpkcs11/softpkcs11.exports
+new file mode 100644
+index 000000000..aa7284511
+--- /dev/null
++++ b/src/tests/softpkcs11/softpkcs11.exports
+@@ -0,0 +1,39 @@
++C_CloseAllSessions
++C_CloseSession
++C_Decrypt
++C_DecryptFinal
++C_DecryptInit
++C_DecryptUpdate
++C_DigestInit
++C_Encrypt
++C_EncryptFinal
++C_EncryptInit
++C_EncryptUpdate
++C_Finalize
++C_FindObjects
++C_FindObjectsFinal
++C_FindObjectsInit
++C_GenerateRandom
++C_GetAttributeValue
++C_GetFunctionList
++C_GetInfo
++C_GetMechanismInfo
++C_GetMechanismList
++C_GetObjectSize
++C_GetSessionInfo
++C_GetSlotInfo
++C_GetSlotList
++C_GetTokenInfo
++C_Initialize
++C_InitToken
++C_Login
++C_Logout
++C_OpenSession
++C_Sign
++C_SignFinal
++C_SignInit
++C_SignUpdate
++C_Verify
++C_VerifyFinal
++C_VerifyInit
++C_VerifyUpdate
+diff --git a/src/tests/t_pkinit.py b/src/tests/t_pkinit.py
+index 1dadb1b96..384bf1426 100755
+--- a/src/tests/t_pkinit.py
++++ b/src/tests/t_pkinit.py
+@@ -4,14 +4,7 @@ from k5test import *
+ if not os.path.exists(os.path.join(plugins, 'preauth', 'pkinit.so')):
+     skip_rest('PKINIT tests', 'PKINIT module not built')
+ 
+-# Check if soft-pkcs11.so is available.
+-try:
+-    import ctypes
+-    lib = ctypes.LibraryLoader(ctypes.CDLL).LoadLibrary('soft-pkcs11.so')
+-    del lib
+-    have_soft_pkcs11 = True
+-except:
+-    have_soft_pkcs11 = False
++soft_pkcs11 = os.path.join(buildtop, 'tests', 'softpkcs11', 'softpkcs11.so')
+ 
+ # Construct a krb5.conf fragment configuring pkinit.
+ certs = os.path.join(srctop, 'tests', 'dejagnu', 'pkinit-certs')
+@@ -69,9 +62,9 @@ p12_upn2_identity = 'PKCS12:%s' % user_upn2_p12
+ p12_upn3_identity = 'PKCS12:%s' % user_upn3_p12
+ p12_generic_identity = 'PKCS12:%s' % generic_p12
+ p12_enc_identity = 'PKCS12:%s' % user_enc_p12
+-p11_identity = 'PKCS11:soft-pkcs11.so'
+-p11_token_identity = ('PKCS11:module_name=soft-pkcs11.so:'
+-                      'slotid=1:token=SoftToken (token)')
++p11_identity = 'PKCS11:' + soft_pkcs11
++p11_token_identity = ('PKCS11:module_name=' + soft_pkcs11 +
++                      ':slotid=1:token=SoftToken (token)')
+ 
+ # Start a realm with the test kdb module for the following UPN SAN tests.
+ realm = K5Realm(krb5_conf=pkinit_krb5_conf, kdc_conf=alias_kdc_conf,
+@@ -398,9 +391,6 @@ realm.klist(realm.user_princ)
+ realm.kinit(realm.user_princ, flags=['-X', 'X509_user_identity=,'],
+             expected_code=1, expected_msg='Preauthentication failed while')
+ 
+-if not have_soft_pkcs11:
+-    skip_rest('PKINIT PKCS11 tests', 'soft-pkcs11.so not found')
+-
+ softpkcs11rc = os.path.join(os.getcwd(), 'testdir', 'soft-pkcs11.rc')
+ realm.env['SOFTPKCS11RC'] = softpkcs11rc
+ 
diff --git a/SOURCES/Use-openssl-s-PRNG-in-FIPS-mode.patch b/SOURCES/Use-openssl-s-PRNG-in-FIPS-mode.patch
deleted file mode 100644
index 632859f..0000000
--- a/SOURCES/Use-openssl-s-PRNG-in-FIPS-mode.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From 196ee40d489e4e6a72232a3cdbb7af19a72362b3 Mon Sep 17 00:00:00 2001
-From: Robbie Harwood <rharwood@redhat.com>
-Date: Fri, 4 Jan 2019 17:00:15 -0500
-Subject: [PATCH] Use openssl's PRNG in FIPS mode
-
-(cherry picked from commit 31277d79675a76612015ea00d420b41b9a232d5a)
----
- src/lib/crypto/krb/prng.c | 11 ++++++++++-
- 1 file changed, 10 insertions(+), 1 deletion(-)
-
-diff --git a/src/lib/crypto/krb/prng.c b/src/lib/crypto/krb/prng.c
-index cb9ca9b98..f0e9984ca 100644
---- a/src/lib/crypto/krb/prng.c
-+++ b/src/lib/crypto/krb/prng.c
-@@ -26,6 +26,8 @@
- 
- #include "crypto_int.h"
- 
-+#include <openssl/rand.h>
-+
- krb5_error_code KRB5_CALLCONV
- krb5_c_random_seed(krb5_context context, krb5_data *data)
- {
-@@ -99,9 +101,16 @@ krb5_boolean
- k5_get_os_entropy(unsigned char *buf, size_t len, int strong)
- {
-     const char *device;
--#if defined(__linux__) && defined(SYS_getrandom)
-     int r;
- 
-+    /* A wild FIPS mode appeared! */
-+    if (FIPS_mode()) {
-+        /* The return codes on this API are not good */
-+        r = RAND_bytes(buf, len);
-+        return r == 1;
-+    }
-+
-+#if defined(__linux__) && defined(SYS_getrandom)
-     while (len > 0) {
-         /*
-          * Pull from the /dev/urandom pool, but require it to have been seeded.
diff --git a/SOURCES/krb5-1.17post2-DES-3DES-fixups.patch b/SOURCES/krb5-1.17post2-DES-3DES-fixups.patch
new file mode 100644
index 0000000..0fcb76c
--- /dev/null
+++ b/SOURCES/krb5-1.17post2-DES-3DES-fixups.patch
@@ -0,0 +1,103 @@
+From fe66536c1b7aec67233739df97cbe0301ee6475e Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Tue, 19 Nov 2019 15:03:19 -0500
+Subject: [PATCH] krb5-1.17post2 DES/3DES fixups
+
+Kept separate from the other patch because rawhide doesn't have DES.
+
+post2 adds krb5kdf workarounds.
+---
+ src/lib/crypto/krb/derive.c                | 6 +++++-
+ src/lib/crypto/openssl/enc_provider/des.c  | 9 +++++++++
+ src/lib/crypto/openssl/enc_provider/des3.c | 6 ++++++
+ 3 files changed, 20 insertions(+), 1 deletion(-)
+
+diff --git a/src/lib/crypto/krb/derive.c b/src/lib/crypto/krb/derive.c
+index 915a173dd..ebdab69bc 100644
+--- a/src/lib/crypto/krb/derive.c
++++ b/src/lib/crypto/krb/derive.c
+@@ -348,6 +348,7 @@ cleanup:
+     zapfree(prf.data, blocksize);
+     return ret;
+ }
++#endif /* OSSL_KDFS */
+ 
+ static krb5_error_code
+ builtin_derive_random_rfc3961(const struct krb5_enc_provider *enc,
+@@ -400,7 +401,6 @@ cleanup:
+     zapfree(block.data, blocksize);
+     return ret;
+ }
+-#endif /* OSSL_KDFS */
+ 
+ krb5_error_code
+ k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
+@@ -432,6 +432,10 @@ k5_derive_random_rfc3961(const struct krb5_enc_provider *enc,
+                          krb5_key inkey, krb5_data *outrnd,
+                          const krb5_data *in_constant)
+ {
++    /* DES (single and triple).  They'll be gone very soon. */
++    if (enc->keylength == 8 || enc->keylength == 24)
++        return builtin_derive_random_rfc3961(enc, inkey, outrnd, in_constant);
++
+ #ifdef OSSL_KDFS
+     return openssl_krb5kdf(enc, inkey, outrnd, in_constant);
+ #else
+diff --git a/src/lib/crypto/openssl/enc_provider/des.c b/src/lib/crypto/openssl/enc_provider/des.c
+index a662db512..7d17d287e 100644
+--- a/src/lib/crypto/openssl/enc_provider/des.c
++++ b/src/lib/crypto/openssl/enc_provider/des.c
+@@ -85,6 +85,9 @@ k5_des_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
+     EVP_CIPHER_CTX *ctx;
+     krb5_boolean empty;
+ 
++    if (FIPS_mode())
++        return KRB5_CRYPTO_INTERNAL;
++
+     ret = validate(key, ivec, data, num_data, &empty);
+     if (ret != 0 || empty)
+         return ret;
+@@ -133,6 +136,9 @@ k5_des_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
+     EVP_CIPHER_CTX *ctx;
+     krb5_boolean empty;
+ 
++    if (FIPS_mode())
++        return KRB5_CRYPTO_INTERNAL;
++
+     ret = validate(key, ivec, data, num_data, &empty);
+     if (ret != 0 || empty)
+         return ret;
+@@ -182,6 +188,9 @@ k5_des_cbc_mac(krb5_key key, const krb5_crypto_iov *data, size_t num_data,
+     DES_key_schedule sched;
+     krb5_boolean empty;
+ 
++    if (FIPS_mode())
++        return KRB5_CRYPTO_INTERNAL;
++
+     ret = validate(key, ivec, data, num_data, &empty);
+     if (ret != 0)
+         return ret;
+diff --git a/src/lib/crypto/openssl/enc_provider/des3.c b/src/lib/crypto/openssl/enc_provider/des3.c
+index 1c439c2cd..8be555a8d 100644
+--- a/src/lib/crypto/openssl/enc_provider/des3.c
++++ b/src/lib/crypto/openssl/enc_provider/des3.c
+@@ -84,6 +84,9 @@ k5_des3_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
+     EVP_CIPHER_CTX *ctx;
+     krb5_boolean empty;
+ 
++    if (FIPS_mode())
++        return KRB5_CRYPTO_INTERNAL;
++
+     ret = validate(key, ivec, data, num_data, &empty);
+     if (ret != 0 || empty)
+         return ret;
+@@ -133,6 +136,9 @@ k5_des3_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
+     EVP_CIPHER_CTX *ctx;
+     krb5_boolean empty;
+ 
++    if (FIPS_mode())
++        return KRB5_CRYPTO_INTERNAL;
++
+     ret = validate(key, ivec, data, num_data, &empty);
+     if (ret != 0 || empty)
+         return ret;
diff --git a/SOURCES/krb5-1.17post6-FIPS-with-PRNG-and-RADIUS-and-MD4.patch b/SOURCES/krb5-1.17post6-FIPS-with-PRNG-and-RADIUS-and-MD4.patch
new file mode 100644
index 0000000..308137f
--- /dev/null
+++ b/SOURCES/krb5-1.17post6-FIPS-with-PRNG-and-RADIUS-and-MD4.patch
@@ -0,0 +1,568 @@
+From 2a4e2418875b41c3273db5f6b4e9e1b01c1fb5ff Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Fri, 9 Nov 2018 15:12:21 -0500
+Subject: [PATCH] krb5-1.17post6 FIPS with PRNG and RADIUS and MD4
+
+NB: Use openssl's PRNG in FIPS mode and taint within krad.
+
+A lot of the FIPS error conditions from OpenSSL are incredibly
+mysterious (at best, things return NULL unexpectedly; at worst,
+internal assertions are tripped; most of the time, you just get
+ENOMEM).  In order to cope with this, we need to have some level of
+awareness of what we can and can't safely call.
+
+This will slow down some calls slightly (FIPS_mode() takes multiple
+locks), but not for any ciphers we care about - which is to say that
+AES is fine.  Shame about SPAKE though.
+
+post6 restores MD4 (and therefore keygen-only RC4).
+
+(cherry picked from commit 80b56b04d90fcacd9f78fed305c7d5528d863b38)
+---
+ src/lib/crypto/krb/prng.c                     | 11 ++++-
+ .../crypto/openssl/enc_provider/camellia.c    |  6 +++
+ src/lib/crypto/openssl/enc_provider/rc4.c     | 13 +++++-
+ .../crypto/openssl/hash_provider/hash_evp.c   | 12 +++++
+ src/lib/crypto/openssl/hmac.c                 |  6 ++-
+ src/lib/krad/attr.c                           | 45 ++++++++++++++-----
+ src/lib/krad/attrset.c                        |  5 ++-
+ src/lib/krad/internal.h                       | 13 +++++-
+ src/lib/krad/packet.c                         | 22 ++++-----
+ src/lib/krad/remote.c                         | 10 ++++-
+ src/lib/krad/t_attr.c                         |  3 +-
+ src/lib/krad/t_attrset.c                      |  4 +-
+ src/plugins/preauth/spake/spake_client.c      |  6 +++
+ src/plugins/preauth/spake/spake_kdc.c         |  6 +++
+ 14 files changed, 129 insertions(+), 33 deletions(-)
+
+diff --git a/src/lib/crypto/krb/prng.c b/src/lib/crypto/krb/prng.c
+index cb9ca9b98..f0e9984ca 100644
+--- a/src/lib/crypto/krb/prng.c
++++ b/src/lib/crypto/krb/prng.c
+@@ -26,6 +26,8 @@
+ 
+ #include "crypto_int.h"
+ 
++#include <openssl/rand.h>
++
+ krb5_error_code KRB5_CALLCONV
+ krb5_c_random_seed(krb5_context context, krb5_data *data)
+ {
+@@ -99,9 +101,16 @@ krb5_boolean
+ k5_get_os_entropy(unsigned char *buf, size_t len, int strong)
+ {
+     const char *device;
+-#if defined(__linux__) && defined(SYS_getrandom)
+     int r;
+ 
++    /* A wild FIPS mode appeared! */
++    if (FIPS_mode()) {
++        /* The return codes on this API are not good */
++        r = RAND_bytes(buf, len);
++        return r == 1;
++    }
++
++#if defined(__linux__) && defined(SYS_getrandom)
+     while (len > 0) {
+         /*
+          * Pull from the /dev/urandom pool, but require it to have been seeded.
+diff --git a/src/lib/crypto/openssl/enc_provider/camellia.c b/src/lib/crypto/openssl/enc_provider/camellia.c
+index 2da691329..f79679a0b 100644
+--- a/src/lib/crypto/openssl/enc_provider/camellia.c
++++ b/src/lib/crypto/openssl/enc_provider/camellia.c
+@@ -304,6 +304,9 @@ krb5int_camellia_cbc_mac(krb5_key key, const krb5_crypto_iov *data,
+     unsigned char blockY[CAMELLIA_BLOCK_SIZE], blockB[CAMELLIA_BLOCK_SIZE];
+     struct iov_cursor cursor;
+ 
++    if (FIPS_mode())
++        return KRB5_CRYPTO_INTERNAL;
++
+     if (output->length < CAMELLIA_BLOCK_SIZE)
+         return KRB5_BAD_MSIZE;
+ 
+@@ -331,6 +334,9 @@ static krb5_error_code
+ krb5int_camellia_init_state (const krb5_keyblock *key, krb5_keyusage usage,
+                              krb5_data *state)
+ {
++    if (FIPS_mode())
++        return KRB5_CRYPTO_INTERNAL;
++
+     state->length = 16;
+     state->data = (void *) malloc(16);
+     if (state->data == NULL)
+diff --git a/src/lib/crypto/openssl/enc_provider/rc4.c b/src/lib/crypto/openssl/enc_provider/rc4.c
+index a65d57b7a..6ccaca94a 100644
+--- a/src/lib/crypto/openssl/enc_provider/rc4.c
++++ b/src/lib/crypto/openssl/enc_provider/rc4.c
+@@ -66,6 +66,9 @@ k5_arcfour_docrypt(krb5_key key, const krb5_data *state, krb5_crypto_iov *data,
+     EVP_CIPHER_CTX *ctx = NULL;
+     struct arcfour_state *arcstate;
+ 
++    if (FIPS_mode())
++        return KRB5_CRYPTO_INTERNAL;
++
+     arcstate = (state != NULL) ? (void *)state->data : NULL;
+     if (arcstate != NULL) {
+         ctx = arcstate->ctx;
+@@ -113,7 +116,12 @@ k5_arcfour_docrypt(krb5_key key, const krb5_data *state, krb5_crypto_iov *data,
+ static void
+ k5_arcfour_free_state(krb5_data *state)
+ {
+-    struct arcfour_state *arcstate = (void *)state->data;
++    struct arcfour_state *arcstate;
++
++    if (FIPS_mode())
++        return;
++
++    arcstate = (void *) state->data;
+ 
+     EVP_CIPHER_CTX_free(arcstate->ctx);
+     free(arcstate);
+@@ -125,6 +133,9 @@ k5_arcfour_init_state(const krb5_keyblock *key,
+ {
+     struct arcfour_state *arcstate;
+ 
++    if (FIPS_mode())
++        return KRB5_CRYPTO_INTERNAL;
++
+     /*
+      * The cipher state here is a saved pointer to a struct arcfour_state
+      * object, rather than a flat byte array as in most enc providers.  The
+diff --git a/src/lib/crypto/openssl/hash_provider/hash_evp.c b/src/lib/crypto/openssl/hash_provider/hash_evp.c
+index 957ed8d9c..915da9dbe 100644
+--- a/src/lib/crypto/openssl/hash_provider/hash_evp.c
++++ b/src/lib/crypto/openssl/hash_provider/hash_evp.c
+@@ -49,6 +49,11 @@ hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
+     if (ctx == NULL)
+         return ENOMEM;
+ 
++    if (type == EVP_md4()) {
++        /* See comment below in hash_md4(). */
++        EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
++    }
++
+     ok = EVP_DigestInit_ex(ctx, type, NULL);
+     for (i = 0; i < num_data; i++) {
+         if (!SIGN_IOV(&data[i]))
+@@ -64,12 +69,19 @@ hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
+ static krb5_error_code
+ hash_md4(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
+ {
++    /*
++     * MD4 is needed in FIPS mode to perform key generation for RC4 keys used
++     * by IPA.  These keys are only used along a (separately) secured channel
++     * for legacy reasons when performing trusts to Active Directory.
++     */
+     return hash_evp(EVP_md4(), data, num_data, output);
+ }
+ 
+ static krb5_error_code
+ hash_md5(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
+ {
++    if (FIPS_mode())
++        return KRB5_CRYPTO_INTERNAL;
+     return hash_evp(EVP_md5(), data, num_data, output);
+ }
+ 
+diff --git a/src/lib/crypto/openssl/hmac.c b/src/lib/crypto/openssl/hmac.c
+index b2db6ec02..d94d9ac94 100644
+--- a/src/lib/crypto/openssl/hmac.c
++++ b/src/lib/crypto/openssl/hmac.c
+@@ -103,7 +103,11 @@ map_digest(const struct krb5_hash_provider *hash)
+         return EVP_sha256();
+     else if (!strncmp(hash->hash_name, "SHA-384",7))
+         return EVP_sha384();
+-    else if (!strncmp(hash->hash_name, "MD5", 3))
++
++    if (FIPS_mode())
++        return NULL;
++
++    if (!strncmp(hash->hash_name, "MD5", 3))
+         return EVP_md5();
+     else if (!strncmp(hash->hash_name, "MD4", 3))
+         return EVP_md4();
+diff --git a/src/lib/krad/attr.c b/src/lib/krad/attr.c
+index 9c13d9d75..275327e67 100644
+--- a/src/lib/krad/attr.c
++++ b/src/lib/krad/attr.c
+@@ -30,6 +30,7 @@
+ #include <k5-int.h>
+ #include "internal.h"
+ 
++#include <openssl/crypto.h>
+ #include <string.h>
+ 
+ /* RFC 2865 */
+@@ -38,7 +39,8 @@
+ typedef krb5_error_code
+ (*attribute_transform_fn)(krb5_context ctx, const char *secret,
+                           const unsigned char *auth, const krb5_data *in,
+-                          unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++                          unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
++                          krb5_boolean *is_fips);
+ 
+ typedef struct {
+     const char *name;
+@@ -51,12 +53,14 @@ typedef struct {
+ static krb5_error_code
+ user_password_encode(krb5_context ctx, const char *secret,
+                      const unsigned char *auth, const krb5_data *in,
+-                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
++                     krb5_boolean *is_fips);
+ 
+ static krb5_error_code
+ user_password_decode(krb5_context ctx, const char *secret,
+                      const unsigned char *auth, const krb5_data *in,
+-                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
++                     krb5_boolean *ignored);
+ 
+ static const attribute_record attributes[UCHAR_MAX] = {
+     {"User-Name", 1, MAX_ATTRSIZE, NULL, NULL},
+@@ -128,7 +132,8 @@ static const attribute_record attributes[UCHAR_MAX] = {
+ static krb5_error_code
+ user_password_encode(krb5_context ctx, const char *secret,
+                      const unsigned char *auth, const krb5_data *in,
+-                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
++                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
++                     krb5_boolean *is_fips)
+ {
+     const unsigned char *indx;
+     krb5_error_code retval;
+@@ -154,8 +159,14 @@ user_password_encode(krb5_context ctx, const char *secret,
+     for (blck = 0, indx = auth; blck * BLOCKSIZE < len; blck++) {
+         memcpy(tmp.data + seclen, indx, BLOCKSIZE);
+ 
+-        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
+-                                      &sum);
++        if (FIPS_mode()) {
++            /* Skip encryption here.  Taint so that we won't pass it out of
++             * the machine by accident. */
++            *is_fips = TRUE;
++            sum.contents = calloc(1, BLOCKSIZE);
++        } else
++            retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
++                                          &sum);
+         if (retval != 0) {
+             zap(tmp.data, tmp.length);
+             zap(outbuf, len);
+@@ -180,7 +191,8 @@ user_password_encode(krb5_context ctx, const char *secret,
+ static krb5_error_code
+ user_password_decode(krb5_context ctx, const char *secret,
+                      const unsigned char *auth, const krb5_data *in,
+-                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
++                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
++                     krb5_boolean *is_fips)
+ {
+     const unsigned char *indx;
+     krb5_error_code retval;
+@@ -204,8 +216,14 @@ user_password_decode(krb5_context ctx, const char *secret,
+     for (blck = 0, indx = auth; blck * BLOCKSIZE < in->length; blck++) {
+         memcpy(tmp.data + seclen, indx, BLOCKSIZE);
+ 
+-        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
+-                                      &tmp, &sum);
++        if (FIPS_mode()) {
++            /* Skip encryption here.  Taint so that we won't pass it out of
++             * the machine by accident. */
++            *is_fips = TRUE;
++            sum.contents = calloc(1, BLOCKSIZE);
++        } else
++            retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
++                                          &tmp, &sum);
+         if (retval != 0) {
+             zap(tmp.data, tmp.length);
+             zap(outbuf, in->length);
+@@ -248,7 +266,7 @@ krb5_error_code
+ kr_attr_encode(krb5_context ctx, const char *secret,
+                const unsigned char *auth, krad_attr type,
+                const krb5_data *in, unsigned char outbuf[MAX_ATTRSIZE],
+-               size_t *outlen)
++               size_t *outlen, krb5_boolean *is_fips)
+ {
+     krb5_error_code retval;
+ 
+@@ -265,7 +283,8 @@ kr_attr_encode(krb5_context ctx, const char *secret,
+         return 0;
+     }
+ 
+-    return attributes[type - 1].encode(ctx, secret, auth, in, outbuf, outlen);
++    return attributes[type - 1].encode(ctx, secret, auth, in, outbuf, outlen,
++                                       is_fips);
+ }
+ 
+ krb5_error_code
+@@ -274,6 +293,7 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
+                unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
+ {
+     krb5_error_code retval;
++    krb5_boolean ignored;
+ 
+     retval = kr_attr_valid(type, in);
+     if (retval != 0)
+@@ -288,7 +308,8 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
+         return 0;
+     }
+ 
+-    return attributes[type - 1].decode(ctx, secret, auth, in, outbuf, outlen);
++    return attributes[type - 1].decode(ctx, secret, auth, in, outbuf, outlen,
++                                       &ignored);
+ }
+ 
+ krad_attr
+diff --git a/src/lib/krad/attrset.c b/src/lib/krad/attrset.c
+index 03c613716..d89982a13 100644
+--- a/src/lib/krad/attrset.c
++++ b/src/lib/krad/attrset.c
+@@ -167,7 +167,8 @@ krad_attrset_copy(const krad_attrset *set, krad_attrset **copy)
+ krb5_error_code
+ kr_attrset_encode(const krad_attrset *set, const char *secret,
+                   const unsigned char *auth,
+-                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen)
++                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen,
++                  krb5_boolean *is_fips)
+ {
+     unsigned char buffer[MAX_ATTRSIZE];
+     krb5_error_code retval;
+@@ -181,7 +182,7 @@ kr_attrset_encode(const krad_attrset *set, const char *secret,
+ 
+     K5_TAILQ_FOREACH(a, &set->list, list) {
+         retval = kr_attr_encode(set->ctx, secret, auth, a->type, &a->attr,
+-                                buffer, &attrlen);
++                                buffer, &attrlen, is_fips);
+         if (retval != 0)
+             return retval;
+ 
+diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h
+index 996a89372..a53ce31ce 100644
+--- a/src/lib/krad/internal.h
++++ b/src/lib/krad/internal.h
+@@ -49,6 +49,13 @@
+ 
+ typedef struct krad_remote_st krad_remote;
+ 
++struct krad_packet_st {
++    char buffer[KRAD_PACKET_SIZE_MAX];
++    krad_attrset *attrset;
++    krb5_data pkt;
++    krb5_boolean is_fips;
++};
++
+ /* Validate constraints of an attribute. */
+ krb5_error_code
+ kr_attr_valid(krad_attr type, const krb5_data *data);
+@@ -57,7 +64,8 @@ kr_attr_valid(krad_attr type, const krb5_data *data);
+ krb5_error_code
+ kr_attr_encode(krb5_context ctx, const char *secret, const unsigned char *auth,
+                krad_attr type, const krb5_data *in,
+-               unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++               unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen,
++               krb5_boolean *is_fips);
+ 
+ /* Decode an attribute. */
+ krb5_error_code
+@@ -69,7 +77,8 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
+ krb5_error_code
+ kr_attrset_encode(const krad_attrset *set, const char *secret,
+                   const unsigned char *auth,
+-                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen);
++                  unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen,
++                  krb5_boolean *is_fips);
+ 
+ /* Decode attributes from a buffer. */
+ krb5_error_code
+diff --git a/src/lib/krad/packet.c b/src/lib/krad/packet.c
+index c597174b6..794ac84c4 100644
+--- a/src/lib/krad/packet.c
++++ b/src/lib/krad/packet.c
+@@ -32,6 +32,7 @@
+ #include <string.h>
+ 
+ #include <arpa/inet.h>
++#include <openssl/crypto.h>
+ 
+ typedef unsigned char uchar;
+ 
+@@ -53,12 +54,6 @@ typedef unsigned char uchar;
+ #define pkt_auth(p) ((uchar *)offset(&(p)->pkt, OFFSET_AUTH))
+ #define pkt_attr(p) ((unsigned char *)offset(&(p)->pkt, OFFSET_ATTR))
+ 
+-struct krad_packet_st {
+-    char buffer[KRAD_PACKET_SIZE_MAX];
+-    krad_attrset *attrset;
+-    krb5_data pkt;
+-};
+-
+ typedef struct {
+     uchar x[(UCHAR_MAX + 1) / 8];
+ } idmap;
+@@ -187,8 +182,13 @@ auth_generate_response(krb5_context ctx, const char *secret,
+     memcpy(data.data + response->pkt.length, secret, strlen(secret));
+ 
+     /* Hash it. */
+-    retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
+-                                  &hash);
++    if (FIPS_mode()) {
++        /* This checksum does very little security-wise anyway, so don't
++         * taint. */
++        hash.contents = calloc(1, AUTH_FIELD_SIZE);
++    } else
++        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
++                                      &hash);
+     free(data.data);
+     if (retval != 0)
+         return retval;
+@@ -276,7 +276,7 @@ krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
+ 
+     /* Encode the attributes. */
+     retval = kr_attrset_encode(set, secret, pkt_auth(pkt), pkt_attr(pkt),
+-                               &attrset_len);
++                               &attrset_len, &pkt->is_fips);
+     if (retval != 0)
+         goto error;
+ 
+@@ -314,7 +314,7 @@ krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
+ 
+     /* Encode the attributes. */
+     retval = kr_attrset_encode(set, secret, pkt_auth(request), pkt_attr(pkt),
+-                               &attrset_len);
++                               &attrset_len, &pkt->is_fips);
+     if (retval != 0)
+         goto error;
+ 
+@@ -451,6 +451,8 @@ krad_packet_decode_response(krb5_context ctx, const char *secret,
+ const krb5_data *
+ krad_packet_encode(const krad_packet *pkt)
+ {
++    if (pkt->is_fips)
++        return NULL;
+     return &pkt->pkt;
+ }
+ 
+diff --git a/src/lib/krad/remote.c b/src/lib/krad/remote.c
+index 437f7e91a..0f90443ce 100644
+--- a/src/lib/krad/remote.c
++++ b/src/lib/krad/remote.c
+@@ -263,7 +263,7 @@ on_io_write(krad_remote *rr)
+     request *r;
+ 
+     K5_TAILQ_FOREACH(r, &rr->list, list) {
+-        tmp = krad_packet_encode(r->request);
++        tmp = &r->request->pkt;
+ 
+         /* If the packet has already been sent, do nothing. */
+         if (r->sent == tmp->length)
+@@ -359,7 +359,7 @@ on_io_read(krad_remote *rr)
+     if (req != NULL) {
+         K5_TAILQ_FOREACH(r, &rr->list, list) {
+             if (r->request == req &&
+-                r->sent == krad_packet_encode(req)->length) {
++                r->sent == req->pkt.length) {
+                 request_finish(r, 0, rsp);
+                 break;
+             }
+@@ -455,6 +455,12 @@ kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
+                                      (krad_packet_iter_cb)iterator, &r, &tmp);
+     if (retval != 0)
+         goto error;
++    else if (tmp->is_fips && rr->info->ai_family != AF_LOCAL &&
++        rr->info->ai_family != AF_UNIX) {
++        /* This would expose cleartext passwords, so abort. */
++        retval = ESOCKTNOSUPPORT;
++        goto error;
++    }
+ 
+     K5_TAILQ_FOREACH(r, &rr->list, list) {
+         if (r->request == tmp) {
+diff --git a/src/lib/krad/t_attr.c b/src/lib/krad/t_attr.c
+index eb2a780c8..4d285ad9d 100644
+--- a/src/lib/krad/t_attr.c
++++ b/src/lib/krad/t_attr.c
+@@ -50,6 +50,7 @@ main()
+     const char *tmp;
+     krb5_data in;
+     size_t len;
++    krb5_boolean is_fips = FALSE;
+ 
+     noerror(krb5_init_context(&ctx));
+ 
+@@ -73,7 +74,7 @@ main()
+     in = string2data((char *)decoded);
+     retval = kr_attr_encode(ctx, secret, auth,
+                             krad_attr_name2num("User-Password"),
+-                            &in, outbuf, &len);
++                            &in, outbuf, &len, &is_fips);
+     insist(retval == 0);
+     insist(len == sizeof(encoded));
+     insist(memcmp(outbuf, encoded, len) == 0);
+diff --git a/src/lib/krad/t_attrset.c b/src/lib/krad/t_attrset.c
+index 7928335ca..0f9576253 100644
+--- a/src/lib/krad/t_attrset.c
++++ b/src/lib/krad/t_attrset.c
+@@ -49,6 +49,7 @@ main()
+     krb5_context ctx;
+     size_t len = 0, encode_len;
+     krb5_data tmp;
++    krb5_boolean is_fips = FALSE;
+ 
+     noerror(krb5_init_context(&ctx));
+     noerror(krad_attrset_new(ctx, &set));
+@@ -62,7 +63,8 @@ main()
+     noerror(krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp));
+ 
+     /* Encode attrset. */
+-    noerror(kr_attrset_encode(set, "foo", auth, buffer, &encode_len));
++    noerror(kr_attrset_encode(set, "foo", auth, buffer, &encode_len,
++                              &is_fips));
+     krad_attrset_free(set);
+ 
+     /* Manually encode User-Name. */
+diff --git a/src/plugins/preauth/spake/spake_client.c b/src/plugins/preauth/spake/spake_client.c
+index 00734a13b..a3ce22b70 100644
+--- a/src/plugins/preauth/spake/spake_client.c
++++ b/src/plugins/preauth/spake/spake_client.c
+@@ -38,6 +38,8 @@
+ #include "groups.h"
+ #include <krb5/clpreauth_plugin.h>
+ 
++#include <openssl/crypto.h>
++
+ typedef struct reqstate_st {
+     krb5_pa_spake *msg;         /* set in prep_questions, used in process */
+     krb5_keyblock *initial_key;
+@@ -375,6 +377,10 @@ clpreauth_spake_initvt(krb5_context context, int maj_ver, int min_ver,
+ 
+     if (maj_ver != 1)
+         return KRB5_PLUGIN_VER_NOTSUPP;
++
++    if (FIPS_mode())
++        return KRB5_CRYPTO_INTERNAL;
++
+     vt = (krb5_clpreauth_vtable)vtable;
+     vt->name = "spake";
+     vt->pa_type_list = pa_types;
+diff --git a/src/plugins/preauth/spake/spake_kdc.c b/src/plugins/preauth/spake/spake_kdc.c
+index 59e88409e..1b3e569e9 100644
+--- a/src/plugins/preauth/spake/spake_kdc.c
++++ b/src/plugins/preauth/spake/spake_kdc.c
+@@ -41,6 +41,8 @@
+ 
+ #include <krb5/kdcpreauth_plugin.h>
+ 
++#include <openssl/crypto.h>
++
+ /*
+  * The SPAKE kdcpreauth module uses a secure cookie containing the following
+  * concatenated fields (all integer fields are big-endian):
+@@ -578,6 +580,10 @@ kdcpreauth_spake_initvt(krb5_context context, int maj_ver, int min_ver,
+ 
+     if (maj_ver != 1)
+         return KRB5_PLUGIN_VER_NOTSUPP;
++
++    if (FIPS_mode())
++        return KRB5_CRYPTO_INTERNAL;
++
+     vt = (krb5_kdcpreauth_vtable)vtable;
+     vt->name = "spake";
+     vt->pa_type_list = pa_types;
diff --git a/SPECS/krb5.spec b/SPECS/krb5.spec
index c37a2ec..2e014c3 100644
--- a/SPECS/krb5.spec
+++ b/SPECS/krb5.spec
@@ -18,12 +18,12 @@ Summary: The Kerberos network authentication system
 Name: krb5
 Version: 1.17
 # for prerelease, should be e.g., 0.% {prerelease}.1% { ?dist } (without spaces)
-Release: 7%{?dist}
+Release: 17%{?dist}
 
 # lookaside-cached sources; two downloads and a build artifact
-Source0: https://web.mit.edu/kerberos/dist/krb5/1.16/krb5-%{version}%{prerelease}.tar.gz
+Source0: https://web.mit.edu/kerberos/dist/krb5/1.17/krb5-%{version}%{prerelease}.tar.gz
 # rharwood has trust path to signing key and verifies on check-in
-Source1: https://web.mit.edu/kerberos/dist/krb5/1.16/krb5-%{version}%{prerelease}.tar.gz.asc
+Source1: https://web.mit.edu/kerberos/dist/krb5/1.17/krb5-%{version}%{prerelease}.tar.gz.asc
 # This source is generated during the build because it is documentation.
 # To override this behavior (e.g., new upstream version), do:
 #     tar cfT krb5-1.15.2-pdfs.tar /dev/null
@@ -57,14 +57,25 @@ Patch33: krb5-1.13-dirsrv-accountlock.patch
 Patch34: krb5-1.9-debuginfo.patch
 Patch35: krb5-1.11-run_user_0.patch
 Patch36: krb5-1.11-kpasswdtest.patch
-Patch87: In-FIPS-mode-add-plaintext-fallback-for-RC4-usages-a.patch
 Patch88: Add-tests-for-KCM-ccache-type.patch
 Patch89: Properly-size-ifdef-in-k5_cccol_lock.patch
 Patch90: Fix-memory-leak-in-none-replay-cache-type.patch
 Patch91: Address-some-optimized-out-memset-calls.patch
-Patch92: Use-openssl-s-PRNG-in-FIPS-mode.patch
-Patch93: Become-FIPS-aware-with-3DES.patch
-Patch94: FIPS-aware-SPAKE-group-negotiation.patch
+Patch95: In-rd_req_dec-always-log-non-permitted-enctypes.patch
+Patch96: In-kpropd-debug-log-proper-ticket-enctype-names.patch
+Patch97: Make-etype-names-in-KDC-logs-human-readable.patch
+Patch98: Mark-deprecated-enctypes-when-used.patch
+Patch99: Add-function-and-enctype-flag-for-deprecations.patch
+Patch100: Fix-argument-order-on-strlcpy-in-enctype_name.patch
+Patch101: Fix-KCM-client-time-offset-propagation.patch
+Patch102: Add-soft-pkcs11-source-code.patch
+Patch103: Use-imported-soft-pkcs11-for-tests.patch
+Patch104: Fix-Coverity-defects-in-soft-pkcs11-test-code.patch
+Patch105: Fix-memory-leaks-in-soft-pkcs11-code.patch
+Patch106: Avoid-alignment-warnings-in-openssl-rc4.c.patch
+Patch107: Use-backported-version-of-OpenSSL-3-KDF-interface.patch
+Patch108: krb5-1.17post6-FIPS-with-PRNG-and-RADIUS-and-MD4.patch
+Patch109: krb5-1.17post2-DES-3DES-fixups.patch
 
 License: MIT
 URL: http://web.mit.edu/kerberos/www/
@@ -77,12 +88,12 @@ BuildRequires: gzip, ncurses-devel
 BuildRequires: python3-sphinx, texlive-pdftex, latexmk
 BuildRequires: libverto-devel
 BuildRequires: openldap-devel
-BuildRequires: openssl-devel >= 0.9.8
 BuildRequires: python3
 BuildRequires: keyutils, keyutils-libs-devel >= 1.5.8
 BuildRequires: libselinux-devel
 BuildRequires: pam-devel
 BuildRequires: tcl-devel
+BuildRequires: openssl-devel >= 1:1.1.1c-4
 
 # For autosetup
 BuildRequires: git
@@ -147,6 +158,7 @@ Summary: The non-admin shared libraries used by Kerberos 5
 Group: System Environment/Libraries
 Requires: coreutils, gawk, grep, sed
 Requires: keyutils-libs >= 1.5.8
+Requires: openssl-libs >= 1:1.1.1c-4
 Requires: /etc/crypto-policies/back-ends/krb5.config
 
 %description libs
@@ -677,6 +689,46 @@ exit 0
 %{_libdir}/libkadm5srv_mit.so.*
 
 %changelog
+* Thu Nov 21 2019 Robbie Harwood <rharwood@redhat.com> - 1.17-17
+- OpenSSL has an epoch, apparently
+- Resolves: #1754690
+
+* Wed Nov 20 2019 Robbie Harwood <rharwood@redhat.com> - 1.17-16
+- Put openssl runtime requirement in the right place this time
+- Resolves: #1754690
+
+* Wed Nov 20 2019 Robbie Harwood <rharwood@redhat.com> - 1.17-15
+- Restore accidentally dropped patch
+- Resolves: #1754690
+
+* Wed Nov 20 2019 Robbie Harwood <rharwood@redhat.com> - 1.17-14
+- Fix krb5kdf support and add proper openssl version requirements
+- Resolves: #1754690
+
+* Tue Nov 19 2019 Robbie Harwood <rharwood@redhat.com> - 1.17-13
+- Full FIPS compliance
+- Resolves: #1754690
+
+* Tue Oct 15 2019 Robbie Harwood <rharwood@redhat.com> - 1.17-12
+- Backport soft-pkcs11 testing code
+- Resolves: #1734158
+
+* Tue Oct 15 2019 Robbie Harwood <rharwood@redhat.com> - 1.17-11
+- Fix KCM client time offset propagation
+- Resolves: #1738553
+
+* Tue Oct 15 2019 Robbie Harwood <rharwood@redhat.com> - 1.17-10
+- Fix source URLs in spec file
+- Resolves: #1755959
+
+* Tue Sep 17 2019 Robbie Harwood <rharwood@redhat.com> - 1.17-9
+- Fix argument order on strlcpy() in enctype_name()
+- Resolves: #1754369
+
+* Wed Aug 07 2019 Robbie Harwood <rharwood@redhat.com> - 1.17-8
+- Clean up etype display on KDC
+- Resolves: #1664157
+
 * Tue Jun 04 2019 Robbie Harwood <rharwood@redhat.com> - 1.17-7
 - Fix pkinit_anchors path
 - Resolves: #1661339