예제 #1
0
static CSSM_RETURN tpIssuerCertViaNet(
	const CSSM_DATA &url,
	CSSM_CL_HANDLE	clHand,
	CSSM_CSP_HANDLE	cspHand,
	const char		*verifyTime,
	TPCertInfo 		&subject,	
	TPCertInfo 		*&rtnCert)
{
	TPCertInfo *issuer = NULL;
	CSSM_DATA certData;
	CSSM_RETURN crtn;
	Allocator &alloc = Allocator::standard();
	
	crtn = tpFetchViaNet(url, NULL, LT_Cert, NULL, alloc, certData);
	if(crtn) {
		tpErrorLog("tpIssuerCertViaNet: net fetch failed\n");
		return CSSMERR_APPLETP_CERT_NOT_FOUND_FROM_ISSUER;
	}	
	try {
		issuer = new TPCertInfo(clHand,
			cspHand,
			&certData,
			TIC_CopyData,
			verifyTime);
	}
	catch(...) {
		tpErrorLog("tpIssuerCertViaNet: bad cert via net fetch\n");
		alloc.free(certData.Data);
		rtnCert = NULL;
		return CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
	}
	alloc.free(certData.Data);
	
	/* subject/issuer match? */
	if(!issuer->isIssuerOf(subject)) {
		tpErrorLog("tpIssuerCertViaNet: wrong issuer cert via net fetch\n");
		crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
	}
	else {
		/* yep, do a sig verify */
		crtn = subject.verifyWithIssuer(issuer);
		if(crtn) {
			tpErrorLog("tpIssuerCertViaNet: sig verify fail for cert via net "
				"fetch\n");
			crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
		}
	}
	if(crtn) {
		assert(issuer != NULL);
		delete issuer;
		issuer = NULL;
	}
	rtnCert = issuer;
	return crtn;
}
/* 
 * Does this cert have a CrlDistributionPoints extension? We don't parse it, we
 * just tell the caller whether or not it has one.
 */
static bool tpCertHasCrlDistPt(
	TPCertInfo &cert)
{
	CSSM_DATA_PTR fieldValue;		
	CSSM_RETURN crtn = cert.fetchField(&CSSMOID_CrlDistributionPoints, &fieldValue);
	if(crtn) {
		return false;
	}
	else {
		cert.freeField(&CSSMOID_CrlDistributionPoints,	fieldValue);
		return true;
	}
}
/*
 * Get current CRL status for a certificate and its issuers.
 *
 * Possible results:
 *
 * CSSM_OK (we have a valid CRL; certificate is not revoked)
 * CSSMERR_TP_CERT_REVOKED (we have a valid CRL; certificate is revoked)
 * CSSMERR_APPLETP_NETWORK_FAILURE (CRL not available, download in progress)
 * CSSMERR_APPLETP_CRL_NOT_FOUND (CRL not available, and not being fetched)
 * CSSMERR_TP_INTERNAL_ERROR (unexpected error)
 *
 * Note that ocspdCRLStatus does NOT wait for the CRL to be downloaded before
 * returning, nor does it initiate a CRL download.
 */
static
CSSM_RETURN tpGetCrlStatusForCert(
	TPCertInfo						&subject,
	const CSSM_DATA					&issuers)
{
	CSSM_DATA *serialNumber=NULL;
	CSSM_RETURN crtn = subject.fetchField(&CSSMOID_X509V1SerialNumber, &serialNumber);
	if(crtn || !serialNumber) {
		return CSSMERR_TP_INTERNAL_ERROR;
	}
	crtn = ocspdCRLStatus(*serialNumber, issuers, subject.issuerName(), NULL);
	subject.freeField(&CSSMOID_X509V1SerialNumber, serialNumber);
	return crtn;
}
/*
 * Perform CRL verification on a cert group.
 * The cert group has already passed basic issuer/subject and signature
 * verification. The status of the incoming CRLs is completely unknown. 
 * 
 * FIXME - No mechanism to get CRLs from net with non-NULL verifyTime.
 * How are we supposed to get the CRL which was valid at a specified 
 * time in the past?
 */
