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