X-Git-Url: https://git.saurik.com/apple/securityd.git/blobdiff_plain/eeadf2e6470f45ea0275a6019635573f2a7b5a2c..ee396ef47db58c01c7ceaecfec60781c95ffeea1:/src/tokenacl.cpp diff --git a/src/tokenacl.cpp b/src/tokenacl.cpp new file mode 100644 index 0000000..efaf36a --- /dev/null +++ b/src/tokenacl.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2004 Apple Computer, 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@ + */ + + +// +// tokenacl - Token-based ACL implementation +// +#include "tokenacl.h" +#include "tokend.h" +#include "token.h" +#include "tokendatabase.h" +#include "agentquery.h" +#include +#include + + +// +// A TokenAcl initializes to "invalid, needs update". +// Note how our Token will start its ResetGeneration at 1, while we start at zero. +// +TokenAcl::TokenAcl() + : mLastReset(0) +{ +} + + +// +// Instantiate is called (by the ACL machinery core) before this ACL object's +// contents are used in any way. Here is where we fetch the ACL data from tokend +// (if we haven't got it yet). +// +void TokenAcl::instantiateAcl() +{ + if (token().resetGeneration(mLastReset)) + return; + + secdebug("tokenacl", "%p loading ACLs from tokend", this); + + // read owner + AclOwnerPrototype *owner = NULL; + token().tokend().getOwner(aclKind(), tokenHandle(), owner); + assert(owner); + + // read entries + uint32 count; + AclEntryInfo *infos; + token().tokend().getAcl(aclKind(), tokenHandle(), NULL, count, infos); + + // commit to setting the ACL data + ObjectAcl::owner(*owner); + ObjectAcl::entries(count, infos); + + // and if we actually made it to here... + mLastReset = token().resetGeneration(); +} + + +// +// The ACL machinery core calls this after successfully making changes to our ACL. +// +void TokenAcl::changedAcl() +{ +} + + +// +// CSSM-layer read gates. This accesses a cached version prepared in our instantiateAcl(). +// +void TokenAcl::getOwner(AclOwnerPrototype &owner) +{ + ObjectAcl::cssmGetOwner(owner); +} + +void TokenAcl::getAcl(const char *tag, uint32 &count, AclEntryInfo *&acls) +{ + ObjectAcl::cssmGetAcl(tag, count, acls); +} + + +// +// CSSM-layer write gates. +// This doesn't directly write to the local ObjectAcl at all. The call is relayed to +// tokend, and the resulting ACL is being re-read when next needed. +// +void TokenAcl::changeAcl(const AclEdit &edit, const AccessCredentials *cred, Database *db) +{ + // changeAcl from/to a PIN (source) ACL has special UI handling here + // @@@ this is an ad-hoc hack; general solution awaits the ACL machinery rebuild later + instantiateAcl(); // (redundant except in error cases) + if (TokenDatabase *tokenDb = dynamic_cast(db)) + if (edit.mode() == CSSM_ACL_EDIT_MODE_REPLACE) + if (const AclEntryInput *input = edit.newEntry()) { + unsigned int pin; + if (sscanf(input->proto().s_tag().c_str(), "PIN%d", &pin) == 1) { + // assume this is a PIN change request + pinChange(pin, edit.handle(), *tokenDb); + invalidateAcl(); + return; + } + } + + // hand the request off to tokend to do as it will + token().tokend().changeAcl(aclKind(), tokenHandle(), Required(cred), edit); + invalidateAcl(); +} + +void TokenAcl::changeOwner(const AclOwnerPrototype &newOwner, + const AccessCredentials *cred, Database *db) +{ + token().tokend().changeOwner(aclKind(), tokenHandle(), Required(cred), newOwner); + invalidateAcl(); +} + + +// +// Ad-hoc PIN change processing. +// This cooks a suitable changeAcl call to tokend, ad hoc. +// It does NOT complete the originating request; it replaces it completely. +// (Completion processing requires not-yet-implemented ACL machine UI coalescing features.) +// +class QueryNewPin : public QueryNewPassphrase { +public: + QueryNewPin(unsigned int pinn, CSSM_ACL_HANDLE h, TokenDatabase &db, Reason reason) + : QueryNewPassphrase(db, reason), pin(pinn), handle(h) { } + + const unsigned int pin; + const CSSM_ACL_HANDLE handle; + + Reason accept(CssmManagedData &passphrase, CssmData *oldPassphrase); +}; + +SecurityAgent::Reason QueryNewPin::accept(CssmManagedData &passphrase, CssmData *oldPassphrase) +{ + assert(oldPassphrase); // we don't handle the new-pin case (yet) + + // form a changeAcl query and send it to tokend + try { + TrackingAllocator alloc(Allocator::standard()); + AclEntryPrototype proto(TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PROMPTED_PASSWORD, + new(alloc) ListElement(passphrase) + )); + proto.authorization() = AuthorizationGroup(CSSM_ACL_AUTHORIZATION_PREAUTH(pin), alloc); + char pintag[10]; sprintf(pintag, "PIN%d", pin); + proto.tag(pintag); + AclEntryInput input(proto); + AclEdit edit(CSSM_ACL_EDIT_MODE_REPLACE, handle, &input); + AutoCredentials cred(alloc); + cred += TypedList(alloc, CSSM_SAMPLE_TYPE_PROMPTED_PASSWORD, + new(alloc) ListElement(*oldPassphrase)); + safer_cast(database).token().tokend().changeAcl(dbAcl, noDb, cred, edit); + return SecurityAgent::noReason; + } catch (const CssmError &err) { + switch (err.error) { + default: + return SecurityAgent::unknownReason; + } + } catch (...) { + return SecurityAgent::unknownReason; + } +} + +void TokenAcl::pinChange(unsigned int pin, CSSM_ACL_HANDLE handle, TokenDatabase &database) +{ + QueryNewPin query(pin, handle, database, SecurityAgent::changePassphrase); + CssmAutoData newPin(Allocator::standard(Allocator::sensitive)); + switch (query(newPin)) { + case SecurityAgent::noReason: // worked + return; + default: + CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); + } +}