CSSM_RETURN tpVerifyCertGroupWithCrls(
	TPVerifyContext					&vfyCtx,
	TPCertGroup 					&certGroup)		// to be verified 
{
	CSSM_RETURN 	crtn;
	CSSM_RETURN		ourRtn = CSSM_OK;

	assert(vfyCtx.clHand != 0);
	assert(vfyCtx.policy == kRevokeCrlBasic);
	tpCrlDebug("tpVerifyCertGroupWithCrls numCerts %u", certGroup.numCerts());
	CSSM_DATA issuers = { 0, NULL };
	CSSM_APPLE_TP_CRL_OPT_FLAGS optFlags = 0;
	if(vfyCtx.crlOpts != NULL) {
		optFlags = vfyCtx.crlOpts->CrlFlags;
	}
	
	/* found & verified CRLs we need to release */
	TPCrlGroup foundCrls(vfyCtx.alloc, TGO_Caller);
	
	try {
		
		unsigned certDex;
		TPCrlInfo *crl = NULL;
		
		/* get issuers as PEM-encoded data blob; we need to release */
		certGroup.encodeIssuers(issuers);

		/* main loop, verify each cert */
		for(certDex=0; certDex<certGroup.numCerts(); certDex++) {
			TPCertInfo *cert = certGroup.certAtIndex(certDex);

			tpCrlDebug("...verifying %s cert %u", 
				cert->isAnchor() ? "anchor " : "", cert->index());
			if(cert->isSelfSigned() || cert->trustSettingsFound()) {
				/* CRL meaningless for a root or trusted cert */
				continue;
			}
			if(cert->revokeCheckComplete()) {
				/* Another revocation policy claimed that this cert is good to go */
				tpCrlDebug("   ...cert at index %u revokeCheckComplete; skipping", 
					cert->index());
				continue;
			}
			crl = NULL;
			do {
				/* first, see if we have CRL status available for this cert */
				crtn = tpGetCrlStatusForCert(*cert, issuers);
				tpCrlDebug("...tpGetCrlStatusForCert: %u", crtn);
				if(crtn == CSSM_OK) {
					tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u verified by local .crl\n",
								cert->index());
					cert->revokeCheckGood(true);
					if(optFlags & CSSM_TP_ACTION_CRL_SUFFICIENT) {
						/* no more revocation checking necessary for this cert */
						cert->revokeCheckComplete(true);
					}
					break;
				}
				if(crtn == CSSMERR_TP_CERT_REVOKED) {
					tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u revoked in local .crl\n",
								cert->index());
					cert->addStatusCode(crtn);
					break;
				}
				if(crtn == CSSMERR_APPLETP_NETWORK_FAILURE) {
					/* crl is being fetched from net, but we don't have it yet */
					if((optFlags & CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT) &&
								tpCertHasCrlDistPt(*cert)) {
						/* crl is required; we don't have it yet, so we fail */
						tpCrlDebug("   ...cert %u: REQUIRE_CRL_IF_PRESENT abort",
								cert->index());
						break;
					}
					/* "Best Attempt" case, so give the cert a pass for now */
					tpCrlDebug("   ...cert %u: no CRL; tolerating", cert->index());
					crtn = CSSM_OK;
					break;
				}
				/* all other CRL status results: try to fetch the CRL */

				/* find a CRL for this cert by hook or crook */
				crtn = tpFindCrlForCert(*cert, crl, vfyCtx);
				if(crtn) {
					/* tpFindCrlForCert may have simply caused ocspd to start
					 * downloading a CRL asynchronously; depending on the speed
					 * of the network and the CRL size, this may return 0 bytes
					 * of data with a CSSMERR_APPLETP_NETWORK_FAILURE result.
					 * We won't know the actual revocation result until the
					 * next time we call tpGetCrlStatusForCert after the full
					 * CRL has been downloaded successfully.
					 */
					if(optFlags & CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT) {
						tpCrlDebug("   ...cert %u: REQUIRE_CRL_PER_CERT abort",
								cert->index());
						break;
					}
					if((optFlags & CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT) && 
								tpCertHasCrlDistPt(*cert)) {
						tpCrlDebug("   ...cert %u: REQUIRE_CRL_IF_PRESENT abort",
								cert->index());
						break;
					}
					/* 
					 * This is the only place where "Best Attempt" tolerates an error
					 */
					tpCrlDebug("   ...cert %u: no CRL; tolerating", cert->index());
					crtn = CSSM_OK;
					assert(crl == NULL);
					break;
				}
				
				/* Keep track; we'll release all when done. */
				assert(crl != NULL);
				foundCrls.appendCrl(*crl);
				
				/* revoked? */
				crtn = crl->isCertRevoked(*cert, vfyCtx.verifyTime);
				if(crtn) {
					break;
				}
				tpCrlDebug("   ...cert %u VERIFIED by CRL", cert->index());
				cert->revokeCheckGood(true);
				if(optFlags & CSSM_TP_ACTION_CRL_SUFFICIENT) {
					/* no more revocation checking necessary for this cert */
					cert->revokeCheckComplete(true);
				}
			} while(0);
			
			/* done processing one cert */
			if(crtn) {
				tpCrlDebug("   ...cert at index %u FAILED crl vfy", 
					cert->index());
				if(ourRtn == CSSM_OK) {
					ourRtn = crtn;
				}
				/* continue on to next cert */
			}	/* error on one cert */
		}		/* for each cert */
	}
	catch(const CssmError &cerr) {
		if(ourRtn == CSSM_OK) {
			ourRtn = cerr.error;
		}
	}
	/* other exceptions fatal */

	/* release all found CRLs */
	for(unsigned dex=0; dex<foundCrls.numCrls(); dex++) {
		TPCrlInfo *crl = foundCrls.crlAtIndex(dex);
		assert(crl != NULL);
		tpDisposeCrl(*crl, vfyCtx);
	}
	/* release issuers */
	if(issuers.Data) {
		free(issuers.Data);
	}
	return ourRtn;
}
/*
 * Find CRL for specified cert. Only returns a fully verified CRL. 
 * Cert-specific errors such as CSSMERR_APPLETP_CRL_NOT_FOUND will be added
 * to cert's return codes. 
 */
