diff options
author | wtc%google.com <devnull@localhost> | 2011-09-14 23:16:16 +0000 |
---|---|---|
committer | wtc%google.com <devnull@localhost> | 2011-09-14 23:16:16 +0000 |
commit | ee0880238fb2119deaef3b8f122f289809845c9f (patch) | |
tree | 9fdba0024460db2b0ca7f33fb9af46c89db4c253 | |
parent | 96df47953b68d6a177eed23a659c5a925288fd65 (diff) | |
download | nss-hg-ee0880238fb2119deaef3b8f122f289809845c9f.tar.gz |
Bug 642503: Generic blacklisting mechanism for bogus certs (NSS trust
module), patch 2 part 2: revoke certs in libpkix and add test cases. The
patch is written by Bob Relyea <rrelyea@redhat.com>. r=wtc.
Modified Files:
lib/certdb/certi.h lib/libpkix/include/pkix_pl_pki.h
lib/libpkix/pkix/top/pkix_build.c
lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c tests/cert/cert.sh
tests/common/init.sh
-rw-r--r-- | security/nss/lib/certdb/certi.h | 14 | ||||
-rwxr-xr-x | security/nss/lib/libpkix/include/pkix_pl_pki.h | 40 | ||||
-rwxr-xr-x | security/nss/lib/libpkix/pkix/top/pkix_build.c | 10 | ||||
-rw-r--r-- | security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c | 163 | ||||
-rwxr-xr-x | security/nss/tests/cert/cert.sh | 84 | ||||
-rw-r--r-- | security/nss/tests/common/init.sh | 2 |
6 files changed, 268 insertions, 45 deletions
diff --git a/security/nss/lib/certdb/certi.h b/security/nss/lib/certdb/certi.h index e46b9108a..bc44d7822 100644 --- a/security/nss/lib/certdb/certi.h +++ b/security/nss/lib/certdb/certi.h @@ -397,5 +397,19 @@ cert_GetSubjectAltNameList(CERTCertificate *cert, PRArenaPool *arena); PRUint32 cert_CountDNSPatterns(CERTGeneralName *firstName); +/* + * returns the trust status of the leaf certificate based on usage. + * If the leaf is explicitly untrusted, this function will fail and + * failedFlags will be set to the trust bit value that lead to the failure. + * If the leaf is trusted, isTrusted is set to true and the function returns + * SECSuccess. This function does not check if the cert is fit for a + * particular usage. + */ +SECStatus +cert_CheckLeafTrust(CERTCertificate *cert, + SECCertUsage usage, + unsigned int *failedFlags, + PRBool *isTrusted); + #endif /* _CERTI_H_ */ diff --git a/security/nss/lib/libpkix/include/pkix_pl_pki.h b/security/nss/lib/libpkix/include/pkix_pl_pki.h index 32d3a79a4..5fc66ffa5 100755 --- a/security/nss/lib/libpkix/include/pkix_pl_pki.h +++ b/security/nss/lib/libpkix/include/pkix_pl_pki.h @@ -1535,6 +1535,9 @@ PKIX_PL_Cert_VerifySignature( * If the Certificate is not intrinsically trustworthy, it still might end up a * component in a successful chain. * + * If the Certificate is intrinsically untrustworthy, this function will return + * an error. + * * PARAMETERS * "cert" * Address of Cert whose trustworthiness is to be determined. Must be @@ -1559,6 +1562,43 @@ PKIX_PL_Cert_IsCertTrusted( PKIX_Boolean *pTrusted, void *plContext); +/* + * FUNCTION: PKIX_PL_Cert_IsLeafCertTrusted + * DESCRIPTION: + * + * Checks the Leaf Cert specified by "cert" to determine, in a manner that + * depends on the underlying platform, whether it is trusted, and stores the + * result in "pTrusted". If a certificate is trusted it means that this + * End Entify certificate has been marked as trusted for the requested usage, + * policy, validity, and other tests. + * + * If the Certificate is not intrinsically trustworthy, we can still try to + * build a successful chain. + * + * If the Certificate is intrinsically untrustworthy, this function will return + * an error. + * + * PARAMETERS + * "cert" + * Address of Cert whose trustworthiness is to be determined. Must be + * non-NULL. + * "pTrusted" + * Address where the Boolean value will be stored. Must be non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a CERT Error if the function fails in a non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +PKIX_Error * +PKIX_PL_Cert_IsLeafCertTrusted( + PKIX_PL_Cert *cert, + PKIX_Boolean *pTrusted, + void *plContext); + /* FUNCTION: PKIX_PL_Cert_SetAsTrustAnchor */ PKIX_Error* PKIX_PL_Cert_SetAsTrustAnchor(PKIX_PL_Cert *cert, diff --git a/security/nss/lib/libpkix/pkix/top/pkix_build.c b/security/nss/lib/libpkix/pkix/top/pkix_build.c index 4045a76fd..650226554 100755 --- a/security/nss/lib/libpkix/pkix/top/pkix_build.c +++ b/security/nss/lib/libpkix/pkix/top/pkix_build.c @@ -3235,6 +3235,7 @@ pkix_Build_InitiateBuildChain( PKIX_ForwardBuilderState *state = NULL; PKIX_CertStore_CheckTrustCallback trustCallback = NULL; PKIX_CertSelector_MatchCallback selectorCallback = NULL; + PKIX_Boolean trusted = PKIX_FALSE; PKIX_PL_AIAMgr *aiaMgr = NULL; PKIX_ENTER(BUILD, "pkix_Build_InitiateBuildChain"); @@ -3340,6 +3341,15 @@ pkix_Build_InitiateBuildChain( PKIX_ERROR(PKIX_NOTARGETCERTSUPPLIED); } + PKIX_CHECK(PKIX_PL_Cert_IsLeafCertTrusted + (targetCert, + &trusted, + plContext), + PKIX_CERTISCERTTRUSTEDFAILED); + /* future: look at the |trusted| flag and force success. We only + * want to do this if we aren't validating against a policy (like + * EV). */ + PKIX_CHECK(PKIX_PL_Cert_GetAllSubjectNames (targetCert, &targetSubjNames, diff --git a/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c b/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c index df5f03a0e..8986e50c5 100644 --- a/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c +++ b/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c @@ -3242,6 +3242,82 @@ cleanup: } /* + * Find out the state of the NSS trust bits for the requested usage. + * Returns SECFailure if the cert is explicitly distrusted. + * Returns SECSuccess if the cert can be used to form a chain (normal case), + * or it is explicitly trusted. The trusted bool is set to true if it is + * explicitly trusted. + */ +static SECStatus +pkix_pl_Cert_GetTrusted(void *plContext, + PKIX_PL_Cert *cert, + PKIX_Boolean *trusted, + PKIX_Boolean isCA) +{ + SECStatus rv; + CERTCertificate *nssCert = NULL; + SECCertUsage certUsage = 0; + SECCertificateUsage certificateUsage; + SECTrustType trustType; + unsigned int trustFlags; + unsigned int requiredFlags; + CERTCertTrust trust; + + *trusted = PKIX_FALSE; + + /* no key usage information */ + if (plContext == NULL) { + return SECSuccess; + } + + certificateUsage = ((PKIX_PL_NssContext*)plContext)->certificateUsage; + + /* ensure we obtained a single usage bit only */ + PORT_Assert(!(certificateUsage & (certificateUsage - 1))); + + /* convert SECertificateUsage (bit mask) to SECCertUsage (enum) */ + while (0 != (certificateUsage = certificateUsage >> 1)) { certUsage++; } + + nssCert = cert->nssCert; + + if (!isCA) { + PRBool prTrusted; + unsigned int failedFlags; + rv = cert_CheckLeafTrust(nssCert, certUsage, + &failedFlags, &prTrusted); + *trusted = (PKIX_Boolean) prTrusted; + return rv; + } + rv = CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags, + &trustType); + if (rv != SECSuccess) { + return SECSuccess; + } + + rv = CERT_GetCertTrust(nssCert, &trust); + if (rv != SECSuccess) { + return SECSuccess; + } + trustFlags = SEC_GET_TRUST_FLAGS(&trust, trustType); + /* normally trustTypeNone usages accept any of the given trust bits + * being on as acceptable. If any are distrusted (and none are trusted), + * then we will also distrust the cert */ + if ((trustFlags == 0) && (trustType == trustTypeNone)) { + trustFlags = trust.sslFlags | trust.emailFlags | + trust.objectSigningFlags; + } + if ((trustFlags & requiredFlags) == requiredFlags) { + *trusted = PKIX_TRUE; + return SECSuccess; + } + if ((trustFlags & CERTDB_TERMINAL_RECORD) && + ((trustFlags & (CERTDB_VALID_CA|CERTDB_TRUSTED)) == 0)) { + return SECFailure; + } + return SECSuccess; +} + +/* * FUNCTION: PKIX_PL_Cert_IsCertTrusted * (see comments in pkix_pl_pki.h) */ @@ -3253,73 +3329,80 @@ PKIX_PL_Cert_IsCertTrusted( void *plContext) { PKIX_CertStore_CheckTrustCallback trustCallback = NULL; - SECCertUsage certUsage = 0; PKIX_Boolean trusted = PKIX_FALSE; SECStatus rv = SECFailure; - unsigned int requiredFlags; - SECTrustType trustType; - CERTCertTrust trust; - CERTCertificate *nssCert = NULL; - SECCertificateUsage certificateUsage; - PKIX_ENTER(CERT, "pkix_pl_Cert_IsCertTrusted"); + PKIX_ENTER(CERT, "PKIX_PL_Cert_IsCertTrusted"); PKIX_NULLCHECK_TWO(cert, pTrusted); + /* Call GetTrusted first to see if we are going to distrust the + * certificate */ + rv = pkix_pl_Cert_GetTrusted(plContext, cert, &trusted, PKIX_TRUE); + if (rv != SECSuccess) { + /* Failure means the cert is explicitly distrusted, + * let the next level know not to use it. */ + *pTrusted = PKIX_FALSE; + PKIX_ERROR(PKIX_CERTISCERTTRUSTEDFAILED); + } + if (trustOnlyUserAnchors) { + /* discard our |trusted| value since we are using the anchors */ *pTrusted = cert->isUserTrustAnchor; goto cleanup; } - /* no key usage information and store is not trusted */ + /* no key usage information or store is not trusted */ if (plContext == NULL || cert->store == NULL) { *pTrusted = PKIX_FALSE; goto cleanup; } - if (cert->store) { - PKIX_CHECK(PKIX_CertStore_GetTrustCallback - (cert->store, &trustCallback, plContext), - PKIX_CERTSTOREGETTRUSTCALLBACKFAILED); + PKIX_CHECK(PKIX_CertStore_GetTrustCallback + (cert->store, &trustCallback, plContext), + PKIX_CERTSTOREGETTRUSTCALLBACKFAILED); - PKIX_CHECK_ONLY_FATAL(trustCallback - (cert->store, cert, &trusted, plContext), - PKIX_CHECKTRUSTCALLBACKFAILED); + PKIX_CHECK_ONLY_FATAL(trustCallback + (cert->store, cert, &trusted, plContext), + PKIX_CHECKTRUSTCALLBACKFAILED); - if (PKIX_ERROR_RECEIVED || (trusted == PKIX_FALSE)) { + /* allow trust store to override if we can trust the trust + * bits */ + if (PKIX_ERROR_RECEIVED || (trusted == PKIX_FALSE)) { + *pTrusted = PKIX_FALSE; + goto cleanup; + } - *pTrusted = PKIX_FALSE; - goto cleanup; - } + *pTrusted = trusted; - } +cleanup: + PKIX_RETURN(CERT); +} - certificateUsage = ((PKIX_PL_NssContext*)plContext)->certificateUsage; +/* + * FUNCTION: PKIX_PL_Cert_IsLeafCertTrusted + * (see comments in pkix_pl_pki.h) + */ +PKIX_Error * +PKIX_PL_Cert_IsLeafCertTrusted( + PKIX_PL_Cert *cert, + PKIX_Boolean *pTrusted, + void *plContext) +{ + SECStatus rv; - /* ensure we obtained a single usage bit only */ - PORT_Assert(!(certificateUsage & (certificateUsage - 1))); + PKIX_ENTER(CERT, "PKIX_PL_Cert_IsLeafCertTrusted"); + PKIX_NULLCHECK_TWO(cert, pTrusted); - /* convert SECertificateUsage (bit mask) to SECCertUsage (enum) */ - while (0 != (certificateUsage = certificateUsage >> 1)) { certUsage++; } + *pTrusted = PKIX_FALSE; - rv = CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags, &trustType); + rv = pkix_pl_Cert_GetTrusted(plContext, cert, pTrusted, PKIX_FALSE); if (rv != SECSuccess) { + /* Failure means the cert is explicitly distrusted, + * let the next level know not to use it. */ *pTrusted = PKIX_FALSE; - goto cleanup; - } - - nssCert = cert->nssCert; - - rv = CERT_GetCertTrust(nssCert, &trust); - if (rv == SECSuccess) { - unsigned int certFlags; - certFlags = SEC_GET_TRUST_FLAGS((&trust), trustType); - if ((certFlags & requiredFlags) == requiredFlags) { - trusted = PKIX_TRUE; - } + PKIX_ERROR(PKIX_CERTISCERTTRUSTEDFAILED); } - *pTrusted = trusted; - cleanup: PKIX_RETURN(CERT); } diff --git a/security/nss/tests/cert/cert.sh b/security/nss/tests/cert/cert.sh index 65473588f..966a40d54 100755 --- a/security/nss/tests/cert/cert.sh +++ b/security/nss/tests/cert/cert.sh @@ -115,6 +115,7 @@ cert_log() ###################### write the cert_status file certu() { echo "$SCRIPTNAME: ${CU_ACTION} --------------------------" + EXPECTED=${RETEXPECTED-0} if [ -n "${CU_SUBJECT}" ]; then #the subject of the cert contains blanks, and the shell @@ -128,9 +129,9 @@ certu() ${PROFTOOL} ${BINDIR}/certutil $* RET=$? fi - if [ "$RET" -ne 0 ]; then + if [ "$RET" -ne "$EXPECTED" ]; then CERTFAILED=$RET - html_failed "${CU_ACTION} ($RET) " + html_failed "${CU_ACTION} ($RET=$EXPECTED) " cert_log "ERROR: ${CU_ACTION} failed $RET" else html_passed "${CU_ACTION}" @@ -280,7 +281,7 @@ cert_create_cert() CU_ACTION="Import Root CA for $CERTNAME" certu -A -n "TestCA" -t "TC,TC,TC" -f "${R_PWFILE}" -d "${PROFILEDIR}" \ - -i "${R_CADIR}/root.cert" 2>&1 + -i "${R_CADIR}/TestCA.ca.cert" 2>&1 if [ "$RET" -ne 0 ]; then return $RET fi @@ -288,7 +289,7 @@ cert_create_cert() if [ -n "$NSS_ENABLE_ECC" ] ; then CU_ACTION="Import EC Root CA for $CERTNAME" certu -A -n "TestCA-ec" -t "TC,TC,TC" -f "${R_PWFILE}" \ - -d "${PROFILEDIR}" -i "${R_CADIR}/ecroot.cert" 2>&1 + -d "${PROFILEDIR}" -i "${R_CADIR}/TestCA-ec.ca.cert" 2>&1 if [ "$RET" -ne 0 ]; then return $RET fi @@ -1029,7 +1030,7 @@ cert_eccurves() CU_ACTION="Import EC Root CA for $CERTNAME" certu -A -n "TestCA-ec" -t "TC,TC,TC" -f "${R_PWFILE}" \ - -d "${PROFILEDIR}" -i "${R_CADIR}/ecroot.cert" 2>&1 + -d "${PROFILEDIR}" -i "${R_CADIR}/TestCA-ec.ca.cert" 2>&1 if [ -n "${NSS_ECC_MORE_THAN_SUITE_B}" ] ; then CURVE_LIST="c2pnb163v1 c2pnb163v2 c2pnb163v3 c2pnb176v1 \ @@ -1396,6 +1397,78 @@ cert_test_password() certu -V -n PasswordCert -u S -d "${PROFILEDIR}" -f "${R_FIPSPWFILE}" 2>&1 } +############################### +# test if we can distrust a certificate. +# +# we create 3 new certs: +# 1 leaf signed by the trusted root. +# 1 intermediate signed by the trusted root. +# 1 leaf signed by the intermediate. +# +# we mark the first leaf and the intermediate as explicitly untrusted. +# we then try to verify the two leaf certs for our possible usages. +# All verification should fail. +# +cert_test_distrust() +{ + echo "$SCRIPTNAME: Creating Distrusted Certificate" + cert_create_cert ${DISTRUSTDIR} "Distrusted" 2000 ${D_DISTRUST} + CU_ACTION="Mark CERT as unstrusted" + certu -M -n "Distrusted" -t p,p,p -d ${PROFILEDIR} -f "${R_PWFILE}" 2>&1 + echo "$SCRIPTNAME: Creating Distrusted Intermediate" + CERTNAME="DistrustedCA" + ALL_CU_SUBJECT="CN=${CERTNAME}, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US" + cert_CA ${CADIR} "${CERTNAME}" "-c TestCA" ",," ${D_CA} 2010 2>&1 + CU_ACTION="Import Distrusted Intermediate" + certu -A -n "${CERTNAME}" -t "p,p,p" -f "${R_PWFILE}" -d "${PROFILEDIR}" \ + -i "${R_CADIR}/DistrustedCA.ca.cert" 2>&1 + + # now create the last leaf signed by our distrusted CA + # since it's not signed by TestCA it requires more steps. + CU_ACTION="Generate Cert Request for Leaf Chained to Distrusted CA" + CERTNAME="LeafChainedToDistrustedCA" + CU_SUBJECT="CN=${CERTNAME}, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US" + certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" -o req 2>&1 + + CU_ACTION="Sign ${CERTNAME}'s Request" + cp ${CERTDIR}/req ${CADIR} + certu -C -c "DistrustedCA" -m 100 -v 60 -d "${P_R_CADIR}" \ + -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" 2>&1 + + CU_ACTION="Import $CERTNAME's Cert -t u,u,u" + certu -A -n "$CERTNAME" -t "u,u,u" -d "${PROFILEDIR}" -f "${R_PWFILE}" \ + -i "${CERTNAME}.cert" 2>&1 + + RETEXPECTED=255 + CU_ACTION="Verify ${CERTNAME} Cert for SSL Server" + certu -V -n ${CERTNAME} -u V -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + CU_ACTION="Verify ${CERTNAME} Cert for SSL Client" + certu -V -n ${CERTNAME} -u C -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + CU_ACTION="Verify ${CERTNAME} Cert for Email signer" + certu -V -n ${CERTNAME} -u S -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + CU_ACTION="Verify ${CERTNAME} Cert for Email recipient" + certu -V -n ${CERTNAME} -u R -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + CU_ACTION="Verify ${CERTNAME} Cert for OCSP responder" + certu -V -n ${CERTNAME} -u O -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + CU_ACTION="Verify ${CERTNAME} Cert for Object Signer" + certu -V -n ${CERTNAME} -u J -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + + CERTNAME="Distrusted" + CU_ACTION="Verify ${CERTNAME} Cert for SSL Server" + certu -V -n ${CERTNAME} -u V -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + CU_ACTION="Verify ${CERTNAME} Cert for SSL Client" + certu -V -n ${CERTNAME} -u C -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + CU_ACTION="Verify ${CERTNAME} Cert for Email signer" + certu -V -n ${CERTNAME} -u S -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + CU_ACTION="Verify ${CERTNAME} Cert for Email recipient" + certu -V -n ${CERTNAME} -u R -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + CU_ACTION="Verify ${CERTNAME} Cert for OCSP responder" + certu -V -n ${CERTNAME} -u O -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + CU_ACTION="Verify ${CERTNAME} Cert for Object Signer" + certu -V -n ${CERTNAME} -u J -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + RETEXPECTED=0 +} + ############################## cert_cleanup ############################ # local shell function to finish this script (no exit since it might be # sourced) @@ -1419,6 +1492,7 @@ cert_fips cert_eccurves cert_extensions cert_test_password +cert_test_distrust if [ -z "$NSS_TEST_DISABLE_CRL" ] ; then cert_crl_ssl diff --git a/security/nss/tests/common/init.sh b/security/nss/tests/common/init.sh index dc8a5ce73..786cf0749 100644 --- a/security/nss/tests/common/init.sh +++ b/security/nss/tests/common/init.sh @@ -95,6 +95,7 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then FIPSDIR=${HOSTDIR}/fips DBPASSDIR=${HOSTDIR}/dbpass ECCURVES_DIR=${HOSTDIR}/eccurves + DISTRUSTDIR=${HOSTDIR}/distrust SERVER_CADIR=${HOSTDIR}/serverCA CLIENT_CADIR=${HOSTDIR}/clientCA @@ -526,6 +527,7 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then D_EXT_SERVER="ExtendedServer.$version" D_EXT_CLIENT="ExtendedClient.$version" D_CERT_EXTENSTIONS="CertExtensions.$version" + D_DISTRUST="Distrust.$version" # we need relative pathnames of these files abd directories, since our # tools can't handle the unix style absolut pathnames on cygnus |