2 * Copyright (c) 2000-2008 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 // acls - securityd ACL implementation
29 #include "connection.h"
31 #include "agentquery.h"
32 #include "tokendatabase.h"
33 #include "acl_keychain.h"
35 // ACL subjects whose Environments we implement
36 #include <security_cdsa_utilities/acl_any.h>
37 #include <security_cdsa_utilities/acl_password.h>
38 #include <security_cdsa_utilities/acl_threshold.h>
40 #include <sys/sysctl.h>
41 #include <security_utilities/logging.h>
44 // SecurityServerAcl is virtual
46 SecurityServerAcl::~SecurityServerAcl()
51 // The default implementation of the ACL interface simply uses the local ObjectAcl
52 // data. You can customize this by implementing instantiateAcl() [from ObjectAcl]
53 // or by overriding these methods as desired.
54 // Note: While you can completely ignore the ObjectAcl personality if you wish, it's
55 // usually smarter to adapt it.
57 void SecurityServerAcl::getOwner(AclOwnerPrototype
&owner
)
59 StLock
<Mutex
> _(aclSequence
);
60 ObjectAcl::cssmGetOwner(owner
);
63 void SecurityServerAcl::getAcl(const char *tag
, uint32
&count
, AclEntryInfo
*&acls
)
65 StLock
<Mutex
> _(aclSequence
);
66 ObjectAcl::cssmGetAcl(tag
, count
, acls
);
69 void SecurityServerAcl::changeAcl(const AclEdit
&edit
, const AccessCredentials
*cred
,
72 StLock
<Mutex
> _(aclSequence
);
73 SecurityServerEnvironment
env(*this, db
);
74 ObjectAcl::cssmChangeAcl(edit
, cred
, &env
);
77 void SecurityServerAcl::changeOwner(const AclOwnerPrototype
&newOwner
,
78 const AccessCredentials
*cred
, Database
*db
)
80 StLock
<Mutex
> _(aclSequence
);
81 SecurityServerEnvironment
env(*this, db
);
82 ObjectAcl::cssmChangeOwner(newOwner
, cred
, &env
);
87 // Modified validate() methods to connect all the conduits...
89 void SecurityServerAcl::validate(AclAuthorization auth
, const AccessCredentials
*cred
, Database
*db
)
91 SecurityServerEnvironment
env(*this, db
);
93 StLock
<Mutex
> objectSequence(aclSequence
);
94 StLock
<Mutex
> processSequence(Server::process().aclSequence
);
95 ObjectAcl::validate(auth
, cred
, &env
);
98 void SecurityServerAcl::validate(AclAuthorization auth
, const Context
&context
, Database
*db
)
101 context
.get
<AccessCredentials
>(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS
), db
);
106 // This helper tries to add the (new) subject given to the ACL
107 // whose validation is currently proceeding through context.
108 // This will succeed if the ACL is in standard form, which means
109 // a ThresholdAclSubject.
110 // The new subject will be added at the front (so it is checked first
111 // from now on), and as a side effect we'll notify the client side to
112 // re-encode the object.
113 // Returns true if the edit could be done, or false if the ACL wasn't
114 // standard enough. May throw if the ACL is malformed or otherwise messed up.
116 // This is a self-contained helper that is here merely because it's "about"
117 // ACLs and has no better home.
119 bool SecurityServerAcl::addToStandardACL(const AclValidationContext
&context
, AclSubject
*subject
)
121 if (SecurityServerEnvironment
*env
= context
.environment
<SecurityServerEnvironment
>())
122 if (ThresholdAclSubject
*threshold
= env
->standardSubject(context
)) {
123 unsigned size
= threshold
->count();
124 if (dynamic_cast<KeychainPromptAclSubject
*>(threshold
->subject(size
-1))) {
125 // looks standard enough
126 secdebug("acl", "adding new subject %p to from of threshold ACL", subject
);
127 threshold
->add(subject
, 0);
129 // tell the ACL it's been modified
130 context
.acl()->changedAcl();
132 // trigger a special notification code on (otherwise successful) return
133 Server::connection().overrideReturn(CSSMERR_CSP_APPLE_ADD_APPLICATION_ACL_SUBJECT
);
137 secdebug("acl", "ACL is not standard form; cannot edit");
143 // Look at the ACL whose validation is currently proceeding through context.
144 // If it LOOKS like a plausible version of a legacy "dot mac item" ACL.
145 // We don't have access to the database attributes of the item up here in the
146 // securityd sky, so we have to apply a heuristic based on which applications (by path)
147 // are given access to the item.
148 // So this is strictly a heuristic. The potential downside is that we may inadvertently
149 // give access to new .Mac authorized Apple (only) applications when the user only intended
150 // a limited set of extremely popular Apple (only) applications that just happen to all be
151 // .Mac authorized today. We can live with that.
153 bool SecurityServerAcl::looksLikeLegacyDotMac(const AclValidationContext
&context
)
155 static const char * const prototypicalDotMacPath
[] = {
156 "/Applications/Mail.app",
157 "/Applications/Safari.app",
158 "/Applications/iSync.app",
159 "/Applications/System Preferences.app",
160 "/Applications/iCal.app",
161 "/Applications/iChat.app",
162 "/Applications/iTunes.app",
163 "/Applications/Address Book.app",
164 "/Applications/iSync.app",
168 static const unsigned threshold
= 6;
170 if (SecurityServerEnvironment
*env
= context
.environment
<SecurityServerEnvironment
>()) {
171 if (ThresholdAclSubject
*list
= env
->standardSubject(context
)) {
172 unsigned count
= list
->count();
173 unsigned matches
= 0;
174 for (unsigned n
= 0; n
< count
; ++n
) {
175 if (CodeSignatureAclSubject
*app
= dynamic_cast<CodeSignatureAclSubject
*>(list
->subject(n
))) {
176 for (const char * const *p
= prototypicalDotMacPath
; *p
; p
++)
177 if (app
->path() == *p
)
181 secdebug("codesign", "matched %d of %zd candididates (threshold=%d)",
182 matches
, sizeof(prototypicalDotMacPath
) / sizeof(char *) - 1, threshold
);
183 return matches
>= threshold
;
191 // External storage interface
193 Adornable
&SecurityServerEnvironment::store(const AclSubject
*subject
)
195 switch (subject
->type()) {
196 case CSSM_ACL_SUBJECT_TYPE_PREAUTH
:
198 if (TokenDatabase
*tokenDb
= dynamic_cast<TokenDatabase
*>(database
))
199 return tokenDb
->common().store();
205 CssmError::throwMe(CSSM_ERRCODE_ACL_SUBJECT_TYPE_NOT_SUPPORTED
);
210 // ProcessAclSubject personality: uid/gid/pid come from the active Process object
212 uid_t
SecurityServerEnvironment::getuid() const
214 return Server::process().uid();
217 gid_t
SecurityServerEnvironment::getgid() const
219 return Server::process().gid();
222 pid_t
SecurityServerEnvironment::getpid() const
224 return Server::process().pid();
229 // CodeSignatureAclSubject personality: take code signature from active Process object
231 bool SecurityServerEnvironment::verifyCodeSignature(const OSXVerifier
&verifier
,
232 const AclValidationContext
&context
)
234 return Server::codeSignatures().verify(Server::process(), verifier
, context
);
239 // PromptedAclSubject personality: Get a secret by prompting through SecurityAgent
241 bool SecurityServerEnvironment::getSecret(CssmOwnedData
&secret
, const CssmData
&prompt
) const
243 //@@@ ignoring prompt - not used right now
245 QueryPIN
query(*database
);
246 query
.inferHints(Server::process());
247 if (!query()) { // success
248 secret
= query
.pin();
257 // SecretAclSubject personality: externally validate a secret (passphrase etc.)
258 // Right now, this always goes to the (Token)Database object, because that's where
259 // the PIN ACL entries are. We could direct this at the ObjectAcl (database or key)
260 // instead and rely on tokend to perform the PIN mapping, but the generic tokend
261 // wrappers do not (currently) perform any ACL validation, so every tokend would have
262 // to re-implement that. Perhaps in the next ACL revamp cycle...
264 bool SecurityServerEnvironment::validateSecret(const SecretAclSubject
*me
,
265 const AccessCredentials
*cred
)
267 return database
&& database
->validateSecret(me
, cred
);
272 // PreAuthenticationAclSubject personality - refer to database (ObjectAcl)
274 ObjectAcl
*SecurityServerEnvironment::preAuthSource()
276 return database
? &database
->acl() : NULL
;
281 // Autonomous ACL editing support
283 ThresholdAclSubject
*SecurityServerEnvironment::standardSubject(const AclValidationContext
&context
)
285 return dynamic_cast<ThresholdAclSubject
*>(context
.subject());
290 // The default AclSource denies having an ACL at all
292 AclSource::~AclSource()
295 SecurityServerAcl
&AclSource::acl()
297 CssmError::throwMe(CSSM_ERRCODE_OBJECT_ACL_NOT_SUPPORTED
);
300 Database
*AclSource::relatedDatabase()