defaultcreds.cpp
1 /* 2 * Copyright (c) 2004,2011-2012,2014 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 24 // 25 // defaultcreds - default computations for keychain open credentials 26 // 27 #include "Keychains.h" 28 #include "defaultcreds.h" 29 #include "StorageManager.h" 30 #include "Globals.h" 31 #include "KCCursor.h" 32 #include <security_cdsa_utilities/Schema.h> 33 #include <algorithm> 34 35 36 namespace Security { 37 namespace KeychainCore { 38 39 using namespace CssmClient; 40 41 42 DefaultCredentials::DefaultCredentials(KeychainImpl* kcImpl, Allocator &alloc) 43 : TrackingAllocator(alloc), AutoCredentials(static_cast<TrackingAllocator&>(*this)), 44 mMade(false), mKeychainImpl (kcImpl) 45 { 46 } 47 48 49 void DefaultCredentials::clear() 50 { 51 TrackingAllocator::reset(); 52 mNeededItems.clear(); 53 mMade = false; 54 } 55 56 57 // 58 // The main driver. 59 // This scans a database for referral records and forms corresponding 60 // credentials to trigger unlocks. 61 // Returns true if any valid unlock credentials were found; false otherwise. 62 // Only throws if the database is messed up. 63 // 64 bool DefaultCredentials::operator () (Db database) 65 { 66 if (!mMade) { 67 try { 68 // before we do anything else, see if we have a relation in the database of the appropriate type 69 KeychainSchema keychainSchema = mKeychainImpl->keychainSchema(); 70 if (keychainSchema->hasRecordType(UnlockReferralRecord::recordType)) 71 { 72 clear(); 73 Table<UnlockReferralRecord> referrals(database); 74 for (Table<UnlockReferralRecord>::iterator it = referrals.begin(); it != referrals.end(); it++) { 75 switch ((*it)->type()) { 76 case CSSM_APPLE_UNLOCK_TYPE_KEY_DIRECT: 77 case CSSM_APPLE_UNLOCK_TYPE_WRAPPED_PRIVATE: 78 keyReferral(**it); 79 break; 80 case CSSM_APPLE_UNLOCK_TYPE_KEYBAG: 81 keybagReferral(**it); 82 break; 83 default: 84 secinfo("kcreferral", "referral type %lu (to %s) not supported", 85 (unsigned long)(*it)->type(), (*it)->dbName().c_str()); 86 break; 87 } 88 } 89 } 90 secinfo("kcreferral", "%lu samples generated", (unsigned long)size()); 91 } catch (...) { 92 secinfo("kcreferral", "exception setting default credentials for %s; using standard value", database->name()); 93 } 94 mMade = true; 95 } 96 97 return size() > 0; // got credentials? 98 } 99 100 101 // 102 // Process a single referral record. This will handle all known types 103 // of referrals, other than keybag (see keybagReferral). 104 // 105 void DefaultCredentials::keyReferral(const UnlockReferralRecord &ref) 106 { 107 secinfo("kcreferral", "processing type %ld referral to %s", 108 (long)ref.type(), ref.dbName().c_str()); 109 DLDbIdentifier identifier(ref.dbName().c_str(), ref.dbGuid(), ref.dbSSID(), ref.dbSSType()); 110 111 // first, try the keychain indicated 112 try { 113 KeychainList list; 114 list.push_back(globals().storageManager.keychain(identifier)); 115 if (unlockKey(ref, list)) // try just this database... 116 return; // ... bingo! 117 } catch (...) { } 118 119 // try the entire search list (just in case) 120 try { 121 secinfo("kcreferral", "no joy with %s; trying the entire keychain list for guid %s", 122 ref.dbName().c_str(), ref.dbGuid().toString().c_str()); 123 unlockKey(ref, fallbackSearchList(identifier)); 124 return; 125 } catch (...) { } 126 secinfo("kcreferral", "no luck at all; we'll skip this record"); 127 } 128 129 130 bool DefaultCredentials::unlockKey(const UnlockReferralRecord &ref, const KeychainList &list) 131 { 132 bool foundSome = false; 133 try { 134 // form the query 135 SecKeychainAttribute attributes[1] = { 136 { kSecKeyLabel, (UInt32)ref.keyLabel().length(), ref.keyLabel().data() } 137 }; 138 SecKeychainAttributeList search = { 1, attributes }; 139 CSSM_DB_RECORDTYPE recordType = 140 (ref.type() == CSSM_APPLE_UNLOCK_TYPE_KEY_DIRECT) ? 141 CSSM_DL_DB_RECORD_SYMMETRIC_KEY : CSSM_DL_DB_RECORD_PRIVATE_KEY; 142 KCCursor cursor(list, (SecItemClass) recordType, &search); 143 144 Item keyItem; 145 while (cursor->next(keyItem)) { 146 secinfo("kcreferral", "located source key in %s", keyItem->keychain()->name()); 147 148 // get a reference to the key in the provider keychain 149 CssmClient::Key key = dynamic_cast<KeyItem &>(*keyItem).key(); 150 const CssmKey &masterKey = key; 151 152 // get the CSP handle FOR THE UNLOCKING KEY'S KEYCHAIN 153 CSSM_CSP_HANDLE cspHandle = key->csp()->handle(); 154 155 // (a)symmetric-key form: KCLOCK, (A)SYMMETRIC_KEY, cspHandle, masterKey 156 // Note that the last list element ("ref") is doing an implicit cast to a 157 // CssmData, which passes the data portion of the UnlockReferralRecord 158 append(TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, 159 new(allocator) ListElement((recordType==CSSM_DL_DB_RECORD_SYMMETRIC_KEY)? 160 CSSM_WORDID_SYMMETRIC_KEY:CSSM_WORDID_ASYMMETRIC_KEY), 161 new(allocator) ListElement(allocator, CssmData::wrap(cspHandle)), 162 new(allocator) ListElement(allocator, CssmData::wrap(masterKey)), 163 new(allocator) ListElement(allocator, ref.get()) 164 )); 165 166 // let's make sure everything we need stays around 167 mNeededItems.insert(keyItem); 168 foundSome = true; 169 } 170 } catch (...) { 171 // (ignore it) 172 } 173 return foundSome; 174 } 175 176 void 177 DefaultCredentials::keybagReferral(const UnlockReferralRecord &ref) 178 { 179 secinfo("kcreferral", "processing type %ld referral", (long)ref.type()); 180 181 try { 182 // assemble and add CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK item 183 append(TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, 184 new(allocator) ListElement(CSSM_WORDID_KEYBAG_KEY), 185 new(allocator) ListElement(allocator, CssmData::wrap(ref.dbGuid())), 186 new(allocator) ListElement(allocator, ref.get()) 187 )); 188 } catch (...) { 189 } 190 } 191 192 // 193 // Take the official keychain search list, and return those keychains whose 194 // module Guid matches the one given. Essentially, this focuses the search list 195 // to a particular type of keychain. 196 // 197 struct NotGuid { 198 NotGuid(const Guid &g) : guid(g) { } 199 const Guid &guid; 200 bool operator () (Keychain kc) { return kc->database()->dl()->guid() != guid; } 201 }; 202 203 DefaultCredentials::KeychainList DefaultCredentials::fallbackSearchList(const DLDbIdentifier &ident) 204 { 205 KeychainList list; 206 globals().storageManager.getSearchList(list); 207 list.erase(remove_if(list.begin(), list.end(), NotGuid(ident.ssuid().guid())), list.end()); 208 return list; 209 } 210 211 212 } // namespace KeychainCore 213 } // namespace Security