/ securityd / src / tokenacl.cpp
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  }