static CSSM_RETURN tpFindCrlForCert(
	TPCertInfo						&subject,
	TPCrlInfo						*&foundCrl,		// RETURNED
	TPVerifyContext					&vfyCtx)
{
	
	tpCrlDebug("tpFindCrlForCert top");
	TPCrlInfo *crl = NULL;
	foundCrl = NULL;
	CSSM_APPLE_TP_CRL_OPT_FLAGS crlOptFlags = 0;
	
	if(vfyCtx.crlOpts) {
		crlOptFlags = vfyCtx.crlOpts->CrlFlags;
	}
	
	/* Search inputCrls for a CRL for subject cert */
	if(vfyCtx.inputCrls != NULL) {
		crl = vfyCtx.inputCrls->findCrlForCert(subject);
		if(crl && (crl->verifyWithContextNow(vfyCtx, &subject) == CSSM_OK)) {
			foundCrl = crl;
			crl->mFromWhere = CFW_InGroup;
			tpCrlDebug("   ...CRL found in CrlGroup");
			return CSSM_OK;
		}
	}

	/* local process-wide cache */
	crl = tpGlobalCrlCache().search(subject, vfyCtx);
	if(crl) {
		tpCrlDebug("...tpFindCrlForCert found CRL in cache, calling verifyWithContext");
		if(crl->verifyWithContextNow(vfyCtx, &subject) == CSSM_OK) {
			foundCrl = crl;
			crl->mFromWhere = CFW_LocalCache;
			tpCrlDebug("   ...CRL found in local cache");
			return CSSM_OK;
		}
		else {
			tpGlobalCrlCache().release(*crl);
		}
	}
	
	/* 
	 * Try DL/DB.
	 * Note tpDbFindIssuerCrl() returns a verified CRL.
	 */
	crl = tpDbFindIssuerCrl(vfyCtx, *subject.issuerName(), subject);
	if(crl) {
		foundCrl = crl;
		crl->mFromWhere = CFW_DlDb;
		tpCrlDebug("   ...CRL found in DlDb");
		return CSSM_OK;
	}
	
	/* Last resort: try net if enabled */
	CSSM_RETURN crtn = CSSMERR_APPLETP_CRL_NOT_FOUND;
	crl = NULL;
	if(crlOptFlags & CSSM_TP_ACTION_FETCH_CRL_FROM_NET) {
		crtn = tpFetchCrlFromNet(subject, vfyCtx, crl);
	}
	
	if(crtn) {
		tpCrlDebug("   ...tpFindCrlForCert: CRL not found");
		if(subject.addStatusCode(crtn)) {
			return crtn;
		}
		else {
			return CSSM_OK;
		}
	}
	
	/* got one from net - add to global cache */
	assert(crl != NULL);
	tpGlobalCrlCache().add(*crl);
	crl->mFromWhere = CFW_Net;
	tpCrlDebug("   ...CRL found from net");
	
	foundCrl = crl;
	return CSSM_OK;
}
예제 #6
0
/* 
 * Returns true if we are to allow/trust the specified
 * cert as a PKINIT-only anchor.
 */
