]> git.saurik.com Git - apple/securityd.git/blob - src/AuthorizationEngine.cpp
securityd-55199.tar.gz
[apple/securityd.git] / src / AuthorizationEngine.cpp
1 /*
2 * Copyright (c) 2000-2004,2009 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 #include "AuthorizationEngine.h"
25 #include <security_cdsa_utilities/AuthorizationWalkers.h>
26 #include <Security/AuthorizationPriv.h>
27 #include <Security/AuthorizationDB.h>
28
29 #include "authority.h"
30
31 #include <Security/AuthorizationTags.h>
32 #include <Security/AuthorizationTagsPriv.h>
33 #include <security_utilities/logging.h>
34 #include <security_utilities/cfutilities.h>
35 #include <security_utilities/debugging.h>
36 #include "server.h"
37
38 #include <CoreFoundation/CFData.h>
39 #include <CoreFoundation/CFNumber.h>
40 #include <CoreFoundation/CFPropertyList.h>
41
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <float.h>
45 #include <sandbox.h>
46
47 #include <bsm/audit_uevents.h> // AUE_ssauth*
48 #include "ccaudit_extensions.h"
49
50 namespace Authorization {
51
52 using namespace CommonCriteria::Securityd;
53
54
55 //
56 // Errors to be thrown
57 //
58 Error::Error(int err) : error(err)
59 {
60 }
61
62 const char *Error::what() const throw()
63 { return "Authorization error"; }
64
65 int Error::unixError() const throw()
66 { return error; } // @@@ eventually...
67
68 OSStatus Error::osStatus() const throw()
69 { return error; }
70
71 void Error::throwMe(int err) { throw Error(err); }
72
73 //
74 // Engine class
75 //
76 Engine::Engine(const char *configFile) : mAuthdb(configFile)
77 {
78 }
79
80 Engine::~Engine()
81 {
82 }
83
84
85 /*!
86 @function AuthorizationEngine::authorize
87
88 @@@.
89
90 @param inRights (input) List of rights being requested for authorization.
91 @param environment (optional/input) Environment containing information to be used during evaluation.
92 @param flags (input) Optional flags @@@ see AuthorizationCreate for a description.
93 @param inCredentials (input) Credentials already held by the caller.
94 @param outCredentials (output/optional) Credentials obtained, used or refreshed during this call to authorize the requested rights.
95 @param outRights (output/optional) Subset of inRights which were actually authorized.
96
97 @results Returns errAuthorizationSuccess if all rights requested are authorized, or if the kAuthorizationFlagPartialRights flag was specified. Might return other status values like errAuthorizationDenied, errAuthorizationCanceled or errAuthorizationInteractionNotAllowed
98 */
99 OSStatus
100 Engine::authorize(const AuthItemSet &inRights, const AuthItemSet &environment,
101 AuthorizationFlags flags, const CredentialSet *inCredentials, CredentialSet *outCredentials,
102 AuthItemSet &outRights, AuthorizationToken &auth)
103 {
104 CredentialSet credentials;
105 OSStatus status = errAuthorizationSuccess;
106 SecurityAgent::Reason reason = SecurityAgent::noReason;
107
108 // Get current time of day.
109 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
110
111 // Update rules from database if needed
112 mAuthdb.sync(now);
113
114 // Check if a credential was passed into the environment and we were asked to extend the rights
115 if (flags & kAuthorizationFlagExtendRights)
116 {
117 string username, password;
118 bool shared = false;
119 for (AuthItemSet::iterator item = environment.begin(); item != environment.end(); item ++)
120 {
121 if (!strcmp((*item)->name(), kAuthorizationEnvironmentUsername))
122 username = (*item)->stringValue();
123 else if (!strcmp((*item)->name(), kAuthorizationEnvironmentPassword))
124 password = (*item)->stringValue();
125 else if (!strcmp((*item)->name(), kAuthorizationEnvironmentShared))
126 shared = true;
127 }
128
129 if (username.length())
130 {
131 // Let's create a credential from the passed in username and password.
132 Credential newCredential(username, password, shared);
133 // If it's valid insert it into the credentials list. Normally this is
134 // only done if it actually authorizes a requested right, but for this
135 // special case (environment) we do it even when no rights are being requested.
136 if (newCredential->isValid())
137 credentials.insert(newCredential);
138 }
139 }
140
141 // generate hints for every authorization
142 AuthItemSet environmentToClient = environment;
143
144 RightAuthenticationLogger logger(auth.creatorAuditToken(), AUE_ssauthorize);
145
146 // create a vector with the first right first
147 std::vector<AuthItemRef> tempRights;
148 for (AuthItemSet::const_iterator it = inRights.begin(); it != inRights.end(); ++it) {
149 if (inRights.firstItemName != NULL && strcmp((*it)->name(), inRights.firstItemName) == 0)
150 tempRights.insert(tempRights.begin(), *it);
151 else
152 tempRights.push_back(*it);
153 }
154
155 bool authExtractPassword = false;
156 std::vector<AuthItemRef>::const_iterator end = tempRights.end();
157 for (std::vector<AuthItemRef>::const_iterator it = tempRights.begin(); it != end; ++it)
158 {
159 // Get the rule for each right we are trying to obtain.
160 const Rule &toplevelRule = mAuthdb.getRule(*it);
161
162 if (false == authExtractPassword)
163 authExtractPassword = toplevelRule->extractPassword();
164
165 string processName = "unknown";
166 string authCreatorName = "unknown";
167 {
168 StLock<Mutex> _(Server::process());
169 if (SecCodeRef code = Server::process().currentGuest()) {
170 CFRef<CFURLRef> path;
171 if (!SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref()))
172 processName = cfString(path);
173 }
174 }
175 authCreatorName = auth.creatorPath();
176
177 if (sandbox_check(Server::process().pid(), "authorization-right-obtain", SANDBOX_FILTER_RIGHT_NAME, (*it)->name())) {
178 Syslog::error("Sandbox denied authorizing right '%s' by client '%s' [%d]", (*it)->name(), processName.c_str(), Server::process().pid());
179 return errAuthorizationDenied;
180 }
181 if (auth.creatorSandboxed() && sandbox_check(auth.creatorPid(), "authorization-right-obtain", SANDBOX_FILTER_RIGHT_NAME, (*it)->name())) {
182 Syslog::error("Sandbox denied authorizing right '%s' for authorization created by '%s' [%d]", (*it)->name(), authCreatorName.c_str(), auth.creatorPid());
183 return errAuthorizationDenied;
184 }
185
186 OSStatus result = toplevelRule->evaluate(*it, toplevelRule, environmentToClient, flags, now, inCredentials, credentials, auth, reason, authExtractPassword);
187 secdebug("autheval", "evaluate rule %s for right %s returned %d.", toplevelRule->name().c_str(), (*it)->name(), int(result));
188 SECURITYD_AUTH_EVALRIGHT(&auth, (char *)(*it)->name(), result);
189
190 logger.setRight((*it)->name());
191 logger.logAuthorizationResult(processName.c_str(), authCreatorName.c_str(), result);
192
193 if (result == errAuthorizationSuccess)
194 {
195 outRights.insert(*it);
196 Syslog::info("Succeeded authorizing right '%s' by client '%s' [%d] for authorization created by '%s' [%d] (%X,%d)", (*it)->name(), processName.c_str(), Server::process().pid(), authCreatorName.c_str(), auth.creatorPid(), uint32_t(flags), auth.operatesAsLeastPrivileged());
197 }
198 else if (result == errAuthorizationDenied || result == errAuthorizationInteractionNotAllowed)
199 {
200 if (result == errAuthorizationDenied)
201 {
202 secdebug("autheval", "Failed to authorize right '%s' by client '%s' [%d] for authorization created by '%s' [%d] (%X,%d)", (*it)->name(), processName.c_str(), Server::process().pid(), authCreatorName.c_str(), auth.creatorPid(), uint32_t(flags), auth.operatesAsLeastPrivileged());
203 }
204
205 // add creator pid to authorization token
206 if (!(flags & kAuthorizationFlagPartialRights))
207 {
208 status = result;
209 break;
210 }
211 }
212 else if (result == errAuthorizationCanceled)
213 {
214 status = result;
215 break;
216 }
217 else
218 {
219 Syslog::error("Engine::authorize: Rule::evaluate returned %ld returning errAuthorizationInternal", result);
220 status = errAuthorizationInternal;
221 break;
222 }
223 }
224
225 // purge all uid credentials from the outCredentials for least privileged mode
226 if (auth.operatesAsLeastPrivileged()) {
227 CredentialSet::const_iterator current, it = outCredentials->begin();
228 while(it != outCredentials->end()) {
229 current = it++;
230 if (!(*current)->isRight()) {
231 outCredentials->erase(current);
232 }
233 }
234 }
235
236 if (outCredentials)
237 outCredentials->swap(credentials);
238
239 return status;
240 }
241
242 OSStatus
243 Engine::verifyModification(string inRightName, bool remove,
244 const CredentialSet *inCredentials, CredentialSet *outCredentials, AuthorizationToken &auth)
245 {
246 // Validate right
247
248 // meta rights are constructed as follows:
249 // we don't allow setting of wildcard rights, so you can only be more specific
250 // note that you should never restrict things with a wildcard right without disallowing
251 // changes to the entire domain. ie.
252 // system.privilege. -> never
253 // config.add.system.privilege. -> never
254 // config.modify.system.privilege. -> never
255 // config.delete.system.privilege. -> never
256 // For now we don't allow any configuration of configuration rules
257 // config.config. -> never
258
259 string rightnameToCheck;
260
261 // @@@ verify right name is is not NULL or zero length
262 if (inRightName.length() == 0)
263 return errAuthorizationDenied;
264
265 // @@@ make sure it isn't a wildcard right by checking trailing "."
266 if ( *(inRightName.rbegin()) == '.')
267 return errAuthorizationDenied;
268
269 // @@@ make sure it isn't a configure right by checking it doesn't start with config.
270 if (inRightName.find(kConfigRight, 0) != string::npos)
271 {
272 // special handling of meta right change:
273 // config.add. config.modify. config.remove. config.{}.
274 // check for config.<right> (which always starts with config.config.)
275 rightnameToCheck = string(kConfigRight) + inRightName;
276 }
277 else
278 {
279 // regular check of rights
280 bool existingRule = mAuthdb.existRule(inRightName);
281 if (!remove)
282 {
283 if (existingRule)
284 rightnameToCheck = string(kAuthorizationConfigRightModify) + inRightName;
285 else
286 rightnameToCheck = string(kAuthorizationConfigRightAdd) + inRightName;
287 }
288 else
289 {
290 if (existingRule)
291 rightnameToCheck = string(kAuthorizationConfigRightRemove) + inRightName;
292 else
293 {
294 secdebug("engine", "rule %s doesn't exist.", inRightName.c_str());
295 return errAuthorizationSuccess; // doesn't exist, done
296 }
297 }
298 }
299
300
301 AuthItemSet rights, environment, outRights;
302 rights.insert(AuthItemRef(rightnameToCheck.c_str()));
303 secdebug("engine", "authorizing %s for db modification.", rightnameToCheck.c_str());
304 return authorize(rights, environment, kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights, inCredentials, outCredentials, outRights, auth);
305 }
306
307 OSStatus
308 Engine::getRule(string &inRightName, CFDictionaryRef *outRuleDefinition)
309 {
310 // Get current time of day.
311 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
312
313 // Update rules from database if needed
314 mAuthdb.sync(now);
315
316 CFDictionaryRef definition = mAuthdb.getRuleDefinition(inRightName);
317 if (definition)
318 {
319 if (outRuleDefinition)
320 *outRuleDefinition = definition;
321 else
322 CFRelease(definition);
323
324 return errAuthorizationSuccess;
325 }
326
327 return errAuthorizationDenied;
328 }
329
330 OSStatus
331 Engine::setRule(const char *inRightName, CFDictionaryRef inRuleDefinition, const CredentialSet *inCredentials, CredentialSet *outCredentials, AuthorizationToken &auth)
332 {
333 // Validate rule by constructing it from the passed dictionary
334 if (!mAuthdb.validateRule(inRightName, inRuleDefinition))
335 return errAuthorizationDenied; // @@@ separate error for this?
336
337 OSStatus result = verifyModification(inRightName, false /*setting, not removing*/, inCredentials, outCredentials, auth);
338 if (result != errAuthorizationSuccess)
339 return result;
340
341 // set the rule for the right and save the database
342 mAuthdb.setRule(inRightName, inRuleDefinition);
343
344 return errAuthorizationSuccess;
345 }
346
347 OSStatus
348 Engine::removeRule(const char *inRightName, const CredentialSet *inCredentials, CredentialSet *outCredentials, AuthorizationToken &auth)
349 {
350 // Get current time of day.
351 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
352
353 // Update rules from database if needed
354 mAuthdb.sync(now);
355
356 OSStatus result = verifyModification(inRightName, true /*removing*/, inCredentials, outCredentials, auth);
357 if (result != errAuthorizationSuccess)
358 return result;
359
360 // set the rule for the right and save the database
361 mAuthdb.removeRule(inRightName);
362
363 return errAuthorizationSuccess;
364 }
365
366 } // end namespace Authorization