X-Git-Url: https://git.openssl.org/?a=blobdiff_plain;f=crypto%2Fcms%2Fcms_smime.c;h=7ad827697e082f619b6e9065f6b5624d54e6d1b6;hb=2afb29b480d87c4c24f830e69dfe82762e3db608;hp=3a813de2466334b86b948d08fa96dbe7e2a95299;hpb=761ffa729f396dc4b8607a64ad522f6104eaa7bd;p=openssl.git diff --git a/crypto/cms/cms_smime.c b/crypto/cms/cms_smime.c index 3a813de246..7ad827697e 100644 --- a/crypto/cms/cms_smime.c +++ b/crypto/cms/cms_smime.c @@ -58,24 +58,36 @@ #include #include #include "cms_lcl.h" +#include "asn1_locl.h" + +static BIO *cms_get_text_bio(BIO *out, unsigned int flags) + { + BIO *rbio; + if (out == NULL) + rbio = BIO_new(BIO_s_null()); + else if (flags & CMS_TEXT) + { + rbio = BIO_new(BIO_s_mem()); + BIO_set_mem_eof_return(rbio, 0); + } + else + rbio = out; + return rbio; + } static int cms_copy_content(BIO *out, BIO *in, unsigned int flags) { unsigned char buf[4096]; int r = 0, i; - BIO *tmpout = NULL; + BIO *tmpout; - if(flags & CMS_TEXT) + tmpout = cms_get_text_bio(out, flags); + + if(!tmpout) { - tmpout = BIO_new(BIO_s_mem()); - if(!tmpout) - { - CMSerr(CMS_F_CMS_COPY_CONTENT,ERR_R_MALLOC_FAILURE); - goto err; - } + CMSerr(CMS_F_CMS_COPY_CONTENT,ERR_R_MALLOC_FAILURE); + goto err; } - else - tmpout = out; /* Read all content through chain to process digest, decrypt etc */ for (;;) @@ -88,11 +100,13 @@ static int cms_copy_content(BIO *out, BIO *in, unsigned int flags) if (!BIO_get_cipher_status(in)) goto err; } + if (i < 0) + goto err; break; } - if (tmpout) - BIO_write(tmpout, buf, i); + if (tmpout && (BIO_write(tmpout, buf, i) != i)) + goto err; } if(flags & CMS_TEXT) @@ -124,6 +138,23 @@ static int check_content(CMS_ContentInfo *cms) return 1; } +static void do_free_upto(BIO *f, BIO *upto) + { + if (upto) + { + BIO *tbio; + do + { + tbio = BIO_pop(f); + BIO_free(f); + f = tbio; + } + while (f && f != upto); + } + else + BIO_free_all(f); + } + int CMS_data(CMS_ContentInfo *cms, BIO *out, unsigned int flags) { BIO *cont; @@ -148,7 +179,7 @@ CMS_ContentInfo *CMS_data_create(BIO *in, unsigned int flags) if (!cms) return NULL; - if ((flags & CMS_STREAM) || CMS_final(cms, in, flags)) + if ((flags & CMS_STREAM) || CMS_final(cms, in, NULL, flags)) return cms; CMS_ContentInfo_free(cms); @@ -176,7 +207,7 @@ int CMS_digest_verify(CMS_ContentInfo *cms, BIO *dcont, BIO *out, r = cms_copy_content(out, cont, flags); if (r) r = cms_DigestedData_do_final(cms, cont, 1); - BIO_free_all(cont); + do_free_upto(cont, dcont); return r; } @@ -193,7 +224,7 @@ CMS_ContentInfo *CMS_digest_create(BIO *in, const EVP_MD *md, if(!(flags & CMS_DETACHED)) CMS_set_detached(cms, 0); - if ((flags & CMS_STREAM) || CMS_final(cms, in, flags)) + if ((flags & CMS_STREAM) || CMS_final(cms, in, NULL, flags)) return cms; CMS_ContentInfo_free(cms); @@ -222,7 +253,7 @@ int CMS_EncryptedData_decrypt(CMS_ContentInfo *cms, if (!cont) return 0; r = cms_copy_content(out, cont, flags); - BIO_free_all(cont); + do_free_upto(cont, dcont); return r; } @@ -245,7 +276,8 @@ CMS_ContentInfo *CMS_EncryptedData_encrypt(BIO *in, const EVP_CIPHER *cipher, if(!(flags & CMS_DETACHED)) CMS_set_detached(cms, 0); - if ((flags & (CMS_STREAM|CMS_PARTIAL)) || CMS_final(cms, in, flags)) + if ((flags & (CMS_STREAM|CMS_PARTIAL)) + || CMS_final(cms, in, NULL, flags)) return cms; CMS_ContentInfo_free(cms); @@ -268,7 +300,7 @@ static int cms_signerinfo_verify_cert(CMS_SignerInfo *si, CMS_R_STORE_INIT_ERROR); goto err; } - X509_STORE_CTX_set_purpose(&ctx, X509_PURPOSE_SMIME_SIGN); + X509_STORE_CTX_set_default(&ctx, "smime_sign"); if (crls) X509_STORE_CTX_set0_crls(&ctx, crls); @@ -298,10 +330,16 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs, STACK_OF(X509_CRL) *crls = NULL; X509 *signer; int i, scount = 0, ret = 0; - BIO *cmsbio = NULL, *tmpin = NULL; + BIO *cmsbio = NULL, *tmpin = NULL, *tmpout = NULL; if (!dcont && !check_content(cms)) return 0; + if (dcont && !(flags & CMS_BINARY)) + { + const ASN1_OBJECT *coid = CMS_get0_eContentType(cms); + if (OBJ_obj2nid(coid) == NID_id_ct_asciiTextWithCRLF) + flags |= CMS_ASCIICRLF; + } /* Attempt to find all signer certificates */ @@ -335,7 +373,8 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs, if (!(flags & CMS_NO_SIGNER_CERT_VERIFY)) { cms_certs = CMS_get1_certs(cms); - crls = CMS_get1_crls(cms); + if (!(flags & CMS_NOCRL)) + crls = CMS_get1_crls(cms); for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) { si = sk_CMS_SignerInfo_value(sinfos, i); @@ -380,21 +419,54 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs, } else tmpin = dcont; - + /* If not binary mode and detached generate digests by *writing* + * through the BIO. That makes it possible to canonicalise the + * input. + */ + if (!(flags & SMIME_BINARY) && dcont) + { + /* Create output BIO so we can either handle text or to + * ensure included content doesn't override detached content. + */ + tmpout = cms_get_text_bio(out, flags); + if(!tmpout) + { + CMSerr(CMS_F_CMS_VERIFY,ERR_R_MALLOC_FAILURE); + goto err; + } + cmsbio = CMS_dataInit(cms, tmpout); + if (!cmsbio) + goto err; + /* Don't use SMIME_TEXT for verify: it adds headers and + * we want to remove them. + */ + SMIME_crlf_copy(dcont, cmsbio, flags & ~SMIME_TEXT); - cmsbio=CMS_dataInit(cms, tmpin); - if (!cmsbio) - goto err; + if(flags & CMS_TEXT) + { + if (!SMIME_text(tmpout, out)) + { + CMSerr(CMS_F_CMS_VERIFY,CMS_R_SMIME_TEXT_ERROR); + goto err; + } + } + } + else + { + cmsbio=CMS_dataInit(cms, tmpin); + if (!cmsbio) + goto err; - if (!cms_copy_content(out, cmsbio, flags)) - goto err; + if (!cms_copy_content(out, cmsbio, flags)) + goto err; + } if (!(flags & CMS_NO_CONTENT_VERIFY)) { for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) { si = sk_CMS_SignerInfo_value(sinfos, i); - if (!CMS_SignerInfo_verify_content(si, cmsbio)) + if (CMS_SignerInfo_verify_content(si, cmsbio) <= 0) { CMSerr(CMS_F_CMS_VERIFY, CMS_R_CONTENT_VERIFY_ERROR); @@ -406,10 +478,23 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs, ret = 1; err: - - if (dcont && (tmpin == dcont)) - BIO_pop(cmsbio); - BIO_free_all(cmsbio); + if (!(flags & SMIME_BINARY) && dcont) + { + do_free_upto(cmsbio, tmpout); + if (tmpin != dcont) + BIO_free(tmpin); + } + else + { + + if (dcont && (tmpin == dcont)) + do_free_upto(cmsbio, dcont); + else + BIO_free_all(cmsbio); + } + + if (tmpout && out != tmpout) + BIO_free_all(tmpout); if (cms_certs) sk_X509_pop_free(cms_certs, X509_free); @@ -419,36 +504,51 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs, return ret; } +int CMS_verify_receipt(CMS_ContentInfo *rcms, CMS_ContentInfo *ocms, + STACK_OF(X509) *certs, + X509_STORE *store, unsigned int flags) + { + int r; + flags &= ~(CMS_DETACHED|CMS_TEXT); + r = CMS_verify(rcms, certs, store, NULL, NULL, flags); + if (r <= 0) + return r; + return cms_Receipt_verify(rcms, ocms); + } + CMS_ContentInfo *CMS_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs, BIO *data, unsigned int flags) { CMS_ContentInfo *cms; int i; + cms = CMS_ContentInfo_new(); - if (!cms) + if (!cms || !CMS_SignedData_init(cms)) goto merr; + if (flags & CMS_ASCIICRLF && !CMS_set1_eContentType(cms, OBJ_nid2obj(NID_id_ct_asciiTextWithCRLF))) + goto err; + if (pkey && !CMS_add1_signer(cms, signcert, pkey, NULL, flags)) { CMSerr(CMS_F_CMS_SIGN, CMS_R_ADD_SIGNER_ERROR); goto err; } + for (i = 0; i < sk_X509_num(certs); i++) { X509 *x = sk_X509_value(certs, i); if (!CMS_add1_cert(cms, x)) goto merr; } - /* If no signer or certs initialize signedData */ - if (!pkey && !i && !CMS_SignedData_init(cms)) - goto merr; if(!(flags & CMS_DETACHED)) CMS_set_detached(cms, 0); - if ((flags & (CMS_STREAM|CMS_PARTIAL)) || CMS_final(cms, data, flags)) + if ((flags & (CMS_STREAM|CMS_PARTIAL)) + || CMS_final(cms, data, NULL, flags)) return cms; - - return cms; + else + goto err; merr: CMSerr(CMS_F_CMS_SIGN, ERR_R_MALLOC_FAILURE); @@ -459,6 +559,78 @@ CMS_ContentInfo *CMS_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs, return NULL; } +CMS_ContentInfo *CMS_sign_receipt(CMS_SignerInfo *si, + X509 *signcert, EVP_PKEY *pkey, + STACK_OF(X509) *certs, + unsigned int flags) + { + CMS_SignerInfo *rct_si; + CMS_ContentInfo *cms = NULL; + ASN1_OCTET_STRING **pos, *os; + BIO *rct_cont = NULL; + int r = 0; + + flags &= ~(CMS_STREAM|CMS_TEXT); + /* Not really detached but avoids content being allocated */ + flags |= CMS_PARTIAL|CMS_BINARY|CMS_DETACHED; + if (!pkey || !signcert) + { + CMSerr(CMS_F_CMS_SIGN_RECEIPT, CMS_R_NO_KEY_OR_CERT); + return NULL; + } + + /* Initialize signed data */ + + cms = CMS_sign(NULL, NULL, certs, NULL, flags); + if (!cms) + goto err; + + /* Set inner content type to signed receipt */ + if (!CMS_set1_eContentType(cms, OBJ_nid2obj(NID_id_smime_ct_receipt))) + goto err; + + rct_si = CMS_add1_signer(cms, signcert, pkey, NULL, flags); + if (!rct_si) + { + CMSerr(CMS_F_CMS_SIGN_RECEIPT, CMS_R_ADD_SIGNER_ERROR); + goto err; + } + + os = cms_encode_Receipt(si); + + if (!os) + goto err; + + /* Set content to digest */ + rct_cont = BIO_new_mem_buf(os->data, os->length); + if (!rct_cont) + goto err; + + /* Add msgSigDigest attribute */ + + if (!cms_msgSigDigest_add1(rct_si, si)) + goto err; + + /* Finalize structure */ + if (!CMS_final(cms, rct_cont, NULL, flags)) + goto err; + + /* Set embedded content */ + pos = CMS_get0_content(cms); + *pos = os; + + r = 1; + + err: + if (rct_cont) + BIO_free(rct_cont); + if (r) + return cms; + CMS_ContentInfo_free(cms); + return NULL; + + } + CMS_ContentInfo *CMS_encrypt(STACK_OF(X509) *certs, BIO *data, const EVP_CIPHER *cipher, unsigned int flags) { @@ -481,10 +653,11 @@ CMS_ContentInfo *CMS_encrypt(STACK_OF(X509) *certs, BIO *data, if(!(flags & CMS_DETACHED)) CMS_set_detached(cms, 0); - if ((flags & (CMS_STREAM|CMS_PARTIAL)) || CMS_final(cms, data, flags)) + if ((flags & (CMS_STREAM|CMS_PARTIAL)) + || CMS_final(cms, data, NULL, flags)) return cms; - - return cms; + else + goto err; merr: CMSerr(CMS_F_CMS_ENCRYPT, ERR_R_MALLOC_FAILURE); @@ -493,58 +666,206 @@ CMS_ContentInfo *CMS_encrypt(STACK_OF(X509) *certs, BIO *data, CMS_ContentInfo_free(cms); return NULL; } - -int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert, - BIO *dcont, BIO *out, - unsigned int flags) + +static int cms_kari_set1_pkey(CMS_ContentInfo *cms, CMS_RecipientInfo *ri, + EVP_PKEY *pk, X509 *cert) + { + int i; + STACK_OF(CMS_RecipientEncryptedKey) *reks; + CMS_RecipientEncryptedKey *rek; + reks = CMS_RecipientInfo_kari_get0_reks(ri); + if (!cert) + return 0; + for (i = 0; i < sk_CMS_RecipientEncryptedKey_num(reks); i++) + { + int rv; + rek = sk_CMS_RecipientEncryptedKey_value(reks, i); + if (CMS_RecipientEncryptedKey_cert_cmp(rek, cert)) + continue; + CMS_RecipientInfo_kari_set0_pkey(ri, pk); + rv = CMS_RecipientInfo_kari_decrypt(cms, ri, rek); + CMS_RecipientInfo_kari_set0_pkey(ri, NULL); + if (rv > 0) + return 1; + return -1; + } + return 0; + } + +int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert) { STACK_OF(CMS_RecipientInfo) *ris; CMS_RecipientInfo *ri; - int i, r; - BIO *cont; - if (OBJ_obj2nid(CMS_get0_type(cms)) != NID_pkcs7_enveloped) + int i, r, ri_type; + int debug = 0, match_ri = 0; + ris = CMS_get0_RecipientInfos(cms); + if (ris) + debug = cms->d.envelopedData->encryptedContentInfo->debug; + ri_type = cms_pkey_get_ri_type(pk); + if (ri_type == CMS_RECIPINFO_NONE) { - CMSerr(CMS_F_CMS_DECRYPT, CMS_R_TYPE_NOT_ENVELOPED_DATA); + CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY, + CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE); return 0; } - if (!dcont && !check_content(cms)) - return 0; + + for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++) + { + ri = sk_CMS_RecipientInfo_value(ris, i); + if (CMS_RecipientInfo_type(ri) != ri_type) + continue; + match_ri = 1; + if (ri_type == CMS_RECIPINFO_AGREE) + { + r = cms_kari_set1_pkey(cms, ri, pk, cert); + if (r > 0) + return 1; + if (r < 0) + return 0; + } + /* If we have a cert try matching RecipientInfo + * otherwise try them all. + */ + else if (!cert || !CMS_RecipientInfo_ktri_cert_cmp(ri, cert)) + { + CMS_RecipientInfo_set0_pkey(ri, pk); + r = CMS_RecipientInfo_decrypt(cms, ri); + CMS_RecipientInfo_set0_pkey(ri, NULL); + if (cert) + { + /* If not debugging clear any error and + * return success to avoid leaking of + * information useful to MMA + */ + if (!debug) + { + ERR_clear_error(); + return 1; + } + if (r > 0) + return 1; + CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY, + CMS_R_DECRYPT_ERROR); + return 0; + } + /* If no cert and not debugging don't leave loop + * after first successful decrypt. Always attempt + * to decrypt all recipients to avoid leaking timing + * of a successful decrypt. + */ + else if (r > 0 && debug) + return 1; + } + } + /* If no cert and not debugging always return success */ + if (match_ri && !cert && !debug) + { + ERR_clear_error(); + return 1; + } + + CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY, CMS_R_NO_MATCHING_RECIPIENT); + return 0; + + } + +int CMS_decrypt_set1_key(CMS_ContentInfo *cms, + unsigned char *key, size_t keylen, + unsigned char *id, size_t idlen) + { + STACK_OF(CMS_RecipientInfo) *ris; + CMS_RecipientInfo *ri; + int i, r; ris = CMS_get0_RecipientInfos(cms); for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++) { ri = sk_CMS_RecipientInfo_value(ris, i); - if (CMS_RecipientInfo_type(ri) != CMS_RECIPINFO_TRANS) - continue; - /* If we have a cert try matching RecipientInfo otherwise - * try them all. + if (CMS_RecipientInfo_type(ri) != CMS_RECIPINFO_KEK) + continue; + + /* If we have an id try matching RecipientInfo + * otherwise try them all. */ - if (!cert || (CMS_RecipientInfo_ktri_cert_cmp(ri, cert) == 0)) + if (!id || (CMS_RecipientInfo_kekri_id_cmp(ri, id, idlen) == 0)) { - if (CMS_RecipientInfo_decrypt(cms, ri, pk) > 0) - break; - else if (cert) + CMS_RecipientInfo_set0_key(ri, key, keylen); + r = CMS_RecipientInfo_decrypt(cms, ri); + CMS_RecipientInfo_set0_key(ri, NULL, 0); + if (r > 0) + return 1; + if (id) + { + CMSerr(CMS_F_CMS_DECRYPT_SET1_KEY, + CMS_R_DECRYPT_ERROR); return 0; + } + ERR_clear_error(); } } - if (i == sk_CMS_RecipientInfo_num(ris)) + CMSerr(CMS_F_CMS_DECRYPT_SET1_KEY, CMS_R_NO_MATCHING_RECIPIENT); + return 0; + + } + +int CMS_decrypt_set1_password(CMS_ContentInfo *cms, + unsigned char *pass, ossl_ssize_t passlen) + { + STACK_OF(CMS_RecipientInfo) *ris; + CMS_RecipientInfo *ri; + int i, r; + ris = CMS_get0_RecipientInfos(cms); + for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++) + { + ri = sk_CMS_RecipientInfo_value(ris, i); + if (CMS_RecipientInfo_type(ri) != CMS_RECIPINFO_PASS) + continue; + CMS_RecipientInfo_set0_password(ri, pass, passlen); + r = CMS_RecipientInfo_decrypt(cms, ri); + CMS_RecipientInfo_set0_password(ri, NULL, 0); + if (r > 0) + return 1; + } + + CMSerr(CMS_F_CMS_DECRYPT_SET1_PASSWORD, CMS_R_NO_MATCHING_RECIPIENT); + return 0; + + } + +int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert, + BIO *dcont, BIO *out, + unsigned int flags) + { + int r; + BIO *cont; + if (OBJ_obj2nid(CMS_get0_type(cms)) != NID_pkcs7_enveloped) { - CMSerr(CMS_F_CMS_DECRYPT, CMS_R_NO_MATCHING_RECIPIENT); + CMSerr(CMS_F_CMS_DECRYPT, CMS_R_TYPE_NOT_ENVELOPED_DATA); return 0; } + if (!dcont && !check_content(cms)) + return 0; + if (flags & CMS_DEBUG_DECRYPT) + cms->d.envelopedData->encryptedContentInfo->debug = 1; + else + cms->d.envelopedData->encryptedContentInfo->debug = 0; + if (!pk && !cert && !dcont && !out) + return 1; + if (pk && !CMS_decrypt_set1_pkey(cms, pk, cert)) + return 0; cont = CMS_dataInit(cms, dcont); if (!cont) return 0; r = cms_copy_content(out, cont, flags); - BIO_free_all(cont); + do_free_upto(cont, dcont); return r; } -int CMS_final(CMS_ContentInfo *cms, BIO *data, int flags) +int CMS_final(CMS_ContentInfo *cms, BIO *data, BIO *dcont, unsigned int flags) { BIO *cmsbio; int ret = 0; - if (!(cmsbio = CMS_dataInit(cms, NULL))) + if (!(cmsbio = CMS_dataInit(cms, dcont))) { CMSerr(CMS_F_CMS_FINAL,ERR_R_MALLOC_FAILURE); return 0; @@ -564,7 +885,7 @@ int CMS_final(CMS_ContentInfo *cms, BIO *data, int flags) ret = 1; err: - BIO_free_all(cmsbio); + do_free_upto(cmsbio, dcont); return ret; @@ -591,7 +912,7 @@ int CMS_uncompress(CMS_ContentInfo *cms, BIO *dcont, BIO *out, if (!cont) return 0; r = cms_copy_content(out, cont, flags); - BIO_free_all(cont); + do_free_upto(cont, dcont); return r; } @@ -607,7 +928,7 @@ CMS_ContentInfo *CMS_compress(BIO *in, int comp_nid, unsigned int flags) if(!(flags & CMS_DETACHED)) CMS_set_detached(cms, 0); - if ((flags & CMS_STREAM) || CMS_final(cms, in, flags)) + if ((flags & CMS_STREAM) || CMS_final(cms, in, NULL, flags)) return cms; CMS_ContentInfo_free(cms);