2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
20 // cssmacl - core ACL management interface
22 #include <Security/cssmacl.h>
23 #include <Security/debugging.h>
28 using namespace DataWalkers
;
32 // The static map of available ACL subject makers.
33 // These are the kinds of ACL subjects we can deal with.
35 ModuleNexus
<ObjectAcl::MakerMap
> ObjectAcl::makers
;
39 // Common (basic) features of AclSubjects
41 AclSubject::~AclSubject()
44 AclValidationEnvironment::~AclValidationEnvironment()
47 void AclSubject::exportBlob(Writer::Counter
&, Writer::Counter
&)
50 void AclSubject::exportBlob(Writer
&, Writer
&)
53 void AclSubject::importBlob(Reader
&, Reader
&)
56 AclSubject::Maker::~Maker()
61 // A SimpleAclSubject accepts only a single type of sample, validates
62 // samples independently, and makes no use of certificates.
64 bool SimpleAclSubject::validate(const AclValidationContext
&ctx
) const
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
78 // Create an ObjectAcl
80 ObjectAcl::ObjectAcl(CssmAllocator
&alloc
) : allocator(alloc
), nextHandle(1)
84 ObjectAcl::ObjectAcl(const AclEntryPrototype
&proto
, CssmAllocator
&alloc
)
85 : allocator(alloc
), nextHandle(1)
87 cssmSetInitial(proto
);
90 ObjectAcl::~ObjectAcl()
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.
99 void ObjectAcl::cssmSetInitial(const AclEntryPrototype
&proto
)
101 owner
= OwnerEntry(proto
);
102 entries
.insert(EntryMap::value_type(proto
.tag(), proto
))->second
.handle
= nextHandle
++;
103 IFDUMPING("acl", debugDump("create/proto"));
106 void ObjectAcl::cssmSetInitial(const AclSubjectPointer
&subject
)
108 owner
= OwnerEntry(subject
);
109 entries
.insert(EntryMap::value_type("", subject
))->second
.handle
= nextHandle
++;
110 IFDUMPING("acl", debugDump("create/subject"));
113 ObjectAcl::Entry::~Entry()
117 AclValidationContext::~AclValidationContext()
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?
127 class BaseValidationContext
: public AclValidationContext
{
129 BaseValidationContext(const AccessCredentials
*cred
,
130 AclAuthorization auth
, AclValidationEnvironment
*env
)
131 : AclValidationContext(cred
, auth
, env
) { }
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
]; }
138 void ObjectAcl::validate(AclAuthorization auth
, const AccessCredentials
*cred
,
139 AclValidationEnvironment
*env
)
141 // make sure we are ready to go
144 //@@@ should pre-screen based on requested auth, maybe?
145 BaseValidationContext
ctx(cred
, auth
, env
);
147 #if defined(ACL_OMNIPOTENT_OWNER)
148 // try owner (owner can do anything)
149 if (owner
.validate(ctx
))
151 #endif //ACL_OMNIPOTENT_OWNER
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
))
163 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
); //@@@ imprecise
166 void ObjectAcl::validateOwner(AclAuthorization authorizationHint
,
167 const AccessCredentials
*cred
, AclValidationEnvironment
*env
)
170 BaseValidationContext
ctx(cred
, authorizationHint
, env
);
171 if (owner
.validate(ctx
))
173 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
178 // Export an ObjectAcl to two memory blobs: public and private data separated.
179 // This is a standard two-pass size+copy operation.
181 void ObjectAcl::exportBlob(CssmData
&publicBlob
, CssmData
&privateBlob
)
183 Writer::Counter pubSize
, privSize
;
184 Endian
<uint32
> entryCount
= entries
.size();
185 owner
.exportBlob(pubSize
, privSize
);
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"));
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
204 // Prior contents (if any) are deleted and replaced.
206 void ObjectAcl::importBlob(const void *publicBlob
, const void *privateBlob
)
208 Reader
pubReader(publicBlob
), privReader(privateBlob
);
209 owner
.importBlob(pubReader
, privReader
);
210 Endian
<uint32
> entryCountIn
; pubReader(entryCountIn
);
211 uint32 entryCount
= entryCountIn
;
213 entries
.erase(begin(), end());
214 for (uint32 n
= 0; n
< entryCount
; n
++) {
216 newEntry
.importBlob(pubReader
, privReader
);
217 entries
.insert(EntryMap::value_type(newEntry
.tag
, newEntry
))->second
.handle
= nextHandle
++;
219 IFDUMPING("acl", debugDump("imported"));
224 // Import/export helpers for subjects.
225 // This is exported to (subject implementation) callers to maintain consistency
226 // in binary format handling.
228 AclSubject
*ObjectAcl::importSubject(Reader
&pub
, Reader
&priv
)
230 Endian
<uint32
> typeAndVersion
; pub(typeAndVersion
);
231 return make(typeAndVersion
, pub
, priv
);
236 // Setup/update hooks
238 void ObjectAcl::instantiateAcl()
240 // nothing by default
243 void ObjectAcl::changedAcl()
245 // nothing by default
250 // ACL utility methods
252 unsigned int ObjectAcl::getRange(const char *tag
, pair
<ConstIterator
, ConstIterator
> &range
) const
254 if (tag
&& tag
[0]) { // tag restriction in effect
255 range
= entries
.equal_range(tag
);
256 uint32 count
= entries
.count(tag
);
258 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_ENTRY_TAG
);
260 } else { // try all tags
261 range
.first
= entries
.begin();
262 range
.second
= entries
.end();
263 return entries
.size();
267 ObjectAcl::Iterator
ObjectAcl::findEntryHandle(CSSM_ACL_HANDLE handle
)
269 for (Iterator it
= entries
.begin(); it
!= entries
.end(); it
++)
270 if (it
->second
.handle
== handle
)
272 CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE
); //%%% imprecise error code
277 // CSSM style ACL access and modification functions.
279 void ObjectAcl::cssmGetAcl(const char *tag
, uint32
&count
, AclEntryInfo
* &acls
)
282 pair
<ConstIterator
, ConstIterator
> range
;
283 count
= getRange(tag
, range
);
284 acls
= allocator
.alloc
<AclEntryInfo
>(count
);
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
);
293 void ObjectAcl::cssmChangeAcl(const AclEdit
&edit
,
294 const AccessCredentials
*cred
, AclValidationEnvironment
*env
)
296 IFDUMPING("acl", debugDump("acl-change-from"));
298 // make sure we're ready to go
301 // validate access credentials
302 validateOwner(CSSM_ACL_AUTHORIZATION_CHANGE_ACL
, cred
, env
);
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
));
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
));
321 case CSSM_ACL_EDIT_MODE_DELETE
:
322 entries
.erase(findEntryHandle(edit
.OldEntryHandle
));
325 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_EDIT_MODE
);
331 IFDUMPING("acl", debugDump("acl-change-to"));
334 void ObjectAcl::cssmGetOwner(AclOwnerPrototype
&outOwner
)
337 outOwner
.TypedSubject
= owner
.subject
->toList(allocator
);
338 outOwner
.Delegate
= owner
.delegate
;
341 void ObjectAcl::cssmChangeOwner(const AclOwnerPrototype
&newOwner
,
342 const AccessCredentials
*cred
, AclValidationEnvironment
*env
)
344 IFDUMPING("acl", debugDump("owner-change-from"));
348 // only the owner entry can match
349 validateOwner(CSSM_ACL_AUTHORIZATION_CHANGE_OWNER
, cred
, env
);
356 IFDUMPING("acl", debugDump("owner-change-to"));
361 // Common features of ACL entries/owners
363 void ObjectAcl::Entry::init(const AclSubjectPointer
&subject
, bool delegate
)
365 this->subject
= subject
;
366 this->delegate
= delegate
;
369 void ObjectAcl::Entry::importBlob(Reader
&pub
, Reader
&priv
)
372 pub(del
); // read del from the public blob
374 delegate
= del
; // 4 bytes delegate flag
375 subject
= importSubject(pub
, priv
);
380 // An OwnerEntry is a restricted EntryPrototype for use as the ACL owner.
382 bool ObjectAcl::OwnerEntry::authorizes(AclAuthorization
) const
384 return true; // owner can do anything
387 bool ObjectAcl::OwnerEntry::validate(const AclValidationContext
&ctx
) const
389 return subject
->validate(ctx
); // simple subject match - no strings attached
394 // An AclEntry has some extra goodies
396 ObjectAcl::AclEntry::AclEntry(const AclEntryPrototype
&proto
) : Entry(proto
)
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
404 authorizesAnything
= false;
405 authorizations
= proto
.authorization();
407 //@@@ not setting time range
408 // handle = not set here. Set by caller when the AclEntry is created.
411 ObjectAcl::AclEntry::AclEntry(const AclSubjectPointer
&subject
) : Entry(subject
)
413 authorizesAnything
= true; // by default, everything
414 //@@@ not setting time range
417 void ObjectAcl::AclEntry::toEntryInfo(CSSM_ACL_ENTRY_PROTOTYPE
&info
, CssmAllocator
&alloc
) const
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);
427 bool ObjectAcl::AclEntry::authorizes(AclAuthorization auth
) const
429 return authorizesAnything
|| authorizations
.find(auth
) != authorizations
.end();
432 bool ObjectAcl::AclEntry::validate(const AclValidationContext
&ctx
) const
434 //@@@ not checking time ranges
435 return subject
->validate(ctx
);
438 void ObjectAcl::AclEntry::importBlob(Reader
&pub
, Reader
&priv
)
440 Entry::importBlob(pub
, priv
);
441 const char *s
; pub(s
); tag
= s
;
443 // authorizesAnything is on disk as a 4-byte flag
444 Endian
<uint32
> tmpAuthorizesAnything
;
445 pub(tmpAuthorizesAnything
);
446 authorizesAnything
= tmpAuthorizesAnything
;
448 authorizations
.erase(authorizations
.begin(), authorizations
.end());
449 if (!authorizesAnything
) {
450 Endian
<uint32
> countIn
; pub(countIn
);
451 uint32 count
= countIn
;
453 for (uint32 n
= 0; n
< count
; n
++) {
454 Endian
<AclAuthorization
> auth
; pub(auth
);
455 authorizations
.insert(auth
);
458 //@@@ import time range
463 // Subject factory and makers
465 AclSubject::Maker::Maker(CSSM_ACL_SUBJECT_TYPE type
) : myType(type
)
467 ObjectAcl::makers()[type
] = this;
470 AclSubject
*ObjectAcl::make(const TypedList
&list
)
472 if (!list
.isProper())
473 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE
);
474 return makerFor(list
.type()).make(list
);
477 AclSubject
*ObjectAcl::make(uint32 typeAndVersion
, Reader
&pub
, Reader
&priv
)
479 // this type is encoded as (version << 24) | type
480 return makerFor(typeAndVersion
& ~AclSubject::versionMask
).make(typeAndVersion
>> AclSubject::versionShift
, pub
, priv
);
483 AclSubject::Maker
&ObjectAcl::makerFor(CSSM_ACL_SUBJECT_TYPE type
)
485 AclSubject::Maker
*maker
= makers()[type
];
487 CssmError::throwMe(CSSM_ERRCODE_ACL_SUBJECT_TYPE_NOT_SUPPORTED
);
493 // Parsing helper for subject makers.
494 // Note that count/array exclude the first element of list, which is the subject type wordid.
496 void AclSubject::Maker::crack(const CssmList
&list
, uint32 count
, ListElement
**array
, ...)
498 if (count
!= list
.length() - 1)
499 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE
);
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
);
514 CSSM_WORDID_TYPE
AclSubject::Maker::getWord(const ListElement
&elem
,
515 int min
/*= 0*/, int max
/*= INT_MAX*/)
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
);
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.
532 void ObjectAcl::debugDump(const char *what
) const
534 #if defined(DEBUGDUMP)
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());
545 Debug::dump("%p ACL END\n", this);
549 void AclSubject::debugDump() const
551 #if defined(DEBUGDUMP)
553 case CSSM_ACL_SUBJECT_TYPE_ANY
:
557 Debug::dump("subject type=%d", int(type()));
563 #if defined(DEBUGDUMP)
565 void ObjectAcl::Entry::debugDump() const
567 if (AclSubject::Version v
= subject
->version())
568 Debug::dump("V=%d ", v
);
569 subject
->debugDump();
571 Debug::dump(" DELEGATE");
574 void ObjectAcl::AclEntry::debugDump() const
577 if (authorizesAnything
) {
578 Debug::dump(" auth(ALL)");
580 Debug::dump(" auth(");
581 for (AclAuthorizationSet::iterator it
= authorizations
.begin();
582 it
!= authorizations
.end(); it
++)
583 Debug::dump(" %ld", *it
);