argument = "-s"
isEnabled = "NO">
</CommandLineArgument>
+ <CommandLineArgument
+ argument = "su_07_debugging"
+ isEnabled = "NO">
+ </CommandLineArgument>
<CommandLineArgument
argument = "si_33_keychain_backup"
isEnabled = "NO">
_SecItemAdd
_SecItemCopyDisplayNames
_SecItemCopyMatching
+#if TARGET_OS_MAC
+_SecItemAdd_ios
+_SecItemCopyMatching_ios
+_SecItemDelete_ios
+_SecItemUpdate_ios
+#endif
_SecItemDelete
_SecItemUpdate
__SecItemGetPersistentReference
/*
* Following a CSSMOID_ECDSA_WithSpecified algorithm is an encoded
- * ECDSA_SigAlgParams containing the digest agorithm OID. Decode and return
+ * ECDSA_SigAlgParams containing the digest algorithm OID. Decode and return
* a unified ECDSA/digest alg (e.g. CSSM_ALGID_SHA512WithECDSA).
* Returns nonzero on error.
*/
/*
* Copyright (c) 2004,2011-2012,2014 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,
* 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@
*/
/*
* ocspRequest.cpp - OCSP Request class
*/
-
+
#include "ocspRequest.h"
#include "certGroupUtils.h"
#include "tpdebugging.h"
#define OCSP_NONCE_SIZE 8
/*
- * The only constructor. Subject and issuer must remain valid for the
- * lifetime of this object (they are not refcounted).
+ * The only constructor. Subject and issuer must remain valid for the
+ * lifetime of this object (they are not refcounted).
*/
OCSPRequest::OCSPRequest(
TPCertInfo &subject,
TPCertInfo &issuer,
bool genNonce)
: mCoder(NULL),
- mSubject(subject),
+ mSubject(subject),
mIssuer(issuer),
mGenNonce(genNonce),
mCertID(NULL)
CSSM_DATA_PTR issuerName;
CSSM_DATA_PTR issuerKey;
CSSM_KEY_PTR issuerPubKey;
+ CSSM_DATA issuerPubKeyBytes;
/* from subject */
CSSM_DATA_PTR subjectSerial=NULL;
CSSM_DATA nonceData = {OCSP_NONCE_SIZE, nonceBytes};
OCSPNonce *nonce = NULL;
NSS_CertExtension *extenArray[2] = {NULL, NULL};
-
+
if(mEncoded.Data) {
/* already done */
return &mEncoded;
}
- /*
+ /*
* One single request, no extensions
*/
memset(&singleReq, 0, sizeof(singleReq));
-
+
/* algId refers to the hash we'll perform in issuer name and key */
certId.algId.algorithm = CSSMOID_SHA1;
certId.algId.parameters.Data = nullParam;
goto errOut;
}
issuerPubKey = (CSSM_KEY_PTR)issuerKey->Data;
- ocspdSha1(issuerPubKey->KeyData.Data, (CC_LONG)issuerPubKey->KeyData.Length, pubKeyHash);
-
+ ocspdGetPublicKeyBytes(mCoder, issuerPubKey, issuerPubKeyBytes);
+ ocspdSha1(issuerPubKeyBytes.Data, (CC_LONG)issuerPubKeyBytes.Length, pubKeyHash);
+
/* build the CertID from those components */
certId.issuerNameHash.Data = issuerNameHash;
certId.issuerNameHash.Length = CC_SHA1_DIGEST_LENGTH;
certId.issuerPubKeyHash.Data = pubKeyHash;
- certId.issuerPubKeyHash.Length = CC_SHA1_DIGEST_LENGTH;
+ certId.issuerPubKeyHash.Length = CC_SHA1_DIGEST_LENGTH;
certId.serialNumber = *subjectSerial;
- /*
+ /*
* Build top level request with one entry in requestList, no signature,
* one optional extension (a nonce)
*/
tbs.requestExtensions = extenArray;
SecAsn1AllocCopyItem(mCoder, &nonceData, &mNonce);
}
-
+
/* Encode */
- if(SecAsn1EncodeItem(mCoder, &signedReq, kSecAsn1OCSPSignedRequestTemplate,
+ if(SecAsn1EncodeItem(mCoder, &signedReq, kSecAsn1OCSPSignedRequestTemplate,
&mEncoded)) {
tpErrorLog("OCSPRequest::encode: error encoding OCSP req\n");
crtn = CSSMERR_TP_INTERNAL_ERROR;
goto errOut;
}
/* save a copy of the CertID */
- mCertID = new OCSPClientCertID(*issuerName, issuerPubKey->KeyData, *subjectSerial);
-
+ mCertID = new OCSPClientCertID(*issuerName, issuerPubKeyBytes, *subjectSerial);
+
errOut:
if(issuerName) {
mIssuer.freeField(&CSSMOID_X509V1IssuerNameStd, issuerName);
/*
* Copyright (c) 2004,2011,2014 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,
* 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@
*/
/*
* ocspRequest.h - OCSP Request class
*/
-
+
#ifndef _OCSP_REQUEST_H_
#define _OCSP_REQUEST_H_
NOCOPY(OCSPRequest)
public:
/*
- * The only constructor. Subject and issuer must remain valid for the
- * lifetime of this object (they are not refcounted).
+ * The only constructor. Subject and issuer must remain valid for the
+ * lifetime of this object (they are not refcounted).
*/
OCSPRequest(
TPCertInfo &subject,
TPCertInfo &issuer,
bool genNonce);
-
+
~OCSPRequest();
-
- /*
+
+ /*
* Obtain encoded OCSP request suitable for posting to responder.
* This object owns and maintains the memory.
*/
const CSSM_DATA *encode();
- /*
+ /*
* Obtain this request's nonce (which we randomly generate at encode() time),
* This object owns and maintains the memory. Result is NULL} if we
- * didn't generate a nonce.
+ * didn't generate a nonce.
*/
const CSSM_DATA *nonce();
-
- /*
+
+ /*
* Obtain this request's CertID. Used to look up matching SingleResponse
* in the OCSPResponse.
*/
OCSPClientCertID *certID();
-
+
private:
SecAsn1CoderRef mCoder;
TPCertInfo &mSubject;
CSSM_DATA mNonce;
CSSM_DATA mEncoded; /* lazily evaluated */
OCSPClientCertID *mCertID; /* calculated during encode() */
-
+
};
#endif /* _OCSP_REQUEST_H_ */
/*
* Copyright (c) 2004,2011-2012,2014 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,
* 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@
*/
#include <CommonCrypto/CommonDigest.h>
#include <security_ocspd/ocspdUtils.h>
+#ifndef NDEBUG
+#include <Security/SecCertificate.h>
+#endif
+
/*
* Is signerCert authorized to sign OCSP responses by issuerCert? IssuerCert is
- * assumed to be (i.e., must, but we don't check that here) the signer of the
+ * assumed to be (i.e., must, but we don't check that here) the signer of the
* cert being verified, which is not in the loop for this op. Just a bool returned;
- * it's autoritized or it's not.
+ * it's authorized or it's not.
*/
static bool tpIsAuthorizedOcspSigner(
TPCertInfo &issuerCert, // issuer of cert being verified
bool ourRtn = false;
CE_ExtendedKeyUsage *eku = NULL;
bool foundEku = false;
-
- /*
+
+ /*
* First see if issuerCert issued signerCert (No signature vfy yet, just
* subject/issuer check).
*/
if(!issuerCert.isIssuerOf(signerCert)) {
+#ifndef NDEBUG
+ SecCertificateRef issuerRef = NULL;
+ SecCertificateRef signerRef = NULL;
+ const CSSM_DATA *issuerData = issuerCert.itemData();
+ const CSSM_DATA *signerData = signerCert.itemData();
+ crtn = SecCertificateCreateFromData(issuerData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER, &issuerRef);
+ crtn = SecCertificateCreateFromData(signerData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER, &signerRef);
+ CFStringRef issuerName = SecCertificateCopySubjectSummary(issuerRef);
+ CFStringRef signerName = SecCertificateCopySubjectSummary(signerRef);
+ if(issuerName) {
+ CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(issuerName), kCFStringEncodingUTF8) + 1;
+ char* buf = (char*) malloc(maxLength);
+ if (buf) {
+ if (CFStringGetCString(issuerName, buf, (CFIndex)maxLength, kCFStringEncodingUTF8)) {
+ tpOcspDebug("tpIsAuthorizedOcspSigner: issuerCert \"%s\"", buf);
+ }
+ free(buf);
+ }
+ CFRelease(issuerName);
+ }
+ if(signerName) {
+ CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(signerName), kCFStringEncodingUTF8) + 1;
+ char* buf = (char*) malloc(maxLength);
+ if (buf) {
+ if (CFStringGetCString(signerName, buf, (CFIndex)maxLength, kCFStringEncodingUTF8)) {
+ tpOcspDebug("tpIsAuthorizedOcspSigner: signerCert \"%s\"", buf);
+ }
+ free(buf);
+ }
+ CFRelease(signerName);
+ }
+ if(issuerRef) {
+ CFRelease(issuerRef);
+ }
+ if(signerRef) {
+ CFRelease(signerRef);
+ }
+#endif
+ tpOcspDebug("tpIsAuthorizedOcspSigner: signer is not issued by issuerCert");
return false;
}
-
+
/* Fetch ExtendedKeyUse field from signerCert */
crtn = signerCert.fetchField(&CSSMOID_ExtendedKeyUsage, &fieldValue);
if(crtn) {
tpOcspDebug("tpIsAuthorizedOcspSigner: signer is issued by issuer, no EKU");
return false;
- }
+ }
CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
if(cssmExt->format != CSSM_X509_DATAFORMAT_PARSED) {
tpOcspDebug("tpIsAuthorizedOcspSigner: bad extension format");
goto errOut;
}
eku = (CE_ExtendedKeyUsage *)cssmExt->value.parsedValue;
-
+
/* Look for OID_KP_OCSPSigning */
for(unsigned dex=0; dex<eku->numPurposes; dex++) {
if(tpCompareCssmData(&eku->purposes[dex], &CSSMOID_OCSPSigning)) {
goto errOut;
}
- /*
- * OK, signerCert is authorized by *someone* to sign OCSP requests, and
- * it claims to be issued by issuer. Sig verify to be sure.
+ /*
+ * OK, signerCert is authorized by *someone* to sign OCSP requests, and
+ * it claims to be issued by issuer. Sig verify to be sure.
* FIXME this is not handling partial public keys, which would be a colossal
* mess to handle in this module...so we don't.
*/
return ourRtn;
}
-/*
- * Check ResponderID linkage between an OCSPResponse and a cert we believe to
+/*
+ * Check ResponderID linkage between an OCSPResponse and a cert we believe to
* be the issuer of both that response and the cert being verified. Returns
* true if OK.
*/
{
bool shouldBeSigner = false;
if(ocspResp.responderIDTag() == RIT_Name) {
- /*
+ /*
* Name inside response must == signer's SubjectName.
* Note we can't use signer.subjectName(); that's normalized.
*/
const CSSM_DATA *respIdName = ocspResp.encResponderName();
CSSM_DATA *subjectName = NULL;
- CSSM_RETURN crtn = signer.fetchField(&CSSMOID_X509V1SubjectNameStd,
+ CSSM_RETURN crtn = signer.fetchField(&CSSMOID_X509V1SubjectNameStd,
&subjectName);
if(crtn) {
/* bad cert */
}
else {
/* ResponderID.byKey must == SHA1(signer's public key) */
- const CSSM_KEY *pubKey = signer.pubKey();
+ const CSSM_KEY_PTR pubKey = signer.pubKey();
assert(pubKey != NULL);
uint8 digest[CC_SHA1_DIGEST_LENGTH];
CSSM_DATA keyHash = {CC_SHA1_DIGEST_LENGTH, digest};
- ocspdSha1(pubKey->KeyData.Data, (CC_LONG)pubKey->KeyData.Length, digest);
+ CSSM_DATA pubKeyBytes = {0, NULL};
+ ocspdGetPublicKeyBytes(NULL, pubKey, pubKeyBytes);
+ ocspdSha1(pubKeyBytes.Data, (CC_LONG)pubKeyBytes.Length, digest);
const CSSM_DATA *respKeyHash = &ocspResp.responderID().byKey;
if(tpCompareCssmData(&keyHash, respKeyHash)) {
tpOcspDebug("tpOcspResponderIDCheck: good ResponderID.byKey");
}
/*
- * Verify the signature of an OCSP response. Caller is responsible for all other
+ * Verify the signature of an OCSP response. Caller is responsible for all other
* verification of the response, this is just the crypto.
- * Returns true on success.
+ * Returns true on success.
*/
static bool tpOcspResponseSigVerify(
TPVerifyContext &vfyCtx,
const SecAsn1OCSPBasicResponse &basicResp = ocspResp.basicResponse();
const CSSM_OID *algOid = &basicResp.algId.algorithm;
CSSM_ALGORITHMS sigAlg;
-
+
if(!cssmOidToAlg(algOid, &sigAlg)) {
tpOcspDebug("tpOcspResponseSigVerify: unknown signature algorithm");
}
-
+
/* signer's public key from the cert */
const CSSM_KEY *pubKey = signer.pubKey();
-
+
/* signature: on decode, length is in BITS */
CSSM_DATA sig = basicResp.sig;
sig.Length /= 8;
-
+
CSSM_RETURN crtn;
CSSM_CC_HANDLE sigHand;
bool ourRtn = false;
typedef enum {
OCT_Local, // LocalResponder - no checking other than signature
OCT_Issuer, // it's the issuer of the cert being verified
- OCT_Provided, // came with response, provenance unknown
+ OCT_Provided, // came with response, provenance unknown
} OcspCertType;
/*
- * Did specified cert issue the OCSP response?
+ * Did specified cert issue the OCSP response?
*
* This implements the algorithm described in RFC2560, section 4.2.2.2,
- * "Authorized Responders". It sees if the cert could be the issuer of the
+ * "Authorized Responders". It sees if the cert could be the issuer of the
* OCSP response per that algorithm; then if it could, it performs signature
* verification.
*/
/* on input specify at least one of the following two */
const CSSM_DATA *signerData,
TPCertInfo *signer,
- OcspCertType certType, // where rawCert came from
+ OcspCertType certType, // where rawCert came from
TPCertInfo *issuer, // OPTIONAL, if known
TPCertInfo **signerRtn) // optionally RETURNED if at all possible
{
assert((signerData != NULL) || (signer != NULL));
-
+
/* get signer as TPCertInfo if caller hasn't provided */
TPCertInfo *tmpSigner = NULL;
if(signer == NULL) {
if(signerRtn != NULL) {
*signerRtn = signer;
}
-
- /*
+
+ /*
* Qualification of "this can be the signer" depends on where the
* signer came from.
*/
bool shouldBeSigner = false;
OcspIssuerStatus ourRtn = OIS_No;
-
+
switch(certType) {
case OCT_Local: // caller trusts this and thinks it's the signer
shouldBeSigner = true;
break;
case OCT_Provided:
{
- /*
+ /*
* This cert came with the response.
*/
if(issuer == NULL) {
- /*
- * careful, might not know the issuer...how would this path ever
+ /*
+ * careful, might not know the issuer...how would this path ever
* work then? I don't think it needs to because you can NOT
* do OCSP on a cert without its issuer in hand.
*/
if(!shouldBeSigner) {
goto errOut;
}
-
+
/* verify the signature */
if(tpOcspResponseSigVerify(vfyCtx, ocspResp, *signer)) {
ourRtn = OIS_Good;
}
-
+
errOut:
if((signerRtn == NULL) && (tmpSigner != NULL)) {
delete tmpSigner;
OcspRespStatus tpVerifyOcspResp(
TPVerifyContext &vfyCtx,
SecNssCoder &coder,
- TPCertInfo *issuer, // issuer of the related cert, may be issuer of
- // reply, may not be known
+ TPCertInfo *issuer, // issuer of the related cert, may be issuer of
+ // reply, may not be known
OCSPResponse &ocspResp,
- CSSM_RETURN &cssmErr) // possible per-cert error
+ CSSM_RETURN &cssmErr) // possible per-cert error
{
OcspRespStatus ourRtn = ORS_Unknown;
CSSM_RETURN crtn;
-
+
tpOcspDebug("tpVerifyOcspResp top");
-
+
switch(ocspResp.responseStatus()) {
- case RS_Success:
- crtn = CSSM_OK;
+ case RS_Success:
+ crtn = CSSM_OK;
break;
case RS_MalformedRequest:
crtn = CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ;
return ORS_Unknown;
}
cssmErr = CSSM_OK;
-
+
/* one of our main jobs is to locate the signer of the response, here */
TPCertInfo *signerInfo = NULL;
TPCertInfo *signerInfoTBD = NULL; // if non NULL at end, we delete
CSSM_BOOL verifiedToRoot;
CSSM_BOOL verifiedToAnchor;
CSSM_BOOL verifiedViaTrustSetting;
-
+
const CSSM_APPLE_TP_OCSP_OPTIONS *ocspOpts = vfyCtx.ocspOpts;
OcspIssuerStatus issuerStat;
-
- /*
+
+ /*
* Set true if we ever find an apparent issuer which does not correctly
* pass signature verify. If true and we never success, that's a XXX error.
*/
bool foundBadIssuer = false;
bool foundLocalResponder = false;
uint32 numSignerCerts = ocspResp.numSignerCerts();
-
+
/*
- * This cert group, allocated by AppleTPSession::CertGroupVerify(),
+ * This cert group, allocated by AppleTPSession::CertGroupVerify(),
* serves two functions here:
*
* -- it accumulates certs we get from the net (as parts of OCSP responses)
* for user in verifying OCSPResponse-related certs.
- * TPCertGroup::buildCertGroup() uses this group as one of the many
+ * TPCertGroup::buildCertGroup() uses this group as one of the many
* sources of certs when building a cert chain.
*
- * -- it provides a container into which to stash TPCertInfos which
+ * -- it provides a container into which to stash TPCertInfos which
* persist at least as long as the TPVerifyContext; it's of type TGO_Group,
- * so all of the certs added to it get freed when the group does.
+ * so all of the certs added to it get freed when the group does.
*/
assert(vfyCtx.signerCerts != NULL);
-
+
TPCertGroup &gatheredCerts = vfyCtx.gatheredCerts;
-
+
/* set up for disposal of TPCertInfos created by TPCertGroup::buildCertGroup() */
TPCertGroup certsToBeFreed(vfyCtx.alloc, TGO_Group);
-
- /*
+
+ /*
* First job is to find the cert which signed this response.
* Give priority to caller's LocalResponderCert.
*/
if((ocspOpts != NULL) && (ocspOpts->LocalResponderCert != NULL)) {
TPCertInfo *responderInfo = NULL;
- issuerStat = tpIsOcspIssuer(vfyCtx, ocspResp,
+ issuerStat = tpIsOcspIssuer(vfyCtx, ocspResp,
ocspOpts->LocalResponderCert, NULL,
OCT_Local, issuer, &responderInfo);
switch(issuerStat) {
break;
}
}
-
+
if((signerInfo == NULL) && (numSignerCerts != 0)) {
- /*
+ /*
* App did not specify a local responder (or provided a bad one)
* and the response came with some certs. Try those.
*/
const CSSM_DATA *certData = ocspResp.signerCert(dex);
if(signerInfo == NULL) {
/* stop trying this after we succeed... */
- issuerStat = tpIsOcspIssuer(vfyCtx, ocspResp,
+ issuerStat = tpIsOcspIssuer(vfyCtx, ocspResp,
certData, NULL,
OCT_Provided, issuer, &respCert);
switch(issuerStat) {
}
}
else {
- /*
+ /*
* At least add this cert to certGroup for verification.
* OcspCert will own the TPCertInfo.
*/
}
}
}
-
+
if((signerInfo == NULL) && (issuer != NULL)) {
- /*
+ /*
* Haven't found it yet, try the actual issuer
*/
- issuerStat = tpIsOcspIssuer(vfyCtx, ocspResp,
- NULL, issuer,
+ issuerStat = tpIsOcspIssuer(vfyCtx, ocspResp,
+ NULL, issuer,
OCT_Issuer, issuer, NULL);
switch(issuerStat) {
case OIS_BadSig:
break;
}
}
-
+
if(signerInfo == NULL) {
if((issuer != NULL) && !issuer->isStatusFatal(CSSMERR_APPLETP_OCSP_NO_SIGNER)) {
/* user wants to proceed without verifying! */
ourRtn = ORS_Good;
goto errOut;
}
-
- /*
- * Last remaining task is to verify the signer, and all the certs back to
- * an anchor
+
+ /*
+ * Last remaining task is to verify the signer, and all the certs back to
+ * an anchor
*/
/* start from scratch with both of these groups */
vfyCtx.policyStr,
vfyCtx.policyStrLen,
kSecTrustSettingsKeyUseSignRevocation,
- verifiedToRoot,
+ verifiedToRoot,
verifiedToAnchor,
verifiedViaTrustSetting);
if(crtn) {
tpOcspDebug("tpVerifyOcspResp SUCCESS; chain verified");
ourRtn = ORS_Good;
}
-
+
/* FIXME policy verify? */
-
+
errOut:
delete signerInfoTBD;
/* any other cleanup? */
/*
* Copyright (c) 2004,2011-2012,2014 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,
* 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@
*/
/*
* tpOcspVerify.cpp - top-level OCSP verification
*/
-
+
#include "tpOcspVerify.h"
#include "tpdebugging.h"
#include "ocspRequest.h"
#pragma mark ---- private routines ----
-/*
+/*
* Get a smart CertID for specified cert and issuer
*/
static CSSM_RETURN tpOcspGetCertId(
CSSM_DATA_PTR issuerSubject = NULL;
CSSM_DATA_PTR issuerPubKeyData = NULL;
CSSM_KEY_PTR issuerPubKey;
+ CSSM_DATA issuerPubKeyBytes;
CSSM_DATA_PTR subjectSerial = NULL;
-
+
crtn = subject.fetchField(&CSSMOID_X509V1SerialNumber, &subjectSerial);
if(crtn) {
return crtn;
}
assert(issuerPubKeyData->Length == sizeof(CSSM_KEY));
issuerPubKey = (CSSM_KEY_PTR)issuerPubKeyData->Data;
- certID = new OCSPClientCertID(*issuerSubject, issuerPubKey->KeyData, *subjectSerial);
-
+ ocspdGetPublicKeyBytes(NULL, issuerPubKey, issuerPubKeyBytes);
+ certID = new OCSPClientCertID(*issuerSubject, issuerPubKeyBytes, *subjectSerial);
+
subject.freeField(&CSSMOID_X509V1SerialNumber, subjectSerial);
issuer.freeField(&CSSMOID_X509V1IssuerNameStd, issuerSubject);
issuer.freeField(&CSSMOID_CSSMKeyStruct, issuerPubKeyData);
return CSSM_OK;
}
-/*
+/*
* Examine cert, looking for AuthorityInfoAccess, with id-ad-ocsp URIs. Create
- * an NULL_terminated array of CSSM_DATAs containing the URIs if found.
+ * an NULL_terminated array of CSSM_DATAs containing the URIs if found.
*/
static CSSM_DATA **tpOcspUrlsFromCert(
- TPCertInfo &subject,
+ TPCertInfo &subject,
SecNssCoder &coder)
{
CSSM_DATA_PTR extField = NULL;
CSSM_RETURN crtn;
-
+
crtn = subject.fetchField(&CSSMOID_AuthorityInfoAccess, &extField);
if(crtn) {
tpOcspDebug("tpOcspUrlsFromCert: no AIA extension");
tpErrorLog("tpOcspUrlsFromCert: malformed CSSM_X509_EXTENSION");
return NULL;
}
-
- CE_AuthorityInfoAccess *aia =
+
+ CE_AuthorityInfoAccess *aia =
(CE_AuthorityInfoAccess *)cssmExt->value.parsedValue;
CSSM_DATA **urls = NULL;
unsigned numUrls = 0;
tpErrorLog("tpOcspUrlsFromCert: CSSMOID_AD_OCSP, but not type URI");
continue;
}
-
+
/* got one */
if(urls == NULL) {
urls = coder.mallocn<CSSM_DATA_PTR>(2);
return urls;
}
-/*
+/*
* Create an SecAsn1OCSPDRequest for one cert. This consists of:
*
* -- cooking up an OCSPRequest if net fetch is enabled or a local responder
OCSPClientCertID *certID = NULL;
CSSM_RETURN crtn;
bool deleteCertID = false;
-
+
/* gather options or their defaults */
CSSM_APPLE_TP_OCSP_OPT_FLAGS optFlags = 0;
const CSSM_APPLE_TP_OCSP_OPTIONS *ocspOpts = vfyCtx.ocspOpts;
}
bool genNonce = optFlags & CSSM_TP_OCSP_GEN_NONCE ? true : false;
bool requireRespNonce = optFlags & CSSM_TP_OCSP_REQUIRE_RESP_NONCE ? true : false;
-
- /*
+
+ /*
* One degenerate case in case we can't really do anything.
* If no URI and no local responder, only proceed if cache is not disabled
* and we're requiring full OCSP per cert.
*/
if( ( (optFlags & CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE) ||
!(optFlags & CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT)
- ) &&
- (localResponder == NULL) &&
+ ) &&
+ (localResponder == NULL) &&
(urls == NULL)) {
tpOcspDebug("tpGenOcspdReq: no route to OCSP; NULL return");
return NULL;
}
}
}
-
+
/* certID needed one way or the other */
if(certID == NULL) {
crtn = tpOcspGetCertId(subject, issuer, certID);
}
deleteCertID = true;
}
-
+
/*
* Create the SecAsn1OCSPDRequest. All fields optional.
*/
ocspdReq->cacheWriteDisable->Data[0] = 0xff;
ocspdReq->cacheWriteDisable->Length = 1;
}
- /*
- * Note we're enforcing a not-so-obvious policy here: if nonce match is
- * required, disk cache reads by ocspd are disabled. In-core cache is
- * still enabled and hits in that cache do NOT require nonce matches.
+ /*
+ * Note we're enforcing a not-so-obvious policy here: if nonce match is
+ * required, disk cache reads by ocspd are disabled. In-core cache is
+ * still enabled and hits in that cache do NOT require nonce matches.
*/
if((optFlags & CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE) || requireRespNonce) {
ocspdReq->cacheReadDisable = coder.mallocn<CSSM_DATA>();
if(!(optFlags & CSSM_TP_ACTION_OCSP_DISABLE_NET)) {
ocspdReq->urls = const_cast<CSSM_DATA **>(urls);
}
-
+
errOut:
delete ocspReq;
if(deleteCertID) {
{
// Return true if the revocation time is after the specified verification time (i.e. "good")
// If verifyTime not specified, use now for the verifyTime
-
+
CFAbsoluteTime verifyTime = 0;
-
+
if (verifyTimeStr)
{
CFDateRef cfVerifyTime = NULL; // made with CFDateCreate
CFRelease(cfVerifyTime);
}
}
-
+
if (verifyTime == 0)
verifyTime = CFAbsoluteTimeGetCurrent();
{
SecAsn1OCSPCertStatusTag certStatus = singleResp.certStatus();
CSSM_RETURN crtn = CSSM_OK;
- if ((certStatus == CS_Revoked) &&
+ if ((certStatus == CS_Revoked) &&
revocationTimeAfterVerificationTime(singleResp.revokedTime(), verifyTime))
{
tpOcspDebug("tpApplySingleResp: CS_Revoked for cert %u, but revoked after verification time", dex);
certStatus = CS_Good;
}
-
+
switch(certStatus) {
case CS_Good:
tpOcspDebug("tpApplySingleResp: CS_Good for cert %u", dex);
tpOcspDebug("tpApplySingleResp: CS_Unknown for cert %u", dex);
break;
default:
- tpOcspDebug("tpApplySingleResp: BAD certStatus (%d) for cert %u",
+ tpOcspDebug("tpApplySingleResp: BAD certStatus (%d) for cert %u",
(int)certStatus, dex);
if(cert.addStatusCode(CSSMERR_APPLETP_OCSP_STATUS_UNRECOGNIZED)) {
crtn = CSSMERR_APPLETP_OCSP_STATUS_UNRECOGNIZED;
return crtn;
}
-/*
+/*
* An exceptional case: synchronously flush the OCSPD cache and send a new
- * resquest for just this one cert.
+ * resquest for just this one cert.
*/
static OCSPResponse *tpOcspFlushAndReFetch(
- TPVerifyContext &vfyCtx,
- SecNssCoder &coder,
+ TPVerifyContext &vfyCtx,
+ SecNssCoder &coder,
TPCertInfo &subject,
- TPCertInfo &issuer,
+ TPCertInfo &issuer,
OCSPClientCertID &certID)
{
const CSSM_DATA *derCertID = certID.encode();
CSSM_RETURN crtn;
-
+
crtn = ocspdCacheFlush(*derCertID);
if(crtn) {
#ifndef NDEBUG
#endif
return NULL;
}
-
+
/* Cook up an OCSPDRequests, one request, just for this */
/* send it to ocsdp */
/* munge reply into an OCSPRsponse and return it */
CSSM_DATA **u,
unsigned dex);
~PendingRequest() {}
-
+
TPCertInfo &subject;
TPCertInfo &issuer;
OCSPClientCertID &certID; // owned by caller
CSSM_DATA **urls; // owner-managed array of URLs obtained from subject's
- // AuthorityInfoAccess.id-ad-ocsp.
- CSSM_DATA nonce; // owner-managed copy of this requests' nonce, if it
- // has one
+ // AuthorityInfoAccess.id-ad-ocsp.
+ CSSM_DATA nonce; // owner-managed copy of this requests' nonce, if it
+ // has one
unsigned dex; // in inputCerts, for debug
bool processed;
};
OCSPClientCertID &cid,
CSSM_DATA **u,
unsigned dx)
- : subject(subj), issuer(iss), certID(cid),
+ : subject(subj), issuer(iss), certID(cid),
urls(u), dex(dx), processed(false)
{
nonce.Data = NULL;
CSSM_RETURN tpVerifyCertGroupWithOCSP(
TPVerifyContext &vfyCtx,
- TPCertGroup &certGroup) // to be verified
+ TPCertGroup &certGroup) // to be verified
{
assert(vfyCtx.clHand != 0);
assert(vfyCtx.policy == kRevokeOcsp);
-
+
CSSM_RETURN ourRtn = CSSM_OK;
OcspRespStatus respStat;
SecNssCoder coder;
return CSSM_OK;
}
numCerts--;
-
+
/* gather options or their defaults */
CSSM_APPLE_TP_OCSP_OPT_FLAGS optFlags = 0;
const CSSM_APPLE_TP_OCSP_OPTIONS *ocspOpts = vfyCtx.ocspOpts;
bool genNonce = false; // in outgoing request
bool requireRespNonce = false; // in incoming response
PRErrorCode prtn;
-
+
if(ocspOpts != NULL) {
optFlags = vfyCtx.ocspOpts->Flags;
localResponder = ocspOpts->LocalResponder;
tpErrorLog("tpVerifyCertGroupWithOCSP: requireRespNonce, !genNonce\n");
return CSSMERR_TP_INVALID_REQUEST_INPUTS;
}
-
- tpOcspDebug("tpVerifyCertGroupWithOCSP numCerts %u optFlags 0x%lx",
+
+ tpOcspDebug("tpVerifyCertGroupWithOCSP numCerts %u optFlags 0x%lx",
numCerts, (unsigned long)optFlags);
-
+
/*
* create list of pendingRequests parallel to certGroup
*/
PendingRequest **pending = coder.mallocn<PendingRequest *>(numCerts);
memset(pending, 0, (numCerts * sizeof(PendingRequest *)));
-
+
for(unsigned dex=0; dex<numCerts; dex++) {
OCSPClientCertID *certID = NULL;
TPCertInfo *subject = certGroup.certAtIndex(dex);
-
+
if(subject->trustSettingsFound()) {
/* functionally equivalent to root - we're done */
- tpOcspDebug("...tpVerifyCertGroupWithOCSP: terminate per user trust at %u",
+ tpOcspDebug("...tpVerifyCertGroupWithOCSP: terminate per user trust at %u",
(unsigned)dex);
goto postOcspd;
}
"aborting\n");
goto errOut;
}
-
- /*
+
+ /*
* We use the URLs in the subject cert's AuthorityInfoAccess extension
* for two things - mainly to get the URL(s) for actual OCSP transactions,
* but also for CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT processing.
* So, we do the per-cert processing to get them right now even if we
- * wind up using a local responder or getting verification from cache.
+ * wind up using a local responder or getting verification from cache.
*/
CSSM_DATA **urls = tpOcspUrlsFromCert(*subject, coder);
pending[dex] = new PendingRequest(*subject, *issuer, *certID, urls, dex);
}
/* subsequent errors to errOut: */
-
- /*
- * Create empty SecAsn1OCSPDRequests big enough for all certs
+
+ /*
+ * Create empty SecAsn1OCSPDRequests big enough for all certs
*/
ocspdReqs.requests = coder.mallocn<SecAsn1OCSPDRequest *>(numCerts + 1);
memset(ocspdReqs.requests, 0, (numCerts + 1) * sizeof(SecAsn1OCSPDRequest *));
ocspdReqs.version.Data = &version;
ocspdReqs.version.Length = 1;
-
- /*
+
+ /*
* For each cert, either obtain a cached OCSPResponse, or create
- * a request to get one.
+ * a request to get one.
*
* NOTE: in-core cache reads (via tpOcspCacheLookup() do NOT involve a
* nonce check, no matter what the app says. If nonce checking is required by the
* app, responses don't get added to cache if the nonce doesn't match, but once
- * a response is validated and added to cache it's fair game for that task.
+ * a response is validated and added to cache it's fair game for that task.
*/
for(unsigned dex=0; dex<numCerts; dex++) {
PendingRequest *pendReq = pending[dex];
singleResp = tpOcspCacheLookup(pendReq->certID, localResponder);
}
if(singleResp) {
- tpOcspDebug("...tpVerifyCertGroupWithOCSP: localCache hit (1) dex %u",
+ tpOcspDebug("...tpVerifyCertGroupWithOCSP: localCache hit (1) dex %u",
(unsigned)dex);
crtn = tpApplySingleResp(*singleResp, pendReq->subject, dex, optFlags,
vfyCtx.verifyTime, pendReq->processed);
continue;
}
if(crtn) {
- /*
- * This indicates a bad cached response. Well that's kinda weird, let's
+ /*
+ * This indicates a bad cached response. Well that's kinda weird, let's
* just flush this out and try a normal transaction.
*/
tpOcspCacheFlush(pendReq->certID);
}
}
-
- /*
+
+ /*
* Prepare a request for ocspd
*/
- SecAsn1OCSPDRequest *ocspdReq = tpGenOcspdReq(vfyCtx, coder,
- pendReq->subject, pendReq->issuer, pendReq->certID,
+ SecAsn1OCSPDRequest *ocspdReq = tpGenOcspdReq(vfyCtx, coder,
+ pendReq->subject, pendReq->issuer, pendReq->certID,
const_cast<const CSSM_DATA **>(pendReq->urls),
pendReq->nonce);
if(ocspdReq == NULL) {
/* no candidates for OCSP: almost done */
goto postOcspd;
}
-
+
/* ship requests off to ocspd, get ocspReplies back */
if(coder.encodeItem(&ocspdReqs, kSecAsn1OCSPDRequestsTemplate, derOcspdRequests)) {
tpErrorLog("tpVerifyCertGroupWithOCSP: error encoding ocspdReqs\n");
#ifndef NDEBUG
cssmPerror("ocspdFetch", crtn);
#endif
- /* But this is not necessarily fatal...update per-cert status and check
+ /* But this is not necessarily fatal...update per-cert status and check
* caller requirements below */
goto postOcspd;
}
memset(&ocspdReplies, 0, sizeof(ocspdReplies));
- prtn = coder.decodeItem(derOcspdReplies, kSecAsn1OCSPDRepliesTemplate,
+ prtn = coder.decodeItem(derOcspdReplies, kSecAsn1OCSPDRepliesTemplate,
&ocspdReplies);
/* we're done with this, mallocd in ocspdFetch() */
vfyCtx.alloc.free(derOcspdReplies.Data);
if(prtn) {
- /*
+ /*
* This can happen when an OCSP server provides bad data...we cannot
* determine which cert is associated with this bad response;
* just flag it with the first one and proceed to the loop that
}
goto errOut;
}
-
+
/* process each reply */
numReplies = ocspdArraySize((const void **)ocspdReplies.replies);
for(unsigned dex=0; dex<numReplies; dex++) {
SecAsn1OCSPDReply *reply = ocspdReplies.replies[dex];
-
+
/* Cook up our version of an OCSPResponse from the encoded data */
OCSPResponse *ocspResp = NULL;
try {
/* what the heck, keep going */
continue;
}
-
- /*
- * Find matching subject cert if possible (it's technically optional for
+
+ /*
+ * Find matching subject cert if possible (it's technically optional for
* verification of the response in some cases, e.g., local responder).
*/
PendingRequest *pendReq = NULL; // fully qualified
PendingRequest *reqWithIdMatch = NULL; // CertID match only, not nonce
for(unsigned pdex=0; pdex<numCerts; pdex++) {
-
+
/* first check ID match; that is required no matter what */
if((pending[pdex])->certID.compareToExist(reply->certID)) {
reqWithIdMatch = pending[pdex];
if(!genNonce) {
/* that's good enough */
pendReq = reqWithIdMatch;
- tpOcspDebug("OCSP processs reply: CertID match, no nonce");
+ tpOcspDebug("OCSP process reply: CertID match, no nonce");
break;
}
if(tpCompareCssmData(&reqWithIdMatch->nonce, ocspResp->nonce())) {
- tpOcspDebug("OCSP processs reply: nonce MATCH");
+ tpOcspDebug("OCSP process reply: nonce MATCH");
pendReq = reqWithIdMatch;
break;
}
-
+
/*
- * In this case we keep going; if we never find a match, then we can
+ * In this case we keep going; if we never find a match, then we can
* use reqWithIdMatch if !requireRespNonce.
*/
- tpOcspDebug("OCSP processs reply: certID match, nonce MISMATCH");
+ tpOcspDebug("OCSP process reply: certID match, nonce MISMATCH");
}
if(pendReq == NULL) {
if(requireRespNonce) {
- tpOcspDebug("OCSP processs reply: tossing out response due to "
+ tpOcspDebug("OCSP process reply: tossing out response due to "
"requireRespNonce");
delete ocspResp;
if(ourRtn == CSSM_OK) {
* Nonce mismatch but caller thinks that's OK. Log it and proceed.
*/
assert(genNonce);
- tpOcspDebug("OCSP processs reply: using bad nonce due to !requireRespNonce");
+ tpOcspDebug("OCSP process reply: using bad nonce due to !requireRespNonce");
pendReq = reqWithIdMatch;
pendReq->subject.addStatusCode(CSSMERR_APPLETP_OCSP_NONCE_MISMATCH);
}
if(pendReq != NULL) {
issuer = &pendReq->issuer;
}
-
+
/* verify response and either throw out or add to local cache */
respStat = tpVerifyOcspResp(vfyCtx, coder, issuer, *ocspResp, crtn);
switch(respStat) {
continue;
case ORS_Bad:
delete ocspResp;
- /*
- * An exceptional case: synchronously flush the OCSPD cache and send a
- * new request for just this one cert.
+ /*
+ * An exceptional case: synchronously flush the OCSPD cache and send a
+ * new request for just this one cert.
* FIXME: does this really buy us anything? A DOS attacker who managed
* to get this bogus response into our cache is likely to be able
* to do it again and again.
goto errOut;
}
/* Voila! Recovery. Proceed. */
- tpOcspDebug("tpVerifyCertGroupWithOCSP: refetch for cert %u SUCCEEDED",
+ tpOcspDebug("tpVerifyCertGroupWithOCSP: refetch for cert %u SUCCEEDED",
dex);
break;
} /* switch response status */
-
+
if(!cacheWriteDisable) {
tpOcspCacheAdd(reply->ocspResp, localResponder);
}
-
+
/* attempt to apply to pendReq */
if(pendReq != NULL) {
- OCSPSingleResponse *singleResp =
+ OCSPSingleResponse *singleResp =
ocspResp->singleResponseFor(pendReq->certID);
if(singleResp) {
- crtn = tpApplySingleResp(*singleResp, pendReq->subject, pendReq->dex,
+ crtn = tpApplySingleResp(*singleResp, pendReq->subject, pendReq->dex,
optFlags, vfyCtx.verifyTime, pendReq->processed);
if(crtn && (ourRtn == CSSM_OK)) {
ourRtn = crtn;
delete singleResp;
}
} /* a reply which matches a pending request */
-
- /*
+
+ /*
* Done with this - note local OCSP response cache doesn't store this
* object; it stores an encoded copy.
*/
postOcspd:
- /*
- * Now process each cert which hasn't had an OCSP response applied to it.
- * This can happen if we get back replies which are not strictly in 1-1 sync with
+ /*
+ * Now process each cert which hasn't had an OCSP response applied to it.
+ * This can happen if we get back replies which are not strictly in 1-1 sync with
* our requests but which nevertheless contain valid info for more than one
* cert each.
*/
PendingRequest *pendReq = pending[dex];
if(pendReq == NULL) {
/* i.e. terminated due to user trust */
- tpOcspDebug("...tpVerifyCertGroupWithOCSP: NULL pendReq dex %u",
+ tpOcspDebug("...tpVerifyCertGroupWithOCSP: NULL pendReq dex %u",
(unsigned)dex);
break;
}
singleResp = tpOcspCacheLookup(pendReq->certID, localResponder);
}
if(singleResp) {
- tpOcspDebug("...tpVerifyCertGroupWithOCSP: localCache (2) hit dex %u",
+ tpOcspDebug("...tpVerifyCertGroupWithOCSP: localCache (2) hit dex %u",
(unsigned)dex);
- crtn = tpApplySingleResp(*singleResp, pendReq->subject, dex, optFlags,
+ crtn = tpApplySingleResp(*singleResp, pendReq->subject, dex, optFlags,
vfyCtx.verifyTime, pendReq->processed);
if(crtn) {
if(ourRtn == CSSM_OK) {
}
}
}
-errOut:
+errOut:
for(unsigned dex=0; dex<numCerts; dex++) {
PendingRequest *pendReq = pending[dex];
if(pendReq == NULL) {
return values;
}
+
+Boolean SecTaskEntitlementsValidated(SecTaskRef task) {
+ // TODO: Cache the result
+ uint32_t csflags = 0;
+ const uint32_t mask = CS_VALID | CS_KILL | CS_ENTITLEMENTS_VALIDATED;
+ int rc = csops_task(task, CS_OPS_STATUS, &csflags, sizeof(csflags));
+ return rc != -1 && ((csflags & mask) == mask);
+}
/*
- * Copyright (c) 2013 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
*/
OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement);
+/*!
+ @function SecTaskEntitlementsValidated
+ @abstract Check whether entitlements can be trusted or not. If this returns
+ false the tasks entitlements must not be used for anything security sensetive.
+ @param task A previously created SecTask object
+*/
+Boolean SecTaskEntitlementsValidated(SecTaskRef task);
+
#if defined(__cplusplus)
}
#endif
void SecStaticCode::validateDirectory()
{
// echo previous outcome, if any
- if (!validated())
+ // track revocation separately, as it may not have been checked
+ // during the initial validation
+ if (!validated() || ((mValidationFlags & kSecCSEnforceRevocationChecks) && !revocationChecked()))
try {
// perform validation (or die trying)
CODESIGN_EVAL_STATIC_DIRECTORY(this);
mValidationExpired = verifySignature();
+ if (mValidationFlags & kSecCSEnforceRevocationChecks)
+ mRevocationChecked = true;
+
for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
if (mCache[slot]) // if we already loaded that resource...
validateComponent(slot, errorForSlot(slot)); // ... then check it now
void resetValidity(); // clear validation caches (if something may have changed)
bool validated() const { return mValidated; }
+ bool revocationChecked() const { return mRevocationChecked; }
bool valid() const
{ assert(validated()); return mValidated && (mValidationResult == errSecSuccess); }
bool validatedExecutable() const { return mExecutableValidated; }
// master validation state
bool mValidated; // core validation was attempted
+ bool mRevocationChecked; // the signature was checked for revocation
OSStatus mValidationResult; // outcome of core validation
bool mValidationExpired; // outcome had expired certificates
_SecTaskCreateFromSelf
_SecTaskCopyValueForEntitlement
_SecTaskCopyValuesForEntitlements
+_SecTaskEntitlementsValidated
_SecTaskValidateForRequirement
# Assessments
18B965951472FE30005A4D2E /* cdbuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383150A237F47005C63A2 /* cdbuilder.h */; settings = {ATTRIBUTES = (Public, ); }; };
37DDE33C1947A4F3005CE18B /* dirscanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 37DDE33B1947A4F3005CE18B /* dirscanner.h */; };
37DDE3421947A501005CE18B /* dirscanner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37DDE3411947A501005CE18B /* dirscanner.cpp */; };
+ 48674DE319EC9E610049EB7D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48674DE219EC9E610049EB7D /* Security.framework */; };
7A4FAF1B19C215DF00D297CB /* CrashReporterSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A4FAF1A19C215DF00D297CB /* CrashReporterSupport.framework */; };
7A9DA65C1948D1BA004635E6 /* opaquewhitelist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A9DA65A1948D1BA004635E6 /* opaquewhitelist.cpp */; };
7A9DA65D1948D1BA004635E6 /* opaquewhitelist.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A9DA65B1948D1BA004635E6 /* opaquewhitelist.h */; };
184461A2146E9AD100B12992 /* release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = release.xcconfig; sourceTree = "<group>"; };
37DDE33B1947A4F3005CE18B /* dirscanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dirscanner.h; sourceTree = "<group>"; };
37DDE3411947A501005CE18B /* dirscanner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dirscanner.cpp; sourceTree = "<group>"; };
+ 48674DE219EC9E610049EB7D /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = "../../../Volumes/Data/Users/murf/Projects/Security/build/Debug-iphoneos/Security.framework"; sourceTree = "<group>"; };
4CA1FEBE052A3C8100F22E42 /* libsecurity_codesigning.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libsecurity_codesigning.a; sourceTree = BUILT_PRODUCTS_DIR; };
7A4FAF1A19C215DF00D297CB /* CrashReporterSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CrashReporterSupport.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.Internal.sdk/System/Library/PrivateFrameworks/CrashReporterSupport.framework; sourceTree = DEVELOPER_DIR; };
7A9DA65A1948D1BA004635E6 /* opaquewhitelist.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opaquewhitelist.cpp; sourceTree = "<group>"; };
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 48674DE319EC9E610049EB7D /* Security.framework in Frameworks */,
7A4FAF1B19C215DF00D297CB /* CrashReporterSupport.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
C2CC30EF0B8519CF005FA59D /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 48674DE219EC9E610049EB7D /* Security.framework */,
C200424915D425B7004AE0A1 /* libsecurity_codesigning.a */,
C200424A15D425B7004AE0A1 /* libsecurity_utilities.a */,
C2CC30A00B8519CC005FA59D /* CoreFoundation.framework */,
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "/Volumes/Data/Users/murf/Projects/Security/build/Debug-iphoneos",
);
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "/Volumes/Data/Users/murf/Projects/Security/build/Debug-iphoneos",
);
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
* Copyright (c) 2002-2007,2011-2014 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,
* 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@
*/
#include <vector>
#include <CommonCrypto/CommonDigestSPI.h>
#include <SecBase.h>
+#include <libDER/libDER.h>
+#include <libDER/DER_Decode.h>
using namespace KeychainCore;
mV1SubjectPublicKeyCStructValue(NULL),
mV1SubjectNameCStructValue(NULL),
mV1IssuerNameCStructValue(NULL),
- mSha1Hash(NULL)
+ mSha1Hash(NULL),
+ mEncodingVerified(false)
{
if (data.Length == 0 || data.Data == NULL)
MacOSError::throwMe(errSecParam);
mV1SubjectPublicKeyCStructValue(NULL),
mV1SubjectNameCStructValue(NULL),
mV1IssuerNameCStructValue(NULL),
- mSha1Hash(NULL)
+ mSha1Hash(NULL),
+ mEncodingVerified(false)
{
}
mV1SubjectPublicKeyCStructValue(NULL),
mV1SubjectNameCStructValue(NULL),
mV1IssuerNameCStructValue(NULL),
- mSha1Hash(NULL)
+ mSha1Hash(NULL),
+ mEncodingVerified(false)
{
// @@@ In this case we don't know the type...
}
mV1SubjectPublicKeyCStructValue(NULL),
mV1SubjectNameCStructValue(NULL),
mV1IssuerNameCStructValue(NULL),
- mSha1Hash(NULL)
+ mSha1Hash(NULL),
+ mEncodingVerified(false)
{
}
mPopulated = true;
}
+bool
+Certificate::verifyEncoding(CSSM_DATA_PTR data)
+{
+ bool verified = false;
+ CSSM_SIZE verifiedLength = 0;
+ {
+ StLock<Mutex>_(mMutex);
+ if (!data || !data->Data || !data->Length) {
+ mEncodingVerified = false;
+ return false;
+ }
+ verified = mEncodingVerified;
+ if (verified) {
+ return true;
+ }
+
+ // Note: the Certificate class supports X509v1 through X509v3 certs,
+ // with CSSM_CERT_ENCODING_BER or CSSM_CERT_ENCODING_DER encoding.
+ // Any other types/encodings would need additional verification code here.
+
+ if (mHaveTypeAndEncoding) {
+ if (mType < CSSM_CERT_X_509v1 || mType > CSSM_CERT_X_509v3) {
+ secdebug("Certificate", "verifyEncoding: certificate has custom type (%d)", (int)mType);
+ }
+ if (mEncoding < CSSM_CERT_ENCODING_BER || mEncoding > CSSM_CERT_ENCODING_DER) {
+ secdebug("Certificate", "verifyEncoding: certificate has custom encoding (%d)", (int)mEncoding);
+ }
+ }
+
+ // attempt to decode the top-level ASN.1 sequence
+ const DERItem der = { (DERByte *)data->Data, (DERSize)data->Length };
+ DERDecodedInfo derInfo;
+ // sanity check the first byte to avoid decoding a non-DER blob
+ if ((DERByte)0x30 != *(der.data)) {
+ return false;
+ }
+ DERReturn drtn = DERDecodeItem(&der, &derInfo);
+ if (drtn == DR_Success) {
+ CSSM_SIZE tagLength = (CSSM_SIZE)((uintptr_t)derInfo.content.data - (uintptr_t)der.data);
+ CSSM_SIZE derLength = (CSSM_SIZE)derInfo.content.length + tagLength;
+ if (derLength != data->Length) {
+ secdebug("Certificate", "Certificate DER length is %d, but data length is %d",
+ (int)derLength, (int)data->Length);
+ // will adjust data size if DER length is positive, but smaller than actual length
+ if ((derLength > 0) && (derLength < data->Length)) {
+ verifiedLength = derLength;
+ secdebug("Certificate", "Will adjust certificate data length to %d",
+ (int)derLength);
+ }
+ else {
+ secdebug("Certificate", "Certificate encoding invalid (DER length is %d)",
+ (int)derLength);
+ return false;
+ }
+ }
+ verified = mEncodingVerified = true;
+ }
+ else {
+ // failure to decode provided data as DER sequence
+ secdebug("Certificate", "Certificate not in DER encoding (error %d)",
+ (int)drtn);
+ return false;
+ }
+ }
+
+ if (verifiedLength > 0) {
+ // setData acquires the mMutex lock, so we call it while not holding the lock
+ setData((UInt32)verifiedLength, data->Data);
+ secdebug("Certificate", "Adjusted certificate data length to %d",
+ (int)verifiedLength);
+ }
+
+ return verified;
+}
+
const CssmData &
Certificate::data()
{
- StLock<Mutex>_(mMutex);
- CssmDataContainer *data = mData.get();
- if (!data && mKeychain)
+ CssmDataContainer *data = NULL;
+ bool hasKeychain = false;
+ bool verified = false;
+ {
+ StLock<Mutex>_(mMutex);
+ data = mData.get();
+ hasKeychain = (mKeychain != NULL);
+ verified = mEncodingVerified;
+ }
+
+ // If data has been set but not yet verified, verify it now.
+ if (!verified && data) {
+ // verifyEncoding might modify mData, so refresh the data container
+ verified = verifyEncoding(data);
+ {
+ StLock<Mutex>_(mMutex);
+ data = mData.get();
+ }
+ }
+
+ // If data isn't set at this point, try to read it from the db record
+ if (!data && hasKeychain)
{
// Make sure mUniqueId is set.
dbUniqueRecord();
CssmDataContainer _data;
- mData = NULL;
- /* new data allocated by CSPDL, implicitly freed by CssmDataContainer */
- mUniqueId->get(NULL, &_data);
+ {
+ StLock<Mutex>_(mMutex);
+ mData = NULL;
+ /* new data allocated by CSPDL, implicitly freed by CssmDataContainer */
+ mUniqueId->get(NULL, &_data);
+ }
/* this saves a copy to be freed at destruction and to be passed to caller */
setData((UInt32)_data.length(), _data.data());
- return *mData.get();
+ // verifyEncoding might modify mData, so refresh the data container
+ verified = verifyEncoding(&_data);
+ {
+ StLock<Mutex>_(mMutex);
+ data = mData.get();
+ }
}
// If the data hasn't been set we can't return it.
void addSubjectKeyIdentifier();
void populateAttributes();
+ bool verifyEncoding(CSSM_DATA_PTR data);
private:
bool mHaveTypeAndEncoding;
CSSM_DATA_PTR mV1SubjectNameCStructValue;
CSSM_DATA_PTR mV1IssuerNameCStructValue;
CFDataRef mSha1Hash;
+ bool mEncodingVerified;
};
} // end namespace KeychainCore
/*
* Copyright (c) 2004,2011-2012,2014 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,
* 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@
*/
allocCopyData(issuerPubKey, mIssuerPubKey);
allocCopyData(subjectSerial, mSubjectSerial);
}
-
+
OCSPClientCertID::~OCSPClientCertID()
{
freeData(mIssuerName);
freeData(mSubjectSerial);
freeData(mEncoded);
}
-
+
/* preencoded DER NULL */
static uint8 nullParam[2] = {5, 0};
if(mEncoded.Data != NULL) {
return &mEncoded;
}
-
+
SecAsn1OCSPCertID certID;
uint8 issuerNameHash[CC_SHA1_DIGEST_LENGTH];
uint8 pubKeyHash[CC_SHA1_DIGEST_LENGTH];
-
+
/* algId refers to the hash we'll perform in issuer name and key */
certID.algId.algorithm = CSSMOID_SHA1;
certID.algId.parameters.Data = nullParam;
/* SHA1(issuerName) */
ocspdSha1(mIssuerName.Data, (CC_LONG)mIssuerName.Length, issuerNameHash);
- /* SHA1(issuer public key) */
+
+ /* SHA1(issuer public key bytes) */
ocspdSha1(mIssuerPubKey.Data, (CC_LONG)mIssuerPubKey.Length, pubKeyHash);
-
+
/* build the CertID from those components */
certID.issuerNameHash.Data = issuerNameHash;
certID.issuerNameHash.Length = CC_SHA1_DIGEST_LENGTH;
certID.issuerPubKeyHash.Data = pubKeyHash;
- certID.issuerPubKeyHash.Length = CC_SHA1_DIGEST_LENGTH;
+ certID.issuerPubKeyHash.Length = CC_SHA1_DIGEST_LENGTH;
certID.serialNumber = mSubjectSerial;
-
+
/* encode */
SecAsn1CoderRef coder;
SecAsn1CoderCreate(&coder);
-
+
CSSM_DATA tmp = {0, NULL};
SecAsn1EncodeItem(coder, &certID, kSecAsn1OCSPCertIDTemplate, &tmp);
allocCopyData(tmp, mEncoded);
SecAsn1CoderRelease(coder);
return &mEncoded;
}
-
+
/*
* Does this object refer to the same cert as specified SecAsn1OCSPCertID?
- * This is the main purpose of this class's existence; this function works
+ * This is the main purpose of this class's existence; this function works
* even if specified SecAsn1OCSPCertID uses a different hash algorithm
- * than we do, since we keep copies of our basic components.
+ * than we do, since we keep copies of our basic components.
*
* Returns true if compare successful.
*/
const CSSM_OID *alg = &exist.algId.algorithm;
uint8 digest[OCSPD_MAX_DIGEST_LEN];
CSSM_DATA digestData = {0, digest};
-
+
if(ocspdCompareCssmData(alg, &CSSMOID_SHA1)) {
hf = ocspdSha1;
digestData.Length = CC_SHA1_DIGEST_LENGTH;
else {
return false;
}
-
+
/* generate digests using exist's hash algorithm */
hf(mIssuerName.Data, (CC_LONG)mIssuerName.Length, digest);
if(!ocspdCompareCssmData(&digestData, &exist.issuerNameHash)) {
if(!ocspdCompareCssmData(&digestData, &exist.issuerPubKeyHash)) {
return false;
}
-
+
return true;
}
SecAsn1CoderRef coder;
SecAsn1OCSPCertID certID;
bool brtn = false;
-
+
SecAsn1CoderCreate(&coder);
memset(&certID, 0, sizeof(certID));
if(SecAsn1DecodeData(coder, &exist, kSecAsn1OCSPCertIDTemplate, &certID)) {
mExtensions(NULL)
{
assert(resp != NULL);
-
+
SecAsn1CoderCreate(&mCoder);
if((resp->certStatus.Data == NULL) || (resp->certStatus.Length == 0)) {
ocspdErrorLog("OCSPSingleResponse: bad certStatus\n");
/* decode further to get SecAsn1OCSPRevokedInfo */
SecAsn1OCSPCertStatus certStatus;
memset(&certStatus, 0, sizeof(certStatus));
- if(SecAsn1DecodeData(mCoder, &resp->certStatus,
+ if(SecAsn1DecodeData(mCoder, &resp->certStatus,
kSecAsn1OCSPCertStatusRevokedTemplate, &certStatus)) {
ocspdErrorLog("OCSPSingleResponse: err decoding certStatus\n");
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
mNextUpdate = genTimeToCFAbsTime(resp->nextUpdate);
}
mExtensions = new OCSPExtensions(resp->singleExtensions);
- ocspdDebug("OCSPSingleResponse: status %d reason %d", (int)mCertStatus,
+ ocspdDebug("OCSPSingleResponse: status %d reason %d", (int)mCertStatus,
(int)mCrlReason);
}
OCSPResponse::OCSPResponse(
const CSSM_DATA &resp,
CFTimeInterval defaultTTL) // default time-to-live in seconds
- : mLatestNextUpdate(NULL_TIME),
+ : mLatestNextUpdate(NULL_TIME),
mExpireTime(NULL_TIME),
mExtensions(NULL)
{
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)) {
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
+ /* not a failure of our constructor; this object is now useful, but
* only for this one byte of status info */
return;
}
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: 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;
+ case RIT_Name:
+ templ = kSecAsn1OCSPResponderIDAsNameTemplate;
break;
- case RIT_Key:
- templ = kSecAsn1OCSPResponderIDAsKeyTemplate;
+ case RIT_Key:
+ templ = kSecAsn1OCSPResponderIDAsKeyTemplate;
break;
default:
ocspdErrorLog("OCSPResponse: bad responderID tag\n");
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()
*/
return mBasicResponse.certs[dex];
}
-/*
- * Obtain a OCSPSingleResponse for a given "smart" CertID.
+/*
+ * Obtain a OCSPSingleResponse for a given "smart" CertID.
*/
OCSPSingleResponse *OCSPResponse::singleResponseFor(OCSPClientCertID &matchCertID)
{
}
/*
- * If responderID is of form RIT_Name, return the encoded version of the
+ * If responderID is of form RIT_Name, return the encoded version of the
* NSS_Name (for comparison with issuer's subjectName). Evaluated lazily,
* once, in mCoder space.
*/
return &mEncResponderName;
}
-/*
- * Obtain a OCSPSingleResponse for a given raw encoded CertID.
+/*
+ * Obtain a OCSPSingleResponse for a given raw encoded CertID.
*/
OCSPSingleResponse *OCSPResponse::singleResponseFor(const CSSM_DATA &matchCertID)
{
}
-/*
+/*
* Calculate temporal validity; set mLatestNextUpdate and mExpireTime. Only
- * called from constructor. Returns true if valid, else returns false.
+ * 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.
+
+ /*
+ * 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 defaultExpire = now + defaultTTL;
if(mLatestNextUpdate == NULL_TIME) {
/* absolute expire time = current time plus default TTL */
/*
* Copyright (c) 2004,2011,2014 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,
* 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@
*/
/*
* ocspResponse.h - OCSP Response class
*/
-
+
#ifndef _OCSP_RESPONSE_H_
#define _OCSP_RESPONSE_H_
#define CrlReason_NONE ((CE_CrlReason)-1)
/*
- * CertIDs can be represented differently by two peers even though they refer to
- * the same cert. Client can use SHA1 hash and server can use MD5, for example.
+ * CertIDs can be represented differently by two peers even though they refer to
+ * the same cert. Client can use SHA1 hash and server can use MD5, for example.
* So all of our code which creates a CertID based on known, existing subject and
- * issuer certs uses one of these "smart" certIDs which can encode itself and also
+ * issuer certs uses one of these "smart" certIDs which can encode itself and also
* compare against any form of existing SecAsn1OCSPCertID.
*/
class OCSPClientCertID
const CSSM_DATA &issuerName,
const CSSM_DATA &issuerPubKey,
const CSSM_DATA &subjectSerial);
-
+
~OCSPClientCertID();
-
+
/*
* DER encode.
*/
const CSSM_DATA *encode();
-
+
/*
* Does this object refer to the same cert as specified SecAsn1OCSPCertID?
- * This is the main purpose of this class's existence; this function works
+ * This is the main purpose of this class's existence; this function works
* even if specified SecAsn1OCSPCertID uses a different hash algorithm
- * than we do, since we keep copies of our basic components.
+ * than we do, since we keep copies of our basic components.
*
* Returns true if compare successful.
*/
bool compareToExist(
const SecAsn1OCSPCertID &exist);
-
- /*
+
+ /*
* Convenience function, like compareToExist, with a raw encoded CertID.
*/
- bool compareToExist(
+ bool compareToExist(
const CSSM_DATA &exist);
-
+
private:
CSSM_DATA mIssuerName;
CSSM_DATA mIssuerPubKey;
};
/*
- * Object representing one SecAsn1OCSPSingleResponse, i.e., the portion of
+ * Object representing one SecAsn1OCSPSingleResponse, i.e., the portion of
* an OCSP response associated with a single CertID. These are created and
* vended solely by an OCSPResponse object. The client which gets them from
* an OCSPResponse (via singleResponse()) must delete the object when finished
- * with it.
+ * with it.
*/
class OCSPSingleResponse
{
~OCSPSingleResponse();
friend class OCSPResponse;
protected:
-
+
OCSPSingleResponse(
SecAsn1OCSPSingleResponse *resp);
public:
CFAbsoluteTime nextUpdate() { return mNextUpdate; }
CFAbsoluteTime revokedTime() { return mRevokedTime; }
CE_CrlReason crlReason() { return mCrlReason; }
-
+
/* Extension accessors - all are optional */
-
+
/* CRL Reference */
const CSSM_DATA *crlUrl();
const CSSM_DATA *crlNum();
CFAbsoluteTime crlTime(); /* may be NULL_TIME */
-
+
/* archive cutoff */
CFAbsoluteTime archiveCutoff();
-
+
/* service locator not implemented yet */
private:
SecAsn1CoderRef mCoder;
CFAbsoluteTime mThisUpdate;
CFAbsoluteTime mNextUpdate; /* may be NULL_TIME */
CFAbsoluteTime mRevokedTime; /* != NULL_TIME for CS_Revoked */
- CE_CrlReason mCrlReason;
+ CE_CrlReason mCrlReason;
OCSPExtensions *mExtensions;
};
/*
- * OCSPResponse maintains its own temporal validity status based on the values of
+ * OCSPResponse maintains its own temporal validity status based on the values of
* all of the enclosed SingleResponses' thisUpdate and (optional) nextUpdate
* fields, in addition to a default time-to-live (TTL) value passed to
* OCSPResponse's constructor.
*
- * First, all of the thisUpdate fields are checked during OCSPResponse's constructor.
+ * First, all of the thisUpdate fields are checked during OCSPResponse's constructor.
* if any of these are later than the current time, the entire response is considered
- * invalid and the constructor throws a CssmError(CSSMERR_APPLETP_OCSP_BAD_RESPONSE).
- * Subsequent to construction, all thisUpdate fields are ignored.
+ * invalid and the constructor throws a CssmError(CSSMERR_APPLETP_OCSP_BAD_RESPONSE).
+ * Subsequent to construction, all thisUpdate fields are ignored.
*
- * The NextUpdate times are handled as follows.
+ * The NextUpdate times are handled as follows.
*
- * 1. An OCSPResponse's latestNextUpdate is defined as the latest of all of the
- * nextUpdate fields in its SingleResponses. This is evaluated during construction.
+ * 1. An OCSPResponse's latestNextUpdate is defined as the latest of all of the
+ * nextUpdate fields in its SingleResponses. This is evaluated during construction.
*
- * 2. An OCSPResponse's latestNextUpdate is NULL_TIME if none of its SingleResponses
- * contain any nextUpdate (this field is in fact optional).
+ * 2. An OCSPResponse's latestNextUpdate is NULL_TIME if none of its SingleResponses
+ * contain any nextUpdate (this field is in fact optional).
*
- * 3. The caller of OCSPResponse's constructor passes in a default time-to-live
- * (TTL) in seconds; call this defaultTTL. Call the time at which the
+ * 3. The caller of OCSPResponse's constructor passes in a default time-to-live
+ * (TTL) in seconds; call this defaultTTL. Call the time at which the
* constructor is called, PLUS defaultTTL, "defaultExpire".
- *
+ *
* -- If the OCSPResponse's latestNextUpdate is NULL_TIME then expireTime() returns
* defaultExpire.
*
- * -- Otherwise, expireTime() returns the lesser of (latestNextUpdate,
+ * -- Otherwise, expireTime() returns the lesser of (latestNextUpdate,
* defaultExpire).
*
* Note that this mechanism is used by both the TP's in-core cache and ocspd's
* on-disk cache; the two have different default TTLs values but the mechanism
- * for calcuating expireTime() is identical.
+ * for calcuating expireTime() is identical.
*/
class OCSPResponse
{
OCSPResponse(
const CSSM_DATA &resp,
CFTimeInterval defaultTTL); // default time-to-live in seconds
-
+
~OCSPResponse();
-
- /*
- * Info obtained during decode (which is don\ 1e immediately during constructor)
+
+ /*
+ * Info obtained during decode (which is don\ 1e immediately during constructor)
*/
SecAsn1OCSPResponseStatus responseStatus();
const CSSM_DATA *nonce(); /* NULL means not present */
CSSM_RETURN sigStatus();
uint32 numSignerCerts();
const CSSM_DATA *signerCert(uint32 dex);
-
- /*
- * Obtain a OCSPSingleResponse for a given CertID.
+
+ /*
+ * Obtain a OCSPSingleResponse for a given CertID.
*/
OCSPSingleResponse *singleResponseFor(OCSPClientCertID &certID);
OCSPSingleResponse *singleResponseFor(const CSSM_DATA &matchCertID);
-
+
CFAbsoluteTime expireTime() { return mExpireTime; }
/*
SecAsn1OCSPResponderIDTag responderIDTag() { return mResponderIdTag; }
const CSSM_DATA *encResponderName();
-
+
private:
bool calculateValidity(CFTimeInterval defaultTTL);
-
+
SecAsn1CoderRef mCoder;
CFAbsoluteTime mLatestNextUpdate;
CFAbsoluteTime mExpireTime;
- CSSM_DATA mEncResponderName; // encoded ResponderId.byName,
+ CSSM_DATA mEncResponderName; // encoded ResponderId.byName,
// if responder is in that format,
// lazily evaluated
- /*
- * Fields we decode - all in mCoder's memory space
+ /*
+ * Fields we decode - all in mCoder's memory space
*/
SecAsn1OCSPResponse mTopResp;
SecAsn1OCSPBasicResponse mBasicResponse;
SecAsn1OCSPResponderID mResponderId; // we have to decode
SecAsn1OCSPResponderIDTag mResponderIdTag; // IDs previous field
OCSPExtensions *mExtensions;
-};
+};
#endif /* _OCSP_RESPONSE_H_ */
* Copyright (c) 2000,2002,2011-2012,2014 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,
* 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@
*/
-/*
+/*
* ocspUtils.cpp - common utilities for OCSPD
*/
#include "ocspdUtils.h"
+#include "ocspdDebug.h"
+#include <Security/cssmerr.h>
+#include <Security/keyTemplates.h>
#include <CoreFoundation/CoreFoundation.h>
/*
CSSM_BOOL ocspdCompareCssmData(
const CSSM_DATA *data1,
const CSSM_DATA *data2)
-{
- if((data1 == NULL) || (data1->Data == NULL) ||
+{
+ if((data1 == NULL) || (data1->Data == NULL) ||
(data2 == NULL) || (data2->Data == NULL) ||
(data1->Length != data2->Length)) {
return CSSM_FALSE;
/*
* Convert a generalized time string, with a 4-digit year and no trailing
- * fractional seconds or time zone info, to a CFAbsoluteTime. Returns
- * NULL_TIME (0.0) on error.
+ * fractional seconds or time zone info, to a CFAbsoluteTime. Returns
+ * NULL_TIME (0.0) on error.
*/
static CFAbsoluteTime parseGenTime(
const uint8 *str,
if((str == NULL) || (len == 0)) {
return NULL_TIME;
}
-
+
/* tolerate NULL terminated or not */
if(str[len - 1] == '\0') {
len--;
CFGregorianDate greg;
memset(&greg, 0, sizeof(greg));
const uint8 *cp = str;
-
+
/* YEAR */
szTemp[0] = *cp++;
szTemp[1] = *cp++;
szTemp[4] = '\0';
len -= 4;
greg.year = atoi(szTemp);
-
+
/* MONTH - CFGregorianDate ranges 1..12, just like the string */
if(len < 2) {
return NULL_TIME;
szTemp[2] = '\0';
greg.day = atoi( szTemp );
len -= 2;
-
+
if(len >= 2) {
/* HOUR 0..23 */
szTemp[0] = *cp++;
/*
* Parse a GeneralizedTime string into a CFAbsoluteTime. Returns NULL on parse error.
- * Fractional parts of a second are discarded.
+ * Fractional parts of a second are discarded.
*/
CFAbsoluteTime genTimeToCFAbsTime(
const CSSM_DATA *strData)
if((strData == NULL) || (strData->Data == NULL) || (strData->Length == 0)) {
return NULL_TIME;
}
-
+
uint8 *timeStr = strData->Data;
size_t timeStrLen = strData->Length;
-
+
/* tolerate NULL terminated or not */
if(timeStr[timeStrLen - 1] == '\0') {
timeStrLen--;
}
-
+
/* start with a fresh editable copy */
uint8 *str = (uint8 *)malloc(timeStrLen);
uint32 strLen = 0;
-
- /*
+
+ /*
* If there is a decimal point, strip it and all trailing digits off
*/
const uint8 *inCp = timeStr;
bool minusOffset = false;
bool isGMT = false;
size_t toGo = timeStrLen;
-
+
do {
if(*inCp == '.') {
if(foundDecimal) {
}
}
foundDecimal++;
-
+
/* skip the decimal point... */
inCp++;
toGo--;
toGo--;
}
} while(toGo != 0);
-
+
if(str[strLen - 1] == 'Z') {
isGMT = true;
strLen--;
}
-
+
CFAbsoluteTime absTime;
absTime = parseGenTime(str, strLen);
free(str);
if(absTime == NULL_TIME) {
return NULL_TIME;
}
-
+
/* post processing needed? */
if(isGMT) {
/* Nope, string was in GMT */
return absTime;
}
-/*
+/*
* Convert CFAbsoluteTime to generalized time string, GMT format (4 digit year,
* trailing 'Z'). Caller allocated the output which is GENERAL_TIME_STRLEN+1 bytes.
*/
CFGregorianDate greg = CFAbsoluteTimeGetGregorianDate(absTime, tz);
int seconds = (int)greg.second;
sprintf(genTime, "%04d%02d%02d%02d%02d%02dZ",
- (int)greg.year, greg.month, greg.day, greg.hour,
+ (int)greg.year, greg.month, greg.day, greg.hour,
greg.minute, seconds);
}
-
+
void ocspdSha1(
const void *data,
CC_LONG len,
}
return count;
}
+
+/* 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;
+ }
+ else {
+ /* Unable to decode using kSecAsn1SubjectPublicKeyInfoTemplate.
+ * This may or may not be an error; just return the unchanged key.
+ */
+ ocspdErrorLog("ocspdGetPublicKeyBytes: unable to decode SubjectPublicKeyInfo\n");
+ }
+ }
+
+exit:
+ if(_coder) {
+ SecAsn1CoderRelease(_coder);
+ }
+ return crtn;
+}
* Copyright (c) 2000,2002,2011,2014 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,
* 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@
*/
-/*
+/*
* ocspUtils.h - common utilities for OCSPD
*/
#ifndef _OCSPD_UTILS_H_
#include <CommonCrypto/CommonDigest.h>
#include <Security/cssmtype.h>
+#include <Security/SecAsn1Coder.h>
#include <CoreFoundation/CoreFoundation.h>
/*
const CSSM_DATA *data2);
/*
- * Parse a GeneralizedTime string into a CFAbsoluteTime. Returns NULL_TIME on
- * parse error. Fractional parts of a second are discarded.
+ * Parse a GeneralizedTime string into a CFAbsoluteTime. Returns NULL_TIME on
+ * parse error. Fractional parts of a second are discarded.
*/
-#define NULL_TIME 0.0
+#define NULL_TIME 0.0
CFAbsoluteTime genTimeToCFAbsTime(
const CSSM_DATA *strData);
-/*
+/*
* Convert CFAbsoluteTime to generalized time string, GMT format (4 digit year,
* trailing 'Z'). Caller allocated the output which is GENERAL_TIME_STRLEN bytes plus
* a NULL.
unsigned ocspdArraySize(
const void **array);
+/*
+ * 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.
+ */
+CSSM_RETURN ocspdGetPublicKeyBytes(
+ SecAsn1CoderRef coder,
+ CSSM_KEY_PTR publicKey,
+ CSSM_DATA &publicKeyBytes); // filled out by this function
+
+
#define CFRELEASE(cf) \
if(cf != NULL) { \
CFRelease(cf); \
}
-
+
#ifdef __cplusplus
}
#endif
_inCallout = YES;
if (!_oldInCallout)
- secnotice("deaf", ">>>>>>>>>>> WTFBBQ _oldInCallout is NO and we're heading in to the callout!");
+ secnotice("deaf", ">>>>>>>>>>> _oldInCallout is NO and we're heading in to the callout!");
_shadowPendingKeys = [NSMutableSet set];
_shadowSyncWithPeersPending = NO;
if (SOSFullPeerInfoUpdateGestalt(full_peer, new_gestalt, NULL)) {
SOSAccountModifyCircle(account, SOSCircleGetName(circle),
NULL, ^(SOSCircleRef circle_to_change) {
+ secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(full_peer));
});
};
}
void SOSAccountSetToNew(SOSAccountRef a) {
+ secnotice("accountChange", "Setting Account to New");
CFAllocatorRef allocator = CFGetAllocator(a);
CFReleaseNull(a->circle_identities);
CFReleaseNull(a->circles);
bool sosAccountLeaveCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircle(account, circle, NULL);
if(!fpi) return false;
+ if(!SOSFullPeerInfoValidate(fpi, NULL)) return false;
CFErrorRef localError = NULL;
SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
CFStringRef retire_id = SOSPeerInfoGetPeerID(retire_peer);
//
static bool SOSAccountResetThisCircleToOffering(SOSAccountRef account, SOSCircleRef circle, SecKeyRef user_key, CFErrorRef *error) {
- SOSFullPeerInfoRef myCirclePeer = SOSAccountGetMyFullPeerInCircle(account, circle, error);
+ SOSFullPeerInfoRef myCirclePeer = SOSAccountMakeMyFullPeerInCircleNamed(account, SOSCircleGetName(circle), error);
if (!myCirclePeer)
return false;
+ if(!SOSFullPeerInfoValidate(myCirclePeer, NULL)) return false;
+
SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) {
bool result = false;
__block bool result = false;
__block SOSFullPeerInfoRef cloud_full_peer = NULL;
- SOSFullPeerInfoRef myCirclePeer = SOSAccountGetMyFullPeerInCircle(account, circle, error);
+ SOSFullPeerInfoRef myCirclePeer = SOSAccountMakeMyFullPeerInCircleNamed(account, SOSCircleGetName(circle), error);
require_action_quiet(myCirclePeer, fail,
SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Can't find/create peer for circle: %@"), circle));
SOSPeerInfoRef SOSAccountGetMyPeerInCircleNamed(SOSAccountRef account, CFStringRef circle, CFErrorRef* error);
SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error);
-SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error);
+SOSFullPeerInfoRef SOSAccountMakeMyFullPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error);
//
// MARK: Credential management
SOSTransportKeyParameterRef tKey = NULL;
SOSTransportCircleRef tCircle = NULL;
SOSTransportMessageRef tMessage = NULL;
+
+#if 0 // IDS_FUTURE
+ // Solve determining transport type without fullpeer
SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamed(account, circleName, error);
require_quiet(fpi, fail);
SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(fpi);
require_quiet(myPeer, fail);
CFStringRef type = SOSPeerInfoGetTransportType(myPeer);
if(CFStringCompare(type, CFSTR("KVS"), 0) == kCFCompareEqualTo){
- tKey = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(account, error);
- tCircle = (SOSTransportCircleRef)SOSTransportCircleKVSCreate(account, circleName, error);
- tMessage = (SOSTransportMessageRef)SOSTransportMessageKVSCreate(account, circleName, error);
- require_quiet(tKey, fail);
- require_quiet(tCircle, fail);
- require_quiet(tMessage, fail);
-
- CFRetainAssign(account->key_transport, (SOSTransportKeyParameterRef)tKey);
- CFDictionarySetValue(account->circle_transports, circleName, tCircle);
- CFDictionarySetValue(account->message_transports, circleName, tMessage);
+#endif
+ tKey = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(account, error);
+ tCircle = (SOSTransportCircleRef)SOSTransportCircleKVSCreate(account, circleName, error);
+ tMessage = (SOSTransportMessageRef)SOSTransportMessageKVSCreate(account, circleName, error);
+ require_quiet(tKey, fail);
+ require_quiet(tCircle, fail);
+ require_quiet(tMessage, fail);
+
+ CFRetainAssign(account->key_transport, (SOSTransportKeyParameterRef)tKey);
+ CFDictionarySetValue(account->circle_transports, circleName, tCircle);
+ CFDictionarySetValue(account->message_transports, circleName, tMessage);
+#if 0 // IDS_FUTURE
}
-
+#endif
success = true;
+
fail:
CFReleaseNull(tKey);
CFReleaseNull(tCircle);
SOSUpdateKeyInterest();
}
-
+
require_quiet(SOSAccountInflateTransportsForCircle(a, name, error), fail);
-
+
fail:
CFReleaseNull(localError);
return circle;
-SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error) {
+SOSFullPeerInfoRef SOSAccountMakeMyFullPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error) {
if (CFDictionaryGetValue(account->circles, name) == NULL) {
SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No circle named '%@'"), name);
return NULL;
SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
- return SOSAccountGetMyFullPeerInCircleNamed(account, SOSCircleGetName(circle), error);
+ return SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, SOSCircleGetName(circle), error);
}
}
SOSPeerInfoRef SOSAccountGetMyPeerInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
- SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamed(account, SOSCircleGetName(circle), error);
+ SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, SOSCircleGetName(circle), error);
return fpi ? SOSFullPeerInfoGetPeerInfo(fpi) : NULL;
}
SOSPeerInfoRef SOSAccountGetMyPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error)
{
- SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamed(account, name, error);
+ SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, name, error);
return fpi ? SOSFullPeerInfoGetPeerInfo(fpi) : NULL;
}
if (fullPeerInfoData) {
SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error);
require_action_quiet(full_peer, fail, success = false);
-
+
CFDictionaryAddValue(account->circle_identities, circleName, full_peer);
CFReleaseNull(full_peer);
}
CFReleaseNull(array);
require_quiet(success, fail);
+
require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail,
SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error));
int SOSAccountCountCircles(SOSAccountRef a);
-SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error);
+SOSFullPeerInfoRef SOSAccountMakeMyFullPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error);
bool SOSAccountDestroyCirclePeerInfoNamed(SOSAccountRef account, CFStringRef name, CFErrorRef* error);
bool SOSAccountIsAccountIdentity(SOSAccountRef account, SOSPeerInfoRef peer_info, CFErrorRef *error);
-SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error);
+SOSFullPeerInfoRef SOSAccountMakeMyFullPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error);
SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error);
// SOSAccountDestroyCirclePeerInfo(account, oldCircle, NULL);
}
- SOSFullPeerInfoRef me_full = SOSAccountGetMyFullPeerInCircle(account, oldCircle, NULL);
- SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(me_full);
+ // Changed to just get the fullpeerinfo if present. We don't want to make up FPIs here.
+ SOSPeerInfoRef me = NULL;
+ SOSFullPeerInfoRef me_full = SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, SOSCircleGetName(oldCircle), NULL);
+ if(me_full) me = SOSFullPeerInfoGetPeerInfo(me_full);
SOSTransportCircleRef transport = (SOSTransportCircleRef)CFDictionaryGetValue(account->circle_transports, SOSCircleGetName(prospective_circle));
if (me && SOSCircleHasPeer(oldCircle, me, NULL)) {
if (sosAccountLeaveCircle(account, newCircle, error)) {
- account->departure_code = leave_reason;
circleToPush = newCircle;
- circle_action = accept;
- me = NULL;
- me_full = NULL;
+ } else {
+ secnotice("signing", "Can't leave circle %@, but dumping identities", oldCircle);
+ success = false;
}
- }
- else {
+ account->departure_code = leave_reason;
+ circle_action = accept;
+ me = NULL;
+ me_full = NULL;
+ } else {
// We are not in this circle, but we need to update account with it, since we got it from cloud
- secnotice("updatecircle", "We are not in this circle, but we need to update account with it");
+ secnotice("signing", "We are not in this circle, but we need to update account with it");
circle_action = accept;
}
}
me = NULL;
me_full = NULL;
} else {
- SOSCircleRequestReadmission(newCircle, account->user_public, me_full, NULL);
+ SOSCircleRequestReadmission(newCircle, account->user_public, me, NULL);
writeUpdate = true;
}
}
secnotice("signing", "%@, Accepting circle: %@", concStr, newCircle);
- if (me_full && account->user_public_trusted
+ if (me && account->user_public_trusted
&& SOSCircleHasApplicant(oldCircle, me, NULL)
&& SOSCircleCountPeers(newCircle) > 0
&& !SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleHasApplicant(newCircle, me, NULL)) {
// We were applying and we weren't accepted.
// Our application is declared lost, let us reapply.
- if (SOSCircleRequestReadmission(newCircle, account->user_public, me_full, NULL))
+ if (SOSCircleRequestReadmission(newCircle, account->user_public, me, NULL))
writeUpdate = true;
}
if (circleToPush != NULL) {
- secnotice("circleUpdate", "Pushing:[%s] %@", local_remote, circleToPush);
+ secnotice("signing", "Pushing:[%s] %@", local_remote, circleToPush);
CFDataRef circle_data = SOSCircleCopyEncodedData(circleToPush, kCFAllocatorDefault, error);
if (circle_data) {
success &= SOSTransportCirclePostCircle(transport, SOSCircleGetName(circleToPush), circle_data, error);
}
bool SOSCircleHasPeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
+ if(!peerInfo) return false;
return SOSCircleHasPeerWithID(circle, SOSPeerInfoGetPeerID(peerInfo), error);
}
return true;
}
-static bool SOSCircleRecordAdmissionRequest(SOSCircleRef circle, SecKeyRef user_pubkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
+static bool SOSCircleRecordAdmissionRequest(SOSCircleRef circle, SecKeyRef user_pubkey, SOSPeerInfoRef requestorPeerInfo, CFErrorRef *error) {
SOSCircleAssertStable(circle);
- SOSPeerInfoRef requestorPeerInfo = SOSFullPeerInfoGetPeerInfo(requestor);
- bool isPeer = SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(requestor), error);
+ bool isPeer = SOSCircleHasPeer(circle, requestorPeerInfo, error);
require_action_quiet(!isPeer, fail, SOSCreateError(kSOSErrorAlreadyPeer, CFSTR("Cannot request admission when already a peer"), NULL, error));
}
-bool SOSCircleRequestReadmission(SOSCircleRef circle, SecKeyRef user_pubkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
+bool SOSCircleRequestReadmission(SOSCircleRef circle, SecKeyRef user_pubkey, SOSPeerInfoRef peer, CFErrorRef *error) {
bool success = false;
- SOSPeerInfoRef peer = SOSFullPeerInfoGetPeerInfo(requestor);
require_quiet(SOSPeerInfoApplicationVerify(peer, user_pubkey, error), fail);
- success = SOSCircleRecordAdmissionRequest(circle, user_pubkey, requestor, error);
+ success = SOSCircleRecordAdmissionRequest(circle, user_pubkey, peer, error);
fail:
return success;
}
require(SOSFullPeerInfoPromoteToApplication(requestor, user_privkey, error), fail);
- success = SOSCircleRecordAdmissionRequest(circle, user_pubkey, requestor, error);
+ success = SOSCircleRecordAdmissionRequest(circle, user_pubkey, SOSFullPeerInfoGetPeerInfo(requestor), error);
fail:
CFReleaseNull(user_pubkey);
return success;
bool SOSCircleResetToOffering(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error);
bool SOSCircleResetToEmpty(SOSCircleRef circle, CFErrorRef *error);
bool SOSCircleRequestAdmission(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error);
-bool SOSCircleRequestReadmission(SOSCircleRef circle, SecKeyRef user_pubkey, SOSFullPeerInfoRef requestor, CFErrorRef *error);
+bool SOSCircleRequestReadmission(SOSCircleRef circle, SecKeyRef user_pubkey, SOSPeerInfoRef requestor, CFErrorRef *error);
bool SOSCircleAcceptRequest(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef device_approver, SOSPeerInfoRef peerInfo, CFErrorRef *error);
bool SOSCircleRejectRequest(SOSCircleRef circle, SOSFullPeerInfoRef device_approver, SOSPeerInfoRef peerInfo, CFErrorRef *error);
CFReleaseSafe(mfadd);
};
- if (source == kSOSDataSourceSOSTransaction) {
- processUpdates();
- } else {
- // WARNING: This will deadlock the engine if you call a
- // SecItem API function while holding the engine lock!
- // However making this async right now isn't safe yet either
- // Due to some code in the enginer using Get v/s copy to
- // access some of the values that would be modified
- // asynchronously here since the engine is coded as if
- // running on a serial queue.
- dispatch_sync(engine->queue, processUpdates);
- }
+ // WARNING: This will deadlock the engine if you call a
+ // SecItem API function while holding the engine lock!
+ // However making this async right now isn't safe yet either
+ // Due to some code in the enginer using Get v/s copy to
+ // access some of the values that would be modified
+ // asynchronously here since the engine is coded as if
+ // running on a serial queue.
+ dispatch_sync(engine->queue, processUpdates);
});
} else {
SOSDataSourceSetNotifyPhaseBlock(engine->dataSource, ^(SOSDataSourceRef ds, SOSTransactionRef txn, SOSDataSourceTransactionPhase phase, SOSDataSourceTransactionSource source, struct SOSDigestVector *removals, struct SOSDigestVector *additions) {
__block bool somethingChanged = false;
SOSMessageRef message = SOSMessageCreateWithData(kCFAllocatorDefault, raw_message, error);
result = message && SOSDataSourceWith(engine->dataSource, error, ^(SOSTransactionRef txn, bool *commit) {
- result = SOSEngineHandleMessage_locked(engine, peerID, message, txn, commit, &somethingChanged, error);
+ dispatch_sync(engine->queue, ^{
+ result = SOSEngineHandleMessage_locked(engine, peerID, message, txn, commit, &somethingChanged, error);
});
+ });
CFReleaseSafe(message);
if (somethingChanged)
SecKeychainChanged(false);
SOSFullPeerInfoRef SOSFullPeerInfoCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error,
const uint8_t** der_p, const uint8_t *der_end) {
SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator);
- SecKeyRef device_key = NULL;
const uint8_t *sequence_end;
require_quiet(fpi->peer_info != NULL, fail);
*der_p = der_decode_data(allocator, kCFPropertyListImmutable, &fpi->key_ref, error, *der_p, sequence_end);
-
- OSStatus result = SecKeyFindWithPersistentRef(fpi->key_ref, &device_key);
-
- require_quiet(result == errSecSuccess, fail);
require_quiet(*der_p != NULL, fail);
- CFReleaseNull(device_key);
return fpi;
fail:
CFReleaseNull(fpi);
- CFReleaseNull(device_key);
return NULL;
}
bool SOSFullPeerInfoValidate(SOSFullPeerInfoRef peer, CFErrorRef* error) {
- return true;
+ SecKeyRef device_key = NULL;
+ OSStatus result = SecKeyFindWithPersistentRef(peer->key_ref, &device_key);
+ CFReleaseNull(device_key);
+ if(result == errSecSuccess) return true;
+ return false;
}
bool SOSFullPeerInfoPurgePersistentKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error) {
CFMutableArrayRef handledKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
if(CFDictionaryContainsKey(updates, kSOSKVSAccountChangedKey)){
+ secnotice("accountChange", "SOSTransportDispatchMessages received kSOSKVSAccountChangedKey");
// While changing accounts we may modify the key params array. To avoid stepping on ourselves we
// copy the list for iteration.
CFArrayRef originalKeyParams = CFArrayCreateCopy(kCFAllocatorDefault, SOSGetTransportKeyParameters());
if (transport->destroy)
transport->destroy(transport);
- CFReleaseSafe(transport->engine);
+ CFReleaseSafe(transport->account);
}
SOSAccountRef SOSTransportMessageGetAccount(SOSTransportMessageRef transport){
#include <xpc/private.h>
#include <xpc/xpc.h>
+#if TARGET_OS_MAC
+#include <Security/SecTaskPriv.h>
+#endif
+
static CFStringRef SecTaskCopyStringForEntitlement(SecTaskRef task,
CFStringRef entitlement)
{
CFStringRef appID = SecTaskCopyApplicationIdentifier(task);
CFIndex kagLen = keychainAccessGroups ? CFArrayGetCount(keychainAccessGroups) : 0;
CFIndex asagLen = appleSecurityApplicationGroups ? CFArrayGetCount(appleSecurityApplicationGroups) : 0;
+#if TARGET_OS_MAC
+ if ((appID || asagLen) && !SecTaskEntitlementsValidated(task)) {
+ CFReleaseNull(appID);
+ asagLen = 0;
+ }
+#endif
CFIndex len = kagLen + asagLen + (appID ? 1 : 0);
if (len) {
groups = CFArrayCreateMutable(kCFAllocatorDefault, len, &kCFTypeArrayCallBacks);
#include "SOSAccountTesting.h"
-static int kTestTestCount = 304;
+static int kTestTestCount = 298;
static void tests(void)
{
ok(peers && CFArrayGetCount(peers) == 2, "See two peers %@ (%@)", peers, error);
CFReleaseNull(peers);
- SOSFullPeerInfoRef fpiAlice = SOSAccountGetMyFullPeerInCircleNamed(alice_account, circle_name, NULL);
+ SOSFullPeerInfoRef fpiAlice = SOSAccountGetMyFullPeerInCircleNamedIfPresent(alice_account, circle_name, NULL);
CFStringRef alice_id = CFStringCreateCopy(NULL, SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(fpiAlice)));
ok(SOSAccountLeaveCircles(alice_account, &error), "Alice Leaves (%@)", error);
static CFDataRef sLastSavedAccountData = NULL;
CFErrorRef saveError = NULL;
+
+ if(SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, CFSTR("ak"), NULL) == NULL) {
+ return;
+ }
+
SOSCCCircleIsOn_UpdateArtifact(SOSAccountIsInCircles(account, NULL));
CFDataRef accountAsData = SOSAccountCopyEncodedData(account, kCFAllocatorDefault, &saveError);
account = SOSAccountCreateFromData(kCFAllocatorDefault, savedAccount, factory, &inflationError);
+ if(account && SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, CFSTR("ak"), NULL) == NULL) {
+ SOSAccountRef newAccount = SOSAccountCreate(kCFAllocatorDefault, our_gestalt, factory);
+
+ if (!newAccount) {
+ secnotice("repair_account", "Tried to repair bad account - got null account");
+ } else {
+ account = newAccount;
+ }
+ }
+
if (account){
SOSAccountUpdateGestalt(account, our_gestalt);
- }
- else
+ } else {
secerror("Got error inflating account: %@", inflationError);
+ }
+
CFReleaseNull(inflationError);
}
CFReleaseSafe(savedAccount);
});
SecKeyRef privateKeyRef = SOSFullPeerInfoCopyDeviceKey(full_peer_info, error);
+ if(!privateKeyRef) {
+ secnotice("otr_keysetup", "Could not get private key for FullPeerInfo");
+ return NULL;
+ }
CFStringRef publicKeyString = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, publicPeerId, kCFStringEncodingUTF8);
__block SOSPeerInfoRef peer_info = NULL;
ApplyScopeListForIDC(cur_scope, kScopeIDEnvironment);
}
+#define XPCSCOPESTRWANT "api,account,accountChange,circle,circleChange,circleCreat,flush,fresh,keygen,signing,talkwithkvs"
+#define XPCSCOPESTRDONTWANT "-event,http,item,keytrace,lockassertions,otr_keysetup,securityd,server,serverxpc,session,sync,titc,transport,trust,updates,xpc"
+static void setup_xpcdefault_scopes() {
+
+ CFDictionaryRef noticeLogging = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+ CFSTR(ASL_STRING_NOTICE), CFSTR(XPCSCOPESTRDONTWANT), NULL);
+
+ ApplyScopeDictionaryForID(noticeLogging, kScopeIDXPC);
+
+ CFReleaseNull(noticeLogging);
+}
+
void __security_debug_init(void) {
static dispatch_once_t sdOnceToken;
setup_environment_scopes();
setup_config_settings();
setup_defaults_settings();
+ //setup_xpcdefault_scopes();
});
}
-
// MARK: Log handler recording (e.g. grabbing security logging and sending it to test results).
static void clean_aslclient(void *client)
{
self.textLabel.textColor = [UIColor blackColor];
self.textLabel.textAlignment = NSTextAlignmentLeft;
- self.textLabel.AdjustsFontSizeToFitWidth = YES;
+ self.textLabel.adjustsFontSizeToFitWidth = YES;
self.textLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
NSString *title = [dict objectForKey:SWC_ACCOUNT_KEY];
self.detailTextLabel.textColor = [UIColor darkGrayColor];
self.detailTextLabel.textAlignment = NSTextAlignmentLeft;
- self.detailTextLabel.AdjustsFontSizeToFitWidth = YES;
+ self.detailTextLabel.adjustsFontSizeToFitWidth = YES;
self.detailTextLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
NSString *subtitle = [dict objectForKey:SWC_SERVER_KEY];