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


git-svn-id: svn://10.0.0.236/trunk@262874 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
wtc%google.com
2011-09-14 23:16:16 +00:00
parent 3fdcbab48d
commit 731bd799ab
6 changed files with 273 additions and 50 deletions

View File

@@ -36,7 +36,7 @@
/*
* certi.h - private data structures for the certificate library
*
* $Id: certi.h,v 1.35 2011-01-29 22:17:20 nelson%bolyard.com Exp $
* $Id: certi.h,v 1.36 2011-09-14 23:16:14 wtc%google.com Exp $
*/
#ifndef _CERTI_H_
#define _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_ */

View File

@@ -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,

View File

@@ -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,

View File

@@ -3241,6 +3241,82 @@ cleanup:
PKIX_RETURN(CERT);
}
/*
* 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,77 +3329,84 @@ 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)) {
*pTrusted = PKIX_FALSE;
goto cleanup;
}
}
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++; }
rv = CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags, &trustType);
if (rv != SECSuccess) {
/* allow trust store to override if we can trust the trust
* bits */
if (PKIX_ERROR_RECEIVED || (trusted == PKIX_FALSE)) {
*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;
}
}
*pTrusted = trusted;
cleanup:
PKIX_RETURN(CERT);
}
/*
* 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;
PKIX_ENTER(CERT, "PKIX_PL_Cert_IsLeafCertTrusted");
PKIX_NULLCHECK_TWO(cert, pTrusted);
*pTrusted = PKIX_FALSE;
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;
PKIX_ERROR(PKIX_CERTISCERTTRUSTEDFAILED);
}
cleanup:
PKIX_RETURN(CERT);
}
/* FUNCTION: PKIX_PL_Cert_SetAsTrustAnchor */
PKIX_Error*
PKIX_PL_Cert_SetAsTrustAnchor(PKIX_PL_Cert *cert,

View File

@@ -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

View File

@@ -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