2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
21 #include <Security/ACL.h>
22 #include <Security/SecCFTypes.h>
23 #include <Security/osxsigning.h>
24 #include <Security/osxsigner.h>
25 #include <Security/trackingallocator.h>
26 #include <Security/TrustedApplication.h>
27 #include <Security/SecTrustedApplication.h>
28 #include <Security/devrandom.h>
29 #include <Security/uniformrandom.h>
33 using namespace KeychainCore
;
37 // The default form of a prompt selector
39 const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
ACL::defaultSelector
= {
40 CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION
, 0
45 // Create an ACL object from the result of a CSSM ACL query
47 ACL::ACL(Access
&acc
, const AclEntryInfo
&info
, CssmAllocator
&alloc
)
48 : allocator(alloc
), access(acc
), mState(unchanged
), mSubjectForm(NULL
)
51 parse(info
.proto().subject());
53 // fill in AclEntryInfo layer information
54 const AclEntryPrototype
&proto
= info
.proto();
55 mAuthorizations
= proto
.authorization();
56 mDelegate
= proto
.delegate();
57 mEntryTag
= proto
.tag();
59 // take CSSM entry handle from info layer
60 mCssmHandle
= info
.handle();
63 ACL::ACL(Access
&acc
, const AclOwnerPrototype
&owner
, CssmAllocator
&alloc
)
64 : allocator(alloc
), access(acc
), mState(unchanged
), mSubjectForm(NULL
)
67 parse(owner
.subject());
69 // for an owner "entry", the next-layer information is fixed (and fake)
70 mAuthorizations
.insert(CSSM_ACL_AUTHORIZATION_CHANGE_ACL
);
71 mDelegate
= owner
.delegate();
74 // use fixed (fake) entry handle
75 mCssmHandle
= ownerHandle
;
80 // Create a new ACL that authorizes anyone to do anything.
81 // This constructor produces a "pure" ANY ACL, without descriptor or selector.
82 // To generate a "standard" form of ANY, use the appListForm constructor below,
83 // then change its form to allowAnyForm.
85 ACL::ACL(Access
&acc
, CssmAllocator
&alloc
)
86 : allocator(alloc
), access(acc
), mSubjectForm(NULL
)
88 mState
= inserted
; // new
89 mForm
= allowAllForm
; // everybody
90 mAuthorizations
.insert(CSSM_ACL_AUTHORIZATION_ANY
); // anything
93 //mPromptDescription stays empty
94 mPromptSelector
= defaultSelector
;
96 // randomize the CSSM handle
97 UniformRandomBlobs
<DevRandomGenerator
>().random(mCssmHandle
);
102 // Create a new ACL in standard form.
103 // As created, it authorizes all activities.
105 ACL::ACL(Access
&acc
, string description
, const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
&promptSelector
,
106 CssmAllocator
&alloc
)
107 : allocator(alloc
), access(acc
), mSubjectForm(NULL
)
109 mState
= inserted
; // new
111 mAuthorizations
.insert(CSSM_ACL_AUTHORIZATION_ANY
); // anything
114 mPromptDescription
= description
;
115 mPromptSelector
= promptSelector
;
117 // randomize the CSSM handle
118 UniformRandomBlobs
<DevRandomGenerator
>().random(mCssmHandle
);
127 // release subject form (if any)
128 chunkFree(mSubjectForm
, allocator
);
133 // Does this ACL authorize a particular right?
135 bool ACL::authorizes(AclAuthorization right
) const
137 return mAuthorizations
.find(right
) != mAuthorizations
.end()
138 || mAuthorizations
.find(CSSM_ACL_AUTHORIZATION_ANY
) != mAuthorizations
.end()
139 || mAuthorizations
.empty();
144 // Add an application to the trusted-app list of this ACL.
145 // Will fail unless this is a standard "simple" form ACL.
147 void ACL::addApplication(TrustedApplication
*app
)
150 case appListForm
: // simple...
151 mAppList
.push_back(app
);
154 case allowAllForm
: // hmm...
155 if (!mPromptDescription
.empty()) {
156 // verbose "any" form (has description, "any" override)
157 mAppList
.push_back(app
);
161 // pure "any" form without description. Cannot convert to appListForm
163 MacOSError::throwMe(errSecACLNotSimple
);
169 // Mark an ACL as modified.
173 if (mState
== unchanged
) {
174 secdebug("SecAccess", "ACL %p marked modified", this);
181 // Mark an ACL as "removed"
182 // Removed ACLs have no valid contents (they are invalid on their face).
183 // When "updated" to the originating item, they will cause the corresponding
184 // ACL entry to be deleted. Otherwise, they are irrelevant.
185 // Note: Removing an ACL does not actually remove it from its Access's map.
196 // Produce CSSM-layer form (ACL prototype) copies of our content.
197 // Note that the result is chunk-allocated, and becomes the responsibility
200 void ACL::copyAclEntry(AclEntryPrototype
&proto
, CssmAllocator
&alloc
)
202 proto
.clearPod(); // preset
204 // carefully copy the subject
206 assert(mSubjectForm
);
207 proto
= AclEntryPrototype(*mSubjectForm
, mDelegate
); // shares subject
208 ChunkCopyWalker
w(alloc
);
209 walk(w
, proto
.subject()); // copy subject in-place
211 // the rest of a prototype
212 assert(mEntryTag
.size() <= CSSM_MODULE_STRING_SIZE
); // no kidding
213 strcpy(proto
.tag(), mEntryTag
.c_str());
214 AuthorizationGroup
tags(mAuthorizations
, allocator
);
215 proto
.authorization() = tags
;
218 void ACL::copyAclOwner(AclOwnerPrototype
&proto
, CssmAllocator
&alloc
)
223 assert(mSubjectForm
);
224 proto
= AclOwnerPrototype(*mSubjectForm
, mDelegate
); // shares subject
225 ChunkCopyWalker
w(alloc
);
226 walk(w
, proto
.subject()); // copy subject in-place
231 // (Re)place this ACL's setting into the AclBearer specified.
232 // If update, assume this is an update operation and the ACL was
233 // originally derived from this object; specifically, assume the
234 // CSSM handle is valid. If not update, assume this is a different
235 // object that has no related ACL entry (yet).
237 void ACL::setAccess(AclBearer
&target
, bool update
,
238 const AccessCredentials
*cred
)
240 // determine what action we need to perform
241 State action
= state();
243 action
= (action
== deleted
) ? unchanged
: inserted
;
245 // the owner acl (pseudo) "entry" is a special case
249 secdebug("SecAccess", "ACL %p owner unchanged", this);
251 case inserted
: // means modify the initial owner
254 secdebug("SecAccess", "ACL %p owner modified", this);
256 assert(mSubjectForm
);
257 AclOwnerPrototype
proto(*mSubjectForm
, mDelegate
);
258 target
.changeOwner(proto
, cred
);
269 case unchanged
: // ignore
270 secdebug("SecAccess", "ACL %p handle 0x%lx unchanged", this, entryHandle());
272 case deleted
: // delete
273 secdebug("SecAccess", "ACL %p handle 0x%lx deleted", this, entryHandle());
274 target
.deleteAcl(entryHandle(), cred
);
280 // build the byzantine data structures that CSSM loves so much
282 assert(mSubjectForm
);
283 AclEntryPrototype
proto(*mSubjectForm
, mDelegate
);
284 assert(mEntryTag
.size() <= CSSM_MODULE_STRING_SIZE
); // no kidding
285 strcpy(proto
.tag(), mEntryTag
.c_str());
286 AutoAuthorizationGroup
tags(mAuthorizations
, allocator
);
287 proto
.authorization() = tags
;
288 AclEntryInput
input(proto
);
290 case inserted
: // insert
291 secdebug("SecAccess", "ACL %p inserted", this);
292 target
.addAcl(input
, cred
);
294 case modified
: // update
295 secdebug("SecAccess", "ACL %p handle 0x%lx modified", this, entryHandle());
296 target
.changeAcl(entryHandle(), input
, cred
);
305 // Parse an AclEntryPrototype (presumably from a CSSM "Get" ACL operation
306 // into internal form.
308 void ACL::parse(const TypedList
&subject
)
311 switch (subject
.type()) {
312 case CSSM_ACL_SUBJECT_TYPE_ANY
:
313 // subsume an "any" as a standard form
314 mForm
= allowAllForm
;
316 case CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
:
317 // pure keychain prompt - interpret as applist form with no apps
318 parsePrompt(subject
);
321 case CSSM_ACL_SUBJECT_TYPE_THRESHOLD
:
323 // app-list format: THRESHOLD(1, n): sign(1), ..., sign(n), PROMPT
326 uint32 count
= subject
[2];
328 // parse final (PROMPT) element
329 TypedList
&end
= subject
[count
+ 2]; // last choice
330 if (end
.type() != CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
)
331 throw ParseError(); // not PROMPT at end
334 // check for leading ANY
335 TypedList
&first
= subject
[3];
336 if (first
.type() == CSSM_ACL_SUBJECT_TYPE_ANY
) {
337 mForm
= allowAllForm
;
341 // parse other (SIGN) elements
342 for (uint32 n
= 0; n
< count
- 1; n
++)
343 mAppList
.push_back(new TrustedApplication(subject
[n
+ 3]));
349 mSubjectForm
= chunkCopy(&subject
);
352 } catch (const ParseError
&) {
353 secdebug("SecAccess", "acl compile failed; marking custom");
359 void ACL::parsePrompt(const TypedList
&subject
)
361 assert(subject
.length() == 3);
363 *subject
[1].data().interpretedAs
<CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
>(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE
);
364 mPromptDescription
= subject
[2].toString();
369 // Take this ACL and produce its meaning as a CSSM ACL subject in mSubjectForm
371 void ACL::makeSubject()
375 chunkFree(mSubjectForm
, allocator
); // release previous
376 if (mPromptDescription
.empty()) {
377 // no description -> pure ANY
378 mSubjectForm
= new(allocator
) TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_ANY
);
380 // have description -> threshold(1 of 2) of { ANY, PROMPT }
381 mSubjectForm
= new(allocator
) TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_THRESHOLD
,
382 new(allocator
) ListElement(1),
383 new(allocator
) ListElement(2));
384 *mSubjectForm
+= new(allocator
) ListElement(TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_ANY
));
385 TypedList
prompt(allocator
, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
,
386 new(allocator
) ListElement(allocator
, CssmData::wrap(mPromptSelector
)),
387 new(allocator
) ListElement(allocator
, mPromptDescription
));
388 *mSubjectForm
+= new(allocator
) ListElement(prompt
);
392 // threshold(1 of n+1) of { app1, ..., appn, PROMPT }
393 chunkFree(mSubjectForm
, allocator
); // release previous
394 uint32 appCount
= mAppList
.size();
395 mSubjectForm
= new(allocator
) TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_THRESHOLD
,
396 new(allocator
) ListElement(1),
397 new(allocator
) ListElement(appCount
+ 1));
398 for (uint32 n
= 0; n
< appCount
; n
++)
400 new(allocator
) ListElement(mAppList
[n
]->makeSubject(allocator
));
401 TypedList
prompt(allocator
, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
,
402 new(allocator
) ListElement(allocator
, CssmData::wrap(mPromptSelector
)),
403 new(allocator
) ListElement(allocator
, mPromptDescription
));
404 *mSubjectForm
+= new(allocator
) ListElement(prompt
);
408 assert(mSubjectForm
); // already set; keep it
411 assert(false); // unexpected