]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/IdentityCursor.cpp
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / IdentityCursor.cpp
1 /*
2 * Copyright (c) 2002-2008,2011-2012 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * IdentityCursor.cpp -- Working with IdentityCursor
24 */
25
26 #include <security_keychain/IdentityCursor.h>
27 #include <security_keychain/Identity.h>
28 #include <security_keychain/Trust.h>
29 #include <security_keychain/Item.h>
30 #include <security_keychain/Certificate.h>
31 #include <security_keychain/KeyItem.h>
32 #include <security_keychain/Globals.h>
33 #include <security_cdsa_utilities/Schema.h>
34 #include <security_cdsa_utilities/KeySchema.h>
35 #include <Security/oidsalg.h>
36 #include <Security/SecKeychainItemPriv.h>
37 #include <security_utilities/simpleprefs.h>
38 #include <sys/param.h>
39
40 using namespace KeychainCore;
41
42 IdentityCursorPolicyAndID::IdentityCursorPolicyAndID(const StorageManager::KeychainList &searchList, CSSM_KEYUSE keyUsage, CFStringRef idString, SecPolicyRef policy, bool returnOnlyValidIdentities) :
43 IdentityCursor(searchList, keyUsage),
44 mPolicy(policy),
45 mIDString(idString),
46 mReturnOnlyValidIdentities(returnOnlyValidIdentities),
47 mPreferredIdentityChecked(false),
48 mPreferredIdentity(nil)
49 {
50 if (mPolicy) {
51 CFRetain(mPolicy);
52 }
53 if (mIDString) {
54 CFRetain(mIDString);
55 }
56 }
57
58 IdentityCursorPolicyAndID::~IdentityCursorPolicyAndID() throw()
59 {
60 if (mPolicy) {
61 CFRelease(mPolicy);
62 }
63 if (mIDString) {
64 CFRelease(mIDString);
65 }
66 }
67
68 void
69 IdentityCursorPolicyAndID::findPreferredIdentity()
70 {
71 char idUTF8[MAXPATHLEN];
72 if (!mIDString || !CFStringGetCString(mIDString, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
73 idUTF8[0] = (char)'\0';
74 uint32_t iprfValue = 'iprf'; // value is specified in host byte order, since kSecTypeItemAttr has type uint32 in the db schema
75 SecKeychainAttribute sAttrs[] = {
76 { kSecTypeItemAttr, sizeof(uint32_t), &iprfValue },
77 { kSecServiceItemAttr, (UInt32)strlen(idUTF8), (char *)idUTF8 }
78 };
79 SecKeychainAttributeList sAttrList = { sizeof(sAttrs) / sizeof(sAttrs[0]), sAttrs };
80
81 // StorageManager::KeychainList keychains;
82 // globals().storageManager.optionalSearchList((CFTypeRef)nil, keychains);
83
84 Item item;
85 KCCursor cursor(mSearchList /*keychains*/, kSecGenericPasswordItemClass, &sAttrList);
86 if (!cursor->next(item))
87 return;
88
89 // get persistent certificate reference
90 SecKeychainAttribute itemAttrs[] = { { kSecGenericItemAttr, 0, NULL } };
91 SecKeychainAttributeList itemAttrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
92 item->getContent(NULL, &itemAttrList, NULL, NULL);
93
94 // find certificate, given persistent reference data
95 CFDataRef pItemRef = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)itemAttrs[0].data, itemAttrs[0].length, kCFAllocatorNull);
96 SecKeychainItemRef certItemRef = nil;
97 OSStatus status = SecKeychainItemCopyFromPersistentReference(pItemRef, &certItemRef);
98 if (pItemRef)
99 CFRelease(pItemRef);
100 item->freeContent(&itemAttrList, NULL);
101 if (status || !certItemRef)
102 return;
103
104 // create identity reference, given certificate
105 Item certItem = ItemImpl::required(SecKeychainItemRef(certItemRef));
106 SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
107 SecPointer<Identity> identity(new Identity(mSearchList /*keychains*/, certificate));
108
109 mPreferredIdentity = identity;
110
111 if (certItemRef)
112 CFRelease(certItemRef);
113 }
114
115 bool
116 IdentityCursorPolicyAndID::next(SecPointer<Identity> &identity)
117 {
118 SecPointer<Identity> currIdentity;
119 Boolean identityOK = true;
120
121 if (!mPreferredIdentityChecked)
122 {
123 try
124 {
125 findPreferredIdentity();
126 }
127 catch(...) {}
128 mPreferredIdentityChecked = true;
129 if (mPreferredIdentity)
130 {
131 identity = mPreferredIdentity;
132 return true;
133 }
134 }
135
136 for (;;)
137 {
138 bool result = IdentityCursor::next(currIdentity); // base class finds the next identity by keyUsage
139 if ( result )
140 {
141 if (mPreferredIdentity && (currIdentity == mPreferredIdentity))
142 {
143 identityOK = false; // we already returned this one, move on to the next
144 continue;
145 }
146
147 // If there was no policy specified, we're done.
148 if ( !mPolicy )
149 {
150 identityOK = true; // return this identity
151 break;
152 }
153
154 // To reduce the number of (potentially expensive) trust evaluations performed, we need
155 // to do some pre-processing to filter out certs that don't match the search criteria.
156 // Rather than try to duplicate the TP's policy logic here, we'll just call the TP with
157 // a single-element certificate array, no anchors, and no keychains to search.
158
159 SecPointer<Certificate> certificate = currIdentity->certificate();
160 CFRef<SecCertificateRef> certRef(certificate->handle());
161 CFRef<CFMutableArrayRef> anchorsArray(CFArrayCreateMutable(NULL, 1, NULL));
162 CFRef<CFMutableArrayRef> certArray(CFArrayCreateMutable(NULL, 1, NULL));
163 if ( !certArray || !anchorsArray )
164 {
165 identityOK = false; // skip this and move on to the next one
166 continue;
167 }
168 CFArrayAppendValue(certArray, certRef);
169
170 SecPointer<Trust> trustLite = new Trust(certArray, mPolicy);
171 StorageManager::KeychainList emptyList;
172 // Set the anchors and keychain search list to be empty
173 trustLite->anchors(anchorsArray);
174 trustLite->searchLibs(emptyList);
175 trustLite->evaluate();
176 SecTrustResultType trustResult = trustLite->result();
177
178 if (trustResult == kSecTrustResultRecoverableTrustFailure ||
179 trustResult == kSecTrustResultFatalTrustFailure)
180 {
181 CFArrayRef certChain = NULL;
182 CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL, *evInfo = NULL;
183 trustLite->buildEvidence(certChain, TPEvidenceInfo::overlayVar(statusChain));
184 if (statusChain)
185 evInfo = &statusChain[0];
186 if (!evInfo || evInfo->NumStatusCodes > 0) // per-cert codes means we can't use this cert for this policy
187 trustResult = kSecTrustResultInvalid; // handled below
188 if (certChain)
189 CFRelease(certChain);
190 }
191 if (trustResult == kSecTrustResultInvalid)
192 {
193 identityOK = false; // move on to the next one
194 continue;
195 }
196
197 // If trust evaluation isn't requested, we're done.
198 if ( !mReturnOnlyValidIdentities )
199 {
200 identityOK = true; // return this identity
201 break;
202 }
203
204 // Perform a full trust evaluation on the certificate with the specified policy.
205 SecPointer<Trust> trust = new Trust(certArray, mPolicy);
206 trust->evaluate();
207 trustResult = trust->result();
208
209 if (trustResult == kSecTrustResultInvalid ||
210 trustResult == kSecTrustResultRecoverableTrustFailure ||
211 trustResult == kSecTrustResultFatalTrustFailure)
212 {
213 identityOK = false; // move on to the next one
214 continue;
215 }
216
217 identityOK = true; // this one was OK; return it.
218 break;
219 }
220 else
221 {
222 identityOK = false; // no more left.
223 break;
224 }
225 } // for(;;)
226
227 if ( identityOK )
228 {
229 identity = currIdentity; // caller will release the identity
230 return true;
231 }
232 else
233 {
234 return false;
235 }
236 }
237
238
239 IdentityCursor::IdentityCursor(const StorageManager::KeychainList &searchList, CSSM_KEYUSE keyUsage) :
240 mSearchList(searchList),
241 mKeyCursor(mSearchList, CSSM_DL_DB_RECORD_PRIVATE_KEY, NULL),
242 mMutex(Mutex::recursive)
243 {
244 StLock<Mutex>_(mMutex);
245
246 // If keyUsage is CSSM_KEYUSE_ANY then we need a key that can do everything
247 if (keyUsage & CSSM_KEYUSE_ANY)
248 keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT
249 | CSSM_KEYUSE_DERIVE | CSSM_KEYUSE_SIGN
250 | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_SIGN_RECOVER
251 | CSSM_KEYUSE_VERIFY_RECOVER | CSSM_KEYUSE_WRAP
252 | CSSM_KEYUSE_UNWRAP;
253
254 if (keyUsage & CSSM_KEYUSE_ENCRYPT)
255 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Encrypt, true);
256 if (keyUsage & CSSM_KEYUSE_DECRYPT)
257 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Decrypt, true);
258 if (keyUsage & CSSM_KEYUSE_DERIVE)
259 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Derive, true);
260 if (keyUsage & CSSM_KEYUSE_SIGN)
261 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Sign, true);
262 if (keyUsage & CSSM_KEYUSE_VERIFY)
263 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Verify, true);
264 if (keyUsage & CSSM_KEYUSE_SIGN_RECOVER)
265 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::SignRecover, true);
266 if (keyUsage & CSSM_KEYUSE_VERIFY_RECOVER)
267 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::VerifyRecover, true);
268 if (keyUsage & CSSM_KEYUSE_WRAP)
269 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Wrap, true);
270 if (keyUsage & CSSM_KEYUSE_UNWRAP)
271 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Unwrap, true);
272 }
273
274 IdentityCursor::~IdentityCursor() throw()
275 {
276 }
277
278 CFDataRef
279 IdentityCursor::pubKeyHashForSystemIdentity(CFStringRef domain)
280 {
281 StLock<Mutex>_(mMutex);
282
283 CFDataRef entryValue = nil;
284 auto_ptr<Dictionary> identDict;
285 Dictionary* d = Dictionary::CreateDictionary("com.apple.security.systemidentities", Dictionary::US_System);
286 if (d)
287 {
288 identDict.reset(d);
289 entryValue = identDict->getDataValue(domain);
290 if (entryValue == nil) {
291 /* try for default entry if we're not already looking for default */
292 if(!CFEqual(domain, kSecIdentityDomainDefault)) {
293 entryValue = identDict->getDataValue(kSecIdentityDomainDefault);
294 }
295 }
296 }
297
298 if (entryValue) {
299 CFRetain(entryValue);
300 }
301 return entryValue;
302 }
303
304 bool
305 IdentityCursor::next(SecPointer<Identity> &identity)
306 {
307 StLock<Mutex>_(mMutex);
308
309 for (;;)
310 {
311 if (!mCertificateCursor)
312 {
313 Item key;
314 if (!mKeyCursor->next(key))
315 return false;
316
317 mCurrentKey = static_cast<KeyItem *>(key.get());
318
319 CssmClient::DbUniqueRecord uniqueId = mCurrentKey->dbUniqueRecord();
320 CssmClient::DbAttributes dbAttributes(uniqueId->database(), 1);
321 dbAttributes.add(KeySchema::Label);
322 uniqueId->get(&dbAttributes, NULL);
323 const CssmData &keyHash = dbAttributes[0];
324
325 mCertificateCursor = KCCursor(mSearchList, CSSM_DL_DB_RECORD_X509_CERTIFICATE, NULL);
326 mCertificateCursor->add(CSSM_DB_EQUAL, Schema::kX509CertificatePublicKeyHash, keyHash);
327
328 // if we have entries for the system identities, exclude their public key hashes in the search
329 CFDataRef systemDefaultCertPubKeyHash = pubKeyHashForSystemIdentity(kSecIdentityDomainDefault);
330 if (systemDefaultCertPubKeyHash) {
331 CssmData pkHash((void *)CFDataGetBytePtr(systemDefaultCertPubKeyHash), CFDataGetLength(systemDefaultCertPubKeyHash));
332 mCertificateCursor->add(CSSM_DB_NOT_EQUAL, Schema::kX509CertificatePublicKeyHash, pkHash);
333 CFRelease(systemDefaultCertPubKeyHash);
334 }
335 CFDataRef kerbKDCCertPubKeyHash = pubKeyHashForSystemIdentity(kSecIdentityDomainKerberosKDC);
336 if (kerbKDCCertPubKeyHash) {
337 CssmData pkHash((void *)CFDataGetBytePtr(kerbKDCCertPubKeyHash), CFDataGetLength(kerbKDCCertPubKeyHash));
338 mCertificateCursor->add(CSSM_DB_NOT_EQUAL, Schema::kX509CertificatePublicKeyHash, pkHash);
339 CFRelease(kerbKDCCertPubKeyHash);
340 }
341 }
342
343 Item cert;
344 if (mCertificateCursor->next(cert))
345 {
346 SecPointer<Certificate> certificate(static_cast<Certificate *>(cert.get()));
347 identity = new Identity(mCurrentKey, certificate);
348 return true;
349 }
350 else
351 mCertificateCursor = KCCursor();
352 }
353 }