]> git.saurik.com Git - apple/security.git/blobdiff - securityd/src/AuthorizationEngine.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / securityd / src / AuthorizationEngine.cpp
diff --git a/securityd/src/AuthorizationEngine.cpp b/securityd/src/AuthorizationEngine.cpp
new file mode 100644 (file)
index 0000000..c34507c
--- /dev/null
@@ -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 <security_cdsa_utilities/AuthorizationWalkers.h>
+#include <Security/AuthorizationPriv.h>
+#include <Security/AuthorizationDB.h>
+
+#include "authority.h"
+
+#include <Security/AuthorizationTags.h>
+#include <Security/AuthorizationTagsPriv.h>
+#include <security_utilities/logging.h>
+#include <security_utilities/cfutilities.h>
+#include <security_utilities/debugging.h>
+#include "server.h"
+
+#include <CoreFoundation/CFData.h>
+#include <CoreFoundation/CFNumber.h>
+#include <CoreFoundation/CFPropertyList.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <float.h>
+#include <sandbox.h>
+
+#include <bsm/audit_uevents.h>      // 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<AuthItemRef>                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<AuthItemRef>::const_iterator end = tempRights.end();
+       for (std::vector<AuthItemRef>::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<Mutex> _(Server::process());
+               if (SecCodeRef code = Server::process().currentGuest()) {
+                   CFRef<CFURLRef> 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.<right> (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