2 * Copyright (c) 2002-2008,2011-2012 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 * IdentityCursor.cpp -- Working with IdentityCursor
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>
40 using namespace KeychainCore
;
42 IdentityCursorPolicyAndID::IdentityCursorPolicyAndID(const StorageManager::KeychainList
&searchList
, CSSM_KEYUSE keyUsage
, CFStringRef idString
, SecPolicyRef policy
, bool returnOnlyValidIdentities
) :
43 IdentityCursor(searchList
, keyUsage
),
46 mReturnOnlyValidIdentities(returnOnlyValidIdentities
),
47 mPreferredIdentityChecked(false),
48 mPreferredIdentity(nil
)
57 IdentityCursorPolicyAndID::~IdentityCursorPolicyAndID() throw()
67 IdentityCursorPolicyAndID::findPreferredIdentity()
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
, (UInt32
)strlen(idUTF8
), (char *)idUTF8
}
77 SecKeychainAttributeList sAttrList
= { sizeof(sAttrs
) / sizeof(sAttrs
[0]), sAttrs
};
79 // StorageManager::KeychainList keychains;
80 // globals().storageManager.optionalSearchList((CFTypeRef)nil, keychains);
83 KCCursor
cursor(mSearchList
/*keychains*/, kSecGenericPasswordItemClass
, &sAttrList
);
84 if (!cursor
->next(item
))
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
);
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
);
98 item
->freeContent(&itemAttrList
, NULL
);
99 if (status
|| !certItemRef
)
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
));
107 mPreferredIdentity
= identity
;
110 CFRelease(certItemRef
);
114 IdentityCursorPolicyAndID::next(SecPointer
<Identity
> &identity
)
116 SecPointer
<Identity
> currIdentity
;
117 Boolean identityOK
= true;
119 if (!mPreferredIdentityChecked
)
123 findPreferredIdentity();
126 mPreferredIdentityChecked
= true;
127 if (mPreferredIdentity
)
129 identity
= mPreferredIdentity
;
136 bool result
= IdentityCursor::next(currIdentity
); // base class finds the next identity by keyUsage
139 if (mPreferredIdentity
&& (currIdentity
== mPreferredIdentity
))
141 identityOK
= false; // we already returned this one, move on to the next
145 // If there was no policy specified, we're done.
148 identityOK
= true; // return this identity
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.
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
)
163 identityOK
= false; // skip this and move on to the next one
166 CFArrayAppendValue(certArray
, certRef
);
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();
176 if (trustResult
== kSecTrustResultRecoverableTrustFailure
||
177 trustResult
== kSecTrustResultFatalTrustFailure
)
179 CFArrayRef certChain
= NULL
;
180 CSSM_TP_APPLE_EVIDENCE_INFO
*statusChain
= NULL
, *evInfo
= NULL
;
181 trustLite
->buildEvidence(certChain
, TPEvidenceInfo::overlayVar(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
187 CFRelease(certChain
);
189 if (trustResult
== kSecTrustResultInvalid
)
191 identityOK
= false; // move on to the next one
195 // If trust evaluation isn't requested, we're done.
196 if ( !mReturnOnlyValidIdentities
)
198 identityOK
= true; // return this identity
202 // Perform a full trust evaluation on the certificate with the specified policy.
203 SecPointer
<Trust
> trust
= new Trust(certArray
, mPolicy
);
205 trustResult
= trust
->result();
207 if (trustResult
== kSecTrustResultInvalid
||
208 trustResult
== kSecTrustResultRecoverableTrustFailure
||
209 trustResult
== kSecTrustResultFatalTrustFailure
)
211 identityOK
= false; // move on to the next one
215 identityOK
= true; // this one was OK; return it.
220 identityOK
= false; // no more left.
227 identity
= currIdentity
; // caller will release the identity
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
)
242 StLock
<Mutex
>_(mMutex
);
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
;
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);
272 IdentityCursor::~IdentityCursor() throw()
277 IdentityCursor::pubKeyHashForSystemIdentity(CFStringRef domain
)
279 StLock
<Mutex
>_(mMutex
);
281 CFDataRef entryValue
= nil
;
282 auto_ptr
<Dictionary
> identDict
;
283 Dictionary
* d
= Dictionary::CreateDictionary("com.apple.security.systemidentities", Dictionary::US_System
);
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
);
297 CFRetain(entryValue
);
303 IdentityCursor::next(SecPointer
<Identity
> &identity
)
305 StLock
<Mutex
>_(mMutex
);
309 if (!mCertificateCursor
)
312 if (!mKeyCursor
->next(key
))
315 mCurrentKey
= static_cast<KeyItem
*>(key
.get());
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];
323 mCertificateCursor
= KCCursor(mSearchList
, CSSM_DL_DB_RECORD_X509_CERTIFICATE
, NULL
);
324 mCertificateCursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificatePublicKeyHash
, keyHash
);
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
);
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
);
342 if (mCertificateCursor
->next(cert
))
344 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(cert
.get()));
345 identity
= new Identity(mCurrentKey
, certificate
);
349 mCertificateCursor
= KCCursor();