]> git.saurik.com Git - apple/security.git/blob - libsecurity_keychain/lib/IdentityCursor.cpp
Security-55179.13.tar.gz
[apple/security.git] / libsecurity_keychain / lib / IdentityCursor.cpp
1 /*
2 * Copyright (c) 2002-2008 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 IdentityCursorPolicyAndID::~IdentityCursorPolicyAndID() throw()
58 {
59 if (mPolicy)
60 CFRelease(mPolicy);
61
62 if (mIDString)
63 CFRelease(mIDString);
64 }
65
66 void
67 IdentityCursorPolicyAndID::findPreferredIdentity()
68 {
69 char idUTF8[MAXPATHLEN];
70 if (!mIDString || !CFStringGetCString(mIDString, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
71 idUTF8[0] = (char)'\0';
72 uint32_t iprfValue = 'iprf'; // value is specified in host byte order, since kSecTypeItemAttr has type uint32 in the db schema
73 SecKeychainAttribute sAttrs[] = {
74 { kSecTypeItemAttr, sizeof(uint32_t), &iprfValue },
75 { kSecServiceItemAttr, strlen(idUTF8), (char *)idUTF8 }
76 };
77 SecKeychainAttributeList sAttrList = { sizeof(sAttrs) / sizeof(sAttrs[0]), sAttrs };
78
79 // StorageManager::KeychainList keychains;
80 // globals().storageManager.optionalSearchList((CFTypeRef)nil, keychains);
81
82 Item item;
83 KCCursor cursor(mSearchList /*keychains*/, kSecGenericPasswordItemClass, &sAttrList);
84 if (!cursor->next(item))
85 return;
86
87 // get persistent certificate reference
88 SecKeychainAttribute itemAttrs[] = { { kSecGenericItemAttr, 0, NULL } };
89 SecKeychainAttributeList itemAttrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
90 item->getContent(NULL, &itemAttrList, NULL, NULL);
91
92 // find certificate, given persistent reference data
93 CFDataRef pItemRef = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)itemAttrs[0].data, itemAttrs[0].length, kCFAllocatorNull);
94 SecKeychainItemRef certItemRef = nil;
95 OSStatus status = SecKeychainItemCopyFromPersistentReference(pItemRef, &certItemRef);
96 if (pItemRef)
97 CFRelease(pItemRef);
98 item->freeContent(&itemAttrList, NULL);
99 if (status || !certItemRef)
100 return;
101
102 // create identity reference, given certificate
103 Item certItem = ItemImpl::required(SecKeychainItemRef(certItemRef));
104 SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
105 SecPointer<Identity> identity(new Identity(mSearchList /*keychains*/, certificate));
106
107 mPreferredIdentity = identity;
108
109 if (certItemRef)
110 CFRelease(certItemRef);
111 }
112
113 bool
114 IdentityCursorPolicyAndID::next(SecPointer<Identity> &identity)
115 {
116 SecPointer<Identity> currIdentity;
117 Boolean identityOK = true;
118
119 if (!mPreferredIdentityChecked)
120 {
121 try
122 {
123 findPreferredIdentity();
124 }
125 catch(...) {}
126 mPreferredIdentityChecked = true;
127 if (mPreferredIdentity)
128 {
129 identity = mPreferredIdentity;
130 return true;
131 }
132 }
133
134 for (;;)
135 {
136 bool result = IdentityCursor::next(currIdentity); // base class finds the next identity by keyUsage
137 if ( result )
138 {
139 if (mPreferredIdentity && (currIdentity == mPreferredIdentity))
140 {
141 identityOK = false; // we already returned this one, move on to the next
142 continue;
143 }
144
145 // If there was no policy specified, we're done.
146 if ( !mPolicy )
147 {
148 identityOK = true; // return this identity
149 break;
150 }
151
152 // To reduce the number of (potentially expensive) trust evaluations performed, we need
153 // to do some pre-processing to filter out certs that don't match the search criteria.
154 // Rather than try to duplicate the TP's policy logic here, we'll just call the TP with
155 // a single-element certificate array, no anchors, and no keychains to search.
156
157 SecPointer<Certificate> certificate = currIdentity->certificate();
158 CFRef<SecCertificateRef> certRef(certificate->handle());
159 CFRef<CFMutableArrayRef> anchorsArray(CFArrayCreateMutable(NULL, 1, NULL));
160 CFRef<CFMutableArrayRef> certArray(CFArrayCreateMutable(NULL, 1, NULL));
161 if ( !certArray || !anchorsArray )
162 {
163 identityOK = false; // skip this and move on to the next one
164 continue;
165 }
166 CFArrayAppendValue(certArray, certRef);
167
168 SecPointer<Trust> trustLite = new Trust(certArray, mPolicy);
169 StorageManager::KeychainList emptyList;
170 // Set the anchors and keychain search list to be empty
171 trustLite->anchors(anchorsArray);
172 trustLite->searchLibs(emptyList);
173 trustLite->evaluate();
174 SecTrustResultType trustResult = trustLite->result();
175
176 if (trustResult == kSecTrustResultRecoverableTrustFailure ||
177 trustResult == kSecTrustResultFatalTrustFailure)
178 {
179 CFArrayRef certChain = NULL;
180 CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL, *evInfo = NULL;
181 trustLite->buildEvidence(certChain, TPEvidenceInfo::overlayVar(statusChain));
182 if (statusChain)
183 evInfo = &statusChain[0];
184 if (!evInfo || evInfo->NumStatusCodes > 0) // per-cert codes means we can't use this cert for this policy
185 trustResult = kSecTrustResultInvalid; // handled below
186 if (certChain)
187 CFRelease(certChain);
188 }
189 if (trustResult == kSecTrustResultInvalid)
190 {
191 identityOK = false; // move on to the next one
192 continue;
193 }
194
195 // If trust evaluation isn't requested, we're done.
196 if ( !mReturnOnlyValidIdentities )
197 {
198 identityOK = true; // return this identity
199 break;
200 }
201
202 // Perform a full trust evaluation on the certificate with the specified policy.
203 SecPointer<Trust> trust = new Trust(certArray, mPolicy);
204 trust->evaluate();
205 trustResult = trust->result();
206
207 if (trustResult == kSecTrustResultInvalid ||
208 trustResult == kSecTrustResultRecoverableTrustFailure ||
209 trustResult == kSecTrustResultFatalTrustFailure)
210 {
211 identityOK = false; // move on to the next one
212 continue;
213 }
214
215 identityOK = true; // this one was OK; return it.
216 break;
217 }
218 else
219 {
220 identityOK = false; // no more left.
221 break;
222 }
223 } // for(;;)
224
225 if ( identityOK )
226 {
227 identity = currIdentity; // caller will release the identity
228 return true;
229 }
230 else
231 {
232 return false;
233 }
234 }
235
236
237 IdentityCursor::IdentityCursor(const StorageManager::KeychainList &searchList, CSSM_KEYUSE keyUsage) :
238 mSearchList(searchList),
239 mKeyCursor(mSearchList, CSSM_DL_DB_RECORD_PRIVATE_KEY, NULL),
240 mMutex(Mutex::recursive)
241 {
242 StLock<Mutex>_(mMutex);
243
244 // If keyUsage is CSSM_KEYUSE_ANY then we need a key that can do everything
245 if (keyUsage & CSSM_KEYUSE_ANY)
246 keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT
247 | CSSM_KEYUSE_DERIVE | CSSM_KEYUSE_SIGN
248 | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_SIGN_RECOVER
249 | CSSM_KEYUSE_VERIFY_RECOVER | CSSM_KEYUSE_WRAP
250 | CSSM_KEYUSE_UNWRAP;
251
252 if (keyUsage & CSSM_KEYUSE_ENCRYPT)
253 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Encrypt, true);
254 if (keyUsage & CSSM_KEYUSE_DECRYPT)
255 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Decrypt, true);
256 if (keyUsage & CSSM_KEYUSE_DERIVE)
257 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Derive, true);
258 if (keyUsage & CSSM_KEYUSE_SIGN)
259 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Sign, true);
260 if (keyUsage & CSSM_KEYUSE_VERIFY)
261 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Verify, true);
262 if (keyUsage & CSSM_KEYUSE_SIGN_RECOVER)
263 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::SignRecover, true);
264 if (keyUsage & CSSM_KEYUSE_VERIFY_RECOVER)
265 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::VerifyRecover, true);
266 if (keyUsage & CSSM_KEYUSE_WRAP)
267 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Wrap, true);
268 if (keyUsage & CSSM_KEYUSE_UNWRAP)
269 mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Unwrap, true);
270 }
271
272 IdentityCursor::~IdentityCursor() throw()
273 {
274 }
275
276 CFDataRef
277 IdentityCursor::pubKeyHashForSystemIdentity(CFStringRef domain)
278 {
279 StLock<Mutex>_(mMutex);
280
281 CFDataRef entryValue = nil;
282 auto_ptr<Dictionary> identDict;
283 Dictionary* d = Dictionary::CreateDictionary("com.apple.security.systemidentities", Dictionary::US_System);
284 if (d)
285 {
286 identDict.reset(d);
287 entryValue = identDict->getDataValue(domain);
288 if (entryValue == nil) {
289 /* try for default entry if we're not already looking for default */
290 if(!CFEqual(domain, kSecIdentityDomainDefault)) {
291 entryValue = identDict->getDataValue(kSecIdentityDomainDefault);
292 }
293 }
294 }
295
296 if (entryValue) {
297 CFRetain(entryValue);
298 }
299 return entryValue;
300 }
301
302 bool
303 IdentityCursor::next(SecPointer<Identity> &identity)
304 {
305 StLock<Mutex>_(mMutex);
306
307 for (;;)
308 {
309 if (!mCertificateCursor)
310 {
311 Item key;
312 if (!mKeyCursor->next(key))
313 return false;
314
315 mCurrentKey = static_cast<KeyItem *>(key.get());
316
317 CssmClient::DbUniqueRecord uniqueId = mCurrentKey->dbUniqueRecord();
318 CssmClient::DbAttributes dbAttributes(uniqueId->database(), 1);
319 dbAttributes.add(KeySchema::Label);
320 uniqueId->get(&dbAttributes, NULL);
321 const CssmData &keyHash = dbAttributes[0];
322
323 mCertificateCursor = KCCursor(mSearchList, CSSM_DL_DB_RECORD_X509_CERTIFICATE, NULL);
324 mCertificateCursor->add(CSSM_DB_EQUAL, Schema::kX509CertificatePublicKeyHash, keyHash);
325
326 // if we have entries for the system identities, exclude their public key hashes in the search
327 CFDataRef systemDefaultCertPubKeyHash = pubKeyHashForSystemIdentity(kSecIdentityDomainDefault);
328 if (systemDefaultCertPubKeyHash) {
329 CssmData pkHash((void *)CFDataGetBytePtr(systemDefaultCertPubKeyHash), CFDataGetLength(systemDefaultCertPubKeyHash));
330 mCertificateCursor->add(CSSM_DB_NOT_EQUAL, Schema::kX509CertificatePublicKeyHash, pkHash);
331 CFRelease(systemDefaultCertPubKeyHash);
332 }
333 CFDataRef kerbKDCCertPubKeyHash = pubKeyHashForSystemIdentity(kSecIdentityDomainKerberosKDC);
334 if (kerbKDCCertPubKeyHash) {
335 CssmData pkHash((void *)CFDataGetBytePtr(kerbKDCCertPubKeyHash), CFDataGetLength(kerbKDCCertPubKeyHash));
336 mCertificateCursor->add(CSSM_DB_NOT_EQUAL, Schema::kX509CertificatePublicKeyHash, pkHash);
337 CFRelease(kerbKDCCertPubKeyHash);
338 }
339 }
340
341 Item cert;
342 if (mCertificateCursor->next(cert))
343 {
344 SecPointer<Certificate> certificate(static_cast<Certificate *>(cert.get()));
345 identity = new Identity(mCurrentKey, certificate);
346 return true;
347 }
348 else
349 mCertificateCursor = KCCursor();
350 }
351 }