+++ /dev/null
-/*
- * Copyright (c) 2008-2009 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/*
- * SecOCSPResponse.c - Wrapper to decode ocsp responses.
- */
-
-#include <securityd/SecOCSPResponse.h>
-#include <Security/SecCertificateInternal.h>
-#include <Security/SecFramework.h>
-#include <Security/SecKeyPriv.h>
-#include <AssertMacros.h>
-#include <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)
-
-
-/*
- OCSPResponse ::= SEQUENCE {
- responseStatus OCSPResponseStatus,
- responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
-
- OCSPResponseStatus ::= ENUMERATED {
- successful (0), --Response has valid confirmations
- malformedRequest (1), --Illegal confirmation request
- internalError (2), --Internal error in issuer
- tryLater (3), --Try again later
- --(4) is not used
- sigRequired (5), --Must sign the request
- unauthorized (6) --Request unauthorized
- }
-
- ResponseBytes ::= SEQUENCE {
- responseType OBJECT IDENTIFIER,
- response OCTET STRING }
-
- id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp }
- id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 }
-
- The value for response SHALL be the DER encoding of
- BasicOCSPResponse.
-
- BasicOCSPResponse ::= SEQUENCE {
- tbsResponseData ResponseData,
- signatureAlgorithm AlgorithmIdentifier,
- signature BIT STRING,
- certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
-
- The value for signature SHALL be computed on the hash of the DER
- encoding ResponseData.
-
- ResponseData ::= SEQUENCE {
- version [0] EXPLICIT Version DEFAULT v1,
- responderID ResponderID,
- producedAt GeneralizedTime,
- responses SEQUENCE OF SingleResponse,
- responseExtensions [1] EXPLICIT Extensions OPTIONAL }
-
- ResponderID ::= CHOICE {
- byName [1] Name,
- byKey [2] KeyHash }
-
- KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
- (excluding the tag and length fields)
-
- SingleResponse ::= SEQUENCE {
- certID CertID,
- certStatus CertStatus,
- thisUpdate GeneralizedTime,
- nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
- singleExtensions [1] EXPLICIT Extensions OPTIONAL }
-
- CertStatus ::= CHOICE {
- good [0] IMPLICIT NULL,
- revoked [1] IMPLICIT RevokedInfo,
- unknown [2] IMPLICIT UnknownInfo }
-
- RevokedInfo ::= SEQUENCE {
- revocationTime GeneralizedTime,
- revocationReason [0] EXPLICIT CRLReason OPTIONAL }
-
- UnknownInfo ::= NULL -- this can be replaced with an enumeration
-*/
-
-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;
- //this->extensions = NULL;
-
- 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) {
- /* Decode further to get SecAsn1OCSPRevokedInfo */
- 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) {
- /* Treat this as optional even for CS_Revoked */
- 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);
- }
- //mExtensions = new OCSPExtensions(resp->singleExtensions);
- ocspdDebug("status %d reason %d", (int)this->certStatus,
- (int)this->crlReason);
- return this;
-errOut:
- if (this)
- SecOCSPSingleResponseDestroy(this);
- return NULL;
-}
-
-#define LEEWAY (4500.0)
-
-/* Calculate temporal validity; set latestNextUpdate and expireTime. Only
- called from SecOCSPResponseCreate. Returns true if valid, else returns
- false. */
-static bool SecOCSPResponseCalculateValidity(SecOCSPResponseRef this,
- CFTimeInterval maxAge, CFTimeInterval defaultTTL)
-{
- this->latestNextUpdate = NULL_TIME;
- CFAbsoluteTime now = this->verifyTime = CFAbsoluteTimeGetCurrent();
-
- if (this->producedAt > now + LEEWAY) {
- ocspdErrorLog("OCSPResponse: producedAt more than 1:15 from now");
- return false;
- }
-
- /* Make this->latestNextUpdate be the date farthest in the future
- of any of the singleResponses nextUpdate fields. */
- SecAsn1OCSPSingleResponse **responses;
- for (responses = this->responseData.responses; *responses; ++responses) {
- SecAsn1OCSPSingleResponse *resp = *responses;
-
- /* thisUpdate later than 'now' invalidates the whole response. */
- CFAbsoluteTime thisUpdate = genTimeToCFAbsTime(&resp->thisUpdate);
- if (thisUpdate > now + LEEWAY) {
- ocspdErrorLog("OCSPResponse: thisUpdate more than 1:15 from now");
- return false;
- }
-
- /* Keep track of latest nextUpdate. */
- if (resp->nextUpdate != NULL) {
- CFAbsoluteTime nextUpdate = genTimeToCFAbsTime(resp->nextUpdate);
- if (nextUpdate > this->latestNextUpdate) {
- this->latestNextUpdate = nextUpdate;
- }
- }
-#ifdef STRICT_RFC5019
- else {
- /* RFC 5019 section 2.2.4 states on nextUpdate:
- Responders MUST always include this value to aid in
- response caching. See Section 6 for additional
- information on caching.
- */
- ocspdErrorLog("OCSPResponse: nextUpdate not present");
- return false;
- }
-#endif
- }
-
- /* Now that we have this->latestNextUpdate, we figure out the latest
- date at which we will expire this response from our cache. To comply
- with rfc5019s:
-
-6.1. Caching at the Client
-
- To minimize bandwidth usage, clients MUST locally cache authoritative
- OCSP responses (i.e., a response with a signature that has been
- successfully validated and that indicate an OCSPResponseStatus of
- 'successful').
-
- Most OCSP clients will send OCSPRequests at or near the nextUpdate
- time (when a cached response expires). To avoid large spikes in
- responder load that might occur when many clients refresh cached
- responses for a popular certificate, responders MAY indicate when the
- client should fetch an updated OCSP response by using the cache-
- control:max-age directive. Clients SHOULD fetch the updated OCSP
- Response on or after the max-age time. To ensure that clients
- receive an updated OCSP response, OCSP responders MUST refresh the
- OCSP response before the max-age time.
-
-6.2 [...]
-
- we need to take the cache-control:max-age directive into account.
-
- The way the code below is written we ignore a max-age=0 in the
- http header. Since a value of 0 (NULL_TIME) also means there
- was no max-age in the header. This seems ok since that would imply
- no-cache so we also ignore negative values for the same reason,
- instead we'll expire whenever this->latestNextUpdate tells us to,
- which is the signed value if max-age is too low, since we don't
- want to refetch multilple times for a single page load in a browser. */
- if (this->latestNextUpdate == NULL_TIME) {
- /* See comment above on RFC 5019 section 2.2.4. */
- /* Absolute expire time = current time plus defaultTTL */
- this->expireTime = now + defaultTTL;
- } else if (this->latestNextUpdate < now - LEEWAY) {
- ocspdErrorLog("OCSPResponse: latestNextUpdate more than 1:15 ago");
- return false;
- } else if (maxAge > 0) {
- /* Beware of double overflows such as:
-
- now + maxAge < this->latestNextUpdate
-
- in the math below since an attacker could create any positive
- value for maxAge. */
- if (maxAge < this->latestNextUpdate - now) {
- /* maxAge header wants us to expire the cache entry sooner than
- nextUpdate would allow, to balance server load. */
- this->expireTime = now + maxAge;
- } else {
- /* maxAge http header attempting to make us cache the response
- longer than it's valid for, bad http header! Ignoring you. */
- ocspdErrorLog("OCSPResponse: now + maxAge > latestNextUpdate,"
- " using latestNextUpdate");
- this->expireTime = this->latestNextUpdate;
- }
- } else {
- /* No maxAge provided, just use latestNextUpdate. */
- 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");
- }
- /* remainder is valid only on RS_Success */
- 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);
- /* not a failure of our constructor; this object is now useful, but
- * only for this one byte of status info */
- return this;
- }
- if (topResp.responseBytes == NULL) {
- /* I don't see how this can be legal on RS_Success */
- ocspdErrorLog("OCSPResponse: empty responseBytes");
- goto errOut;
- }
- if (!SecAsn1OidCompare(&topResp.responseBytes->responseType,
- &OID_PKIX_OCSP_BASIC)) {
- ocspdErrorLog("OCSPResponse: unknown responseType");
- goto errOut;
-
- }
-
- /* decode the SecAsn1OCSPBasicResponse */
- if (SecAsn1DecodeData(this->coder, &topResp.responseBytes->response,
- kSecAsn1OCSPBasicResponseTemplate, &this->basicResponse)) {
- ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPBasicResponse");
- goto errOut;
- }
-
- /* signature and cert evaluation done externally */
-
- /* decode the SecAsn1OCSPResponseData */
- 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;
- }
-
- /* Choice processing for ResponderID */
- this->responderIdTag = (SecAsn1OCSPResponderIDTag)
- (this->responseData.responderID.Data[0] & SEC_ASN1_TAGNUM_MASK);
- const SecAsn1Template *templ;
- switch(this->responderIdTag) {
- case RIT_Name:
- /* @@@ Since we don't use the decoded byName value we could skip
- decoding it but we do it anyway for validation. */
- 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;
- }
-
- /* We should probably get the defaultTTL from the policy.
- For now defaultTTL is hardcoded to 24 hours. */
- CFTimeInterval defaultTTL = 24 * 60 * 60;
- /* Check temporal validity, default TTL 24 hours. */
- require_quiet(SecOCSPResponseCalculateValidity(this, maxAge, defaultTTL), errOut);
-
-#if 0
- /* Individual responses looked into when we're asked for a specific one
- via SecOCSPResponseCopySingleResponse(). */
- 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);
- CFReleaseSafe(this->nonce);
- 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;
-
- /* We should probably get the defaultTTL from the policy.
- For now defaultTTL is hardcoded to 24 hours. This is how long we trust
- a response without a nextUpdate field. */
- CFTimeInterval defaultTTL = 24 * 60 * 60;
-
- SecAsn1OCSPSingleResponse **responses;
- for (responses = this->responseData.responses; *responses; ++responses) {
- SecAsn1OCSPSingleResponse *resp = *responses;
- SecAsn1OCSPCertID *certId = &resp->certID;
- /* First check the easy part, serial number should match. */
- if (certId->serialNumber.Length != (size_t)CFDataGetLength(serial) ||
- memcmp(CFDataGetBytePtr(serial), certId->serialNumber.Data,
- certId->serialNumber.Length)) {
- /* Serial # mismatch, skip this singleResponse. */
- continue;
- }
-
- /* Calcluate the issuerKey and issuerName digests using the
- hashAlgorithm and parameters specified in the certId, if
- they differ from the ones we already computed. */
- 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) {
- /* rfc2560 section 2.4 states: "If nextUpdate is not set, the
- responder is indicating that newer revocation information
- is available all the time".
- Let's ensure that thisUpdate isn't more than defaultTTL in
- the past then. */
- 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;
- }
- }
-
- /* resp matches the certificate in request, so let's use it. */
- 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) {
- /* Beware this->basicResponse.sig: on decode, length is in BITS */
- return SecKeyDigestAndVerify(key, &this->basicResponse.algId,
- this->basicResponse.tbsResponseData.Data,
- this->basicResponse.tbsResponseData.Length,
- this->basicResponse.sig.Data,
- this->basicResponse.sig.Length / 8) == errSecSuccess;
-}
-
-static bool SecOCSPResponseIsIssuer(SecOCSPResponseRef this,
- SecCertificatePathRef issuer) {
- bool shouldBeSigner = false;
- SecCertificateRef signer = SecCertificatePathGetCertificateAtIndex(issuer, 0);
- if (this->responderIdTag == RIT_Name) {
- /* Name inside response must == signer's SubjectName. */
- 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 /* if (this->responderIdTag == RIT_Key) */ {
- /* ResponderID.byKey must == SHA1(signer's public key) */
- 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;
-}
-
-/* Returns the SecCertificatePathRef who's leaf signed this ocspResponse if
- we can find one and NULL if we can't find a valid signer. */
-SecCertificatePathRef SecOCSPResponseCopySigner(SecOCSPResponseRef this,
- SecCertificatePathRef issuer) {
- SecCertificateRef issuerCert = SecCertificatePathGetCertificateAtIndex(issuer, 0);
- CFDataRef issuerSubject = SecCertificateGetNormalizedSubjectContent(issuerCert);
- /* Look though any certs that came with the response and see if they were
- both issued by the issuerPath and signed the response. */
- 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 none of the returned certs work, try the issuer of the certificate
- being checked directly. */
- if (SecOCSPResponseIsIssuer(this, issuer)) {
- CFRetain(issuer);
- return issuer;
- }
-
- /* We couldn't find who signed this ocspResponse, give up. */
- return NULL;
-}