static bool tpCheckPkinitServerCert(
	TPCertGroup &certGroup)
{
	/* 
	 * Basic requirement: exactly one cert, self-signed.
	 * The numCerts == 1 requirement might change...
	 */
	unsigned numCerts = certGroup.numCerts();
	if(numCerts != 1) {
		tpDebug("tpCheckPkinitServerCert: too many certs");
		return false;
	}
	/* end of chain... */
	TPCertInfo *theCert = certGroup.certAtIndex(numCerts - 1);
	if(!theCert->isSelfSigned()) {
		tpDebug("tpCheckPkinitServerCert: 1 cert, not self-signed");
		return false;
	}
	const CSSM_DATA *subjectName = theCert->subjectName();
	
	/* 
	 * Open the magic keychain.
	 * We're going up and over the Sec layer here, not generally 
	 * kosher, but this is a temp hack.
	 */
	OSStatus ortn;
	SecKeychainRef kcRef = NULL;
	string fullPathName;
	const char *homeDir = getenv("HOME");
	if (homeDir == NULL)
	{
		// If $HOME is unset get the current user's home directory
		// from the passwd file.
		uid_t uid = geteuid();
		if (!uid) uid = getuid();
		struct passwd *pw = getpwuid(uid);
		if (!pw) {
			return false;
		}
		homeDir = pw->pw_dir;
	}
	fullPathName = homeDir;
	fullPathName += "/Library/Application Support/PKINIT/TrustedServers.keychain";
	ortn = SecKeychainOpen(fullPathName.c_str(), &kcRef);
	if(ortn) {
		tpDebug("tpCheckPkinitServerCert: keychain not found (1)");
		return false;
	}
	/* subsequent errors to errOut: */
	
	bool ourRtn = false;
	SecKeychainStatus kcStatus;
	CSSM_DATA_PTR subjSerial = NULL;
	CSSM_RETURN crtn;
	SecKeychainSearchRef		srchRef = NULL;
	SecKeychainAttributeList	attrList;
	SecKeychainAttribute		attrs[2];
	SecKeychainItemRef			foundItem = NULL;
	
	ortn = SecKeychainGetStatus(kcRef, &kcStatus);
	if(ortn) {
		tpDebug("tpCheckPkinitServerCert: keychain not found (2)");
		goto errOut;
	}
	
	/*
	 * We already have this cert's normalized name; get its
	 * serial number.
	 */
	crtn = theCert->fetchField(&CSSMOID_X509V1SerialNumber, &subjSerial);
	if(crtn) {
		/* should never happen */
		tpDebug("tpCheckPkinitServerCert: error fetching serial number");
		goto errOut;
	}
	
	attrs[0].tag    = kSecSubjectItemAttr;
	attrs[0].length = subjectName->Length;
	attrs[0].data   = subjectName->Data;
	attrs[1].tag    = kSecSerialNumberItemAttr;
	attrs[1].length = subjSerial->Length;
	attrs[1].data   = subjSerial->Data;
	attrList.count  = 2;
	attrList.attr   = attrs;
	
	ortn = SecKeychainSearchCreateFromAttributes(kcRef,
		kSecCertificateItemClass,
		&attrList,
		&srchRef);
	if(ortn) {
		tpDebug("tpCheckPkinitServerCert: search failure");
		goto errOut;
	}
	for(;;) {
		ortn = SecKeychainSearchCopyNext(srchRef, &foundItem);
		if(ortn) {
			tpDebug("tpCheckPkinitServerCert: end search");
			break;
		}
		
		/* found a matching cert; do byte-for-byte compare */
		CSSM_DATA certData;
		ortn = SecCertificateGetData((SecCertificateRef)foundItem, &certData);
		if(ortn) {
			tpDebug("tpCheckPkinitServerCert: SecCertificateGetData failure");
			continue;
		}
		if(tpCompareCssmData(&certData, theCert->itemData())){
			tpDebug("tpCheckPkinitServerCert: FOUND CERT");
			ourRtn = true;
			break;
		}
		tpDebug("tpCheckPkinitServerCert: skipping matching cert");
		CFRelease(foundItem);
		foundItem = NULL;
	}
errOut:
	CFRELEASE(kcRef);
	CFRELEASE(srchRef);
	CFRELEASE(foundItem);
	if(subjSerial != NULL) {
		theCert->freeField(&CSSMOID_X509V1SerialNumber, subjSerial);
	}
	return ourRtn;
}
예제 #7
0
/* 
 * Private version of CertGroupConstruct, used by CertGroupConstruct and
 * CertGroupVerify. Populates a TP-style TPCertGroup for further processing.
 * This only throws CSSM-style exceptions in the following cases: 
 * 
 *  -- input parameter errors
 *  -- the first (leaf) cert is bad (doesn't parse, expired, not valid yet).
 *  -- root found but it doesn't self-verify 
 *
 *  All other cert-related errors simply result in the bad cert being ignored.
 *  Other exceptions are gross system errors like malloc failure.
 */
