summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwtc%google.com <devnull@localhost>2011-09-14 23:16:16 +0000
committerwtc%google.com <devnull@localhost>2011-09-14 23:16:16 +0000
commitee0880238fb2119deaef3b8f122f289809845c9f (patch)
tree9fdba0024460db2b0ca7f33fb9af46c89db4c253
parent96df47953b68d6a177eed23a659c5a925288fd65 (diff)
downloadnss-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.h14
-rwxr-xr-xsecurity/nss/lib/libpkix/include/pkix_pl_pki.h40
-rwxr-xr-xsecurity/nss/lib/libpkix/pkix/top/pkix_build.c10
-rw-r--r--security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c163
-rwxr-xr-xsecurity/nss/tests/cert/cert.sh84
-rw-r--r--security/nss/tests/common/init.sh2
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