]> git.saurik.com Git - apple/security.git/blob - Keychain/Trust.cpp
dfe46b35cde33e08d27f941f314a6f26d3dbf74d
[apple/security.git] / Keychain / Trust.cpp
1 /*
2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18 //
19 // Trust.cpp
20 //
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"
27
28 using namespace KeychainCore;
29
30
31 //
32 // For now, we use a global TrustStore
33 //
34 ModuleNexus<TrustStore> Trust::gStore;
35
36
37 //
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.
41 //
42 inline bool operator == (const CSSM_DL_DB_HANDLE &h1, const CSSM_DL_DB_HANDLE &h2)
43 {
44 return Security::operator == (h1, h2);
45 }
46
47
48 //
49 // Construct a Trust object with suitable defaults.
50 // Use setters for additional arguments before calling evaluate().
51 //
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)
56 {
57 // set default search list from user's default
58 globals().storageManager.getSearchList(mSearchLibs);
59 }
60
61
62 //
63 // Clean up a Trust object
64 //
65 Trust::~Trust() throw()
66 {
67 clearResults();
68 }
69
70
71 //
72 // Retrieve the last TP evaluation result, if any
73 //
74 CSSM_TP_VERIFY_CONTEXT_RESULT_PTR Trust::cssmResult()
75 {
76 if (mResult == kSecTrustResultInvalid)
77 MacOSError::throwMe(errSecTrustNotAvailable);
78 return &mTpResult;
79 }
80
81
82 // SecCertificateRef -> CssmData
83 CssmData cfCertificateData(SecCertificateRef certificate)
84 {
85 return Certificate::required(certificate)->data();
86 }
87
88 // SecPolicyRef -> CssmField (CFDataRef/NULL or oid/value of a SecPolicy)
89 CssmField cfField(SecPolicyRef item)
90 {
91 SecPointer<Policy> policy = Policy::required(SecPolicyRef(item));
92 return CssmField(policy->oid(), policy->value());
93 }
94
95 // SecKeychain -> CssmDlDbHandle
96 CSSM_DL_DB_HANDLE cfKeychain(SecKeychainRef ref)
97 {
98 Keychain keychain = KeychainImpl::required(ref);
99 return keychain->database()->handle();
100 }
101
102
103 //
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.
110 //
111 void Trust::evaluate()
112 {
113 // if we have evaluated before, release prior result
114 clearResults();
115
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;
122
123 // build a TP_VERIFY_CONTEXT, a veritable nightmare of a data structure
124 TPBuildVerifyContext context(mAction);
125 if (mActionData)
126 context.actionData() = cfData(mActionData);
127
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);
133
134 // anchor certificates
135 CFCopyRef<CFArrayRef> anchors(mAnchors);
136 if (!anchors)
137 anchors = gStore().copyRootCertificates(); // retains
138 CFToVector<CssmData, SecCertificateRef, cfCertificateData> roots(anchors);
139 context.anchors(roots, roots);
140
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++)
145 {
146 try
147 {
148 dlDbList.push_back((*it)->database()->handle());
149 }
150 catch (...)
151 {
152 }
153 }
154 context.setDlDbList(dlDbList.size(), &dlDbList[0]);
155
156 // verification time
157 char timeString[15];
158 if (mVerifyTime) {
159 CssmUniformDate(static_cast<CFDateRef>(mVerifyTime)).convertTo(
160 timeString, sizeof(timeString));
161 context.time(timeString);
162 }
163
164 // Go TP!
165 try {
166 mTP->certGroupVerify(subjectCertGroup, context, &mTpResult);
167 mTpReturn = noErr;
168 } catch (CssmCommonError &err) {
169 mTpReturn = err.osStatus();
170 }
171 mResult = diagnoseOutcome();
172
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);
182 } else {
183 // unexpected evidence information. Can't use it
184 secdebug("trusteval", "unexpected evidence ignored");
185 }
186 }
187
188
189 //
190 // Classify the TP outcome in terms of a SecTrustResultType
191 //
192 SecTrustResultType Trust::diagnoseOutcome()
193 {
194 switch (mTpReturn) {
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;
206 default:
207 return kSecTrustResultOtherError; // unknown
208 }
209 }
210
211
212 //
213 // Assuming a good evidence chain, check user trust
214 // settings and set mResult accordingly.
215 //
216 void Trust::evaluateUserTrust(const CertGroup &chain,
217 const CSSM_TP_APPLE_EVIDENCE_INFO *infoList, CFCopyRef<CFArrayRef> anchors)
218 {
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,
234 info.index()));
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,
240 info.index()));
241 mCertChain[n] = Certificate::required(cert);
242 } else {
243 // unknown source; make a new Certificate for it
244 secdebug("trusteval", "evidence %ld from unknown source", n);
245 mCertChain[n] =
246 new Certificate(chain.blobCerts()[n],
247 CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER);
248 }
249 }
250
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();
257 mResultIndex++)
258 {
259 if (!mCertChain[mResultIndex])
260 {
261 assert(false);
262 continue;
263 }
264 mResult = store.find(mCertChain[mResultIndex], policy);
265 }
266 }
267
268
269 //
270 // Release TP evidence information.
271 // This information is severely under-defined by CSSM, so we proceed
272 // as follows:
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.
276 //
277 void Trust::releaseTPEvidence(TPVerifyResult &result, CssmAllocator &allocator)
278 {
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) {
286 // proper format
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
296 } else {
297 secdebug("trusteval", "unrecognized Apple TP evidence format");
298 // drop it -- better leak than kill
299 }
300 } else {
301 // unknown format -- blindly assume flat blobs
302 secdebug("trusteval", "destroying unknown TP evidence format");
303 for (uint32 n = 0; n < result.count(); n++)
304 {
305 allocator.free(result[n].data());
306 }
307 }
308
309 allocator.free (result.Evidence);
310 }
311 }
312
313
314 //
315 // Clear evaluation results unless state is initial (invalid)
316 //
317 void Trust::clearResults()
318 {
319 if (mResult != kSecTrustResultInvalid) {
320 releaseTPEvidence(mTpResult, mTP.allocator());
321 mResult = kSecTrustResultInvalid;
322 }
323 }
324
325
326 // Convert a SecPointer to a CF object.
327 static SecCertificateRef
328 convert(const SecPointer<Certificate> &certificate)
329 {
330 return *certificate;
331 }
332
333 //
334 // Build evidence information
335 //
336 void Trust::buildEvidence(CFArrayRef &certChain, TPEvidenceInfo * &statusChain)
337 {
338 if (mResult == kSecTrustResultInvalid)
339 MacOSError::throwMe(errSecTrustNotAvailable);
340 certChain = mEvidenceReturned =
341 makeCFArray(convert, mCertChain);
342 statusChain = mTpResult[2].as<TPEvidenceInfo>();
343 }
344
345
346 //
347 // Given a DL_DB_HANDLE, locate the Keychain object (from the search list)
348 //
349 Keychain Trust::keychainByDLDb(const CSSM_DL_DB_HANDLE &handle) const
350 {
351 for (StorageManager::KeychainList::const_iterator it = mSearchLibs.begin();
352 it != mSearchLibs.end(); it++)
353 {
354 try
355 {
356 if ((*it)->database()->handle() == handle)
357 return *it;
358 }
359 catch (...)
360 {
361 }
362 }
363
364 // could not find in search list - internal error
365 assert(false);
366 return Keychain();
367 }