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