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(Access
&acc
, const AclEntryInfo
&info
, Allocator
&alloc
)
61 : allocator(alloc
), access(acc
), mState(unchanged
), mSubjectForm(NULL
), 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();
76 ACL::ACL(Access
&acc
, const AclOwnerPrototype
&owner
, Allocator
&alloc
)
77 : allocator(alloc
), access(acc
), mState(unchanged
), mSubjectForm(NULL
), 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(Access
&acc
, Allocator
&alloc
)
99 : allocator(alloc
), access(acc
), mSubjectForm(NULL
), 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 UniformRandomBlobs
<DevRandomGenerator
>().random(mCssmHandle
);
115 // Create a new ACL in standard form.
116 // As created, it authorizes all activities.
118 ACL::ACL(Access
&acc
, string description
, const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
&promptSelector
,
120 : allocator(alloc
), access(acc
), mSubjectForm(NULL
), 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 UniformRandomBlobs
<DevRandomGenerator
>().random(mCssmHandle
);
140 // release subject form (if any)
141 chunkFree(mSubjectForm
, allocator
);
146 // Does this ACL authorize a particular right?
148 bool ACL::authorizes(AclAuthorization right
)
150 StLock
<Mutex
>_(mMutex
);
151 return mAuthorizations
.find(right
) != mAuthorizations
.end()
152 || mAuthorizations
.find(CSSM_ACL_AUTHORIZATION_ANY
) != mAuthorizations
.end()
153 || mAuthorizations
.empty();
158 // Add an application to the trusted-app list of this ACL.
159 // Will fail unless this is a standard "simple" form ACL.
161 void ACL::addApplication(TrustedApplication
*app
)
163 StLock
<Mutex
>_(mMutex
);
165 case appListForm
: // simple...
166 mAppList
.push_back(app
);
169 case allowAllForm
: // hmm...
170 if (!mPromptDescription
.empty()) {
171 // verbose "any" form (has description, "any" override)
172 mAppList
.push_back(app
);
176 // pure "any" form without description. Cannot convert to appListForm
178 MacOSError::throwMe(errSecACLNotSimple
);
184 // Mark an ACL as modified.
188 StLock
<Mutex
>_(mMutex
);
189 if (mState
== unchanged
) {
190 secdebug("SecAccess", "ACL %p marked modified", this);
197 // Mark an ACL as "removed"
198 // Removed ACLs have no valid contents (they are invalid on their face).
199 // When "updated" to the originating item, they will cause the corresponding
200 // ACL entry to be deleted. Otherwise, they are irrelevant.
201 // Note: Removing an ACL does not actually remove it from its Access's map.
205 StLock
<Mutex
>_(mMutex
);
213 // Produce CSSM-layer form (ACL prototype) copies of our content.
214 // Note that the result is chunk-allocated, and becomes owned by the caller.
216 void ACL::copyAclEntry(AclEntryPrototype
&proto
, Allocator
&alloc
)
218 StLock
<Mutex
>_(mMutex
);
219 proto
.clearPod(); // preset
221 // carefully copy the subject
223 assert(mSubjectForm
);
224 proto
= AclEntryPrototype(*mSubjectForm
, mDelegate
); // shares subject
225 ChunkCopyWalker
w(alloc
);
226 walk(w
, proto
.subject()); // copy subject in-place
228 // the rest of a prototype
229 proto
.tag(mEntryTag
);
230 AuthorizationGroup
tags(mAuthorizations
, allocator
);
231 proto
.authorization() = tags
;
234 void ACL::copyAclOwner(AclOwnerPrototype
&proto
, Allocator
&alloc
)
236 StLock
<Mutex
>_(mMutex
);
240 assert(mSubjectForm
);
241 proto
= AclOwnerPrototype(*mSubjectForm
, mDelegate
); // shares subject
242 ChunkCopyWalker
w(alloc
);
243 walk(w
, proto
.subject()); // copy subject in-place
248 // (Re)place this ACL's setting into the AclBearer specified.
249 // If update, assume this is an update operation and the ACL was
250 // originally derived from this object; specifically, assume the
251 // CSSM handle is valid. If not update, assume this is a different
252 // object that has no related ACL entry (yet).
254 void ACL::setAccess(AclBearer
&target
, bool update
,
255 const AccessCredentials
*cred
)
257 StLock
<Mutex
>_(mMutex
);
258 // determine what action we need to perform
259 State action
= state();
261 action
= (action
== deleted
) ? unchanged
: inserted
;
263 // the owner acl (pseudo) "entry" is a special case
267 secdebug("SecAccess", "ACL %p owner unchanged", this);
269 case inserted
: // means modify the initial owner
272 secdebug("SecAccess", "ACL %p owner modified", this);
274 assert(mSubjectForm
);
275 AclOwnerPrototype
proto(*mSubjectForm
, mDelegate
);
276 target
.changeOwner(proto
, cred
);
287 case unchanged
: // ignore
288 secdebug("SecAccess", "ACL %p handle 0x%lx unchanged", this, entryHandle());
290 case deleted
: // delete
291 secdebug("SecAccess", "ACL %p handle 0x%lx deleted", this, entryHandle());
292 target
.deleteAcl(entryHandle(), cred
);
298 // build the byzantine data structures that CSSM loves so much
300 assert(mSubjectForm
);
301 AclEntryPrototype
proto(*mSubjectForm
, mDelegate
);
302 proto
.tag(mEntryTag
);
303 AutoAuthorizationGroup
tags(mAuthorizations
, allocator
);
304 proto
.authorization() = tags
;
305 AclEntryInput
input(proto
);
307 case inserted
: // insert
308 secdebug("SecAccess", "ACL %p inserted", this);
309 target
.addAcl(input
, cred
);
311 case modified
: // update
312 secdebug("SecAccess", "ACL %p handle 0x%lx modified", this, entryHandle());
313 target
.changeAcl(entryHandle(), input
, cred
);
322 // Parse an AclEntryPrototype (presumably from a CSSM "Get" ACL operation
323 // into internal form.
325 void ACL::parse(const TypedList
&subject
)
327 StLock
<Mutex
>_(mMutex
);
329 switch (subject
.type()) {
330 case CSSM_ACL_SUBJECT_TYPE_ANY
:
331 // subsume an "any" as a standard form
332 mForm
= allowAllForm
;
334 case CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
:
335 // pure keychain prompt - interpret as applist form with no apps
336 parsePrompt(subject
);
339 case CSSM_ACL_SUBJECT_TYPE_THRESHOLD
:
341 // app-list format: THRESHOLD(1, n): sign(1), ..., sign(n), PROMPT
344 uint32 count
= subject
[2];
346 // parse final (PROMPT) element
347 TypedList
&end
= subject
[count
+ 2]; // last choice
348 if (end
.type() != CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
)
349 throw ParseError(); // not PROMPT at end
352 // check for leading ANY
353 TypedList
&first
= subject
[3];
354 if (first
.type() == CSSM_ACL_SUBJECT_TYPE_ANY
) {
355 mForm
= allowAllForm
;
359 // parse other (code signing) elements
360 for (uint32 n
= 0; n
< count
- 1; n
++)
361 mAppList
.push_back(new TrustedApplication(TypedList(subject
[n
+ 3].list())));
367 mSubjectForm
= chunkCopy(&subject
);
370 } catch (const ParseError
&) {
371 secdebug("SecAccess", "acl compile failed; marking custom");
373 mSubjectForm
= chunkCopy(&subject
);
378 void ACL::parsePrompt(const TypedList
&subject
)
380 StLock
<Mutex
>_(mMutex
);
381 assert(subject
.length() == 3);
383 *subject
[1].data().interpretedAs
<CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
>(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE
);
384 mPromptDescription
= subject
[2].toString();
389 // Take this ACL and produce its meaning as a CSSM ACL subject in mSubjectForm
391 void ACL::makeSubject()
393 StLock
<Mutex
>_(mMutex
);
396 chunkFree(mSubjectForm
, allocator
); // release previous
397 if (mPromptDescription
.empty()) {
398 // no description -> pure ANY
399 mSubjectForm
= new(allocator
) TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_ANY
);
401 // have description -> threshold(1 of 2) of { ANY, PROMPT }
402 mSubjectForm
= new(allocator
) TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_THRESHOLD
,
403 new(allocator
) ListElement(1),
404 new(allocator
) ListElement(2));
405 *mSubjectForm
+= new(allocator
) ListElement(TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_ANY
));
406 TypedList
prompt(allocator
, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
,
407 new(allocator
) ListElement(allocator
, CssmData::wrap(mPromptSelector
)),
408 new(allocator
) ListElement(allocator
, mPromptDescription
));
409 *mSubjectForm
+= new(allocator
) ListElement(prompt
);
413 // threshold(1 of n+1) of { app1, ..., appn, PROMPT }
414 chunkFree(mSubjectForm
, allocator
); // release previous
415 uint32 appCount
= (uint32
)mAppList
.size();
416 mSubjectForm
= new(allocator
) TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_THRESHOLD
,
417 new(allocator
) ListElement(1),
418 new(allocator
) ListElement(appCount
+ 1));
419 for (uint32 n
= 0; n
< appCount
; n
++)
421 new(allocator
) ListElement(mAppList
[n
]->makeSubject(allocator
));
422 TypedList
prompt(allocator
, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
,
423 new(allocator
) ListElement(allocator
, CssmData::wrap(mPromptSelector
)),
424 new(allocator
) ListElement(allocator
, mPromptDescription
));
425 *mSubjectForm
+= new(allocator
) ListElement(prompt
);
429 assert(mSubjectForm
); // already set; keep it
432 assert(false); // unexpected