]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2002-2004,2011-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 | // Access.cpp | |
26 | // | |
27 | #include <security_keychain/Access.h> | |
28 | #include <Security/SecBase.h> | |
29 | #include "SecBridge.h" | |
30 | #include <security_utilities/devrandom.h> | |
31 | #include <security_cdsa_utilities/uniformrandom.h> | |
32 | #include <security_cdsa_client/aclclient.h> | |
33 | #include <vector> | |
427c49bc | 34 | #include <SecBase.h> |
b1ab9ed8 A |
35 | using namespace KeychainCore; |
36 | using namespace CssmClient; | |
37 | ||
38 | ||
39 | // | |
40 | // Access static constants | |
41 | // | |
42 | const CSSM_ACL_HANDLE Access::ownerHandle; | |
43 | ||
44 | ||
45 | // | |
46 | // Create a completely open Access (anyone can do anything) | |
47 | // Note that this means anyone can *change* the ACL at will, too. | |
48 | // These ACL entries contain no descriptor names. | |
49 | // | |
50 | Access::Access() : mMutex(Mutex::recursive) | |
51 | { | |
e3d460c9 | 52 | SecPointer<ACL> owner = new ACL(); |
b1ab9ed8 A |
53 | owner->setAuthorization(CSSM_ACL_AUTHORIZATION_CHANGE_ACL); |
54 | addOwner(owner); | |
55 | ||
e3d460c9 | 56 | SecPointer<ACL> any = new ACL(); |
b1ab9ed8 A |
57 | add(any); |
58 | } | |
59 | ||
60 | ||
61 | // | |
62 | // Create a default Access object. | |
63 | // This construct an Access with "default form", whatever that happens to be | |
64 | // in this release. | |
65 | // | |
66 | Access::Access(const string &descriptor, const ACL::ApplicationList &trusted) : mMutex(Mutex::recursive) | |
67 | { | |
68 | makeStandard(descriptor, trusted); | |
69 | } | |
70 | ||
71 | Access::Access(const string &descriptor) : mMutex(Mutex::recursive) | |
72 | { | |
73 | ACL::ApplicationList trusted; | |
74 | trusted.push_back(new TrustedApplication); | |
75 | makeStandard(descriptor, trusted); | |
76 | } | |
77 | ||
78 | Access::Access(const string &descriptor, const ACL::ApplicationList &trusted, | |
79 | const AclAuthorizationSet &limitedRights, const AclAuthorizationSet &freeRights) : mMutex(Mutex::recursive) | |
80 | { | |
81 | makeStandard(descriptor, trusted, limitedRights, freeRights); | |
82 | } | |
83 | ||
84 | void Access::makeStandard(const string &descriptor, const ACL::ApplicationList &trusted, | |
85 | const AclAuthorizationSet &limitedRights, const AclAuthorizationSet &freeRights) | |
86 | { | |
87 | StLock<Mutex>_(mMutex); | |
88 | ||
89 | // owner "entry" | |
e3d460c9 | 90 | SecPointer<ACL> owner = new ACL(descriptor, ACL::defaultSelector); |
b1ab9ed8 A |
91 | owner->setAuthorization(CSSM_ACL_AUTHORIZATION_CHANGE_ACL); |
92 | addOwner(owner); | |
93 | ||
94 | // unlimited entry | |
e3d460c9 | 95 | SecPointer<ACL> unlimited = new ACL(descriptor, ACL::defaultSelector); |
b1ab9ed8 A |
96 | if (freeRights.empty()) { |
97 | unlimited->authorizations().clear(); | |
98 | unlimited->authorizations().insert(CSSM_ACL_AUTHORIZATION_ENCRYPT); | |
99 | } else | |
100 | unlimited->authorizations() = freeRights; | |
101 | unlimited->form(ACL::allowAllForm); | |
102 | add(unlimited); | |
103 | ||
104 | // limited entry | |
e3d460c9 | 105 | SecPointer<ACL> limited = new ACL(descriptor, ACL::defaultSelector); |
b1ab9ed8 A |
106 | if (limitedRights.empty()) { |
107 | limited->authorizations().clear(); | |
108 | limited->authorizations().insert(CSSM_ACL_AUTHORIZATION_DECRYPT); | |
109 | limited->authorizations().insert(CSSM_ACL_AUTHORIZATION_SIGN); | |
110 | limited->authorizations().insert(CSSM_ACL_AUTHORIZATION_MAC); | |
111 | limited->authorizations().insert(CSSM_ACL_AUTHORIZATION_DERIVE); | |
112 | limited->authorizations().insert(CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR); | |
113 | limited->authorizations().insert(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED); | |
114 | } else | |
115 | limited->authorizations() = limitedRights; | |
116 | limited->applications() = trusted; | |
117 | add(limited); | |
118 | } | |
119 | ||
120 | ||
121 | // | |
122 | // Create an Access object whose initial value is taken | |
123 | // from a CSSM ACL bearing object. | |
124 | // | |
125 | Access::Access(AclBearer &source) : mMutex(Mutex::recursive) | |
126 | { | |
127 | // retrieve and set | |
128 | AutoAclOwnerPrototype owner; | |
129 | source.getOwner(owner); | |
130 | AutoAclEntryInfoList acls; | |
131 | source.getAcl(acls); | |
132 | compile(*owner, acls.count(), acls.entries()); | |
133 | } | |
134 | ||
135 | ||
136 | // | |
137 | // Create an Access object from CSSM-layer access controls | |
138 | // | |
139 | Access::Access(const CSSM_ACL_OWNER_PROTOTYPE &owner, | |
140 | uint32 aclCount, const CSSM_ACL_ENTRY_INFO *acls) : mMutex(Mutex::recursive) | |
141 | { | |
142 | compile(owner, aclCount, acls); | |
143 | } | |
144 | ||
145 | ||
146 | Access::~Access() | |
147 | { | |
148 | } | |
149 | ||
150 | ||
151 | // Convert a SecPointer to a SecACLRef. | |
152 | static SecACLRef | |
153 | convert(const SecPointer<ACL> &acl) | |
154 | { | |
155 | return *acl; | |
156 | } | |
157 | ||
158 | // | |
159 | // Return all ACL components in a newly-made CFArray. | |
160 | // | |
161 | CFArrayRef Access::copySecACLs() const | |
162 | { | |
e3d460c9 | 163 | return makeCFArrayFrom(convert, mAcls); |
b1ab9ed8 A |
164 | } |
165 | ||
166 | CFArrayRef Access::copySecACLs(CSSM_ACL_AUTHORIZATION_TAG action) const | |
167 | { | |
168 | list<ACL *> choices; | |
169 | for (Map::const_iterator it = mAcls.begin(); it != mAcls.end(); it++) | |
170 | if (it->second->authorizes(action)) | |
171 | choices.push_back(it->second); | |
e3d460c9 | 172 | return choices.empty() ? NULL : makeCFArrayFrom(convert, choices); |
b1ab9ed8 A |
173 | } |
174 | ||
175 | ||
176 | // | |
177 | // Enter the complete access configuration into a AclBearer. | |
178 | // If update, skip any part marked unchanged. (If not update, skip | |
179 | // any part marked deleted.) | |
180 | // | |
181 | void Access::setAccess(AclBearer &target, bool update /* = false */) | |
182 | { | |
183 | StLock<Mutex>_(mMutex); | |
184 | AclFactory factory; | |
185 | editAccess(target, update, factory.promptCred()); | |
186 | } | |
187 | ||
188 | void Access::setAccess(AclBearer &target, Maker &maker) | |
189 | { | |
190 | StLock<Mutex>_(mMutex); | |
191 | if (maker.makerType() == Maker::kStandardMakerType) | |
192 | { | |
193 | // remove initial-setup ACL | |
194 | target.deleteAcl(Maker::creationEntryTag, maker.cred()); | |
195 | ||
196 | // insert our own ACL entries | |
197 | editAccess(target, false, maker.cred()); | |
198 | } | |
199 | } | |
200 | ||
201 | void Access::editAccess(AclBearer &target, bool update, const AccessCredentials *cred) | |
202 | { | |
203 | StLock<Mutex>_(mMutex); | |
204 | assert(mAcls[ownerHandle]); // have owner | |
205 | ||
206 | // apply all non-owner ACLs first | |
207 | for (Map::iterator it = mAcls.begin(); it != mAcls.end(); it++) | |
208 | if (!it->second->isOwner()) | |
209 | it->second->setAccess(target, update, cred); | |
210 | ||
211 | // finally, apply owner | |
212 | mAcls[ownerHandle]->setAccess(target, update, cred); | |
213 | } | |
214 | ||
215 | ||
216 | // | |
217 | // A convenience function to add one application to a standard ("simple") form | |
218 | // ACL entry. This will only work if | |
219 | // -- there is exactly one ACL entry authorizing the right | |
220 | // -- that entry is in simple form | |
221 | // | |
222 | void Access::addApplicationToRight(AclAuthorization right, TrustedApplication *app) | |
223 | { | |
224 | StLock<Mutex>_(mMutex); | |
225 | vector<ACL *> acls; | |
226 | findAclsForRight(right, acls); | |
227 | if (acls.size() != 1) | |
228 | MacOSError::throwMe(errSecACLNotSimple); // let's not guess here... | |
229 | (*acls.begin())->addApplication(app); | |
230 | } | |
231 | ||
232 | ||
233 | // | |
234 | // Yield new (copied) CSSM level owner and acls values, presumably | |
235 | // for use at CSSM layer operations. | |
236 | // Caller is responsible for releasing the beasties when done. | |
237 | // | |
238 | void Access::copyOwnerAndAcl(CSSM_ACL_OWNER_PROTOTYPE * &ownerResult, | |
239 | uint32 &aclCount, CSSM_ACL_ENTRY_INFO * &aclsResult) | |
240 | { | |
241 | StLock<Mutex>_(mMutex); | |
242 | Allocator& alloc = Allocator::standard(); | |
427c49bc | 243 | unsigned long count = mAcls.size() - 1; // one will be owner, others are acls |
b1ab9ed8 A |
244 | AclOwnerPrototype owner; |
245 | CssmAutoPtr<AclEntryInfo> acls = new(alloc) AclEntryInfo[count]; | |
246 | AclEntryInfo *aclp = acls; // -> next unfilled acl element | |
247 | for (Map::const_iterator it = mAcls.begin(); it != mAcls.end(); it++) { | |
248 | SecPointer<ACL> acl = it->second; | |
249 | if (acl->isOwner()) { | |
250 | acl->copyAclOwner(owner, alloc); | |
251 | } else { | |
252 | aclp->handle() = acl->entryHandle(); | |
253 | acl->copyAclEntry(*aclp, alloc); | |
254 | ++aclp; | |
255 | } | |
256 | } | |
257 | assert((aclp - acls) == count); // all ACL elements filled | |
258 | ||
259 | // commit output | |
260 | ownerResult = new(alloc) AclOwnerPrototype(owner); | |
427c49bc | 261 | aclCount = (uint32)count; |
b1ab9ed8 A |
262 | aclsResult = acls.release(); |
263 | } | |
264 | ||
265 | ||
e3d460c9 A |
266 | // |
267 | // Remove all ACLs that confer this right. | |
268 | // | |
269 | void Access::removeAclsForRight(AclAuthorization right) { | |
270 | for (Map::const_iterator it = mAcls.begin(); it != mAcls.end(); ) { | |
271 | if (it->second->authorizesSpecifically(right)) { | |
272 | it = mAcls.erase(it); | |
fa7225c8 | 273 | secinfo("SecAccess", "%p removed an acl, %lu left", this, mAcls.size()); |
e3d460c9 A |
274 | } else { |
275 | it++; | |
276 | } | |
277 | } | |
278 | } | |
279 | ||
b1ab9ed8 A |
280 | // |
281 | // Retrieve the description from a randomly chosen ACL within this Access. | |
282 | // In the conventional case where all ACLs have the same descriptor, this | |
283 | // is deterministic. But you have been warned. | |
284 | // | |
285 | string Access::promptDescription() const | |
286 | { | |
287 | for (Map::const_iterator it = mAcls.begin(); it != mAcls.end(); it++) { | |
288 | ACL *acl = it->second; | |
289 | switch (acl->form()) { | |
290 | case ACL::allowAllForm: | |
291 | case ACL::appListForm: | |
292 | { | |
293 | string descr = acl->promptDescription(); | |
294 | if (!descr.empty()) | |
295 | return descr; | |
296 | } | |
297 | default: | |
298 | break; | |
299 | } | |
300 | } | |
301 | // couldn't find suitable ACL (no description anywhere) | |
302 | CssmError::throwMe(errSecACLNotSimple); | |
303 | } | |
304 | ||
305 | ||
306 | // | |
307 | // Add a new ACL to the resident set. The ACL must have been | |
308 | // newly made for this Access. | |
309 | // | |
310 | void Access::add(ACL *newAcl) | |
311 | { | |
312 | StLock<Mutex>_(mMutex); | |
b1ab9ed8 A |
313 | assert(!mAcls[newAcl->entryHandle()]); |
314 | mAcls[newAcl->entryHandle()] = newAcl; | |
315 | } | |
316 | ||
317 | ||
318 | // | |
319 | // Add the owner ACL to the resident set. The ACL must have been | |
320 | // newly made for this Access. | |
321 | // Since an Access must have exactly one owner ACL, this call | |
322 | // should only be made (exactly once) for a newly created Access. | |
323 | // | |
324 | void Access::addOwner(ACL *newAcl) | |
325 | { | |
326 | StLock<Mutex>_(mMutex); | |
327 | newAcl->makeOwner(); | |
328 | assert(mAcls.find(ownerHandle) == mAcls.end()); // no owner yet | |
329 | add(newAcl); | |
330 | } | |
331 | ||
332 | ||
333 | // | |
334 | // Compile a set of ACL entries and owner into internal form. | |
335 | // | |
336 | void Access::compile(const CSSM_ACL_OWNER_PROTOTYPE &owner, | |
337 | uint32 aclCount, const CSSM_ACL_ENTRY_INFO *acls) | |
338 | { | |
339 | StLock<Mutex>_(mMutex); | |
340 | // add owner acl | |
e3d460c9 | 341 | mAcls[ownerHandle] = new ACL(AclOwnerPrototype::overlay(owner)); |
fa7225c8 | 342 | secinfo("SecAccess", "form of owner is: %d", mAcls[ownerHandle]->form()); |
b1ab9ed8 A |
343 | |
344 | // add acl entries | |
345 | const AclEntryInfo *acl = AclEntryInfo::overlay(acls); | |
346 | for (uint32 n = 0; n < aclCount; n++) { | |
fa7225c8 | 347 | secinfo("SecAccess", "%p compiling entry %ld", this, acl[n].handle()); |
e3d460c9 | 348 | mAcls[acl[n].handle()] = new ACL(acl[n]); |
fa7225c8 | 349 | secinfo("SecAccess", "form is: %d", mAcls[acl[n].handle()]->form()); |
b1ab9ed8 | 350 | } |
fa7225c8 | 351 | secinfo("SecAccess", "%p %ld entries compiled", this, mAcls.size()); |
b1ab9ed8 A |
352 | } |
353 | ||
354 | ||
355 | // | |
356 | // Creation helper objects | |
357 | // | |
358 | const char Access::Maker::creationEntryTag[] = "___setup___"; | |
359 | ||
360 | Access::Maker::Maker(Allocator &alloc, MakerType makerType) | |
361 | : allocator(alloc), mKey(alloc), mCreds(allocator), mMakerType(makerType) | |
362 | { | |
363 | if (makerType == kStandardMakerType) | |
364 | { | |
365 | // generate random key | |
366 | mKey.malloc(keySize); | |
367 | UniformRandomBlobs<DevRandomGenerator>().random(mKey.get()); | |
368 | ||
369 | // create entry info for resource creation | |
370 | mInput = AclEntryPrototype(TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_PASSWORD, | |
371 | new(allocator) ListElement(mKey.get()))); | |
372 | mInput.proto().tag(creationEntryTag); | |
fa7225c8 A |
373 | secinfo("SecAccess", "made a CSSM_ACL_SUBJECT_TYPE_PASSWORD ACL entry for %p", this); |
374 | secinfo("SecAccess", "mInput: %p, typedList %p", &mInput, &(mInput.Prototype.TypedSubject)); | |
b1ab9ed8 A |
375 | |
376 | // create credential sample for access | |
377 | mCreds += TypedList(allocator, CSSM_SAMPLE_TYPE_PASSWORD, new(allocator) ListElement(mKey.get())); | |
378 | } | |
379 | else | |
380 | { | |
381 | // just make it an CSSM_ACL_SUBJECT_TYPE_ANY list | |
382 | mInput = AclEntryPrototype(TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_ANY)); | |
fa7225c8 | 383 | secinfo("SecAccess", "made a CSSM_ACL_SUBJECT_TYPE_ANY ACL entry for %p", this); |
b1ab9ed8 A |
384 | } |
385 | } | |
386 | ||
387 | void Access::Maker::initialOwner(ResourceControlContext &ctx, const AccessCredentials *creds) | |
388 | { | |
389 | //@@@ make up ctx.entry-info | |
390 | ctx.input() = mInput; | |
391 | ctx.credentials(creds); | |
392 | } | |
393 | ||
394 | const AccessCredentials *Access::Maker::cred() | |
395 | { | |
396 | return &mCreds; | |
397 | } |