Exemplo n.º 1
0
OCSPExtensions::OCSPExtensions(
	NSS_CertExtension **nssExts)
		: mCoder(NULL), mNumExtensions(0), mExtensions(NULL)
{
	SecAsn1CoderCreate(&mCoder);
	mNumExtensions = ocspdArraySize((const void **)nssExts);
	if(mNumExtensions == 0) {
		return;
	}
	
	mExtensions = (OCSPExtension **)SecAsn1Malloc(mCoder, 
		(mNumExtensions * sizeof(OCSPExtension *)));
	for(unsigned dex=0; dex<mNumExtensions; dex++) {
		try {
			mExtensions[dex] = 
				OCSPExtension::createFromNSS(mCoder, *nssExts[dex]);
			if(mExtensions[dex] == NULL) {
				ocspdErrorLog("OCSPExtensions: extension failure (NULL) dex %u\n", dex);
				CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
			}
			if(mExtensions[dex]->unrecognizedCritical()) {
				ocspdErrorLog("OCSPExtensions: unrecognized critical extension\n");
				CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
			}
		}
		catch (...) {
			ocspdErrorLog("OCSPExtensions: extension failure dex %u\n", dex);
			CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
		}
	}
}
Exemplo n.º 2
0
/* 
 * Calculate temporal validity; set mLatestNextUpdate and mExpireTime. Only
 * called from constructor. Returns true if valid, else returns false. 
 */
bool OCSPResponse::calculateValidity(CFTimeInterval defaultTTL)
{
	mLatestNextUpdate = NULL_TIME;
	CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
	
	unsigned numResponses = ocspdArraySize((const void **)mResponseData.responses);
	for(unsigned dex=0; dex<numResponses; dex++) {
		SecAsn1OCSPSingleResponse *resp = mResponseData.responses[dex];
		
		/* 
		 * First off, a thisUpdate later than 'now' invalidates the whole response. 
		 */
		CFAbsoluteTime thisUpdate = genTimeToCFAbsTime(&resp->thisUpdate);
		if(thisUpdate > now) {
			ocspdErrorLog("OCSPResponse::calculateValidity: thisUpdate not passed\n");
			return false;
		}
		
		/* 
		 * Accumulate latest nextUpdate
		 */
		if(resp->nextUpdate != NULL) {
			CFAbsoluteTime nextUpdate = genTimeToCFAbsTime(resp->nextUpdate);
			if(nextUpdate > mLatestNextUpdate) {
				mLatestNextUpdate = nextUpdate;
			}
		}
	}
	
	CFAbsoluteTime defaultExpire = now + defaultTTL;
	if(mLatestNextUpdate == NULL_TIME) {
		/* absolute expire time = current time plus default TTL */
		mExpireTime = defaultExpire;
	}
	else if(defaultExpire < mLatestNextUpdate) {
		/* caller more stringent than response */
		mExpireTime = defaultExpire;
	}
	else {
		/* response more stringent than caller */
		if(mLatestNextUpdate < now) {
			ocspdErrorLog("OCSPResponse::calculateValidity: now > mLatestNextUpdate\n");
			return false;
		}
		mExpireTime = mLatestNextUpdate;
	}
	return true;
}
Exemplo n.º 3
0
/*
 * Given time strings representing 'update time' and 'stale time', 
 * calculate mIsExpired and mIsStale. 
 */
void CrlInfo::validateTimes(
	const char *updateTime,		// now - expireOverlap
	const char *staleTime,		// now - staleTime
	unsigned dex)				// for debug info
{
	CSSM_DATA *nextUpdateData = fetchValidAttr(ATTR_DEX_NEXT_UPDATE);
	if((nextUpdateData == NULL) || 
		(nextUpdateData->Length != CSSM_TIME_STRLEN)) {
		ocspdErrorLog("CrlInfo::validateTimes: Badly formed NextUpdate attr on "
			"CRL %u", dex);
		mIsBadlyFormed = true;
		return;
	}
	printString("NextUpdate ", nextUpdateData); 
	char *nextUpdate = (char *)nextUpdateData->Data;
	if(compareTimes(nextUpdate, updateTime) < 0) {
		ocspdCrlDebug("...CRL %u is expired", dex);
		mIsExpired = true;
		if(compareTimes(nextUpdate, staleTime) < 0) {
			ocspdCrlDebug("...CRL %u is stale", dex);
			mIsStale = true;
		}
		/* note it can't be stale and not expired */
	}
}
Exemplo n.º 4
0
/*
 * Constructor, called by OCSPResponse.
 */
