]> git.saurik.com Git - apple/security.git/blob - Keychain/Trust.cpp
14cb813d9b6533740fb3326710c6bba22c12385a
[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 // Construct a Trust object with suitable defaults.
39 // Use setters for additional arguments before calling evaluate().
40 //
41 Trust::Trust(CFTypeRef certificates, CFTypeRef policies)
42 : mTP(gGuidAppleX509TP), mAction(CSSM_TP_ACTION_DEFAULT),
43 mVerifyTime(NULL),
44 mCerts(cfArrayize(certificates)), mPolicies(cfArrayize(policies)),
45 mResult(kSecTrustResultInvalid)
46 {
47 // set default search list from user's default
48 globals().storageManager.getSearchList(mSearchLibs);
49 }
50
51
52 //
53 // Clean up a Trust object
54 //
55 Trust::~Trust()
56 {
57 clearResults();
58 }
59
60
61 //
62 // Retrieve the last TP evaluation result, if any
63 //
64 CSSM_TP_VERIFY_CONTEXT_RESULT_PTR Trust::cssmResult()
65 {
66 if (mResult == kSecTrustResultInvalid)
67 MacOSError::throwMe(errSecNotAvailable);
68 return &mTpResult;
69 }
70
71
72 // SecCertificateRef -> CssmData
73 CssmData cfCertificateData(SecCertificateRef certificate)
74 {
75 return gTypes().certificate.required(certificate)->data();
76 }
77
78 // SecPolicyRef -> CssmField (CFDataRef/NULL or oid/value of a SecPolicy)
79 CssmField cfField(SecPolicyRef item)
80 {
81 RefPointer<Policy> policy = gTypes().policy.required(SecPolicyRef(item));
82 return CssmField(policy->oid(), policy->value());
83 }
84
85 // SecKeychain -> CssmDlDbHandle
86 CSSM_DL_DB_HANDLE cfKeychain(SecKeychainRef ref)
87 {
88 Keychain keychain = gTypes().keychain.required(ref);
89 return keychain->database()->handle();
90 }
91
92
93 //
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.
100 //
101 void Trust::evaluate()
102 {
103 // if we have evaluated before, release prior result
104 clearResults();
105
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;
112
113 // build a TP_VERIFY_CONTEXT, a veritable nightmare of a data structure
114 TPBuildVerifyContext context(mAction);
115 if (mActionData)
116 context.actionData() = cfData(mActionData);
117
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);
123
124 // anchor certificates
125 CFCopyRef<CFArrayRef> anchors(mAnchors);
126 if (!anchors)
127 anchors = gStore().copyRootCertificates(); // retains
128 CFToVector<CssmData, SecCertificateRef, cfCertificateData> roots(anchors);
129 context.anchors(roots, roots);
130
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]);
137
138 // verification time
139 char timeString[15];
140 if (mVerifyTime) {
141 CssmUniformDate(mVerifyTime).convertTo(timeString, sizeof(timeString));
142 context.time(timeString);
143 }
144
145 // Go TP!
146 try {
147 mTP->certGroupVerify(subjectCertGroup, context, &mTpResult);
148 mTpReturn = noErr;
149 } catch (CssmCommonError &err) {
150 mTpReturn = err.osStatus();
151 }
152 mResult = diagnoseOutcome();
153
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>());
163 } else {
164 // unexpected evidence information. Can't use it
165 debug("trusteval", "unexpected evidence ignored");
166 }
167 }
168
169
170 //
171 // Classify the TP outcome in terms of a SecTrustResultType
172 //
173 SecTrustResultType Trust::diagnoseOutcome()
174 {
175 switch (mTpReturn) {
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;
187 default:
188 return kSecTrustResultOtherError; // unknown
189 }
190 }
191
192
193 //
194 // Assuming a good evidence chain, check user trust
195 // settings and set mResult accordingly.
196 //
197 void Trust::evaluateUserTrust(const CertGroup &chain,
198 const CSSM_TP_APPLE_EVIDENCE_INFO *infoList)
199 {
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,
212 info.index()));
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,
218 info.index()));
219 mCertChain[n] = gTypes().certificate.required(cert);
220 } else {
221 // unknown source; make a new Certificate for it
222 debug("trusteval", "evidence %ld from unknown source", n);
223 mCertChain[n] =
224 new Certificate(chain.blobCerts()[n],
225 CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER);
226 }
227 }
228
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();
235 mResultIndex++)
236 mResult = store.find(mCertChain[mResultIndex], policy);
237 }
238
239
240 //
241 // Release TP evidence information.
242 // This information is severely under-defined by CSSM, so we proceed
243 // as follows:
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.
247 //
248 void Trust::releaseTPEvidence(TPVerifyResult &result, CssmAllocator &allocator)
249 {
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) {
257 // proper format
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
262 } else {
263 debug("trusteval", "unrecognized Apple TP evidence format");
264 // drop it -- better leak than kill
265 }
266 } else {
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());
271 }
272 }
273 }
274
275
276 //
277 // Clear evaluation results unless state is initial (invalid)
278 //
279 void Trust::clearResults()
280 {
281 if (mResult != kSecTrustResultInvalid) {
282 releaseTPEvidence(mTpResult, mTP.allocator());
283 mResult = kSecTrustResultInvalid;
284 }
285 }
286
287
288 //
289 // Build evidence information
290 //
291 void Trust::buildEvidence(CFArrayRef &certChain, TPEvidenceInfo * &statusChain)
292 {
293 if (mResult == kSecTrustResultInvalid)
294 MacOSError::throwMe(errSecNotAvailable);
295 certChain = mEvidenceReturned =
296 makeCFArray(gTypes().certificate, mCertChain);
297 statusChain = mTpResult[2].as<TPEvidenceInfo>();
298 }