void AppleTPSession::CertGroupConstructPriv(CSSM_CL_HANDLE clHand,
		CSSM_CSP_HANDLE 		cspHand,
		TPCertGroup 			&inCertGroup,
		const CSSM_DL_DB_LIST 	*DBList,			// optional here
		const char 				*cssmTimeStr,		// optional
		
		/* trusted anchors, optional */
		/* FIXME - maybe this should be a TPCertGroup */
		uint32 					numAnchorCerts,
		const CSSM_DATA			*anchorCerts,
		
		/* CSSM_TP_ACTION_FETCH_CERT_FROM_NET, CSSM_TP_ACTION_TRUST_SETTINGS */
		CSSM_APPLE_TP_ACTION_FLAGS	actionFlags,

		/* optional user trust parameters */
		const CSSM_OID			*policyOid,
		const char				*policyStr,
		uint32					policyStrLen,
		SecTrustSettingsKeyUsage	keyUse,
		
		/* 
		 * Certs to be freed by caller (i.e., TPCertInfo which we allocate
		 * as a result of using a cert from anchorCerts or dbList) are added
		 * to this group.
		 */
		TPCertGroup				&certsToBeFreed,

		/* returned */
		CSSM_BOOL				&verifiedToRoot,		// end of chain self-verifies
		CSSM_BOOL				&verifiedToAnchor,		// end of chain in anchors
		CSSM_BOOL				&verifiedViaTrustSetting,	// chain ends per User Trust setting
		TPCertGroup 			&outCertGroup)			// RETURNED
{
	TPCertInfo			*subjectCert;				// the one we're working on
	CSSM_RETURN			outErr = CSSM_OK;
	
	/* this'll be the first subject cert in the main loop */
	subjectCert = inCertGroup.certAtIndex(0);

	/* Append leaf cert to outCertGroup */
	outCertGroup.appendCert(subjectCert);
	subjectCert->isLeaf(true);
	subjectCert->isFromInputCerts(true);
	outCertGroup.setAllUnused();
	subjectCert->used(true);
	
	outErr = outCertGroup.buildCertGroup(
		*subjectCert,	
		&inCertGroup,
		DBList,
		clHand,
		cspHand,
		cssmTimeStr,	
		numAnchorCerts,
		anchorCerts,
		certsToBeFreed,
		&certsToBeFreed,	// gatheredCerts to accumulate net/DB fetches
		CSSM_TRUE,			// subjectIsInGroup - enables root check on
							//    subject cert
		actionFlags,
		policyOid,
		policyStr,
		policyStrLen,
		keyUse,
		
		verifiedToRoot,	
		verifiedToAnchor,
		verifiedViaTrustSetting);
	if(outErr) {
		CssmError::throwMe(outErr);
	}
}
예제 #8
0
/*
 * Fetch issuer cert of specified cert if the cert has an issuerAltName
 * with a URI. If non-NULL cert is returned, it has passed subject/issuer
 * name comparison and signature verification with target cert.
 *
 * Return values:
 *   CSSM_OK - found and returned issuer cert 
 *   CSSMERR_TP_CERTGROUP_INCOMPLETE - no URL in issuerAltName
 *   CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE - found and returned issuer
 *      cert, but signature verification needs subsequent retry.
 *   Anything else - gross error, typically from last LDAP/HTTP attempt
 */
CSSM_RETURN tpFetchIssuerFromNet(
	TPCertInfo			&subject,
	CSSM_CL_HANDLE		clHand,
	CSSM_CSP_HANDLE		cspHand,
	const char			*verifyTime,
	TPCertInfo			*&issuer)		// RETURNED
{
	CSSM_OID_PTR fieldOid = NULL;
	CSSM_DATA_PTR fieldValue = NULL;	// mallocd by CL
	CSSM_RETURN crtn;
	bool hasAIA = false;

	/* look for the Authority Info Access extension first */
	fieldOid = (CSSM_OID_PTR)&CSSMOID_AuthorityInfoAccess;
	crtn = subject.fetchField(fieldOid,
		&fieldValue);
	hasAIA = (crtn == CSSM_OK);
	if (!hasAIA) {
		/* fall back to Issuer Alternative Name extension */
		fieldOid = (CSSM_OID_PTR)&CSSMOID_IssuerAltName;
		crtn = subject.fetchField(fieldOid,
								  &fieldValue);
	}
	switch(crtn) {
		case CSSM_OK:
			break;
		case CSSMERR_CL_NO_FIELD_VALUES:
			/* field not present */
			return CSSMERR_TP_CERTGROUP_INCOMPLETE;
		default:
			/* gross error */
			return crtn;
	}
	if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
		tpPolicyError("tpFetchIssuerFromNet: malformed CSSM_FIELD");
		return CSSMERR_TP_UNKNOWN_FORMAT;
	}
	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
	CE_GeneralNames *names = (CE_GeneralNames *)cssmExt->value.parsedValue;
	TPCertInfo *rtnCert = NULL;
	if (hasAIA) {	/* authority info access */
		CE_AuthorityInfoAccess *access = (CE_AuthorityInfoAccess *)cssmExt->value.parsedValue;
		for (uint32 index = 0; access && index < access->numAccessDescriptions; index++) {
			CE_AccessDescription *accessDesc = &access->accessDescriptions[index];
			CSSM_OID_PTR methodOid = (CSSM_OID_PTR)&accessDesc->accessMethod;
			/* look for the CA Issuers method */
			if(methodOid->Data != NULL && methodOid->Length == CSSMOID_CA_ISSUERS.Length &&
			   !memcmp(methodOid->Data, CSSMOID_CA_ISSUERS.Data, methodOid->Length)) {
				CE_GeneralNames aiaNames = { 1, &accessDesc->accessLocation };
				/* attempt to fetch cert from named location */
				crtn = tpFetchViaGeneralNames(&aiaNames,
											  subject,
											  NULL,		// issuer - not used
											  NULL,		// verifyContext
											  clHand,
											  cspHand,
											  verifyTime,
											  &rtnCert,
											  NULL);
				if (crtn == CSSM_OK ||
					crtn == CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE) {
					break; // got one
				}
			}
		}
		subject.freeField(fieldOid,	fieldValue);
	}
	else {  /* issuer alt name */
		crtn = tpFetchViaGeneralNames(names,
						subject,
						NULL,		// issuer - not used
						NULL,		// verifyContext
						clHand,
						cspHand,
						verifyTime,
						&rtnCert,
						NULL);
		subject.freeField(fieldOid,	fieldValue);
	}
	switch(crtn) {
		case CSSM_OK:
		case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
			issuer = rtnCert;
			break;
		default:
			break;
	}
	return crtn;
}
예제 #9
0
/*
 * Fetch CRL(s) from specified cert if the cert has a cRlDistributionPoint
 * extension.
 *
 * Return values:
 *   CSSM_OK - found and returned fully verified CRL 
 *   CSSMERR_APPLETP_CRL_NOT_FOUND - no CRL in cRlDistributionPoint
 *   Anything else - gross error, typically from last LDAP/HTTP attempt
 *
 * FIXME - this whole mechanism sort of falls apart if verifyContext.verifyTime
 * is non-NULL. How are we supposed to get the CRL which was valid at 
 * a specified time in the past?
 */
