301 lines
9.8 KiB
Diff
301 lines
9.8 KiB
Diff
From 172390c584726ecd5747b064587acc1db44a98ca Mon Sep 17 00:00:00 2001
|
|
From: Robbie Harwood <rharwood@redhat.com>
|
|
Date: Mon, 6 May 2019 13:13:06 -0400
|
|
Subject: [PATCH] Simply OpenSSL PKCS7 decryption code
|
|
|
|
Fold pkcs7_decrypt() and pkcs7_dataDecode() into a single function,
|
|
and make it output the plaintext rather than a BIO.
|
|
|
|
[ghudson@mit.edu: continued a modernization of pkcs7_dataDecode() into
|
|
a larger refactoring]
|
|
|
|
(cherry picked from commit 210356653a2f963ffe9a8a1b1627c64fb8ca7a3d)
|
|
---
|
|
.../preauth/pkinit/pkinit_crypto_openssl.c | 211 +++++-------------
|
|
1 file changed, 62 insertions(+), 149 deletions(-)
|
|
|
|
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
|
|
index 5ff81d8cf..8aa2c5257 100644
|
|
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
|
|
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
|
|
@@ -81,12 +81,8 @@ static int openssl_callback (int, X509_STORE_CTX *);
|
|
static int openssl_callback_ignore_crls (int, X509_STORE_CTX *);
|
|
|
|
static int pkcs7_decrypt
|
|
-(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
|
|
- PKCS7 *p7, BIO *bio);
|
|
-
|
|
-static BIO * pkcs7_dataDecode
|
|
-(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
|
|
- PKCS7 *p7);
|
|
+(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, PKCS7 *p7,
|
|
+ unsigned char **data_out, unsigned int *len_out);
|
|
|
|
static ASN1_OBJECT * pkinit_pkcs7type2oid
|
|
(pkinit_plg_crypto_context plg_cryptoctx, int pkcs7_type);
|
|
@@ -1964,9 +1960,6 @@ cms_envelopeddata_verify(krb5_context context,
|
|
{
|
|
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
|
|
PKCS7 *p7 = NULL;
|
|
- BIO *out = NULL;
|
|
- int i = 0;
|
|
- unsigned int size = 0;
|
|
const unsigned char *p = enveloped_data;
|
|
unsigned int tmp_buf_len = 0, tmp_buf2_len = 0, vfy_buf_len = 0;
|
|
unsigned char *tmp_buf = NULL, *tmp_buf2 = NULL, *vfy_buf = NULL;
|
|
@@ -1991,26 +1984,13 @@ cms_envelopeddata_verify(krb5_context context,
|
|
}
|
|
|
|
/* decrypt received PKCS7 message */
|
|
- out = BIO_new(BIO_s_mem());
|
|
- if (pkcs7_decrypt(context, id_cryptoctx, p7, out)) {
|
|
+ if (pkcs7_decrypt(context, id_cryptoctx, p7, &tmp_buf, &tmp_buf_len)) {
|
|
pkiDebug("PKCS7 decryption successful\n");
|
|
} else {
|
|
retval = oerr(context, 0, _("Failed to decrypt PKCS7 message"));
|
|
goto cleanup;
|
|
}
|
|
|
|
- /* transfer the decoded PKCS7 SignedData message into a separate buffer */
|
|
- for (;;) {
|
|
- if ((tmp_buf = realloc(tmp_buf, size + 1024 * 10)) == NULL)
|
|
- goto cleanup;
|
|
- i = BIO_read(out, &(tmp_buf[size]), 1024 * 10);
|
|
- if (i <= 0)
|
|
- break;
|
|
- else
|
|
- size += i;
|
|
- }
|
|
- tmp_buf_len = size;
|
|
-
|
|
#ifdef DEBUG_ASN1
|
|
print_buffer_bin(tmp_buf, tmp_buf_len, "/tmp/client_enc_keypack");
|
|
#endif
|
|
@@ -2072,8 +2052,6 @@ cleanup:
|
|
|
|
if (p7 != NULL)
|
|
PKCS7_free(p7);
|
|
- if (out != NULL)
|
|
- BIO_free(out);
|
|
free(tmp_buf);
|
|
free(tmp_buf2);
|
|
|
|
@@ -5714,39 +5692,6 @@ cleanup:
|
|
return retval;
|
|
}
|
|
|
|
-static int
|
|
-pkcs7_decrypt(krb5_context context,
|
|
- pkinit_identity_crypto_context id_cryptoctx,
|
|
- PKCS7 *p7,
|
|
- BIO *data)
|
|
-{
|
|
- BIO *tmpmem = NULL;
|
|
- int retval = 0, i = 0;
|
|
- char buf[4096];
|
|
-
|
|
- if(p7 == NULL)
|
|
- return 0;
|
|
-
|
|
- if(!PKCS7_type_is_enveloped(p7)) {
|
|
- pkiDebug("wrong pkcs7 content type\n");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- if(!(tmpmem = pkcs7_dataDecode(context, id_cryptoctx, p7))) {
|
|
- pkiDebug("unable to decrypt pkcs7 object\n");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- for(;;) {
|
|
- i = BIO_read(tmpmem, buf, sizeof(buf));
|
|
- if (i <= 0) break;
|
|
- BIO_write(data, buf, i);
|
|
- BIO_free_all(tmpmem);
|
|
- return 1;
|
|
- }
|
|
- return retval;
|
|
-}
|
|
-
|
|
krb5_error_code
|
|
pkinit_process_td_trusted_certifiers(
|
|
krb5_context context,
|
|
@@ -5827,118 +5772,86 @@ cleanup:
|
|
return retval;
|
|
}
|
|
|
|
-static BIO *
|
|
-pkcs7_dataDecode(krb5_context context,
|
|
- pkinit_identity_crypto_context id_cryptoctx,
|
|
- PKCS7 *p7)
|
|
+/* Originally based on OpenSSL's PKCS7_dataDecode(), now modified to remove the
|
|
+ * use of BIO objects and to fit the PKINIT internal interfaces. */
|
|
+static int
|
|
+pkcs7_decrypt(krb5_context context,
|
|
+ pkinit_identity_crypto_context id_cryptoctx, PKCS7 *p7,
|
|
+ unsigned char **data_out, unsigned int *len_out)
|
|
{
|
|
- unsigned int eklen=0, tkeylen=0;
|
|
- BIO *out=NULL,*etmp=NULL,*bio=NULL;
|
|
- unsigned char *ek=NULL, *tkey=NULL;
|
|
- ASN1_OCTET_STRING *data_body=NULL;
|
|
- const EVP_CIPHER *evp_cipher=NULL;
|
|
- EVP_CIPHER_CTX *evp_ctx=NULL;
|
|
- X509_ALGOR *enc_alg=NULL;
|
|
- STACK_OF(PKCS7_RECIP_INFO) *rsk=NULL;
|
|
- PKCS7_RECIP_INFO *ri=NULL;
|
|
-
|
|
- p7->state=PKCS7_S_HEADER;
|
|
-
|
|
- rsk=p7->d.enveloped->recipientinfo;
|
|
- enc_alg=p7->d.enveloped->enc_data->algorithm;
|
|
- data_body=p7->d.enveloped->enc_data->enc_data;
|
|
- evp_cipher=EVP_get_cipherbyobj(enc_alg->algorithm);
|
|
- if (evp_cipher == NULL) {
|
|
- PKCS7err(PKCS7_F_PKCS7_DATADECODE,PKCS7_R_UNSUPPORTED_CIPHER_TYPE);
|
|
- goto cleanup;
|
|
- }
|
|
+ krb5_error_code ret;
|
|
+ int ok = 0, plaintext_len = 0, final_len;
|
|
+ unsigned int keylen = 0, eklen = 0, blocksize;
|
|
+ unsigned char *ek = NULL, *tkey = NULL, *plaintext = NULL, *use_key;
|
|
+ ASN1_OCTET_STRING *data_body = p7->d.enveloped->enc_data->enc_data;
|
|
+ const EVP_CIPHER *evp_cipher;
|
|
+ EVP_CIPHER_CTX *evp_ctx = NULL;
|
|
+ X509_ALGOR *enc_alg = p7->d.enveloped->enc_data->algorithm;
|
|
+ STACK_OF(PKCS7_RECIP_INFO) *rsk = p7->d.enveloped->recipientinfo;
|
|
+ PKCS7_RECIP_INFO *ri = NULL;
|
|
|
|
- if ((etmp=BIO_new(BIO_f_cipher())) == NULL) {
|
|
- PKCS7err(PKCS7_F_PKCS7_DATADECODE,ERR_R_BIO_LIB);
|
|
- goto cleanup;
|
|
- }
|
|
+ *data_out = NULL;
|
|
+ *len_out = 0;
|
|
|
|
- /* It was encrypted, we need to decrypt the secret key
|
|
- * with the private key */
|
|
+ p7->state = PKCS7_S_HEADER;
|
|
|
|
/* RFC 4556 section 3.2.3.2 requires that there be exactly one
|
|
* recipientInfo. */
|
|
if (sk_PKCS7_RECIP_INFO_num(rsk) != 1) {
|
|
pkiDebug("invalid number of EnvelopedData RecipientInfos\n");
|
|
- goto cleanup;
|
|
+ return 0;
|
|
}
|
|
-
|
|
ri = sk_PKCS7_RECIP_INFO_value(rsk, 0);
|
|
- (void)pkinit_decode_data(context, id_cryptoctx,
|
|
- ASN1_STRING_get0_data(ri->enc_key),
|
|
- ASN1_STRING_length(ri->enc_key), &ek, &eklen);
|
|
|
|
- evp_ctx=NULL;
|
|
- BIO_get_cipher_ctx(etmp,&evp_ctx);
|
|
- if (EVP_CipherInit_ex(evp_ctx,evp_cipher,NULL,NULL,NULL,0) <= 0)
|
|
+ evp_cipher = EVP_get_cipherbyobj(enc_alg->algorithm);
|
|
+ if (evp_cipher == NULL)
|
|
+ goto cleanup;
|
|
+ keylen = EVP_CIPHER_key_length(evp_cipher);
|
|
+ blocksize = EVP_CIPHER_block_size(evp_cipher);
|
|
+
|
|
+ evp_ctx = EVP_CIPHER_CTX_new();
|
|
+ if (evp_ctx == NULL)
|
|
goto cleanup;
|
|
- if (EVP_CIPHER_asn1_to_param(evp_ctx,enc_alg->parameter) < 0)
|
|
+ if (!EVP_DecryptInit(evp_ctx, evp_cipher, NULL, NULL) ||
|
|
+ EVP_CIPHER_asn1_to_param(evp_ctx, enc_alg->parameter) <= 0)
|
|
goto cleanup;
|
|
|
|
/* Generate a random symmetric key to avoid exposing timing data if RSA
|
|
* decryption fails the padding check. */
|
|
- tkeylen = EVP_CIPHER_CTX_key_length(evp_ctx);
|
|
- tkey = OPENSSL_malloc(tkeylen);
|
|
- if (tkey == NULL)
|
|
+ tkey = malloc(keylen);
|
|
+ if (tkey == NULL || !EVP_CIPHER_CTX_rand_key(evp_ctx, tkey))
|
|
goto cleanup;
|
|
- if (EVP_CIPHER_CTX_rand_key(evp_ctx, tkey) <= 0)
|
|
- goto cleanup;
|
|
- if (ek == NULL) {
|
|
- ek = tkey;
|
|
- eklen = tkeylen;
|
|
- tkey = NULL;
|
|
- }
|
|
|
|
- if (eklen != (unsigned)EVP_CIPHER_CTX_key_length(evp_ctx)) {
|
|
- /* Some S/MIME clients don't use the same key
|
|
- * and effective key length. The key length is
|
|
- * determined by the size of the decrypted RSA key.
|
|
- */
|
|
- if (!EVP_CIPHER_CTX_set_key_length(evp_ctx, (int)eklen)) {
|
|
- ek = tkey;
|
|
- eklen = tkeylen;
|
|
- tkey = NULL;
|
|
- }
|
|
- }
|
|
- if (EVP_CipherInit_ex(evp_ctx,NULL,NULL,ek,NULL,0) <= 0)
|
|
- goto cleanup;
|
|
+ /* Decrypt the secret key with the private key. */
|
|
+ ret = pkinit_decode_data(context, id_cryptoctx,
|
|
+ ASN1_STRING_get0_data(ri->enc_key),
|
|
+ ASN1_STRING_length(ri->enc_key), &ek, &eklen);
|
|
+ use_key = (ret || eklen != keylen) ? tkey : ek;
|
|
|
|
- if (out == NULL)
|
|
- out=etmp;
|
|
- else
|
|
- BIO_push(out,etmp);
|
|
- etmp=NULL;
|
|
+ /* Allocate a plaintext buffer and decrypt data_body into it. */
|
|
+ plaintext = malloc(data_body->length + blocksize);
|
|
+ if (plaintext == NULL)
|
|
+ goto cleanup;
|
|
+ if (!EVP_DecryptInit(evp_ctx, NULL, use_key, NULL))
|
|
+ goto cleanup;
|
|
+ if (!EVP_DecryptUpdate(evp_ctx, plaintext, &plaintext_len,
|
|
+ data_body->data, data_body->length))
|
|
+ goto cleanup;
|
|
+ if (!EVP_DecryptFinal(evp_ctx, plaintext + plaintext_len, &final_len))
|
|
+ goto cleanup;
|
|
+ plaintext_len += final_len;
|
|
|
|
- if (data_body->length > 0)
|
|
- bio = BIO_new_mem_buf(data_body->data, data_body->length);
|
|
- else {
|
|
- bio=BIO_new(BIO_s_mem());
|
|
- BIO_set_mem_eof_return(bio,0);
|
|
- }
|
|
- BIO_push(out,bio);
|
|
- bio=NULL;
|
|
+ *len_out = plaintext_len;
|
|
+ *data_out = plaintext;
|
|
+ plaintext = NULL;
|
|
+ ok = 1;
|
|
|
|
- if (0) {
|
|
- cleanup:
|
|
- if (out != NULL) BIO_free_all(out);
|
|
- if (etmp != NULL) BIO_free_all(etmp);
|
|
- if (bio != NULL) BIO_free_all(bio);
|
|
- out=NULL;
|
|
- }
|
|
- if (ek != NULL) {
|
|
- OPENSSL_cleanse(ek, eklen);
|
|
- OPENSSL_free(ek);
|
|
- }
|
|
- if (tkey != NULL) {
|
|
- OPENSSL_cleanse(tkey, tkeylen);
|
|
- OPENSSL_free(tkey);
|
|
- }
|
|
- return(out);
|
|
+cleanup:
|
|
+ EVP_CIPHER_CTX_free(evp_ctx);
|
|
+ zapfree(plaintext, plaintext_len);
|
|
+ zapfree(ek, eklen);
|
|
+ zapfree(tkey, keylen);
|
|
+ return ok;
|
|
}
|
|
|
|
#ifdef DEBUG_DH
|