2 * Copyright (c) 2004-2007,2013 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // tokenacl - Token-based ACL implementation
31 #include "tokendatabase.h"
32 #include "agentquery.h"
33 #include <security_utilities/trackingallocator.h>
34 #include <security_cdsa_utilities/cssmbridge.h>
38 // A TokenAcl initializes to "invalid, needs update".
39 // Note how our Token will start its ResetGeneration at 1, while we start at zero.
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).
52 void TokenAcl::instantiateAcl()
54 if (token().resetGeneration(mLastReset
))
57 secinfo("tokenacl", "%p loading ACLs from tokend", this);
60 AclOwnerPrototype
*owner
= NULL
;
61 token().tokend().getOwner(aclKind(), tokenHandle(), owner
);
67 token().tokend().getAcl(aclKind(), tokenHandle(), NULL
, count
, infos
);
69 // commit to setting the ACL data
70 ObjectAcl::owner(*owner
);
71 ObjectAcl::entries(count
, infos
);
73 // and if we actually made it to here...
74 mLastReset
= token().resetGeneration();
79 // The ACL machinery core calls this after successfully making changes to our ACL.
81 void TokenAcl::changedAcl()
87 // CSSM-layer read gates. This accesses a cached version prepared in our instantiateAcl().
89 void TokenAcl::getOwner(AclOwnerPrototype
&owner
)
91 ObjectAcl::cssmGetOwner(owner
);
94 void TokenAcl::getAcl(const char *tag
, uint32
&count
, AclEntryInfo
*&acls
)
96 ObjectAcl::cssmGetAcl(tag
, count
, acls
);
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.
105 void TokenAcl::changeAcl(const AclEdit
&edit
, const AccessCredentials
*cred
, Database
*db
)
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
);
121 // hand the request off to tokend to do as it will
122 token().tokend().changeAcl(aclKind(), tokenHandle(), Required(cred
), edit
);
126 void TokenAcl::changeOwner(const AclOwnerPrototype
&newOwner
,
127 const AccessCredentials
*cred
, Database
*db
)
129 token().tokend().changeOwner(aclKind(), tokenHandle(), Required(cred
), newOwner
);
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.)
140 class QueryNewPin
: public QueryNewPassphrase
{
142 QueryNewPin(unsigned int pinn
, CSSM_ACL_HANDLE h
, TokenDatabase
&db
, Reason reason
)
143 : QueryNewPassphrase(db
, reason
), pin(pinn
), handle(h
) { }
145 const unsigned int pin
;
146 const CSSM_ACL_HANDLE handle
;
148 Reason
accept(CssmManagedData
&passphrase
, CssmData
*oldPassphrase
);
151 SecurityAgent::Reason
QueryNewPin::accept(CssmManagedData
&passphrase
, CssmData
*oldPassphrase
)
153 assert(oldPassphrase
); // we don't handle the new-pin case (yet)
155 // form a changeAcl query and send it to tokend
157 TrackingAllocator
alloc(Allocator::standard());
158 AclEntryPrototype
proto(TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PROMPTED_PASSWORD
,
159 new(alloc
) ListElement(passphrase
)
161 proto
.authorization() = AuthorizationGroup(CSSM_ACL_AUTHORIZATION_PREAUTH(pin
), alloc
);
162 char pintag
[20]; sprintf(pintag
, "PIN%d", pin
);
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
) {
174 return SecurityAgent::unknownReason
;
177 return SecurityAgent::unknownReason
;
181 void TokenAcl::pinChange(unsigned int pin
, CSSM_ACL_HANDLE handle
, TokenDatabase
&database
)
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
191 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
);