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>
30 #include "keychainacl.h"
34 using namespace KeychainCore
;
38 // The default form of a prompt selector
40 const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
ACL::defaultSelector
= {
41 CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION
, 0
46 // Create an ACL object from the result of a CSSM ACL query
48 ACL::ACL(Access
&acc
, const AclEntryInfo
&info
, CssmAllocator
&alloc
)
49 : allocator(alloc
), access(acc
), mState(unchanged
), mSubjectForm(NULL
)
52 parse(info
.proto().subject());
54 // fill in AclEntryInfo layer information
55 const AclEntryPrototype
&proto
= info
.proto();
56 mAuthorizations
= proto
.authorization();
57 mDelegate
= proto
.delegate();
58 mEntryTag
= proto
.tag();
60 // take CSSM entry handle from info layer
61 mCssmHandle
= info
.handle();
64 ACL::ACL(Access
&acc
, const AclOwnerPrototype
&owner
, CssmAllocator
&alloc
)
65 : allocator(alloc
), access(acc
), mState(unchanged
), mSubjectForm(NULL
)
68 parse(owner
.subject());
70 // for an owner "entry", the next-layer information is fixed (and fake)
71 mAuthorizations
.insert(CSSM_ACL_AUTHORIZATION_CHANGE_ACL
);
72 mDelegate
= owner
.delegate();
75 // use fixed (fake) entry handle
76 mCssmHandle
= ownerHandle
;
81 // Create a new ACL that authorizes anyone to do anything.
82 // This constructor produces a "pure" ANY ACL, without descriptor or selector.
83 // To generate a "standard" form of ANY, use the appListForm constructor below,
84 // then change its form to allowAnyForm.
86 ACL::ACL(Access
&acc
, CssmAllocator
&alloc
)
87 : allocator(alloc
), access(acc
), mSubjectForm(NULL
)
89 mState
= inserted
; // new
90 mForm
= allowAllForm
; // everybody
91 mAuthorizations
.insert(CSSM_ACL_AUTHORIZATION_ANY
); // anything
94 //mPromptDescription stays empty
95 mPromptSelector
= defaultSelector
;
97 // randomize the CSSM handle
98 UniformRandomBlobs
<DevRandomGenerator
>().random(mCssmHandle
);
103 // Create a new ACL in standard form.
104 // As created, it authorizes all activities.
106 ACL::ACL(Access
&acc
, string description
, const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
&promptSelector
,
107 CssmAllocator
&alloc
)
108 : allocator(alloc
), access(acc
), mSubjectForm(NULL
)
110 mState
= inserted
; // new
112 mAuthorizations
.insert(CSSM_ACL_AUTHORIZATION_ANY
); // anything
115 mPromptDescription
= description
;
116 mPromptSelector
= promptSelector
;
118 // randomize the CSSM handle
119 UniformRandomBlobs
<DevRandomGenerator
>().random(mCssmHandle
);
132 // Does this ACL authorize a particular right?
134 bool ACL::authorizes(AclAuthorization right
) const
136 return mAuthorizations
.find(right
) != mAuthorizations
.end()
138 mAuthorizations
.find(CSSM_ACL_AUTHORIZATION_ANY
) != mAuthorizations
.end();
143 // Add an application to the trusted-app list of this ACL.
144 // Will fail unless this is a standard "simple" form ACL.
146 void ACL::addApplication(TrustedApplication
*app
)
149 case appListForm
: // simple...
150 mAppList
.push_back(app
);
153 case allowAllForm
: // hmm...
154 if (!mPromptDescription
.empty()) {
155 // verbose "any" form (has description, "any" override)
156 mAppList
.push_back(app
);
160 // pure "any" form without description. Cannot convert to appListForm
162 MacOSError::throwMe(errSecACLNotSimple
);
168 // Mark an ACL as modified.
172 if (mState
== unchanged
) {
173 debug("SecAccess", "ACL %p marked modified", this);
180 // Mark an ACL as "removed"
181 // Removed ACLs have no valid contents (they are invalid on their face).
182 // When "updated" to the originating item, they will cause the corresponding
183 // ACL entry to be deleted. Otherwise, they are irrelevant.
184 // Note: Removing an ACL does not actually remove it from its Access's map.
195 // (Re)place this ACL's setting into the AclBearer specified.
196 // If update, assume this is an update operation and the ACL was
197 // originally derived from this object; specifically, assume the
198 // CSSM handle is valid. If not update, assume this is a different
199 // object that has no related ACL entry (yet).
201 void ACL::setAccess(AclBearer
&target
, bool update
,
202 const AccessCredentials
*cred
)
204 // determine what action we need to perform
205 State action
= state();
207 action
= (action
== deleted
) ? unchanged
: inserted
;
209 // the owner acl (pseudo) "entry" is a special case
213 debug("SecAccess", "ACL %p owner unchanged", this);
215 case inserted
: // means modify the initial owner
218 debug("SecAccess", "ACL %p owner modified", this);
220 assert(mSubjectForm
);
221 AclOwnerPrototype
proto(*mSubjectForm
, mDelegate
);
222 target
.changeOwner(proto
, cred
);
233 case unchanged
: // ignore
234 debug("SecAccess", "ACL %p handle 0x%lx unchanged", this, entryHandle());
236 case deleted
: // delete
237 debug("SecAccess", "ACL %p handle 0x%lx deleted", this, entryHandle());
238 target
.deleteAcl(entryHandle(), cred
);
244 // build the byzantine data structures that CSSM loves so much
246 assert(mSubjectForm
);
247 AclEntryPrototype
proto(*mSubjectForm
, mDelegate
);
248 assert(mEntryTag
.size() <= CSSM_MODULE_STRING_SIZE
); // no kidding
249 strcpy(proto
.tag(), mEntryTag
.c_str());
250 AutoAuthorizationGroup
tags(mAuthorizations
, allocator
);
251 proto
.authorization() = tags
;
252 AclEntryInput
input(proto
);
254 case inserted
: // insert
255 debug("SecAccess", "ACL %p inserted", this);
256 target
.addAcl(input
, cred
);
258 case modified
: // update
259 debug("SecAccess", "ACL %p handle 0x%lx modified", this, entryHandle());
260 target
.changeAcl(entryHandle(), input
, cred
);
269 // Parse an AclEntryPrototype (presumably from a CSSM "Get" ACL operation
270 // into internal form.
272 void ACL::parse(const TypedList
&subject
)
275 switch (subject
.type()) {
276 case CSSM_ACL_SUBJECT_TYPE_ANY
:
277 // subsume an "any" as a standard form
278 mForm
= allowAllForm
;
280 case CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
:
281 // pure keychain prompt - interpret as applist form with no apps
282 parsePrompt(subject
);
285 case CSSM_ACL_SUBJECT_TYPE_THRESHOLD
:
287 // app-list format: THRESHOLD(1, n): sign(1), ..., sign(n), PROMPT
290 uint32 count
= subject
[2];
292 // parse final (PROMPT) element
293 const TypedList
&end
= subject
[count
+ 2]; // last choice
294 if (end
.type() != CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
)
295 throw ParseError(); // not PROMPT at end
298 // check for leading ANY
299 const TypedList
&first
= subject
[3];
300 if (first
.type() == CSSM_ACL_SUBJECT_TYPE_ANY
) {
301 mForm
= allowAllForm
;
305 // parse other (SIGN) elements
306 for (uint32 n
= 0; n
< count
- 1; n
++)
307 mAppList
.push_back(new TrustedApplication(subject
[n
+ 3]));
315 } catch (const ParseError
&) {
316 debug("SecAccess", "acl compile failed; marking custom");
322 void ACL::parsePrompt(const TypedList
&subject
)
324 assert(subject
.length() == 3);
325 mPromptSelector
= *subject
[1].data().interpretedAs
<CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR
>();
326 mPromptDescription
= subject
[2].toString();
331 // Take this ACL and produce its meaning as a CSSM ACL subject in mSubjectForm
333 void ACL::makeSubject()
335 // release previous value, if any
336 chunkFree(mSubjectForm
, allocator
);
340 if (mPromptDescription
.empty()) {
341 // no description -> pure ANY
342 mSubjectForm
= new(allocator
) TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_ANY
);
344 // have description -> threshold(1 of 2) of { ANY, PROMPT }
345 mSubjectForm
= new(allocator
) TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_THRESHOLD
,
346 new(allocator
) ListElement(1),
347 new(allocator
) ListElement(2));
348 *mSubjectForm
+= new(allocator
) ListElement(TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_ANY
));
349 TypedList
prompt(allocator
, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
,
350 new(allocator
) ListElement(allocator
, CssmData::wrap(mPromptSelector
)),
351 new(allocator
) ListElement(allocator
, mPromptDescription
));
352 *mSubjectForm
+= new(allocator
) ListElement(prompt
);
356 // threshold(1 of n+1) of { app1, ..., appn, PROMPT }
357 uint32 appCount
= mAppList
.size();
358 mSubjectForm
= new(allocator
) TypedList(allocator
, CSSM_ACL_SUBJECT_TYPE_THRESHOLD
,
359 new(allocator
) ListElement(1),
360 new(allocator
) ListElement(appCount
+ 1));
361 for (uint32 n
= 0; n
< appCount
; n
++)
363 new(allocator
) ListElement(mAppList
[n
]->makeSubject(allocator
));
364 TypedList
prompt(allocator
, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
,
365 new(allocator
) ListElement(allocator
, CssmData::wrap(mPromptSelector
)),
366 new(allocator
) ListElement(allocator
, mPromptDescription
));
367 *mSubjectForm
+= new(allocator
) ListElement(prompt
);
371 assert(false); // @@@ not yet
373 assert(false); // unexpected