]> git.saurik.com Git - apple/security.git/blob - Keychain/ACL.cpp
Security-54.1.tar.gz
[apple/security.git] / Keychain / ACL.cpp
1 /*
2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18 //
19 // ACL.cpp
20 //
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"
31 #include <memory>
32
33
34 using namespace KeychainCore;
35
36
37 //
38 // The default form of a prompt selector
39 //
40 const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR ACL::defaultSelector = {
41 CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION, 0
42 };
43
44
45 //
46 // Create an ACL object from the result of a CSSM ACL query
47 //
48 ACL::ACL(Access &acc, const AclEntryInfo &info, CssmAllocator &alloc)
49 : allocator(alloc), access(acc), mState(unchanged), mSubjectForm(NULL)
50 {
51 // parse the subject
52 parse(info.proto().subject());
53
54 // fill in AclEntryInfo layer information
55 const AclEntryPrototype &proto = info.proto();
56 mAuthorizations = proto.authorization();
57 mDelegate = proto.delegate();
58 mEntryTag = proto.tag();
59
60 // take CSSM entry handle from info layer
61 mCssmHandle = info.handle();
62 }
63
64 ACL::ACL(Access &acc, const AclOwnerPrototype &owner, CssmAllocator &alloc)
65 : allocator(alloc), access(acc), mState(unchanged), mSubjectForm(NULL)
66 {
67 // parse subject
68 parse(owner.subject());
69
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();
73 mEntryTag[0] = '\0';
74
75 // use fixed (fake) entry handle
76 mCssmHandle = ownerHandle;
77 }
78
79
80 //
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.
85 //
86 ACL::ACL(Access &acc, CssmAllocator &alloc)
87 : allocator(alloc), access(acc), mSubjectForm(NULL)
88 {
89 mState = inserted; // new
90 mForm = allowAllForm; // everybody
91 mAuthorizations.insert(CSSM_ACL_AUTHORIZATION_ANY); // anything
92 mDelegate = false;
93
94 //mPromptDescription stays empty
95 mPromptSelector = defaultSelector;
96
97 // randomize the CSSM handle
98 UniformRandomBlobs<DevRandomGenerator>().random(mCssmHandle);
99 }
100
101
102 //
103 // Create a new ACL in standard form.
104 // As created, it authorizes all activities.
105 //
106 ACL::ACL(Access &acc, string description, const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR &promptSelector,
107 CssmAllocator &alloc)
108 : allocator(alloc), access(acc), mSubjectForm(NULL)
109 {
110 mState = inserted; // new
111 mForm = appListForm;
112 mAuthorizations.insert(CSSM_ACL_AUTHORIZATION_ANY); // anything
113 mDelegate = false;
114
115 mPromptDescription = description;
116 mPromptSelector = promptSelector;
117
118 // randomize the CSSM handle
119 UniformRandomBlobs<DevRandomGenerator>().random(mCssmHandle);
120 }
121
122
123 //
124 // Destroy an ACL
125 //
126 ACL::~ACL()
127 {
128 }
129
130
131 //
132 // Does this ACL authorize a particular right?
133 //
134 bool ACL::authorizes(AclAuthorization right) const
135 {
136 return mAuthorizations.find(right) != mAuthorizations.end()
137 ||
138 mAuthorizations.find(CSSM_ACL_AUTHORIZATION_ANY) != mAuthorizations.end();
139 }
140
141
142 //
143 // Add an application to the trusted-app list of this ACL.
144 // Will fail unless this is a standard "simple" form ACL.
145 //
146 void ACL::addApplication(TrustedApplication *app)
147 {
148 switch (mForm) {
149 case appListForm: // simple...
150 mAppList.push_back(app);
151 modify();
152 break;
153 case allowAllForm: // hmm...
154 if (!mPromptDescription.empty()) {
155 // verbose "any" form (has description, "any" override)
156 mAppList.push_back(app);
157 modify();
158 break;
159 }
160 // pure "any" form without description. Cannot convert to appListForm
161 default:
162 MacOSError::throwMe(errSecACLNotSimple);
163 }
164 }
165
166
167 //
168 // Mark an ACL as modified.
169 //
170 void ACL::modify()
171 {
172 if (mState == unchanged) {
173 debug("SecAccess", "ACL %p marked modified", this);
174 mState = modified;
175 }
176 }
177
178
179 //
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.
185 //
186 void ACL::remove()
187 {
188 mAppList.clear();
189 mForm = invalidForm;
190 mState = deleted;
191 }
192
193
194 //
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).
200 //
201 void ACL::setAccess(AclBearer &target, bool update,
202 const AccessCredentials *cred)
203 {
204 // determine what action we need to perform
205 State action = state();
206 if (!update)
207 action = (action == deleted) ? unchanged : inserted;
208
209 // the owner acl (pseudo) "entry" is a special case
210 if (isOwner()) {
211 switch (action) {
212 case unchanged:
213 debug("SecAccess", "ACL %p owner unchanged", this);
214 return;
215 case inserted: // means modify the initial owner
216 case modified:
217 {
218 debug("SecAccess", "ACL %p owner modified", this);
219 makeSubject();
220 assert(mSubjectForm);
221 AclOwnerPrototype proto(*mSubjectForm, mDelegate);
222 target.changeOwner(proto, cred);
223 return;
224 }
225 default:
226 assert(false);
227 return;
228 }
229 }
230
231 // simple cases
232 switch (action) {
233 case unchanged: // ignore
234 debug("SecAccess", "ACL %p handle 0x%lx unchanged", this, entryHandle());
235 return;
236 case deleted: // delete
237 debug("SecAccess", "ACL %p handle 0x%lx deleted", this, entryHandle());
238 target.deleteAcl(entryHandle(), cred);
239 return;
240 default:
241 break;
242 }
243
244 // build the byzantine data structures that CSSM loves so much
245 makeSubject();
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);
253 switch (action) {
254 case inserted: // insert
255 debug("SecAccess", "ACL %p inserted", this);
256 target.addAcl(input, cred);
257 break;
258 case modified: // update
259 debug("SecAccess", "ACL %p handle 0x%lx modified", this, entryHandle());
260 target.changeAcl(entryHandle(), input, cred);
261 break;
262 default:
263 assert(false);
264 }
265 }
266
267
268 //
269 // Parse an AclEntryPrototype (presumably from a CSSM "Get" ACL operation
270 // into internal form.
271 //
272 void ACL::parse(const TypedList &subject)
273 {
274 try {
275 switch (subject.type()) {
276 case CSSM_ACL_SUBJECT_TYPE_ANY:
277 // subsume an "any" as a standard form
278 mForm = allowAllForm;
279 return;
280 case CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT:
281 // pure keychain prompt - interpret as applist form with no apps
282 parsePrompt(subject);
283 mForm = appListForm;
284 return;
285 case CSSM_ACL_SUBJECT_TYPE_THRESHOLD:
286 {
287 // app-list format: THRESHOLD(1, n): sign(1), ..., sign(n), PROMPT
288 if (subject[1] != 1)
289 throw ParseError();
290 uint32 count = subject[2];
291
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
296 parsePrompt(end);
297
298 // check for leading ANY
299 const TypedList &first = subject[3];
300 if (first.type() == CSSM_ACL_SUBJECT_TYPE_ANY) {
301 mForm = allowAllForm;
302 return;
303 }
304
305 // parse other (SIGN) elements
306 for (uint32 n = 0; n < count - 1; n++)
307 mAppList.push_back(new TrustedApplication(subject[n + 3]));
308 }
309 mForm = appListForm;
310 return;
311 default:
312 mForm = customForm;
313 return;
314 }
315 } catch (const ParseError &) {
316 debug("SecAccess", "acl compile failed; marking custom");
317 mForm = customForm;
318 mAppList.clear();
319 }
320 }
321
322 void ACL::parsePrompt(const TypedList &subject)
323 {
324 assert(subject.length() == 3);
325 mPromptSelector = *subject[1].data().interpretedAs<CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR>();
326 mPromptDescription = subject[2].toString();
327 }
328
329
330 //
331 // Take this ACL and produce its meaning as a CSSM ACL subject in mSubjectForm
332 //
333 void ACL::makeSubject()
334 {
335 // release previous value, if any
336 chunkFree(mSubjectForm, allocator);
337
338 switch (form()) {
339 case allowAllForm:
340 if (mPromptDescription.empty()) {
341 // no description -> pure ANY
342 mSubjectForm = new(allocator) TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_ANY);
343 } else {
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);
353 }
354 return;
355 case appListForm: {
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++)
362 *mSubjectForm +=
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);
368 }
369 return;
370 case customForm:
371 assert(false); // @@@ not yet
372 default:
373 assert(false); // unexpected
374 }
375 }