From c238d7c31712e12b7e30819dc5c2a40233e82f31 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Tue, 12 Oct 2010 17:20:26 -0400 Subject: [PATCH 048/150] - the enveloped data routines actually need to process signed-then-enveloped data, so factor out as much of the existing signed-data logic as we can and share it --- src/plugins/preauth/pkinit/pkinit_crypto_nss.c | 360 ++++++++++++++++-------- 1 files changed, 241 insertions(+), 119 deletions(-) diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c index f267432..1de17d4 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c +++ b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c @@ -3011,6 +3011,69 @@ cms_contentinfo_create(krb5_context context, return 0; } +/* Create a signed-data content info, add a signature to it, and return it. */ +static krb5_error_code +crypto_signeddata_common_create(krb5_context context, + pkinit_plg_crypto_context plg_cryptoctx, + pkinit_req_crypto_context req_cryptoctx, + pkinit_identity_crypto_context id_cryptoctx, + NSSCMSMessage *msg, + NSSCMSContentInfo *info, + SECOidTag digest, + PRBool add_signed_attributes, + NSSCMSSignedData **signed_data) +{ + NSSCMSSignedData *sdata; + NSSCMSSignerInfo *signer; + + if (id_cryptoctx->id_cert == NULL) { + pkiDebug("%s: no signer identity\n", __FUNCTION__); + return ENOENT; + } + + /* Create a signed-data object. */ + sdata = NSS_CMSSignedData_Create(msg); + if (sdata == NULL) { + return ENOMEM; + } + + /* Create a signer and add it to the signed-data pointer. */ + signer = NSS_CMSSignerInfo_Create(msg, id_cryptoctx->id_cert, digest); + if (signer == NULL) { + return ENOMEM; + } + if (NSS_CMSSignerInfo_IncludeCerts(signer, NSSCMSCM_CertChainWithRoot, + certUsageAnyCA) != SECSuccess) { + pkiDebug("%s: error setting IncludeCerts\n", __FUNCTION__); + return ENOMEM; + } + if (NSS_CMSSignedData_AddSignerInfo(sdata, signer) != SECSuccess) { + return ENOMEM; + } + + if (add_signed_attributes) { + /* The presence of any signed attribute means the digest + * becomes a signed attribute, too. */ + if (NSS_CMSSignerInfo_AddSigningTime(signer, + PR_Now()) != SECSuccess) { + pkiDebug("%s: error adding signing time\n", + __FUNCTION__); + return ENOMEM; + } + } + + /* Store the signed-data object in the passed-in content-info. */ + if (NSS_CMSContentInfo_SetContent_SignedData(msg, info, + sdata) != SECSuccess) { + pkiDebug("%s: error adding signing time\n", + __FUNCTION__); + return ENOMEM; + } + *signed_data = sdata; + return 0; +} + +/* Create signed-then-enveloped data. */ krb5_error_code cms_envelopeddata_create(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, @@ -3028,11 +3091,37 @@ cms_envelopeddata_create(krb5_context context, NSSCMSContentInfo *info; NSSCMSEnvelopedData *env; NSSCMSRecipientInfo *recipient; + NSSCMSSignedData *sdata; PLArenaPool *pool; - SECOidTag encapsulated_tag; + SECOidTag encapsulated_tag, digest; SECItem plain, encoded; + PRBool add_signed_attributes; + + switch (pa_type) { + case KRB5_PADATA_PK_AS_REQ_OLD: + case KRB5_PADATA_PK_AS_REP_OLD: + digest = SEC_OID_MD5; + add_signed_attributes = PR_FALSE; + encapsulated_tag = get_pkinit_data_rkey_data_tag(); + break; + case KRB5_PADATA_PK_AS_REQ: + case KRB5_PADATA_PK_AS_REP: + digest = SEC_OID_SHA1; + add_signed_attributes = PR_TRUE; + encapsulated_tag = get_pkinit_data_rkey_data_tag(); + break; + default: + return ENOSYS; + break; + } + + if (id_cryptoctx->id_cert == NULL) { + pkiDebug("%s: no signer identity\n", __FUNCTION__); + return ENOENT; + } if (req_cryptoctx->peer_cert == NULL) { + pkiDebug("%s: no recipient identity\n", __FUNCTION__); return ENOENT; } @@ -3083,15 +3172,36 @@ cms_envelopeddata_create(krb5_context context, return ENOMEM; } - /* Set the raw data as the contents for the enveloped-data pointer. */ + /* Create a signed-data pointer and set it as the enveloped-data's + * contents. */ info = NSS_CMSEnvelopedData_GetContentInfo(env); if (info == NULL) { PORT_FreeArena(pool, PR_TRUE); return ENOMEM; } + sdata = NULL; + if ((crypto_signeddata_common_create(context, + plg_cryptoctx, + req_cryptoctx, + id_cryptoctx, + msg, + info, + digest, + add_signed_attributes, + &sdata) != 0) || + (sdata == NULL)) { + PORT_FreeArena(pool, PR_TRUE); + return ENOMEM; + } + + /* Set the raw data as the contents for the signed-data. */ + info = NSS_CMSSignedData_GetContentInfo(sdata); + if (info == NULL) { + PORT_FreeArena(pool, PR_TRUE); + return ENOMEM; + } plain.data = key_pack; plain.len = key_pack_len; - encapsulated_tag = get_pkinit_data_rkey_data_tag(); if (NSS_CMSContentInfo_SetContent(msg, info, encapsulated_tag, &plain) != SECSuccess) { PORT_FreeArena(pool, PR_TRUE); @@ -3120,6 +3230,75 @@ cms_envelopeddata_create(krb5_context context, return 0; } +/* Verify that we have a signed-data content info, that it has one signer, that + * the signer can be trusted, and then check the type of the encapsulated + * content and return that content. */ +static krb5_error_code +crypto_signeddata_common_verify(krb5_context context, + pkinit_plg_crypto_context plg_cryptoctx, + pkinit_req_crypto_context req_cryptoctx, + pkinit_identity_crypto_context id_cryptoctx, + int require_crl_checking, + NSSCMSContentInfo *cinfo, + CERTCertDBHandle *certdb, + SECCertUsage usage, + SECOidTag expected_type, + SECItem **plain, + int *is_signed) +{ + NSSCMSSignedData *sdata; + NSSCMSSignerInfo *signer; + NSSCMSContentInfo *ecinfo; + CERTCertificate *cert; + SECOidTag encapsulated_tag; + SECStatus status; + + *is_signed = 0; + + /* Check that we have signed data, that it has exactly one signature, + * and fish out the signer information. */ + if (NSS_CMSContentInfo_GetContentTypeTag(cinfo) != + SEC_OID_PKCS7_SIGNED_DATA) { + return ENOMEM; /* FIXME: better error? */ + } + sdata = NSS_CMSContentInfo_GetContent(cinfo); + if ((sdata == NULL) || + (NSS_CMSSignedData_SignerInfoCount(sdata) != 1)) { + return ENOMEM; /* FIXME: better error? */ + } + signer = NSS_CMSSignedData_GetSignerInfo(sdata, 0); + if (signer == NULL) { + return ENOMEM; /* FIXME: better error? */ + } + /* Verify the signer's certificate. */ + status = NSS_CMSSignedData_VerifySignerInfo(sdata, 0, certdb, + usage); + if (status != SECSuccess) { + return ENOMEM; /* FIXME: better error? */ + } + /* Pull out the payload. */ + ecinfo = NSS_CMSSignedData_GetContentInfo(sdata); + if (ecinfo == NULL) { + return ENOMEM; + } + encapsulated_tag = NSS_CMSContentInfo_GetContentTypeTag(ecinfo); + if (encapsulated_tag != expected_type) { + return ENOMEM; /* FIXME: better error? */ + } + *plain = NSS_CMSContentInfo_GetContent(ecinfo); + /* Save the peer cert -- we'll need it later. */ + if (req_cryptoctx->peer_cert != NULL) { + CERT_DestroyCertificate(req_cryptoctx->peer_cert); + } + cert = NSS_CMSSignerInfo_GetSigningCertificate(signer, certdb); + req_cryptoctx->peer_cert = CERT_DupCertificate(cert); + if (is_signed != NULL) { + *is_signed = 1; + } + return 0; +} + +/* Verify signed-then-enveloped data. */ krb5_error_code cms_envelopeddata_verify(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, @@ -3138,7 +3317,9 @@ cms_envelopeddata_verify(krb5_context context, CERTCertDBHandle *certdb; PLArenaPool *pool; SECItem *plain, encoded; - SECOidTag expected_tag, encapsulated_tag; + SECCertUsage usage; + SECOidTag expected_tag; + int is_signed; pool = PORT_NewArena(sizeof(double)); if (pool == NULL) { @@ -3176,26 +3357,38 @@ cms_envelopeddata_verify(krb5_context context, /* FIXME: is that it? We don't have to do anything else? */ /* FIXME: what do we do about "require_crl_checking"? */ - /* Pull out the payload. */ + /* Pull out the encapsulated content. It should be signed-data. */ info = NSS_CMSEnvelopedData_GetContentInfo(env); if (info == NULL) { NSS_CMSMessage_Destroy(msg); PORT_FreeArena(pool, PR_TRUE); return ENOMEM; } + + /* Pull out the signed data and verify it. */ expected_tag = get_pkinit_data_rkey_data_tag(); - encapsulated_tag = NSS_CMSContentInfo_GetContentTypeTag(info); - if (encapsulated_tag != expected_tag) { + usage = certUsageSSLServer; + plain = NULL; + if ((crypto_signeddata_common_verify(context, + plg_cryptoctx, + req_cryptoctx, + id_cryptoctx, + require_crl_checking, + info, + certdb, + usage, + expected_tag, + &plain, + &is_signed) != 0) || + (plain == NULL) || + !is_signed) { NSS_CMSMessage_Destroy(msg); PORT_FreeArena(pool, PR_TRUE); return ENOMEM; /* FIXME: better error? */ } - plain = NSS_CMSContentInfo_GetContent(info); - /* Export the payload. */ - if ((plain == NULL) || - (secitem_to_buf_len(plain, signed_data, signed_data_len) != 0)) { + if (secitem_to_buf_len(plain, signed_data, signed_data_len) != 0) { NSS_CMSMessage_Destroy(msg); PORT_FreeArena(pool, PR_TRUE); return ENOMEM; @@ -3221,10 +3414,9 @@ cms_signeddata_create(krb5_context context, NSSCMSMessage *msg; NSSCMSContentInfo *info; NSSCMSSignedData *sdata; - NSSCMSSignerInfo *signer; PLArenaPool *pool; SECItem plain, encoded; - struct wrapped_data wrapped_plain; + struct wrapped_data wrapper; SECOidTag digest, encapsulated_tag; PRBool add_signed_attributes; @@ -3245,20 +3437,11 @@ cms_signeddata_create(krb5_context context, encapsulated_tag = get_pkinit_data_dhkey_data_tag(); break; case CMS_ENVEL_SERVER: - digest = SEC_OID_SHA1; - add_signed_attributes = PR_FALSE; - encapsulated_tag = get_pkinit_data_rkey_data_tag(); - break; default: return ENOSYS; break; } - if (id_cryptoctx->id_cert == NULL) { - pkiDebug("%s: no signer identity\n", __FUNCTION__); - return ENOENT; - } - pool = PORT_NewArena(sizeof(double)); if (pool == NULL) { return ENOMEM; @@ -3273,75 +3456,49 @@ cms_signeddata_create(krb5_context context, /* Create a signed-data pointer and set it as the message's * contents. */ - sdata = NSS_CMSSignedData_Create(msg); - if (sdata == NULL) { - PORT_FreeArena(pool, PR_TRUE); - return ENOMEM; - } info = NSS_CMSMessage_GetContentInfo(msg); if (info == NULL) { PORT_FreeArena(pool, PR_TRUE); return ENOMEM; } - if (NSS_CMSContentInfo_SetContent_SignedData(msg, info, - sdata) != SECSuccess) { - PORT_FreeArena(pool, PR_TRUE); - return ENOMEM; - } - - /* Create a signer and add it to the signed-data pointer. */ - signer = NSS_CMSSignerInfo_Create(msg, id_cryptoctx->id_cert, - digest); - if (signer == NULL) { - PORT_FreeArena(pool, PR_TRUE); - return ENOMEM; - } - if (NSS_CMSSignerInfo_IncludeCerts(signer, NSSCMSCM_CertChainWithRoot, - certUsageAnyCA) != SECSuccess) { - PORT_FreeArena(pool, PR_TRUE); - pkiDebug("%s: error setting IncludeCerts\n", __FUNCTION__); - return ENOMEM; - } - if (NSS_CMSSignedData_AddSignerInfo(sdata, signer) != SECSuccess) { + sdata = NULL; + if ((crypto_signeddata_common_create(context, + plg_cryptoctx, + req_cryptoctx, + id_cryptoctx, + msg, + info, + digest, + add_signed_attributes, + &sdata) != 0) || + (sdata == NULL)) { PORT_FreeArena(pool, PR_TRUE); return ENOMEM; } - if (add_signed_attributes) { - /* The presence of any signed attribute means the digest - * becomes a signed attribute, too. */ - if (NSS_CMSSignerInfo_AddSigningTime(signer, - PR_Now()) != SECSuccess) { - PORT_FreeArena(pool, PR_TRUE); - pkiDebug("%s: error adding signing time\n", - __FUNCTION__); - return ENOMEM; - } - } - - memset(&plain, 0, sizeof(plain)); - plain.data = payload; - plain.len = payload_len; - /* Set the data as the contents of the signed-data. */ info = NSS_CMSSignedData_GetContentInfo(sdata); if (info == NULL) { PORT_FreeArena(pool, PR_TRUE); return ENOMEM; } - memset(&wrapped_plain, 0, sizeof(wrapped_plain)); - wrapped_plain.wrapped_data = &plain; if (NSS_CMSContentInfo_SetContent(msg, info, encapsulated_tag, - &wrapped_plain) != SECSuccess) { + NULL) != SECSuccess) { pkiDebug("%s: error setting encapsulated content type\n", __FUNCTION__); PORT_FreeArena(pool, PR_TRUE); return ENOMEM; } + memset(&plain, 0, sizeof(plain)); + plain.data = payload; + plain.len = payload_len; + memset(&wrapper, 0, sizeof(wrapper)); + wrapper.wrapped_data = &plain; + /* Encode and export. */ memset(&encoded, 0, sizeof(encoded)); - if (NSS_CMSDEREncode(msg, NULL, &encoded, pool) != SECSuccess) { + if (NSS_CMSDEREncode(msg, &plain, &encoded, pool) != SECSuccess) { PORT_FreeArena(pool, PR_TRUE); pkiDebug("%s: error encoding signed-data\n", __FUNCTION__); @@ -3378,13 +3535,9 @@ cms_signeddata_verify(krb5_context context, { NSSCMSMessage *msg; NSSCMSContentInfo *info; - NSSCMSSignedData *sdata; - NSSCMSSignerInfo *signer; CERTCertDBHandle *certdb; - CERTCertificate *cert; SECCertUsage usage; - SECOidTag expected_tag, encapsulated_tag; - SECStatus status; + SECOidTag expected_tag; PLArenaPool *pool; SECItem *plain, encoded; @@ -3402,9 +3555,6 @@ cms_signeddata_verify(krb5_context context, expected_tag = get_pkinit_data_dhkey_data_tag(); break; case CMS_ENVEL_SERVER: - usage = certUsageSSLServer; - expected_tag = get_pkinit_data_rkey_data_tag(); - break; default: return ENOSYS; break; @@ -3434,58 +3584,30 @@ cms_signeddata_verify(krb5_context context, PORT_FreeArena(pool, PR_TRUE); return ENOMEM; } - switch (NSS_CMSContentInfo_GetContentTypeTag(info)) { case SEC_OID_PKCS7_SIGNED_DATA: - /* Check that we have exactly one signature, and fish out the - * signer information. */ - sdata = NSS_CMSContentInfo_GetContent(info); - if (NSS_CMSMessage_IsSigned(msg) && - (sdata != NULL) && - (NSS_CMSSignedData_SignerInfoCount(sdata) != 1)) { - *is_signed = 1; - } else { - NSS_CMSMessage_Destroy(msg); - PORT_FreeArena(pool, PR_TRUE); - return ENOMEM; /* FIXME: better error? */ - } - signer = NSS_CMSSignedData_GetSignerInfo(sdata, 0); - if (signer == NULL) { - NSS_CMSMessage_Destroy(msg); - PORT_FreeArena(pool, PR_TRUE); - return ENOMEM; - } - /* Verify the signer's certificate. */ - status = NSS_CMSSignedData_VerifySignerInfo(sdata, 0, certdb, - usage); - if (status != SECSuccess) { - NSS_CMSMessage_Destroy(msg); - PORT_FreeArena(pool, PR_TRUE); - return ENOMEM; /* FIXME: better error? */ - } - /* Pull out the payload. */ - info = NSS_CMSSignedData_GetContentInfo(sdata); - if (info == NULL) { - NSS_CMSMessage_Destroy(msg); - PORT_FreeArena(pool, PR_TRUE); - return ENOMEM; - } - encapsulated_tag = NSS_CMSContentInfo_GetContentTypeTag(info); - if (encapsulated_tag != expected_tag) { + /* It's signed: try to verify the signature. */ + plain = NULL; + if ((crypto_signeddata_common_verify(context, + plg_cryptoctx, + req_cryptoctx, + id_cryptoctx, + require_crl_checking, + info, + certdb, + usage, + expected_tag, + &plain, + is_signed) != 0) || + (plain == NULL) || + (!(*is_signed))) { NSS_CMSMessage_Destroy(msg); PORT_FreeArena(pool, PR_TRUE); return ENOMEM; /* FIXME: better error? */ } - plain = NSS_CMSContentInfo_GetContent(info); - /* Save the peer cert -- we'll need it later. */ - if (req_cryptoctx->peer_cert != NULL) { - CERT_DestroyCertificate(req_cryptoctx->peer_cert); - } - cert = NSS_CMSSignerInfo_GetSigningCertificate(signer, certdb); - req_cryptoctx->peer_cert = CERT_DupCertificate(cert); break; case SEC_OID_PKCS7_DATA: - /* Pull out the payload. */ + /* It's not signed: try to pull out the payload. */ *is_signed = 0; plain = NSS_CMSContentInfo_GetContent(info); break; -- 1.7.6.4