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 // Construct a Trust object with suitable defaults.
39 // Use setters for additional arguments before calling evaluate().
41 Trust::Trust(CFTypeRef certificates
, CFTypeRef policies
)
42 : mTP(gGuidAppleX509TP
), mAction(CSSM_TP_ACTION_DEFAULT
),
44 mCerts(cfArrayize(certificates
)), mPolicies(cfArrayize(policies
)),
45 mResult(kSecTrustResultInvalid
)
47 // set default search list from user's default
48 globals().storageManager
.getSearchList(mSearchLibs
);
53 // Clean up a Trust object
62 // Retrieve the last TP evaluation result, if any
64 CSSM_TP_VERIFY_CONTEXT_RESULT_PTR
Trust::cssmResult()
66 if (mResult
== kSecTrustResultInvalid
)
67 MacOSError::throwMe(errSecNotAvailable
);
72 // SecCertificateRef -> CssmData
73 CssmData
cfCertificateData(SecCertificateRef certificate
)
75 return gTypes().certificate
.required(certificate
)->data();
78 // SecPolicyRef -> CssmField (CFDataRef/NULL or oid/value of a SecPolicy)
79 CssmField
cfField(SecPolicyRef item
)
81 RefPointer
<Policy
> policy
= gTypes().policy
.required(SecPolicyRef(item
));
82 return CssmField(policy
->oid(), policy
->value());
85 // SecKeychain -> CssmDlDbHandle
86 CSSM_DL_DB_HANDLE
cfKeychain(SecKeychainRef ref
)
88 Keychain keychain
= gTypes().keychain
.required(ref
);
89 return keychain
->database()->handle();
94 // Here's the big "E" - evaluation.
95 // We build most of the CSSM-layer input structures dynamically right here;
96 // they will auto-destruct when we're done. The output structures are kept
97 // around (in our data members) for later analysis.
98 // Note that evaluate() can be called repeatedly, so we must be careful to
99 // dispose of prior results.
101 void Trust::evaluate()
103 // if we have evaluated before, release prior result
106 // build the target cert group
107 CFToVector
<CssmData
, SecCertificateRef
, cfCertificateData
> subjects(mCerts
);
108 CertGroup
subjectCertGroup(CSSM_CERT_X_509v3
,
109 CSSM_CERT_ENCODING_BER
, CSSM_CERTGROUP_DATA
);
110 subjectCertGroup
.count() = subjects
;
111 subjectCertGroup
.blobCerts() = subjects
;
113 // build a TP_VERIFY_CONTEXT, a veritable nightmare of a data structure
114 TPBuildVerifyContext
context(mAction
);
116 context
.actionData() = cfData(mActionData
);
118 // policies (one at least, please)
119 CFToVector
<CssmField
, SecPolicyRef
, cfField
> policies(mPolicies
);
120 if (policies
.empty())
121 MacOSError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
);
122 context
.setPolicies(policies
, policies
);
124 // anchor certificates
125 CFCopyRef
<CFArrayRef
> anchors(mAnchors
);
127 anchors
= gStore().copyRootCertificates(); // retains
128 CFToVector
<CssmData
, SecCertificateRef
, cfCertificateData
> roots(anchors
);
129 context
.anchors(roots
, roots
);
131 // dlDbList (keychain list)
132 vector
<CSSM_DL_DB_HANDLE
> dlDbList
;
133 for (StorageManager::KeychainList::const_iterator it
= mSearchLibs
.begin();
134 it
!= mSearchLibs
.end(); it
++)
135 dlDbList
.push_back((*it
)->database()->handle());
136 context
.setDlDbList(dlDbList
.size(), &dlDbList
[0]);
141 CssmUniformDate(mVerifyTime
).convertTo(timeString
, sizeof(timeString
));
142 context
.time(timeString
);
147 mTP
->certGroupVerify(subjectCertGroup
, context
, &mTpResult
);
149 } catch (CssmCommonError
&err
) {
150 mTpReturn
= err
.osStatus();
152 mResult
= diagnoseOutcome();
154 // see if we can use the evidence
155 if (mTpResult
.count() > 0
156 && mTpResult
[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER
157 && mTpResult
[0].as
<CSSM_TP_APPLE_EVIDENCE_HEADER
>()->Version
== CSSM_TP_APPLE_EVIDENCE_VERSION
158 && mTpResult
.count() == 3
159 && mTpResult
[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP
160 && mTpResult
[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO
) {
161 evaluateUserTrust(*mTpResult
[1].as
<CertGroup
>(),
162 mTpResult
[2].as
<CSSM_TP_APPLE_EVIDENCE_INFO
>());
164 // unexpected evidence information. Can't use it
165 debug("trusteval", "unexpected evidence ignored");
171 // Classify the TP outcome in terms of a SecTrustResultType
173 SecTrustResultType
Trust::diagnoseOutcome()
176 case noErr
: // peachy
177 return kSecTrustResultUnspecified
;
178 case CSSMERR_TP_CERT_EXPIRED
: // expired cert
179 case CSSMERR_TP_CERT_NOT_VALID_YET
: // mis-expired cert
180 case CSSMERR_TP_NOT_TRUSTED
: // no root, no anchor
181 case CSSMERR_TP_VERIFICATION_FAILURE
: // root does not self-verify
182 case CSSMERR_TP_INVALID_ANCHOR_CERT
: // valid is not an anchor
183 case CSSMERR_TP_VERIFY_ACTION_FAILED
: // policy action failed
184 return kSecTrustResultRecoverableTrustFailure
;
185 case CSSMERR_TP_INVALID_CERTIFICATE
: // bad certificate
186 return kSecTrustResultFatalTrustFailure
;
188 return kSecTrustResultOtherError
; // unknown
194 // Assuming a good evidence chain, check user trust
195 // settings and set mResult accordingly.
197 void Trust::evaluateUserTrust(const CertGroup
&chain
,
198 const CSSM_TP_APPLE_EVIDENCE_INFO
*infoList
)
200 // extract cert chain as Certificate objects
201 //@@@ once new Evidence is in, use it to build the Certificates
202 mCertChain
.resize(chain
.count());
203 for (uint32 n
= 0; n
< mCertChain
.size(); n
++) {
204 const TPEvidenceInfo
&info
= TPEvidenceInfo::overlay(infoList
[n
]);
205 if (info
.recordId()) {
206 debug("trusteval", "evidence %ld from DLDB source", n
);
207 assert(false); // from DL/DB search - not yet implemented
208 } else if (info
.status(CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
)) {
209 debug("trusteval", "evidence %ld from input cert %ld", n
, info
.index());
210 assert(info
.index() < uint32(CFArrayGetCount(mCerts
)));
211 SecCertificateRef cert
= SecCertificateRef(CFArrayGetValueAtIndex(mCerts
,
213 mCertChain
[n
] = gTypes().certificate
.required(cert
);
214 } else if (info
.status(CSSM_CERT_STATUS_IS_IN_ANCHORS
)) {
215 debug("trusteval", "evidence %ld from anchor cert %ld", n
, info
.index());
216 assert(info
.index() < uint32(CFArrayGetCount(mAnchors
)));
217 SecCertificateRef cert
= SecCertificateRef(CFArrayGetValueAtIndex(mAnchors
,
219 mCertChain
[n
] = gTypes().certificate
.required(cert
);
221 // unknown source; make a new Certificate for it
222 debug("trusteval", "evidence %ld from unknown source", n
);
224 new Certificate(chain
.blobCerts()[n
],
225 CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_BER
);
229 // now walk the chain, leaf-to-root, checking for user settings
230 TrustStore
&store
= gStore();
231 RefPointer
<Policy
> policy
=
232 gTypes().policy
.required(SecPolicyRef(CFArrayGetValueAtIndex(mPolicies
, 0)));
233 for (mResultIndex
= 0;
234 mResult
== kSecTrustResultUnspecified
&& mResultIndex
< mCertChain
.size();
236 mResult
= store
.find(mCertChain
[mResultIndex
], policy
);
241 // Release TP evidence information.
242 // This information is severely under-defined by CSSM, so we proceed
244 // (a) If the evidence matches an Apple-defined pattern, use specific
245 // knowledge of that format.
246 // (b) Otherwise, assume that the void * are flat blocks of memory.
248 void Trust::releaseTPEvidence(TPVerifyResult
&result
, CssmAllocator
&allocator
)
250 if (result
.count() > 0) { // something to do
251 if (result
[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER
) {
252 // Apple defined evidence form -- use intimate knowledge
253 if (result
[0].as
<CSSM_TP_APPLE_EVIDENCE_HEADER
>()->Version
== CSSM_TP_APPLE_EVIDENCE_VERSION
254 && result
.count() == 3
255 && result
[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP
256 && result
[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO
) {
258 allocator
.free(result
[0].data()); // just a struct
259 result
[1].as
<CertGroup
>()->destroy(allocator
); // CertGroup contents
260 allocator
.free(result
[1].data()); // the CertGroup itself
261 allocator
.free(result
[2].data()); // array of (flat) info structs
263 debug("trusteval", "unrecognized Apple TP evidence format");
264 // drop it -- better leak than kill
267 // unknown format -- blindly assume flat blobs
268 debug("trusteval", "destroying unknown TP evidence format");
269 for (uint32 n
= 0; n
< result
.count(); n
++)
270 allocator
.free(result
[n
].data());
277 // Clear evaluation results unless state is initial (invalid)
279 void Trust::clearResults()
281 if (mResult
!= kSecTrustResultInvalid
) {
282 releaseTPEvidence(mTpResult
, mTP
.allocator());
283 mResult
= kSecTrustResultInvalid
;
289 // Build evidence information
291 void Trust::buildEvidence(CFArrayRef
&certChain
, TPEvidenceInfo
* &statusChain
)
293 if (mResult
== kSecTrustResultInvalid
)
294 MacOSError::throwMe(errSecNotAvailable
);
295 certChain
= mEvidenceReturned
=
296 makeCFArray(gTypes().certificate
, mCertChain
);
297 statusChain
= mTpResult
[2].as
<TPEvidenceInfo
>();