OCSPSingleResponse::OCSPSingleResponse(
	SecAsn1OCSPSingleResponse	*resp)
	  : mCertStatus(CS_NotParsed),
		mThisUpdate(NULL_TIME),
		mNextUpdate(NULL_TIME),
		mRevokedTime(NULL_TIME),
		mCrlReason(CrlReason_NONE),
		mExtensions(NULL)
{
	assert(resp != NULL);
	
	SecAsn1CoderCreate(&mCoder);
	if((resp->certStatus.Data == NULL) || (resp->certStatus.Length == 0)) {
		ocspdErrorLog("OCSPSingleResponse: bad certStatus\n");
		CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
	}
	mCertStatus = (SecAsn1OCSPCertStatusTag)(resp->certStatus.Data[0] & SEC_ASN1_TAGNUM_MASK);
	if(mCertStatus == CS_Revoked) {
		/* decode further to get SecAsn1OCSPRevokedInfo */
		SecAsn1OCSPCertStatus certStatus;
		memset(&certStatus, 0, sizeof(certStatus));
		if(SecAsn1DecodeData(mCoder, &resp->certStatus, 
				kSecAsn1OCSPCertStatusRevokedTemplate, &certStatus)) {
			ocspdErrorLog("OCSPSingleResponse: err decoding certStatus\n");
			CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
		}
		SecAsn1OCSPRevokedInfo *revokedInfo = certStatus.revokedInfo;
		if(revokedInfo != NULL) {
			/* Treat this as optional even for CS_Revoked */
			mRevokedTime = genTimeToCFAbsTime(&revokedInfo->revocationTime);
			const CSSM_DATA *revReason = revokedInfo->revocationReason;
			if((revReason != NULL) &&
			   (revReason->Data != NULL) &&
			   (revReason->Length != 0)) {
			   mCrlReason = revReason->Data[0];
			}
		}
	}
	mThisUpdate = genTimeToCFAbsTime(&resp->thisUpdate);
	if(resp->nextUpdate != NULL) {
		mNextUpdate = genTimeToCFAbsTime(resp->nextUpdate);
	}
	mExtensions = new OCSPExtensions(resp->singleExtensions);
	ocspdDebug("OCSPSingleResponse: status %d reason %d", (int)mCertStatus, 
		(int)mCrlReason);
}
Exemplo n.º 5
0
/* Fill out a CSSM_DATA with the subset of public key bytes from the given
 * CSSM_KEY_PTR which should be hashed to produce the issuerKeyHash field
 * of a CertID in an OCSP request.
 *
 * For RSA keys, this simply copies the input key pointer and length.
 * For EC keys, we need to further deconstruct the SubjectPublicKeyInfo
 * to obtain the key bytes (i.e. curve point) for hashing.
 *
 * Returns CSSM_OK on success, or non-zero error if the bytes could not
 * be retrieved.
 */
