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/SecRandom.h>
38 using namespace KeychainCore
;
39 using namespace DataWalkers
;
43 // The default form of a prompt selector
45 const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
ACL::defaultSelector
= {
46 CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION
, 0
51 // ACL static constants
53 const CSSM_ACL_HANDLE
ACL::ownerHandle
;
57 // Create an ACL object from the result of a CSSM ACL query
59 ACL::ACL(const AclEntryInfo
&info
, Allocator
&alloc
)
60 : allocator(alloc
), mState(unchanged
), mSubjectForm(NULL
), mIntegrity(alloc
), mMutex(Mutex::recursive
)
63 parse(info
.proto().subject());
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();
71 // take CSSM entry handle from info layer
72 mCssmHandle
= info
.handle();
76 ACL::ACL(const AclOwnerPrototype
&owner
, Allocator
&alloc
)
77 : allocator(alloc
), mState(unchanged
), mSubjectForm(NULL
), mIntegrity(alloc
), mMutex(Mutex::recursive
)
80 parse(owner
.subject());
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();
87 // use fixed (fake) entry handle
88 mCssmHandle
= ownerHandle
;
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.
98 ACL::ACL(Allocator
&alloc
)
99 : allocator(alloc
), mSubjectForm(NULL
), mIntegrity(alloc
), mMutex(Mutex::recursive
)
101 mState
= inserted
; // new
102 mForm
= allowAllForm
; // everybody
103 mAuthorizations
.insert(CSSM_ACL_AUTHORIZATION_ANY
); // anything
106 //mPromptDescription stays empty
107 mPromptSelector
= defaultSelector
;
109 // randomize the CSSM handle
110 MacOSError::check(SecRandomCopyBytes(kSecRandomDefault
, sizeof(mCssmHandle
), (void *)mCssmHandle
));
115 // Create a new ACL in standard form.
116 // As created, it authorizes all activities.
118 ACL::ACL(string description
, const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
&promptSelector
,
120 : allocator(alloc
), mSubjectForm(NULL
), mIntegrity(alloc
), mMutex(Mutex::recursive
)
122 mState
= inserted
; // new
124 mAuthorizations
.insert(CSSM_ACL_AUTHORIZATION_ANY
); // anything
127 mPromptDescription
= description
;
128 mPromptSelector
= promptSelector
;
130 // randomize the CSSM handle
131 MacOSError::check(SecRandomCopyBytes(kSecRandomDefault
, sizeof(mCssmHandle
), &mCssmHandle
));
136 // Create an "integrity" ACL
138 ACL::ACL(const CssmData
&digest
, Allocator
&alloc
)
139 : allocator(alloc
), mSubjectForm(NULL
), mIntegrity(alloc
, digest
), mMutex(Mutex::recursive
)
141 mState
= inserted
; // new
142 mForm
= integrityForm
;
143 mAuthorizations
.insert(CSSM_ACL_AUTHORIZATION_INTEGRITY
);
144 mEntryTag
= CSSM_APPLE_ACL_TAG_INTEGRITY
;
147 //mPromptDescription stays empty
148 //mPromptSelector stays empty
150 // randomize the CSSM handle
151 MacOSError::check(SecRandomCopyBytes(kSecRandomDefault
, sizeof(mCssmHandle
), &mCssmHandle
));
160 // release subject form (if any)
161 chunkFree(mSubjectForm
, allocator
);
166 // Does this ACL authorize a particular right?
168 bool ACL::authorizes(AclAuthorization right
)
170 StLock
<Mutex
>_(mMutex
);
171 return mAuthorizations
.find(right
) != mAuthorizations
.end()
172 || mAuthorizations
.find(CSSM_ACL_AUTHORIZATION_ANY
) != mAuthorizations
.end()
173 || mAuthorizations
.empty();
177 // Does this ACL have a specific authorization for a particular right?
179 bool ACL::authorizesSpecifically(AclAuthorization right
)
181 StLock
<Mutex
>_(mMutex
);
182 return mAuthorizations
.find(right
) != mAuthorizations
.end();
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
);
195 const CssmData
& ACL::integrity() {
196 return mIntegrity
.get();
200 // Add an application to the trusted-app list of this ACL.
201 // Will fail unless this is a standard "simple" form ACL.
203 void ACL::addApplication(TrustedApplication
*app
)
205 StLock
<Mutex
>_(mMutex
);
207 case appListForm
: // simple...
208 mAppList
.push_back(app
);
211 case allowAllForm
: // hmm...
212 if (!mPromptDescription
.empty()) {
213 // verbose "any" form (has description, "any" override)
214 mAppList
.push_back(app
);
218 // pure "any" form without description. Cannot convert to appListForm
220 MacOSError::throwMe(errSecACLNotSimple
);
226 // Mark an ACL as modified.
230 StLock
<Mutex
>_(mMutex
);
231 if (mState
== unchanged
) {
232 secinfo("SecAccess", "ACL %p marked modified", this);
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.
247 StLock
<Mutex
>_(mMutex
);
250 secinfo("SecAccess", "ACL %p marked deleted", this);
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.
259 void ACL::copyAclEntry(AclEntryPrototype
&proto
, Allocator
&alloc
)
261 StLock
<Mutex
>_(mMutex
);
262 proto
.clearPod(); // preset
264 // carefully copy the subject
266 assert(mSubjectForm
);
267 proto
= AclEntryPrototype(*mSubjectForm
, mDelegate
); // shares subject
268 ChunkCopyWalker
w(alloc
);
269 walk(w
, proto
.subject()); // copy subject in-place
271 // the rest of a prototype
272 proto
.tag(mEntryTag
);
273 AuthorizationGroup
tags(mAuthorizations
, allocator
);
274 proto
.authorization() = tags
;
277 void ACL::copyAclOwner(AclOwnerPrototype
&proto
, Allocator
&alloc
)
279 StLock
<Mutex
>_(mMutex
);
283 assert(mSubjectForm
);
284 proto
= AclOwnerPrototype(*mSubjectForm
, mDelegate
); // shares subject
285 ChunkCopyWalker
w(alloc
);
286 walk(w
, proto
.subject()); // copy subject in-place
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).
297 void ACL::setAccess(AclBearer
&target
, bool update
,
298 const AccessCredentials
*cred
)
300 StLock
<Mutex
>_(mMutex
);
301 // determine what action we need to perform
302 State action
= state();
304 action
= (action
== deleted
) ? unchanged
: inserted
;
306 // the owner acl (pseudo) "entry" is a special case
310 secinfo("SecAccess", "ACL %p owner unchanged", this);
312 case inserted
: // means modify the initial owner
315 secinfo("SecAccess", "ACL %p owner modified", this);
317 assert(mSubjectForm
);
318 AclOwnerPrototype
proto(*mSubjectForm
, mDelegate
);
319 target
.changeOwner(proto
, cred
);
330 case unchanged
: // ignore
331 secinfo("SecAccess", "ACL %p handle 0x%lx unchanged", this, entryHandle());
333 case deleted
: // delete
334 secinfo("SecAccess", "ACL %p handle 0x%lx deleted", this, entryHandle());
335 target
.deleteAcl(entryHandle(), cred
);
341 // build the byzantine data structures that CSSM loves so much
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
);
350 case inserted
: // insert
351 secinfo("SecAccess", "ACL %p inserted", this);
352 target
.addAcl(input
, cred
);
355 case modified
: // update
356 secinfo("SecAccess", "ACL %p handle 0x%lx modified", this, entryHandle());
357 target
.changeAcl(entryHandle(), input
, cred
);
367 // Parse an AclEntryPrototype (presumably from a CSSM "Get" ACL operation
368 // into internal form.
370 void ACL::parse(const TypedList
&subject
)
372 StLock
<Mutex
>_(mMutex
);
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
);
380 case CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
:
381 // pure keychain prompt - interpret as applist form with no apps
382 parsePrompt(subject
);
384 secinfo("SecAccess", "parsed a Keychain Prompt (%d) as an appListForm (%d)", subject
.type(), mForm
);
386 case CSSM_ACL_SUBJECT_TYPE_THRESHOLD
:
388 // app-list format: THRESHOLD(1, n): sign(1), ..., sign(n), PROMPT
391 uint32 count
= subject
[2];
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
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
);
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());
414 secinfo("SecAccess", "parsed a Threshhold (%d) as an appListForm (%d)", subject
.type(), mForm
);
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
);
422 secinfo("SecAccess", "didn't find a type for %d, marking custom (%d)", subject
.type(), mForm
);
424 mSubjectForm
= chunkCopy(&subject
);
427 } catch (const ParseError
&) {
428 secinfo("SecAccess", "acl compile failed for type (%d); marking custom", subject
.type());
430 mSubjectForm
= chunkCopy(&subject
);
435 void ACL::parsePrompt(const TypedList
&subject
)
437 StLock
<Mutex
>_(mMutex
);
438 assert(subject
.length() == 3);
440 *subject
[1].data().interpretedAs
<CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
>(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE
);
441 mPromptDescription
= subject
[2].toString();
446 // Take this ACL and produce its meaning as a CSSM ACL subject in mSubjectForm
448 void ACL::makeSubject()
450 StLock
<Mutex
>_(mMutex
);
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
);
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
);
468 secinfo("SecAccess", "made an allowAllForm (%d) into a subjectForm (%d)", mForm
, mSubjectForm
->type());
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
++)
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
);
485 secinfo("SecAccess", "made an appListForm (%d) into a subjectForm (%d)", mForm
, mSubjectForm
->type());
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());
494 assert(mSubjectForm
); // already set; keep it
495 secinfo("SecAccess", "have a customForm (%d), already have a subjectForm (%d)", mForm
, mSubjectForm
->type());
499 assert(false); // unexpected