]> git.saurik.com Git - apple/security.git/blob - securityd/src/acls.cpp
Security-57031.40.6.tar.gz
[apple/security.git] / securityd / src / acls.cpp
1 /*
2 * Copyright (c) 2000-2009,2012-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 // acls - securityd ACL implementation
27 //
28 #include "acls.h"
29 #include "connection.h"
30 #include "server.h"
31 #include "agentquery.h"
32 #include "tokendatabase.h"
33 #include "acl_keychain.h"
34
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>
39
40 #include <sys/sysctl.h>
41 #include <security_utilities/logging.h>
42
43 //
44 // SecurityServerAcl is virtual
45 //
46 SecurityServerAcl::~SecurityServerAcl()
47 { }
48
49
50 //
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.
56 //
57 void SecurityServerAcl::getOwner(AclOwnerPrototype &owner)
58 {
59 StLock<Mutex> _(aclSequence);
60 ObjectAcl::cssmGetOwner(owner);
61 }
62
63 void SecurityServerAcl::getAcl(const char *tag, uint32 &count, AclEntryInfo *&acls)
64 {
65 StLock<Mutex> _(aclSequence);
66 ObjectAcl::cssmGetAcl(tag, count, acls);
67 }
68
69 void SecurityServerAcl::changeAcl(const AclEdit &edit, const AccessCredentials *cred,
70 Database *db)
71 {
72 StLock<Mutex> _(aclSequence);
73 SecurityServerEnvironment env(*this, db);
74 ObjectAcl::cssmChangeAcl(edit, cred, &env);
75 }
76
77 void SecurityServerAcl::changeOwner(const AclOwnerPrototype &newOwner,
78 const AccessCredentials *cred, Database *db)
79 {
80 StLock<Mutex> _(aclSequence);
81 SecurityServerEnvironment env(*this, db);
82 ObjectAcl::cssmChangeOwner(newOwner, cred, &env);
83 }
84
85
86 //
87 // Modified validate() methods to connect all the conduits...
88 //
89 void SecurityServerAcl::validate(AclAuthorization auth, const AccessCredentials *cred, Database *db)
90 {
91 SecurityServerEnvironment env(*this, db);
92
93 StLock<Mutex> objectSequence(aclSequence);
94 StLock<Mutex> processSequence(Server::process().aclSequence);
95 ObjectAcl::validate(auth, cred, &env);
96 }
97
98 void SecurityServerAcl::validate(AclAuthorization auth, const Context &context, Database *db)
99 {
100 validate(auth,
101 context.get<AccessCredentials>(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS), db);
102 }
103
104
105 //
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.
115 //
116 // This is a self-contained helper that is here merely because it's "about"
117 // ACLs and has no better home.
118 //
119 bool SecurityServerAcl::addToStandardACL(const AclValidationContext &context, AclSubject *subject)
120 {
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);
128
129 // tell the ACL it's been modified
130 context.acl()->changedAcl();
131
132 // trigger a special notification code on (otherwise successful) return
133 Server::connection().overrideReturn(CSSMERR_CSP_APPLE_ADD_APPLICATION_ACL_SUBJECT);
134 return true;
135 }
136 }
137 secdebug("acl", "ACL is not standard form; cannot edit");
138 return false;
139 }
140
141
142 //
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.
152 //
153 bool SecurityServerAcl::looksLikeLegacyDotMac(const AclValidationContext &context)
154 {
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",
165 NULL // sentinel
166 };
167
168 static const unsigned threshold = 6;
169
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)
178 matches++;
179 }
180 }
181 secdebug("codesign", "matched %d of %zd candididates (threshold=%d)",
182 matches, sizeof(prototypicalDotMacPath) / sizeof(char *) - 1, threshold);
183 return matches >= threshold;
184 }
185 }
186 return false;
187 }
188
189
190 //
191 // External storage interface
192 //
193 Adornable &SecurityServerEnvironment::store(const AclSubject *subject)
194 {
195 switch (subject->type()) {
196 case CSSM_ACL_SUBJECT_TYPE_PREAUTH:
197 {
198 if (TokenDatabase *tokenDb = dynamic_cast<TokenDatabase *>(database))
199 return tokenDb->common().store();
200 }
201 break;
202 default:
203 break;
204 }
205 CssmError::throwMe(CSSM_ERRCODE_ACL_SUBJECT_TYPE_NOT_SUPPORTED);
206 }
207
208
209 //
210 // ProcessAclSubject personality: uid/gid/pid come from the active Process object
211 //
212 uid_t SecurityServerEnvironment::getuid() const
213 {
214 return Server::process().uid();
215 }
216
217 gid_t SecurityServerEnvironment::getgid() const
218 {
219 return Server::process().gid();
220 }
221
222 pid_t SecurityServerEnvironment::getpid() const
223 {
224 return Server::process().pid();
225 }
226
227
228 //
229 // CodeSignatureAclSubject personality: take code signature from active Process object
230 //
231 bool SecurityServerEnvironment::verifyCodeSignature(const OSXVerifier &verifier,
232 const AclValidationContext &context)
233 {
234 return Server::codeSignatures().verify(Server::process(), verifier, context);
235 }
236
237
238 //
239 // PromptedAclSubject personality: Get a secret by prompting through SecurityAgent
240 //
241 bool SecurityServerEnvironment::getSecret(CssmOwnedData &secret, const CssmData &prompt) const
242 {
243 //@@@ ignoring prompt - not used right now
244 if (database) {
245 QueryPIN query(*database);
246 query.inferHints(Server::process());
247 if (!query()) { // success
248 secret = query.pin();
249 return true;
250 }
251 }
252 return false;
253 }
254
255
256 //
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...
263 //
264 bool SecurityServerEnvironment::validateSecret(const SecretAclSubject *me,
265 const AccessCredentials *cred)
266 {
267 return database && database->validateSecret(me, cred);
268 }
269
270
271 //
272 // PreAuthenticationAclSubject personality - refer to database (ObjectAcl)
273 //
274 ObjectAcl *SecurityServerEnvironment::preAuthSource()
275 {
276 return database ? &database->acl() : NULL;
277 }
278
279
280 //
281 // Autonomous ACL editing support
282 //
283 ThresholdAclSubject *SecurityServerEnvironment::standardSubject(const AclValidationContext &context)
284 {
285 return dynamic_cast<ThresholdAclSubject *>(context.subject());
286 }
287
288
289 //
290 // The default AclSource denies having an ACL at all
291 //
292 AclSource::~AclSource()
293 { /* virtual */ }
294
295 SecurityServerAcl &AclSource::acl()
296 {
297 CssmError::throwMe(CSSM_ERRCODE_OBJECT_ACL_NOT_SUPPORTED);
298 }
299
300 Database *AclSource::relatedDatabase()
301 {
302 return NULL;
303 }