CSSM_RETURN ocspdGetPublicKeyBytes(
	SecAsn1CoderRef coder,		// optional
	CSSM_KEY_PTR publicKey,		// input public key
	CSSM_DATA &publicKeyBytes)	// filled in by this function
{
	CSSM_RETURN crtn = CSSM_OK;
	SecAsn1CoderRef _coder = NULL;

	if(publicKey == NULL) {
		crtn = CSSMERR_CSP_INVALID_KEY_POINTER;
		goto exit;
	}

	if(coder == NULL) {
		crtn = SecAsn1CoderCreate(&_coder);
		if(crtn) {
			goto exit;
		}
		coder = _coder;
	}

	publicKeyBytes.Length = publicKey->KeyData.Length;
	publicKeyBytes.Data = publicKey->KeyData.Data;

	if(publicKey->KeyHeader.AlgorithmId == CSSM_ALGID_ECDSA) {
		/*
		 * For an EC key, publicKey->KeyData is a SubjectPublicKeyInfo
		 * ASN.1 sequence that includes the algorithm identifier.
		 * We only want to return the bit string portion of the key here.
		 */
		SecAsn1PubKeyInfo pkinfo;
		memset(&pkinfo, 0, sizeof(pkinfo));
		if(SecAsn1Decode(coder,
			publicKey->KeyData.Data,
			publicKey->KeyData.Length,
			kSecAsn1SubjectPublicKeyInfoTemplate,
			&pkinfo) == 0) {
			if(pkinfo.subjectPublicKey.Length &&
			   pkinfo.subjectPublicKey.Data) {
				publicKeyBytes.Length = pkinfo.subjectPublicKey.Length >> 3;
				publicKeyBytes.Data = pkinfo.subjectPublicKey.Data;
				/*
				 * Important: if we allocated the SecAsn1Coder, the memory
				 * being pointed to by pkinfo.subjectPublicKey.Data will be
				 * deallocated when the coder is released below. We want to
				 * point to the identical data inside the caller's public key,
				 * now that the decoder has identified it for us.
				 */
				if(publicKeyBytes.Length <= publicKey->KeyData.Length) {
					publicKeyBytes.Data = (uint8*)((uintptr_t)publicKey->KeyData.Data +
						(publicKey->KeyData.Length - publicKeyBytes.Length));
					goto exit;
				}
				/* intentional fallthrough to error exit */
			}
			ocspdErrorLog("ocspdGetPublicKeyBytes: invalid SecAsn1PubKeyInfo\n");
			crtn = CSSMERR_CSP_INVALID_KEY_POINTER;
		}
Exemplo n.º 6
0
/*
 * Validate each CRL's integrity (Not including expiration or stale time).
 */
static void validateCrls(
	CrlInfo		**crlInfo, 
	unsigned	numCrls)
{
	CrlInfo *crl;
	
	for(unsigned dex=0; dex<numCrls; dex++) {
		crl = crlInfo[dex];
		
		/* get CrlType, make sure it's acceptable */
		uint32 i;
		
		if(crl->fetchIntAttr(ATTR_DEX_CRL_TYPE, i)) {
			continue;
		}
		switch(i) {
			case CSSM_CRL_TYPE_X_509v1:
			case CSSM_CRL_TYPE_X_509v2:
				/* OK */
				break;
			default:
				ocspdErrorLog("validateCrls: bad CRL type (%u) on CRL %u\n", 
					(unsigned)i, dex);
				crl->mIsBadlyFormed = true;
				continue;
		}
		
		/* ditto for encoding */
		if(crl->fetchIntAttr(ATTR_DEX_CRL_ENC, i)) {
			continue;
		}
		switch(i) {
			case CSSM_CRL_ENCODING_BER:
			case CSSM_CRL_ENCODING_DER:
				/* OK */
				break;
			default:
				ocspdErrorLog("validateCrls: bad CRL encoding (%u) on CRL %u\n", 
					(unsigned)i, dex);
				crl->mIsBadlyFormed = true;
				continue;
		}
		/* any other grounds for deletion? */
	}
}
Exemplo n.º 7
0
const CSSM_DATA *OCSPResponse::signerCert(uint32 dex)
{
	uint32 numCerts = numSignerCerts();
	if(dex >= numCerts) {
		ocspdErrorLog("OCSPResponse::signerCert: numCerts overflow\n");
		CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
	}
	return mBasicResponse.certs[dex];
}
Exemplo n.º 8
0
/*
 * Perform full crypto CRL validation. 
 * We use the system-wide intermediate cert keychain here, but do
 * NOT use the CRL cache we're working on (or any other), since
 * we don't really want to trust anything at this point. 
 */
static void cryptoValidateCrls(
	CrlInfo			**crlInfo, 
	unsigned		numCrls,
	CSSM_TP_HANDLE	tpHand,
	CSSM_CSP_HANDLE	cspHand,
	CSSM_CL_HANDLE	clHand,
	CSSM_DL_HANDLE	dlHand)
{
	CrlInfo 		*crl;
	
	/* the system-wide intermediate certs */
	CSSM_DL_DB_HANDLE certDb;
	CSSM_DL_DB_HANDLE_PTR certDbPtr = NULL;
	CSSM_RETURN crtn = CSSM_DL_DbOpen(dlHand,
		SYSTEM_CERT_STORE_PATH, 
		NULL,			// DbLocation
		CSSM_DB_ACCESS_READ,
		NULL, 			// CSSM_ACCESS_CREDENTIALS *AccessCred
		NULL,			// void *OpenParameters
		&certDb.DBHandle);
	if(crtn) {
		ocspdErrorLog("cryptoValidateCrls: CSSM_DL_DbOpen returned %u", (unsigned)crtn);
		/* Oh well, keep trying */
	}
	else {
		certDb.DLHandle = dlHand;
		certDbPtr = &certDb;
	}
	
	for(unsigned dex=0; dex<numCrls; dex++) {
		crl = crlInfo[dex];
		crtn = cuCrlVerify(tpHand, clHand, cspHand,
			&crl->mCrlBlob,
			certDbPtr,
			NULL,		// anchors - use Trust Settings
			0);			// anchorCount
		switch(crtn) {
			case CSSMERR_APPLETP_CRL_EXPIRED:
				/* special case, we'll handle this via its attrs */
			case CSSM_OK:
				break;
			default:
				ocspdCrlDebug("...CRL %u FAILED crypto verify", dex);
				crl->printName("FAILED crypto verify");
				crl->mIsBadlyFormed = true;
			break;
		}
	}
	CSSM_DL_DbClose(certDb);
}
Exemplo n.º 9
0
/* 
 * Fetch uint32 attr if it's there at specified attr index.
 * Returns non zero if it's not there and flags the CRL as bad.
 */
int CrlInfo::fetchIntAttr(
	unsigned	dex,
	uint32		&rtn)
{
	CSSM_DATA *val = fetchValidAttr(dex);
	if((val == NULL) || (val->Length != sizeof(uint32))) {
		ocspdErrorLog("CrlInfo::fetchIntAttr: Badly formed uint32 attr at dex %u\n", 
			dex);
		mIsBadlyFormed = true;
		return 1;
	}
	rtn = *(uint32 *)val->Data;
	return 0;
}
Exemplo n.º 10
0
/*
 * Delete all stale and badly formed CRLs from cache.
 */
static void deleteBadCrls(
	CrlInfo		**crlInfo, 
	unsigned	numCrls)
{
	CrlInfo *crl;

	for(unsigned dex=0; dex<numCrls; dex++) {
		crl = crlInfo[dex];
	
		/* 
		 * expired is not grounds for deletion; mIsStale is.
		 */
		if(crl->mIsBadlyFormed || crl->mIsStale) {
			crl->printName("deleting");
			CSSM_RETURN crtn = CSSM_DL_DataDelete(crl->dlDbHand(), 
				crl->record());
			if(crtn) {
				ocspdErrorLog("deleteBadCrls: CSSM_DL_DataDelete returned %u", (unsigned)crtn);
			}
		}
	}
}
Exemplo n.º 11
0
/*
 * Calculate expired/stale state for all CRLs.
 */
int calcCurrent(
	CrlInfo		**crlInfo, 
	unsigned	numCrls, 
	int 		expireOverlapSeconds,
	int			staleTimeSeconds)
{
	if(expireOverlapSeconds > staleTimeSeconds) {
		ocspdErrorLog("calcCurrent: ExpireOverlap greater than StaleTime; aborting.\n");
		return 1;
	}
	char *updateTime = cuTimeAtNowPlus(expireOverlapSeconds, TIME_CSSM);
	char *staleTime  = cuTimeAtNowPlus(-staleTimeSeconds, TIME_CSSM);

	ocspdCrlDebug("updateTime : %s", updateTime);
	ocspdCrlDebug("staleTime  : %s", staleTime);

	for(unsigned dex=0; dex<numCrls; dex++) {
		crlInfo[dex]->validateTimes(updateTime, staleTime, dex);
	}
	APP_FREE(updateTime);
	APP_FREE(staleTime);
	return 0;
}
Exemplo n.º 12
0
/* free attribute(s) allocated by DL */
static void freeAttrs(
	CSSM_DB_ATTRIBUTE_DATA 	*attrs,
	unsigned				numAttrs)
{
	unsigned i;
	
	for(i=0; i<numAttrs; i++) {
		CSSM_DB_ATTRIBUTE_DATA_PTR attrData = &attrs[i];
		unsigned j;
		for(j=0; j<attrData->NumberOfValues; j++) {
			CSSM_DATA_PTR data = &attrData->Value[j];
			if(data == NULL) {
				/* fault of DL, who said there was a value here */
				ocspdErrorLog("***freeAttrs screwup: NULL data\n");
				return;
			}
			APP_FREE(data->Data);
			data->Data = NULL;
			data->Length = 0;
		}
		APP_FREE(attrData->Value);
		attrData->Value = NULL;
	}
}
Exemplo n.º 13
0
static void handle_server_response(CFReadStreamRef stream,
    CFStreamEventType type, void *info) {
    asynchttp_t *http = (asynchttp_t *)info;
    if (!http->stream) {
        secerror("Avoiding crash due to CFReadStream invoking us after we called CFReadStreamSetDispatchQueue(stream, NULL) on a different block on our serial queue");
        return;
    }

    switch (type) {
    case kCFStreamEventHasBytesAvailable:
    {
        UInt8 buffer[POST_BUFSIZE];
        CFIndex length;
        do {
#if 1
            length = CFReadStreamRead(stream, buffer, sizeof(buffer));
#else
            const UInt8 *buffer = CFReadStreamGetBuffer(stream, -1, &length);
#endif
            secdebug("http",
                "stream: %@ kCFStreamEventHasBytesAvailable read: %lu bytes",
                stream, length);
            if (length < 0) {
                /* Negative length == error */
                asynchttp_complete(http);
                break;
            } else if (length > 0) {
                //CFHTTPMessageAppendBytes(http->response, buffer, length);
                CFDataAppendBytes(http->data, buffer, length);
            } else {
                /* Read 0 bytes, are we are done or do we wait for
                   kCFStreamEventEndEncountered? */
                asynchttp_complete(http);
                break;
            }
        } while (CFReadStreamHasBytesAvailable(stream));
        break;
    }
    case kCFStreamEventErrorOccurred:
    {
        CFStreamError error = CFReadStreamGetError(stream);

        secdebug("http",
            "stream: %@ kCFStreamEventErrorOccurred domain: %ld error: %ld",
            stream, error.domain, (long) error.error);

        if (error.domain == kCFStreamErrorDomainPOSIX) {
            ocspdErrorLog("CFReadStream posix: %s", strerror(error.error));
        } else if (error.domain == kCFStreamErrorDomainMacOSStatus) {
            ocspdErrorLog("CFReadStream osstatus: %"PRIstatus, error.error);
        } else {
            ocspdErrorLog("CFReadStream domain: %ld error: %"PRIstatus,
                error.domain, error.error);
        }
        asynchttp_complete(http);
        break;
    }
    case kCFStreamEventEndEncountered:
    {
        http->response = (CFHTTPMessageRef)CFReadStreamCopyProperty(
            stream, kCFStreamPropertyHTTPResponseHeader);
        secdebug("http", "stream: %@ kCFStreamEventEndEncountered hdr: %@",
            stream, http->response);
        CFHTTPMessageSetBody(http->response, http->data);
        asynchttp_complete(http);
        break;
    }
    default:
        ocspdErrorLog("handle_server_response unexpected event type: %lu",
            type);
        break;
    }
}
Exemplo n.º 14
0
/*
 * Fetch attrs for all CRLs from DB. CRL blobs themselves are not fetched
 * unless the fetchBlobs argument is asserted.
 */
static CSSM_RETURN fetchAllCrls(
	CSSM_DL_DB_HANDLE 	dlDbHand, 
	bool				fetchBlobs,		// fetch actual CRL data
	CrlInfo				**&rtnCrlInfo,	// RETURNED
	unsigned			&numCrls)		// RETURNED
{
	CSSM_QUERY						query;
	CSSM_DB_RECORD_ATTRIBUTE_DATA	recordAttrs;
	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
	CSSM_RETURN						crtn;
	CSSM_HANDLE						resultHand;
	unsigned 						attrDex;
	CSSM_DB_ATTRIBUTE_DATA			attrData[NUM_CRL_ATTRS];
	CSSM_DATA_PTR					crlDataPtr = NULL;
	CSSM_DATA						crlData;
	
	numCrls = 0;
	rtnCrlInfo = NULL;
	
	/* build an ATTRIBUTE_DATA array from list attrs */
	memset(attrData, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * NUM_CRL_ATTRS);
	for(attrDex=0; attrDex<NUM_CRL_ATTRS; attrDex++) {
		attrData[attrDex].Info = x509CrlRecordAttrs[attrDex];
	}

	recordAttrs.DataRecordType     = CSSM_DL_DB_RECORD_X509_CRL;
	recordAttrs.NumberOfAttributes = NUM_CRL_ATTRS;
	recordAttrs.AttributeData      = &attrData[0];
	
	/* just search by recordType, no predicates */
	query.RecordType = CSSM_DL_DB_RECORD_X509_CRL;
	query.Conjunctive = CSSM_DB_NONE;
	query.NumSelectionPredicates = 0;
	query.SelectionPredicate = NULL;
	query.QueryLimits.TimeLimit = 0;			// FIXME - meaningful?
	query.QueryLimits.SizeLimit = 1;			// FIXME - meaningful?
	query.QueryFlags = 0;		// CSSM_QUERY_RETURN_DATA...FIXME - used?

	if(fetchBlobs) {
		crlDataPtr = &crlData;
	}
	
	crtn = CSSM_DL_DataGetFirst(dlDbHand,
		&query,
		&resultHand,
		&recordAttrs,
		crlDataPtr,		
		&record);
	switch(crtn) {
		case CSSM_OK:
			break;		// proceed
		case CSSMERR_DL_ENDOFDATA:
			/* we're done here */
			return CSSM_OK;
		case CSSMERR_DL_INVALID_RECORDTYPE:
			/* this means that this keychain hasn't been initialized
			 * for CRL schema; treat it as empty. */
			return CSSM_OK;
		default:
			ocspdErrorLog("fetchAllCrls: DataGetFirst returned %u", (unsigned)crtn);
			return crtn;
	}

	/* Cook up a CrlInfo, add it to outgoing array */
	CrlInfo *crlInfo = new CrlInfo(dlDbHand, &attrData[0], record, crlDataPtr);
	rtnCrlInfo = (CrlInfo **)malloc(sizeof(CrlInfo*));
	rtnCrlInfo[0] = crlInfo;
	numCrls++;

	/* now the rest of them */
	for(;;) {
		crtn = CSSM_DL_DataGetNext(dlDbHand,
			resultHand, 
			&recordAttrs,
			crlDataPtr,
			&record);
		switch(crtn) {
			case CSSM_OK:
				rtnCrlInfo = (CrlInfo **)realloc(rtnCrlInfo, 
					sizeof(CrlInfo *) * (numCrls + 1));
				rtnCrlInfo[numCrls] = new CrlInfo(dlDbHand, &attrData[0], record,
					crlDataPtr);
				numCrls++;
				break;		// and go again 
			case CSSMERR_DL_ENDOFDATA:
				/* normal termination */
				return CSSM_OK;
			default:
				ocspdErrorLog("fetchAllCrls: DataGetNext returned %u", (unsigned)crtn);
				return crtn;
		}
	}
	/* not reached */
}
Exemplo n.º 15
0
OCSPResponse::OCSPResponse(
	const CSSM_DATA &resp,
	CFTimeInterval defaultTTL)		// default time-to-live in seconds
		: mLatestNextUpdate(NULL_TIME), 
		  mExpireTime(NULL_TIME),
		  mExtensions(NULL)
{
	SecAsn1CoderCreate(&mCoder);
	memset(&mTopResp, 0, sizeof(mTopResp));
	memset(&mBasicResponse, 0, sizeof(mBasicResponse));
	memset(&mResponseData, 0, sizeof(mResponseData));
	memset(&mResponderId, 0, sizeof(mResponderId));
	mResponderIdTag = (SecAsn1OCSPResponderIDTag)0;		// invalid
	mEncResponderName.Data = NULL;
	mEncResponderName.Length = 0;
	
	if(SecAsn1DecodeData(mCoder, &resp, kSecAsn1OCSPResponseTemplate, &mTopResp)) {
		ocspdErrorLog("OCSPResponse: decode failure at top level\n");
		CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
	}
	
	/* remainder is valid only on RS_Success */
	if((mTopResp.responseStatus.Data == NULL) ||
	   (mTopResp.responseStatus.Length == 0)) {
		ocspdErrorLog("OCSPResponse: no responseStatus\n");
		CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
	}
	if(mTopResp.responseStatus.Data[0] != RS_Success) {
		/* not a failure of our constructor; this object is now useful, but 
		 * only for this one byte of status info */
		return;
	}
	if(mTopResp.responseBytes == NULL) {
		/* I don't see how this can be legal on RS_Success */
		ocspdErrorLog("OCSPResponse: empty responseBytes\n");
		CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
	}
	if(!ocspdCompareCssmData(&mTopResp.responseBytes->responseType,
			&CSSMOID_PKIX_OCSP_BASIC)) {
		ocspdErrorLog("OCSPResponse: unknown responseType\n");
		CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
	}
	
	/* decode the SecAsn1OCSPBasicResponse */
	if(SecAsn1DecodeData(mCoder, &mTopResp.responseBytes->response,
			kSecAsn1OCSPBasicResponseTemplate, &mBasicResponse)) {
		ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPBasicResponse\n");
		CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
	}
	
	/* signature and cert evaluation done externally */
	
	/* decode the SecAsn1OCSPResponseData */
	if(SecAsn1DecodeData(mCoder, &mBasicResponse.tbsResponseData,
			kSecAsn1OCSPResponseDataTemplate, &mResponseData)) {
		ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPResponseData\n");
		CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
	}
	if(mResponseData.responderID.Data == NULL) {
		ocspdErrorLog("OCSPResponse: bad responderID\n");
		CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
	}
		
	/* choice processing for ResponderID */
	mResponderIdTag = (SecAsn1OCSPResponderIDTag)
		(mResponseData.responderID.Data[0] & SEC_ASN1_TAGNUM_MASK);
	const SecAsn1Template *templ;
	switch(mResponderIdTag) {
		case RIT_Name: 
			templ = kSecAsn1OCSPResponderIDAsNameTemplate; 
			break;
		case RIT_Key: 
			templ = kSecAsn1OCSPResponderIDAsKeyTemplate; 
			break;
		default:
			ocspdErrorLog("OCSPResponse: bad responderID tag\n");
			CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
	}
	if(SecAsn1DecodeData(mCoder, &mResponseData.responderID, templ, &mResponderId)) {
		ocspdErrorLog("OCSPResponse: decode failure at responderID\n");
		CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
	}
	
	/* check temporal validity */
	if(!calculateValidity(defaultTTL)) {
		/* Whoops, abort */
		CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
	}
	
	/* 
	 * Individual responses looked into when we're asked for a specific one
	 * via singleResponse()
	 */
	mExtensions = new OCSPExtensions(mResponseData.responseExtensions);
}
Exemplo n.º 16
0
/*
 * For each expired CRL, fetch a new one if we don't have a current
 * CRL from the same place. 
 */
static void refreshExpiredCrls(
	CrlInfo			**crlInfo, 
	unsigned		numCrls,
	CSSM_CL_HANDLE	clHand)
{
	CrlInfo *crl;
	bool haveCurrent;
	CSSM_DATA	newCrl;
	
	for(unsigned dex=0; dex<numCrls; dex++) {
		crl = crlInfo[dex];
		
		if(!crl->mIsExpired || crl->mRefreshed) {
			continue;
		}

		/* do we have one for the same issuer that's current? */
		haveCurrent = false;
		for(unsigned i=0; i<numCrls; i++) {
			if(i == dex) {
				/* skip identity */
				continue;
			}
			CrlInfo *checkCrl = crlInfo[i];
			if(checkCrl->mIsBadlyFormed) {
				/* forget this one */
				continue;
			}
			if(checkCrl->mIsExpired && !checkCrl->mRefreshed) {
				continue;
			}
			if(crl->isSameIssuer(checkCrl)) {
				/* have a match; this one's OK */
				ocspdCrlDebug("up-to-date CRL at dex %u matching expired CRL %u", 
					i, dex);
				haveCurrent = true;
				break;
			}
		}
		if(haveCurrent) {
			continue;
		}
		
		/* 
		 * Not all CRLs have a URI attribute, which is required for 
		 * refresh 
		 */
		CSSM_DATA_PTR uri = crl->fetchValidAttr(ATTR_DEX_URI);
		if(uri == NULL) {
			ocspdCrlDebug("Expired CRL with no URI at dex %u", dex);
			continue;
		}
		
		/* fetch a new one */
		crl->printName("fetching new");
		Allocator &alloc = Allocator::standard();
		CSSM_RETURN crtn = ocspdNetFetch(alloc, *uri, LT_Crl, newCrl);
		if(crtn) {
			ocspdErrorLog("ocspdNetFetch returned %u", (unsigned)crtn);
			continue;
		}
		
		/* store it in the DB */
		crtn = cuAddCrlToDb(crl->dlDbHand(), clHand, &newCrl, uri);
		alloc.free(newCrl.Data);
		
		/*
		 * One special error case - UNIQUE_INDEX_DATA indicates that 
		 * the CRL we just fetched is already in the cache. This
		 * can occur when expireOverlap is sufficiently large that
		 * we decide to fetch before a CRL is actually expired. In
		 * this case process as usual, avoiding any further updates
		 * from this CA/URI.
		 */
		switch(crtn) {
			case CSSM_OK:
				ocspdCrlDebug("...refreshed CRL added to DB to account "
					"for expired CRL %u",	dex);
				break;
			case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:
				ocspdCrlDebug("...refreshed CRL is a dup of CRL %u; skipping",
					dex);
				break;
			default:
				continue;
		}
		
			
		/*
		 * In case there are other CRLs still to be discovered
		 * in our list which are a) expired, and b) from this same issuer,
		 * we flag the current (expired) CRL as refreshed to ensure that
		 * we don't do this fetch again. A lot easier than cooking up 
		 * a new CrlInfo object for the CRL we just fetched.
		 */
		crl->mRefreshed = true;
	}
}
Exemplo n.º 17
0
CSSM_RETURN ocspdCrlRefresh(
	CSSM_DL_DB_HANDLE	dlDbHand,
	CSSM_CL_HANDLE		clHand,
	unsigned			staleDays,
	unsigned			expireOverlapSeconds,
	bool				purgeAll,
	bool				fullCryptoVerify,
	bool				doRefresh)		// false: just purge stale entries
{
	CSSM_TP_HANDLE	tpHand = 0;
	CSSM_CSP_HANDLE cspHand = 0;
	CSSM_RETURN		crtn = CSSM_OK;
	
	assert((dlDbHand.DLHandle != 0) &&
		   (dlDbHand.DBHandle != 0) &&
		   (clHand != 0));
		   
	if(staleDays == 0) {
		staleDays = DEFAULT_STALE_DAYS;
	}
	if(expireOverlapSeconds == 0) {
		expireOverlapSeconds = DEFAULT_EXPIRE_OVERLAP_SECONDS;
	}
	
	if(fullCryptoVerify) {
		/* also need TP, CSP */
		cspHand = attachCommon(&gGuidAppleCSP, CSSM_SERVICE_CSP);
		if(cspHand == 0) {
			Syslog::alert("Error loading AppleCSP");
			return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
		}
		tpHand = attachCommon(&gGuidAppleX509TP, CSSM_SERVICE_TP);
		if(tpHand == 0) {
			Syslog::alert("Error loading AppleX509TP");
			crtn = CSSMERR_CSSM_ADDIN_LOAD_FAILED;
			goto errOut;
		}
	}
	/* subsequent errors to errOut: */
	
	/* fetch all CRLs from the keychain */
	CrlInfo		**crlInfo;
	unsigned	numCrls;
	
	crtn = fetchAllCrls(dlDbHand, fullCryptoVerify, crlInfo, numCrls);
	if(crtn) {
		ocspdErrorLog("ocspdCrlRefresh: Error reading CRLs.");
		return crtn;
	}
	ocspdCrlDebug("ocspdCrlRefresh: %u CRLs found", numCrls);
	
	/* basic validation */
	validateCrls(crlInfo, numCrls);
	
	/* Optional full crypto validation */
	if(fullCryptoVerify) {
		cryptoValidateCrls(crlInfo, numCrls, tpHand, cspHand, clHand, 
			dlDbHand.DLHandle);
	}
	
	/* update the validity time flags on the CRLs */
	if(calcCurrent(crlInfo, numCrls, expireOverlapSeconds,
			staleDays * SECONDS_PER_DAY)) {
		ocspdErrorLog("ocspdCrlRefresh: Error calculating CRL times.");
		crtn = CSSMERR_TP_INTERNAL_ERROR;
		goto errOut;
	}
	
	if(purgeAll) {
		/* mark all of them stale */
		purgeAllCrls(crlInfo, numCrls);
	}
	
	/* 
	 * Delete all bad CRLs from DB. We do this before the refresh in 
	 * case of the purgeAll option, in which case we really want to 
	 * insert newly fetched CRLs in the DB even if they appear to 
	 * be trhe same as the ones they're replacing.
	 */
	deleteBadCrls(crlInfo, numCrls);
	
	/* refresh the out-of-date CRLs */
	if(doRefresh) {
		refreshExpiredCrls(crlInfo, numCrls, clHand);
	}
	
	/* clean up */
	for(unsigned dex=0; dex<numCrls; dex++) {
		delete crlInfo[dex];
	}
	free(crlInfo);

errOut:
	if(cspHand) {
		detachCommon(&gGuidAppleCSP, cspHand);
	}
	if(tpHand) {
		detachCommon(&gGuidAppleX509TP, tpHand);
	}
	return crtn;
}