From 6e378a5f5db5a83b864da68e6b767911386269a1 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Mon, 26 Sep 2011 18:23:23 -0400 Subject: [PATCH 144/150] - do a precheck to make require_crl_checking work as expected --- src/plugins/preauth/pkinit/pkinit_crypto_nss.c | 215 +++++++++++++++++++++--- 1 files changed, 188 insertions(+), 27 deletions(-) diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c index c52811a..db350e4 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c +++ b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c @@ -4433,6 +4433,148 @@ cms_envelopeddata_create(krb5_context context, return 0; } +/* Check if this cert is marked as a CA which is trusted to issue certs for + * the indicated usage. Return PR_TRUE if it is. */ +static PRBool +crypto_is_cert_trusted(CERTCertificate *cert, SECCertUsage usage) +{ + CERTCertTrust trust; + unsigned int ca_trust; + + if (usage == certUsageSSLClient) + ca_trust = CERTDB_TRUSTED_CLIENT_CA; + else if (usage == certUsageSSLServer) + ca_trust = CERTDB_TRUSTED_CA; + else { + pkiDebug("%s: internal error: needed CA trust unknown\n", __FUNCTION__); + return PR_FALSE; + } + memset(&trust, 0, sizeof(trust)); + if (CERT_GetCertTrust(cert, &trust) != SECSuccess) { + pkiDebug("%s: unable to find trust for \"%s\"\n", __FUNCTION__, + cert->subjectName); + return PR_FALSE; + } + if ((SEC_GET_TRUST_FLAGS(&trust, trustSSL) & ca_trust) != ca_trust) { + pkiDebug("%s: \"%s\" is not a trusted CA\n", __FUNCTION__, + cert->subjectName); + return PR_FALSE; + } + return PR_TRUE; +} + +/* Check if this cert includes an AuthorityInfoAccess extension which points + * to an OCSP responder. Return PR_TRUE if it does. */ +static PRBool +crypto_cert_has_ocsp_responder(CERTCertificate *cert) +{ + CERTAuthInfoAccess **aia; + SECOidData *ocsp; + SECItem encoded_aia; + int i; + + /* Look up the OID for "use an OCSP responder". */ + ocsp = SECOID_FindOIDByTag(SEC_OID_PKIX_OCSP); + if (ocsp == NULL) { + pkiDebug("%s: internal error: OCSP not known\n", __FUNCTION__); + return PR_FALSE; + } + /* Find the AIA extension. */ + memset(&encoded_aia, 0, sizeof(encoded_aia)); + if (CERT_FindCertExtension(cert, SEC_OID_X509_AUTH_INFO_ACCESS, + &encoded_aia) != SECSuccess) { + pkiDebug("%s: no AuthorityInfoAccess extension for \"%s\"\n", + __FUNCTION__, cert->subjectName); + return PR_FALSE; + } + /* Decode the AIA extension. */ + aia = CERT_DecodeAuthInfoAccessExtension(cert->arena, &encoded_aia); + if (aia == NULL) { + pkiDebug("%s: error parsing AuthorityInfoAccess for \"%s\"\n", + __FUNCTION__, cert->subjectName); + return PR_FALSE; + } + /* We're looking for at least one OCSP responder. */ + for (i = 0; (aia[i] != NULL); i++) + if (SECITEM_ItemsAreEqual(&(aia[i]->method), &(ocsp->oid))) { + pkiDebug("%s: found OCSP responder for \"%s\"\n", + __FUNCTION__, cert->subjectName); + return PR_TRUE; + } + return PR_FALSE; +} + +/* In the original implementation, the assumption has been that we'd use any + * CRLs, and if we were missing a CRL for the certificate or any point in its + * issuing chain, we'd raise a failure iff the require_crl_checking flag was + * set. + * + * This is not exactly how NSS does things. When checking the revocation + * status of a particular certificate, NSS will consult a cached copy of a CRL + * issued by the certificate's issuer if one's available. If the CRL shows + * that the certificate is revoked, it returns an error. If it succeeds, + * however, processing continues, and if the certificate contains an AIA + * extension which lists an OCSP responder, the library attempts to contact the + * responder to also give it a chance to tell us that the certificate has been + * revoked. We can control what happens if this connection attempt fails by + * calling CERT_SetOCSPFailureMode(). + * + * We attempt to compensate for this difference in behavior by walking the + * issuing chain ourselves, ensuring that for the certificate and all of its + * issuers, that either we have a CRL on-hand for its issuer, or if OCSP + * checking is allowed, that the certificate contains the location of an OCSP + * responder. We stop only when we reach a trusted CA certificate, as NSS + * does. */ +static int +crypto_check_for_revocation_information(CERTCertificate *cert, + CERTCertDBHandle *certdb, + PRBool allow_ocsp_checking, + SECCertUsage usage) +{ + CERTCertificate *issuer; + CERTSignedCrl *crl; + + issuer = CERT_FindCertIssuer(cert, PR_Now(), usage); + while (issuer != NULL) { + /* Do we have a CRL for this cert's issuer? */ + crl = SEC_FindCrlByName(certdb, &cert->derIssuer, SEC_CRL_TYPE); + if (crl != NULL) { + pkiDebug("%s: have CRL for \"%s\"\n", __FUNCTION__, + cert->issuerName); + } else { + if (allow_ocsp_checking) { + /* Check if the cert points to an OCSP responder. */ + if (!crypto_cert_has_ocsp_responder(cert)) { + /* No CRL, no OCSP responder. */ + pkiDebug("%s: no OCSP responder for \"%s\"\n", __FUNCTION__, + cert->subjectName); + return -1; + } + } else { + /* No CRL, and OCSP not allowed. */ + pkiDebug("%s: no CRL for issuer \"%s\"\n", __FUNCTION__, + cert->issuerName); + return -1; + } + } + /* Check if this issuer is a trusted CA. If it is, we're done. */ + if (crypto_is_cert_trusted(issuer, usage)) { + pkiDebug("%s: \"%s\" is a trusted CA\n", __FUNCTION__, + issuer->subjectName); + return 0; + } + /* Move on to the next link in the chain. */ + cert = issuer; + issuer = CERT_FindCertIssuer(cert, PR_Now(), usage); + if (issuer == NULL) { + pkiDebug("%s: unable to find issuer for \"%s\"\n", __FUNCTION__, + cert->subjectName); + return -1; + } + } + return -1; +} + /* 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. */ @@ -4463,6 +4605,7 @@ crypto_signeddata_common_verify(krb5_context context, SECStatus status; SECItem *edata; int n_signers; + PRBool allow_ocsp_checking = PR_TRUE; *is_signed_out = 0; @@ -4547,39 +4690,57 @@ crypto_signeddata_common_verify(krb5_context context, NSS_CMSMessage_Destroy(ecmsg); return ENOENT; } - /* Verify the signer's certificate. */ if (!NSS_CMSSignedData_HasDigests(sdata)) { pkiDebug("%s: no digests?\n", __FUNCTION__); if (ecmsg != NULL) NSS_CMSMessage_Destroy(ecmsg); return ENOENT; } - status = CERT_EnableOCSPChecking(certdb); - if (status != SECSuccess) { - pkiDebug("%s: error enabling OCSP: %s\n", __FUNCTION__, - PR_ErrorToString(status == SECFailure ? - PORT_GetError() : status, - PR_LANGUAGE_I_DEFAULT)); - if (ecmsg != NULL) - NSS_CMSMessage_Destroy(ecmsg); - return ENOMEM; - } - /* FIXME: NSS will use OCSP if there's no applicable CRL cached, and if - * OCSP fails, we'll take advice from the require_crl_checking flag, - * because it has to affect something. */ - ocsp_failure_mode = require_crl_checking ? - ocspMode_FailureIsVerificationFailure : - ocspMode_FailureIsNotAVerificationFailure; - status = CERT_SetOCSPFailureMode(ocsp_failure_mode); - if (status != SECSuccess) { - pkiDebug("%s: error setting OCSP failure mode: %s\n", - __FUNCTION__, - PR_ErrorToString(status == SECFailure ? - PORT_GetError() : status, - PR_LANGUAGE_I_DEFAULT)); - if (ecmsg != NULL) - NSS_CMSMessage_Destroy(ecmsg); - return ENOMEM; + if (require_crl_checking && (signer->cert != NULL)) + if (crypto_check_for_revocation_information(signer->cert, certdb, + allow_ocsp_checking, + usage) != 0) { + if (ecmsg != NULL) + NSS_CMSMessage_Destroy(ecmsg); + return KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE; + } + if (allow_ocsp_checking) { + status = CERT_EnableOCSPChecking(certdb); + if (status != SECSuccess) { + pkiDebug("%s: error enabling OCSP: %s\n", __FUNCTION__, + PR_ErrorToString(status == SECFailure ? + PORT_GetError() : status, + PR_LANGUAGE_I_DEFAULT)); + if (ecmsg != NULL) + NSS_CMSMessage_Destroy(ecmsg); + return ENOMEM; + } + ocsp_failure_mode = require_crl_checking ? + ocspMode_FailureIsVerificationFailure : + ocspMode_FailureIsNotAVerificationFailure; + status = CERT_SetOCSPFailureMode(ocsp_failure_mode); + if (status != SECSuccess) { + pkiDebug("%s: error setting OCSP failure mode: %s\n", + __FUNCTION__, + PR_ErrorToString(status == SECFailure ? + PORT_GetError() : status, + PR_LANGUAGE_I_DEFAULT)); + if (ecmsg != NULL) + NSS_CMSMessage_Destroy(ecmsg); + return ENOMEM; + } + } else { + status = CERT_DisableOCSPChecking(certdb); + if ((status != SECSuccess) && + (PORT_GetError() != SEC_ERROR_OCSP_NOT_ENABLED)) { + pkiDebug("%s: error disabling OCSP: %s\n", __FUNCTION__, + PR_ErrorToString(status == SECFailure ? + PORT_GetError() : status, + PR_LANGUAGE_I_DEFAULT)); + if (ecmsg != NULL) + NSS_CMSMessage_Destroy(ecmsg); + return ENOMEM; + } } status = NSS_CMSSignedData_VerifySignerInfo(sdata, 0, certdb, usage); if (status != SECSuccess) { -- 1.7.6.4