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