diff --git a/SOURCES/Add-function-and-enctype-flag-for-deprecations.patch b/SOURCES/Add-function-and-enctype-flag-for-deprecations.patch
index 99290da..2a094e7 100644
--- a/SOURCES/Add-function-and-enctype-flag-for-deprecations.patch
+++ b/SOURCES/Add-function-and-enctype-flag-for-deprecations.patch
@@ -1,4 +1,4 @@
-From 656fb920da2d6be3c55976320e3e13a69af30c8a Mon Sep 17 00:00:00 2001
+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
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
index 9edc3ae..99648bd 100644
--- a/SOURCES/Fix-argument-order-on-strlcpy-in-enctype_name.patch
+++ b/SOURCES/Fix-argument-order-on-strlcpy-in-enctype_name.patch
@@ -1,4 +1,4 @@
-From 3a64eb3477e0e60d447c99f86b7bf4fd1259cb58 Mon Sep 17 00:00:00 2001
+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()
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
index 7c83389..8a20e3d 100644
--- a/SOURCES/In-kpropd-debug-log-proper-ticket-enctype-names.patch
+++ b/SOURCES/In-kpropd-debug-log-proper-ticket-enctype-names.patch
@@ -1,4 +1,4 @@
-From e07ddc519254a35e54af020643685b9da9e0e973 Mon Sep 17 00:00:00 2001
+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
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
index 51115f6..6ca3da0 100644
--- a/SOURCES/In-rd_req_dec-always-log-non-permitted-enctypes.patch
+++ b/SOURCES/In-rd_req_dec-always-log-non-permitted-enctypes.patch
@@ -1,4 +1,4 @@
-From 83c2f0bac35c2ad0872dc9b2a9ca1bdde948f216 Mon Sep 17 00:00:00 2001
+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
diff --git a/SOURCES/Make-etype-names-in-KDC-logs-human-readable.patch b/SOURCES/Make-etype-names-in-KDC-logs-human-readable.patch
index b2e21f3..8b3413b 100644
--- a/SOURCES/Make-etype-names-in-KDC-logs-human-readable.patch
+++ b/SOURCES/Make-etype-names-in-KDC-logs-human-readable.patch
@@ -1,4 +1,4 @@
-From c3dd133cf06d55e3fe516a2aa8b4b37e203878da Mon Sep 17 00:00:00 2001
+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
@@ -12,9 +12,9 @@ ticket: 8772 (new)
 (cherry picked from commit a649279727490687d54becad91fde8cf7429d951)
 ---
  src/kdc/kdc_log.c  |  42 +++++++--------
- src/kdc/kdc_util.c | 125 +++++++++++++++++++++++----------------------
+ src/kdc/kdc_util.c | 131 +++++++++++++++++++++++----------------------
  src/kdc/kdc_util.h |   6 +--
- 3 files changed, 87 insertions(+), 86 deletions(-)
+ 3 files changed, 90 insertions(+), 89 deletions(-)
 
 diff --git a/src/kdc/kdc_log.c b/src/kdc/kdc_log.c
 index 4eec50373..b160ba21a 100644
@@ -132,75 +132,40 @@ index 0155c28c6..f5c581c82 100644
 - * 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)
++/* 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)
  {
-+    struct k5buf buf;
-     int i;
+-    int i;
 -    char stmp[D_LEN(krb5_enctype) + 1];
 -    char *p;
-+    char name[64];
++    char *name;
  
 -    if (nktypes < 0
 -        || len < (sizeof(" etypes {...}") + D_LEN(int))) {
 -        *s = '\0';
 -        return;
 -    }
-+    if (nktypes < 0)
-+        return NULL;
++    if (buflen == 0)
++        return EINVAL;
++    *buf = '\0'; /* ensure these are always valid C-strings */
  
 -    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++) {
+-    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.
@@ -218,27 +183,65 @@ index 0155c28c6..f5c581c82 100644
 -    }
 -    strlcat(s, "}", len);
 -    return;