CSSM_RETURN tpFetchCrlFromNet(
	TPCertInfo 			&cert,
	TPVerifyContext		&vfyCtx,
	TPCrlInfo			*&crl)			// RETURNED
{
	/* does the cert have a cRlDistributionPoint? */
	CSSM_DATA_PTR fieldValue;			// mallocd by CL
	
	CSSM_RETURN crtn = cert.fetchField(&CSSMOID_CrlDistributionPoints,
		&fieldValue);
	switch(crtn) {
		case CSSM_OK:
			break;
		case CSSMERR_CL_NO_FIELD_VALUES:
			/* field not present */
			return CSSMERR_APPLETP_CRL_NOT_FOUND;
		default:
			/* gross error */
			return crtn;
	}
	if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
		tpErrorLog("tpFetchCrlFromNet: malformed CSSM_FIELD");
		return CSSMERR_TP_UNKNOWN_FORMAT;
	}
	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
	CE_CRLDistPointsSyntax *dps = 
		(CE_CRLDistPointsSyntax *)cssmExt->value.parsedValue;
	TPCrlInfo *rtnCrl = NULL;

	/* default return if we don't find anything */
	crtn = CSSMERR_APPLETP_CRL_NOT_FOUND;
	for(unsigned dex=0; dex<dps->numDistPoints; dex++) {
		CE_CRLDistributionPoint *dp = &dps->distPoints[dex];
		if(dp->distPointName == NULL) {
			continue;
		}
		/*
		 * FIXME if this uses an indirect CRL, we need to follow the 
		 * crlIssuer field... TBD.
		 */
		switch(dp->distPointName->nameType) {
			case CE_CDNT_NameRelativeToCrlIssuer:
				/* not yet */
				tpErrorLog("tpFetchCrlFromNet: "
					"CE_CDNT_NameRelativeToCrlIssuer not implemented\n");
				break;
				
			case CE_CDNT_FullName:
			{
				/*
				 * Since we don't support indirect CRLs (yet), we always pass
				 * the cert-to-be-verified's issuer as the CRL issuer for 
				 * cache lookup.
				 */
				CE_GeneralNames *names = dp->distPointName->dpn.fullName;
				crtn = tpFetchViaGeneralNames(names,
					cert,
					cert.issuerName(),
					&vfyCtx,
					0,			// clHand, use the one in vfyCtx
					0,			// cspHand, ditto
					vfyCtx.verifyTime,	
					NULL,		
					&rtnCrl);
				break;
			}	/* CE_CDNT_FullName */
			
			default:
				/* not yet */
				tpErrorLog("tpFetchCrlFromNet: "
					"unknown distPointName->nameType (%u)\n",
						(unsigned)dp->distPointName->nameType);
				break;
		}	/* switch distPointName->nameType */
		if(crtn == CSSM_OK) {
			/* i.e., tpFetchViaGeneralNames SUCCEEDED */
			break;
		}
	}	/* for each distPoints */

	cert.freeField(&CSSMOID_CrlDistributionPoints,	fieldValue);
	if(crtn == CSSM_OK) {
		assert(rtnCrl != NULL);
		crl = rtnCrl;
	}
	return crtn;
}
/*
 * Search a list of DBs for a cert which verifies specified subject item.
 * Just a boolean return - we found it, or not. If we did, we return
 * TPCertInfo associated with the raw cert.
 * A true partialIssuerKey on return indicates that caller must deal
 * with partial public key processing later.
 * If verifyCurrent is true, we will not return a cert which is not
 * temporally valid; else we may well do so.
 */
