andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: #include "nspr.h" andre@0: #include "secerr.h" andre@0: #include "secport.h" andre@0: #include "seccomon.h" andre@0: #include "secoid.h" andre@0: #include "sslerr.h" andre@0: #include "genname.h" andre@0: #include "keyhi.h" andre@0: #include "cert.h" andre@0: #include "certdb.h" andre@0: #include "certi.h" andre@0: #include "cryptohi.h" andre@0: #ifndef NSS_DISABLE_LIBPKIX andre@0: #include "pkix.h" andre@0: /*#include "pkix_sample_modules.h" */ andre@0: #include "pkix_pl_cert.h" andre@0: #endif /* NSS_DISABLE_LIBPKIX */ andre@0: andre@0: andre@0: #include "nsspki.h" andre@0: #include "pkitm.h" andre@0: #include "pkim.h" andre@0: #include "pki3hack.h" andre@0: #include "base.h" andre@0: andre@0: #ifdef NSS_DISABLE_LIBPKIX andre@0: SECStatus andre@0: cert_VerifyCertChainPkix( andre@0: CERTCertificate *cert, andre@0: PRBool checkSig, andre@0: SECCertUsage requiredUsage, andre@0: PRTime time, andre@0: void *wincx, andre@0: CERTVerifyLog *log, andre@0: PRBool *pSigerror, andre@0: PRBool *pRevoked) andre@0: { andre@0: PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); andre@0: return SECFailure; andre@0: } andre@0: andre@0: SECStatus andre@0: CERT_SetUsePKIXForValidation(PRBool enable) andre@0: { andre@0: PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); andre@0: return SECFailure; andre@0: } andre@0: andre@0: PRBool andre@0: CERT_GetUsePKIXForValidation() andre@0: { andre@0: return PR_FALSE; andre@0: } andre@0: andre@0: SECStatus CERT_PKIXVerifyCert( andre@0: CERTCertificate *cert, andre@0: SECCertificateUsage usages, andre@0: CERTValInParam *paramsIn, andre@0: CERTValOutParam *paramsOut, andre@0: void *wincx) andre@0: { andre@0: PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); andre@0: return SECFailure; andre@0: } andre@0: #endif /* NSS_DISABLE_LIBPKIX */ andre@0: andre@0: /* andre@0: * Check the validity times of a certificate andre@0: */ andre@0: SECStatus andre@0: CERT_CertTimesValid(CERTCertificate *c) andre@0: { andre@0: SECCertTimeValidity valid = CERT_CheckCertValidTimes(c, PR_Now(), PR_TRUE); andre@0: return (valid == secCertTimeValid) ? SECSuccess : SECFailure; andre@0: } andre@0: andre@0: /* andre@0: * verify the signature of a signed data object with the given DER publickey andre@0: */ andre@0: SECStatus andre@0: CERT_VerifySignedDataWithPublicKey(const CERTSignedData *sd, andre@0: SECKEYPublicKey *pubKey, andre@0: void *wincx) andre@0: { andre@0: SECStatus rv; andre@0: SECItem sig; andre@0: SECOidTag hashAlg = SEC_OID_UNKNOWN; andre@0: andre@0: if ( !pubKey || !sd ) { andre@0: PORT_SetError(PR_INVALID_ARGUMENT_ERROR); andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* check the signature */ andre@0: sig = sd->signature; andre@0: /* convert sig->len from bit counts to byte count. */ andre@0: DER_ConvertBitString(&sig); andre@0: andre@0: rv = VFY_VerifyDataWithAlgorithmID(sd->data.data, sd->data.len, pubKey, andre@0: &sig, &sd->signatureAlgorithm, &hashAlg, wincx); andre@0: if (rv == SECSuccess) { andre@0: /* Are we honoring signatures for this algorithm? */ andre@0: PRUint32 policyFlags = 0; andre@0: rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags); andre@0: if (rv == SECSuccess && andre@0: !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { andre@0: PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); andre@0: rv = SECFailure; andre@0: } andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: /* andre@0: * verify the signature of a signed data object with the given DER publickey andre@0: */ andre@0: SECStatus andre@0: CERT_VerifySignedDataWithPublicKeyInfo(CERTSignedData *sd, andre@0: CERTSubjectPublicKeyInfo *pubKeyInfo, andre@0: void *wincx) andre@0: { andre@0: SECKEYPublicKey *pubKey; andre@0: SECStatus rv = SECFailure; andre@0: andre@0: /* get cert's public key */ andre@0: pubKey = SECKEY_ExtractPublicKey(pubKeyInfo); andre@0: if (pubKey) { andre@0: rv = CERT_VerifySignedDataWithPublicKey(sd, pubKey, wincx); andre@0: SECKEY_DestroyPublicKey(pubKey); andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: /* andre@0: * verify the signature of a signed data object with the given certificate andre@0: */ andre@0: SECStatus andre@0: CERT_VerifySignedData(CERTSignedData *sd, CERTCertificate *cert, andre@0: PRTime t, void *wincx) andre@0: { andre@0: SECKEYPublicKey *pubKey = 0; andre@0: SECStatus rv = SECFailure; andre@0: SECCertTimeValidity validity; andre@0: andre@0: /* check the certificate's validity */ andre@0: validity = CERT_CheckCertValidTimes(cert, t, PR_FALSE); andre@0: if ( validity != secCertTimeValid ) { andre@0: return rv; andre@0: } andre@0: andre@0: /* get cert's public key */ andre@0: pubKey = CERT_ExtractPublicKey(cert); andre@0: if (pubKey) { andre@0: rv = CERT_VerifySignedDataWithPublicKey(sd, pubKey, wincx); andre@0: SECKEY_DestroyPublicKey(pubKey); andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: andre@0: SECStatus andre@0: SEC_CheckCRL(CERTCertDBHandle *handle,CERTCertificate *cert, andre@0: CERTCertificate *caCert, PRTime t, void * wincx) andre@0: { andre@0: return CERT_CheckCRL(cert, caCert, NULL, t, wincx); andre@0: } andre@0: andre@0: /* andre@0: * Find the issuer of a cert. Use the authorityKeyID if it exists. andre@0: */ andre@0: CERTCertificate * andre@0: CERT_FindCertIssuer(CERTCertificate *cert, PRTime validTime, SECCertUsage usage) andre@0: { andre@0: NSSCertificate *me; andre@0: NSSTime *nssTime; andre@0: NSSTrustDomain *td; andre@0: NSSCryptoContext *cc; andre@0: NSSCertificate *chain[3]; andre@0: NSSUsage nssUsage; andre@0: PRStatus status; andre@0: andre@0: me = STAN_GetNSSCertificate(cert); andre@0: if (!me) { andre@0: PORT_SetError(SEC_ERROR_NO_MEMORY); andre@0: return NULL; andre@0: } andre@0: nssTime = NSSTime_SetPRTime(NULL, validTime); andre@0: nssUsage.anyUsage = PR_FALSE; andre@0: nssUsage.nss3usage = usage; andre@0: nssUsage.nss3lookingForCA = PR_TRUE; andre@0: memset(chain, 0, 3*sizeof(NSSCertificate *)); andre@0: td = STAN_GetDefaultTrustDomain(); andre@0: cc = STAN_GetDefaultCryptoContext(); andre@0: (void)NSSCertificate_BuildChain(me, nssTime, &nssUsage, NULL, andre@0: chain, 2, NULL, &status, td, cc); andre@0: nss_ZFreeIf(nssTime); andre@0: if (status == PR_SUCCESS) { andre@0: PORT_Assert(me == chain[0]); andre@0: /* if it's a root, the chain will only have one cert */ andre@0: if (!chain[1]) { andre@0: /* already has a reference from the call to BuildChain */ andre@0: return cert; andre@0: } andre@0: NSSCertificate_Destroy(chain[0]); /* the first cert in the chain */ andre@0: return STAN_GetCERTCertificate(chain[1]); /* return the 2nd */ andre@0: } andre@0: if (chain[0]) { andre@0: PORT_Assert(me == chain[0]); andre@0: NSSCertificate_Destroy(chain[0]); /* the first cert in the chain */ andre@0: } andre@0: PORT_SetError (SEC_ERROR_UNKNOWN_ISSUER); andre@0: return NULL; andre@0: } andre@0: andre@0: /* andre@0: * return required trust flags for various cert usages for CAs andre@0: */ andre@0: SECStatus andre@0: CERT_TrustFlagsForCACertUsage(SECCertUsage usage, andre@0: unsigned int *retFlags, andre@0: SECTrustType *retTrustType) andre@0: { andre@0: unsigned int requiredFlags; andre@0: SECTrustType trustType; andre@0: andre@0: switch ( usage ) { andre@0: case certUsageSSLClient: andre@0: requiredFlags = CERTDB_TRUSTED_CLIENT_CA; andre@0: trustType = trustSSL; andre@0: break; andre@0: case certUsageSSLServer: andre@0: case certUsageSSLCA: andre@0: requiredFlags = CERTDB_TRUSTED_CA; andre@0: trustType = trustSSL; andre@0: break; andre@0: case certUsageSSLServerWithStepUp: andre@0: requiredFlags = CERTDB_TRUSTED_CA | CERTDB_GOVT_APPROVED_CA; andre@0: trustType = trustSSL; andre@0: break; andre@0: case certUsageEmailSigner: andre@0: case certUsageEmailRecipient: andre@0: requiredFlags = CERTDB_TRUSTED_CA; andre@0: trustType = trustEmail; andre@0: break; andre@0: case certUsageObjectSigner: andre@0: requiredFlags = CERTDB_TRUSTED_CA; andre@0: trustType = trustObjectSigning; andre@0: break; andre@0: case certUsageVerifyCA: andre@0: case certUsageAnyCA: andre@0: case certUsageStatusResponder: andre@0: requiredFlags = CERTDB_TRUSTED_CA; andre@0: trustType = trustTypeNone; andre@0: break; andre@0: default: andre@0: PORT_Assert(0); andre@0: goto loser; andre@0: } andre@0: if ( retFlags != NULL ) { andre@0: *retFlags = requiredFlags; andre@0: } andre@0: if ( retTrustType != NULL ) { andre@0: *retTrustType = trustType; andre@0: } andre@0: andre@0: return(SECSuccess); andre@0: loser: andre@0: return(SECFailure); andre@0: } andre@0: andre@0: void andre@0: cert_AddToVerifyLog(CERTVerifyLog *log, CERTCertificate *cert, long error, andre@0: unsigned int depth, void *arg) andre@0: { andre@0: CERTVerifyLogNode *node, *tnode; andre@0: andre@0: PORT_Assert(log != NULL); andre@0: andre@0: node = (CERTVerifyLogNode *)PORT_ArenaAlloc(log->arena, andre@0: sizeof(CERTVerifyLogNode)); andre@0: if ( node != NULL ) { andre@0: node->cert = CERT_DupCertificate(cert); andre@0: node->error = error; andre@0: node->depth = depth; andre@0: node->arg = arg; andre@0: andre@0: if ( log->tail == NULL ) { andre@0: /* empty list */ andre@0: log->head = log->tail = node; andre@0: node->prev = NULL; andre@0: node->next = NULL; andre@0: } else if ( depth >= log->tail->depth ) { andre@0: /* add to tail */ andre@0: node->prev = log->tail; andre@0: log->tail->next = node; andre@0: log->tail = node; andre@0: node->next = NULL; andre@0: } else if ( depth < log->head->depth ) { andre@0: /* add at head */ andre@0: node->prev = NULL; andre@0: node->next = log->head; andre@0: log->head->prev = node; andre@0: log->head = node; andre@0: } else { andre@0: /* add in middle */ andre@0: tnode = log->tail; andre@0: while ( tnode != NULL ) { andre@0: if ( depth >= tnode->depth ) { andre@0: /* insert after tnode */ andre@0: node->prev = tnode; andre@0: node->next = tnode->next; andre@0: tnode->next->prev = node; andre@0: tnode->next = node; andre@0: break; andre@0: } andre@0: andre@0: tnode = tnode->prev; andre@0: } andre@0: } andre@0: andre@0: log->count++; andre@0: } andre@0: return; andre@0: } andre@0: andre@0: #define EXIT_IF_NOT_LOGGING(log) \ andre@0: if ( log == NULL ) { \ andre@0: goto loser; \ andre@0: } andre@0: andre@0: #define LOG_ERROR_OR_EXIT(log,cert,depth,arg) \ andre@0: if ( log != NULL ) { \ andre@0: cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, \ andre@0: (void *)(PRWord)arg); \ andre@0: } else { \ andre@0: goto loser; \ andre@0: } andre@0: andre@0: #define LOG_ERROR(log,cert,depth,arg) \ andre@0: if ( log != NULL ) { \ andre@0: cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, \ andre@0: (void *)(PRWord)arg); \ andre@0: } andre@0: andre@0: static SECStatus andre@0: cert_VerifyCertChainOld(CERTCertDBHandle *handle, CERTCertificate *cert, andre@0: PRBool checkSig, PRBool* sigerror, andre@0: SECCertUsage certUsage, PRTime t, void *wincx, andre@0: CERTVerifyLog *log, PRBool* revoked) andre@0: { andre@0: SECTrustType trustType; andre@0: CERTBasicConstraints basicConstraint; andre@0: CERTCertificate *issuerCert = NULL; andre@0: CERTCertificate *subjectCert = NULL; andre@0: CERTCertificate *badCert = NULL; andre@0: PRBool isca; andre@0: SECStatus rv; andre@0: SECStatus rvFinal = SECSuccess; andre@0: int count; andre@0: int currentPathLen = 0; andre@0: int pathLengthLimit = CERT_UNLIMITED_PATH_CONSTRAINT; andre@0: unsigned int caCertType; andre@0: unsigned int requiredCAKeyUsage; andre@0: unsigned int requiredFlags; andre@0: PLArenaPool *arena = NULL; andre@0: CERTGeneralName *namesList = NULL; andre@0: CERTCertificate **certsList = NULL; andre@0: int certsListLen = 16; andre@0: int namesCount = 0; andre@0: PRBool subjectCertIsSelfIssued; andre@0: CERTCertTrust issuerTrust; andre@0: andre@0: if (revoked) { andre@0: *revoked = PR_FALSE; andre@0: } andre@0: andre@0: if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE, andre@0: &requiredCAKeyUsage, andre@0: &caCertType) andre@0: != SECSuccess ) { andre@0: PORT_Assert(0); andre@0: EXIT_IF_NOT_LOGGING(log); andre@0: requiredCAKeyUsage = 0; andre@0: caCertType = 0; andre@0: } andre@0: andre@0: switch ( certUsage ) { andre@0: case certUsageSSLClient: andre@0: case certUsageSSLServer: andre@0: case certUsageSSLCA: andre@0: case certUsageSSLServerWithStepUp: andre@0: case certUsageEmailSigner: andre@0: case certUsageEmailRecipient: andre@0: case certUsageObjectSigner: andre@0: case certUsageVerifyCA: andre@0: case certUsageAnyCA: andre@0: case certUsageStatusResponder: andre@0: if ( CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags, andre@0: &trustType) != SECSuccess ) { andre@0: PORT_Assert(0); andre@0: EXIT_IF_NOT_LOGGING(log); andre@0: /* XXX continuing with requiredFlags = 0 seems wrong. It'll andre@0: * cause the following test to be true incorrectly: andre@0: * flags = SEC_GET_TRUST_FLAGS(issuerCert->trust, trustType); andre@0: * if (( flags & requiredFlags ) == requiredFlags) { andre@0: * rv = rvFinal; andre@0: * goto done; andre@0: * } andre@0: * There are three other instances of this problem. andre@0: */ andre@0: requiredFlags = 0; andre@0: trustType = trustSSL; andre@0: } andre@0: break; andre@0: default: andre@0: PORT_Assert(0); andre@0: EXIT_IF_NOT_LOGGING(log); andre@0: requiredFlags = 0; andre@0: trustType = trustSSL;/* This used to be 0, but we need something andre@0: * that matches the enumeration type. andre@0: */ andre@0: caCertType = 0; andre@0: } andre@0: andre@0: subjectCert = CERT_DupCertificate(cert); andre@0: if ( subjectCert == NULL ) { andre@0: goto loser; andre@0: } andre@0: andre@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); andre@0: if (arena == NULL) { andre@0: goto loser; andre@0: } andre@0: andre@0: certsList = PORT_ZNewArray(CERTCertificate *, certsListLen); andre@0: if (certsList == NULL) andre@0: goto loser; andre@0: andre@0: /* RFC 3280 says that the name constraints will apply to the names andre@0: ** in the leaf (EE) cert, whether it is self issued or not, so andre@0: ** we pretend that it is not. andre@0: */ andre@0: subjectCertIsSelfIssued = PR_FALSE; andre@0: for ( count = 0; count < CERT_MAX_CERT_CHAIN; count++ ) { andre@0: PRBool validCAOverride = PR_FALSE; andre@0: andre@0: /* Construct a list of names for the current and all previous andre@0: * certifcates (except leaf (EE) certs, root CAs, and self-issued andre@0: * intermediate CAs) to be verified against the name constraints andre@0: * extension of the issuer certificate. andre@0: */ andre@0: if (subjectCertIsSelfIssued == PR_FALSE) { andre@0: CERTGeneralName *subjectNameList; andre@0: int subjectNameListLen; andre@0: int i; andre@0: PRBool getSubjectCN = (!count && certUsage == certUsageSSLServer); andre@0: subjectNameList = andre@0: CERT_GetConstrainedCertificateNames(subjectCert, arena, andre@0: getSubjectCN); andre@0: if (!subjectNameList) andre@0: goto loser; andre@0: subjectNameListLen = CERT_GetNamesLength(subjectNameList); andre@0: if (!subjectNameListLen) andre@0: goto loser; andre@0: if (certsListLen <= namesCount + subjectNameListLen) { andre@0: CERTCertificate **tmpCertsList; andre@0: certsListLen = (namesCount + subjectNameListLen) * 2; andre@0: tmpCertsList = andre@0: (CERTCertificate **)PORT_Realloc(certsList, andre@0: certsListLen * sizeof(CERTCertificate *)); andre@0: if (tmpCertsList == NULL) { andre@0: goto loser; andre@0: } andre@0: certsList = tmpCertsList; andre@0: } andre@0: for (i = 0; i < subjectNameListLen; i++) { andre@0: certsList[namesCount + i] = subjectCert; andre@0: } andre@0: namesCount += subjectNameListLen; andre@0: namesList = cert_CombineNamesLists(namesList, subjectNameList); andre@0: } andre@0: andre@0: /* check if the cert has an unsupported critical extension */ andre@0: if ( subjectCert->options.bits.hasUnsupportedCriticalExt ) { andre@0: PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); andre@0: LOG_ERROR_OR_EXIT(log,subjectCert,count,0); andre@0: } andre@0: andre@0: /* find the certificate of the issuer */ andre@0: issuerCert = CERT_FindCertIssuer(subjectCert, t, certUsage); andre@0: if ( ! issuerCert ) { andre@0: PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); andre@0: LOG_ERROR(log,subjectCert,count,0); andre@0: goto loser; andre@0: } andre@0: andre@0: /* verify the signature on the cert */ andre@0: if ( checkSig ) { andre@0: rv = CERT_VerifySignedData(&subjectCert->signatureWrap, andre@0: issuerCert, t, wincx); andre@0: andre@0: if ( rv != SECSuccess ) { andre@0: if (sigerror) { andre@0: *sigerror = PR_TRUE; andre@0: } andre@0: if ( PORT_GetError() == SEC_ERROR_EXPIRED_CERTIFICATE ) { andre@0: PORT_SetError(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE); andre@0: LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0); andre@0: } else { andre@0: if (PORT_GetError() != andre@0: SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) { andre@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); andre@0: } andre@0: LOG_ERROR_OR_EXIT(log,subjectCert,count,0); andre@0: } andre@0: } andre@0: } andre@0: andre@0: /* If the basicConstraint extension is included in an immediate CA andre@0: * certificate, make sure that the isCA flag is on. If the andre@0: * pathLenConstraint component exists, it must be greater than the andre@0: * number of CA certificates we have seen so far. If the extension andre@0: * is omitted, we will assume that this is a CA certificate with andre@0: * an unlimited pathLenConstraint (since it already passes the andre@0: * netscape-cert-type extension checking). andre@0: */ andre@0: andre@0: rv = CERT_FindBasicConstraintExten(issuerCert, &basicConstraint); andre@0: if ( rv != SECSuccess ) { andre@0: if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) { andre@0: LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0); andre@0: } andre@0: pathLengthLimit = CERT_UNLIMITED_PATH_CONSTRAINT; andre@0: /* no basic constraints found, we aren't (yet) a CA. */ andre@0: isca = PR_FALSE; andre@0: } else { andre@0: if ( basicConstraint.isCA == PR_FALSE ) { andre@0: PORT_SetError (SEC_ERROR_CA_CERT_INVALID); andre@0: LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0); andre@0: } andre@0: pathLengthLimit = basicConstraint.pathLenConstraint; andre@0: isca = PR_TRUE; andre@0: } andre@0: /* make sure that the path len constraint is properly set.*/ andre@0: if (pathLengthLimit >= 0 && currentPathLen > pathLengthLimit) { andre@0: PORT_SetError (SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID); andre@0: LOG_ERROR_OR_EXIT(log, issuerCert, count+1, pathLengthLimit); andre@0: } andre@0: andre@0: /* make sure that the entire chain is within the name space of the andre@0: * current issuer certificate. andre@0: */ andre@0: rv = CERT_CompareNameSpace(issuerCert, namesList, certsList, andre@0: arena, &badCert); andre@0: if (rv != SECSuccess || badCert != NULL) { andre@0: PORT_SetError(SEC_ERROR_CERT_NOT_IN_NAME_SPACE); andre@0: LOG_ERROR_OR_EXIT(log, badCert, count + 1, 0); andre@0: goto loser; andre@0: } andre@0: andre@0: /* XXX - the error logging may need to go down into CRL stuff at some andre@0: * point andre@0: */ andre@0: /* check revoked list (issuer) */ andre@0: rv = SEC_CheckCRL(handle, subjectCert, issuerCert, t, wincx); andre@0: if (rv == SECFailure) { andre@0: if (revoked) { andre@0: *revoked = PR_TRUE; andre@0: } andre@0: LOG_ERROR_OR_EXIT(log,subjectCert,count,0); andre@0: } else if (rv == SECWouldBlock) { andre@0: /* We found something fishy, so we intend to issue an andre@0: * error to the user, but the user may wish to continue andre@0: * processing, in which case we better make sure nothing andre@0: * worse has happened... so keep cranking the loop */ andre@0: rvFinal = SECFailure; andre@0: if (revoked) { andre@0: *revoked = PR_TRUE; andre@0: } andre@0: LOG_ERROR(log,subjectCert,count,0); andre@0: } andre@0: andre@0: if ( CERT_GetCertTrust(issuerCert, &issuerTrust) == SECSuccess) { andre@0: /* we have some trust info, but this does NOT imply that this andre@0: * cert is actually trusted for any purpose. The cert may be andre@0: * explicitly UNtrusted. We won't know until we examine the andre@0: * trust bits. andre@0: */ andre@0: unsigned int flags; andre@0: andre@0: if (certUsage != certUsageAnyCA && andre@0: certUsage != certUsageStatusResponder) { andre@0: andre@0: /* andre@0: * XXX This choice of trustType seems arbitrary. andre@0: */ andre@0: if ( certUsage == certUsageVerifyCA ) { andre@0: if ( subjectCert->nsCertType & NS_CERT_TYPE_EMAIL_CA ) { andre@0: trustType = trustEmail; andre@0: } else if ( subjectCert->nsCertType & NS_CERT_TYPE_SSL_CA ) { andre@0: trustType = trustSSL; andre@0: } else { andre@0: trustType = trustObjectSigning; andre@0: } andre@0: } andre@0: andre@0: flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType); andre@0: if (( flags & requiredFlags ) == requiredFlags) { andre@0: /* we found a trusted one, so return */ andre@0: rv = rvFinal; andre@0: goto done; andre@0: } andre@0: if (flags & CERTDB_VALID_CA) { andre@0: validCAOverride = PR_TRUE; andre@0: } andre@0: /* is it explicitly distrusted? */ andre@0: if ((flags & CERTDB_TERMINAL_RECORD) && andre@0: ((flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA)) == 0)) { andre@0: /* untrusted -- the cert is explicitly untrusted, not andre@0: * just that it doesn't chain to a trusted cert */ andre@0: PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); andre@0: LOG_ERROR_OR_EXIT(log,issuerCert,count+1,flags); andre@0: } andre@0: } else { andre@0: /* Check if we have any valid trust when cheching for andre@0: * certUsageAnyCA or certUsageStatusResponder. */ andre@0: for (trustType = trustSSL; trustType < trustTypeNone; andre@0: trustType++) { andre@0: flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType); andre@0: if ((flags & requiredFlags) == requiredFlags) { andre@0: rv = rvFinal; andre@0: goto done; andre@0: } andre@0: if (flags & CERTDB_VALID_CA) andre@0: validCAOverride = PR_TRUE; andre@0: } andre@0: /* We have 2 separate loops because we want any single trust andre@0: * bit to allow this usage to return trusted. Only if none of andre@0: * the trust bits are on do we check to see if the cert is andre@0: * untrusted */ andre@0: for (trustType = trustSSL; trustType < trustTypeNone; andre@0: trustType++) { andre@0: flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType); andre@0: /* is it explicitly distrusted? */ andre@0: if ((flags & CERTDB_TERMINAL_RECORD) && andre@0: ((flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA)) == 0)) { andre@0: /* untrusted -- the cert is explicitly untrusted, not andre@0: * just that it doesn't chain to a trusted cert */ andre@0: PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); andre@0: LOG_ERROR_OR_EXIT(log,issuerCert,count+1,flags); andre@0: } andre@0: } andre@0: } andre@0: } andre@0: andre@0: if (!validCAOverride) { andre@0: /* andre@0: * Make sure that if this is an intermediate CA in the chain that andre@0: * it was given permission by its signer to be a CA. andre@0: */ andre@0: /* andre@0: * if basicConstraints says it is a ca, then we check the andre@0: * nsCertType. If the nsCertType has any CA bits set, then andre@0: * it must have the right one. andre@0: */ andre@0: if (!isca || (issuerCert->nsCertType & NS_CERT_TYPE_CA)) { andre@0: isca = (issuerCert->nsCertType & caCertType) ? PR_TRUE : PR_FALSE; andre@0: } andre@0: andre@0: if ( !isca ) { andre@0: PORT_SetError(SEC_ERROR_CA_CERT_INVALID); andre@0: LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0); andre@0: } andre@0: andre@0: /* make sure key usage allows cert signing */ andre@0: if (CERT_CheckKeyUsage(issuerCert, requiredCAKeyUsage) != SECSuccess) { andre@0: PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); andre@0: LOG_ERROR_OR_EXIT(log,issuerCert,count+1,requiredCAKeyUsage); andre@0: } andre@0: } andre@0: andre@0: /* make sure that the issuer is not self signed. If it is, then andre@0: * stop here to prevent looping. andre@0: */ andre@0: if (issuerCert->isRoot) { andre@0: PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); andre@0: LOG_ERROR(log, issuerCert, count+1, 0); andre@0: goto loser; andre@0: } andre@0: /* The issuer cert will be the subject cert in the next loop. andre@0: * A cert is self-issued if its subject and issuer are equal and andre@0: * both are of non-zero length. andre@0: */ andre@0: subjectCertIsSelfIssued = (PRBool) andre@0: SECITEM_ItemsAreEqual(&issuerCert->derIssuer, andre@0: &issuerCert->derSubject) && andre@0: issuerCert->derSubject.len > 0; andre@0: if (subjectCertIsSelfIssued == PR_FALSE) { andre@0: /* RFC 3280 says only non-self-issued intermediate CA certs andre@0: * count in path length. andre@0: */ andre@0: ++currentPathLen; andre@0: } andre@0: andre@0: CERT_DestroyCertificate(subjectCert); andre@0: subjectCert = issuerCert; andre@0: issuerCert = NULL; andre@0: } andre@0: andre@0: PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); andre@0: LOG_ERROR(log,subjectCert,count,0); andre@0: loser: andre@0: rv = SECFailure; andre@0: done: andre@0: if (certsList != NULL) { andre@0: PORT_Free(certsList); andre@0: } andre@0: if ( issuerCert ) { andre@0: CERT_DestroyCertificate(issuerCert); andre@0: } andre@0: andre@0: if ( subjectCert ) { andre@0: CERT_DestroyCertificate(subjectCert); andre@0: } andre@0: andre@0: if ( arena != NULL ) { andre@0: PORT_FreeArena(arena, PR_FALSE); andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: SECStatus andre@0: cert_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert, andre@0: PRBool checkSig, PRBool* sigerror, andre@0: SECCertUsage certUsage, PRTime t, void *wincx, andre@0: CERTVerifyLog *log, PRBool* revoked) andre@0: { andre@0: if (CERT_GetUsePKIXForValidation()) { andre@0: return cert_VerifyCertChainPkix(cert, checkSig, certUsage, t, andre@0: wincx, log, sigerror, revoked); andre@0: } andre@0: return cert_VerifyCertChainOld(handle, cert, checkSig, sigerror, andre@0: certUsage, t, wincx, log, revoked); andre@0: } andre@0: andre@0: SECStatus andre@0: CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert, andre@0: PRBool checkSig, SECCertUsage certUsage, PRTime t, andre@0: void *wincx, CERTVerifyLog *log) andre@0: { andre@0: return cert_VerifyCertChain(handle, cert, checkSig, NULL, certUsage, t, andre@0: wincx, log, NULL); andre@0: } andre@0: andre@0: /* andre@0: * verify that a CA can sign a certificate with the requested usage. andre@0: */ andre@0: SECStatus andre@0: CERT_VerifyCACertForUsage(CERTCertDBHandle *handle, CERTCertificate *cert, andre@0: PRBool checkSig, SECCertUsage certUsage, PRTime t, andre@0: void *wincx, CERTVerifyLog *log) andre@0: { andre@0: SECTrustType trustType; andre@0: CERTBasicConstraints basicConstraint; andre@0: PRBool isca; andre@0: PRBool validCAOverride = PR_FALSE; andre@0: SECStatus rv; andre@0: SECStatus rvFinal = SECSuccess; andre@0: unsigned int flags; andre@0: unsigned int caCertType; andre@0: unsigned int requiredCAKeyUsage; andre@0: unsigned int requiredFlags; andre@0: CERTCertificate *issuerCert; andre@0: CERTCertTrust certTrust; andre@0: andre@0: andre@0: if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE, andre@0: &requiredCAKeyUsage, andre@0: &caCertType) != SECSuccess ) { andre@0: PORT_Assert(0); andre@0: EXIT_IF_NOT_LOGGING(log); andre@0: requiredCAKeyUsage = 0; andre@0: caCertType = 0; andre@0: } andre@0: andre@0: switch ( certUsage ) { andre@0: case certUsageSSLClient: andre@0: case certUsageSSLServer: andre@0: case certUsageSSLCA: andre@0: case certUsageSSLServerWithStepUp: andre@0: case certUsageEmailSigner: andre@0: case certUsageEmailRecipient: andre@0: case certUsageObjectSigner: andre@0: case certUsageVerifyCA: andre@0: case certUsageStatusResponder: andre@0: if ( CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags, andre@0: &trustType) != SECSuccess ) { andre@0: PORT_Assert(0); andre@0: EXIT_IF_NOT_LOGGING(log); andre@0: requiredFlags = 0; andre@0: trustType = trustSSL; andre@0: } andre@0: break; andre@0: default: andre@0: PORT_Assert(0); andre@0: EXIT_IF_NOT_LOGGING(log); andre@0: requiredFlags = 0; andre@0: trustType = trustSSL;/* This used to be 0, but we need something andre@0: * that matches the enumeration type. andre@0: */ andre@0: caCertType = 0; andre@0: } andre@0: andre@0: /* If the basicConstraint extension is included in an intermmediate CA andre@0: * certificate, make sure that the isCA flag is on. If the andre@0: * pathLenConstraint component exists, it must be greater than the andre@0: * number of CA certificates we have seen so far. If the extension andre@0: * is omitted, we will assume that this is a CA certificate with andre@0: * an unlimited pathLenConstraint (since it already passes the andre@0: * netscape-cert-type extension checking). andre@0: */ andre@0: andre@0: rv = CERT_FindBasicConstraintExten(cert, &basicConstraint); andre@0: if ( rv != SECSuccess ) { andre@0: if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) { andre@0: LOG_ERROR_OR_EXIT(log,cert,0,0); andre@0: } andre@0: /* no basic constraints found, we aren't (yet) a CA. */ andre@0: isca = PR_FALSE; andre@0: } else { andre@0: if ( basicConstraint.isCA == PR_FALSE ) { andre@0: PORT_SetError (SEC_ERROR_CA_CERT_INVALID); andre@0: LOG_ERROR_OR_EXIT(log,cert,0,0); andre@0: } andre@0: andre@0: /* can't check path length if we don't know the previous path */ andre@0: isca = PR_TRUE; andre@0: } andre@0: andre@0: if ( CERT_GetCertTrust(cert, &certTrust) == SECSuccess ) { andre@0: /* we have some trust info, but this does NOT imply that this andre@0: * cert is actually trusted for any purpose. The cert may be andre@0: * explicitly UNtrusted. We won't know until we examine the andre@0: * trust bits. andre@0: */ andre@0: if (certUsage == certUsageStatusResponder) { andre@0: /* Check the special case of certUsageStatusResponder */ andre@0: issuerCert = CERT_FindCertIssuer(cert, t, certUsage); andre@0: if (issuerCert) { andre@0: if (SEC_CheckCRL(handle, cert, issuerCert, t, wincx) andre@0: != SECSuccess) { andre@0: PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); andre@0: CERT_DestroyCertificate(issuerCert); andre@0: goto loser; andre@0: } andre@0: CERT_DestroyCertificate(issuerCert); andre@0: } andre@0: /* XXX We have NOT determined that this cert is trusted. andre@0: * For years, NSS has treated this as trusted, andre@0: * but it seems incorrect. andre@0: */ andre@0: rv = rvFinal; andre@0: goto done; andre@0: } andre@0: andre@0: /* andre@0: * check the trust params of the issuer andre@0: */ andre@0: flags = SEC_GET_TRUST_FLAGS(&certTrust, trustType); andre@0: if ( ( flags & requiredFlags ) == requiredFlags) { andre@0: /* we found a trusted one, so return */ andre@0: rv = rvFinal; andre@0: goto done; andre@0: } andre@0: if (flags & CERTDB_VALID_CA) { andre@0: validCAOverride = PR_TRUE; andre@0: } andre@0: /* is it explicitly distrusted? */ andre@0: if ((flags & CERTDB_TERMINAL_RECORD) && andre@0: ((flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA)) == 0)) { andre@0: /* untrusted -- the cert is explicitly untrusted, not andre@0: * just that it doesn't chain to a trusted cert */ andre@0: PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); andre@0: LOG_ERROR_OR_EXIT(log,cert,0,flags); andre@0: } andre@0: } andre@0: if (!validCAOverride) { andre@0: /* andre@0: * Make sure that if this is an intermediate CA in the chain that andre@0: * it was given permission by its signer to be a CA. andre@0: */ andre@0: /* andre@0: * if basicConstraints says it is a ca, then we check the andre@0: * nsCertType. If the nsCertType has any CA bits set, then andre@0: * it must have the right one. andre@0: */ andre@0: if (!isca || (cert->nsCertType & NS_CERT_TYPE_CA)) { andre@0: isca = (cert->nsCertType & caCertType) ? PR_TRUE : PR_FALSE; andre@0: } andre@0: andre@0: if (!isca) { andre@0: PORT_SetError(SEC_ERROR_CA_CERT_INVALID); andre@0: LOG_ERROR_OR_EXIT(log,cert,0,0); andre@0: } andre@0: andre@0: /* make sure key usage allows cert signing */ andre@0: if (CERT_CheckKeyUsage(cert, requiredCAKeyUsage) != SECSuccess) { andre@0: PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); andre@0: LOG_ERROR_OR_EXIT(log,cert,0,requiredCAKeyUsage); andre@0: } andre@0: } andre@0: /* make sure that the issuer is not self signed. If it is, then andre@0: * stop here to prevent looping. andre@0: */ andre@0: if (cert->isRoot) { andre@0: PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); andre@0: LOG_ERROR(log, cert, 0, 0); andre@0: goto loser; andre@0: } andre@0: andre@0: return CERT_VerifyCertChain(handle, cert, checkSig, certUsage, t, andre@0: wincx, log); andre@0: loser: andre@0: rv = SECFailure; andre@0: done: andre@0: return rv; andre@0: } andre@0: andre@0: #define NEXT_USAGE() { \ andre@0: i*=2; \ andre@0: certUsage++; \ andre@0: continue; \ andre@0: } andre@0: andre@0: #define VALID_USAGE() { \ andre@0: NEXT_USAGE(); \ andre@0: } andre@0: andre@0: #define INVALID_USAGE() { \ andre@0: if (returnedUsages) { \ andre@0: *returnedUsages &= (~i); \ andre@0: } \ andre@0: if (PR_TRUE == requiredUsage) { \ andre@0: valid = SECFailure; \ andre@0: } \ andre@0: NEXT_USAGE(); \ andre@0: } andre@0: andre@0: /* andre@0: * check the leaf cert against trust and usage. andre@0: * returns success if the cert is not distrusted. If the cert is andre@0: * trusted, then the trusted bool will be true. andre@0: * returns failure if the cert is distrusted. If failure, flags andre@0: * will return the flag bits that indicated distrust. andre@0: */ andre@0: SECStatus andre@0: cert_CheckLeafTrust(CERTCertificate *cert, SECCertUsage certUsage, andre@0: unsigned int *failedFlags, PRBool *trusted) andre@0: { andre@0: unsigned int flags; andre@0: CERTCertTrust trust; andre@0: andre@0: *failedFlags = 0; andre@0: *trusted = PR_FALSE; andre@0: andre@0: /* check trust flags to see if this cert is directly trusted */ andre@0: if ( CERT_GetCertTrust(cert, &trust) == SECSuccess ) { andre@0: switch ( certUsage ) { andre@0: case certUsageSSLClient: andre@0: case certUsageSSLServer: andre@0: flags = trust.sslFlags; andre@0: andre@0: /* is the cert directly trusted or not trusted ? */ andre@0: if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is andre@0: * authoritative */ andre@0: if ( flags & CERTDB_TRUSTED ) { /* trust this cert */ andre@0: *trusted = PR_TRUE; andre@0: return SECSuccess; andre@0: } else { /* don't trust this cert */ andre@0: *failedFlags = flags; andre@0: return SECFailure; andre@0: } andre@0: } andre@0: break; andre@0: case certUsageSSLServerWithStepUp: andre@0: /* XXX - step up certs can't be directly trusted, only distrust */ andre@0: flags = trust.sslFlags; andre@0: if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is andre@0: * authoritative */ andre@0: if (( flags & CERTDB_TRUSTED ) == 0) { andre@0: /* don't trust this cert */ andre@0: *failedFlags = flags; andre@0: return SECFailure; andre@0: } andre@0: } andre@0: break; andre@0: case certUsageSSLCA: andre@0: flags = trust.sslFlags; andre@0: if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is andre@0: * authoritative */ andre@0: if (( flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA) ) == 0) { andre@0: /* don't trust this cert */ andre@0: *failedFlags = flags; andre@0: return SECFailure; andre@0: } andre@0: } andre@0: break; andre@0: case certUsageEmailSigner: andre@0: case certUsageEmailRecipient: andre@0: flags = trust.emailFlags; andre@0: if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is andre@0: * authoritative */ andre@0: if ( flags & CERTDB_TRUSTED ) { /* trust this cert */ andre@0: *trusted = PR_TRUE; andre@0: return SECSuccess; andre@0: } andre@0: else { /* don't trust this cert */ andre@0: *failedFlags = flags; andre@0: return SECFailure; andre@0: } andre@0: } andre@0: andre@0: break; andre@0: case certUsageObjectSigner: andre@0: flags = trust.objectSigningFlags; andre@0: andre@0: if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is andre@0: * authoritative */ andre@0: if ( flags & CERTDB_TRUSTED ) { /* trust this cert */ andre@0: *trusted = PR_TRUE; andre@0: return SECSuccess; andre@0: } else { /* don't trust this cert */ andre@0: *failedFlags = flags; andre@0: return SECFailure; andre@0: } andre@0: } andre@0: break; andre@0: case certUsageVerifyCA: andre@0: case certUsageStatusResponder: andre@0: flags = trust.sslFlags; andre@0: /* is the cert directly trusted or not trusted ? */ andre@0: if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) == andre@0: ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) { andre@0: *trusted = PR_TRUE; andre@0: return SECSuccess; andre@0: } andre@0: flags = trust.emailFlags; andre@0: /* is the cert directly trusted or not trusted ? */ andre@0: if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) == andre@0: ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) { andre@0: *trusted = PR_TRUE; andre@0: return SECSuccess; andre@0: } andre@0: flags = trust.objectSigningFlags; andre@0: /* is the cert directly trusted or not trusted ? */ andre@0: if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) == andre@0: ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) { andre@0: *trusted = PR_TRUE; andre@0: return SECSuccess; andre@0: } andre@0: /* fall through to test distrust */ andre@0: case certUsageAnyCA: andre@0: case certUsageUserCertImport: andre@0: /* do we distrust these certs explicitly */ andre@0: flags = trust.sslFlags; andre@0: if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is andre@0: * authoritative */ andre@0: if ((flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA)) == 0) { andre@0: *failedFlags = flags; andre@0: return SECFailure; andre@0: } andre@0: } andre@0: flags = trust.emailFlags; andre@0: if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is andre@0: * authoritative */ andre@0: if ((flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA)) == 0) { andre@0: *failedFlags = flags; andre@0: return SECFailure; andre@0: } andre@0: } andre@0: /* fall through */ andre@0: case certUsageProtectedObjectSigner: andre@0: flags = trust.objectSigningFlags; andre@0: if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is andre@0: * authoritative */ andre@0: if ((flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA)) == 0) { andre@0: *failedFlags = flags; andre@0: return SECFailure; andre@0: } andre@0: } andre@0: break; andre@0: } andre@0: } andre@0: return SECSuccess; andre@0: } andre@0: andre@0: /* andre@0: * verify a certificate by checking if it's valid and that we andre@0: * trust the issuer. andre@0: * andre@0: * certificateUsage contains a bitfield of all cert usages that are andre@0: * required for verification to succeed andre@0: * andre@0: * a bitfield of cert usages is returned in *returnedUsages andre@0: * if requiredUsages is non-zero, the returned bitmap is only andre@0: * for those required usages, otherwise it is for all usages andre@0: * andre@0: */ andre@0: SECStatus andre@0: CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert, andre@0: PRBool checkSig, SECCertificateUsage requiredUsages, PRTime t, andre@0: void *wincx, CERTVerifyLog *log, SECCertificateUsage* returnedUsages) andre@0: { andre@0: SECStatus rv; andre@0: SECStatus valid; andre@0: unsigned int requiredKeyUsage; andre@0: unsigned int requiredCertType; andre@0: unsigned int flags; andre@0: unsigned int certType; andre@0: PRBool allowOverride; andre@0: SECCertTimeValidity validity; andre@0: CERTStatusConfig *statusConfig; andre@0: PRInt32 i; andre@0: SECCertUsage certUsage = 0; andre@0: PRBool checkedOCSP = PR_FALSE; andre@0: PRBool checkAllUsages = PR_FALSE; andre@0: PRBool revoked = PR_FALSE; andre@0: PRBool sigerror = PR_FALSE; andre@0: PRBool trusted = PR_FALSE; andre@0: andre@0: if (!requiredUsages) { andre@0: /* there are no required usages, so the user probably wants to andre@0: get status for all usages */ andre@0: checkAllUsages = PR_TRUE; andre@0: } andre@0: andre@0: if (returnedUsages) { andre@0: *returnedUsages = 0; andre@0: } else { andre@0: /* we don't have a place to return status for all usages, andre@0: so we can skip checks for usages that aren't required */ andre@0: checkAllUsages = PR_FALSE; andre@0: } andre@0: valid = SECSuccess ; /* start off assuming cert is valid */ andre@0: andre@0: /* make sure that the cert is valid at time t */ andre@0: allowOverride = (PRBool)((requiredUsages & certificateUsageSSLServer) || andre@0: (requiredUsages & certificateUsageSSLServerWithStepUp)); andre@0: validity = CERT_CheckCertValidTimes(cert, t, allowOverride); andre@0: if ( validity != secCertTimeValid ) { andre@0: valid = SECFailure; andre@0: LOG_ERROR_OR_EXIT(log,cert,0,validity); andre@0: } andre@0: andre@0: /* check key usage and netscape cert type */ andre@0: cert_GetCertType(cert); andre@0: certType = cert->nsCertType; andre@0: andre@0: for (i=1; i<=certificateUsageHighest && andre@0: (SECSuccess == valid || returnedUsages || log) ; ) { andre@0: PRBool requiredUsage = (i & requiredUsages) ? PR_TRUE : PR_FALSE; andre@0: if (PR_FALSE == requiredUsage && PR_FALSE == checkAllUsages) { andre@0: NEXT_USAGE(); andre@0: } andre@0: if (returnedUsages) { andre@0: *returnedUsages |= i; /* start off assuming this usage is valid */ andre@0: } andre@0: switch ( certUsage ) { andre@0: case certUsageSSLClient: andre@0: case certUsageSSLServer: andre@0: case certUsageSSLServerWithStepUp: andre@0: case certUsageSSLCA: andre@0: case certUsageEmailSigner: andre@0: case certUsageEmailRecipient: andre@0: case certUsageObjectSigner: andre@0: case certUsageStatusResponder: andre@0: rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE, andre@0: &requiredKeyUsage, andre@0: &requiredCertType); andre@0: if ( rv != SECSuccess ) { andre@0: PORT_Assert(0); andre@0: /* EXIT_IF_NOT_LOGGING(log); XXX ??? */ andre@0: requiredKeyUsage = 0; andre@0: requiredCertType = 0; andre@0: INVALID_USAGE(); andre@0: } andre@0: break; andre@0: andre@0: case certUsageAnyCA: andre@0: case certUsageProtectedObjectSigner: andre@0: case certUsageUserCertImport: andre@0: case certUsageVerifyCA: andre@0: /* these usages cannot be verified */ andre@0: NEXT_USAGE(); andre@0: andre@0: default: andre@0: PORT_Assert(0); andre@0: requiredKeyUsage = 0; andre@0: requiredCertType = 0; andre@0: INVALID_USAGE(); andre@0: } andre@0: if ( CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess ) { andre@0: if (PR_TRUE == requiredUsage) { andre@0: PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); andre@0: } andre@0: LOG_ERROR(log,cert,0,requiredKeyUsage); andre@0: INVALID_USAGE(); andre@0: } andre@0: if ( !( certType & requiredCertType ) ) { andre@0: if (PR_TRUE == requiredUsage) { andre@0: PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE); andre@0: } andre@0: LOG_ERROR(log,cert,0,requiredCertType); andre@0: INVALID_USAGE(); andre@0: } andre@0: andre@0: rv = cert_CheckLeafTrust(cert, certUsage, &flags, &trusted); andre@0: if (rv == SECFailure) { andre@0: if (PR_TRUE == requiredUsage) { andre@0: PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); andre@0: } andre@0: LOG_ERROR(log, cert, 0, flags); andre@0: INVALID_USAGE(); andre@0: } else if (trusted) { andre@0: VALID_USAGE(); andre@0: } andre@0: andre@0: if (PR_TRUE == revoked || PR_TRUE == sigerror) { andre@0: INVALID_USAGE(); andre@0: } andre@0: andre@0: rv = cert_VerifyCertChain(handle, cert, andre@0: checkSig, &sigerror, andre@0: certUsage, t, wincx, log, andre@0: &revoked); andre@0: andre@0: if (rv != SECSuccess) { andre@0: /* EXIT_IF_NOT_LOGGING(log); XXX ???? */ andre@0: INVALID_USAGE(); andre@0: } andre@0: andre@0: /* andre@0: * Check OCSP revocation status, but only if the cert we are checking andre@0: * is not a status responder itself. We only do this in the case andre@0: * where we checked the cert chain (above); explicit trust "wins" andre@0: * (avoids status checking, just as it avoids CRL checking) by andre@0: * bypassing this code. andre@0: */ andre@0: andre@0: if (PR_FALSE == checkedOCSP) { andre@0: checkedOCSP = PR_TRUE; /* only check OCSP once */ andre@0: statusConfig = CERT_GetStatusConfig(handle); andre@0: if (requiredUsages != certificateUsageStatusResponder && andre@0: statusConfig != NULL) { andre@0: if (statusConfig->statusChecker != NULL) { andre@0: rv = (* statusConfig->statusChecker)(handle, cert, andre@0: t, wincx); andre@0: if (rv != SECSuccess) { andre@0: LOG_ERROR(log,cert,0,0); andre@0: revoked = PR_TRUE; andre@0: INVALID_USAGE(); andre@0: } andre@0: } andre@0: } andre@0: } andre@0: andre@0: NEXT_USAGE(); andre@0: } andre@0: andre@0: loser: andre@0: return(valid); andre@0: } andre@0: andre@0: SECStatus andre@0: CERT_VerifyCert(CERTCertDBHandle *handle, CERTCertificate *cert, andre@0: PRBool checkSig, SECCertUsage certUsage, PRTime t, andre@0: void *wincx, CERTVerifyLog *log) andre@0: { andre@0: return cert_VerifyCertWithFlags(handle, cert, checkSig, certUsage, t, andre@0: CERT_VERIFYCERT_USE_DEFAULTS, wincx, log); andre@0: } andre@0: andre@0: SECStatus andre@0: cert_VerifyCertWithFlags(CERTCertDBHandle *handle, CERTCertificate *cert, andre@0: PRBool checkSig, SECCertUsage certUsage, PRTime t, andre@0: PRUint32 flags, void *wincx, CERTVerifyLog *log) andre@0: { andre@0: SECStatus rv; andre@0: unsigned int requiredKeyUsage; andre@0: unsigned int requiredCertType; andre@0: unsigned int failedFlags; andre@0: unsigned int certType; andre@0: PRBool trusted; andre@0: PRBool allowOverride; andre@0: SECCertTimeValidity validity; andre@0: CERTStatusConfig *statusConfig; andre@0: andre@0: #ifdef notdef andre@0: /* check if this cert is in the Evil list */ andre@0: rv = CERT_CheckForEvilCert(cert); andre@0: if ( rv != SECSuccess ) { andre@0: PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); andre@0: LOG_ERROR_OR_EXIT(log,cert,0,0); andre@0: } andre@0: #endif andre@0: andre@0: /* make sure that the cert is valid at time t */ andre@0: allowOverride = (PRBool)((certUsage == certUsageSSLServer) || andre@0: (certUsage == certUsageSSLServerWithStepUp)); andre@0: validity = CERT_CheckCertValidTimes(cert, t, allowOverride); andre@0: if ( validity != secCertTimeValid ) { andre@0: LOG_ERROR_OR_EXIT(log,cert,0,validity); andre@0: } andre@0: andre@0: /* check key usage and netscape cert type */ andre@0: cert_GetCertType(cert); andre@0: certType = cert->nsCertType; andre@0: switch ( certUsage ) { andre@0: case certUsageSSLClient: andre@0: case certUsageSSLServer: andre@0: case certUsageSSLServerWithStepUp: andre@0: case certUsageSSLCA: andre@0: case certUsageEmailSigner: andre@0: case certUsageEmailRecipient: andre@0: case certUsageObjectSigner: andre@0: case certUsageStatusResponder: andre@0: rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE, andre@0: &requiredKeyUsage, andre@0: &requiredCertType); andre@0: if ( rv != SECSuccess ) { andre@0: PORT_Assert(0); andre@0: EXIT_IF_NOT_LOGGING(log); andre@0: requiredKeyUsage = 0; andre@0: requiredCertType = 0; andre@0: } andre@0: break; andre@0: case certUsageVerifyCA: andre@0: case certUsageAnyCA: andre@0: requiredKeyUsage = KU_KEY_CERT_SIGN; andre@0: requiredCertType = NS_CERT_TYPE_CA; andre@0: if ( ! ( certType & NS_CERT_TYPE_CA ) ) { andre@0: certType |= NS_CERT_TYPE_CA; andre@0: } andre@0: break; andre@0: default: andre@0: PORT_Assert(0); andre@0: EXIT_IF_NOT_LOGGING(log); andre@0: requiredKeyUsage = 0; andre@0: requiredCertType = 0; andre@0: } andre@0: if ( CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess ) { andre@0: PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); andre@0: LOG_ERROR_OR_EXIT(log,cert,0,requiredKeyUsage); andre@0: } andre@0: if ( !( certType & requiredCertType ) ) { andre@0: PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE); andre@0: LOG_ERROR_OR_EXIT(log,cert,0,requiredCertType); andre@0: } andre@0: andre@0: rv = cert_CheckLeafTrust(cert, certUsage, &failedFlags, &trusted); andre@0: if (rv == SECFailure) { andre@0: PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); andre@0: LOG_ERROR_OR_EXIT(log, cert, 0, failedFlags); andre@0: } else if (trusted) { andre@0: goto done; andre@0: } andre@0: andre@0: andre@0: rv = CERT_VerifyCertChain(handle, cert, checkSig, certUsage, andre@0: t, wincx, log); andre@0: if (rv != SECSuccess) { andre@0: EXIT_IF_NOT_LOGGING(log); andre@0: } andre@0: andre@0: /* andre@0: * Check revocation status, but only if the cert we are checking is not a andre@0: * status responder itself and the caller did not ask us to skip the check. andre@0: * We only do this in the case where we checked the cert chain (above); andre@0: * explicit trust "wins" (avoids status checking, just as it avoids CRL andre@0: * checking, which is all done inside VerifyCertChain) by bypassing this andre@0: * code. andre@0: */ andre@0: if (!(flags & CERT_VERIFYCERT_SKIP_OCSP) && andre@0: certUsage != certUsageStatusResponder) { andre@0: statusConfig = CERT_GetStatusConfig(handle); andre@0: if (statusConfig && statusConfig->statusChecker) { andre@0: rv = (* statusConfig->statusChecker)(handle, cert, andre@0: t, wincx); andre@0: if (rv != SECSuccess) { andre@0: LOG_ERROR_OR_EXIT(log,cert,0,0); andre@0: } andre@0: } andre@0: } andre@0: andre@0: done: andre@0: if (log && log->head) { andre@0: return SECFailure; andre@0: } andre@0: return(SECSuccess); andre@0: andre@0: loser: andre@0: rv = SECFailure; andre@0: andre@0: return(rv); andre@0: } andre@0: andre@0: /* andre@0: * verify a certificate by checking if its valid and that we andre@0: * trust the issuer. Verify time against now. andre@0: */ andre@0: SECStatus andre@0: CERT_VerifyCertificateNow(CERTCertDBHandle *handle, CERTCertificate *cert, andre@0: PRBool checkSig, SECCertificateUsage requiredUsages, andre@0: void *wincx, SECCertificateUsage* returnedUsages) andre@0: { andre@0: return(CERT_VerifyCertificate(handle, cert, checkSig, andre@0: requiredUsages, PR_Now(), wincx, NULL, returnedUsages)); andre@0: } andre@0: andre@0: /* obsolete, do not use for new code */ andre@0: SECStatus andre@0: CERT_VerifyCertNow(CERTCertDBHandle *handle, CERTCertificate *cert, andre@0: PRBool checkSig, SECCertUsage certUsage, void *wincx) andre@0: { andre@0: return(CERT_VerifyCert(handle, cert, checkSig, andre@0: certUsage, PR_Now(), wincx, NULL)); andre@0: } andre@0: andre@0: andre@0: /* [ FROM pcertdb.c ] */ andre@0: /* andre@0: * Supported usage values and types: andre@0: * certUsageSSLClient andre@0: * certUsageSSLServer andre@0: * certUsageSSLServerWithStepUp andre@0: * certUsageEmailSigner andre@0: * certUsageEmailRecipient andre@0: * certUsageObjectSigner andre@0: */ andre@0: andre@0: CERTCertificate * andre@0: CERT_FindMatchingCert(CERTCertDBHandle *handle, SECItem *derName, andre@0: CERTCertOwner owner, SECCertUsage usage, andre@0: PRBool preferTrusted, PRTime validTime, PRBool validOnly) andre@0: { andre@0: CERTCertList *certList = NULL; andre@0: CERTCertificate *cert = NULL; andre@0: CERTCertTrust certTrust; andre@0: unsigned int requiredTrustFlags; andre@0: SECTrustType requiredTrustType; andre@0: unsigned int flags; andre@0: andre@0: PRBool lookingForCA = PR_FALSE; andre@0: SECStatus rv; andre@0: CERTCertListNode *node; andre@0: CERTCertificate *saveUntrustedCA = NULL; andre@0: andre@0: /* if preferTrusted is set, must be a CA cert */ andre@0: PORT_Assert( ! ( preferTrusted && ( owner != certOwnerCA ) ) ); andre@0: andre@0: if ( owner == certOwnerCA ) { andre@0: lookingForCA = PR_TRUE; andre@0: if ( preferTrusted ) { andre@0: rv = CERT_TrustFlagsForCACertUsage(usage, &requiredTrustFlags, andre@0: &requiredTrustType); andre@0: if ( rv != SECSuccess ) { andre@0: goto loser; andre@0: } andre@0: requiredTrustFlags |= CERTDB_VALID_CA; andre@0: } andre@0: } andre@0: andre@0: certList = CERT_CreateSubjectCertList(NULL, handle, derName, validTime, andre@0: validOnly); andre@0: if ( certList != NULL ) { andre@0: rv = CERT_FilterCertListByUsage(certList, usage, lookingForCA); andre@0: if ( rv != SECSuccess ) { andre@0: goto loser; andre@0: } andre@0: andre@0: node = CERT_LIST_HEAD(certList); andre@0: andre@0: while ( !CERT_LIST_END(node, certList) ) { andre@0: cert = node->cert; andre@0: andre@0: /* looking for a trusted CA cert */ andre@0: if ( ( owner == certOwnerCA ) && preferTrusted && andre@0: ( requiredTrustType != trustTypeNone ) ) { andre@0: andre@0: if ( CERT_GetCertTrust(cert, &certTrust) != SECSuccess ) { andre@0: flags = 0; andre@0: } else { andre@0: flags = SEC_GET_TRUST_FLAGS(&certTrust, requiredTrustType); andre@0: } andre@0: andre@0: if ( ( flags & requiredTrustFlags ) != requiredTrustFlags ) { andre@0: /* cert is not trusted */ andre@0: /* if this is the first cert to get this far, then save andre@0: * it, so we can use it if we can't find a trusted one andre@0: */ andre@0: if ( saveUntrustedCA == NULL ) { andre@0: saveUntrustedCA = cert; andre@0: } andre@0: goto endloop; andre@0: } andre@0: } andre@0: /* if we got this far, then this cert meets all criteria */ andre@0: break; andre@0: andre@0: endloop: andre@0: node = CERT_LIST_NEXT(node); andre@0: cert = NULL; andre@0: } andre@0: andre@0: /* use the saved one if we have it */ andre@0: if ( cert == NULL ) { andre@0: cert = saveUntrustedCA; andre@0: } andre@0: andre@0: /* if we found one then bump the ref count before freeing the list */ andre@0: if ( cert != NULL ) { andre@0: /* bump the ref count */ andre@0: cert = CERT_DupCertificate(cert); andre@0: } andre@0: andre@0: CERT_DestroyCertList(certList); andre@0: } andre@0: andre@0: return(cert); andre@0: andre@0: loser: andre@0: if ( certList != NULL ) { andre@0: CERT_DestroyCertList(certList); andre@0: } andre@0: andre@0: return(NULL); andre@0: } andre@0: andre@0: andre@0: /* [ From certdb.c ] */ andre@0: /* andre@0: * Filter a list of certificates, removing those certs that do not have andre@0: * one of the named CA certs somewhere in their cert chain. andre@0: * andre@0: * "certList" - the list of certificates to filter andre@0: * "nCANames" - number of CA names andre@0: * "caNames" - array of CA names in string(rfc 1485) form andre@0: * "usage" - what use the certs are for, this is used when andre@0: * selecting CA certs andre@0: */ andre@0: SECStatus andre@0: CERT_FilterCertListByCANames(CERTCertList *certList, int nCANames, andre@0: char **caNames, SECCertUsage usage) andre@0: { andre@0: CERTCertificate *issuerCert = NULL; andre@0: CERTCertificate *subjectCert; andre@0: CERTCertListNode *node, *freenode; andre@0: CERTCertificate *cert; andre@0: int n; andre@0: char **names; andre@0: PRBool found; andre@0: PRTime time; andre@0: andre@0: if ( nCANames <= 0 ) { andre@0: return(SECSuccess); andre@0: } andre@0: andre@0: time = PR_Now(); andre@0: andre@0: node = CERT_LIST_HEAD(certList); andre@0: andre@0: while ( ! CERT_LIST_END(node, certList) ) { andre@0: cert = node->cert; andre@0: andre@0: subjectCert = CERT_DupCertificate(cert); andre@0: andre@0: /* traverse the CA certs for this cert */ andre@0: found = PR_FALSE; andre@0: while ( subjectCert != NULL ) { andre@0: n = nCANames; andre@0: names = caNames; andre@0: andre@0: if (subjectCert->issuerName != NULL) { andre@0: while ( n > 0 ) { andre@0: if ( PORT_Strcmp(*names, subjectCert->issuerName) == 0 ) { andre@0: found = PR_TRUE; andre@0: break; andre@0: } andre@0: andre@0: n--; andre@0: names++; andre@0: } andre@0: } andre@0: andre@0: if ( found ) { andre@0: break; andre@0: } andre@0: andre@0: issuerCert = CERT_FindCertIssuer(subjectCert, time, usage); andre@0: if ( issuerCert == subjectCert ) { andre@0: CERT_DestroyCertificate(issuerCert); andre@0: issuerCert = NULL; andre@0: break; andre@0: } andre@0: CERT_DestroyCertificate(subjectCert); andre@0: subjectCert = issuerCert; andre@0: andre@0: } andre@0: CERT_DestroyCertificate(subjectCert); andre@0: if ( !found ) { andre@0: /* CA was not found, so remove this cert from the list */ andre@0: freenode = node; andre@0: node = CERT_LIST_NEXT(node); andre@0: CERT_RemoveCertListNode(freenode); andre@0: } else { andre@0: /* CA was found, so leave it in the list */ andre@0: node = CERT_LIST_NEXT(node); andre@0: } andre@0: } andre@0: andre@0: return(SECSuccess); andre@0: } andre@0: andre@0: /* andre@0: * Given a certificate, return a string containing the nickname, and possibly andre@0: * one of the validity strings, based on the current validity state of the andre@0: * certificate. andre@0: * andre@0: * "arena" - arena to allocate returned string from. If NULL, then heap andre@0: * is used. andre@0: * "cert" - the cert to get nickname from andre@0: * "expiredString" - the string to append to the nickname if the cert is andre@0: * expired. andre@0: * "notYetGoodString" - the string to append to the nickname if the cert is andre@0: * not yet good. andre@0: */ andre@0: char * andre@0: CERT_GetCertNicknameWithValidity(PLArenaPool *arena, CERTCertificate *cert, andre@0: char *expiredString, char *notYetGoodString) andre@0: { andre@0: SECCertTimeValidity validity; andre@0: char *nickname = NULL, *tmpstr = NULL; andre@0: andre@0: validity = CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE); andre@0: andre@0: /* if the cert is good, then just use the nickname directly */ andre@0: if ( validity == secCertTimeValid ) { andre@0: if ( arena == NULL ) { andre@0: nickname = PORT_Strdup(cert->nickname); andre@0: } else { andre@0: nickname = PORT_ArenaStrdup(arena, cert->nickname); andre@0: } andre@0: andre@0: if ( nickname == NULL ) { andre@0: goto loser; andre@0: } andre@0: } else { andre@0: andre@0: /* if the cert is not valid, then tack one of the strings on the andre@0: * end andre@0: */ andre@0: if ( validity == secCertTimeExpired ) { andre@0: tmpstr = PR_smprintf("%s%s", cert->nickname, andre@0: expiredString); andre@0: } else if ( validity == secCertTimeNotValidYet ) { andre@0: /* not yet valid */ andre@0: tmpstr = PR_smprintf("%s%s", cert->nickname, andre@0: notYetGoodString); andre@0: } else { andre@0: /* undetermined */ andre@0: tmpstr = PR_smprintf("%s", andre@0: "(NULL) (Validity Unknown)"); andre@0: } andre@0: andre@0: if ( tmpstr == NULL ) { andre@0: goto loser; andre@0: } andre@0: andre@0: if ( arena ) { andre@0: /* copy the string into the arena and free the malloc'd one */ andre@0: nickname = PORT_ArenaStrdup(arena, tmpstr); andre@0: PORT_Free(tmpstr); andre@0: } else { andre@0: nickname = tmpstr; andre@0: } andre@0: if ( nickname == NULL ) { andre@0: goto loser; andre@0: } andre@0: } andre@0: return(nickname); andre@0: andre@0: loser: andre@0: return(NULL); andre@0: } andre@0: andre@0: /* andre@0: * Collect the nicknames from all certs in a CertList. If the cert is not andre@0: * valid, append a string to that nickname. andre@0: * andre@0: * "certList" - the list of certificates andre@0: * "expiredString" - the string to append to the nickname of any expired cert andre@0: * "notYetGoodString" - the string to append to the nickname of any cert andre@0: * that is not yet valid andre@0: */ andre@0: CERTCertNicknames * andre@0: CERT_NicknameStringsFromCertList(CERTCertList *certList, char *expiredString, andre@0: char *notYetGoodString) andre@0: { andre@0: CERTCertNicknames *names; andre@0: PLArenaPool *arena; andre@0: CERTCertListNode *node; andre@0: char **nn; andre@0: andre@0: /* allocate an arena */ andre@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); andre@0: if ( arena == NULL ) { andre@0: return(NULL); andre@0: } andre@0: andre@0: /* allocate the structure */ andre@0: names = PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames)); andre@0: if ( names == NULL ) { andre@0: goto loser; andre@0: } andre@0: andre@0: /* init the structure */ andre@0: names->arena = arena; andre@0: names->head = NULL; andre@0: names->numnicknames = 0; andre@0: names->nicknames = NULL; andre@0: names->totallen = 0; andre@0: andre@0: /* count the certs in the list */ andre@0: node = CERT_LIST_HEAD(certList); andre@0: while ( ! CERT_LIST_END(node, certList) ) { andre@0: names->numnicknames++; andre@0: node = CERT_LIST_NEXT(node); andre@0: } andre@0: andre@0: /* allocate nicknames array */ andre@0: names->nicknames = PORT_ArenaAlloc(arena, andre@0: sizeof(char *) * names->numnicknames); andre@0: if ( names->nicknames == NULL ) { andre@0: goto loser; andre@0: } andre@0: andre@0: /* just in case printf can't deal with null strings */ andre@0: if (expiredString == NULL ) { andre@0: expiredString = ""; andre@0: } andre@0: andre@0: if ( notYetGoodString == NULL ) { andre@0: notYetGoodString = ""; andre@0: } andre@0: andre@0: /* traverse the list of certs and collect the nicknames */ andre@0: nn = names->nicknames; andre@0: node = CERT_LIST_HEAD(certList); andre@0: while ( ! CERT_LIST_END(node, certList) ) { andre@0: *nn = CERT_GetCertNicknameWithValidity(arena, node->cert, andre@0: expiredString, andre@0: notYetGoodString); andre@0: if ( *nn == NULL ) { andre@0: goto loser; andre@0: } andre@0: andre@0: names->totallen += PORT_Strlen(*nn); andre@0: andre@0: nn++; andre@0: node = CERT_LIST_NEXT(node); andre@0: } andre@0: andre@0: return(names); andre@0: andre@0: loser: andre@0: PORT_FreeArena(arena, PR_FALSE); andre@0: return(NULL); andre@0: } andre@0: andre@0: /* andre@0: * Extract the nickname from a nickmake string that may have either andre@0: * expiredString or notYetGoodString appended. andre@0: * andre@0: * Args: andre@0: * "namestring" - the string containing the nickname, and possibly andre@0: * one of the validity label strings andre@0: * "expiredString" - the expired validity label string andre@0: * "notYetGoodString" - the not yet good validity label string andre@0: * andre@0: * Returns the raw nickname andre@0: */ andre@0: char * andre@0: CERT_ExtractNicknameString(char *namestring, char *expiredString, andre@0: char *notYetGoodString) andre@0: { andre@0: int explen, nyglen, namelen; andre@0: int retlen; andre@0: char *retstr; andre@0: andre@0: namelen = PORT_Strlen(namestring); andre@0: explen = PORT_Strlen(expiredString); andre@0: nyglen = PORT_Strlen(notYetGoodString); andre@0: andre@0: if ( namelen > explen ) { andre@0: if ( PORT_Strcmp(expiredString, &namestring[namelen-explen]) == 0 ) { andre@0: retlen = namelen - explen; andre@0: retstr = (char *)PORT_Alloc(retlen+1); andre@0: if ( retstr == NULL ) { andre@0: goto loser; andre@0: } andre@0: andre@0: PORT_Memcpy(retstr, namestring, retlen); andre@0: retstr[retlen] = '\0'; andre@0: goto done; andre@0: } andre@0: } andre@0: andre@0: if ( namelen > nyglen ) { andre@0: if ( PORT_Strcmp(notYetGoodString, &namestring[namelen-nyglen]) == 0) { andre@0: retlen = namelen - nyglen; andre@0: retstr = (char *)PORT_Alloc(retlen+1); andre@0: if ( retstr == NULL ) { andre@0: goto loser; andre@0: } andre@0: andre@0: PORT_Memcpy(retstr, namestring, retlen); andre@0: retstr[retlen] = '\0'; andre@0: goto done; andre@0: } andre@0: } andre@0: andre@0: /* if name string is shorter than either invalid string, then it must andre@0: * be a raw nickname andre@0: */ andre@0: retstr = PORT_Strdup(namestring); andre@0: andre@0: done: andre@0: return(retstr); andre@0: andre@0: loser: andre@0: return(NULL); andre@0: } andre@0: andre@0: CERTCertList * andre@0: CERT_GetCertChainFromCert(CERTCertificate *cert, PRTime time, SECCertUsage usage) andre@0: { andre@0: CERTCertList *chain = NULL; andre@0: int count = 0; andre@0: andre@0: if (NULL == cert) { andre@0: return NULL; andre@0: } andre@0: andre@0: cert = CERT_DupCertificate(cert); andre@0: if (NULL == cert) { andre@0: PORT_SetError(SEC_ERROR_NO_MEMORY); andre@0: return NULL; andre@0: } andre@0: andre@0: chain = CERT_NewCertList(); andre@0: if (NULL == chain) { andre@0: PORT_SetError(SEC_ERROR_NO_MEMORY); andre@0: return NULL; andre@0: } andre@0: andre@0: while (cert != NULL && ++count <= CERT_MAX_CERT_CHAIN) { andre@0: if (SECSuccess != CERT_AddCertToListTail(chain, cert)) { andre@0: /* return partial chain */ andre@0: PORT_SetError(SEC_ERROR_NO_MEMORY); andre@0: return chain; andre@0: } andre@0: andre@0: if (cert->isRoot) { andre@0: /* return complete chain */ andre@0: return chain; andre@0: } andre@0: andre@0: cert = CERT_FindCertIssuer(cert, time, usage); andre@0: } andre@0: andre@0: /* return partial chain */ andre@0: PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); andre@0: return chain; andre@0: }