#include <securityd/SecOCSPResponse.h>
#include <Security/SecCertificateInternal.h>
#include <Security/SecFramework.h>
#include <Security/SecKeyPriv.h>
#include <AssertMacros.h>
#include <security_utilities/debugging.h>
#include <security_asn1/SecAsn1Coder.h>
#include <security_asn1/ocspTemplates.h>
#include <security_asn1/oidsalg.h>
#include <security_asn1/oidsocsp.h>
#include <CommonCrypto/CommonDigest.h>
#include <asl.h>
#include <stdlib.h>
#include "SecInternal.h"
#define ocspdErrorLog(args...) asl_log(NULL, NULL, ASL_LEVEL_ERR, ## args)
#define ocspdHttpDebug(args...) secdebug("ocspdHttp", ## args)
#define ocspdDebug(args...) secdebug("ocsp", ## args)
static CFAbsoluteTime genTimeToCFAbsTime(const SecAsn1Item *datetime)
{
return SecAbsoluteTimeFromDateContent(SEC_ASN1_GENERALIZED_TIME,
datetime->Data, datetime->Length);
}
void SecOCSPSingleResponseDestroy(SecOCSPSingleResponseRef this) {
free(this);
}
static SecOCSPSingleResponseRef SecOCSPSingleResponseCreate(
SecAsn1OCSPSingleResponse *resp, SecAsn1CoderRef coder) {
assert(resp != NULL);
SecOCSPSingleResponseRef this;
require(this = (SecOCSPSingleResponseRef)
malloc(sizeof(struct __SecOCSPSingleResponse)), errOut);
this->certStatus = CS_NotParsed;
this->thisUpdate = NULL_TIME;
this->nextUpdate = NULL_TIME;
this->revokedTime = NULL_TIME;
this->crlReason = kSecRevocationReasonUndetermined;
if ((resp->certStatus.Data == NULL) || (resp->certStatus.Length == 0)) {
ocspdErrorLog("OCSPSingleResponse: bad certStatus");
goto errOut;
}
this->certStatus = (SecAsn1OCSPCertStatusTag)(resp->certStatus.Data[0] & SEC_ASN1_TAGNUM_MASK);
if (this->certStatus == CS_Revoked) {
SecAsn1OCSPCertStatus certStatus;
memset(&certStatus, 0, sizeof(certStatus));
if (SecAsn1DecodeData(coder, &resp->certStatus,
kSecAsn1OCSPCertStatusRevokedTemplate, &certStatus)) {
ocspdErrorLog("OCSPSingleResponse: err decoding certStatus");
goto errOut;
}
SecAsn1OCSPRevokedInfo *revokedInfo = certStatus.revokedInfo;
if (revokedInfo != NULL) {
this->revokedTime = genTimeToCFAbsTime(&revokedInfo->revocationTime);
const SecAsn1Item *revReason = revokedInfo->revocationReason;
if((revReason != NULL) &&
(revReason->Data != NULL) &&
(revReason->Length != 0)) {
this->crlReason = revReason->Data[0];
}
}
}
this->thisUpdate = genTimeToCFAbsTime(&resp->thisUpdate);
if (resp->nextUpdate != NULL) {
this->nextUpdate = genTimeToCFAbsTime(resp->nextUpdate);
}
ocspdDebug("status %d reason %d", (int)this->certStatus,
(int)this->crlReason);
return this;
errOut:
if (this)
SecOCSPSingleResponseDestroy(this);
return NULL;
}
static bool SecOCSPResponseCalculateValidity(SecOCSPResponseRef this,
CFTimeInterval maxAge, CFTimeInterval defaultTTL)
{
this->latestNextUpdate = NULL_TIME;
CFAbsoluteTime now = this->verifyTime = CFAbsoluteTimeGetCurrent();
if (this->producedAt > now) {
ocspdErrorLog("OCSPResponse: producedAt later than current time");
return false;
}
SecAsn1OCSPSingleResponse **responses;
for (responses = this->responseData.responses; *responses; ++responses) {
SecAsn1OCSPSingleResponse *resp = *responses;
CFAbsoluteTime thisUpdate = genTimeToCFAbsTime(&resp->thisUpdate);
if (thisUpdate > now) {
ocspdErrorLog("OCSPResponse: thisUpdate later than current time");
return false;
}
if (resp->nextUpdate != NULL) {
CFAbsoluteTime nextUpdate = genTimeToCFAbsTime(resp->nextUpdate);
if (nextUpdate > this->latestNextUpdate) {
this->latestNextUpdate = nextUpdate;
}
}
#ifdef STRICT_RFC5019
else {
ocspdErrorLog("OCSPResponse: nextUpdate not present");
return false;
}
#endif
}
if (this->latestNextUpdate == NULL_TIME) {
this->expireTime = now + defaultTTL;
} else if (this->latestNextUpdate < now) {
ocspdErrorLog("OCSPResponse: now > latestNextUpdate");
return false;
} else if (maxAge > 0) {
if (maxAge < this->latestNextUpdate - now) {
this->expireTime = now + maxAge;
} else {
ocspdErrorLog("OCSPResponse: now + maxAge > latestNextUpdate,"
" using latestNextUpdate");
this->expireTime = this->latestNextUpdate;
}
} else {
this->expireTime = this->latestNextUpdate;
}
return true;
}
SecOCSPResponseRef SecOCSPResponseCreate(CFDataRef ocspResponse,
CFTimeInterval maxAge) {
SecAsn1OCSPResponse topResp = {};
SecOCSPResponseRef this;
require(this = (SecOCSPResponseRef)calloc(1, sizeof(struct __SecOCSPResponse)),
errOut);
require_noerr(SecAsn1CoderCreate(&this->coder), errOut);
this->data = ocspResponse;
CFRetain(ocspResponse);
SecAsn1Item resp;
resp.Length = CFDataGetLength(ocspResponse);
resp.Data = (uint8_t *)CFDataGetBytePtr(ocspResponse);
if (SecAsn1DecodeData(this->coder, &resp, kSecAsn1OCSPResponseTemplate,
&topResp)) {
ocspdErrorLog("OCSPResponse: decode failure at top level");
}
if ((topResp.responseStatus.Data == NULL) ||
(topResp.responseStatus.Length == 0)) {
ocspdErrorLog("OCSPResponse: no responseStatus");
goto errOut;
}
this->responseStatus = topResp.responseStatus.Data[0];
if (this->responseStatus != kSecOCSPSuccess) {
secdebug("ocsp", "OCSPResponse: status: %d", this->responseStatus);
return this;
}
if (topResp.responseBytes == NULL) {
ocspdErrorLog("OCSPResponse: empty responseBytes");
goto errOut;
}
if (!SecAsn1OidCompare(&topResp.responseBytes->responseType,
&OID_PKIX_OCSP_BASIC)) {
ocspdErrorLog("OCSPResponse: unknown responseType");
goto errOut;
}
if (SecAsn1DecodeData(this->coder, &topResp.responseBytes->response,
kSecAsn1OCSPBasicResponseTemplate, &this->basicResponse)) {
ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPBasicResponse");
goto errOut;
}
if (SecAsn1DecodeData(this->coder, &this->basicResponse.tbsResponseData,
kSecAsn1OCSPResponseDataTemplate, &this->responseData)) {
ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPResponseData");
goto errOut;
}
this->producedAt = genTimeToCFAbsTime(&this->responseData.producedAt);
if (this->producedAt == NULL_TIME) {
ocspdErrorLog("OCSPResponse: bad producedAt");
goto errOut;
}
if (this->responseData.responderID.Data == NULL) {
ocspdErrorLog("OCSPResponse: bad responderID");
goto errOut;
}
this->responderIdTag = (SecAsn1OCSPResponderIDTag)
(this->responseData.responderID.Data[0] & SEC_ASN1_TAGNUM_MASK);
const SecAsn1Template *templ;
switch(this->responderIdTag) {
case RIT_Name:
templ = kSecAsn1OCSPResponderIDAsNameTemplate;
break;
case RIT_Key:
templ = kSecAsn1OCSPResponderIDAsKeyTemplate;
break;
default:
ocspdErrorLog("OCSPResponse: bad responderID tag");
goto errOut;
}
if (SecAsn1DecodeData(this->coder, &this->responseData.responderID, templ,
&this->responderID)) {
ocspdErrorLog("OCSPResponse: decode failure at responderID");
goto errOut;
}
CFTimeInterval defaultTTL = 24 * 60 * 60;
require_quiet(SecOCSPResponseCalculateValidity(this, maxAge, defaultTTL), errOut);
#if 0
mExtensions = new OCSPExtensions(mResponseData.responseExtensions);
#endif
return this;
errOut:
if (this) {
SecOCSPResponseFinalize(this);
}
return NULL;
}
CFDataRef SecOCSPResponseGetData(SecOCSPResponseRef this) {
return this->data;
}
SecOCSPResponseStatus SecOCSPGetResponseStatus(SecOCSPResponseRef this) {
return this->responseStatus;
}
CFAbsoluteTime SecOCSPResponseGetExpirationTime(SecOCSPResponseRef this) {
return this->expireTime;
}
CFDataRef SecOCSPResponseGetNonce(SecOCSPResponseRef this) {
return this->nonce;
}
CFAbsoluteTime SecOCSPResponseProducedAt(SecOCSPResponseRef this) {
return this->producedAt;
}
CFAbsoluteTime SecOCSPResponseVerifyTime(SecOCSPResponseRef this) {
return this->verifyTime;
}
CFArrayRef SecOCSPResponseCopySigners(SecOCSPResponseRef this) {
return NULL;
}
void SecOCSPResponseFinalize(SecOCSPResponseRef this) {
CFReleaseSafe(this->data);
SecAsn1CoderRelease(this->coder);
free(this);
}
SecOCSPSingleResponseRef SecOCSPResponseCopySingleResponse(
SecOCSPResponseRef this, SecOCSPRequestRef request) {
SecOCSPSingleResponseRef sr = NULL;
CFDataRef issuer = SecCertificateCopyIssuerSequence(request->certificate);
const DERItem *publicKey = SecCertificateGetPublicKeyData(request->issuer);
CFDataRef serial = SecCertificateCopySerialNumber(request->certificate);
CFDataRef issuerNameHash = NULL;
CFDataRef issuerPubKeyHash = NULL;
SecAsn1Oid *algorithm = NULL;
SecAsn1Item *parameters = NULL;
CFTimeInterval defaultTTL = 24 * 60 * 60;
SecAsn1OCSPSingleResponse **responses;
for (responses = this->responseData.responses; *responses; ++responses) {
SecAsn1OCSPSingleResponse *resp = *responses;
SecAsn1OCSPCertID *certId = &resp->certID;
if (certId->serialNumber.Length != (size_t)CFDataGetLength(serial) ||
memcmp(CFDataGetBytePtr(serial), certId->serialNumber.Data,
certId->serialNumber.Length)) {
continue;
}
if (!SecAsn1OidCompare(algorithm, &certId->algId.algorithm) ||
!SecAsn1OidCompare(parameters, &certId->algId.parameters)) {
algorithm = &certId->algId.algorithm;
parameters = &certId->algId.parameters;
CFReleaseSafe(issuerNameHash);
CFReleaseSafe(issuerPubKeyHash);
issuerNameHash = SecDigestCreate(kCFAllocatorDefault, algorithm,
parameters, CFDataGetBytePtr(issuer), CFDataGetLength(issuer));
issuerPubKeyHash = SecDigestCreate(kCFAllocatorDefault, algorithm,
parameters, publicKey->data, publicKey->length);
}
if (certId->issuerNameHash.Length == (size_t)CFDataGetLength(issuerNameHash)
&& !memcmp(CFDataGetBytePtr(issuerNameHash),
certId->issuerNameHash.Data, certId->issuerNameHash.Length)
&& certId->issuerPubKeyHash.Length == (size_t)CFDataGetLength(issuerPubKeyHash)
&& !memcmp(CFDataGetBytePtr(issuerPubKeyHash),
certId->issuerPubKeyHash.Data, certId->issuerPubKeyHash.Length)) {
CFAbsoluteTime thisUpdate = genTimeToCFAbsTime(&resp->thisUpdate);
if (thisUpdate > this->verifyTime) {
ocspdErrorLog("OCSPSingleResponse: thisUpdate > now");
continue;
}
if (resp->nextUpdate == NULL) {
if (this->verifyTime > thisUpdate + defaultTTL) {
ocspdErrorLog("OCSPSingleResponse: no nextUpdate present "
"and now > thisUpdate + defaultTTL");
continue;
}
} else {
CFAbsoluteTime nextUpdate = genTimeToCFAbsTime(resp->nextUpdate);
if (this->verifyTime > nextUpdate) {
ocspdErrorLog("OCSPSingleResponse: now > nextUpdate");
continue;
}
}
sr = SecOCSPSingleResponseCreate(resp, this->coder);
if (sr) {
ocspdDebug("found matching singleResponse");
break;
}
}
}
CFReleaseSafe(issuerPubKeyHash);
CFReleaseSafe(issuerNameHash);
CFReleaseSafe(serial);
CFReleaseSafe(issuer);
if (!sr) {
ocspdDebug("certID not found");
}
return sr;
}
static bool SecOCSPResponseVerifySignature(SecOCSPResponseRef this,
SecKeyRef key) {
return SecKeyDigestAndVerify(key, &this->basicResponse.algId,
this->basicResponse.tbsResponseData.Data,
this->basicResponse.tbsResponseData.Length,
this->basicResponse.sig.Data,
this->basicResponse.sig.Length / 8) == noErr;
}
static bool SecOCSPResponseIsIssuer(SecOCSPResponseRef this,
SecCertificatePathRef issuer) {
bool shouldBeSigner = false;
SecCertificateRef signer = SecCertificatePathGetCertificateAtIndex(issuer, 0);
if (this->responderIdTag == RIT_Name) {
CFDataRef subject = SecCertificateCopySubjectSequence(signer);
if (!subject) {
ocspdDebug("error on SecCertificateCopySubjectSequence");
return false;
}
if ((size_t)CFDataGetLength(subject) == this->responderID.byName.Length &&
!memcmp(this->responderID.byName.Data, CFDataGetBytePtr(subject),
this->responderID.byName.Length)) {
ocspdDebug("good ResponderID.byName");
shouldBeSigner = true;
} else {
ocspdDebug("BAD ResponderID.byName");
}
CFRelease(subject);
} else {
CFDataRef pubKeyDigest = SecCertificateCopyPublicKeySHA1Digest(signer);
if ((size_t)CFDataGetLength(pubKeyDigest) == this->responderID.byKey.Length &&
!memcmp(this->responderID.byKey.Data, CFDataGetBytePtr(pubKeyDigest),
this->responderID.byKey.Length)) {
ocspdDebug("good ResponderID.byKey");
shouldBeSigner = true;
} else {
ocspdDebug("BAD ResponderID.byKey");
}
CFRelease(pubKeyDigest);
}
if (shouldBeSigner) {
SecKeyRef key = SecCertificatePathCopyPublicKeyAtIndex(issuer, 0);
if (key) {
shouldBeSigner = SecOCSPResponseVerifySignature(this, key);
ocspdDebug("ocsp response signature %sok", shouldBeSigner ? "" : "not ");
CFRelease(key);
} else {
ocspdDebug("Failed to extract key from leaf certificate");
shouldBeSigner = false;
}
}
return shouldBeSigner;
}
SecCertificatePathRef SecOCSPResponseCopySigner(SecOCSPResponseRef this,
SecCertificatePathRef issuer) {
SecCertificateRef issuerCert = SecCertificatePathGetCertificateAtIndex(issuer, 0);
CFDataRef issuerSubject = SecCertificateGetNormalizedSubjectContent(issuerCert);
SecAsn1Item **certs;
for (certs = this->basicResponse.certs; certs && *certs; ++certs) {
SecCertificateRef cert = SecCertificateCreateWithBytes(
kCFAllocatorDefault, (*certs)->Data, (*certs)->Length);
if (cert) {
CFDataRef certIssuer = SecCertificateGetNormalizedIssuerContent(cert);
if (CFEqual(issuerSubject, certIssuer)) {
SecCertificatePathRef signer = SecCertificatePathCopyAddingLeaf(issuer, cert);
CFRelease(cert);
if (signer) {
if (SecOCSPResponseIsIssuer(this, signer)) {
return signer;
} else {
ocspdErrorLog("ocsp response cert not signed by issuer.");
CFRelease(signer);
}
}
} else {
ocspdErrorLog("ocsp response cert issuer doesn't match issuer subject.");
}
} else {
ocspdErrorLog("ocsp response cert failed to parse");
}
}
if (SecOCSPResponseIsIssuer(this, issuer)) {
CFRetain(issuer);
return issuer;
}
return NULL;
}