2 * Copyright (c) 2002-2004,2011-2012,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
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>
39 using namespace KeychainCore
;
40 using namespace DataWalkers
;
44 // The default form of a prompt selector
46 const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
ACL::defaultSelector
= {
47 CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION
, 0
52 // ACL static constants
54 const CSSM_ACL_HANDLE
ACL::ownerHandle
;
58 // Create an ACL object from the result of a CSSM ACL query
60 ACL::ACL(const AclEntryInfo
&info
, Allocator
&alloc
)
61 : allocator(alloc
), mState(unchanged
), mSubjectForm(NULL
), mIntegrity(alloc
), mMutex(Mutex::recursive
)
64 parse(info
.proto().subject());
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();
72 // take CSSM entry handle from info layer
73 mCssmHandle
= info
.handle();
77 ACL::ACL(const AclOwnerPrototype
&owner
, Allocator
&alloc
)
78 : allocator(alloc
), mState(unchanged
), mSubjectForm(NULL
), mIntegrity(alloc
), mMutex(Mutex::recursive
)
81 parse(owner
.subject());
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();
88 // use fixed (fake) entry handle
89 mCssmHandle
= ownerHandle
;
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.
99 ACL::ACL(Allocator
&alloc
)
100 : allocator(alloc
), mSubjectForm(NULL
), mIntegrity(alloc
), mMutex(Mutex::recursive
)
102 mState
= inserted
; // new
103 mForm
= allowAllForm
; // everybody
104 mAuthorizations
.insert(CSSM_ACL_AUTHORIZATION_ANY
); // anything
107 //mPromptDescription stays empty
108 mPromptSelector
= defaultSelector
;
110 // randomize the CSSM handle
111 UniformRandomBlobs
<DevRandomGenerator
>().random(mCssmHandle
);
116 // Create a new ACL in standard form.
117 // As created, it authorizes all activities.
119 ACL::ACL(string description
, const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
&promptSelector
,
121 : allocator(alloc
), mSubjectForm(NULL
), mIntegrity(alloc
), mMutex(Mutex::recursive
)
123 mState
= inserted
; // new
125 mAuthorizations
.insert(CSSM_ACL_AUTHORIZATION_ANY
); // anything
128 mPromptDescription
= description
;
129 mPromptSelector
= promptSelector
;
131 // randomize the CSSM handle
132 UniformRandomBlobs
<DevRandomGenerator
>().random(mCssmHandle
);
137 // Create an "integrity" ACL
139 ACL::ACL(const CssmData
&digest
, Allocator
&alloc
)
140 : allocator(alloc
), mSubjectForm(NULL
), mIntegrity(alloc
, digest
), mMutex(Mutex::recursive
)
142 mState
= inserted
; // new
143 mForm
= integrityForm
;
144 mAuthorizations
.insert(CSSM_ACL_AUTHORIZATION_INTEGRITY
);
145 mEntryTag
= CSSM_APPLE_ACL_TAG_INTEGRITY
;
148 //mPromptDescription stays empty
149 //mPromptSelector stays empty
151 // randomize the CSSM handle
152 UniformRandomBlobs
<DevRandomGenerator
>().random(mCssmHandle
);
161 // release subject form (if any)
162 chunkFree(mSubjectForm
, allocator
);
167 // Does this ACL authorize a particular right?
169 bool ACL::authorizes(AclAuthorization right
)
171 StLock
<Mutex
>_(mMutex
);
172 return mAuthorizations
.find(right
) != mAuthorizations
.end()
173 || mAuthorizations
.find(CSSM_ACL_AUTHORIZATION_ANY
) != mAuthorizations
.end()
174 || mAuthorizations
.empty();
178 // Does this ACL have a specific authorization for a particular right?
180 bool ACL::authorizesSpecifically(AclAuthorization right
)
182 StLock
<Mutex
>_(mMutex
);
183 return mAuthorizations
.find(right
) != mAuthorizations
.end();
186 void ACL::setIntegrity(const CssmData
& digest
) {
187 if(mForm
!= integrityForm
) {
188 secnotice("integrity", "acl has incorrect form: %d", mForm
);
189 CssmError::throwMe(CSSMERR_CSP_INVALID_ACL_SUBJECT_VALUE
);
196 const CssmData
& ACL::integrity() {
197 return mIntegrity
.get();
201 // Add an application to the trusted-app list of this ACL.
202 // Will fail unless this is a standard "simple" form ACL.
204 void ACL::addApplication(TrustedApplication
*app
)
206 StLock
<Mutex
>_(mMutex
);
208 case appListForm
: // simple...
209 mAppList
.push_back(app
);
212 case allowAllForm
: // hmm...
213 if (!mPromptDescription
.empty()) {
214 // verbose "any" form (has description, "any" override)
215 mAppList
.push_back(app
);
219 // pure "any" form without description. Cannot convert to appListForm
221 MacOSError::throwMe(errSecACLNotSimple
);
227 // Mark an ACL as modified.
231 StLock
<Mutex
>_(mMutex
);
232 if (mState
== unchanged
) {
233 secinfo("SecAccess", "ACL %p marked modified", this);
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.
248 StLock
<Mutex
>_(mMutex
);
251 secinfo("SecAccess", "ACL %p marked deleted", this);
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.
260 void ACL::copyAclEntry(AclEntryPrototype
&proto
, Allocator
&alloc
)
262 StLock
<Mutex
>_(mMutex
);
263 proto
.clearPod(); // preset
265 // carefully copy the subject
267 assert(mSubjectForm
);
268 proto
= AclEntryPrototype(*mSubjectForm
, mDelegate
); // shares subject
269 ChunkCopyWalker
w(alloc
);
270 walk(w
, proto
.subject()); // copy subject in-place
272 // the rest of a prototype
273 proto
.tag(mEntryTag
);
274 AuthorizationGroup
tags(mAuthorizations
, allocator
);
275 proto
.authorization() = tags
;
278 void ACL::copyAclOwner(AclOwnerPrototype
&proto
, Allocator
&alloc
)
280 StLock
<Mutex
>_(mMutex
);
284 assert(mSubjectForm
);
285 proto
= AclOwnerPrototype(*mSubjectForm
, mDelegate
); // shares subject
286 ChunkCopyWalker
w(alloc
);
287 walk(w
, proto
.subject()); // copy subject in-place
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).
298 void ACL::setAccess(AclBearer
&target
, bool update
,
299 const AccessCredentials
*cred
)
301 StLock
<Mutex
>_(mMutex
);
302 // determine what action we need to perform
303 State action
= state();
305 action
= (action
== deleted
) ? unchanged
: inserted
;
307 // the owner acl (pseudo) "entry" is a special case
311 secinfo("SecAccess", "ACL %p owner unchanged", this);
313 case inserted
: // means modify the initial owner
316 secinfo("SecAccess", "ACL %p owner modified", this);
318 assert(mSubjectForm
);
319 AclOwnerPrototype
proto(*mSubjectForm
, mDelegate
);
320 target
.changeOwner(proto
, cred
);
331 case unchanged
: // ignore
332 secinfo("SecAccess", "ACL %p handle 0x%lx unchanged", this, entryHandle());
334 case deleted
: // delete
335 secinfo("SecAccess", "ACL %p handle 0x%lx deleted", this, entryHandle());
336 target
.deleteAcl(entryHandle(), cred
);
342 // build the byzantine data structures that CSSM loves so much
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
);
351 case inserted
: // insert
352 secinfo("SecAccess", "ACL %p inserted", this);
353 target
.addAcl(input
, cred
);
356 case modified
: // update
357 secinfo("SecAccess", "ACL %p handle 0x%lx modified", this, entryHandle());
358 target
.changeAcl(entryHandle(), input
, cred
);
368 // Parse an AclEntryPrototype (presumably from a CSSM "Get" ACL operation
369 // into internal form.
371 void ACL::parse(const TypedList
&subject
)
373 StLock
<Mutex
>_(mMutex
);
375 switch (subject
.type()) {
376 case CSSM_ACL_SUBJECT_TYPE_ANY
:
377 // subsume an "any" as a standard form
378 mForm
= allowAllForm
;
379 secinfo("SecAccess", "parsed an allowAllForm (%d) (%d)", subject
.type(), mForm
);
381 case CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
:
382 // pure keychain prompt - interpret as applist form with no apps
383 parsePrompt(subject
);
385 secinfo("SecAccess", "parsed a Keychain Prompt (%d) as an appListForm (%d)", subject
.type(), mForm
);
387 case CSSM_ACL_SUBJECT_TYPE_THRESHOLD
:
389 // app-list format: THRESHOLD(1, n): sign(1), ..., sign(n), PROMPT
392 uint32 count
= subject
[2];
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
400 // check for leading ANY
401 TypedList
&first
= subject
[3];
402 if (first
.type() == CSSM_ACL_SUBJECT_TYPE_ANY
) {
403 mForm
= allowAllForm
;
404 secinfo("SecAccess", "parsed a Threshhold (%d) as an allowAllForm (%d)", subject
.type(), mForm
);
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 secinfo("SecAccess", "found an application: %s", mAppList
.back()->path());
415 secinfo("SecAccess", "parsed a Threshhold (%d) as an appListForm (%d)", subject
.type(), mForm
);
417 case CSSM_ACL_SUBJECT_TYPE_PARTITION
:
418 mForm
= integrityForm
;
419 mIntegrity
.copy(subject
.last()->data());
420 secinfo("SecAccess", "parsed a Partition (%d) as an integrityForm (%d)", subject
.type(), mForm
);
423 secinfo("SecAccess", "didn't find a type for %d, marking custom (%d)", subject
.type(), mForm
);
425 mSubjectForm
= chunkCopy(&subject
);
428 } catch (const ParseError
&) {
429 secinfo("SecAccess", "acl compile failed for type (%d); marking custom", subject
.type());
431 mSubjectForm
= chunkCopy(&subject
);
436 void ACL::parsePrompt(const TypedList
&subject
)
438 StLock
<Mutex
>_(mMutex
);
439 assert(subject
.length() == 3);
441 *subject
[1].data().interpretedAs
<CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
>(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE
);
442 mPromptDescription
= subject
[2].toString();
447 // Take this ACL and produce its meaning as a CSSM ACL subject in mSubjectForm
449 void ACL::makeSubject()
451 StLock
<Mutex
>_(mMutex
);
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
);
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
);
469 secinfo("SecAccess", "made an allowAllForm (%d) into a subjectForm (%d)", mForm
, mSubjectForm
->type());
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
++)
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
);
486 secinfo("SecAccess", "made an appListForm (%d) into a subjectForm (%d)", mForm
, mSubjectForm
->type());
489 chunkFree(mSubjectForm
, allocator
);
490 mSubjectForm
= new(allocator
) TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_PARTITION
,
491 new(allocator
) ListElement(allocator
, mIntegrity
));
492 secinfo("SecAccess", "made an integrityForm (%d) into a subjectForm (%d)", mForm
, mSubjectForm
->type());
495 assert(mSubjectForm
); // already set; keep it
496 secinfo("SecAccess", "have a customForm (%d), already have a subjectForm (%d)", mForm
, mSubjectForm
->type());
500 assert(false); // unexpected