tokenacl.cpp
1 /* 2 * Copyright (c) 2004-2007,2013 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 // 41 TokenAcl::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 // 52 void TokenAcl::instantiateAcl() 53 { 54 if (token().resetGeneration(mLastReset)) 55 return; 56 57 secinfo("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 // 81 void TokenAcl::changedAcl() 82 { 83 } 84 85 86 // 87 // CSSM-layer read gates. This accesses a cached version prepared in our instantiateAcl(). 88 // 89 void TokenAcl::getOwner(AclOwnerPrototype &owner) 90 { 91 ObjectAcl::cssmGetOwner(owner); 92 } 93 94 void 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 // 105 void 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 126 void 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 // 140 class QueryNewPin : public QueryNewPassphrase { 141 public: 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 151 SecurityAgent::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 181 void 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 }