]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/ACL.cpp
Security-57337.50.23.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / ACL.cpp
1 /*
2 * Copyright (c) 2002-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 // ACL.cpp
26 //
27 #include <security_keychain/ACL.h>
28 #include <security_keychain/SecCFTypes.h>
29 #include <security_utilities/osxcode.h>
30 #include <security_utilities/trackingallocator.h>
31 #include <security_cdsa_utilities/walkers.h>
32 #include <security_keychain/TrustedApplication.h>
33 #include <Security/SecTrustedApplication.h>
34 #include <security_utilities/devrandom.h>
35 #include <security_cdsa_utilities/uniformrandom.h>
36 #include <memory>
37
38
39 using namespace KeychainCore;
40 using namespace DataWalkers;
41
42
43 //
44 // The default form of a prompt selector
45 //
46 const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR ACL::defaultSelector = {
47 CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION, 0
48 };
49
50
51 //
52 // ACL static constants
53 //
54 const CSSM_ACL_HANDLE ACL::ownerHandle;
55
56
57 //
58 // Create an ACL object from the result of a CSSM ACL query
59 //
60 ACL::ACL(const AclEntryInfo &info, Allocator &alloc)
61 : allocator(alloc), mState(unchanged), mSubjectForm(NULL), mIntegrity(alloc), mMutex(Mutex::recursive)
62 {
63 // parse the subject
64 parse(info.proto().subject());
65
66 // fill in AclEntryInfo layer information
67 const AclEntryPrototype &proto = info.proto();
68 mAuthorizations = proto.authorization();
69 mDelegate = proto.delegate();
70 mEntryTag = proto.s_tag();
71
72 // take CSSM entry handle from info layer
73 mCssmHandle = info.handle();
74 }
75
76
77 ACL::ACL(const AclOwnerPrototype &owner, Allocator &alloc)
78 : allocator(alloc), mState(unchanged), mSubjectForm(NULL), mIntegrity(alloc), mMutex(Mutex::recursive)
79 {
80 // parse subject
81 parse(owner.subject());
82
83 // for an owner "entry", the next-layer information is fixed (and fake)
84 mAuthorizations.insert(CSSM_ACL_AUTHORIZATION_CHANGE_ACL);
85 mDelegate = owner.delegate();
86 mEntryTag[0] = '\0';
87
88 // use fixed (fake) entry handle
89 mCssmHandle = ownerHandle;
90 }
91
92
93 //
94 // Create a new ACL that authorizes anyone to do anything.
95 // This constructor produces a "pure" ANY ACL, without descriptor or selector.
96 // To generate a "standard" form of ANY, use the appListForm constructor below,
97 // then change its form to allowAnyForm.
98 //
99 ACL::ACL(Allocator &alloc)
100 : allocator(alloc), mSubjectForm(NULL), mIntegrity(alloc), mMutex(Mutex::recursive)
101 {
102 mState = inserted; // new
103 mForm = allowAllForm; // everybody
104 mAuthorizations.insert(CSSM_ACL_AUTHORIZATION_ANY); // anything
105 mDelegate = false;
106
107 //mPromptDescription stays empty
108 mPromptSelector = defaultSelector;
109
110 // randomize the CSSM handle
111 UniformRandomBlobs<DevRandomGenerator>().random(mCssmHandle);
112 }
113
114
115 //
116 // Create a new ACL in standard form.
117 // As created, it authorizes all activities.
118 //
119 ACL::ACL(string description, const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR &promptSelector,
120 Allocator &alloc)
121 : allocator(alloc), mSubjectForm(NULL), mIntegrity(alloc), mMutex(Mutex::recursive)
122 {
123 mState = inserted; // new
124 mForm = appListForm;
125 mAuthorizations.insert(CSSM_ACL_AUTHORIZATION_ANY); // anything
126 mDelegate = false;
127
128 mPromptDescription = description;
129 mPromptSelector = promptSelector;
130
131 // randomize the CSSM handle
132 UniformRandomBlobs<DevRandomGenerator>().random(mCssmHandle);
133 }
134
135
136 //
137 // Create an "integrity" ACL
138 //
139 ACL::ACL(const CssmData &digest, Allocator &alloc)
140 : allocator(alloc), mSubjectForm(NULL), mIntegrity(alloc, digest), mMutex(Mutex::recursive)
141 {
142 mState = inserted; // new
143 mForm = integrityForm;
144 mAuthorizations.insert(CSSM_ACL_AUTHORIZATION_INTEGRITY);
145 mEntryTag = CSSM_APPLE_ACL_TAG_INTEGRITY;
146 mDelegate = false;
147
148 //mPromptDescription stays empty
149 //mPromptSelector stays empty
150
151 // randomize the CSSM handle
152 UniformRandomBlobs<DevRandomGenerator>().random(mCssmHandle);
153 }
154
155
156 //
157 // Destroy an ACL
158 //
159 ACL::~ACL()
160 {
161 // release subject form (if any)
162 chunkFree(mSubjectForm, allocator);
163 }
164
165
166 //
167 // Does this ACL authorize a particular right?
168 //
169 bool ACL::authorizes(AclAuthorization right)
170 {
171 StLock<Mutex>_(mMutex);
172 return mAuthorizations.find(right) != mAuthorizations.end()
173 || mAuthorizations.find(CSSM_ACL_AUTHORIZATION_ANY) != mAuthorizations.end()
174 || mAuthorizations.empty();
175 }
176
177 //
178 // Does this ACL have a specific authorization for a particular right?
179 //
180 bool ACL::authorizesSpecifically(AclAuthorization right)
181 {
182 StLock<Mutex>_(mMutex);
183 return mAuthorizations.find(right) != mAuthorizations.end();
184 }
185
186 void ACL::setIntegrity(const CssmData& digest) {
187 if(mForm != integrityForm) {
188 secdebugfunc("integrity", "acl has incorrect form: %d", mForm);
189 CssmError::throwMe(CSSMERR_CSP_INVALID_ACL_SUBJECT_VALUE);
190 }
191
192 mIntegrity = digest;
193 modify();
194 }
195
196 const CssmData& ACL::integrity() {
197 return mIntegrity.get();
198 }
199
200 //
201 // Add an application to the trusted-app list of this ACL.
202 // Will fail unless this is a standard "simple" form ACL.
203 //
204 void ACL::addApplication(TrustedApplication *app)
205 {
206 StLock<Mutex>_(mMutex);
207 switch (mForm) {
208 case appListForm: // simple...
209 mAppList.push_back(app);
210 modify();
211 break;
212 case allowAllForm: // hmm...
213 if (!mPromptDescription.empty()) {
214 // verbose "any" form (has description, "any" override)
215 mAppList.push_back(app);
216 modify();
217 break;
218 }
219 // pure "any" form without description. Cannot convert to appListForm
220 default:
221 MacOSError::throwMe(errSecACLNotSimple);
222 }
223 }
224
225
226 //
227 // Mark an ACL as modified.
228 //
229 void ACL::modify()
230 {
231 StLock<Mutex>_(mMutex);
232 if (mState == unchanged) {
233 secdebug("SecAccess", "ACL %p marked modified", this);
234 mState = modified;
235 }
236 }
237
238
239 //
240 // Mark an ACL as "removed"
241 // Removed ACLs have no valid contents (they are invalid on their face).
242 // When "updated" to the originating item, they will cause the corresponding
243 // ACL entry to be deleted. Otherwise, they are irrelevant.
244 // Note: Removing an ACL does not actually remove it from its Access's map.
245 //
246 void ACL::remove()
247 {
248 StLock<Mutex>_(mMutex);
249 mAppList.clear();
250 mForm = invalidForm;
251 secdebug("SecAccess", "ACL %p marked deleted", this);
252 mState = deleted;
253 }
254
255
256 //
257 // Produce CSSM-layer form (ACL prototype) copies of our content.
258 // Note that the result is chunk-allocated, and becomes owned by the caller.
259 //
260 void ACL::copyAclEntry(AclEntryPrototype &proto, Allocator &alloc)
261 {
262 StLock<Mutex>_(mMutex);
263 proto.clearPod(); // preset
264
265 // carefully copy the subject
266 makeSubject();
267 assert(mSubjectForm);
268 proto = AclEntryPrototype(*mSubjectForm, mDelegate); // shares subject
269 ChunkCopyWalker w(alloc);
270 walk(w, proto.subject()); // copy subject in-place
271
272 // the rest of a prototype
273 proto.tag(mEntryTag);
274 AuthorizationGroup tags(mAuthorizations, allocator);
275 proto.authorization() = tags;
276 }
277
278 void ACL::copyAclOwner(AclOwnerPrototype &proto, Allocator &alloc)
279 {
280 StLock<Mutex>_(mMutex);
281 proto.clearPod();
282
283 makeSubject();
284 assert(mSubjectForm);
285 proto = AclOwnerPrototype(*mSubjectForm, mDelegate); // shares subject
286 ChunkCopyWalker w(alloc);
287 walk(w, proto.subject()); // copy subject in-place
288 }
289
290
291 //
292 // (Re)place this ACL's setting into the AclBearer specified.
293 // If update, assume this is an update operation and the ACL was
294 // originally derived from this object; specifically, assume the
295 // CSSM handle is valid. If not update, assume this is a different
296 // object that has no related ACL entry (yet).
297 //
298 void ACL::setAccess(AclBearer &target, bool update,
299 const AccessCredentials *cred)
300 {
301 StLock<Mutex>_(mMutex);
302 // determine what action we need to perform
303 State action = state();
304 if (!update)
305 action = (action == deleted) ? unchanged : inserted;
306
307 // the owner acl (pseudo) "entry" is a special case
308 if (isOwner()) {
309 switch (action) {
310 case unchanged:
311 secdebug("SecAccess", "ACL %p owner unchanged", this);
312 return;
313 case inserted: // means modify the initial owner
314 case modified:
315 {
316 secdebug("SecAccess", "ACL %p owner modified", this);
317 makeSubject();
318 assert(mSubjectForm);
319 AclOwnerPrototype proto(*mSubjectForm, mDelegate);
320 target.changeOwner(proto, cred);
321 return;
322 }
323 default:
324 assert(false);
325 return;
326 }
327 }
328
329 // simple cases
330 switch (action) {
331 case unchanged: // ignore
332 secdebug("SecAccess", "ACL %p handle 0x%lx unchanged", this, entryHandle());
333 return;
334 case deleted: // delete
335 secdebug("SecAccess", "ACL %p handle 0x%lx deleted", this, entryHandle());
336 target.deleteAcl(entryHandle(), cred);
337 return;
338 default:
339 break;
340 }
341
342 // build the byzantine data structures that CSSM loves so much
343 makeSubject();
344 assert(mSubjectForm);
345 AclEntryPrototype proto(*mSubjectForm, mDelegate);
346 proto.tag(mEntryTag);
347 AutoAuthorizationGroup tags(mAuthorizations, allocator);
348 proto.authorization() = tags;
349 AclEntryInput input(proto);
350 switch (action) {
351 case inserted: // insert
352 secdebug("SecAccess", "ACL %p inserted", this);
353 target.addAcl(input, cred);
354 mState = unchanged;
355 break;
356 case modified: // update
357 secdebug("SecAccess", "ACL %p handle 0x%lx modified", this, entryHandle());
358 target.changeAcl(entryHandle(), input, cred);
359 mState = unchanged;
360 break;
361 default:
362 assert(false);
363 }
364 }
365
366
367 //
368 // Parse an AclEntryPrototype (presumably from a CSSM "Get" ACL operation
369 // into internal form.
370 //
371 void ACL::parse(const TypedList &subject)
372 {
373 StLock<Mutex>_(mMutex);
374 try {
375 switch (subject.type()) {
376 case CSSM_ACL_SUBJECT_TYPE_ANY:
377 // subsume an "any" as a standard form
378 mForm = allowAllForm;
379 secdebug("SecAccess", "parsed an allowAllForm (%d) (%d)", subject.type(), mForm);
380 return;
381 case CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT:
382 // pure keychain prompt - interpret as applist form with no apps
383 parsePrompt(subject);
384 mForm = appListForm;
385 secdebug("SecAccess", "parsed a Keychain Prompt (%d) as an appListForm (%d)", subject.type(), mForm);
386 return;
387 case CSSM_ACL_SUBJECT_TYPE_THRESHOLD:
388 {
389 // app-list format: THRESHOLD(1, n): sign(1), ..., sign(n), PROMPT
390 if (subject[1] != 1)
391 throw ParseError();
392 uint32 count = subject[2];
393
394 // parse final (PROMPT) element
395 TypedList &end = subject[count + 2]; // last choice
396 if (end.type() != CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT)
397 throw ParseError(); // not PROMPT at end
398 parsePrompt(end);
399
400 // check for leading ANY
401 TypedList &first = subject[3];
402 if (first.type() == CSSM_ACL_SUBJECT_TYPE_ANY) {
403 mForm = allowAllForm;
404 secdebug("SecAccess", "parsed a Threshhold (%d) as an allowAllForm (%d)", subject.type(), mForm);
405 return;
406 }
407
408 // parse other (code signing) elements
409 for (uint32 n = 0; n < count - 1; n++) {
410 mAppList.push_back(new TrustedApplication(TypedList(subject[n + 3].list())));
411 secdebug("SecAccess", "found an application: %s", mAppList.back()->path());
412 }
413 }
414 mForm = appListForm;
415 secdebug("SecAccess", "parsed a Threshhold (%d) as an appListForm (%d)", subject.type(), mForm);
416 return;
417 case CSSM_ACL_SUBJECT_TYPE_PARTITION:
418 mForm = integrityForm;
419 mIntegrity.copy(subject.last()->data());
420 secdebug("SecAccess", "parsed a Partition (%d) as an integrityForm (%d)", subject.type(), mForm);
421 return;
422 default:
423 secdebug("SecAccess", "didn't find a type for %d, marking custom (%d)", subject.type(), mForm);
424 mForm = customForm;
425 mSubjectForm = chunkCopy(&subject);
426 return;
427 }
428 } catch (const ParseError &) {
429 secdebug("SecAccess", "acl compile failed for type (%d); marking custom", subject.type());
430 mForm = customForm;
431 mSubjectForm = chunkCopy(&subject);
432 mAppList.clear();
433 }
434 }
435
436 void ACL::parsePrompt(const TypedList &subject)
437 {
438 StLock<Mutex>_(mMutex);
439 assert(subject.length() == 3);
440 mPromptSelector =
441 *subject[1].data().interpretedAs<CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR>(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
442 mPromptDescription = subject[2].toString();
443 }
444
445
446 //
447 // Take this ACL and produce its meaning as a CSSM ACL subject in mSubjectForm
448 //
449 void ACL::makeSubject()
450 {
451 StLock<Mutex>_(mMutex);
452 switch (form()) {
453 case allowAllForm:
454 chunkFree(mSubjectForm, allocator); // release previous
455 if (mPromptDescription.empty()) {
456 // no description -> pure ANY
457 mSubjectForm = new(allocator) TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_ANY);
458 } else {
459 // have description -> threshold(1 of 2) of { ANY, PROMPT }
460 mSubjectForm = new(allocator) TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_THRESHOLD,
461 new(allocator) ListElement(1),
462 new(allocator) ListElement(2));
463 *mSubjectForm += new(allocator) ListElement(TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_ANY));
464 TypedList prompt(allocator, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT,
465 new(allocator) ListElement(allocator, CssmData::wrap(mPromptSelector)),
466 new(allocator) ListElement(allocator, mPromptDescription));
467 *mSubjectForm += new(allocator) ListElement(prompt);
468 }
469 secdebug("SecAccess", "made an allowAllForm (%d) into a subjectForm (%d)", mForm, mSubjectForm->type());
470 return;
471 case appListForm: {
472 // threshold(1 of n+1) of { app1, ..., appn, PROMPT }
473 chunkFree(mSubjectForm, allocator); // release previous
474 uint32 appCount = (uint32)mAppList.size();
475 mSubjectForm = new(allocator) TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_THRESHOLD,
476 new(allocator) ListElement(1),
477 new(allocator) ListElement(appCount + 1));
478 for (uint32 n = 0; n < appCount; n++)
479 *mSubjectForm +=
480 new(allocator) ListElement(mAppList[n]->makeSubject(allocator));
481 TypedList prompt(allocator, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT,
482 new(allocator) ListElement(allocator, CssmData::wrap(mPromptSelector)),
483 new(allocator) ListElement(allocator, mPromptDescription));
484 *mSubjectForm += new(allocator) ListElement(prompt);
485 }
486 secdebug("SecAccess", "made an appListForm (%d) into a subjectForm (%d)", mForm, mSubjectForm->type());
487 return;
488 case integrityForm:
489 chunkFree(mSubjectForm, allocator);
490 mSubjectForm = new(allocator) TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_PARTITION,
491 new(allocator) ListElement(allocator, mIntegrity));
492 secdebug("SecAccess", "made an integrityForm (%d) into a subjectForm (%d)", mForm, mSubjectForm->type());
493 return;
494 case customForm:
495 assert(mSubjectForm); // already set; keep it
496 secdebug("SecAccess", "have a customForm (%d), already have a subjectForm (%d)", mForm, mSubjectForm->type());
497 return;
498
499 default:
500 assert(false); // unexpected
501 }
502 }