2 * Copyright (c) 2000-2004,2006-2012 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@
24 #include "AuthorizationEngine.h"
25 #include <security_cdsa_utilities/AuthorizationWalkers.h>
26 #include <Security/AuthorizationPriv.h>
27 #include <Security/AuthorizationDB.h>
29 #include "authority.h"
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>
38 #include <CoreFoundation/CFData.h>
39 #include <CoreFoundation/CFNumber.h>
40 #include <CoreFoundation/CFPropertyList.h>
47 #include <bsm/audit_uevents.h> // AUE_ssauth*
48 #include "ccaudit_extensions.h"
50 namespace Authorization
{
52 using namespace CommonCriteria::Securityd
;
56 // Errors to be thrown
58 Error::Error(int err
) : error(err
)
62 const char *Error::what() const throw()
63 { return "Authorization error"; }
65 int Error::unixError() const throw()
66 { return error
; } // @@@ eventually...
68 OSStatus
Error::osStatus() const throw()
71 void Error::throwMe(int err
) { throw Error(err
); }
76 Engine::Engine(const char *configFile
) : mAuthdb(configFile
)
86 @function AuthorizationEngine::authorize
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.
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
100 Engine::authorize(const AuthItemSet
&inRights
, const AuthItemSet
&environment
,
101 AuthorizationFlags flags
, const CredentialSet
*inCredentials
, CredentialSet
*outCredentials
,
102 AuthItemSet
&outRights
, AuthorizationToken
&auth
)
104 CredentialSet credentials
;
105 OSStatus status
= errAuthorizationSuccess
;
106 SecurityAgent::Reason reason
= SecurityAgent::noReason
;
108 // Get current time of day.
109 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
111 // Update rules from database if needed
114 // Check if a credential was passed into the environment and we were asked to extend the rights
115 if (flags
& kAuthorizationFlagExtendRights
)
117 string username
, password
;
119 for (AuthItemSet::iterator item
= environment
.begin(); item
!= environment
.end(); item
++)
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
))
129 if (username
.length())
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
);
141 // generate hints for every authorization
142 AuthItemSet environmentToClient
= environment
;
144 RightAuthenticationLogger
logger(auth
.creatorAuditToken(), AUE_ssauthorize
);
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
);
152 tempRights
.push_back(*it
);
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
)
159 // Get the rule for each right we are trying to obtain.
160 const Rule
&toplevelRule
= mAuthdb
.getRule(*it
);
162 if (false == authExtractPassword
)
163 authExtractPassword
= toplevelRule
->extractPassword();
165 string processName
= "unknown";
166 string authCreatorName
= "unknown";
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
);
175 authCreatorName
= auth
.creatorPath();
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
;
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
;
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
);
190 logger
.setRight((*it
)->name());
191 logger
.logAuthorizationResult(processName
.c_str(), authCreatorName
.c_str(), result
);
193 if (result
== errAuthorizationSuccess
)
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());
198 else if (result
== errAuthorizationDenied
|| result
== errAuthorizationInteractionNotAllowed
)
200 if (result
== errAuthorizationDenied
)
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());
205 // add creator pid to authorization token
206 if (!(flags
& kAuthorizationFlagPartialRights
))
212 else if (result
== errAuthorizationCanceled
)
219 Syslog::error("Engine::authorize: Rule::evaluate returned %ld returning errAuthorizationInternal", result
);
220 status
= errAuthorizationInternal
;
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()) {
230 if (!(*current
)->isRight()) {
231 outCredentials
->erase(current
);
237 outCredentials
->swap(credentials
);
243 Engine::verifyModification(string inRightName
, bool remove
,
244 const CredentialSet
*inCredentials
, CredentialSet
*outCredentials
, AuthorizationToken
&auth
)
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
259 string rightnameToCheck
;
261 // @@@ verify right name is is not NULL or zero length
262 if (inRightName
.length() == 0)
263 return errAuthorizationDenied
;
265 // @@@ make sure it isn't a wildcard right by checking trailing "."
266 if ( *(inRightName
.rbegin()) == '.')
267 return errAuthorizationDenied
;
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
)
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
;
279 // regular check of rights
280 bool existingRule
= mAuthdb
.existRule(inRightName
);
284 rightnameToCheck
= string(kAuthorizationConfigRightModify
) + inRightName
;
286 rightnameToCheck
= string(kAuthorizationConfigRightAdd
) + inRightName
;
291 rightnameToCheck
= string(kAuthorizationConfigRightRemove
) + inRightName
;
294 secdebug("engine", "rule %s doesn't exist.", inRightName
.c_str());
295 return errAuthorizationSuccess
; // doesn't exist, done
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
);
308 Engine::getRule(string
&inRightName
, CFDictionaryRef
*outRuleDefinition
)
310 // Get current time of day.
311 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
313 // Update rules from database if needed
316 CFDictionaryRef definition
= mAuthdb
.getRuleDefinition(inRightName
);
319 if (outRuleDefinition
)
320 *outRuleDefinition
= definition
;
322 CFRelease(definition
);
324 return errAuthorizationSuccess
;
327 return errAuthorizationDenied
;
331 Engine::setRule(const char *inRightName
, CFDictionaryRef inRuleDefinition
, const CredentialSet
*inCredentials
, CredentialSet
*outCredentials
, AuthorizationToken
&auth
)
333 // Validate rule by constructing it from the passed dictionary
334 if (!mAuthdb
.validateRule(inRightName
, inRuleDefinition
))
335 return errAuthorizationDenied
; // @@@ separate error for this?
337 OSStatus result
= verifyModification(inRightName
, false /*setting, not removing*/, inCredentials
, outCredentials
, auth
);
338 if (result
!= errAuthorizationSuccess
)
341 // set the rule for the right and save the database
342 mAuthdb
.setRule(inRightName
, inRuleDefinition
);
344 return errAuthorizationSuccess
;
348 Engine::removeRule(const char *inRightName
, const CredentialSet
*inCredentials
, CredentialSet
*outCredentials
, AuthorizationToken
&auth
)
350 // Get current time of day.
351 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
353 // Update rules from database if needed
356 OSStatus result
= verifyModification(inRightName
, true /*removing*/, inCredentials
, outCredentials
, auth
);
357 if (result
!= errAuthorizationSuccess
)
360 // set the rule for the right and save the database
361 mAuthdb
.removeRule(inRightName
);
363 return errAuthorizationSuccess
;
366 } // end namespace Authorization