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
:
80 case CSSM_APPLE_UNLOCK_TYPE_KEYBAG
:
84 secinfo("kcreferral", "referral type %lu (to %s) not supported",
85 (unsigned long)(*it
)->type(), (*it
)->dbName().c_str());
90 secinfo("kcreferral", "%lu samples generated", (unsigned long)size());
92 secinfo("kcreferral", "exception setting default credentials for %s; using standard value", database
->name());
97 return size() > 0; // got credentials?
102 // Process a single referral record. This will handle all known types
103 // of referrals, other than keybag (see keybagReferral).
105 void DefaultCredentials::keyReferral(const UnlockReferralRecord
&ref
)
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());
111 // first, try the keychain indicated
114 list
.push_back(globals().storageManager
.keychain(identifier
));
115 if (unlockKey(ref
, list
)) // try just this database...
116 return; // ... bingo!
119 // try the entire search list (just in case)
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
));
126 secinfo("kcreferral", "no luck at all; we'll skip this record");
130 bool DefaultCredentials::unlockKey(const UnlockReferralRecord
&ref
, const KeychainList
&list
)
132 bool foundSome
= false;
135 SecKeychainAttribute attributes
[1] = {
136 { kSecKeyLabel
, (UInt32
)ref
.keyLabel().length(), ref
.keyLabel().data() }
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
);
145 while (cursor
->next(keyItem
)) {
146 secinfo("kcreferral", "located source key in %s", keyItem
->keychain()->name());
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
;
152 // get the CSP handle FOR THE UNLOCKING KEY'S KEYCHAIN
153 CSSM_CSP_HANDLE cspHandle
= key
->csp()->handle();
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())
166 // let's make sure everything we need stays around
167 mNeededItems
.insert(keyItem
);
177 DefaultCredentials::keybagReferral(const UnlockReferralRecord
&ref
)
179 secinfo("kcreferral", "processing type %ld referral", (long)ref
.type());
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())
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.
198 NotGuid(const Guid
&g
) : guid(g
) { }
200 bool operator () (Keychain kc
) { return kc
->database()->dl()->guid() != guid
; }
203 DefaultCredentials::KeychainList
DefaultCredentials::fallbackSearchList(const DLDbIdentifier
&ident
)
206 globals().storageManager
.getSearchList(list
);
207 list
.erase(remove_if(list
.begin(), list
.end(), NotGuid(ident
.ssuid().guid())), list
.end());
212 } // namespace KeychainCore
213 } // namespace Security