1/* 2 * Copyright (c) 2004-2007 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// tokenacl - Token-based ACL implementation 27// 28#include "tokenacl.h" 29#include "tokend.h" 30#include "token.h" 31#include "tokendatabase.h" 32#include "agentquery.h" 33#include <security_utilities/trackingallocator.h> 34#include <security_cdsa_utilities/cssmbridge.h> 35 36 37// 38// A TokenAcl initializes to "invalid, needs update". 39// Note how our Token will start its ResetGeneration at 1, while we start at zero. 40// 41TokenAcl::TokenAcl() 42 : mLastReset(0) 43{ 44} 45 46 47// 48// Instantiate is called (by the ACL machinery core) before this ACL object's 49// contents are used in any way. Here is where we fetch the ACL data from tokend 50// (if we haven't got it yet). 51// 52void TokenAcl::instantiateAcl() 53{ 54 if (token().resetGeneration(mLastReset)) 55 return; 56 57 secdebug("tokenacl", "%p loading ACLs from tokend", this); 58 59 // read owner 60 AclOwnerPrototype *owner = NULL; 61 token().tokend().getOwner(aclKind(), tokenHandle(), owner); 62 assert(owner); 63 64 // read entries 65 uint32 count; 66 AclEntryInfo *infos; 67 token().tokend().getAcl(aclKind(), tokenHandle(), NULL, count, infos); 68 69 // commit to setting the ACL data 70 ObjectAcl::owner(*owner); 71 ObjectAcl::entries(count, infos); 72 73 // and if we actually made it to here... 74 mLastReset = token().resetGeneration(); 75} 76 77 78// 79// The ACL machinery core calls this after successfully making changes to our ACL. 80// 81void TokenAcl::changedAcl() 82{ 83} 84 85 86// 87// CSSM-layer read gates. This accesses a cached version prepared in our instantiateAcl(). 88// 89void TokenAcl::getOwner(AclOwnerPrototype &owner) 90{ 91 ObjectAcl::cssmGetOwner(owner); 92} 93 94void TokenAcl::getAcl(const char *tag, uint32 &count, AclEntryInfo *&acls) 95{ 96 ObjectAcl::cssmGetAcl(tag, count, acls); 97} 98 99 100// 101// CSSM-layer write gates. 102// This doesn't directly write to the local ObjectAcl at all. The call is relayed to 103// tokend, and the resulting ACL is being re-read when next needed. 104// 105void TokenAcl::changeAcl(const AclEdit &edit, const AccessCredentials *cred, Database *db) 106{ 107 // changeAcl from/to a PIN (source) ACL has special UI handling here 108 // @@@ this is an ad-hoc hack; general solution awaits the ACL machinery rebuild later 109 instantiateAcl(); // (redundant except in error cases) 110 if (TokenDatabase *tokenDb = dynamic_cast<TokenDatabase *>(db)) 111 if (edit.mode() == CSSM_ACL_EDIT_MODE_REPLACE) 112 if (const AclEntryInput *input = edit.newEntry()) { 113 if (unsigned pin = pinFromAclTag(input->proto().tag())) { 114 // assume this is a PIN change request 115 pinChange(pin, edit.handle(), *tokenDb); 116 invalidateAcl(); 117 return; 118 } 119 } 120 121 // hand the request off to tokend to do as it will 122 token().tokend().changeAcl(aclKind(), tokenHandle(), Required(cred), edit); 123 invalidateAcl(); 124} 125 126void TokenAcl::changeOwner(const AclOwnerPrototype &newOwner, 127 const AccessCredentials *cred, Database *db) 128{ 129 token().tokend().changeOwner(aclKind(), tokenHandle(), Required(cred), newOwner); 130 invalidateAcl(); 131} 132 133 134// 135// Ad-hoc PIN change processing. 136// This cooks a suitable changeAcl call to tokend, ad hoc. 137// It does NOT complete the originating request; it replaces it completely. 138// (Completion processing requires not-yet-implemented ACL machine UI coalescing features.) 139// 140class QueryNewPin : public QueryNewPassphrase { 141public: 142 QueryNewPin(unsigned int pinn, CSSM_ACL_HANDLE h, TokenDatabase &db, Reason reason) 143 : QueryNewPassphrase(db, reason), pin(pinn), handle(h) { } 144 145 const unsigned int pin; 146 const CSSM_ACL_HANDLE handle; 147 148 Reason accept(CssmManagedData &passphrase, CssmData *oldPassphrase); 149}; 150 151SecurityAgent::Reason QueryNewPin::accept(CssmManagedData &passphrase, CssmData *oldPassphrase) 152{ 153 assert(oldPassphrase); // we don't handle the new-pin case (yet) 154 155 // form a changeAcl query and send it to tokend 156 try { 157 TrackingAllocator alloc(Allocator::standard()); 158 AclEntryPrototype proto(TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PROMPTED_PASSWORD, 159 new(alloc) ListElement(passphrase) 160 )); 161 proto.authorization() = AuthorizationGroup(CSSM_ACL_AUTHORIZATION_PREAUTH(pin), alloc); 162 char pintag[20]; sprintf(pintag, "PIN%d", pin); 163 proto.tag(pintag); 164 AclEntryInput input(proto); 165 AclEdit edit(CSSM_ACL_EDIT_MODE_REPLACE, handle, &input); 166 AutoCredentials cred(alloc); 167 cred += TypedList(alloc, CSSM_SAMPLE_TYPE_PROMPTED_PASSWORD, 168 new(alloc) ListElement(*oldPassphrase)); 169 safer_cast<TokenDatabase &>(database).token().tokend().changeAcl(dbAcl, noDb, cred, edit); 170 return SecurityAgent::noReason; 171 } catch (const CssmError &err) { 172 switch (err.error) { 173 default: 174 return SecurityAgent::unknownReason; 175 } 176 } catch (...) { 177 return SecurityAgent::unknownReason; 178 } 179} 180 181void TokenAcl::pinChange(unsigned int pin, CSSM_ACL_HANDLE handle, TokenDatabase &database) 182{ 183 QueryNewPin query(pin, handle, database, SecurityAgent::changePassphrase); 184 query.inferHints(Server::process()); 185 CssmAutoData newPin(Allocator::standard(Allocator::sensitive)); 186 CssmAutoData oldPin(Allocator::standard(Allocator::sensitive)); 187 switch (query(oldPin, newPin)) { 188 case SecurityAgent::noReason: // worked 189 return; 190 default: 191 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); 192 } 193} 194