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