2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
21 #include <Security/Trust.h>
22 #include <Security/cssmdates.h>
23 #include <Security/cfutilities.h>
24 #include <CoreFoundation/CFData.h>
25 #include <Security/SecCertificate.h>
26 #include "SecBridge.h"
28 using namespace KeychainCore
;
32 // For now, we use a global TrustStore
34 ModuleNexus
<TrustStore
> Trust::gStore
;
38 // @@@ For some reason, the C++ type system won't resolve an operator from Security namespace.
39 // Drag it in here explicitly (the hard way). Someone bored might want to investigate which
40 // language rules ambiguates the Security::operator == inside the Security::KeychainCore namespace.
42 inline bool operator == (const CSSM_DL_DB_HANDLE
&h1
, const CSSM_DL_DB_HANDLE
&h2
)
44 return Security::operator == (h1
, h2
);
49 // Construct a Trust object with suitable defaults.
50 // Use setters for additional arguments before calling evaluate().
52 Trust::Trust(CFTypeRef certificates
, CFTypeRef policies
)
53 : mTP(gGuidAppleX509TP
), mAction(CSSM_TP_ACTION_DEFAULT
),
54 mCerts(cfArrayize(certificates
)), mPolicies(cfArrayize(policies
)),
55 mResult(kSecTrustResultInvalid
)
57 // set default search list from user's default
58 globals().storageManager
.getSearchList(mSearchLibs
);
63 // Clean up a Trust object
65 Trust::~Trust() throw()
72 // Retrieve the last TP evaluation result, if any
74 CSSM_TP_VERIFY_CONTEXT_RESULT_PTR
Trust::cssmResult()
76 if (mResult
== kSecTrustResultInvalid
)
77 MacOSError::throwMe(errSecTrustNotAvailable
);
82 // SecCertificateRef -> CssmData
83 CssmData
cfCertificateData(SecCertificateRef certificate
)
85 return Certificate::required(certificate
)->data();
88 // SecPolicyRef -> CssmField (CFDataRef/NULL or oid/value of a SecPolicy)
89 CssmField
cfField(SecPolicyRef item
)
91 SecPointer
<Policy
> policy
= Policy::required(SecPolicyRef(item
));
92 return CssmField(policy
->oid(), policy
->value());
95 // SecKeychain -> CssmDlDbHandle
96 CSSM_DL_DB_HANDLE
cfKeychain(SecKeychainRef ref
)
98 Keychain keychain
= KeychainImpl::required(ref
);
99 return keychain
->database()->handle();
104 // Here's the big "E" - evaluation.
105 // We build most of the CSSM-layer input structures dynamically right here;
106 // they will auto-destruct when we're done. The output structures are kept
107 // around (in our data members) for later analysis.
108 // Note that evaluate() can be called repeatedly, so we must be careful to
109 // dispose of prior results.
111 void Trust::evaluate()
113 // if we have evaluated before, release prior result
116 // build the target cert group
117 CFToVector
<CssmData
, SecCertificateRef
, cfCertificateData
> subjects(mCerts
);
118 CertGroup
subjectCertGroup(CSSM_CERT_X_509v3
,
119 CSSM_CERT_ENCODING_BER
, CSSM_CERTGROUP_DATA
);
120 subjectCertGroup
.count() = subjects
;
121 subjectCertGroup
.blobCerts() = subjects
;
123 // build a TP_VERIFY_CONTEXT, a veritable nightmare of a data structure
124 TPBuildVerifyContext
context(mAction
);
126 context
.actionData() = cfData(mActionData
);
128 // policies (one at least, please)
129 CFToVector
<CssmField
, SecPolicyRef
, cfField
> policies(mPolicies
);
130 if (policies
.empty())
131 MacOSError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
);
132 context
.setPolicies(policies
, policies
);
134 // anchor certificates
135 CFCopyRef
<CFArrayRef
> anchors(mAnchors
);
137 anchors
= gStore().copyRootCertificates(); // retains
138 CFToVector
<CssmData
, SecCertificateRef
, cfCertificateData
> roots(anchors
);
139 context
.anchors(roots
, roots
);
141 // dlDbList (keychain list)
142 vector
<CSSM_DL_DB_HANDLE
> dlDbList
;
143 for (StorageManager::KeychainList::const_iterator it
= mSearchLibs
.begin();
144 it
!= mSearchLibs
.end(); it
++)
148 dlDbList
.push_back((*it
)->database()->handle());
154 context
.setDlDbList(dlDbList
.size(), &dlDbList
[0]);
159 CssmUniformDate(static_cast<CFDateRef
>(mVerifyTime
)).convertTo(
160 timeString
, sizeof(timeString
));
161 context
.time(timeString
);
166 mTP
->certGroupVerify(subjectCertGroup
, context
, &mTpResult
);
168 } catch (CssmCommonError
&err
) {
169 mTpReturn
= err
.osStatus();
171 mResult
= diagnoseOutcome();
173 // see if we can use the evidence
174 if (mTpResult
.count() > 0
175 && mTpResult
[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER
176 && mTpResult
[0].as
<CSSM_TP_APPLE_EVIDENCE_HEADER
>()->Version
== CSSM_TP_APPLE_EVIDENCE_VERSION
177 && mTpResult
.count() == 3
178 && mTpResult
[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP
179 && mTpResult
[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO
) {
180 evaluateUserTrust(*mTpResult
[1].as
<CertGroup
>(),
181 mTpResult
[2].as
<CSSM_TP_APPLE_EVIDENCE_INFO
>(), anchors
);
183 // unexpected evidence information. Can't use it
184 secdebug("trusteval", "unexpected evidence ignored");
190 // Classify the TP outcome in terms of a SecTrustResultType
192 SecTrustResultType
Trust::diagnoseOutcome()
195 case noErr
: // peachy
196 return kSecTrustResultUnspecified
;
197 case CSSMERR_TP_CERT_EXPIRED
: // expired cert
198 case CSSMERR_TP_CERT_NOT_VALID_YET
: // mis-expired cert
199 case CSSMERR_TP_NOT_TRUSTED
: // no root, no anchor
200 case CSSMERR_TP_VERIFICATION_FAILURE
: // root does not self-verify
201 case CSSMERR_TP_INVALID_ANCHOR_CERT
: // valid is not an anchor
202 case CSSMERR_TP_VERIFY_ACTION_FAILED
: // policy action failed
203 return kSecTrustResultRecoverableTrustFailure
;
204 case CSSMERR_TP_INVALID_CERTIFICATE
: // bad certificate
205 return kSecTrustResultFatalTrustFailure
;
207 return kSecTrustResultOtherError
; // unknown
213 // Assuming a good evidence chain, check user trust
214 // settings and set mResult accordingly.
216 void Trust::evaluateUserTrust(const CertGroup
&chain
,
217 const CSSM_TP_APPLE_EVIDENCE_INFO
*infoList
, CFCopyRef
<CFArrayRef
> anchors
)
219 // extract cert chain as Certificate objects
220 mCertChain
.resize(chain
.count());
221 for (uint32 n
= 0; n
< mCertChain
.size(); n
++) {
222 const TPEvidenceInfo
&info
= TPEvidenceInfo::overlay(infoList
[n
]);
223 if (info
.recordId()) {
224 Keychain keychain
= keychainByDLDb(info
.DlDbHandle
);
225 DbUniqueRecord
uniqueId(keychain
->database()->newDbUniqueRecord());
226 secdebug("trusteval", "evidence #%ld from keychain \"%s\"", n
, keychain
->name());
227 *static_cast<CSSM_DB_UNIQUE_RECORD_PTR
*>(uniqueId
) = info
.UniqueRecord
;
228 uniqueId
->activate(); // transfers ownership
229 mCertChain
[n
] = safe_cast
<Certificate
*>(keychain
->item(CSSM_DL_DB_RECORD_X509_CERTIFICATE
, uniqueId
).get());
230 } else if (info
.status(CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
)) {
231 secdebug("trusteval", "evidence %ld from input cert %ld", n
, info
.index());
232 assert(info
.index() < uint32(CFArrayGetCount(mCerts
)));
233 SecCertificateRef cert
= SecCertificateRef(CFArrayGetValueAtIndex(mCerts
,
235 mCertChain
[n
] = Certificate::required(cert
);
236 } else if (info
.status(CSSM_CERT_STATUS_IS_IN_ANCHORS
)) {
237 secdebug("trusteval", "evidence %ld from anchor cert %ld", n
, info
.index());
238 assert(info
.index() < uint32(CFArrayGetCount(anchors
)));
239 SecCertificateRef cert
= SecCertificateRef(CFArrayGetValueAtIndex(anchors
,
241 mCertChain
[n
] = Certificate::required(cert
);
243 // unknown source; make a new Certificate for it
244 secdebug("trusteval", "evidence %ld from unknown source", n
);
246 new Certificate(chain
.blobCerts()[n
],
247 CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_BER
);
251 // now walk the chain, leaf-to-root, checking for user settings
252 TrustStore
&store
= gStore();
253 SecPointer
<Policy
> policy
=
254 Policy::required(SecPolicyRef(CFArrayGetValueAtIndex(mPolicies
, 0)));
255 for (mResultIndex
= 0;
256 mResult
== kSecTrustResultUnspecified
&& mResultIndex
< mCertChain
.size();
259 if (!mCertChain
[mResultIndex
])
264 mResult
= store
.find(mCertChain
[mResultIndex
], policy
);
270 // Release TP evidence information.
271 // This information is severely under-defined by CSSM, so we proceed
273 // (a) If the evidence matches an Apple-defined pattern, use specific
274 // knowledge of that format.
275 // (b) Otherwise, assume that the void * are flat blocks of memory.
277 void Trust::releaseTPEvidence(TPVerifyResult
&result
, CssmAllocator
&allocator
)
279 if (result
.count() > 0) { // something to do
280 if (result
[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER
) {
281 // Apple defined evidence form -- use intimate knowledge
282 if (result
[0].as
<CSSM_TP_APPLE_EVIDENCE_HEADER
>()->Version
== CSSM_TP_APPLE_EVIDENCE_VERSION
283 && result
.count() == 3
284 && result
[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP
285 && result
[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO
) {
287 CertGroup
& certs
= *result
[1].as
<CertGroup
>();
288 CSSM_TP_APPLE_EVIDENCE_INFO
*evidence
= result
[2].as
<CSSM_TP_APPLE_EVIDENCE_INFO
>();
289 uint32 count
= certs
.count();
290 allocator
.free(result
[0].data()); // just a struct
291 certs
.destroy(allocator
); // certgroup contents
292 allocator
.free(result
[1].data()); // the CertGroup itself
293 for (uint32 n
= 0; n
< count
; n
++)
294 allocator
.free(evidence
[n
].StatusCodes
);
295 allocator
.free(result
[2].data()); // array of (flat) info structs
297 secdebug("trusteval", "unrecognized Apple TP evidence format");
298 // drop it -- better leak than kill
301 // unknown format -- blindly assume flat blobs
302 secdebug("trusteval", "destroying unknown TP evidence format");
303 for (uint32 n
= 0; n
< result
.count(); n
++)
305 allocator
.free(result
[n
].data());
309 allocator
.free (result
.Evidence
);
315 // Clear evaluation results unless state is initial (invalid)
317 void Trust::clearResults()
319 if (mResult
!= kSecTrustResultInvalid
) {
320 releaseTPEvidence(mTpResult
, mTP
.allocator());
321 mResult
= kSecTrustResultInvalid
;
326 // Convert a SecPointer to a CF object.
327 static SecCertificateRef
328 convert(const SecPointer
<Certificate
> &certificate
)
334 // Build evidence information
336 void Trust::buildEvidence(CFArrayRef
&certChain
, TPEvidenceInfo
* &statusChain
)
338 if (mResult
== kSecTrustResultInvalid
)
339 MacOSError::throwMe(errSecTrustNotAvailable
);
340 certChain
= mEvidenceReturned
=
341 makeCFArray(convert
, mCertChain
);
342 statusChain
= mTpResult
[2].as
<TPEvidenceInfo
>();
347 // Given a DL_DB_HANDLE, locate the Keychain object (from the search list)
349 Keychain
Trust::keychainByDLDb(const CSSM_DL_DB_HANDLE
&handle
) const
351 for (StorageManager::KeychainList::const_iterator it
= mSearchLibs
.begin();
352 it
!= mSearchLibs
.end(); it
++)
356 if ((*it
)->database()->handle() == handle
)
364 // could not find in search list - internal error