-+    k5_buf_add(&buf, "}");
-+    return buf.data;
++    /* 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);
++
++    if (strlcpy(name, buf, buflen) >= buflen)
++        return ENOMEM;
++    return 0;
  }
  
 -void
 -rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep)
 +char *
-+rep_etypes2str(krb5_kdc_rep *rep)
++ktypes2str(krb5_enctype *ktype, int nktypes)
  {
 -    char stmp[sizeof("ses=") + D_LEN(krb5_enctype)];
--
++    struct k5buf buf;
++    int i;
++    char name[64];
+ 
 -    if (len < (3 * D_LEN(krb5_enctype)
 -               + sizeof("etypes {rep= tkt= ses=}"))) {
 -        *s = '\0';
 -        return;
--    }
++    if (nktypes < 0)
++        return NULL;
++
++    k5_buf_init_dynamic(&buf);
++    k5_buf_add_fmt(&buf, "%d etypes {", nktypes);
++    for (i = 0; i < nktypes; i++) {
++        enctype_name(ktype[i], name, sizeof(name));
++        k5_buf_add_fmt(&buf, "%s%s(%ld)", i ? ", " : "", name, (long)ktype[i]);
+     }
++    k5_buf_add(&buf, "}");
++    return buf.data;
++}
+ 
+-    snprintf(s, len, "etypes {rep=%ld", (long)rep->enc_part.enctype);
++char *
++rep_etypes2str(krb5_kdc_rep *rep)
++{
 +    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));
diff --git a/SOURCES/Mark-deprecated-enctypes-when-used.patch b/SOURCES/Mark-deprecated-enctypes-when-used.patch
index c638cce..4ded20a 100644
--- a/SOURCES/Mark-deprecated-enctypes-when-used.patch
+++ b/SOURCES/Mark-deprecated-enctypes-when-used.patch
@@ -1,4 +1,4 @@
-From 4f56267a204764ed3d00e69cd16a0e877f055455 Mon Sep 17 00:00:00 2001
+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
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/Put-KDB-authdata-first.patch b/SOURCES/Put-KDB-authdata-first.patch
new file mode 100644
index 0000000..998fa0e
--- /dev/null
+++ b/SOURCES/Put-KDB-authdata-first.patch
@@ -0,0 +1,45 @@
+From 684821fc68fd27ddcc5f809a37819edd35365a9d Mon Sep 17 00:00:00 2001
+From: Isaac Boukris <iboukris@gmail.com>
+Date: Sat, 1 Feb 2020 16:13:30 +0100
+Subject: [PATCH] Put KDB authdata first
+
+Windows services, as well as some versions of Samba, may refuse
+tickets if the PAC is not in the first AD-IF-RELEVANT container.  In
+fetch_kdb_authdata(), change the merge order so that authdata from the
+KDB module appears first.
+
+[ghudson@mit.edu: added comment and clarified commit message]
+
+ticket: 8872 (new)
+tags: pullup
+target_version: 1.18
+target_version: 1.17-next
+
+(cherry picked from commit 331fa4bdd34263ea20667a0f51338cb84357fdaa)
+(cherry picked from commit 1678270de3fda699114122447b1f06b08fb4e53e)
+---
+ src/kdc/kdc_authdata.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c
+index 1b067cb0b..616c3eadc 100644
+--- a/src/kdc/kdc_authdata.c
++++ b/src/kdc/kdc_authdata.c
+@@ -383,11 +383,14 @@ fetch_kdb_authdata(krb5_context context, unsigned int flags,
+     if (ret)
+         return (ret == KRB5_PLUGIN_OP_NOTSUPP) ? 0 : ret;
+ 
+-    /* Add the KDB authdata to the ticket, without copying or filtering. */
+-    ret = merge_authdata(context, db_authdata,
+-                         &enc_tkt_reply->authorization_data, FALSE, FALSE);
++    /* Put the KDB authdata first in the ticket.  A successful merge places the
++     * combined list in db_authdata and releases the old ticket authdata. */
++    ret = merge_authdata(context, enc_tkt_reply->authorization_data,
++                         &db_authdata, FALSE, FALSE);
+     if (ret)
+         krb5_free_authdata(context, db_authdata);
++    else
++        enc_tkt_reply->authorization_data = db_authdata;
+     return ret;
+ }
+ 
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..b8d3916
--- /dev/null
+++ b/SOURCES/Use-backported-version-of-OpenSSL-3-KDF-interface.patch
@@ -0,0 +1,751 @@
+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                   | 356 +++++++++++++-----
+ .../preauth/pkinit/pkinit_crypto_openssl.c    | 257 ++++++++-----
+ 3 files changed, 428 insertions(+), 189 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_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;
++}
++
++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;
++}
++
++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;
++    }
++
++    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)
++        zap(outrnd->data, outrnd->length);
++    EVP_KDF_CTX_free(ctx);
++    return ret;
++}
++
++#else /* OSSL_KDFS */
++
++/*
++ * 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 block = empty_data();
++    krb5_data prf;
++    unsigned char ibuf[4], lbuf[4];
+ 
+-    blocksize = enc->block_size;
+-    keybytes = enc->keybytes;
+-
+-    if (blocksize == 1)
+-        return KRB5_BAD_ENCTYPE;
+-    if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
++    if (hash == NULL || outrnd->length > hash->hashsize)
+         return KRB5_CRYPTO_INTERNAL;
+ 
+     /* Allocate encryption data buffer. */
+-    ret = alloc_data(&block, blocksize);
++    ret = alloc_data(&prf, hash->hashsize);
+     if (ret)
+         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);
+-    }
++    /* [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);
+ 
+-    /* 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);
++    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,56 +349,94 @@ 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.
+- */
++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)
++{
++    size_t blocksize, keybytes, n;
++    krb5_error_code ret;
++    krb5_data block = empty_data();
++
++    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(&block, blocksize);
++    if (ret)
++        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);
++    }
++
++    /* 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)
+ {
+-    krb5_crypto_iov iov[5];
+-    krb5_error_code ret;
+-    krb5_data prf;
+-    unsigned char ibuf[4], lbuf[4];
++#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
++}
+ 
+-    if (hash == NULL || outrnd->length > hash->hashsize)
+-        return KRB5_CRYPTO_INTERNAL;
++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
++}
+ 
+-    /* Allocate encryption data buffer. */
+-    ret = alloc_data(&prf, hash->hashsize);
+-    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);
+-
+-    ret = krb5int_hmac(hash, inkey, iov, 5, &prf);
+-    if (!ret)
+-        memcpy(outrnd->data, prf.data, outrnd->length);
+-    zapfree(prf.data, prf.length);
+-    return ret;
++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
+@@ -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;
+ 
+-/* 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:
++    /* 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;
++    }
++
++    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 afd52e3..b285a4b 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: 9%{?dist}
+Release: 18%{?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,20 +57,26 @@ 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
+Patch110: Put-KDB-authdata-first.patch
 
 License: MIT
 URL: http://web.mit.edu/kerberos/www/
@@ -83,12 +89,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
@@ -153,6 +159,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
@@ -683,6 +690,42 @@ exit 0
 %{_libdir}/libkadm5srv_mit.so.*
 
 %changelog
+* Wed Feb 19 2020 Robbie Harwood <rharwood@redhat.com> - 1.17-18
+- Put KDB authdata first
+- Resolves: #1800575
+
+* 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