]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2004,2011-2012,2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
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; | |
866f8763 A |
80 | case CSSM_APPLE_UNLOCK_TYPE_KEYBAG: |
81 | keybagReferral(**it); | |
82 | break; | |
b1ab9ed8 | 83 | default: |
fa7225c8 | 84 | secinfo("kcreferral", "referral type %lu (to %s) not supported", |
b1ab9ed8 A |
85 | (unsigned long)(*it)->type(), (*it)->dbName().c_str()); |
86 | break; | |
87 | } | |
88 | } | |
89 | } | |
fa7225c8 | 90 | secinfo("kcreferral", "%lu samples generated", (unsigned long)size()); |
b1ab9ed8 | 91 | } catch (...) { |
fa7225c8 | 92 | secinfo("kcreferral", "exception setting default credentials for %s; using standard value", database->name()); |
b1ab9ed8 A |
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 | |
866f8763 | 103 | // of referrals, other than keybag (see keybagReferral). |
b1ab9ed8 A |
104 | // |
105 | void DefaultCredentials::keyReferral(const UnlockReferralRecord &ref) | |
106 | { | |
fa7225c8 | 107 | secinfo("kcreferral", "processing type %ld referral to %s", |
b1ab9ed8 A |
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 { | |
fa7225c8 | 121 | secinfo("kcreferral", "no joy with %s; trying the entire keychain list for guid %s", |
b1ab9ed8 A |
122 | ref.dbName().c_str(), ref.dbGuid().toString().c_str()); |
123 | unlockKey(ref, fallbackSearchList(identifier)); | |
124 | return; | |
125 | } catch (...) { } | |
fa7225c8 | 126 | secinfo("kcreferral", "no luck at all; we'll skip this record"); |
b1ab9ed8 A |
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] = { | |
427c49bc | 136 | { kSecKeyLabel, (UInt32)ref.keyLabel().length(), ref.keyLabel().data() } |
b1ab9ed8 A |
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; | |
6b200bc3 | 142 | KCCursor cursor(list, (SecItemClass) recordType, &search); |
b1ab9ed8 A |
143 | |
144 | Item keyItem; | |
145 | while (cursor->next(keyItem)) { | |
fa7225c8 | 146 | secinfo("kcreferral", "located source key in %s", keyItem->keychain()->name()); |
b1ab9ed8 A |
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 | ||
866f8763 A |
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 | } | |
b1ab9ed8 A |
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 |