]> git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_utilities/cssmacl.cpp
cc700e22d3567c7f37d60a705e75d1d72b22429b
[apple/security.git] / cdsa / cdsa_utilities / cssmacl.cpp
1 /*
2 * Copyright (c) 2000-2001 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 //
20 // cssmacl - core ACL management interface
21 //
22 #ifdef __MWERKS__
23 #define _CPP_CSSMACL
24 #endif
25
26 #include <Security/cssmacl.h>
27 #include <Security/debugging.h>
28 #include <algorithm>
29 #include <cstdarg>
30
31 using namespace DataWalkers;
32
33
34 //
35 // The static map of available ACL subject makers.
36 // These are the kinds of ACL subjects we can deal with.
37 //
38 ModuleNexus<ObjectAcl::MakerMap> ObjectAcl::makers;
39
40
41 //
42 // Common (basic) features of AclSubjects
43 //
44 AclSubject::~AclSubject()
45 { }
46
47 AclValidationEnvironment::~AclValidationEnvironment()
48 { }
49
50 void AclSubject::exportBlob(Writer::Counter &, Writer::Counter &)
51 { }
52
53 void AclSubject::exportBlob(Writer &, Writer &)
54 { }
55
56 void AclSubject::importBlob(Reader &, Reader &)
57 { }
58
59 AclSubject::Maker::~Maker()
60 {
61 }
62
63 //
64 // A SimpleAclSubject accepts only a single type of sample, validates
65 // samples independently, and makes no use of certificates.
66 //
67 bool SimpleAclSubject::validate(const AclValidationContext &ctx) const
68 {
69 for (uint32 n = 0; n < ctx.count(); n++) {
70 const TypedList &sample = ctx[n];
71 if (!sample.isProper())
72 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
73 if (sample.type() == acceptingSamples && validate(ctx, sample))
74 return true; // matched this sample; validation successful
75 }
76 return false;
77 }
78
79
80 //
81 // Create an ObjectAcl
82 //
83 ObjectAcl::ObjectAcl(CssmAllocator &alloc) : allocator(alloc), nextHandle(1)
84 {
85 }
86
87 ObjectAcl::ObjectAcl(const AclEntryPrototype &proto, CssmAllocator &alloc)
88 : allocator(alloc), nextHandle(1)
89 {
90 cssmSetInitial(proto);
91 }
92
93 ObjectAcl::~ObjectAcl()
94 { }
95
96
97 //
98 // Set an "initial ACL" from a CSSM-style initial ACL argument.
99 // This will replace the owner, as well as replace the entire ACL
100 // with a single-item slot, as per CSSM specification.
101 //
102 void ObjectAcl::cssmSetInitial(const AclEntryPrototype &proto)
103 {
104 owner = OwnerEntry(proto);
105 entries.insert(EntryMap::value_type("", proto))->second.handle = nextHandle++;
106 }
107
108 void ObjectAcl::cssmSetInitial(const AclSubjectPointer &subject)
109 {
110 owner = OwnerEntry(subject);
111 entries.insert(EntryMap::value_type("", subject))->second.handle = nextHandle++;
112 }
113
114 ObjectAcl::Entry::~Entry()
115 {
116 }
117
118 AclValidationContext::~AclValidationContext()
119 {
120 }
121
122 //
123 // ObjectAcl::validate validates an access authorization claim.
124 // Returns normally if 'auth' is granted to the bearer of 'cred'.
125 // Otherwise, throws a suitable (ACL-related) CssmError exception.
126 // @@@ Should it return a reference to the Entry that granted access?
127 //
128 class BaseValidationContext : public AclValidationContext {
129 public:
130 BaseValidationContext(const AccessCredentials *cred,
131 AclAuthorization auth, AclValidationEnvironment *env)
132 : AclValidationContext(cred, auth, env) { }
133
134 uint32 count() const { return mCred ? mCred->samples().length() : 0; }
135 const TypedList &sample(uint32 n) const
136 { assert(n < count()); return mCred->samples()[n]; }
137 };
138
139 void ObjectAcl::validate(AclAuthorization auth, const AccessCredentials *cred,
140 AclValidationEnvironment *env) const
141 {
142 //@@@ should pre-screen based on requested auth, maybe?
143 BaseValidationContext ctx(cred, auth, env);
144
145 #if defined(ACL_OMNIPOTENT_OWNER)
146 // try owner (owner can do anything)
147 if (owner.validate(ctx))
148 return;
149 #endif ACL_OMNIPOTENT_OWNER
150
151 // try applicable ACLs
152 pair<ConstIterator, ConstIterator> range;
153 if (getRange(cred->EntryTag, range) == 0) // no such tag
154 CssmError::throwMe(CSSM_ERRCODE_ACL_ENTRY_TAG_NOT_FOUND);
155 // try entries in turn
156 for (ConstIterator it = range.first; it != range.second; it++) {
157 const AclEntry &slot = it->second;
158 if (slot.authorizes(ctx.authorization()) && slot.validate(ctx))
159 return; // passed
160 }
161 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED); //@@@ imprecise
162 }
163
164 void ObjectAcl::validateOwner(AclAuthorization authorizationHint,
165 const AccessCredentials *cred, AclValidationEnvironment *env) const
166 {
167 BaseValidationContext ctx(cred, authorizationHint, env);
168 if (owner.validate(ctx))
169 return;
170 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
171 }
172
173
174 //
175 // Export an ObjectAcl to two memory blobs: public and private data separated.
176 // This is a standard two-pass size+copy operation.
177 //
178 void ObjectAcl::exportBlob(CssmData &publicBlob, CssmData &privateBlob)
179 {
180 Writer::Counter pubSize, privSize;
181 uint32 entryCount = entries.size();
182 owner.exportBlob(pubSize, privSize);
183 pubSize(entryCount);
184 for (Iterator it = begin(); it != end(); it++)
185 it->second.exportBlob(pubSize, privSize);
186 publicBlob = CssmData(allocator.malloc(pubSize), pubSize);
187 privateBlob = CssmData(allocator.malloc(privSize), privSize);
188 Writer pubWriter(publicBlob), privWriter(privateBlob);
189 owner.exportBlob(pubWriter, privWriter);
190 pubWriter(entryCount);
191 for (Iterator it = begin(); it != end(); it++)
192 it->second.exportBlob(pubWriter, privWriter);
193 }
194
195
196 //
197 // Import an ObjectAcl's contents from two memory blobs representing public and
198 // private contents, respectively. These blobs must have been generated by the
199 // export method.
200 // Prior contents (if any) are deleted and replaced.
201 //
202 void ObjectAcl::importBlob(const void *publicBlob, const void *privateBlob)
203 {
204 Reader pubReader(publicBlob), privReader(privateBlob);
205 owner.importBlob(pubReader, privReader);
206 uint32 entryCount; pubReader(entryCount);
207 entries.erase(begin(), end());
208 for (uint32 n = 0; n < entryCount; n++) {
209 AclEntry newEntry;
210 newEntry.importBlob(pubReader, privReader);
211 entries.insert(EntryMap::value_type(newEntry.tag, newEntry))->second.handle = nextHandle++;
212 }
213 IFDUMPING("acl", debugDump("imported"));
214 }
215
216
217 //
218 // ACL utility methods
219 //
220 unsigned int ObjectAcl::getRange(const char *tag, pair<ConstIterator, ConstIterator> &range) const
221 {
222 if (tag) {
223 range = entries.equal_range(tag);
224 uint32 count = entries.count(tag);
225 if (count == 0)
226 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_ENTRY_TAG);
227 return count;
228 } else {
229 range.first = entries.begin();
230 range.second = entries.end();
231 return entries.size();
232 }
233 }
234
235 ObjectAcl::Iterator ObjectAcl::findEntryHandle(CSSM_ACL_HANDLE handle)
236 {
237 for (Iterator it = entries.begin(); it != entries.end(); it++)
238 if (it->second.handle == handle)
239 return it;
240 CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE); //%%% imprecise error code
241 }
242
243
244 //
245 // CSSM style ACL access and modification functions.
246 //
247 void ObjectAcl::cssmGetAcl(const char *tag, uint32 &count, AclEntryInfo * &acls)
248 {
249 pair<ConstIterator, ConstIterator> range;
250 count = getRange(tag, range);
251 acls = allocator.alloc<AclEntryInfo>(count);
252 uint32 n = 0;
253 for (ConstIterator it = range.first; it != range.second; it++, n++) {
254 acls[n].EntryHandle = it->second.handle;
255 it->second.toEntryInfo(acls[n].EntryPublicInfo, allocator);
256 }
257 count = n;
258 }
259
260 void ObjectAcl::cssmChangeAcl(const AclEdit &edit,
261 const AccessCredentials *cred, AclValidationEnvironment *env)
262 {
263 IFDUMPING("acl", debugDump("acl-change-from"));
264
265 // validate access credentials
266 validateOwner(CSSM_ACL_AUTHORIZATION_CHANGE_ACL, cred, env);
267
268 // what is Thy wish, effendi?
269 switch (edit.EditMode) {
270 case CSSM_ACL_EDIT_MODE_ADD: {
271 AclEntry ent(Required(edit.newEntry()).proto()); //@@@ bypassing callback
272 ent.handle = nextHandle++;
273 entries.insert(EntryMap::value_type(edit.NewEntry->Prototype.EntryTag, ent));
274 }
275 break;
276 case CSSM_ACL_EDIT_MODE_REPLACE: {
277 // keep the handle, and try for some modicum of atomicity
278 Iterator it = findEntryHandle(edit.OldEntryHandle);
279 AclEntry ent(Required(edit.newEntry()).proto());
280 ent.handle = edit.OldEntryHandle;
281 entries.insert(EntryMap::value_type(edit.NewEntry->Prototype.EntryTag, ent));
282 entries.erase(it);
283 }
284 break;
285 case CSSM_ACL_EDIT_MODE_DELETE:
286 entries.erase(findEntryHandle(edit.OldEntryHandle));
287 break;
288 default:
289 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_EDIT_MODE);
290 }
291
292 IFDUMPING("acl", debugDump("owner-change-to"));
293 }
294
295 void ObjectAcl::cssmGetOwner(AclOwnerPrototype &outOwner)
296 {
297 outOwner.TypedSubject = owner.subject->toList(allocator);
298 outOwner.Delegate = owner.delegate;
299 }
300
301 void ObjectAcl::cssmChangeOwner(const AclOwnerPrototype &newOwner,
302 const AccessCredentials *cred, AclValidationEnvironment *env)
303 {
304 IFDUMPING("acl", debugDump("owner-change-from"));
305
306 // only the owner entry can match
307 validateOwner(CSSM_ACL_AUTHORIZATION_CHANGE_OWNER, cred, env);
308
309 // okay, replace it
310 owner = newOwner;
311
312 IFDUMPING("acl", debugDump("owner-change-to"));
313 }
314
315
316 //
317 // Common features of ACL entries/owners
318 //
319 void ObjectAcl::Entry::init(const AclSubjectPointer &subject, bool delegate)
320 {
321 this->subject = subject;
322 this->delegate = delegate;
323 }
324
325 void ObjectAcl::Entry::importBlob(Reader &pub, Reader &priv)
326 {
327 // delegate is trivial
328 pub(delegate);
329
330 // now reconstruct the (polymorphic) subject
331 CSSM_ACL_SUBJECT_TYPE subjectType; pub(subjectType);
332 subject = make(subjectType, pub, priv);
333 }
334
335
336 //
337 // An OwnerEntry is a restricted EntryPrototype for use as the ACL owner.
338 //
339 bool ObjectAcl::OwnerEntry::authorizes(AclAuthorization) const
340 {
341 return true; // owner can do anything
342 }
343
344 bool ObjectAcl::OwnerEntry::validate(const AclValidationContext &ctx) const
345 {
346 return subject->validate(ctx); // simple subject match - no strings attached
347 }
348
349
350 //
351 // An AclEntry has some extra goodies
352 //
353 ObjectAcl::AclEntry::AclEntry(const AclEntryPrototype &proto) : Entry(proto)
354 {
355 tag = proto.tag();
356 if (proto.authorization().contains(CSSM_ACL_AUTHORIZATION_ANY))
357 authorizesAnything = true; // anything else wouldn't add anything
358 else if (proto.authorization().empty())
359 authorizesAnything = true; // not in standard, but common sense
360 else {
361 authorizesAnything = false;
362 authorizations = proto.authorization();
363 }
364 //@@@ not setting time range
365 // handle = not set here. Set by caller when the AclEntry is created.
366 }
367
368 ObjectAcl::AclEntry::AclEntry(const AclSubjectPointer &subject) : Entry(subject)
369 {
370 authorizesAnything = true; // by default, everything
371 //@@@ not setting time range
372 }
373
374 void ObjectAcl::AclEntry::toEntryInfo(CSSM_ACL_ENTRY_PROTOTYPE &info, CssmAllocator &alloc) const
375 {
376 info.TypedSubject = subject->toList(alloc);
377 info.Delegate = delegate;
378 info.Authorization = AuthorizationGroup(authorizations, alloc);
379 //@@@ info.TimeRange =
380 assert(tag.length() <= CSSM_MODULE_STRING_SIZE);
381 memcpy(info.EntryTag, tag.c_str(), tag.length() + 1);
382 }
383
384 bool ObjectAcl::AclEntry::authorizes(AclAuthorization auth) const
385 {
386 return authorizesAnything || authorizations.find(auth) != authorizations.end();
387 }
388
389 bool ObjectAcl::AclEntry::validate(const AclValidationContext &ctx) const
390 {
391 //@@@ not checking time ranges
392 return subject->validate(ctx);
393 }
394
395 void ObjectAcl::AclEntry::importBlob(Reader &pub, Reader &priv)
396 {
397 Entry::importBlob(pub, priv);
398 const char *s; pub(s); tag = s;
399 pub(authorizesAnything);
400 authorizations.erase(authorizations.begin(), authorizations.end());
401 if (!authorizesAnything) {
402 uint32 count; pub(count);
403 for (uint32 n = 0; n < count; n++) {
404 AclAuthorization auth; pub(auth);
405 authorizations.insert(auth);
406 }
407 }
408 //@@@ import time range
409 }
410
411
412 //
413 // Subject factory and makers
414 //
415 AclSubject::Maker::Maker(CSSM_ACL_SUBJECT_TYPE type) : myType(type)
416 {
417 ObjectAcl::makers()[type] = this;
418 }
419
420 AclSubject *ObjectAcl::make(const TypedList &list)
421 {
422 if (!list.isProper())
423 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
424 return makerFor(list.type()).make(list);
425 }
426
427 AclSubject *ObjectAcl::make(CSSM_ACL_SUBJECT_TYPE type, Reader &pub, Reader &priv)
428 {
429 return makerFor(type).make(pub, priv);
430 }
431
432 AclSubject::Maker &ObjectAcl::makerFor(CSSM_ACL_SUBJECT_TYPE type)
433 {
434 AclSubject::Maker *maker = makers()[type];
435 if (maker == NULL)
436 CssmError::throwMe(CSSM_ERRCODE_ACL_SUBJECT_TYPE_NOT_SUPPORTED);
437 return *maker;
438 }
439
440
441 //
442 // Parsing helper for subject makers.
443 // Note that count/array exclude the first element of list, which is the subject type wordid.
444 //
445 void AclSubject::Maker::crack(const CssmList &list, uint32 count, ListElement **array, ...)
446 {
447 if (count != list.length() - 1)
448 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
449 if (count > 0) {
450 va_list args;
451 va_start(args, array);
452 ListElement *elem = list.first()->next();
453 for (uint32 n = 0; n < count; n++, elem = elem->next()) {
454 CSSM_LIST_ELEMENT_TYPE expectedType = va_arg(args, CSSM_LIST_ELEMENT_TYPE);
455 if (elem->type() != expectedType)
456 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
457 array[n] = elem;
458 }
459 va_end(args);
460 }
461 }
462
463 CSSM_WORDID_TYPE AclSubject::Maker::getWord(const ListElement &elem,
464 int min = 0, int max = INT_MAX)
465 {
466 if (elem.type() != CSSM_LIST_ELEMENT_WORDID)
467 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
468 CSSM_WORDID_TYPE value = elem;
469 if (value < min || value > max)
470 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
471 return value;
472 }
473
474
475 //
476 // Debug dumping support
477 //
478 #if defined(DEBUGDUMP)
479
480 void ObjectAcl::debugDump(const char *what) const
481 {
482 if (!what)
483 what = "Dump";
484 Debug::dump("%p ACL %s: %d entries\n", this, what, int(entries.size()));
485 Debug::dump(" OWNER ["); owner.debugDump(); Debug::dump("]\n");
486 for (ConstIterator it = begin(); it != end(); it++) {
487 const AclEntry &ent = it->second;
488 Debug::dump(" (%ld:%s) [", ent.handle, ent.tag.c_str());
489 ent.debugDump();
490 Debug::dump("]\n");
491 }
492 Debug::dump("%p ACL END\n", this);
493 }
494
495 void ObjectAcl::Entry::debugDump() const
496 {
497 subject->debugDump();
498 if (delegate)
499 Debug::dump(" DELEGATE");
500 }
501
502 void ObjectAcl::AclEntry::debugDump() const
503 {
504 Entry::debugDump();
505 if (authorizesAnything) {
506 Debug::dump(" auth(ALL)");
507 } else {
508 Debug::dump(" auth(");
509 for (AclAuthorizationSet::iterator it = authorizations.begin();
510 it != authorizations.end(); it++)
511 Debug::dump(" %ld", *it);
512 Debug::dump(")");
513 }
514 }
515
516 void AclSubject::debugDump() const
517 {
518 switch (type()) {
519 case CSSM_ACL_SUBJECT_TYPE_ANY:
520 Debug::dump("ANY");
521 break;
522 default:
523 Debug::dump("subject type=%d", int(type()));
524 break;
525 }
526 }
527
528 #endif //DEBUGDUMP