X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/securityd/src/AuthorizationEngine.cpp diff --git a/securityd/src/AuthorizationEngine.cpp b/securityd/src/AuthorizationEngine.cpp new file mode 100644 index 00000000..c34507c4 --- /dev/null +++ b/securityd/src/AuthorizationEngine.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2000-2004,2006-2012 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "AuthorizationEngine.h" +#include +#include +#include + +#include "authority.h" + +#include +#include +#include +#include +#include +#include "server.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include // AUE_ssauth* +#include "ccaudit_extensions.h" + +namespace Authorization { + +using namespace CommonCriteria::Securityd; + + +// +// Errors to be thrown +// +Error::Error(int err) : error(err) +{ +} + +const char *Error::what() const throw() +{ return "Authorization error"; } + +int Error::unixError() const throw() +{ return error; } // @@@ eventually... + +OSStatus Error::osStatus() const throw() +{ return error; } + +void Error::throwMe(int err) { throw Error(err); } + +// +// Engine class +// +Engine::Engine(const char *configFile) : mAuthdb(configFile) +{ +} + +Engine::~Engine() +{ +} + + +/*! + @function AuthorizationEngine::authorize + + @@@. + + @param inRights (input) List of rights being requested for authorization. + @param environment (optional/input) Environment containing information to be used during evaluation. + @param flags (input) Optional flags @@@ see AuthorizationCreate for a description. + @param inCredentials (input) Credentials already held by the caller. + @param outCredentials (output/optional) Credentials obtained, used or refreshed during this call to authorize the requested rights. + @param outRights (output/optional) Subset of inRights which were actually authorized. + + @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 +*/ +OSStatus +Engine::authorize(const AuthItemSet &inRights, const AuthItemSet &environment, + AuthorizationFlags flags, const CredentialSet *inCredentials, CredentialSet *outCredentials, + AuthItemSet &outRights, AuthorizationToken &auth) +{ + CredentialSet credentials; + OSStatus status = errAuthorizationSuccess; + SecurityAgent::Reason reason = SecurityAgent::noReason; + + // Get current time of day. + CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); + + // Update rules from database if needed + mAuthdb.sync(now); + + // Check if a credential was passed into the environment and we were asked to extend the rights + if (flags & kAuthorizationFlagExtendRights) + { + string username, password; + bool shared = false; + for (AuthItemSet::iterator item = environment.begin(); item != environment.end(); item ++) + { + if (!strcmp((*item)->name(), kAuthorizationEnvironmentUsername)) + username = (*item)->stringValue(); + else if (!strcmp((*item)->name(), kAuthorizationEnvironmentPassword)) + password = (*item)->stringValue(); + else if (!strcmp((*item)->name(), kAuthorizationEnvironmentShared)) + shared = true; + } + + if (username.length()) + { + // Let's create a credential from the passed in username and password. + Credential newCredential(username, password, shared); + // If it's valid insert it into the credentials list. Normally this is + // only done if it actually authorizes a requested right, but for this + // special case (environment) we do it even when no rights are being requested. + if (newCredential->isValid()) + credentials.insert(newCredential); + } + } + + // generate hints for every authorization + AuthItemSet environmentToClient = environment; + + RightAuthenticationLogger logger(auth.creatorAuditToken(), AUE_ssauthorize); + + // create a vector with the first right first + std::vector tempRights; + for (AuthItemSet::const_iterator it = inRights.begin(); it != inRights.end(); ++it) { + if (inRights.firstItemName != NULL && strcmp((*it)->name(), inRights.firstItemName) == 0) + tempRights.insert(tempRights.begin(), *it); + else + tempRights.push_back(*it); + } + + bool authExtractPassword = false; + std::vector::const_iterator end = tempRights.end(); + for (std::vector::const_iterator it = tempRights.begin(); it != end; ++it) + { + // Get the rule for each right we are trying to obtain. + const Rule &toplevelRule = mAuthdb.getRule(*it); + + if (false == authExtractPassword) + authExtractPassword = toplevelRule->extractPassword(); + + string processName = "unknown"; + string authCreatorName = "unknown"; + { + StLock _(Server::process()); + if (SecCodeRef code = Server::process().currentGuest()) { + CFRef path; + if (!SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref())) + processName = cfString(path); + } + } + authCreatorName = auth.creatorPath(); + + if (sandbox_check(Server::process().pid(), "authorization-right-obtain", SANDBOX_FILTER_RIGHT_NAME, (*it)->name())) { + Syslog::error("Sandbox denied authorizing right '%s' by client '%s' [%d]", (*it)->name(), processName.c_str(), Server::process().pid()); + return errAuthorizationDenied; + } + if (auth.creatorSandboxed() && sandbox_check(auth.creatorPid(), "authorization-right-obtain", SANDBOX_FILTER_RIGHT_NAME, (*it)->name())) { + Syslog::error("Sandbox denied authorizing right '%s' for authorization created by '%s' [%d]", (*it)->name(), authCreatorName.c_str(), auth.creatorPid()); + return errAuthorizationDenied; + } + + OSStatus result = toplevelRule->evaluate(*it, toplevelRule, environmentToClient, flags, now, inCredentials, credentials, auth, reason, authExtractPassword); + secdebug("autheval", "evaluate rule %s for right %s returned %d.", toplevelRule->name().c_str(), (*it)->name(), int(result)); + SECURITYD_AUTH_EVALRIGHT(&auth, (char *)(*it)->name(), result); + + logger.setRight((*it)->name()); + logger.logAuthorizationResult(processName.c_str(), authCreatorName.c_str(), result); + + if (result == errAuthorizationSuccess) + { + outRights.insert(*it); + 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()); + } + else if (result == errAuthorizationDenied || result == errAuthorizationInteractionNotAllowed) + { + if (result == errAuthorizationDenied) + { + 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()); + } + + // add creator pid to authorization token + if (!(flags & kAuthorizationFlagPartialRights)) + { + status = result; + break; + } + } + else if (result == errAuthorizationCanceled) + { + status = result; + break; + } + else + { + Syslog::error("Engine::authorize: Rule::evaluate returned %ld returning errAuthorizationInternal", result); + status = errAuthorizationInternal; + break; + } + } + + // purge all uid credentials from the outCredentials for least privileged mode + if (auth.operatesAsLeastPrivileged()) { + CredentialSet::const_iterator current, it = outCredentials->begin(); + while(it != outCredentials->end()) { + current = it++; + if (!(*current)->isRight()) { + outCredentials->erase(current); + } + } + } + + if (outCredentials) + outCredentials->swap(credentials); + + return status; +} + +OSStatus +Engine::verifyModification(string inRightName, bool remove, + const CredentialSet *inCredentials, CredentialSet *outCredentials, AuthorizationToken &auth) +{ + // Validate right + + // meta rights are constructed as follows: + // we don't allow setting of wildcard rights, so you can only be more specific + // note that you should never restrict things with a wildcard right without disallowing + // changes to the entire domain. ie. + // system.privilege. -> never + // config.add.system.privilege. -> never + // config.modify.system.privilege. -> never + // config.delete.system.privilege. -> never + // For now we don't allow any configuration of configuration rules + // config.config. -> never + + string rightnameToCheck; + + // @@@ verify right name is is not NULL or zero length + if (inRightName.length() == 0) + return errAuthorizationDenied; + + // @@@ make sure it isn't a wildcard right by checking trailing "." + if ( *(inRightName.rbegin()) == '.') + return errAuthorizationDenied; + + // @@@ make sure it isn't a configure right by checking it doesn't start with config. + if (inRightName.find(kConfigRight, 0) != string::npos) + { + // special handling of meta right change: + // config.add. config.modify. config.remove. config.{}. + // check for config. (which always starts with config.config.) + rightnameToCheck = string(kConfigRight) + inRightName; + } + else + { + // regular check of rights + bool existingRule = mAuthdb.existRule(inRightName); + if (!remove) + { + if (existingRule) + rightnameToCheck = string(kAuthorizationConfigRightModify) + inRightName; + else + rightnameToCheck = string(kAuthorizationConfigRightAdd) + inRightName; + } + else + { + if (existingRule) + rightnameToCheck = string(kAuthorizationConfigRightRemove) + inRightName; + else + { + secdebug("engine", "rule %s doesn't exist.", inRightName.c_str()); + return errAuthorizationSuccess; // doesn't exist, done + } + } + } + + + AuthItemSet rights, environment, outRights; + rights.insert(AuthItemRef(rightnameToCheck.c_str())); + secdebug("engine", "authorizing %s for db modification.", rightnameToCheck.c_str()); + return authorize(rights, environment, kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights, inCredentials, outCredentials, outRights, auth); +} + +OSStatus +Engine::getRule(string &inRightName, CFDictionaryRef *outRuleDefinition) +{ + // Get current time of day. + CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); + + // Update rules from database if needed + mAuthdb.sync(now); + + CFDictionaryRef definition = mAuthdb.getRuleDefinition(inRightName); + if (definition) + { + if (outRuleDefinition) + *outRuleDefinition = definition; + else + CFRelease(definition); + + return errAuthorizationSuccess; + } + + return errAuthorizationDenied; +} + +OSStatus +Engine::setRule(const char *inRightName, CFDictionaryRef inRuleDefinition, const CredentialSet *inCredentials, CredentialSet *outCredentials, AuthorizationToken &auth) +{ + // Validate rule by constructing it from the passed dictionary + if (!mAuthdb.validateRule(inRightName, inRuleDefinition)) + return errAuthorizationDenied; // @@@ separate error for this? + + OSStatus result = verifyModification(inRightName, false /*setting, not removing*/, inCredentials, outCredentials, auth); + if (result != errAuthorizationSuccess) + return result; + + // set the rule for the right and save the database + mAuthdb.setRule(inRightName, inRuleDefinition); + + return errAuthorizationSuccess; +} + +OSStatus +Engine::removeRule(const char *inRightName, const CredentialSet *inCredentials, CredentialSet *outCredentials, AuthorizationToken &auth) +{ + // Get current time of day. + CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); + + // Update rules from database if needed + mAuthdb.sync(now); + + OSStatus result = verifyModification(inRightName, true /*removing*/, inCredentials, outCredentials, auth); + if (result != errAuthorizationSuccess) + return result; + + // set the rule for the right and save the database + mAuthdb.removeRule(inRightName); + + return errAuthorizationSuccess; +} + +} // end namespace Authorization