TPCertInfo *tpDbFindIssuerCert(
	Allocator				&alloc,
	CSSM_CL_HANDLE			clHand,
	CSSM_CSP_HANDLE			cspHand,
	const TPClItemInfo		*subjectItem,
	const CSSM_DL_DB_LIST	*dbList,
	const char 				*verifyTime,		// may be NULL
	bool					&partialIssuerKey)	// RETURNED
{
	StLock<Mutex> _(SecTrustKeychainsGetMutex());

	uint32						dbDex;
	CSSM_HANDLE					resultHand;
	CSSM_DATA					cert;
	CSSM_DL_DB_HANDLE			dlDb;
	CSSM_DB_UNIQUE_RECORD_PTR	record;
	TPCertInfo 					*issuerCert = NULL;
	bool 						foundIt;
	TPCertInfo					*expiredIssuer = NULL;
	TPCertInfo					*nonRootIssuer = NULL;

	partialIssuerKey = false;
	if(dbList == NULL) {
		return NULL;
	}
	for(dbDex=0; dbDex<dbList->NumHandles; dbDex++) {
		dlDb = dbList->DLDBHandle[dbDex];
		cert.Data = NULL;
		cert.Length = 0;
		resultHand = 0;
		record = tpCertLookup(dlDb,
			subjectItem->issuerName(),
			&resultHand,
			&cert);
		/* remember we have to:
		 * -- abort this query regardless, and
		 * -- free the CSSM_DATA cert regardless, and
		 * -- free the unique record if we don't use it
		 *    (by placing it in issuerCert)...
		 */
		if(record != NULL) {
			/* Found one */
			assert(cert.Data != NULL);
			tpDbDebug("tpDbFindIssuerCert: found cert record (1) %p", record);
			issuerCert = NULL;
			CSSM_RETURN crtn = CSSM_OK;
			try {
				issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData, verifyTime);
			}
			catch(...) {
				crtn = CSSMERR_TP_INVALID_CERTIFICATE;
			}

			/* we're done with raw cert data */
			tpFreePluginMemory(dlDb.DLHandle, cert.Data);
			cert.Data = NULL;
			cert.Length = 0;

			/* Does it verify the subject cert? */
			if(crtn == CSSM_OK) {
				crtn = subjectItem->verifyWithIssuer(issuerCert);
			}

			/*
			 * Handle temporal invalidity - if so and this is the first one
			 * we've seen, hold on to it while we search for better one.
			 */
			if((crtn == CSSM_OK) && (expiredIssuer == NULL)) {
				if(issuerCert->isExpired() || issuerCert->isNotValidYet()) {
					/*
					 * Exact value not important here, this just uniquely identifies
					 * this situation in the switch below.
					 */
					tpDbDebug("tpDbFindIssuerCert: holding expired cert (1)");
					crtn = CSSM_CERT_STATUS_EXPIRED;
					expiredIssuer = issuerCert;
					expiredIssuer->dlDbHandle(dlDb);
					expiredIssuer->uniqueRecord(record);
				}
			}
			/*
			 * Prefer a root over an intermediate issuer if we can get one
			 * (in case a cross-signed intermediate and root are both available)
			 */
			if((crtn == CSSM_OK) && (nonRootIssuer == NULL)) {
				if(!issuerCert->isSelfSigned()) {
					/*
					 * Exact value not important here, this just uniquely identifies
					 * this situation in the switch below.
					 */
					tpDbDebug("tpDbFindIssuerCert: holding non-root cert (1)");
					crtn = CSSM_CERT_STATUS_IS_ROOT;
					nonRootIssuer = issuerCert;
					nonRootIssuer->dlDbHandle(dlDb);
					nonRootIssuer->uniqueRecord(record);
				}
			}
			switch(crtn) {
				case CSSM_OK:
					break;
				case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
					partialIssuerKey = true;
					break;
				default:
					if(issuerCert != NULL) {
						/* either holding onto this cert, or done with it. */
						if(crtn != CSSM_CERT_STATUS_EXPIRED &&
						   crtn != CSSM_CERT_STATUS_IS_ROOT) {
							delete issuerCert;
							CSSM_DL_FreeUniqueRecord(dlDb, record);
						}
						issuerCert = NULL;
					}

					/*
					 * Continue searching this DB. Break on finding the holy
					 * grail or no more records found.
					 */
					for(;;) {
						cert.Data = NULL;
						cert.Length = 0;
						record = NULL;
						CSSM_RETURN crtn = CSSM_DL_DataGetNext(dlDb,
							resultHand,
							NULL,		// no attrs
							&cert,
							&record);
						if(crtn) {
							/* no more, done with this DB */
							assert(cert.Data == NULL);
							break;
						}
						assert(cert.Data != NULL);
						tpDbDebug("tpDbFindIssuerCert: found cert record (2) %p", record);

						/* found one - does it verify subject? */
						try {
							issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData,
									verifyTime);
						}
						catch(...) {
							crtn = CSSMERR_TP_INVALID_CERTIFICATE;
						}
						/* we're done with raw cert data */
						tpFreePluginMemory(dlDb.DLHandle, cert.Data);
						cert.Data = NULL;
						cert.Length = 0;

						if(crtn == CSSM_OK) {
							crtn = subjectItem->verifyWithIssuer(issuerCert);
						}

						/* temporal validity check, again */
						if((crtn == CSSM_OK) && (expiredIssuer == NULL)) {
							if(issuerCert->isExpired() || issuerCert->isNotValidYet()) {
								tpDbDebug("tpDbFindIssuerCert: holding expired cert (2)");
								crtn = CSSM_CERT_STATUS_EXPIRED;
								expiredIssuer = issuerCert;
								expiredIssuer->dlDbHandle(dlDb);
								expiredIssuer->uniqueRecord(record);
							}
						}
						/* self-signed check, again */
						if((crtn == CSSM_OK) && (nonRootIssuer == NULL)) {
							if(!issuerCert->isSelfSigned()) {
								tpDbDebug("tpDbFindIssuerCert: holding non-root cert (2)");
								crtn = CSSM_CERT_STATUS_IS_ROOT;
								nonRootIssuer = issuerCert;
								nonRootIssuer->dlDbHandle(dlDb);
								nonRootIssuer->uniqueRecord(record);
							}
						}

						foundIt = false;
						switch(crtn) {
							case CSSM_OK:
								foundIt = true;
								break;
							case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
								partialIssuerKey = true;
								foundIt = true;
								break;
							default:
								break;
						}
						if(foundIt) {
							/* yes! */
							break;
						}
						if(issuerCert != NULL) {
							/* either holding onto this cert, or done with it. */
							if(crtn != CSSM_CERT_STATUS_EXPIRED &&
							   crtn != CSSM_CERT_STATUS_IS_ROOT) {
								delete issuerCert;
								CSSM_DL_FreeUniqueRecord(dlDb, record);
							}
							issuerCert = NULL;
						}
					} /* searching subsequent records */
			}	/* switch verify */

			if(record != NULL) {
				/* NULL record --> end of search --> DB auto-aborted */
				crtn = CSSM_DL_DataAbortQuery(dlDb, resultHand);
				assert(crtn == CSSM_OK);
			}
			if(issuerCert != NULL) {
				/* successful return */
				tpDbDebug("tpDbFindIssuer: returning record %p", record);
				issuerCert->dlDbHandle(dlDb);
				issuerCert->uniqueRecord(record);
				if(expiredIssuer != NULL) {
					/* We found a replacement */
					tpDbDebug("tpDbFindIssuer: discarding expired cert");
					expiredIssuer->freeUniqueRecord();
					delete expiredIssuer;
				}
				/* Avoid deleting the non-root cert if same as expired cert */
				if(nonRootIssuer != NULL && nonRootIssuer != expiredIssuer) {
					/* We found a replacement */
					tpDbDebug("tpDbFindIssuer: discarding non-root cert");
					nonRootIssuer->freeUniqueRecord();
					delete nonRootIssuer;
				}
				return issuerCert;
			}
		}	/* tpCertLookup, i.e., CSSM_DL_DataGetFirst, succeeded */
		else {
			assert(cert.Data == NULL);
			assert(resultHand == 0);
		}
	}	/* main loop searching dbList */

	if(nonRootIssuer != NULL) {
		/* didn't find root issuer, so use this one */
		tpDbDebug("tpDbFindIssuer: taking non-root issuer cert, record %p",
			nonRootIssuer->uniqueRecord());
		if(expiredIssuer != NULL && expiredIssuer != nonRootIssuer) {
			expiredIssuer->freeUniqueRecord();
			delete expiredIssuer;
		}
		return nonRootIssuer;
	}

	if(expiredIssuer != NULL) {
		/* OK, we'll take this one */
		tpDbDebug("tpDbFindIssuer: taking expired cert after all, record %p",
			expiredIssuer->uniqueRecord());
		return expiredIssuer;
	}
	/* issuer not found */
	return NULL;
}