certvfy.c

来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 1,576 行 · 第 1/3 页

C
1,576
字号
SECStatusCERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,		     PRBool checkSig, SECCertUsage certUsage, int64 t,		     void *wincx, CERTVerifyLog *log){    SECTrustType trustType;    CERTBasicConstraints basicConstraint;    CERTCertificate *issuerCert = NULL;    CERTCertificate *subjectCert = NULL;    CERTCertificate *badCert = NULL;    PRBool isca;    PRBool isFortezzaV1 = PR_FALSE;    SECStatus rv;    SECComparison rvCompare;    SECStatus rvFinal = SECSuccess;    int count;    int currentPathLen = -1;    int flags;    unsigned int caCertType;    unsigned int requiredCAKeyUsage;    unsigned int requiredFlags;    PRArenaPool *arena = NULL;    CERTGeneralName *namesList = NULL;    CERTGeneralName *subjectNameList = NULL;    SECItem *namesIndex = NULL;    int namesIndexLen = 10;    int namesCount = 0;    enum { cbd_None, cbd_User, cbd_CA } last_type = cbd_None;    SECKEYPublicKey *key;    if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE,					 &requiredCAKeyUsage,					 &caCertType)	!= SECSuccess ) {	PORT_Assert(0);	EXIT_IF_NOT_LOGGING(log);	requiredCAKeyUsage = 0;	caCertType = 0;    }    switch ( certUsage ) {      case certUsageSSLClient:      case certUsageSSLServer:      case certUsageSSLCA:      case certUsageSSLServerWithStepUp:      case certUsageEmailSigner:      case certUsageEmailRecipient:      case certUsageObjectSigner:      case certUsageVerifyCA:      case certUsageStatusResponder:	if ( CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags,					   &trustType) != SECSuccess ) {	    PORT_Assert(0);	    EXIT_IF_NOT_LOGGING(log);	    requiredFlags = 0;	    trustType = trustSSL;	}	break;      default:	PORT_Assert(0);	EXIT_IF_NOT_LOGGING(log);	requiredFlags = 0;	trustType = trustSSL;/* This used to be 0, but we need something			      * that matches the enumeration type.			      */	caCertType = 0;    }        subjectCert = CERT_DupCertificate(cert);    if ( subjectCert == NULL ) {	goto loser;    }    /* determine if the cert is fortezza. Getting the key is an easy     * way to determine it, especially since we need to get the privillege     * from the key anyway.     */    key = CERT_ExtractPublicKey(cert);    if (key != NULL) {	isFortezzaV1 = (PRBool)(key->keyType == fortezzaKey);	/* find out what type of cert we are starting with */	if (isFortezzaV1) {	    unsigned char priv = 0;;	    rv = SEC_CheckKRL(handle, key, NULL, t, wincx);	    if (rv == SECFailure) {		/**** PORT_SetError is already set by SEC_CheckKRL **/		SECKEY_DestroyPublicKey(key);		/**** Bob - should we log and continue when logging? **/		LOG_ERROR(log,subjectCert,0,0);		goto loser;	    }                	    if (key->u.fortezza.DSSpriviledge.len > 0) {		priv = key->u.fortezza.DSSpriviledge.data[0];	    }	    last_type = (priv & 0x30) ? cbd_CA : cbd_User;	}			SECKEY_DestroyPublicKey(key);    }    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);    if (arena == NULL) {	goto loser;    }    namesIndex = (SECItem *) PORT_ZAlloc(sizeof(SECItem) * namesIndexLen);    if (namesIndex == NULL) {	goto loser;    }    for ( count = 0; count < CERT_MAX_CERT_CHAIN; count++ ) {	int subjectNameListLen;	int i;	/* Construct a list of names for the current and all previous certifcates 	   to be verified against the name constraints extension of the issuer	   certificate. */	subjectNameList = CERT_GetCertificateNames(subjectCert, arena);	subjectNameListLen = CERT_GetNamesLength(subjectNameList);	for (i = 0; i < subjectNameListLen; i++) {	    if (namesIndexLen < namesCount + i) {		namesIndexLen = namesIndexLen * 2;		namesIndex = (SECItem *) PORT_Realloc(namesIndex, namesIndexLen * 						       sizeof(SECItem));		if (namesIndex == NULL) {		    goto loser;		}	    }	    rv = SECITEM_CopyItem(arena, &(namesIndex[namesCount + i]), &(subjectCert->derSubject));	}	namesCount += subjectNameListLen;	namesList = cert_CombineNamesLists(namesList, subjectNameList);	/* find the certificate of the issuer */	issuerCert = CERT_FindCertIssuer(subjectCert, t, certUsage);	if ( ! issuerCert ) {	    PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);	    LOG_ERROR(log,subjectCert,count,0);	    goto loser;	}	/* verify the signature on the cert */	if ( checkSig ) {	    rv = CERT_VerifySignedData(&subjectCert->signatureWrap,				       issuerCert, t, wincx);    	    if ( rv != SECSuccess ) {		if ( PORT_GetError() == SEC_ERROR_EXPIRED_CERTIFICATE ) {		    PORT_SetError(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE);		    LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);		} else {		    PORT_SetError(SEC_ERROR_BAD_SIGNATURE);		    LOG_ERROR_OR_EXIT(log,subjectCert,count,0);		}	    }	}	/*	 * XXX - fortezza may need error logging stuff added	 */	if (isFortezzaV1) {	    unsigned char priv = 0;	    /* read the key */	    key = CERT_ExtractPublicKey(issuerCert);	    /* Cant' get Key? fail. */	    if (key == NULL) {	    	PORT_SetError(SEC_ERROR_BAD_KEY);		LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);		goto fortezzaDone;	    }	    /* if the issuer is not an old fortezza cert, we bail */	    if (key->keyType != fortezzaKey) {	    	SECKEY_DestroyPublicKey(key);		/* CA Cert not fortezza */	    	PORT_SetError(SEC_ERROR_NOT_FORTEZZA_ISSUER);		LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);		goto fortezzaDone;	    }	    /* get the privilege mask */	    if (key->u.fortezza.DSSpriviledge.len > 0) {		priv = key->u.fortezza.DSSpriviledge.data[0];	    }	    /*	     * make sure the CA's keys are OK	     */            	    rv = SEC_CheckKRL(handle, key, NULL, t, wincx);	    if (rv != SECSuccess) {	    	SECKEY_DestroyPublicKey(key);		LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);		goto fortezzaDone;		/** fall through looking for more stuff **/	    } else {	        SECKEY_DestroyPublicKey(key);	    }	    switch (last_type) {	      case cbd_User:		/* first check for subordination */		/*rv = FortezzaSubordinateCheck(cert,issuerCert);*/		rv = SECSuccess;		/* now check for issuer privilege */		if ((rv != SECSuccess) || ((priv & 0x10) == 0)) {		    /* bail */		    PORT_SetError (SEC_ERROR_CA_CERT_INVALID);		    LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);		}		break;	      case cbd_CA:	      case cbd_None:		if ((priv & 0x20) == 0) {		    /* bail */		    PORT_SetError (SEC_ERROR_CA_CERT_INVALID);		    LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);		}		break;	      default:		/* bail */ /* shouldn't ever happen */	    	PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);		LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);	    }fortezzaDone:	    last_type =  cbd_CA;	}	/* If the basicConstraint extension is included in an immediate CA	 * certificate, make sure that the isCA flag is on.  If the	 * pathLenConstraint component exists, it must be greater than the	 * number of CA certificates we have seen so far.  If the extension	 * is omitted, we will assume that this is a CA certificate with	 * an unlimited pathLenConstraint (since it already passes the	 * netscape-cert-type extension checking).	 *	 * In the fortezza (V1) case, we've already checked the CA bits	 * in the key, so we're presumed to be a CA; however we really don't	 * want to bypass Basic constraint or netscape extension parsing.         *          * In Fortezza V2, basicConstraint will be set for every CA,PCA,PAA	 */	rv = CERT_FindBasicConstraintExten(issuerCert, &basicConstraint);	if ( rv != SECSuccess ) {	    if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {		LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);	    } else {		currentPathLen = CERT_UNLIMITED_PATH_CONSTRAINT;	    }	    /* no basic constraints found, if we're fortezza, CA bit is already	     * verified (isca = PR_TRUE). otherwise, we aren't (yet) a ca	     * isca = PR_FALSE */	    isca = isFortezzaV1;	} else  {	    if ( basicConstraint.isCA == PR_FALSE ) {		PORT_SetError (SEC_ERROR_CA_CERT_INVALID);		LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);	    }	    	    /* make sure that the path len constraint is properly set.	     */	    if ( basicConstraint.pathLenConstraint ==		CERT_UNLIMITED_PATH_CONSTRAINT ) {		currentPathLen = CERT_UNLIMITED_PATH_CONSTRAINT;	    } else if ( currentPathLen == CERT_UNLIMITED_PATH_CONSTRAINT ) {		/* error if the previous CA's path length constraint is		 * unlimited but its CA's path is not.		 */		PORT_SetError (SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID);		LOG_ERROR_OR_EXIT(log,issuerCert,count+1,basicConstraint.pathLenConstraint);	    } else if (basicConstraint.pathLenConstraint > currentPathLen) {		currentPathLen = basicConstraint.pathLenConstraint;	    } else {		PORT_SetError (SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID);		LOG_ERROR_OR_EXIT(log,issuerCert,count+1,basicConstraint.pathLenConstraint);	    }	    isca = PR_TRUE;	}		/* XXX - the error logging may need to go down into CRL stuff at some	 * point	 */	/* check revoked list (issuer) */	rv = SEC_CheckCRL(handle, subjectCert, issuerCert, t, wincx);	if (rv == SECFailure) {	    LOG_ERROR_OR_EXIT(log,subjectCert,count,0);	} else if (rv == SECWouldBlock) {	    /* We found something fishy, so we intend to issue an	     * error to the user, but the user may wish to continue	     * processing, in which case we better make sure nothing	     * worse has happened... so keep cranking the loop */	    rvFinal = SECFailure;	    LOG_ERROR(log,subjectCert,count,0);	}	if ( issuerCert->trust ) {	    /*	     * check the trust parms of the issuer	     */	    if ( certUsage == certUsageVerifyCA ) {		if ( subjectCert->nsCertType & NS_CERT_TYPE_EMAIL_CA ) {		    trustType = trustEmail;		} else if ( subjectCert->nsCertType & NS_CERT_TYPE_SSL_CA ) {		    trustType = trustSSL;		} else {		    trustType = trustObjectSigning;		}	    }	    	    flags = SEC_GET_TRUST_FLAGS(issuerCert->trust, trustType);	    	    if ( (flags & CERTDB_VALID_CA) ||		 (certUsage == certUsageStatusResponder)) {		if ( ( flags & requiredFlags ) == requiredFlags ||		     certUsage == certUsageStatusResponder ) {		    /* we found a trusted one, so return */		    rv = rvFinal; 		    goto done;		}	    }	}	/*	 * Make sure that if this is an intermediate CA in the chain that	 * it was given permission by its signer to be a CA.	 */	if ( isca ) {	    /*	     * if basicConstraints says it is a ca, then we check the	     * nsCertType.  If the nsCertType has any CA bits set, then	     * it must have the right one.	     */	    if ( issuerCert->nsCertType & NS_CERT_TYPE_CA ) {		if ( issuerCert->nsCertType & caCertType ) {		    isca = PR_TRUE;		} else {		    isca = PR_FALSE;		}	    }	} else {	    if ( issuerCert->nsCertType & caCertType ) {		isca = PR_TRUE;	    } else {		isca = PR_FALSE;	    }	}		if (  !isca  ) {	    PORT_SetError(SEC_ERROR_CA_CERT_INVALID);	    LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);	}	    	/* make sure key usage allows cert signing */	if (CERT_CheckKeyUsage(issuerCert, requiredCAKeyUsage) != SECSuccess) {	    PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);	    LOG_ERROR_OR_EXIT(log,issuerCert,count+1,requiredCAKeyUsage);	}	/* make sure that the entire chain is within the name space of the current issuer	 * certificate.	 */	badCert = CERT_CompareNameSpace(issuerCert, namesList, namesIndex, arena, handle);	if (badCert != NULL) {	    PORT_SetError(SEC_ERROR_CERT_NOT_IN_NAME_SPACE);            LOG_ERROR_OR_EXIT(log, badCert, count + 1, 0);	    goto loser;	}	/* make sure that the issuer is not self signed.  If it is, then	 * stop here to prevent looping.	 */	rvCompare = SECITEM_CompareItem(&issuerCert->derSubject,				 &issuerCert->derIssuer);	if (rvCompare == SECEqual) {	    PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);	    LOG_ERROR(log, issuerCert, count+1, 0);	    goto loser;	}	CERT_DestroyCertificate(subjectCert);	subjectCert = issuerCert;    }    subjectCert = NULL;    PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);    LOG_ERROR(log,issuerCert,count,0);loser:    rv = SECFailure;done:    if (namesIndex != NULL) {	PORT_Free(namesIndex);    }    if ( issuerCert ) {	CERT_DestroyCertificate(issuerCert);    }        if ( subjectCert ) {	CERT_DestroyCertificate(subjectCert);    }    if ( arena != NULL ) {	PORT_FreeArena(arena, PR_FALSE);    }    return rv;}			/* * verify a certificate by checking if its valid and that we * trust the issuer. * Note that this routine does not verify the signature of the certificate. */SECStatusCERT_VerifyCert(CERTCertDBHandle *handle, CERTCertificate *cert,		PRBool checkSig, SECCertUsage certUsage, int64 t,		void *wincx, CERTVerifyLog *log){    SECStatus rv;    unsigned int requiredKeyUsage;    unsigned int requiredCertType;    unsigned int flags;    unsigned int certType;    PRBool       allowOverride;    SECCertTimeValidity validity;    CERTStatusConfig *statusConfig;        /* check if this cert is in the Evil list */    rv = CERT_CheckForEvilCert(cert);    if ( rv != SECSuccess ) {	PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);	LOG_ERROR_OR_EXIT(log,cert,0,0);    }        /* make sure that the cert is valid at time t */    allowOverride = (PRBool)((certUsage == certUsageSSLServer) ||                             (certUsage == certUsageSSLServerWithStepUp));    validity = CERT_CheckCertValidTimes(cert, t, allowOverride);    if ( validity != secCertTimeValid ) {	LOG_ERROR_OR_EXIT(log,cert,0,validity);    }    /* check key usage and netscape cert type */    CERT_GetCertType(cert);    certType = cert->nsCertType;    switch ( certUsage ) {      case certUsageSSLClient:      case certUsageSSLServer:      case certUsageSSLServerWithStepUp:      case certUsageSSLCA:      case certUsageEmailSigner:      case certUsageEmailRecipient:      case certUsageObjectSigner:      case certUsageStatusResponder:	rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE,					      &requiredKeyUsage,					      &requiredCertType);	if ( rv != SECSuccess ) {	    PORT_Assert(0);	    EXIT_IF_NOT_LOGGING(log);	    requiredKeyUsage = 0;	    requiredCertType = 0;	}	break;      case certUsageVerifyCA:	requiredKeyUsage = KU_KEY_CERT_SIGN;	requiredCertType = NS_CERT_TYPE_CA;	if ( ! ( certType & NS_CERT_TYPE_CA ) ) {	    certType |= NS_CERT_TYPE_CA;	}	break;      default:	PORT_Assert(0);	EXIT_IF_NOT_LOGGING(log);	requiredKeyUsage = 0;	requiredCertType = 0;    }    if ( CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess ) {	PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);	LOG_ERROR_OR_EXIT(log,cert,0,requiredKeyUsage);    }    if ( !( certType & requiredCertType ) ) {	PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE);	LOG_ERROR_OR_EXIT(log,cert,0,requiredCertType);    }    /* check trust flags to see if this cert is directly trusted */    if ( cert->trust ) { /* the cert is in the DB */	switch ( certUsage ) {	  case certUsageSSLClient:	  case certUsageSSLServer:	    flags = cert->trust->sslFlags;	    	    /* is the cert directly trusted or not trusted ? */	    if ( flags & CERTDB_VALID_PEER ) {/*the trust record is valid*/		if ( flags & CERTDB_TRUSTED ) {	/* trust this cert */		    goto winner;		} else { /* don't trust this cert */		    PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);		    LOG_ERROR_OR_EXIT(log,cert,0,flags);		}	    }	    break;	  case certUsageSSLServerWithStepUp:	    /* XXX - step up certs can't be directly trusted */	    break;	  case certUsageSSLCA:	    break;	  case certUsageEmailSigner:	  case certUsageEmailRecipient:	    flags = cert->trust->emailFlags;	    

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?