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() _NOEXCEPT 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, (SecItemClass) 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() _NOEXCEPT 275 { 276 } 277 278 CFDataRef CF_RETURNS_RETAINED 279 IdentityCursor::pubKeyHashForSystemIdentity(CFStringRef domain) 280 { 281 StLock<Mutex>_(mMutex); 282 283 CFDataRef entryValue = nil; 284 unique_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, (SecItemClass) 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 }