diff --git a/Add-APIs-for-marshalling-credentials.patch b/Add-APIs-for-marshalling-credentials.patch index 105f358..7d77bba 100644 --- a/Add-APIs-for-marshalling-credentials.patch +++ b/Add-APIs-for-marshalling-credentials.patch @@ -1,4 +1,4 @@ -From 4505316756e42db02b6dabe0a6b075fe52852371 Mon Sep 17 00:00:00 2001 +From 3d11179707923b033fa413387a33296b673ff52d Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Thu, 14 Jan 2021 18:13:09 -0500 Subject: [PATCH] Add APIs for marshalling credentials diff --git a/Add-buildsystem-detection-of-the-OpenSSL-3-KDF-inter.patch b/Add-buildsystem-detection-of-the-OpenSSL-3-KDF-inter.patch new file mode 100644 index 0000000..95c9867 --- /dev/null +++ b/Add-buildsystem-detection-of-the-OpenSSL-3-KDF-inter.patch @@ -0,0 +1,25 @@ +From c76a5a01a70733c972627df0bdaa2757d323315c Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Fri, 20 Sep 2019 16:11:29 -0400 +Subject: [PATCH] Add buildsystem detection of the OpenSSL-3 KDF interface + +(cherry picked from commit a3e03dfd40928c4615bd9b8546eac0c104377850) +--- + src/configure.ac | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/configure.ac b/src/configure.ac +index eb6307468..9c2e816fe 100644 +--- a/src/configure.ac ++++ b/src/configure.ac +@@ -282,6 +282,10 @@ AC_SUBST(CRYPTO_IMPL) + AC_SUBST(CRYPTO_IMPL_CFLAGS) + AC_SUBST(CRYPTO_IMPL_LIBS) + ++if test "$CRYPTO_IMPL" = openssl; then ++ AC_CHECK_FUNCS(EVP_KDF_fetch) ++fi ++ + AC_ARG_WITH([prng-alg], + AC_HELP_STRING([--with-prng-alg=ALG], [use specified PRNG algorithm. @<:@fortuna@:>@]), + [PRNG_ALG=$withval diff --git a/Add-hostname-canonicalization-helper-to-k5test.py.patch b/Add-hostname-canonicalization-helper-to-k5test.py.patch index 501984f..2af7a11 100644 --- a/Add-hostname-canonicalization-helper-to-k5test.py.patch +++ b/Add-hostname-canonicalization-helper-to-k5test.py.patch @@ -1,4 +1,4 @@ -From d898d94cef8e1a8772a91cd3a62255c33f109636 Mon Sep 17 00:00:00 2001 +From c76a01279bbbbcfd296d2ead8f6e2a5bee7e8443 Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Fri, 15 Jan 2021 14:43:34 -0500 Subject: [PATCH] Add hostname canonicalization helper to k5test.py diff --git a/Fix-softpkcs11-build-issues-with-openssl-3.0.patch b/Fix-softpkcs11-build-issues-with-openssl-3.0.patch new file mode 100644 index 0000000..3abbf47 --- /dev/null +++ b/Fix-softpkcs11-build-issues-with-openssl-3.0.patch @@ -0,0 +1,523 @@ +From 637773266d74864118d4ae4c6ca2c7f836b400cd Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Sat, 15 May 2021 17:35:25 -0400 +Subject: [PATCH] Fix softpkcs11 build issues with openssl 3.0 + +EVP_PKEY_get0_RSA() has been modified to have const return type. Remove +its usages in favor of the EVP_PKEY interface. Also remove calls to +RSA_blinding_off(), which we don't need and would require a non-const +object. + +Since softpkcs11 doesn't link against krb5 and can't use zap(), allocate +buffers with OPENSSL_malloc() so can use OPENSSL_clear_free(). + +Move several argument validation checks to the top of their functions. + +Fix an incorrect log message (public vs. private key encryption). + +(cherry picked from commit 8a0a2ab0296835380aede3bc190b7d10e2b162aa) +--- + src/tests/softpkcs11/main.c | 306 +++++++++++++++--------------------- + 1 file changed, 128 insertions(+), 178 deletions(-) + +diff --git a/src/tests/softpkcs11/main.c b/src/tests/softpkcs11/main.c +index 1cccdfb43..500e3093d 100644 +--- a/src/tests/softpkcs11/main.c ++++ b/src/tests/softpkcs11/main.c +@@ -375,10 +375,9 @@ add_st_object(void) + return NULL; + soft_token.object.objs = objs; + +- o = malloc(sizeof(*o)); ++ o = calloc(1, sizeof(*o)); + if (o == NULL) + return NULL; +- memset(o, 0, sizeof(*o)); + o->attrs = NULL; + o->num_attributes = 0; + o->object_handle = soft_token.object.num_objs; +@@ -424,7 +423,7 @@ 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 RSA *rsa; + const BIGNUM *n, *e; + + rsa = EVP_PKEY_get0_RSA(key); +@@ -445,8 +444,6 @@ 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(rsa, RSA_PKCS1_OpenSSL()); +- + free(modulus); + free(exponent); + } +@@ -679,10 +676,6 @@ add_certificate(char *label, + } else { + /* XXX verify keytype */ + +- if (key_type == CKK_RSA) +- 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); + o->u.private_key.key = NULL; +@@ -695,7 +688,7 @@ add_certificate(char *label, + } + + ret = CKR_OK; +- out: ++out: + if (ret != CKR_OK) { + st_logf("something went wrong when adding cert!\n"); + +@@ -1224,8 +1217,6 @@ C_Login(CK_SESSION_HANDLE hSession, + } + + /* XXX check keytype */ +- 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); +@@ -1495,8 +1486,9 @@ C_Encrypt(CK_SESSION_HANDLE hSession, + struct st_object *o; + void *buffer = NULL; + CK_RV ret; +- RSA *rsa; +- int padding, len, buffer_len, padding_len; ++ size_t buffer_len; ++ int padding, padding_len; ++ EVP_PKEY_CTX *ctx = NULL; + + st_logf("Encrypt\n"); + +@@ -1512,22 +1504,18 @@ C_Encrypt(CK_SESSION_HANDLE hSession, + return CKR_ARGUMENTS_BAD; + } + +- rsa = EVP_PKEY_get0_RSA(o->u.public_key); +- +- 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; ++ if (pulEncryptedDataLen == NULL) { ++ st_logf("pulEncryptedDataLen NULL\n"); ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ if (pData == NULL) { ++ st_logf("data NULL\n"); ++ ret = CKR_ARGUMENTS_BAD; + goto out; + } + +- ret = CKR_OK; + switch(state->encrypt_mechanism->mechanism) { + case CKM_RSA_PKCS: + padding = RSA_PKCS1_PADDING; +@@ -1542,40 +1530,41 @@ C_Encrypt(CK_SESSION_HANDLE hSession, + goto out; + } + ++ ctx = EVP_PKEY_CTX_new(o->u.public_key, NULL); ++ if (ctx == NULL || EVP_PKEY_encrypt_init(ctx) <= 0 || ++ EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 || ++ EVP_PKEY_encrypt(ctx, NULL, &buffer_len, pData, ulDataLen) <= 0) { ++ ret = CKR_DEVICE_ERROR; ++ goto out; ++ } ++ + if ((CK_ULONG)buffer_len + padding_len < ulDataLen) { + ret = CKR_ARGUMENTS_BAD; + goto out; + } + +- if (pulEncryptedDataLen == NULL) { +- st_logf("pulEncryptedDataLen NULL\n"); +- ret = CKR_ARGUMENTS_BAD; ++ buffer = OPENSSL_malloc(buffer_len); ++ if (buffer == NULL) { ++ ret = CKR_DEVICE_MEMORY; + 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) { ++ if (EVP_PKEY_encrypt(ctx, buffer, &buffer_len, pData, ulDataLen) <= 0) { + ret = CKR_DEVICE_ERROR; + goto out; + } +- if (len > buffer_len) +- abort(); ++ st_logf("Encrypt done\n"); + +- if (pEncryptedData != NULL_PTR) +- memcpy(pEncryptedData, buffer, len); +- *pulEncryptedDataLen = len; ++ if (pEncryptedData != NULL) ++ memcpy(pEncryptedData, buffer, buffer_len); ++ *pulEncryptedDataLen = buffer_len; + +- out: +- if (buffer) { +- memset(buffer, 0, buffer_len); +- free(buffer); +- } ++ ret = CKR_OK; ++out: ++ if (buffer != NULL) ++ OPENSSL_clear_free(buffer, buffer_len); ++ if (ctx != NULL) ++ EVP_PKEY_CTX_free(ctx); + return ret; + } + +@@ -1646,8 +1635,9 @@ C_Decrypt(CK_SESSION_HANDLE hSession, + struct st_object *o; + void *buffer = NULL; + CK_RV ret; +- RSA *rsa; +- int padding, len, buffer_len, padding_len; ++ size_t buffer_len; ++ int padding, padding_len; ++ EVP_PKEY_CTX *ctx = NULL; + + st_logf("Decrypt\n"); + +@@ -1663,22 +1653,18 @@ C_Decrypt(CK_SESSION_HANDLE hSession, + return CKR_ARGUMENTS_BAD; + } + +- rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); +- +- 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; ++ 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; + } + +- ret = CKR_OK; + switch(state->decrypt_mechanism->mechanism) { + case CKM_RSA_PKCS: + padding = RSA_PKCS1_PADDING; +@@ -1693,41 +1679,43 @@ C_Decrypt(CK_SESSION_HANDLE hSession, + goto out; + } + ++ ctx = EVP_PKEY_CTX_new(o->u.private_key.key, NULL); ++ if (ctx == NULL || EVP_PKEY_decrypt_init(ctx) <= 0 || ++ EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 || ++ EVP_PKEY_decrypt(ctx, NULL, &buffer_len, pEncryptedData, ++ ulEncryptedDataLen) <= 0) { ++ ret = CKR_DEVICE_ERROR; ++ goto out; ++ } ++ + if ((CK_ULONG)buffer_len + padding_len < ulEncryptedDataLen) { + ret = CKR_ARGUMENTS_BAD; + goto out; + } + +- if (pulDataLen == NULL) { +- st_logf("pulDataLen NULL\n"); +- ret = CKR_ARGUMENTS_BAD; ++ buffer = OPENSSL_malloc(buffer_len); ++ if (buffer == NULL) { ++ ret = CKR_DEVICE_MEMORY; + 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) { ++ if (EVP_PKEY_decrypt(ctx, buffer, &buffer_len, pEncryptedData, ++ ulEncryptedDataLen) <= 0) { + ret = CKR_DEVICE_ERROR; + goto out; + } +- if (len > buffer_len) +- abort(); ++ st_logf("Decrypt done\n"); + + if (pData != NULL_PTR) +- memcpy(pData, buffer, len); +- *pulDataLen = len; ++ memcpy(pData, buffer, buffer_len); ++ *pulDataLen = buffer_len; + +- out: +- if (buffer) { +- memset(buffer, 0, buffer_len); +- free(buffer); +- } ++ ret = CKR_OK; ++out: ++ if (buffer != NULL) ++ OPENSSL_clear_free(buffer, buffer_len); ++ if (ctx != NULL) ++ EVP_PKEY_CTX_free(ctx); + return ret; + } + +@@ -1806,8 +1794,9 @@ C_Sign(CK_SESSION_HANDLE hSession, + struct st_object *o; + void *buffer = NULL; + CK_RV ret; +- RSA *rsa; +- int padding, len, buffer_len, padding_len; ++ int padding, padding_len; ++ size_t buffer_len; ++ EVP_PKEY_CTX *ctx = NULL; + + st_logf("Sign\n"); + VERIFY_SESSION_HANDLE(hSession, &state); +@@ -1822,18 +1811,15 @@ C_Sign(CK_SESSION_HANDLE hSession, + return CKR_ARGUMENTS_BAD; + } + +- rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); ++ if (pulSignatureLen == NULL) { ++ st_logf("signature len NULL\n"); ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } + +- 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; ++ if (pData == NULL_PTR) { ++ st_logf("data NULL\n"); ++ ret = CKR_ARGUMENTS_BAD; + goto out; + } + +@@ -1851,43 +1837,41 @@ C_Sign(CK_SESSION_HANDLE hSession, + goto out; + } + ++ ctx = EVP_PKEY_CTX_new(o->u.private_key.key, NULL); ++ if (ctx == NULL || EVP_PKEY_sign_init(ctx) <= 0 || ++ EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 || ++ EVP_PKEY_sign(ctx, NULL, &buffer_len, pData, ulDataLen) <= 0) { ++ ret = CKR_DEVICE_ERROR; ++ goto out; ++ } ++ + if ((CK_ULONG)buffer_len < ulDataLen + padding_len) { + ret = CKR_ARGUMENTS_BAD; + goto out; + } + +- if (pulSignatureLen == NULL) { +- st_logf("signature len NULL\n"); +- ret = CKR_ARGUMENTS_BAD; ++ buffer = OPENSSL_malloc(buffer_len); ++ if (buffer == NULL) { ++ ret = CKR_DEVICE_MEMORY; + 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) { ++ if (EVP_PKEY_sign(ctx, buffer, &buffer_len, pData, ulDataLen) <= 0) { + ret = CKR_DEVICE_ERROR; + goto out; + } +- if (len > buffer_len) +- abort(); ++ st_logf("Sign done\n"); + +- if (pSignature != NULL_PTR) +- memcpy(pSignature, buffer, len); +- *pulSignatureLen = len; ++ if (pSignature != NULL) ++ memcpy(pSignature, buffer, buffer_len); ++ *pulSignatureLen = buffer_len; + + ret = CKR_OK; +- +- out: +- if (buffer) { +- memset(buffer, 0, buffer_len); +- free(buffer); +- } ++out: ++ if (buffer != NULL) ++ OPENSSL_clear_free(buffer, buffer_len); ++ if (ctx != NULL) ++ EVP_PKEY_CTX_free(ctx); + return ret; + } + +@@ -1951,10 +1935,9 @@ C_Verify(CK_SESSION_HANDLE hSession, + { + struct session_state *state; + struct st_object *o; +- void *buffer = NULL; + CK_RV ret; +- RSA *rsa; +- int padding, len, buffer_len; ++ int padding; ++ EVP_PKEY_CTX *ctx = NULL; + + st_logf("Verify\n"); + VERIFY_SESSION_HANDLE(hSession, &state); +@@ -1969,39 +1952,6 @@ C_Verify(CK_SESSION_HANDLE hSession, + return CKR_ARGUMENTS_BAD; + } + +- rsa = EVP_PKEY_get0_RSA(o->u.public_key); +- +- 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 ((CK_ULONG)buffer_len < ulDataLen) { +- ret = CKR_ARGUMENTS_BAD; +- goto out; +- } +- + if (pSignature == NULL) { + st_logf("signature NULL\n"); + ret = CKR_ARGUMENTS_BAD; +@@ -2014,34 +1964,35 @@ C_Verify(CK_SESSION_HANDLE hSession, + goto out; + } + +- len = RSA_public_decrypt(ulDataLen, pData, buffer, rsa, padding); +- st_logf("private encrypt done\n"); +- if (len <= 0) { ++ 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; ++ } ++ ++ ctx = EVP_PKEY_CTX_new(o->u.public_key, NULL); ++ if (ctx == NULL || EVP_PKEY_verify_init(ctx) <= 0 || ++ EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 || ++ EVP_PKEY_verify(ctx, pSignature, ulSignatureLen, pData, ++ ulDataLen) <= 0) { + ret = CKR_DEVICE_ERROR; + goto out; + } +- if (len > buffer_len) +- abort(); ++ st_logf("Verify done\n"); + +- if ((CK_ULONG)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); +- } ++ ret = CKR_OK; ++out: ++ if (ctx != NULL) ++ EVP_PKEY_CTX_free(ctx); + return ret; + } + +- + CK_RV + C_VerifyUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pPart, +@@ -2072,7 +2023,6 @@ C_GenerateRandom(CK_SESSION_HANDLE hSession, + return CKR_FUNCTION_NOT_SUPPORTED; + } + +- + CK_FUNCTION_LIST funcs = { + { 2, 11 }, + C_Initialize, diff --git a/Handle-OpenSSL-3-s-providers.patch b/Handle-OpenSSL-3-s-providers.patch new file mode 100644 index 0000000..dd80bfb --- /dev/null +++ b/Handle-OpenSSL-3-s-providers.patch @@ -0,0 +1,268 @@ +From 64a276e3485b7066a3c630d018ca44dabeb7b6c7 Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Sat, 15 May 2021 21:18:06 -0400 +Subject: [PATCH] Handle OpenSSL 3's providers + +OpenSSL 3 compartmentalizes what algorithms it uses, which for us means +another hoop to jump through to use dubious cryptography. Right now, we +need to load "legacy" in order to access MD4 and RC4. + +(cherry picked from commit faac961a0d02c7818aad87c765eb344b87e668fa) +[rharwood@redhat.com: des3 removal, rc4 FIPSification] +--- + src/configure.ac | 1 + + src/lib/crypto/openssl/enc_provider/aes.c | 16 +++++++ + .../crypto/openssl/enc_provider/camellia.c | 16 +++++++ + src/lib/crypto/openssl/enc_provider/rc4.c | 4 ++ + .../crypto/openssl/hash_provider/hash_evp.c | 5 ++ + src/lib/crypto/openssl/init.c | 47 +++++++++++++++++++ + .../preauth/pkinit/pkinit_crypto_openssl.c | 25 ++++++++-- + 7 files changed, 111 insertions(+), 3 deletions(-) + +diff --git a/src/configure.ac b/src/configure.ac +index 9c2e816fe..20066918b 100644 +--- a/src/configure.ac ++++ b/src/configure.ac +@@ -284,6 +284,7 @@ AC_SUBST(CRYPTO_IMPL_LIBS) + + if test "$CRYPTO_IMPL" = openssl; then + AC_CHECK_FUNCS(EVP_KDF_fetch) ++ AC_CHECK_FUNCS(OSSL_PROVIDER_load) + fi + + AC_ARG_WITH([prng-alg], +diff --git a/src/lib/crypto/openssl/enc_provider/aes.c b/src/lib/crypto/openssl/enc_provider/aes.c +index 6b4622fe9..31c90a69d 100644 +--- a/src/lib/crypto/openssl/enc_provider/aes.c ++++ b/src/lib/crypto/openssl/enc_provider/aes.c +@@ -68,6 +68,10 @@ cbc_enc(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data, + EVP_CIPHER_CTX *ctx; + struct iov_cursor cursor; + ++ ret = krb5int_crypto_init(); ++ if (ret) ++ return ret; ++ + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + return ENOMEM; +@@ -102,6 +106,10 @@ cbc_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data, + EVP_CIPHER_CTX *ctx; + struct iov_cursor cursor; + ++ ret = krb5int_crypto_init(); ++ if (ret) ++ return ret; ++ + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + return ENOMEM; +@@ -137,6 +145,10 @@ cts_encr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data, + struct iov_cursor cursor; + AES_KEY enck; + ++ ret = krb5int_crypto_init(); ++ if (ret) ++ return ret; ++ + memset(iv_cts,0,sizeof(iv_cts)); + if (ivec && ivec->data){ + if (ivec->length != sizeof(iv_cts)) +@@ -190,6 +202,10 @@ cts_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data, + struct iov_cursor cursor; + AES_KEY deck; + ++ ret = krb5int_crypto_init(); ++ if (ret) ++ return ret; ++ + memset(iv_cts,0,sizeof(iv_cts)); + if (ivec && ivec->data){ + if (ivec->length != sizeof(iv_cts)) +diff --git a/src/lib/crypto/openssl/enc_provider/camellia.c b/src/lib/crypto/openssl/enc_provider/camellia.c +index f79679a0b..7cc7fc6fb 100644 +--- a/src/lib/crypto/openssl/enc_provider/camellia.c ++++ b/src/lib/crypto/openssl/enc_provider/camellia.c +@@ -92,6 +92,10 @@ cbc_enc(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data, + EVP_CIPHER_CTX *ctx; + struct iov_cursor cursor; + ++ ret = krb5int_crypto_init(); ++ if (ret) ++ return ret; ++ + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + return ENOMEM; +@@ -126,6 +130,10 @@ cbc_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data, + EVP_CIPHER_CTX *ctx; + struct iov_cursor cursor; + ++ ret = krb5int_crypto_init(); ++ if (ret) ++ return ret; ++ + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + return ENOMEM; +@@ -161,6 +169,10 @@ cts_encr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data, + struct iov_cursor cursor; + CAMELLIA_KEY enck; + ++ ret = krb5int_crypto_init(); ++ if (ret) ++ return ret; ++ + memset(iv_cts,0,sizeof(iv_cts)); + if (ivec && ivec->data){ + if (ivec->length != sizeof(iv_cts)) +@@ -214,6 +226,10 @@ cts_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data, + struct iov_cursor cursor; + CAMELLIA_KEY deck; + ++ ret = krb5int_crypto_init(); ++ if (ret) ++ return ret; ++ + memset(iv_cts,0,sizeof(iv_cts)); + if (ivec && ivec->data){ + if (ivec->length != sizeof(iv_cts)) +diff --git a/src/lib/crypto/openssl/enc_provider/rc4.c b/src/lib/crypto/openssl/enc_provider/rc4.c +index 9bf407899..4e7af3555 100644 +--- a/src/lib/crypto/openssl/enc_provider/rc4.c ++++ b/src/lib/crypto/openssl/enc_provider/rc4.c +@@ -69,6 +69,10 @@ k5_arcfour_docrypt(krb5_key key, const krb5_data *state, krb5_crypto_iov *data, + if (FIPS_mode()) + return KRB5_CRYPTO_INTERNAL; + ++ ret = krb5int_crypto_init(); ++ if (ret) ++ return ret; ++ + arcstate = (state != NULL) ? (void *)state->data : NULL; + if (arcstate != NULL) { + ctx = arcstate->ctx; +diff --git a/src/lib/crypto/openssl/hash_provider/hash_evp.c b/src/lib/crypto/openssl/hash_provider/hash_evp.c +index 2eb5139c0..09d7b3896 100644 +--- a/src/lib/crypto/openssl/hash_provider/hash_evp.c ++++ b/src/lib/crypto/openssl/hash_provider/hash_evp.c +@@ -41,6 +41,11 @@ hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data, + const krb5_data *d; + size_t i; + int ok; ++ krb5_error_code ret; ++ ++ ret = krb5int_crypto_init(); ++ if (ret) ++ return ret; + + if (output->length != (unsigned int)EVP_MD_size(type)) + return KRB5_CRYPTO_INTERNAL; +diff --git a/src/lib/crypto/openssl/init.c b/src/lib/crypto/openssl/init.c +index 1139bce53..8342dece1 100644 +--- a/src/lib/crypto/openssl/init.c ++++ b/src/lib/crypto/openssl/init.c +@@ -26,6 +26,51 @@ + + #include "crypto_int.h" + ++#ifdef HAVE_OSSL_PROVIDER_LOAD ++ ++/* ++ * Starting in OpenSSL 3, algorithms are grouped into containers called ++ * "providers", not all of which are loaded by default. At time of writing, ++ * we need MD4 and RC4 from the legacy provider. Oddly, 3DES is not in ++ * legacy. ++ */ ++ ++#include ++ ++OSSL_PROVIDER *legacy_provider = NULL; ++OSSL_PROVIDER *default_provider = NULL; ++ ++int ++krb5int_crypto_impl_init(void) ++{ ++ legacy_provider = OSSL_PROVIDER_load(NULL, "legacy"); ++ default_provider = OSSL_PROVIDER_load(NULL, "default"); ++ ++ /* ++ * Someone might build openssl without the legacy provider. They will ++ * have a bad time, but some things will still work. I don't know think ++ * this configuration is worth supporting. ++ */ ++ if (legacy_provider == NULL || default_provider == NULL) ++ abort(); ++ ++ return 0; ++} ++ ++void ++krb5int_crypto_impl_cleanup(void) ++{ ++ if (legacy_provider != NULL) ++ OSSL_PROVIDER_unload(legacy_provider); ++ if (default_provider != NULL) ++ OSSL_PROVIDER_unload(default_provider); ++ ++ legacy_provider = NULL; ++ default_provider = NULL; ++} ++ ++#else /* !HAVE_OSSL_PROVIDER_LOAD */ ++ + int + krb5int_crypto_impl_init(void) + { +@@ -36,3 +81,5 @@ void + krb5int_crypto_impl_cleanup(void) + { + } ++ ++#endif +diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c +index 350c2118a..284702432 100644 +--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c ++++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c +@@ -44,6 +44,14 @@ + #include + #endif + ++#ifdef HAVE_OSSL_PROVIDER_LOAD ++#include ++ ++/* TODO these leak - where to release them? */ ++OSSL_PROVIDER *legacy_provider = NULL; ++OSSL_PROVIDER *default_provider = NULL; ++#endif ++ + static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context ); + static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context ); + +@@ -2937,12 +2945,23 @@ cleanup: + return retval; + } + ++/* Initialize OpenSSL. */ + int + pkinit_openssl_init() + { +- /* Initialize OpenSSL. */ +- ERR_load_crypto_strings(); +- OpenSSL_add_all_algorithms(); ++#ifdef HAVE_OSSL_PROVIDER_LOAD ++ legacy_provider = OSSL_PROVIDER_load(NULL, "legacy"); ++ default_provider = OSSL_PROVIDER_load(NULL, "default"); ++ ++ /* ++ * Someone might build openssl without the legacy provider. They will ++ * have a bad time, but some things will still work. I don't know think ++ * this configuration is worth supporting. ++ */ ++ if (legacy_provider == NULL || default_provider == NULL) ++ abort(); ++#endif ++ + return 0; + } + diff --git a/Handle-SSL_read-changed-behavior-on-server-hangup.patch b/Handle-SSL_read-changed-behavior-on-server-hangup.patch new file mode 100644 index 0000000..5efa8f8 --- /dev/null +++ b/Handle-SSL_read-changed-behavior-on-server-hangup.patch @@ -0,0 +1,40 @@ +From f135f51b0ec59d320f79cd961411231084364489 Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Wed, 19 May 2021 19:33:34 -0400 +Subject: [PATCH] Handle SSL_read changed behavior on server hangup + +(cherry picked from commit 948e3c5b89fcfdb64ea5af177c7e30a6ce8a477b) +--- + src/include/k5-trace.h | 2 -- + src/lib/krb5/os/sendto_kdc.c | 5 +++-- + 2 files changed, 3 insertions(+), 4 deletions(-) + +diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h +index 79b5a7a85..7bd385d68 100644 +--- a/src/include/k5-trace.h ++++ b/src/include/k5-trace.h +@@ -395,8 +395,6 @@ void krb5int_trace(krb5_context context, const char *fmt, ...); + TRACE(c, "Received answer ({int} bytes) from {raddr}", len, raddr) + #define TRACE_SENDTO_KDC_HTTPS_ERROR_CONNECT(c, raddr) \ + TRACE(c, "HTTPS error connecting to {raddr}", raddr) +-#define TRACE_SENDTO_KDC_HTTPS_ERROR_RECV(c, raddr) \ +- TRACE(c, "HTTPS error receiving from {raddr}", raddr) + #define TRACE_SENDTO_KDC_HTTPS_ERROR_SEND(c, raddr) \ + TRACE(c, "HTTPS error sending to {raddr}", raddr) + #define TRACE_SENDTO_KDC_HTTPS_SEND(c, raddr) \ +diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c +index 0eedec175..e874130d9 100644 +--- a/src/lib/krb5/os/sendto_kdc.c ++++ b/src/lib/krb5/os/sendto_kdc.c +@@ -1320,8 +1320,9 @@ https_read_bytes(krb5_context context, struct conn_state *conn, + } else if (st == WANT_WRITE) { + cm_write(selstate, conn->fd); + } else if (st == ERROR_TLS) { +- TRACE_SENDTO_KDC_HTTPS_ERROR_RECV(context, &conn->addr); +- kill_conn(context, conn, selstate); ++ /* In OpenSSL 3, a server hangup is a TLS error. Rely on our decoder ++ * to handle this instead. */ ++ return TRUE; + } + return FALSE; + } diff --git a/Support-host-based-GSS-initiator-names.patch b/Support-host-based-GSS-initiator-names.patch index ebcae16..4a224d0 100644 --- a/Support-host-based-GSS-initiator-names.patch +++ b/Support-host-based-GSS-initiator-names.patch @@ -1,4 +1,4 @@ -From 8c57937f3ca793fe3f8fdd636be0bc11c24069bc Mon Sep 17 00:00:00 2001 +From e33835c4b6c6ce71757e9f659db03afa4bfd9a9a Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Fri, 15 Jan 2021 13:51:34 -0500 Subject: [PATCH] Support host-based GSS initiator names diff --git a/Use-OpenSSL-s-KBKDF-and-KRB5KDF-for-deriving-long-te.patch b/Use-OpenSSL-s-KBKDF-and-KRB5KDF-for-deriving-long-te.patch new file mode 100644 index 0000000..744366e --- /dev/null +++ b/Use-OpenSSL-s-KBKDF-and-KRB5KDF-for-deriving-long-te.patch @@ -0,0 +1,483 @@ +From 454a1a84ad161bd892c5b388edac09322c08cd06 Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Fri, 4 Oct 2019 14:49:29 -0400 +Subject: [PATCH] Use OpenSSL's KBKDF and KRB5KDF for deriving long-term keys + +If supported, use OpenSSL-provided KBKDF (aes-sha2 and camellia) and +KRB5KDF (3des and aes-sha1). We already use OpenSSL's PBKDF2 where +appropriate. OpenSSL added support for these KDFs in 3.0. + +(cherry picked from commit ef8d11f6fb1232201c9efd2ae2ed567023fb85d2) +--- + src/lib/crypto/krb/derive.c | 411 ++++++++++++++++++++++++++++-------- + 1 file changed, 326 insertions(+), 85 deletions(-) + +diff --git a/src/lib/crypto/krb/derive.c b/src/lib/crypto/krb/derive.c +index 6707a7308..1f07464e0 100644 +--- a/src/lib/crypto/krb/derive.c ++++ b/src/lib/crypto/krb/derive.c +@@ -27,6 +27,12 @@ + + #include "crypto_int.h" + ++#ifdef HAVE_EVP_KDF_FETCH ++#include ++#include ++#include ++#endif ++ + static krb5_key + find_cached_dkey(struct derived_key *list, const krb5_data *constant) + { +@@ -77,55 +83,253 @@ cleanup: + return ENOMEM; + } + ++#ifdef HAVE_EVP_KDF_FETCH + static krb5_error_code +-derive_random_rfc3961(const struct krb5_enc_provider *enc, +- krb5_key inkey, krb5_data *outrnd, +- const krb5_data *in_constant) ++openssl_kbdkf_counter_hmac(const struct krb5_hash_provider *hash, ++ krb5_key inkey, krb5_data *outrnd, ++ const krb5_data *label, const krb5_data *context) + { +- size_t blocksize, keybytes, n; + krb5_error_code ret; +- krb5_data block = empty_data(); ++ EVP_KDF *kdf = NULL; ++ EVP_KDF_CTX *kctx = NULL; ++ OSSL_PARAM params[6]; ++ size_t i = 0; ++ char *digest; + +- blocksize = enc->block_size; +- keybytes = enc->keybytes; ++ /* On NULL hash, preserve default behavior for pbkdf2_string_to_key(). */ ++ if (hash == NULL || !strcmp(hash->hash_name, "SHA1")) { ++ digest = "SHA1"; ++ } else if (!strcmp(hash->hash_name, "SHA-256")) { ++ digest = "SHA256"; ++ } else if (!strcmp(hash->hash_name, "SHA-384")) { ++ digest = "SHA384"; ++ } else { ++ ret = KRB5_CRYPTO_INTERNAL; ++ goto done; ++ } + +- if (blocksize == 1) +- return KRB5_BAD_ENCTYPE; +- if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes) ++ kdf = EVP_KDF_fetch(NULL, "KBKDF", NULL); ++ if (!kdf) { ++ ret = KRB5_CRYPTO_INTERNAL; ++ goto done; ++ } ++ ++ kctx = EVP_KDF_CTX_new(kdf); ++ if (!kctx) { ++ ret = KRB5_CRYPTO_INTERNAL; ++ goto done; ++ } ++ ++ params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, ++ digest, 0); ++ params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MAC, ++ "HMAC", 0); ++ params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, ++ inkey->keyblock.contents, ++ inkey->keyblock.length); ++ params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, ++ context->data, ++ context->length); ++ params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, ++ label->data, ++ label->length); ++ params[i] = OSSL_PARAM_construct_end(); ++ if (EVP_KDF_derive(kctx, (unsigned char *)outrnd->data, outrnd->length, ++ params) <= 0) { ++ ret = KRB5_CRYPTO_INTERNAL; ++ goto done; ++ } ++ ++ ret = 0; ++done: ++ if (ret) ++ zap(outrnd->data, outrnd->length); ++ EVP_KDF_free(kdf); ++ EVP_KDF_CTX_free(kctx); ++ 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; ++ EVP_KDF *kdf = NULL; ++ EVP_KDF_CTX *kctx = NULL; ++ OSSL_PARAM params[7]; ++ size_t i = 0; ++ char *cipher; ++ static unsigned char zeroes[16]; ++ ++ memset(zeroes, 0, sizeof(zeroes)); ++ ++ if (!memcmp(enc, &krb5int_enc_camellia128, sizeof(*enc))) { ++ cipher = "CAMELLIA-128-CBC"; ++ } else if (!memcmp(enc, &krb5int_enc_camellia256, sizeof(*enc))) { ++ cipher = "CAMELLIA-256-CBC"; ++ } else { ++ ret = KRB5_CRYPTO_INTERNAL; ++ goto done; ++ } ++ ++ kdf = EVP_KDF_fetch(NULL, "KBKDF", NULL); ++ if (!kdf) { ++ ret = KRB5_CRYPTO_INTERNAL; ++ goto done; ++ } ++ ++ kctx = EVP_KDF_CTX_new(kdf); ++ if (!kctx) { ++ ret = KRB5_CRYPTO_INTERNAL; ++ goto done; ++ } ++ ++ params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MODE, ++ "FEEDBACK", 0); ++ params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MAC, ++ "CMAC", 0); ++ params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CIPHER, ++ cipher, 0); ++ params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, ++ inkey->keyblock.contents, ++ inkey->keyblock.length); ++ params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, ++ in_constant->data, ++ in_constant->length); ++ params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SEED, ++ zeroes, sizeof(zeroes)); ++ params[i] = OSSL_PARAM_construct_end(); ++ if (EVP_KDF_derive(kctx, (unsigned char *)outrnd->data, outrnd->length, ++ params) <= 0) { ++ ret = KRB5_CRYPTO_INTERNAL; ++ goto done; ++ } ++ ++ ret = 0; ++done: ++ if (ret) ++ zap(outrnd->data, outrnd->length); ++ EVP_KDF_free(kdf); ++ EVP_KDF_CTX_free(kctx); ++ 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; ++ EVP_KDF *kdf = NULL; ++ EVP_KDF_CTX *kctx = NULL; ++ OSSL_PARAM params[4]; ++ size_t i = 0; ++ char *cipher; ++ ++ if (inkey->keyblock.length != enc->keylength || ++ outrnd->length != enc->keybytes) { ++ return KRB5_CRYPTO_INTERNAL; ++ } ++ ++ if (!memcmp(enc, &krb5int_enc_aes128, sizeof(*enc))) { ++ cipher = "AES-128-CBC"; ++ } else if (!memcmp(enc, &krb5int_enc_aes256, sizeof(*enc))) { ++ cipher = "AES-256-CBC"; ++ } else if (!memcmp(enc, &krb5int_enc_des3, sizeof(*enc))) { ++ cipher = "DES-EDE3-CBC"; ++ } else { ++ ret = KRB5_CRYPTO_INTERNAL; ++ goto done; ++ } ++ ++ kdf = EVP_KDF_fetch(NULL, "KRB5KDF", NULL); ++ if (kdf == NULL) { ++ ret = KRB5_CRYPTO_INTERNAL; ++ goto done; ++ } ++ ++ kctx = EVP_KDF_CTX_new(kdf); ++ if (kctx == NULL) { ++ ret = KRB5_CRYPTO_INTERNAL; ++ goto done; ++ } ++ ++ params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CIPHER, ++ cipher, 0); ++ params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, ++ inkey->keyblock.contents, ++ inkey->keyblock.length); ++ params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_CONSTANT, ++ in_constant->data, ++ in_constant->length); ++ params[i] = OSSL_PARAM_construct_end(); ++ if (EVP_KDF_derive(kctx, (unsigned char *)outrnd->data, outrnd->length, ++ params) <= 0) { ++ ret = KRB5_CRYPTO_INTERNAL; ++ goto done; ++ } ++ ++ ret = 0; ++done: ++ if (ret) ++ zap(outrnd->data, outrnd->length); ++ EVP_KDF_free(kdf); ++ EVP_KDF_CTX_free(kctx); ++ return ret; ++} ++ ++#else /* HAVE_EVP_KDF_FETCH */ ++ ++/* ++ * NIST SP800-108 KDF in counter mode (section 5.1). ++ * Parameters: ++ * - HMAC (with hash as the hash provider) is the PRF. ++ * - A block counter of four bytes is used. ++ * - Four bytes are used to encode the output length in the PRF input. ++ * ++ * There are no uses requiring more than a single PRF invocation. ++ */ ++static krb5_error_code ++builtin_sp800_108_counter_hmac(const struct krb5_hash_provider *hash, ++ krb5_key inkey, krb5_data *outrnd, ++ const krb5_data *label, ++ const krb5_data *context) ++{ ++ krb5_crypto_iov iov[5]; ++ krb5_error_code ret; ++ krb5_data prf; ++ unsigned char ibuf[4], lbuf[4]; ++ ++ if (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 +343,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 +408,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 /* HAVE_EVP_KDF_FETCH */ ++ + 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 HAVE_EVP_KDF_FETCH ++ 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 ++sp800_108_feedback_cmac(const struct krb5_enc_provider *enc, ++ krb5_key inkey, krb5_data *outrnd, ++ const krb5_data *in_constant) ++{ ++#ifdef HAVE_EVP_KDF_FETCH ++ 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 ++derive_random_rfc3961(const struct krb5_enc_provider *enc, ++ krb5_key inkey, krb5_data *outrnd, ++ const krb5_data *in_constant) ++{ ++#ifdef HAVE_EVP_KDF_FETCH ++ return openssl_krb5kdf(enc, inkey, outrnd, in_constant); ++#else ++ return builtin_derive_random_rfc3961(enc, inkey, outrnd, in_constant); ++#endif + } + + krb5_error_code +@@ -268,8 +510,7 @@ krb5int_derive_random(const struct krb5_enc_provider *enc, + case DERIVE_RFC3961: + return 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 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/Use-OpenSSL-s-SSKDF-in-PKINIT-when-available.patch b/Use-OpenSSL-s-SSKDF-in-PKINIT-when-available.patch new file mode 100644 index 0000000..81b00b9 --- /dev/null +++ b/Use-OpenSSL-s-SSKDF-in-PKINIT-when-available.patch @@ -0,0 +1,408 @@ +From cfdd0501ffea9cbe9343d1ff1e597df1689b547b Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Fri, 20 Sep 2019 17:20:59 -0400 +Subject: [PATCH] Use OpenSSL's SSKDF in PKINIT when available + +Starting in 3.0, OpenSSL implements SSKDF, which is the basis of our +id-pkinit-kdf (RFC 8636). Factor out common setup code around +other_info. Adjust code to comply to existing style. + +(cherry picked from commit 4376a22e41fb639be31daf81275a332d3f930996) +--- + .../preauth/pkinit/pkinit_crypto_openssl.c | 294 +++++++++++------- + 1 file changed, 181 insertions(+), 113 deletions(-) + +diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c +index e1153344e..350c2118a 100644 +--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c ++++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c +@@ -38,6 +38,12 @@ + #include + #include + ++#ifdef HAVE_EVP_KDF_FETCH ++#include ++#include ++#include ++#endif ++ + static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context ); + static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context ); + +@@ -2294,15 +2300,16 @@ cleanup: + } + + +-/** ++/* + * Given an algorithm_identifier, this function returns the hash length + * and EVP function associated with that algorithm. ++ * ++ * RFC 8636 defines a SHA384 variant, but we don't use it. + */ + static krb5_error_code +-pkinit_alg_values(krb5_context context, +- const krb5_data *alg_id, +- size_t *hash_bytes, +- const EVP_MD *(**func)(void)) ++pkinit_alg_values(krb5_context context, const krb5_data *alg_id, ++ size_t *hash_bytes, const EVP_MD *(**func)(void), ++ char **hash_name) + { + *hash_bytes = 0; + *func = NULL; +@@ -2311,18 +2318,21 @@ pkinit_alg_values(krb5_context context, + krb5_pkinit_sha1_oid_len))) { + *hash_bytes = 20; + *func = &EVP_sha1; ++ *hash_name = strdup("SHA1"); + return 0; + } else if ((alg_id->length == krb5_pkinit_sha256_oid_len) && + (0 == memcmp(alg_id->data, krb5_pkinit_sha256_oid, + krb5_pkinit_sha256_oid_len))) { + *hash_bytes = 32; + *func = &EVP_sha256; ++ *hash_name = strdup("SHA256"); + return 0; + } else if ((alg_id->length == krb5_pkinit_sha512_oid_len) && + (0 == memcmp(alg_id->data, krb5_pkinit_sha512_oid, + krb5_pkinit_sha512_oid_len))) { + *hash_bytes = 64; + *func = &EVP_sha512; ++ *hash_name = strdup("SHA512"); + return 0; + } else { + krb5_set_error_message(context, KRB5_ERR_BAD_S2K_PARAMS, +@@ -2331,11 +2341,60 @@ pkinit_alg_values(krb5_context context, + } + } /* pkinit_alg_values() */ + ++#ifdef HAVE_EVP_KDF_FETCH ++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, char *digest) ++{ ++ krb5_error_code ret; ++ EVP_KDF *kdf = NULL; ++ EVP_KDF_CTX *kctx = NULL; ++ OSSL_PARAM params[4]; ++ size_t i = 0; + +-/* 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: ++ if (digest == NULL) { ++ ret = oerr(context, ENOMEM, ++ _("Failed to allocate space for digest algorithm name")); ++ goto done; ++ } ++ ++ kdf = EVP_KDF_fetch(NULL, "SSKDF", NULL); ++ if (kdf == NULL) { ++ ret = oerr(context, KRB5_CRYPTO_INTERNAL, _("Failed to fetch SSKDF")); ++ goto done; ++ } ++ ++ kctx = EVP_KDF_CTX_new(kdf); ++ if (!kctx) { ++ ret = oerr(context, KRB5_CRYPTO_INTERNAL, ++ _("Failed to instantiate SSKDF")); ++ goto done; ++ } ++ ++ params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, ++ digest, 0); ++ params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, ++ key->data, key->length); ++ params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, ++ info->data, info->length); ++ params[i] = OSSL_PARAM_construct_end(); ++ if (EVP_KDF_derive(kctx, (unsigned char *)out, out_len, params) <= 0) { ++ ret = oerr(context, KRB5_CRYPTO_INTERNAL, ++ _("Failed to derive key using SSKDF")); ++ goto done; ++ } ++ ++ ret = 0; ++done: ++ EVP_KDF_free(kdf); ++ EVP_KDF_CTX_free(kctx); ++ 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) + * +@@ -2349,95 +2408,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; ++ krb5_error_code ret = 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: +@@ -2450,7 +2430,7 @@ pkinit_alg_agility_kdf(krb5_context context, + + ctx = EVP_MD_CTX_new(); + if (ctx == NULL) { +- retval = KRB5_CRYPTO_INTERNAL; ++ ret = KRB5_CRYPTO_INTERNAL; + goto cleanup; + } + +@@ -2458,7 +2438,7 @@ pkinit_alg_agility_kdf(krb5_context context, + if (!EVP_DigestInit(ctx, EVP_func())) { + krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL, + "Call to OpenSSL EVP_DigestInit() returned an error."); +- retval = KRB5_CRYPTO_INTERNAL; ++ ret = KRB5_CRYPTO_INTERNAL; + goto cleanup; + } + +@@ -2467,15 +2447,16 @@ pkinit_alg_agility_kdf(krb5_context context, + !EVP_DigestUpdate(ctx, other_info->data, other_info->length)) { + krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL, + "Call to OpenSSL EVP_DigestUpdate() returned an error."); +- retval = KRB5_CRYPTO_INTERNAL; ++ ret = KRB5_CRYPTO_INTERNAL; + 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; ++ ret = KRB5_CRYPTO_INTERNAL; + goto cleanup; + } + offset += s; +@@ -2484,26 +2465,113 @@ 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 ret; ++} /* builtin_sskdf() */ ++#endif /* HAVE_EVP_KDF_FETCH */ + +- /* 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 ret; ++ 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; ++ char *hash_name = NULL; ++ ++ /* Allocate and initialize the key block. */ ++ key_block->magic = 0; ++ key_block->enctype = enctype; ++ ++ /* Use separate variables to avoid alignment restriction problems. */ ++ ret = krb5_c_keylengths(context, enctype, &rand_len, &key_len); ++ if (ret) ++ goto cleanup; ++ random_data.length = rand_len; ++ key_block->length = key_len; ++ ++ key_block->contents = k5calloc(key_block->length, 1, &ret); ++ 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); ++ ret = pkinit_alg_values(context, alg_oid, &hash_len, &EVP_func, ++ &hash_name); ++ if (ret) ++ 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, &ret); ++ 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; ++ ret = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields, ++ &supp_pub_info); ++ if (ret) ++ 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; ++ ret = encode_krb5_sp80056a_other_info(&other_info_fields, &other_info); ++ if (ret) ++ goto cleanup; ++ ++#ifdef HAVE_EVP_KDF_FETCH ++ ret = openssl_sskdf(context, hash_len, secret, other_info, ++ random_data.data, key_block->length, hash_name); ++#else ++ ret = builtin_sskdf(context, reps, hash_len, EVP_func, secret, ++ other_info, random_data.data, key_block->length); ++#endif ++ if (ret) ++ goto cleanup; ++ ++ ret = krb5_c_random_to_key(context, enctype, &random_data, key_block); ++cleanup: ++ if (ret) ++ krb5_free_keyblock_contents(context, key_block); ++ ++ free(hash_name); ++ 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() */ ++ return ret; ++} + + /* 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/downstream-FIPS-with-PRNG-and-RADIUS-and-MD4.patch b/downstream-FIPS-with-PRNG-and-RADIUS-and-MD4.patch index 047a59e..3de59f1 100644 --- a/downstream-FIPS-with-PRNG-and-RADIUS-and-MD4.patch +++ b/downstream-FIPS-with-PRNG-and-RADIUS-and-MD4.patch @@ -1,4 +1,4 @@ -From 4a62aeae7b747cd289548949f940525365fe0947 Mon Sep 17 00:00:00 2001 +From d2b050a5acfacd8d400560ae097f6d5f392d7398 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Fri, 9 Nov 2018 15:12:21 -0500 Subject: [PATCH] [downstream] FIPS with PRNG and RADIUS and MD4 diff --git a/downstream-Use-backported-version-of-OpenSSL-3-KDF-i.patch b/downstream-Use-backported-version-of-OpenSSL-3-KDF-i.patch deleted file mode 100644 index 84551d1..0000000 --- a/downstream-Use-backported-version-of-OpenSSL-3-KDF-i.patch +++ /dev/null @@ -1,752 +0,0 @@ -From 687bb26cb0877fa5497e90f7d325de42b456da2a Mon Sep 17 00:00:00 2001 -From: Robbie Harwood -Date: Fri, 15 Nov 2019 20:05:16 +0000 -Subject: [PATCH] [downstream] Use backported version of OpenSSL-3 KDF - interface - -Last-updated: krb5-1.17 ---- - src/configure.ac | 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.ac b/src/configure.ac -index 3e1052db7..ea708491b 100644 ---- a/src/configure.ac -+++ b/src/configure.ac -@@ -282,6 +282,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 -+#include -+#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 e1153344e..911e74fd9 100644 ---- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c -+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c -@@ -38,6 +38,13 @@ - #include - #include - -+#ifdef OSSL_KDFS -+#include -+#include -+#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 ); - -@@ -2331,11 +2338,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) - * -@@ -2349,95 +2396,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: -@@ -2471,8 +2439,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; -@@ -2484,26 +2453,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/krb5.spec b/krb5.spec index e52cbf2..8be3477 100644 --- a/krb5.spec +++ b/krb5.spec @@ -42,7 +42,7 @@ Summary: The Kerberos network authentication system Name: krb5 Version: 1.19.1 -Release: %{?zdpd}3%{?dist}.1 +Release: %{?zdpd}4%{?dist} # rharwood has trust path to signing key and verifies on check-in Source0: https://web.mit.edu/kerberos/dist/krb5/%{version}/krb5-%{version}%{?dashpre}.tar.gz @@ -68,11 +68,17 @@ Patch1: downstream-SELinux-integration.patch Patch3: downstream-netlib-and-dns.patch Patch4: downstream-fix-debuginfo-with-y.tab.c.patch Patch5: downstream-Remove-3des-support.patch -Patch6: downstream-Use-backported-version-of-OpenSSL-3-KDF-i.patch Patch7: downstream-FIPS-with-PRNG-and-RADIUS-and-MD4.patch Patch8: Add-APIs-for-marshalling-credentials.patch Patch9: Add-hostname-canonicalization-helper-to-k5test.py.patch Patch10: Support-host-based-GSS-initiator-names.patch +Patch11: Fix-softpkcs11-build-issues-with-openssl-3.0.patch +Patch12: softpkcs11-Remove-all-openssl-deprecated-functions.patch +Patch13: Add-buildsystem-detection-of-the-OpenSSL-3-KDF-inter.patch +Patch14: Use-OpenSSL-s-SSKDF-in-PKINIT-when-available.patch +Patch15: Use-OpenSSL-s-KBKDF-and-KRB5KDF-for-deriving-long-te.patch +Patch16: Handle-OpenSSL-3-s-providers.patch +Patch17: Handle-SSL_read-changed-behavior-on-server-hangup.patch License: MIT URL: https://web.mit.edu/kerberos/www/ @@ -103,9 +109,8 @@ BuildRequires: iproute BuildRequires: python3-pyrad %endif -# Need KDFs. This is the backported version -BuildRequires: openssl-devel >= 1:1.1.1d-4 -BuildRequires: openssl-devel < 1:3.0.0 +# Need KDFs. This is the "real" version +BuildRequires: openssl-devel >= 1:3.0.0 %description Kerberos V5 is a trusted-third-party network authentication system, @@ -131,7 +136,7 @@ to install this package. %package libs Summary: The non-admin shared libraries used by Kerberos 5 -Requires: openssl-libs >= 1:1.1.1d-4 +Requires: openssl-libs >= 1:3.0.0 Requires: coreutils, gawk, grep, sed Requires: keyutils-libs >= 1.5.8 Requires: /etc/crypto-policies/back-ends/krb5.config @@ -635,6 +640,10 @@ exit 0 %{_libdir}/libkadm5srv_mit.so.* %changelog +* Wed May 19 2021 Robbie Harwood - 1.19.1-4 +- Port to OpenSSL 3 (alpha 15) +- Resolves: #1955873 + * Fri Apr 16 2021 Mohan Boddu - 1.19.1-3.1 - Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 diff --git a/softpkcs11-Remove-all-openssl-deprecated-functions.patch b/softpkcs11-Remove-all-openssl-deprecated-functions.patch new file mode 100644 index 0000000..a5d7d60 --- /dev/null +++ b/softpkcs11-Remove-all-openssl-deprecated-functions.patch @@ -0,0 +1,125 @@ +From 7fb3126fd893eaf943734896c92355fe150b44d6 Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Sat, 15 May 2021 18:04:58 -0400 +Subject: [PATCH] softpkcs11: Remove all openssl deprecated functions + +Rewrite add_pubkey_info() in terms of the EVP_PKEY interface. In this +process, fix its unchecked allocations and fail fast for non-RSA keys. + +(cherry picked from commit ec4a325dc939da23967bb115bb5339963da80098) +--- + src/configure.ac | 1 + + src/tests/softpkcs11/main.c | 83 +++++++++++++++++++++++-------------- + 2 files changed, 53 insertions(+), 31 deletions(-) + +diff --git a/src/configure.ac b/src/configure.ac +index 3e1052db7..eb6307468 100644 +--- a/src/configure.ac ++++ b/src/configure.ac +@@ -1114,6 +1114,7 @@ int i = 1; + ])], k5_cv_openssl_version_okay=yes, k5_cv_openssl_version_okay=no)]) + old_LIBS="$LIBS" + AC_CHECK_LIB(crypto, PKCS7_get_signer_info) ++ AC_CHECK_FUNCS(EVP_PKEY_get_bn_param) + LIBS="$old_LIBS" + fi + if test "$k5_cv_openssl_version_okay" = yes && (test "$enable_pkinit" = yes || test "$enable_pkinit" = try); then +diff --git a/src/tests/softpkcs11/main.c b/src/tests/softpkcs11/main.c +index 500e3093d..c6f688dde 100644 +--- a/src/tests/softpkcs11/main.c ++++ b/src/tests/softpkcs11/main.c +@@ -416,42 +416,63 @@ add_object_attribute(struct st_object *o, + 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; +- const RSA *rsa; +- const BIGNUM *n, *e; ++ CK_BYTE *modulus = NULL, *exponent = 0; ++ size_t modulus_len = 0, exponent_len = 0; ++ CK_ULONG modulus_bits = 0; ++ CK_RV ret; + +- rsa = EVP_PKEY_get0_RSA(key); +- RSA_get0_key(rsa, &n, &e, NULL); +- modulus_bits = BN_num_bits(n); ++#ifdef HAVE_EVP_PKEY_GET_BN_PARAM ++ BIGNUM *n = NULL, *e = NULL; ++#else ++ const RSA *rsa; ++ const BIGNUM *n, *e; ++#endif + +- modulus_len = BN_num_bytes(n); +- modulus = malloc(modulus_len); +- BN_bn2bin(n, modulus); ++ if (key_type != CKK_RSA) ++ abort(); + +- exponent_len = BN_num_bytes(e); +- exponent = malloc(exponent_len); +- BN_bn2bin(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); +- +- free(modulus); +- free(exponent); ++#ifdef HAVE_EVP_PKEY_GET_BN_PARAM ++ if (EVP_PKEY_get_bn_param(key, "n", &n) == 0 || ++ EVP_PKEY_get_bn_param(key, "e", &e) == 0) { ++ ret = CKR_DEVICE_ERROR; ++ goto done; + } +- default: +- /* XXX */ +- break; ++#else ++ rsa = EVP_PKEY_get0_RSA(key); ++ RSA_get0_key(rsa, &n, &e, NULL); ++#endif ++ ++ modulus_bits = BN_num_bits(n); ++ modulus_len = BN_num_bytes(n); ++ exponent_len = BN_num_bytes(e); ++ ++ modulus = malloc(modulus_len); ++ exponent = malloc(exponent_len); ++ if (modulus == NULL || exponent == NULL) { ++ ret = CKR_DEVICE_MEMORY; ++ goto done; + } +- return CKR_OK; ++ ++ BN_bn2bin(n, modulus); ++ BN_bn2bin(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); ++ ++ ret = CKR_OK; ++done: ++ free(modulus); ++ free(exponent); ++ ++#ifdef HAVE_EVP_PKEY_GET_BN_PARAM ++ BN_clear_free(n); ++ BN_clear_free(e); ++#endif ++ ++ return ret; + } + +