]> git.saurik.com Git - apple/securityd.git/blob - src/acls.cpp
securityd-55137.5.tar.gz
[apple/securityd.git] / src / acls.cpp
1 /*
2 * Copyright (c) 2000-2008 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
41 //
42 // SecurityServerAcl is virtual
43 //
44 SecurityServerAcl::~SecurityServerAcl()
45 { }
46
47
48 //
49 // The default implementation of the ACL interface simply uses the local ObjectAcl
50 // data. You can customize this by implementing instantiateAcl() [from ObjectAcl]
51 // or by overriding these methods as desired.
52 // Note: While you can completely ignore the ObjectAcl personality if you wish, it's
53 // usually smarter to adapt it.
54 //
55 void SecurityServerAcl::getOwner(AclOwnerPrototype &owner)
56 {
57 StLock<Mutex> _(aclSequence);
58 ObjectAcl::cssmGetOwner(owner);
59 }
60
61 void SecurityServerAcl::getAcl(const char *tag, uint32 &count, AclEntryInfo *&acls)
62 {
63 StLock<Mutex> _(aclSequence);
64 ObjectAcl::cssmGetAcl(tag, count, acls);
65 }
66
67 void SecurityServerAcl::changeAcl(const AclEdit &edit, const AccessCredentials *cred,
68 Database *db)
69 {
70 StLock<Mutex> _(aclSequence);
71 SecurityServerEnvironment env(*this, db);
72 ObjectAcl::cssmChangeAcl(edit, cred, &env);
73 }
74
75 void SecurityServerAcl::changeOwner(const AclOwnerPrototype &newOwner,
76 const AccessCredentials *cred, Database *db)
77 {
78 StLock<Mutex> _(aclSequence);
79 SecurityServerEnvironment env(*this, db);
80 ObjectAcl::cssmChangeOwner(newOwner, cred, &env);
81 }
82
83
84 //
85 // Modified validate() methods to connect all the conduits...
86 //
87 void SecurityServerAcl::validate(AclAuthorization auth, const AccessCredentials *cred, Database *db)
88 {
89 SecurityServerEnvironment env(*this, db);
90
91 {
92 // Migrator gets a free ride
93 Process &thisProcess = Server::process();
94 StLock<Mutex> _(thisProcess);
95 SecCodeRef clientRef = thisProcess.currentGuest();
96 if (clientRef) {
97 std::string clientPath = codePath(clientRef);
98 if (clientPath == std::string("/usr/libexec/KeychainMigrator"))
99 return;
100 }
101 }
102
103 StLock<Mutex> objectSequence(aclSequence);
104 StLock<Mutex> processSequence(Server::process().aclSequence);
105 ObjectAcl::validate(auth, cred, &env);
106 }
107
108 void SecurityServerAcl::validate(AclAuthorization auth, const Context &context, Database *db)
109 {
110 validate(auth,
111 context.get<AccessCredentials>(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS), db);
112 }
113
114
115 //
116 // This helper tries to add the (new) subject given to the ACL
117 // whose validation is currently proceeding through context.
118 // This will succeed if the ACL is in standard form, which means
119 // a ThresholdAclSubject.
120 // The new subject will be added at the front (so it is checked first
121 // from now on), and as a side effect we'll notify the client side to
122 // re-encode the object.
123 // Returns true if the edit could be done, or false if the ACL wasn't
124 // standard enough. May throw if the ACL is malformed or otherwise messed up.
125 //
126 // This is a self-contained helper that is here merely because it's "about"
127 // ACLs and has no better home.
128 //
129 bool SecurityServerAcl::addToStandardACL(const AclValidationContext &context, AclSubject *subject)
130 {
131 if (SecurityServerEnvironment *env = context.environment<SecurityServerEnvironment>())
132 if (ThresholdAclSubject *threshold = env->standardSubject(context)) {
133 unsigned size = threshold->count();
134 if (dynamic_cast<KeychainPromptAclSubject *>(threshold->subject(size-1))) {
135 // looks standard enough
136 secdebug("acl", "adding new subject %p to from of threshold ACL", subject);
137 threshold->add(subject, 0);
138
139 // tell the ACL it's been modified
140 context.acl()->changedAcl();
141
142 // trigger a special notification code on (otherwise successful) return
143 Server::connection().overrideReturn(CSSMERR_CSP_APPLE_ADD_APPLICATION_ACL_SUBJECT);
144 return true;
145 }
146 }
147 secdebug("acl", "ACL is not standard form; cannot edit");
148 return false;
149 }
150
151
152 //
153 // Look at the ACL whose validation is currently proceeding through context.
154 // If it LOOKS like a plausible version of a legacy "dot mac item" ACL.
155 // We don't have access to the database attributes of the item up here in the
156 // securityd sky, so we have to apply a heuristic based on which applications (by path)
157 // are given access to the item.
158 // So this is strictly a heuristic. The potential downside is that we may inadvertently
159 // give access to new .Mac authorized Apple (only) applications when the user only intended
160 // a limited set of extremely popular Apple (only) applications that just happen to all be
161 // .Mac authorized today. We can live with that.
162 //
163 bool SecurityServerAcl::looksLikeLegacyDotMac(const AclValidationContext &context)
164 {
165 static const char * const prototypicalDotMacPath[] = {
166 "/Applications/Mail.app",
167 "/Applications/Safari.app",
168 "/Applications/iSync.app",
169 "/Applications/System Preferences.app",
170 "/Applications/iCal.app",
171 "/Applications/iChat.app",
172 "/Applications/iTunes.app",
173 "/Applications/Address Book.app",
174 "/Applications/iSync.app",
175 NULL // sentinel
176 };
177
178 static const unsigned threshold = 6;
179
180 if (SecurityServerEnvironment *env = context.environment<SecurityServerEnvironment>()) {
181 if (ThresholdAclSubject *list = env->standardSubject(context)) {
182 unsigned count = list->count();
183 unsigned matches = 0;
184 for (unsigned n = 0; n < count; ++n) {
185 if (CodeSignatureAclSubject *app = dynamic_cast<CodeSignatureAclSubject *>(list->subject(n))) {
186 for (const char * const *p = prototypicalDotMacPath; *p; p++)
187 if (app->path() == *p)
188 matches++;
189 }
190 }
191 secdebug("codesign", "matched %d of %zd candididates (threshold=%d)",
192 matches, sizeof(prototypicalDotMacPath) / sizeof(char *) - 1, threshold);
193 return matches >= threshold;
194 }
195 }
196 return false;
197 }
198
199
200 //
201 // External storage interface
202 //
203 Adornable &SecurityServerEnvironment::store(const AclSubject *subject)
204 {
205 switch (subject->type()) {
206 case CSSM_ACL_SUBJECT_TYPE_PREAUTH:
207 {
208 if (TokenDatabase *tokenDb = dynamic_cast<TokenDatabase *>(database))
209 return tokenDb->common().store();
210 }
211 break;
212 default:
213 break;
214 }
215 CssmError::throwMe(CSSM_ERRCODE_ACL_SUBJECT_TYPE_NOT_SUPPORTED);
216 }
217
218
219 //
220 // ProcessAclSubject personality: uid/gid/pid come from the active Process object
221 //
222 uid_t SecurityServerEnvironment::getuid() const
223 {
224 return Server::process().uid();
225 }
226
227 gid_t SecurityServerEnvironment::getgid() const
228 {
229 return Server::process().gid();
230 }
231
232 pid_t SecurityServerEnvironment::getpid() const
233 {
234 return Server::process().pid();
235 }
236
237
238 //
239 // CodeSignatureAclSubject personality: take code signature from active Process object
240 //
241 bool SecurityServerEnvironment::verifyCodeSignature(const OSXVerifier &verifier,
242 const AclValidationContext &context)
243 {
244 return Server::codeSignatures().verify(Server::process(), verifier, context);
245 }
246
247
248 //
249 // PromptedAclSubject personality: Get a secret by prompting through SecurityAgent
250 //
251 bool SecurityServerEnvironment::getSecret(CssmOwnedData &secret, const CssmData &prompt) const
252 {
253 //@@@ ignoring prompt - not used right now
254 if (database) {
255 QueryPIN query(*database);
256 query.inferHints(Server::process());
257 if (!query()) { // success
258 secret = query.pin();
259 return true;
260 }
261 }
262 return false;
263 }
264
265
266 //
267 // SecretAclSubject personality: externally validate a secret (passphrase etc.)
268 // Right now, this always goes to the (Token)Database object, because that's where
269 // the PIN ACL entries are. We could direct this at the ObjectAcl (database or key)
270 // instead and rely on tokend to perform the PIN mapping, but the generic tokend
271 // wrappers do not (currently) perform any ACL validation, so every tokend would have
272 // to re-implement that. Perhaps in the next ACL revamp cycle...
273 //
274 bool SecurityServerEnvironment::validateSecret(const SecretAclSubject *me,
275 const AccessCredentials *cred)
276 {
277 return database && database->validateSecret(me, cred);
278 }
279
280
281 //
282 // PreAuthenticationAclSubject personality - refer to database (ObjectAcl)
283 //
284 ObjectAcl *SecurityServerEnvironment::preAuthSource()
285 {
286 return database ? &database->acl() : NULL;
287 }
288
289
290 //
291 // Autonomous ACL editing support
292 //
293 ThresholdAclSubject *SecurityServerEnvironment::standardSubject(const AclValidationContext &context)
294 {
295 return dynamic_cast<ThresholdAclSubject *>(context.subject());
296 }
297
298
299 //
300 // The default AclSource denies having an ACL at all
301 //
302 AclSource::~AclSource()
303 { /* virtual */ }
304
305 SecurityServerAcl &AclSource::acl()
306 {
307 CssmError::throwMe(CSSM_ERRCODE_OBJECT_ACL_NOT_SUPPORTED);
308 }
309
310 Database *AclSource::relatedDatabase()
311 {
312 return NULL;
313 }