From db62fe97a56f8f8476e3202a492d1c3d784d52b2 Mon Sep 17 00:00:00 2001 From: Robbie Harwood 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