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
)
58 IdentityCursorPolicyAndID::~IdentityCursorPolicyAndID() _NOEXCEPT
69 IdentityCursorPolicyAndID::findPreferredIdentity()
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
}
79 SecKeychainAttributeList sAttrList
= { sizeof(sAttrs
) / sizeof(sAttrs
[0]), sAttrs
};
81 // StorageManager::KeychainList keychains;
82 // globals().storageManager.optionalSearchList((CFTypeRef)nil, keychains);
85 KCCursor
cursor(mSearchList
/*keychains*/, kSecGenericPasswordItemClass
, &sAttrList
);
86 if (!cursor
->next(item
))
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
);
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
);
100 item
->freeContent(&itemAttrList
, NULL
);
101 if (status
|| !certItemRef
)
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
));
109 mPreferredIdentity
= identity
;
112 CFRelease(certItemRef
);
116 IdentityCursorPolicyAndID::next(SecPointer
<Identity
> &identity
)
118 SecPointer
<Identity
> currIdentity
;
119 Boolean identityOK
= true;
121 if (!mPreferredIdentityChecked
)
125 findPreferredIdentity();
128 mPreferredIdentityChecked
= true;
129 if (mPreferredIdentity
)
131 identity
= mPreferredIdentity
;
138 bool result
= IdentityCursor::next(currIdentity
); // base class finds the next identity by keyUsage
141 if (mPreferredIdentity
&& (currIdentity
== mPreferredIdentity
))
143 identityOK
= false; // we already returned this one, move on to the next
147 // If there was no policy specified, we're done.
150 identityOK
= true; // return this identity
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.
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
)
165 identityOK
= false; // skip this and move on to the next one
168 CFArrayAppendValue(certArray
, certRef
);
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();
178 if (trustResult
== kSecTrustResultRecoverableTrustFailure
||
179 trustResult
== kSecTrustResultFatalTrustFailure
)
181 CFArrayRef certChain
= NULL
;
182 CSSM_TP_APPLE_EVIDENCE_INFO
*statusChain
= NULL
, *evInfo
= NULL
;
183 trustLite
->buildEvidence(certChain
, TPEvidenceInfo::overlayVar(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
189 CFRelease(certChain
);
191 if (trustResult
== kSecTrustResultInvalid
)
193 identityOK
= false; // move on to the next one
197 // If trust evaluation isn't requested, we're done.
198 if ( !mReturnOnlyValidIdentities
)
200 identityOK
= true; // return this identity
204 // Perform a full trust evaluation on the certificate with the specified policy.
205 SecPointer
<Trust
> trust
= new Trust(certArray
, mPolicy
);
207 trustResult
= trust
->result();
209 if (trustResult
== kSecTrustResultInvalid
||
210 trustResult
== kSecTrustResultRecoverableTrustFailure
||
211 trustResult
== kSecTrustResultFatalTrustFailure
)
213 identityOK
= false; // move on to the next one
217 identityOK
= true; // this one was OK; return it.
222 identityOK
= false; // no more left.
229 identity
= currIdentity
; // caller will release the identity
239 IdentityCursor::IdentityCursor(const StorageManager::KeychainList
&searchList
, CSSM_KEYUSE keyUsage
) :
240 mSearchList(searchList
),
241 mKeyCursor(mSearchList
, (SecItemClass
) CSSM_DL_DB_RECORD_PRIVATE_KEY
, NULL
),
242 mMutex(Mutex::recursive
)
244 StLock
<Mutex
>_(mMutex
);
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
;
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);
274 IdentityCursor::~IdentityCursor() _NOEXCEPT
278 CFDataRef CF_RETURNS_RETAINED
279 IdentityCursor::pubKeyHashForSystemIdentity(CFStringRef domain
)
281 StLock
<Mutex
>_(mMutex
);
283 CFDataRef entryValue
= nil
;
284 unique_ptr
<Dictionary
> identDict
;
285 Dictionary
* d
= Dictionary::CreateDictionary("com.apple.security.systemidentities", Dictionary::US_System
);
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
);
299 CFRetain(entryValue
);
305 IdentityCursor::next(SecPointer
<Identity
> &identity
)
307 StLock
<Mutex
>_(mMutex
);
311 if (!mCertificateCursor
)
314 if (!mKeyCursor
->next(key
))
317 mCurrentKey
= static_cast<KeyItem
*>(key
.get());
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];
325 mCertificateCursor
= KCCursor(mSearchList
, (SecItemClass
) CSSM_DL_DB_RECORD_X509_CERTIFICATE
, NULL
);
326 mCertificateCursor
->add(CSSM_DB_EQUAL
, Schema::kX509CertificatePublicKeyHash
, keyHash
);
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
);
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
);
344 if (mCertificateCursor
->next(cert
))
346 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(cert
.get()));
347 identity
= new Identity(mCurrentKey
, certificate
);
351 mCertificateCursor
= KCCursor();