]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/defaultcreds.cpp
Security-58286.251.4.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / 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