2  * Copyright (c) 2004,2011-2012,2014 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@ 
  25 // defaultcreds - default computations for keychain open credentials 
  27 #include "Keychains.h" 
  28 #include "defaultcreds.h" 
  29 #include "StorageManager.h" 
  32 #include <security_cdsa_utilities/Schema.h> 
  37 namespace KeychainCore 
{ 
  39 using namespace CssmClient
; 
  42 DefaultCredentials::DefaultCredentials(KeychainImpl
* kcImpl
, Allocator 
&alloc
) 
  43         : TrackingAllocator(alloc
), AutoCredentials(static_cast<TrackingAllocator
&>(*this)), 
  44           mMade(false), mKeychainImpl (kcImpl
) 
  49 void DefaultCredentials::clear() 
  51         TrackingAllocator::reset(); 
  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. 
  64 bool DefaultCredentials::operator () (Db database
) 
  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
)) 
  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
: 
  81                                                 secinfo("kcreferral", "referral type %lu (to %s) not supported", 
  82                                                         (unsigned long)(*it
)->type(), (*it
)->dbName().c_str()); 
  87                         secinfo("kcreferral", "%lu samples generated", (unsigned long)size()); 
  89                         secinfo("kcreferral", "exception setting default credentials for %s; using standard value", database
->name()); 
  94         return size() > 0;      // got credentials? 
  99 // Process a single referral record. This will handle all known types 
 102 void DefaultCredentials::keyReferral(const UnlockReferralRecord 
&ref
) 
 104         secinfo("kcreferral", "processing type %ld referral to %s", 
 105                 (long)ref
.type(), ref
.dbName().c_str()); 
 106         DLDbIdentifier 
identifier(ref
.dbName().c_str(), ref
.dbGuid(), ref
.dbSSID(), ref
.dbSSType()); 
 108         // first, try the keychain indicated 
 111                 list
.push_back(globals().storageManager
.keychain(identifier
)); 
 112                 if (unlockKey(ref
, list
))       // try just this database... 
 113                         return;                                         // ... bingo! 
 116         // try the entire search list (just in case) 
 118                 secinfo("kcreferral", "no joy with %s; trying the entire keychain list for guid %s", 
 119                         ref
.dbName().c_str(), ref
.dbGuid().toString().c_str()); 
 120                 unlockKey(ref
, fallbackSearchList(identifier
)); 
 123         secinfo("kcreferral", "no luck at all; we'll skip this record"); 
 127 bool DefaultCredentials::unlockKey(const UnlockReferralRecord 
&ref
, const KeychainList 
&list
) 
 129         bool foundSome 
= false; 
 132                 SecKeychainAttribute attributes
[1] = { 
 133                         { kSecKeyLabel
, (UInt32
)ref
.keyLabel().length(), ref
.keyLabel().data() } 
 135                 SecKeychainAttributeList search 
= { 1, attributes 
}; 
 136                 CSSM_DB_RECORDTYPE recordType 
= 
 137                         (ref
.type() == CSSM_APPLE_UNLOCK_TYPE_KEY_DIRECT
) ? 
 138                                 CSSM_DL_DB_RECORD_SYMMETRIC_KEY 
: CSSM_DL_DB_RECORD_PRIVATE_KEY
; 
 139                 KCCursor 
cursor(list
, (SecItemClass
) recordType
, &search
); 
 142                 while (cursor
->next(keyItem
)) { 
 143                         secinfo("kcreferral", "located source key in %s", keyItem
->keychain()->name()); 
 145                         // get a reference to the key in the provider keychain 
 146                         CssmClient::Key key 
= dynamic_cast<KeyItem 
&>(*keyItem
).key(); 
 147                         const CssmKey 
&masterKey 
= key
; 
 149                         // get the CSP handle FOR THE UNLOCKING KEY'S KEYCHAIN 
 150                         CSSM_CSP_HANDLE cspHandle 
= key
->csp()->handle(); 
 152                         // (a)symmetric-key form: KCLOCK, (A)SYMMETRIC_KEY, cspHandle, masterKey 
 153                         // Note that the last list element ("ref") is doing an implicit cast to a 
 154                         // CssmData, which passes the data portion of the UnlockReferralRecord 
 155                         append(TypedList(allocator
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, 
 156                                 new(allocator
) ListElement((recordType
==CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)? 
 157                                         CSSM_WORDID_SYMMETRIC_KEY
:CSSM_WORDID_ASYMMETRIC_KEY
), 
 158                                 new(allocator
) ListElement(allocator
, CssmData::wrap(cspHandle
)), 
 159                                 new(allocator
) ListElement(allocator
, CssmData::wrap(masterKey
)), 
 160                                 new(allocator
) ListElement(allocator
, ref
.get())                 
 163                         // let's make sure everything we need stays around 
 164                         mNeededItems
.insert(keyItem
); 
 175 // Take the official keychain search list, and return those keychains whose 
 176 // module Guid matches the one given. Essentially, this focuses the search list 
 177 // to a particular type of keychain. 
 180         NotGuid(const Guid 
&g
) : guid(g
) { } 
 182         bool operator () (Keychain kc
) { return kc
->database()->dl()->guid() != guid
; } 
 185 DefaultCredentials::KeychainList 
DefaultCredentials::fallbackSearchList(const DLDbIdentifier 
&ident
) 
 188         globals().storageManager
.getSearchList(list
); 
 189         list
.erase(remove_if(list
.begin(), list
.end(), NotGuid(ident
.ssuid().guid())), list
.end()); 
 194 }       // namespace KeychainCore 
 195 }       // namespace Security