X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_keychain/lib/Access.cpp?ds=sidebyside diff --git a/Security/libsecurity_keychain/lib/Access.cpp b/Security/libsecurity_keychain/lib/Access.cpp new file mode 100644 index 00000000..d510cd98 --- /dev/null +++ b/Security/libsecurity_keychain/lib/Access.cpp @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2002-2004,2011-2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// Access.cpp +// +#include +#include +#include "SecBridge.h" +#include +#include +#include +#include +#include +using namespace KeychainCore; +using namespace CssmClient; + + +// +// Access static constants +// +const CSSM_ACL_HANDLE Access::ownerHandle; + + +// +// Create a completely open Access (anyone can do anything) +// Note that this means anyone can *change* the ACL at will, too. +// These ACL entries contain no descriptor names. +// +Access::Access() : mMutex(Mutex::recursive) +{ + SecPointer owner = new ACL(*this); + owner->setAuthorization(CSSM_ACL_AUTHORIZATION_CHANGE_ACL); + addOwner(owner); + + SecPointer any = new ACL(*this); + add(any); +} + + +// +// Create a default Access object. +// This construct an Access with "default form", whatever that happens to be +// in this release. +// +Access::Access(const string &descriptor, const ACL::ApplicationList &trusted) : mMutex(Mutex::recursive) +{ + makeStandard(descriptor, trusted); +} + +Access::Access(const string &descriptor) : mMutex(Mutex::recursive) +{ + ACL::ApplicationList trusted; + trusted.push_back(new TrustedApplication); + makeStandard(descriptor, trusted); +} + +Access::Access(const string &descriptor, const ACL::ApplicationList &trusted, + const AclAuthorizationSet &limitedRights, const AclAuthorizationSet &freeRights) : mMutex(Mutex::recursive) +{ + makeStandard(descriptor, trusted, limitedRights, freeRights); +} + +void Access::makeStandard(const string &descriptor, const ACL::ApplicationList &trusted, + const AclAuthorizationSet &limitedRights, const AclAuthorizationSet &freeRights) +{ + StLock_(mMutex); + + // owner "entry" + SecPointer owner = new ACL(*this, descriptor, ACL::defaultSelector); + owner->setAuthorization(CSSM_ACL_AUTHORIZATION_CHANGE_ACL); + addOwner(owner); + + // unlimited entry + SecPointer unlimited = new ACL(*this, descriptor, ACL::defaultSelector); + if (freeRights.empty()) { + unlimited->authorizations().clear(); + unlimited->authorizations().insert(CSSM_ACL_AUTHORIZATION_ENCRYPT); + } else + unlimited->authorizations() = freeRights; + unlimited->form(ACL::allowAllForm); + add(unlimited); + + // limited entry + SecPointer limited = new ACL(*this, descriptor, ACL::defaultSelector); + if (limitedRights.empty()) { + limited->authorizations().clear(); + limited->authorizations().insert(CSSM_ACL_AUTHORIZATION_DECRYPT); + limited->authorizations().insert(CSSM_ACL_AUTHORIZATION_SIGN); + limited->authorizations().insert(CSSM_ACL_AUTHORIZATION_MAC); + limited->authorizations().insert(CSSM_ACL_AUTHORIZATION_DERIVE); + limited->authorizations().insert(CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR); + limited->authorizations().insert(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED); + } else + limited->authorizations() = limitedRights; + limited->applications() = trusted; + add(limited); +} + + +// +// Create an Access object whose initial value is taken +// from a CSSM ACL bearing object. +// +Access::Access(AclBearer &source) : mMutex(Mutex::recursive) +{ + // retrieve and set + AutoAclOwnerPrototype owner; + source.getOwner(owner); + AutoAclEntryInfoList acls; + source.getAcl(acls); + compile(*owner, acls.count(), acls.entries()); +} + + +// +// Create an Access object from CSSM-layer access controls +// +Access::Access(const CSSM_ACL_OWNER_PROTOTYPE &owner, + uint32 aclCount, const CSSM_ACL_ENTRY_INFO *acls) : mMutex(Mutex::recursive) +{ + compile(owner, aclCount, acls); +} + + +Access::~Access() +{ +} + + +// Convert a SecPointer to a SecACLRef. +static SecACLRef +convert(const SecPointer &acl) +{ + return *acl; +} + +// +// Return all ACL components in a newly-made CFArray. +// +CFArrayRef Access::copySecACLs() const +{ + return makeCFArray(convert, mAcls); +} + +CFArrayRef Access::copySecACLs(CSSM_ACL_AUTHORIZATION_TAG action) const +{ + list choices; + for (Map::const_iterator it = mAcls.begin(); it != mAcls.end(); it++) + if (it->second->authorizes(action)) + choices.push_back(it->second); + return choices.empty() ? NULL : makeCFArray(convert, choices); +} + + +// +// Enter the complete access configuration into a AclBearer. +// If update, skip any part marked unchanged. (If not update, skip +// any part marked deleted.) +// +void Access::setAccess(AclBearer &target, bool update /* = false */) +{ + StLock_(mMutex); + AclFactory factory; + editAccess(target, update, factory.promptCred()); +} + +void Access::setAccess(AclBearer &target, Maker &maker) +{ + StLock_(mMutex); + if (maker.makerType() == Maker::kStandardMakerType) + { + // remove initial-setup ACL + target.deleteAcl(Maker::creationEntryTag, maker.cred()); + + // insert our own ACL entries + editAccess(target, false, maker.cred()); + } +} + +void Access::editAccess(AclBearer &target, bool update, const AccessCredentials *cred) +{ + StLock_(mMutex); + assert(mAcls[ownerHandle]); // have owner + + // apply all non-owner ACLs first + for (Map::iterator it = mAcls.begin(); it != mAcls.end(); it++) + if (!it->second->isOwner()) + it->second->setAccess(target, update, cred); + + // finally, apply owner + mAcls[ownerHandle]->setAccess(target, update, cred); +} + + +// +// A convenience function to add one application to a standard ("simple") form +// ACL entry. This will only work if +// -- there is exactly one ACL entry authorizing the right +// -- that entry is in simple form +// +void Access::addApplicationToRight(AclAuthorization right, TrustedApplication *app) +{ + StLock_(mMutex); + vector acls; + findAclsForRight(right, acls); + if (acls.size() != 1) + MacOSError::throwMe(errSecACLNotSimple); // let's not guess here... + (*acls.begin())->addApplication(app); +} + + +// +// Yield new (copied) CSSM level owner and acls values, presumably +// for use at CSSM layer operations. +// Caller is responsible for releasing the beasties when done. +// +void Access::copyOwnerAndAcl(CSSM_ACL_OWNER_PROTOTYPE * &ownerResult, + uint32 &aclCount, CSSM_ACL_ENTRY_INFO * &aclsResult) +{ + StLock_(mMutex); + Allocator& alloc = Allocator::standard(); + unsigned long count = mAcls.size() - 1; // one will be owner, others are acls + AclOwnerPrototype owner; + CssmAutoPtr acls = new(alloc) AclEntryInfo[count]; + AclEntryInfo *aclp = acls; // -> next unfilled acl element + for (Map::const_iterator it = mAcls.begin(); it != mAcls.end(); it++) { + SecPointer acl = it->second; + if (acl->isOwner()) { + acl->copyAclOwner(owner, alloc); + } else { + aclp->handle() = acl->entryHandle(); + acl->copyAclEntry(*aclp, alloc); + ++aclp; + } + } + assert((aclp - acls) == count); // all ACL elements filled + + // commit output + ownerResult = new(alloc) AclOwnerPrototype(owner); + aclCount = (uint32)count; + aclsResult = acls.release(); +} + + +// +// Retrieve the description from a randomly chosen ACL within this Access. +// In the conventional case where all ACLs have the same descriptor, this +// is deterministic. But you have been warned. +// +string Access::promptDescription() const +{ + for (Map::const_iterator it = mAcls.begin(); it != mAcls.end(); it++) { + ACL *acl = it->second; + switch (acl->form()) { + case ACL::allowAllForm: + case ACL::appListForm: + { + string descr = acl->promptDescription(); + if (!descr.empty()) + return descr; + } + default: + break; + } + } + // couldn't find suitable ACL (no description anywhere) + CssmError::throwMe(errSecACLNotSimple); +} + + +// +// Add a new ACL to the resident set. The ACL must have been +// newly made for this Access. +// +void Access::add(ACL *newAcl) +{ + StLock_(mMutex); + if (&newAcl->access != this) + MacOSError::throwMe(errSecParam); + assert(!mAcls[newAcl->entryHandle()]); + mAcls[newAcl->entryHandle()] = newAcl; +} + + +// +// Add the owner ACL to the resident set. The ACL must have been +// newly made for this Access. +// Since an Access must have exactly one owner ACL, this call +// should only be made (exactly once) for a newly created Access. +// +void Access::addOwner(ACL *newAcl) +{ + StLock_(mMutex); + newAcl->makeOwner(); + assert(mAcls.find(ownerHandle) == mAcls.end()); // no owner yet + add(newAcl); +} + + +// +// Compile a set of ACL entries and owner into internal form. +// +void Access::compile(const CSSM_ACL_OWNER_PROTOTYPE &owner, + uint32 aclCount, const CSSM_ACL_ENTRY_INFO *acls) +{ + StLock_(mMutex); + // add owner acl + mAcls[ownerHandle] = new ACL(*this, AclOwnerPrototype::overlay(owner)); + + // add acl entries + const AclEntryInfo *acl = AclEntryInfo::overlay(acls); + for (uint32 n = 0; n < aclCount; n++) { + secdebug("SecAccess", "%p compiling entry %ld", this, acl[n].handle()); + mAcls[acl[n].handle()] = new ACL(*this, acl[n]); + } + secdebug("SecAccess", "%p %ld entries compiled", this, mAcls.size()); +} + + +// +// Creation helper objects +// +const char Access::Maker::creationEntryTag[] = "___setup___"; + +Access::Maker::Maker(Allocator &alloc, MakerType makerType) + : allocator(alloc), mKey(alloc), mCreds(allocator), mMakerType(makerType) +{ + if (makerType == kStandardMakerType) + { + // generate random key + mKey.malloc(keySize); + UniformRandomBlobs().random(mKey.get()); + + // create entry info for resource creation + mInput = AclEntryPrototype(TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_PASSWORD, + new(allocator) ListElement(mKey.get()))); + mInput.proto().tag(creationEntryTag); + + // create credential sample for access + mCreds += TypedList(allocator, CSSM_SAMPLE_TYPE_PASSWORD, new(allocator) ListElement(mKey.get())); + } + else + { + // just make it an CSSM_ACL_SUBJECT_TYPE_ANY list + mInput = AclEntryPrototype(TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_ANY)); + } +} + +void Access::Maker::initialOwner(ResourceControlContext &ctx, const AccessCredentials *creds) +{ + //@@@ make up ctx.entry-info + ctx.input() = mInput; + ctx.credentials(creds); +} + +const AccessCredentials *Access::Maker::cred() +{ + return &mCreds; +}