]> git.saurik.com Git - apple/security.git/blob - libsecurity_keychain/lib/ACL.cpp
Security-55179.13.tar.gz
[apple/security.git] / libsecurity_keychain / lib / ACL.cpp
1 /*
2 * Copyright (c) 2002-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 //
25 // ACL.cpp
26 //
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>
36 #include <memory>
37
38
39 using namespace KeychainCore;
40 using namespace DataWalkers;
41
42
43 //
44 // The default form of a prompt selector
45 //
46 const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR ACL::defaultSelector = {
47 CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION, 0
48 };
49
50
51 //
52 // ACL static constants
53 //
54 const CSSM_ACL_HANDLE ACL::ownerHandle;
55
56
57 //
58 // Create an ACL object from the result of a CSSM ACL query
59 //
60 ACL::ACL(Access &acc, const AclEntryInfo &info, Allocator &alloc)
61 : allocator(alloc), access(acc), mState(unchanged), mSubjectForm(NULL), mMutex(Mutex::recursive)
62 {
63 // parse the subject
64 parse(info.proto().subject());
65
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();
71
72 // take CSSM entry handle from info layer
73 mCssmHandle = info.handle();
74 }
75
76 ACL::ACL(Access &acc, const AclOwnerPrototype &owner, Allocator &alloc)
77 : allocator(alloc), access(acc), mState(unchanged), mSubjectForm(NULL), mMutex(Mutex::recursive)
78 {
79 // parse subject
80 parse(owner.subject());
81
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();
85 mEntryTag[0] = '\0';
86
87 // use fixed (fake) entry handle
88 mCssmHandle = ownerHandle;
89 }
90
91
92 //
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.
97 //
98 ACL::ACL(Access &acc, Allocator &alloc)
99 : allocator(alloc), access(acc), mSubjectForm(NULL), mMutex(Mutex::recursive)
100 {
101 mState = inserted; // new
102 mForm = allowAllForm; // everybody
103 mAuthorizations.insert(CSSM_ACL_AUTHORIZATION_ANY); // anything
104 mDelegate = false;
105
106 //mPromptDescription stays empty
107 mPromptSelector = defaultSelector;
108
109 // randomize the CSSM handle
110 UniformRandomBlobs<DevRandomGenerator>().random(mCssmHandle);
111 }
112
113
114 //
115 // Create a new ACL in standard form.
116 // As created, it authorizes all activities.
117 //
118 ACL::ACL(Access &acc, string description, const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR &promptSelector,
119 Allocator &alloc)
120 : allocator(alloc), access(acc), mSubjectForm(NULL), mMutex(Mutex::recursive)
121 {
122 mState = inserted; // new
123 mForm = appListForm;
124 mAuthorizations.insert(CSSM_ACL_AUTHORIZATION_ANY); // anything
125 mDelegate = false;
126
127 mPromptDescription = description;
128 mPromptSelector = promptSelector;
129
130 // randomize the CSSM handle
131 UniformRandomBlobs<DevRandomGenerator>().random(mCssmHandle);
132 }
133
134
135 //
136 // Destroy an ACL
137 //
138 ACL::~ACL()
139 {
140 // release subject form (if any)
141 chunkFree(mSubjectForm, allocator);
142 }
143
144
145 //
146 // Does this ACL authorize a particular right?
147 //
148 bool ACL::authorizes(AclAuthorization right)
149 {
150 StLock<Mutex>_(mMutex);
151 return mAuthorizations.find(right) != mAuthorizations.end()
152 || mAuthorizations.find(CSSM_ACL_AUTHORIZATION_ANY) != mAuthorizations.end()
153 || mAuthorizations.empty();
154 }
155
156
157 //
158 // Add an application to the trusted-app list of this ACL.
159 // Will fail unless this is a standard "simple" form ACL.
160 //
161 void ACL::addApplication(TrustedApplication *app)
162 {
163 StLock<Mutex>_(mMutex);
164 switch (mForm) {
165 case appListForm: // simple...
166 mAppList.push_back(app);
167 modify();
168 break;
169 case allowAllForm: // hmm...
170 if (!mPromptDescription.empty()) {
171 // verbose "any" form (has description, "any" override)
172 mAppList.push_back(app);
173 modify();
174 break;
175 }
176 // pure "any" form without description. Cannot convert to appListForm
177 default:
178 MacOSError::throwMe(errSecACLNotSimple);
179 }
180 }
181
182
183 //
184 // Mark an ACL as modified.
185 //
186 void ACL::modify()
187 {
188 StLock<Mutex>_(mMutex);
189 if (mState == unchanged) {
190 secdebug("SecAccess", "ACL %p marked modified", this);
191 mState = modified;
192 }
193 }
194
195
196 //
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.
202 //
203 void ACL::remove()
204 {
205 StLock<Mutex>_(mMutex);
206 mAppList.clear();
207 mForm = invalidForm;
208 mState = deleted;
209 }
210
211
212 //
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.
215 //
216 void ACL::copyAclEntry(AclEntryPrototype &proto, Allocator &alloc)
217 {
218 StLock<Mutex>_(mMutex);
219 proto.clearPod(); // preset
220
221 // carefully copy the subject
222 makeSubject();
223 assert(mSubjectForm);
224 proto = AclEntryPrototype(*mSubjectForm, mDelegate); // shares subject
225 ChunkCopyWalker w(alloc);
226 walk(w, proto.subject()); // copy subject in-place
227
228 // the rest of a prototype
229 proto.tag(mEntryTag);
230 AuthorizationGroup tags(mAuthorizations, allocator);
231 proto.authorization() = tags;
232 }
233
234 void ACL::copyAclOwner(AclOwnerPrototype &proto, Allocator &alloc)
235 {
236 StLock<Mutex>_(mMutex);
237 proto.clearPod();
238
239 makeSubject();
240 assert(mSubjectForm);
241 proto = AclOwnerPrototype(*mSubjectForm, mDelegate); // shares subject
242 ChunkCopyWalker w(alloc);
243 walk(w, proto.subject()); // copy subject in-place
244 }
245
246
247 //
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).
253 //
254 void ACL::setAccess(AclBearer &target, bool update,
255 const AccessCredentials *cred)
256 {
257 StLock<Mutex>_(mMutex);
258 // determine what action we need to perform
259 State action = state();
260 if (!update)
261 action = (action == deleted) ? unchanged : inserted;
262
263 // the owner acl (pseudo) "entry" is a special case
264 if (isOwner()) {
265 switch (action) {
266 case unchanged:
267 secdebug("SecAccess", "ACL %p owner unchanged", this);
268 return;
269 case inserted: // means modify the initial owner
270 case modified:
271 {
272 secdebug("SecAccess", "ACL %p owner modified", this);
273 makeSubject();
274 assert(mSubjectForm);
275 AclOwnerPrototype proto(*mSubjectForm, mDelegate);
276 target.changeOwner(proto, cred);
277 return;
278 }
279 default:
280 assert(false);
281 return;
282 }
283 }
284
285 // simple cases
286 switch (action) {
287 case unchanged: // ignore
288 secdebug("SecAccess", "ACL %p handle 0x%lx unchanged", this, entryHandle());
289 return;
290 case deleted: // delete
291 secdebug("SecAccess", "ACL %p handle 0x%lx deleted", this, entryHandle());
292 target.deleteAcl(entryHandle(), cred);
293 return;
294 default:
295 break;
296 }
297
298 // build the byzantine data structures that CSSM loves so much
299 makeSubject();
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);
306 switch (action) {
307 case inserted: // insert
308 secdebug("SecAccess", "ACL %p inserted", this);
309 target.addAcl(input, cred);
310 break;
311 case modified: // update
312 secdebug("SecAccess", "ACL %p handle 0x%lx modified", this, entryHandle());
313 target.changeAcl(entryHandle(), input, cred);
314 break;
315 default:
316 assert(false);
317 }
318 }
319
320
321 //
322 // Parse an AclEntryPrototype (presumably from a CSSM "Get" ACL operation
323 // into internal form.
324 //
325 void ACL::parse(const TypedList &subject)
326 {
327 StLock<Mutex>_(mMutex);
328 try {
329 switch (subject.type()) {
330 case CSSM_ACL_SUBJECT_TYPE_ANY:
331 // subsume an "any" as a standard form
332 mForm = allowAllForm;
333 return;
334 case CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT:
335 // pure keychain prompt - interpret as applist form with no apps
336 parsePrompt(subject);
337 mForm = appListForm;
338 return;
339 case CSSM_ACL_SUBJECT_TYPE_THRESHOLD:
340 {
341 // app-list format: THRESHOLD(1, n): sign(1), ..., sign(n), PROMPT
342 if (subject[1] != 1)
343 throw ParseError();
344 uint32 count = subject[2];
345
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
350 parsePrompt(end);
351
352 // check for leading ANY
353 TypedList &first = subject[3];
354 if (first.type() == CSSM_ACL_SUBJECT_TYPE_ANY) {
355 mForm = allowAllForm;
356 return;
357 }
358
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())));
362 }
363 mForm = appListForm;
364 return;
365 default:
366 mForm = customForm;
367 mSubjectForm = chunkCopy(&subject);
368 return;
369 }
370 } catch (const ParseError &) {
371 secdebug("SecAccess", "acl compile failed; marking custom");
372 mForm = customForm;
373 mSubjectForm = chunkCopy(&subject);
374 mAppList.clear();
375 }
376 }
377
378 void ACL::parsePrompt(const TypedList &subject)
379 {
380 StLock<Mutex>_(mMutex);
381 assert(subject.length() == 3);
382 mPromptSelector =
383 *subject[1].data().interpretedAs<CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR>(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
384 mPromptDescription = subject[2].toString();
385 }
386
387
388 //
389 // Take this ACL and produce its meaning as a CSSM ACL subject in mSubjectForm
390 //
391 void ACL::makeSubject()
392 {
393 StLock<Mutex>_(mMutex);
394 switch (form()) {
395 case allowAllForm:
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);
400 } else {
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);
410 }
411 return;
412 case appListForm: {
413 // threshold(1 of n+1) of { app1, ..., appn, PROMPT }
414 chunkFree(mSubjectForm, allocator); // release previous
415 uint32 appCount = 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++)
420 *mSubjectForm +=
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);
426 }
427 return;
428 case customForm:
429 assert(mSubjectForm); // already set; keep it
430 return;
431 default:
432 assert(false); // unexpected
433 }
434 }