]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
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 | } |