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