/*
- * Copyright (c) 2003-2004 Apple Computer, Inc. All Rights Reserved.
+ * Copyright (c) 2003-2004,2008-2009 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
- * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
- *
* 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
#include <Security/AuthorizationTagsPriv.h>
#include <Security/AuthorizationDB.h>
#include <Security/AuthorizationPriv.h>
+#include <security_utilities/logging.h>
+#include <bsm/audit_uevents.h>
+#include "ccaudit_extensions.h"
#include "authority.h"
#include "server.h"
#include "process.h"
#include "agentquery.h"
#include "AuthorizationMechEval.h"
+#include <asl.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
+#include <membership.h>
+extern "C" {
+#include <membershipPriv.h>
+}
+using namespace CommonCriteria::Securityd;
+
//
// Rule class
//
CFStringRef RuleImpl::kSessionOwnerID = CFSTR(kAuthorizationRuleParameterCredentialSessionOwner);
CFStringRef RuleImpl::kKofNID = CFSTR(kAuthorizationRuleParameterKofN);
CFStringRef RuleImpl::kPromptID = CFSTR(kAuthorizationRuleParameterDefaultPrompt);
+CFStringRef RuleImpl::kButtonID = CFSTR(kAuthorizationRuleParameterDefaultButton);
CFStringRef RuleImpl::kTriesID = CFSTR("tries"); // XXX/cs move to AuthorizationTagsPriv.h
+CFStringRef RuleImpl::kExtractPasswordID = CFSTR(kAuthorizationRuleParameterExtractPassword);
CFStringRef RuleImpl::kRuleClassID = CFSTR(kAuthorizationRuleClass);
CFStringRef RuleImpl::kRuleAllowID = CFSTR(kAuthorizationRuleClassAllow);
CFStringRef RuleImpl::kRuleUserID = CFSTR(kAuthorizationRuleClassUser);
CFStringRef RuleImpl::kRuleDelegateID = CFSTR(kAuthorizationRightRule);
CFStringRef RuleImpl::kRuleMechanismsID = CFSTR(kAuthorizationRuleClassMechanisms);
+CFStringRef RuleImpl::kRuleAuthenticateUserID = CFSTR(kAuthorizationRuleParameterAuthenticateUser);
+
string
-RuleImpl::Attribute::getString(CFDictionaryRef config, CFStringRef key, bool required = false, char *defaultValue = NULL)
+RuleImpl::Attribute::getString(CFDictionaryRef config, CFStringRef key, bool required = false, const char *defaultValue = "")
{
CFTypeRef value = CFDictionaryGetValue(config, key);
if (value && (CFGetTypeID(value) == CFStringGetTypeID()))
if (CFStringGetCString(stringValue, buffer, sizeof(buffer), kCFStringEncodingUTF8))
ptr = buffer;
else
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+ {
+ Syslog::alert("Could not convert CFString to C string");
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
}
return string(ptr);
if (!required)
return string(defaultValue);
else
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+ {
+ Syslog::alert("Failed to get rule string");
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
}
double
if (!required)
return defaultValue;
else
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+ {
+ Syslog::alert("Failed to get rule double value");
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
return doubleValue;
}
if (!required)
return defaultValue;
else
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+ {
+ Syslog::alert("Failed to get rule bool value");
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
return boolValue;
}
-// add reference to string that we're modifying
-void
-RuleImpl::Attribute::setString(CFMutableDictionaryRef config, CFStringRef key, string &value)
-{
- CFStringRef cfstringValue = CFStringCreateWithCString(NULL /*allocator*/, value.c_str(), kCFStringEncodingUTF8);
-
- if (cfstringValue)
- {
- CFDictionarySetValue(config, key, cfstringValue);
- CFRelease(cfstringValue);
- }
- else
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid attribute
-}
-
-void
-RuleImpl::Attribute::setDouble(CFMutableDictionaryRef config, CFStringRef key, double value)
-{
- CFNumberRef doubleValue = CFNumberCreate(NULL /*allocator*/, kCFNumberDoubleType, doubleValue);
-
- if (doubleValue)
- {
- CFDictionarySetValue(config, key, doubleValue);
- CFRelease(doubleValue);
- }
- else
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid attribute
-}
-
-void
-RuleImpl::Attribute::setBool(CFMutableDictionaryRef config, CFStringRef key, bool value)
-{
- if (value)
- CFDictionarySetValue(config, key, kCFBooleanTrue);
- else
- CFDictionarySetValue(config, key, kCFBooleanFalse);
-}
-
vector<string>
RuleImpl::Attribute::getVector(CFDictionaryRef config, CFStringRef key, bool required = false)
{
{
CFArrayRef evalArray = reinterpret_cast<CFArrayRef>(value);
- for (int index=0; index < CFArrayGetCount(evalArray); index++)
+ CFIndex numItems = CFArrayGetCount(evalArray);
+ for (CFIndex index=0; index < numItems; index++)
{
CFTypeRef arrayValue = CFArrayGetValueAtIndex(evalArray, index);
if (arrayValue && (CFGetTypeID(arrayValue) == CFStringGetTypeID()))
if (CFStringGetCString(stringValue, buffer, sizeof(buffer), kCFStringEncodingUTF8))
ptr = buffer;
else
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+ {
+ Syslog::alert("Failed to convert CFString to C string for item %u in array", index);
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
}
valueArray.push_back(string(ptr));
}
}
else
if (required)
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+ {
+ Syslog::alert("Value for key either not present or not a CFArray");
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
return valueArray;
}
-bool RuleImpl::Attribute::getLocalizedPrompts(CFDictionaryRef config, map<string,string> &localizedPrompts)
+bool RuleImpl::Attribute::getLocalizedText(CFDictionaryRef config, map<string,string> &localizedPrompts, CFStringRef dictKey, const char *descriptionKey)
{
CFIndex numberOfPrompts = 0;
CFDictionaryRef promptsDict;
- if (CFDictionaryContainsKey(config, kPromptID))
+ if (CFDictionaryContainsKey(config, dictKey))
{
- promptsDict = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(config, kPromptID));
+ promptsDict = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(config, dictKey));
if (promptsDict && (CFGetTypeID(promptsDict) == CFDictionaryGetTypeID()))
numberOfPrompts = CFDictionaryGetCount(promptsDict);
}
{
CFStringRef keyRef = reinterpret_cast<CFStringRef>(keys[numberOfPrompts]);
CFStringRef valueRef = reinterpret_cast<CFStringRef>(values[numberOfPrompts]);
- if (!keyRef || (CFGetTypeID(keyRef) != CFStringGetTypeID()))
+ if (!keyRef || (CFGetTypeID(keyRef) != CFStringGetTypeID())) {
continue;
- if (!valueRef || (CFGetTypeID(valueRef) != CFStringGetTypeID()))
+ }
+ if (!valueRef || (CFGetTypeID(valueRef) != CFStringGetTypeID())) {
continue;
+ }
string key = cfString(keyRef);
string value = cfString(valueRef);
- localizedPrompts[kAuthorizationRuleParameterDescription+key] = value;
+ localizedPrompts[descriptionKey + key] = value;
}
return true;
// default rule
RuleImpl::RuleImpl() :
-mType(kUser), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false), mSessionOwner(false), mTries(0)
+mType(kUser), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false), mSessionOwner(false), mTries(0), mAuthenticateUser(true), mExtractPassword(false)
{
// XXX/cs read default descriptions from somewhere
// @@@ Default rule is shared admin group with 5 minute timeout
}
// return rule built from rule definition; throw if invalid.
-RuleImpl::RuleImpl(const string &inRightName, CFDictionaryRef cfRight, CFDictionaryRef cfRules) : mRightName(inRightName)
+RuleImpl::RuleImpl(const string &inRightName, CFDictionaryRef cfRight, CFDictionaryRef cfRules) : mRightName(inRightName), mExtractPassword(false)
{
// @@@ make sure cfRight is non mutable and never used that way
if (CFGetTypeID(cfRight) != CFDictionaryGetTypeID())
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+ {
+ Syslog::alert("Invalid rights set");
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
mTries = 0;
mSessionOwner = Attribute::getBool(cfRight, kSessionOwnerID);
// authorization tags can have eval now too
mEvalDef = Attribute::getVector(cfRight, kMechanismsID);
- mTries = int(Attribute::getDouble(cfRight, kTriesID, false, 3.0)); // XXX/cs double(kAuthorizationMaxTries)
+ if (mEvalDef.size() == 0 && cfRules /*only rights default see appserver-admin*/)
+ {
+ CFDictionaryRef cfRuleDef = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(cfRules, CFSTR("authenticate")));
+ if (cfRuleDef && CFGetTypeID(cfRuleDef) == CFDictionaryGetTypeID())
+ mEvalDef = Attribute::getVector(cfRuleDef, kMechanismsID);
+ }
+ mTries = int(Attribute::getDouble(cfRight, kTriesID, false, double(kMaximumAuthorizationTries)));
+ mAuthenticateUser = Attribute::getBool(cfRight, kRuleAuthenticateUserID, false, true);
+ mExtractPassword = Attribute::getBool(cfRight, kExtractPasswordID, false, false);
secdebug("authrule", "%s : rule user in group \"%s\" timeout %g%s%s",
inRightName.c_str(),
// mechanisms to evaluate
mEvalDef = Attribute::getVector(cfRight, kMechanismsID, true);
mTries = int(Attribute::getDouble(cfRight, kTriesID, false, 0.0)); // "forever"
+ mShared = Attribute::getBool(cfRight, kSharedID, false, true);
+ mExtractPassword = Attribute::getBool(cfRight, kExtractPasswordID, false, false);
}
else if (classTag == kAuthorizationRightRule)
{
- assert(cfRules); // this had better not be a rule
+ assert(cfRules); // rules can't delegate to other rules
secdebug("authrule", "%s : rule delegate rule", inRightName.c_str());
mType = kRuleDelegation;
if (ruleDefRef)
CFRelease(ruleDefRef);
if (!cfRuleDef || CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID())
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
- mRuleDef.push_back(Rule(ruleDefString, cfRuleDef, NULL));
+ {
+ Syslog::alert("'%s' does not name a built-in rule", ruleDefString.c_str());
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
+ mRuleDef.push_back(Rule(ruleDefString, cfRuleDef, cfRules));
}
else // array
{
if (ruleNameRef)
CFRelease(ruleNameRef);
if (!cfRuleDef || (CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID()))
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
- mRuleDef.push_back(Rule(*it, cfRuleDef, NULL));
+ {
+ Syslog::alert("Invalid rule '%s'in rule set", it->c_str());
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
+ mRuleDef.push_back(Rule(*it, cfRuleDef, cfRules));
}
}
}
else
{
- secdebug("authrule", "%s : rule class unknown %s.", inRightName.c_str(), classTag.c_str());
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+ secdebug("authrule", "%s : rule class '%s' unknown.", inRightName.c_str(), classTag.c_str());
+ Syslog::alert("%s : rule class '%s' unknown", inRightName.c_str(), classTag.c_str());
+ MacOSError::throwMe(errAuthorizationInternal);
}
}
else
// it _must_ have a definition for "rule" which will be used as a delegate
// it may have a comment (not extracted here)
// it may have a default prompt, or a whole dictionary of languages (not extracted here)
- assert(cfRules);
mType = kRuleDelegation;
string ruleName = Attribute::getString(cfRight, kRuleDelegateID, true);
secdebug("authrule", "%s : rule delegate rule (1): %s", inRightName.c_str(), ruleName.c_str());
if (ruleNameRef)
CFRelease(ruleNameRef);
if (!cfRuleDef || CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID())
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
- mRuleDef.push_back(Rule(ruleName, cfRuleDef, NULL));
+ {
+ Syslog::alert("Rule '%s' for right '%s' does not exist or is not properly formed", ruleName.c_str(), inRightName.c_str());
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
+ mRuleDef.push_back(Rule(ruleName, cfRuleDef, cfRules));
}
- Attribute::getLocalizedPrompts(cfRight, mLocalizedPrompts);
+ Attribute::getLocalizedText(cfRight, mLocalizedPrompts, kPromptID, kAuthorizationRuleParameterDescription);
+ Attribute::getLocalizedText(cfRight, mLocalizedButtons, kButtonID, kAuthorizationRuleParameterButton);
}
/*
RuleImpl::setAgentHints(const AuthItemRef &inRight, const Rule &inTopLevelRule, AuthItemSet &environmentToClient, AuthorizationToken &auth) const
{
string authorizeString(inRight->name());
+ environmentToClient.erase(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT));
environmentToClient.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT, AuthValueOverlay(authorizeString)));
-
- // XXX/cs pid/uid/client should only be added when we're ready to call the agent
- pid_t cPid = Server::process().pid();
- environmentToClient.insert(AuthItemRef(AGENT_HINT_CLIENT_PID, AuthValueOverlay(sizeof(pid_t), &cPid)));
-
- uid_t cUid = auth.creatorUid();
- environmentToClient.insert(AuthItemRef(AGENT_HINT_CLIENT_UID, AuthValueOverlay(sizeof(uid_t), &cUid)));
pid_t creatorPid = auth.creatorPid();
+ environmentToClient.erase(AuthItemRef(AGENT_HINT_CREATOR_PID));
environmentToClient.insert(AuthItemRef(AGENT_HINT_CREATOR_PID, AuthValueOverlay(sizeof(pid_t), &creatorPid)));
-
- {
- CodeSigning::OSXCode *osxcode = auth.creatorCode();
- if (!osxcode)
- MacOSError::throwMe(errAuthorizationDenied);
-
- string encodedBundle = osxcode->encode();
- char bundleType = (encodedBundle.c_str())[0]; // yay, no accessor
- string bundlePath = osxcode->canonicalPath();
-
- environmentToClient.insert(AuthItemRef(AGENT_HINT_CLIENT_TYPE, AuthValueOverlay(sizeof(bundleType), &bundleType)));
- environmentToClient.insert(AuthItemRef(AGENT_HINT_CLIENT_PATH, AuthValueOverlay(bundlePath)));
- }
-
+
+ audit_token_t creatorAuditToken = auth.creatorAuditToken().auditToken();
+ environmentToClient.erase(AuthItemRef(AGENT_HINT_CREATOR_AUDIT_TOKEN));
+ environmentToClient.insert(AuthItemRef(AGENT_HINT_CREATOR_AUDIT_TOKEN, AuthValueOverlay(sizeof(audit_token_t), &creatorAuditToken)));
+
+ Process &thisProcess = Server::process();
+ string bundlePath;
+ if (SecStaticCodeRef clientCode = auth.creatorCode())
+ bundlePath = codePath(clientCode);
+ AuthItemSet processHints = SecurityAgent::Client::clientHints(
+ SecurityAgent::bundle, bundlePath, thisProcess.pid(), thisProcess.uid());
+ environmentToClient.erase(AuthItemRef(AGENT_HINT_CLIENT_TYPE));
+ environmentToClient.erase(AuthItemRef(AGENT_HINT_CLIENT_PATH));
+ environmentToClient.erase(AuthItemRef(AGENT_HINT_CLIENT_PID));
+ environmentToClient.erase(AuthItemRef(AGENT_HINT_CLIENT_UID));
+ environmentToClient.insert(processHints.begin(), processHints.end());
+
map<string,string> defaultPrompts = inTopLevelRule->localizedPrompts();
+ map<string,string> defaultButtons = inTopLevelRule->localizedButtons();
if (defaultPrompts.empty())
defaultPrompts = localizedPrompts();
+ if (defaultButtons.empty())
+ defaultButtons = localizedButtons();
if (!defaultPrompts.empty())
{
environmentToClient.insert(AuthItemRef(key.c_str(), AuthValueOverlay(value)));
}
}
+ if (!defaultButtons.empty())
+ {
+ map<string,string>::const_iterator it;
+ for (it = defaultButtons.begin(); it != defaultButtons.end(); it++)
+ {
+ const string &key = it->first;
+ const string &value = it->second;
+ environmentToClient.insert(AuthItemRef(key.c_str(), AuthValueOverlay(value)));
+ }
+ }
// add rulename as a hint
string ruleName = name();
+ environmentToClient.erase(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE));
environmentToClient.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE, AuthValueOverlay(ruleName)));
}
-string
-RuleImpl::agentNameForAuth(const AuthorizationToken &auth) const
+// If a different evaluation for getting a credential is prescribed,
+// we'll run that and validate the credentials from there.
+// we fall back on a default configuration from the authenticate rule
+OSStatus
+RuleImpl::evaluateAuthentication(const AuthItemRef &inRight, const Rule &inRule,AuthItemSet &environmentToClient, AuthorizationFlags flags, CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials, AuthorizationToken &auth, SecurityAgent::Reason &reason, bool savePassword) const
{
- uint8_t hash[20];
- AuthorizationBlob authBlob = auth.handle();
- CssmData hashedData = CssmData::wrap(&hash, sizeof(hash));
- CssmData data = CssmData::wrap(&authBlob, sizeof(authBlob));
- CssmClient::Digest ctx(Server::csp(), CSSM_ALGID_SHA1);
- try {
- ctx.digest(data, hashedData);
+ OSStatus status = errAuthorizationDenied;
+
+ Credential hintCredential;
+ if (errAuthorizationSuccess == evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, hintCredential, reason)) {
+ if (hintCredential->name().length())
+ environmentToClient.insert(AuthItemRef(AGENT_HINT_SUGGESTED_USER, AuthValueOverlay(hintCredential->name())));
+ if (hintCredential->realname().length())
+ environmentToClient.insert(AuthItemRef(AGENT_HINT_SUGGESTED_USER_LONG, AuthValueOverlay(hintCredential->realname())));
}
- catch (CssmError &e)
- {
- secdebug("auth", "digesting authref failed (%lu)", e.osStatus());
- return string("SecurityAgentMechanism");
+
+ if ((mType == kUser) && (mGroupName.length()))
+ environmentToClient.insert(AuthItemRef(AGENT_HINT_REQUIRE_USER_IN_GROUP, AuthValueOverlay(mGroupName)));
+
+ uint32 tries;
+ reason = SecurityAgent::noReason;
+
+ Process &cltProc = Server::process();
+ // Authorization preserves creator's UID in setuid processes
+ // (which is nice, but cltUid ends up being unused except by the debug
+ // message -- AgentMechanismEvaluator ignores it)
+ uid_t cltUid = (cltProc.uid() != 0) ? cltProc.uid() : auth.creatorUid();
+ secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc.pid(), cltUid);
+
+ // For auditing within AuthorizationMechEval, pass the right name.
+ size_t rightNameSize = inRight->name() ? strlen(inRight->name()) : 0;
+ AuthorizationString rightName = inRight->name() ? inRight->name() : "";
+ // @@@ AuthValueRef's ctor ought to take a const void *
+ AuthValueRef rightValue(rightNameSize, const_cast<char *>(rightName));
+ AuthValueVector authValueVector;
+ authValueVector.push_back(rightValue);
+
+ RightAuthenticationLogger rightAuthLogger(auth.creatorAuditToken(), AUE_ssauthint);
+ rightAuthLogger.setRight(rightName);
+
+ // Just succeed for a continuously active session owner.
+ if (auth.session().originatorUid() == auth.creatorUid() && auth.session().attributes() & AU_SESSION_FLAG_HAS_AUTHENTICATED) {
+ secdebug("AuthEvalMech", "We are an active session owner.");
+ aslmsg m = asl_new(ASL_TYPE_MSG);
+ asl_set(m, "com.apple.message.domain", "com.apple.securityd.UserActivity");
+ asl_set(m, "com.apple.message.signature", "userIsActive");
+ asl_set(m, "com.apple.message.signature2", rightName);
+ asl_set(m, "com.apple.message.result", "failure");
+ asl_log(NULL, m, ASL_LEVEL_NOTICE, "We are an active session owner.");
+ asl_free(m);
+// Credential rightCredential(rightName, auth.creatorUid(), mShared);
+// credentials.erase(rightCredential); credentials.insert(rightCredential);
+// return errAuthorizationSuccess;
}
-
- uint8_t *point = static_cast<uint8_t*>(hashedData.data());
- for (uint8_t i=0; i < hashedData.length(); point++, i++)
- {
- uint8 value = (*point % 62) + '0';
- if (value > '9') value += 7;
- if (value > 'Z') value += 6;
- *point = value;
+ else {
+ secdebug("AuthEvalMech", "We are not an active session owner.");
+ aslmsg m = asl_new(ASL_TYPE_MSG);
+ asl_set(m, "com.apple.message.domain", "com.apple.securityd.UserActivity");
+ asl_set(m, "com.apple.message.signature", "userIsNotActive");
+ asl_set(m, "com.apple.message.signature2", rightName);
+ asl_set(m, "com.apple.message.result", "success");
+ asl_log(NULL, m, ASL_LEVEL_NOTICE, "We are not an active session owner.");
+ asl_free(m);
}
- return string(static_cast<char *>(hashedData.data()), hashedData.length());
-}
-
-OSStatus
-RuleImpl::evaluateAuthorization(const AuthItemRef &inRight, const Rule &inRule,
- AuthItemSet &environmentToClient,
- AuthorizationFlags flags, CFAbsoluteTime now,
- const CredentialSet *inCredentials,
- CredentialSet &credentials, AuthorizationToken &auth) const
-{
- OSStatus status = errAuthorizationDenied;
-
- string usernamehint;
- evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, usernamehint);
- if (usernamehint.length())
- environmentToClient.insert(AuthItemRef(AGENT_HINT_SUGGESTED_USER, AuthValueOverlay(usernamehint)));
-
- if ((mType == kUser) && (mGroupName.length()))
- environmentToClient.insert(AuthItemRef(AGENT_HINT_REQUIRE_USER_IN_GROUP, AuthValueOverlay(mGroupName)));
-
- uint32 tries;
- SecurityAgent::Reason reason = SecurityAgent::noReason;
+
+ AgentMechanismEvaluator eval(cltUid, auth.session(), mEvalDef);
- Process &cltProc = Server::process();
- // Authorization preserves creator's UID in setuid processes
- uid_t cltUid = (cltProc.uid() != 0) ? cltProc.uid() : auth.creatorUid();
- secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc.pid(), cltUid);
-
- AgentMechanismEvaluator eval(cltUid, cltProc.session(), mEvalDef);
-
- for (tries = 0; tries < mTries; tries++)
- {
+ for (tries = 0; tries < mTries; tries++)
+ {
AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
environmentToClient.erase(retryHint); environmentToClient.insert(retryHint); // replace
AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
environmentToClient.erase(triesHint); environmentToClient.insert(triesHint); // replace
- status = eval.run(AuthValueVector(), environmentToClient, auth.infoSet());
+ status = eval.run(authValueVector, environmentToClient, auth);
- if ((status == errAuthorizationSuccess) ||
- (status == errAuthorizationCanceled)) // @@@ can only pass back sideband through context
- {
- secdebug("AuthEvalMech", "storing new context for authorization");
- auth.setInfoSet(eval.context());
- }
-
- // successfully ran mechanisms to obtain credential
- if (status == errAuthorizationSuccess)
- {
- // deny is the default
- status = errAuthorizationDenied;
-
- // fetch context and construct a credential to be tested
- AuthItemSet inContext = auth.infoSet();
- CredentialSet newCredentials = makeCredentials(inContext);
- // clear context after extracting credentials
- auth.clearInfoSet();
-
- for (CredentialSet::const_iterator it = newCredentials.begin(); it != newCredentials.end(); ++it)
+ if ((status == errAuthorizationSuccess) ||
+ (status == errAuthorizationCanceled)) // @@@ can only pass back sideband through context
{
- const Credential& newCredential = *it;
-
- // @@@ we log the uid a process was running under when it created the authref, which is misleading in the case of loginwindow
- if (newCredential->isValid())
- Syslog::info("uid %lu succeeded authenticating as user %s (uid %lu) for right %s.", auth.creatorUid(), newCredential->username().c_str(), newCredential->uid(), inRight->name());
- else
- // we can't be sure that the user actually exists so inhibit logging of uid
- Syslog::error("uid %lu failed to authenticate as user %s for right %s.", auth.creatorUid(), newCredential->username().c_str(), inRight->name());
+ secdebug("AuthEvalMech", "storing new context for authorization");
+ auth.setInfoSet(eval.context(), savePassword);
+ }
+
+ // successfully ran mechanisms to obtain credential
+ if (status == errAuthorizationSuccess)
+ {
+ // deny is the default
+ status = errAuthorizationDenied;
+
+ CredentialSet newCredentials = makeCredentials(auth);
+ // clear context after extracting credentials
+ auth.scrubInfoSet(savePassword);
- if (!newCredential->isValid())
+ for (CredentialSet::const_iterator it = newCredentials.begin(); it != newCredentials.end(); ++it)
{
- reason = SecurityAgent::invalidPassphrase; //invalidPassphrase;
- continue;
+ const Credential& newCredential = *it;
+
+ // @@@ we log the uid a process was running under when it created the authref, which is misleading in the case of loginwindow
+ if (newCredential->isValid()) {
+ Syslog::info("UID %u authenticated as user %s (UID %u) for right '%s'", auth.creatorUid(), newCredential->name().c_str(), newCredential->uid(), rightName);
+ rightAuthLogger.logSuccess(auth.creatorUid(), newCredential->uid(), newCredential->name().c_str());
+ } else {
+ // we can't be sure that the user actually exists so inhibit logging of uid
+ Syslog::error("UID %u failed to authenticate as user '%s' for right '%s'", auth.creatorUid(), newCredential->name().c_str(), rightName);
+ rightAuthLogger.logFailure(auth.creatorUid(), newCredential->name().c_str());
+ }
+
+ if (!newCredential->isValid())
+ {
+ reason = SecurityAgent::invalidPassphrase;
+ continue;
+ }
+
+ // verify that this credential authorizes right
+ status = evaluateUserCredentialForRight(auth, inRight, inRule, environmentToClient, now, newCredential, true, reason);
+
+ if (status == errAuthorizationSuccess)
+ {
+ if (auth.operatesAsLeastPrivileged()) {
+ Credential rightCredential(rightName, mShared);
+ credentials.erase(rightCredential); credentials.insert(rightCredential);
+ if (mShared)
+ credentials.insert(Credential(rightName, false));
+ }
+
+ // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
+ credentials.erase(newCredential); credentials.insert(newCredential);
+ // just got a new credential - if it's shared also add a non-shared one that to stick in the authorizationref local cache
+ if (mShared)
+ credentials.insert(Credential(newCredential->uid(), newCredential->name(), newCredential->realname(), false));
+
+ // use valid credential to set context info
+ // XXX/cs keeping this for now, such that the uid is passed back
+ auth.setCredentialInfo(newCredential, savePassword);
+ secdebug("SSevalMech", "added valid credential for user %s", newCredential->name().c_str());
+ // set the sessionHasAuthenticated
+ if (newCredential->uid() == auth.session().originatorUid()) {
+ secdebug("AuthEvalMech", "We authenticated as the session owner.\n");
+ SessionAttributeBits flags = auth.session().attributes();
+ flags |= AU_SESSION_FLAG_HAS_AUTHENTICATED;
+ auth.session().setAttributes(flags);
+ }
+
+ status = errAuthorizationSuccess;
+ break;
+ }
}
-
- // verify that this credential authorizes right
- status = evaluateCredentialForRight(inRight, inRule, environmentToClient, now, newCredential, true);
- if (status == errAuthorizationSuccess)
- {
- // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
- credentials.erase(newCredential); credentials.insert(newCredential);
- // use valid credential to set context info
- auth.setCredentialInfo(newCredential);
- secdebug("SSevalMech", "added valid credential for user %s", newCredential->username().c_str());
- status = errAuthorizationSuccess;
- break;
- }
- else
- reason = SecurityAgent::userNotInGroup; //unacceptableUser; // userNotInGroup
- }
-
- if (status == errAuthorizationSuccess)
- break;
+ if (status == errAuthorizationSuccess)
+ break;
+ }
+ else
+ if ((status == errAuthorizationCanceled) || (status == errAuthorizationInternal))
+ {
+ auth.scrubInfoSet(false);
+ break;
+ }
+ else // last mechanism is now authentication - fail
+ if (status == errAuthorizationDenied)
+ reason = SecurityAgent::invalidPassphrase;
}
- else
- if ((status == errAuthorizationCanceled) ||
- (status == errAuthorizationInternal))
- {
- auth.clearInfoSet();
- break;
- }
- }
- // If we fell out of the loop because of too many tries, notify user
- if (tries == mTries)
- {
- reason = SecurityAgent::tooManyTries;
+ // If we fell out of the loop because of too many tries, notify user
+ if (tries == mTries)
+ {
+ reason = SecurityAgent::tooManyTries;
AuthItemRef retryHint (AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
environmentToClient.erase(retryHint); environmentToClient.insert(retryHint); // replace
AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
environmentToClient.erase(triesHint); environmentToClient.insert(triesHint); // replace
- eval.run(AuthValueVector(), environmentToClient, auth.infoSet());
- // XXX/cs is this still necessary?
- auth.clearInfoSet();
- }
+ eval.run(AuthValueVector(), environmentToClient, auth);
+ // XXX/cs is this still necessary?
+ auth.scrubInfoSet(false);
+
+ rightAuthLogger.logFailure(NULL, CommonCriteria::errTooManyTries);
+ }
- return status;
+ return status;
}
// create externally verified credentials on the basis of
// mechanism-provided information
CredentialSet
-RuleImpl::makeCredentials(const AuthItemSet &context) const
+RuleImpl::makeCredentials(const AuthorizationToken &auth) const
{
- CredentialSet newCredentials;
-
- do {
- AuthItemSet::const_iterator found = find_if(context.begin(), context.end(), FindAuthItemByRightName(kAuthorizationEnvironmentUsername) );
- if (found == context.end())
- break;
- string username = (**found).stringValue();
- secdebug("AuthEvalMech", "found username");
-
- const uid_t *uid = NULL;
- found = find_if(context.begin(), context.end(), FindAuthItemByRightName("uid") );
- if (found != context.end())
- {
- uid = static_cast<const uid_t *>((**found).value().data);
- secdebug("AuthEvalMech", "found uid");
- }
-
- const gid_t *gid = NULL;
- found = find_if(context.begin(), context.end(), FindAuthItemByRightName("gid") );
- if (found != context.end())
- {
- gid = static_cast<const gid_t *>((**found).value().data);
- secdebug("AuthEvalMech", "found gid");
- }
+ // fetch context and construct a credential to be tested
+ const AuthItemSet &context = const_cast<AuthorizationToken &>(auth).infoSet();
+ CredentialSet newCredentials;
+
+ do {
+ AuthItemSet::const_iterator found = find_if(context.begin(), context.end(), FindAuthItemByRightName(kAuthorizationEnvironmentUsername) );
+ if (found == context.end())
+ break;
+ string username = (**found).stringValue();
+ secdebug("AuthEvalMech", "found username");
+
+ const uid_t *uid = NULL;
+ found = find_if(context.begin(), context.end(), FindAuthItemByRightName("uid") );
+ if (found != context.end())
+ {
+ uid = static_cast<const uid_t *>((**found).value().data);
+ secdebug("AuthEvalMech", "found uid");
+ }
- if (username.length() && uid && gid)
- {
- // credential is valid because mechanism says so
- newCredentials.insert(Credential(username, *uid, *gid, mShared));
- }
- else
- {
- found = find_if(context.begin(), context.end(), FindAuthItemByRightName(kAuthorizationEnvironmentPassword) );
- if (found != context.end())
- {
- secdebug("AuthEvalMech", "found password");
- string password = (**found).stringValue();
- secdebug("AuthEvalMech", "falling back on username/password credential if valid");
- newCredentials.insert(Credential(username, password, mShared));
- }
- }
- } while(0);
+ if (username.length() && uid)
+ {
+ // credential is valid because mechanism says so
+ newCredentials.insert(Credential(*uid, username, "", mShared));
+ }
+ } while(0);
- return newCredentials;
+ return newCredentials;
}
// evaluate whether a good credential of the current session owner would authorize a right
OSStatus
-RuleImpl::evaluateSessionOwner(const AuthItemRef &inRight, const Rule &inRule,
- const AuthItemSet &environment,
- const CFAbsoluteTime now,
- const AuthorizationToken &auth,
- string& usernamehint) const
+RuleImpl::evaluateSessionOwner(const AuthItemRef &inRight, const Rule &inRule, const AuthItemSet &environment, const CFAbsoluteTime now, const AuthorizationToken &auth, Credential &credential, SecurityAgent::Reason &reason) const
{
- // username hint is taken from the user who created the authorization, unless it's clearly ineligible
- OSStatus status = noErr;
+ // username hint is taken from the user who created the authorization, unless it's clearly ineligible
// @@@ we have no access to current requester uid here and the process uid is only taken when the authorization is created
// meaning that a process like loginwindow that drops privs later is screwed.
- uid_t uid = auth.creatorUid();
+ Credential sessionCredential;
+ uid_t uid = auth.session().originatorUid();
Server::active().longTermActivity();
struct passwd *pw = getpwuid(uid);
- if (pw != NULL)
- {
- // avoid hinting a locked account (ie. root)
+ if (pw != NULL) {
+ // avoid hinting a locked account
if ( (pw->pw_passwd == NULL) ||
- strcmp(pw->pw_passwd, "*") ) {
+ strcmp(pw->pw_passwd, "*") ) {
// Check if username will authorize the request and set username to
// be used as a hint to the user if so
- status = evaluateCredentialForRight(inRight, inRule, environment, now, Credential(pw->pw_name, pw->pw_uid, pw->pw_gid, mShared), true);
-
- if (status == errAuthorizationSuccess)
- usernamehint = pw->pw_name;
+ secdebug("AuthEvalMech", "preflight credential from current user, result follows:");
+ sessionCredential = Credential(pw->pw_uid, pw->pw_name, pw->pw_gecos, mShared/*ignored*/);
} //fi
endpwent();
}
+ OSStatus status = evaluateUserCredentialForRight(auth, inRight, inRule, environment, now, sessionCredential, true, reason);
+ if (errAuthorizationSuccess == status)
+ credential = sessionCredential;
+
return status;
}
+OSStatus
+RuleImpl::evaluateCredentialForRight(const AuthorizationToken &auth, const AuthItemRef &inRight, const Rule &inRule, const AuthItemSet &environment, CFAbsoluteTime now, const Credential &credential, bool ignoreShared, SecurityAgent::Reason &reason) const
+{
+ if (auth.operatesAsLeastPrivileged()) {
+ if (credential->isRight() && credential->isValid() && (inRight->name() == credential->name()))
+ {
+ if (!ignoreShared && !mShared && credential->isShared())
+ {
+ // @@@ no proper SA::Reason
+ reason = SecurityAgent::unknownReason;
+ secdebug("autheval", "shared credential cannot be used, denying right %s", inRight->name());
+ return errAuthorizationDenied;
+ } else {
+ return errAuthorizationSuccess;
+ }
+ } else {
+ // @@@ no proper SA::Reason
+ reason = SecurityAgent::unknownReason;
+ return errAuthorizationDenied;
+ }
+ } else
+ return evaluateUserCredentialForRight(auth, inRight, inRule, environment, now, credential, false, reason);
+}
// Return errAuthorizationSuccess if this rule allows access based on the specified credential,
// return errAuthorizationDenied otherwise.
OSStatus
-RuleImpl::evaluateCredentialForRight(const AuthItemRef &inRight, const Rule &inRule, const AuthItemSet &environment, CFAbsoluteTime now, const Credential &credential, bool ignoreShared) const
+RuleImpl::evaluateUserCredentialForRight(const AuthorizationToken &auth, const AuthItemRef &inRight, const Rule &inRule, const AuthItemSet &environment, CFAbsoluteTime now, const Credential &credential, bool ignoreShared, SecurityAgent::Reason &reason) const
{
assert(mType == kUser);
+ // Ideally we'd set the AGENT_HINT_RETRY_REASON hint in this method, but
+ // evaluateAuthentication() overwrites it before
+ // AgentMechanismEvaluator::run(). That's what led to passing "reason"
+ // everywhere, from RuleImpl::evaluate() on down.
+
// Get the username from the credential
- const char *user = credential->username().c_str();
+ const char *user = credential->name().c_str();
- // If the credential is not valid or it's age is more than the allowed maximum age
+ // If the credential is not valid or its age is more than the allowed maximum age
// for a credential, deny.
if (!credential->isValid())
{
+ // @@@ it could be the username, not password, was invalid
+ reason = SecurityAgent::invalidPassphrase;
secdebug("autheval", "credential for user %s is invalid, denying right %s", user, inRight->name());
return errAuthorizationDenied;
}
if (now - credential->creationTime() > mMaxCredentialAge)
{
+ // @@@ no proper SA::Reason
+ reason = SecurityAgent::unknownReason;
secdebug("autheval", "credential for user %s has expired, denying right %s", user, inRight->name());
return errAuthorizationDenied;
}
if (!ignoreShared && !mShared && credential->isShared())
{
+ // @@@ no proper SA::Reason
+ reason = SecurityAgent::unknownReason;
secdebug("autheval", "shared credential for user %s cannot be used, denying right %s", user, inRight->name());
return errAuthorizationDenied;
}
return errAuthorizationSuccess;
}
- // XXX/cs replace with remembered session-owner once that functionality is added to SecurityServer
- if (mSessionOwner)
- {
- uid_t console_user;
- struct stat console_stat;
- if (!lstat("/dev/console", &console_stat))
- {
- console_user = console_stat.st_uid;
- if (credential->uid() == console_user)
- {
- secdebug("autheval", "user %s is session-owner(uid: %d), granting right %s", user, console_user, inRight->name());
- return errAuthorizationSuccess;
- }
- }
- else
- secdebug("autheval", "session-owner check failed.");
- }
+ if (mSessionOwner)
+ {
+ Session &session = auth.session();
+ uid_t console_user = session.originatorUid();
+
+ if (credential->uid() == console_user)
+ {
+ secdebug("autheval", "user %s is session-owner(uid: %d), granting right %s", user, console_user, inRight->name());
+ return errAuthorizationSuccess;
+ }
+ // set "reason" in this case? not that a proper SA::Reason exists
+ }
+ else
+ {
+ // @@@ no proper SA::Reason
+ reason = SecurityAgent::unknownReason;
+ secdebug("autheval", "session-owner check failed.");
+ }
if (mGroupName.length())
{
const char *groupname = mGroupName.c_str();
Server::active().longTermActivity();
- struct group *gr = getgrnam(groupname);
- if (!gr)
+
+ if (!groupname)
return errAuthorizationDenied;
-
- // Is this the default group of this user?
- // PR-2875126 <grp.h> declares gr_gid int, as opposed to advertised (getgrent(3)) gid_t
- // When this is fixed this warning should go away.
- if (credential->gid() == gr->gr_gid)
- {
- secdebug("autheval", "user %s has group %s(%d) as default group, granting right %s",
- user, groupname, gr->gr_gid, inRight->name());
- endgrent();
- return errAuthorizationSuccess;
- }
-
- for (char **group = gr->gr_mem; *group; ++group)
+
+ do
{
- if (!strcmp(*group, user))
+ uuid_t group_uuid, user_uuid;
+ int is_member;
+
+ // @@@ it'd be nice to have SA::Reason codes for the failures
+ // associated with the pre-check-membership mbr_*() functions,
+ // but userNotInGroup will do
+ if (mbr_group_name_to_uuid(groupname, group_uuid))
+ break;
+
+ if (mbr_uid_to_uuid(credential->uid(), user_uuid))
+ {
+ struct passwd *pwd;
+ if (NULL == (pwd = getpwnam(user)))
+ break;
+ if (mbr_uid_to_uuid(pwd->pw_uid, user_uuid))
+ break;
+ }
+
+ if (mbr_check_membership(user_uuid, group_uuid, &is_member))
+ break;
+
+ if (is_member)
{
secdebug("autheval", "user %s is a member of group %s, granting right %s",
user, groupname, inRight->name());
- endgrent();
return errAuthorizationSuccess;
}
+
}
-
+ while (0);
+
+ reason = SecurityAgent::userNotInGroup;
secdebug("autheval", "user %s is not a member of group %s, denying right %s",
user, groupname, inRight->name());
- endgrent();
}
+ else if (mSessionOwner) // rule asks only if user is the session owner
+ {
+ reason = SecurityAgent::unacceptableUser;
+ }
return errAuthorizationDenied;
}
OSStatus
-RuleImpl::evaluateUser(const AuthItemRef &inRight, const Rule &inRule,
- AuthItemSet &environmentToClient, AuthorizationFlags flags,
- CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials,
- AuthorizationToken &auth) const
+RuleImpl::evaluateUser(const AuthItemRef &inRight, const Rule &inRule, AuthItemSet &environmentToClient, AuthorizationFlags flags, CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials, AuthorizationToken &auth, SecurityAgent::Reason &reason, bool savePassword) const
{
- // If we got here, this is a kUser type rule, let's start looking for a
+ // If we got here, this is a kUser type rule, let's start looking for a
// credential that is satisfactory
// Zeroth -- Here is an extra special saucy ugly hack to allow authorizations
// created by a proccess running as root to automatically get a right.
if (mAllowRoot && auth.creatorUid() == 0)
{
+ SECURITYD_AUTH_USER_ALLOWROOT(&auth);
+
secdebug("autheval", "creator of authorization has uid == 0 granting right %s",
inRight->name());
return errAuthorizationSuccess;
}
- // if this is a "is-admin" rule check that and return
- // XXX/cs add way to specify is-admin class of rule: if (mNoVerify)
- if (name() == kAuthorizationRuleIsAdmin)
+ // if we're not supposed to authenticate evaluate the session-owner against the group
+ if (!mAuthenticateUser)
{
- string username;
- if (!evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, username))
+ Credential hintCredential;
+ OSStatus status = evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, hintCredential, reason);
+
+ if (!status)
+ {
+ SECURITYD_AUTH_USER_ALLOWSESSIONOWNER(&auth);
return errAuthorizationSuccess;
+ }
+
+ return errAuthorizationDenied;
}
// First -- go though the credentials we either already used or obtained during this authorize operation.
for (CredentialSet::const_iterator it = credentials.begin(); it != credentials.end(); ++it)
{
- OSStatus status = evaluateCredentialForRight(inRight, inRule, environmentToClient, now, *it, true);
- if (status != errAuthorizationDenied)
+ // Passed-in user credentials are allowed for least-privileged mode
+ if (auth.operatesAsLeastPrivileged() && !(*it)->isRight() && (*it)->isValid())
{
+ OSStatus status = evaluateUserCredentialForRight(auth, inRight, inRule, environmentToClient, now, *it, false, reason);
+ if (errAuthorizationSuccess == status) {
+ Credential rightCredential(inRight->name(), mShared);
+ credentials.erase(rightCredential); credentials.insert(rightCredential);
+ if (mShared)
+ credentials.insert(Credential(inRight->name(), false));
+ return status;
+ }
+ }
+
+ // if this is least privileged, this will function differently: match credential to requested right
+ OSStatus status = evaluateCredentialForRight(auth, inRight, inRule, environmentToClient, now, *it, false, reason);
+
+ if (status != errAuthorizationDenied) {
// add credential to authinfo
- auth.setCredentialInfo(*it);
+ auth.setCredentialInfo(*it, savePassword);
return status;
}
+
}
// Second -- go though the credentials passed in to this authorize operation by the state management layer.
{
for (CredentialSet::const_iterator it = inCredentials->begin(); it != inCredentials->end(); ++it)
{
- OSStatus status = evaluateCredentialForRight(inRight, inRule, environmentToClient, now, *it, false);
+ // if this is least privileged, this will function differently: match credential to requested right
+ OSStatus status = evaluateCredentialForRight(auth, inRight, inRule, environmentToClient, now, *it, false, reason);
+
if (status == errAuthorizationSuccess)
{
// Add the credential we used to the output set.
// whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
credentials.erase(*it); credentials.insert(*it);
- // add credential to authinfo
- auth.setCredentialInfo(*it);
+ // add credential to authinfo
+ auth.setCredentialInfo(*it, savePassword);
return status;
}
}
}
- // Finally -- We didn't find the credential in our passed in credential lists. Obtain a new credential if
- // our flags let us do so.
+ // Finally -- We didn't find the credential in our passed in credential lists. Obtain a new credential if our flags let us do so.
if (!(flags & kAuthorizationFlagExtendRights))
return errAuthorizationDenied;
setAgentHints(inRight, inRule, environmentToClient, auth);
- // If a different evaluation is prescribed,
- // we'll run that and validate the credentials from there
- // we fall back on a default configuration
- return evaluateAuthorization(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
+ return evaluateAuthentication(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth, reason, savePassword);
}
OSStatus
-RuleImpl::evaluateMechanismOnly(const AuthItemRef &inRight, const Rule &inRule, AuthItemSet &environmentToClient, AuthorizationToken &auth, CredentialSet &outCredentials) const
+RuleImpl::evaluateMechanismOnly(const AuthItemRef &inRight, const Rule &inRule, AuthItemSet &environmentToClient, AuthorizationToken &auth, CredentialSet &outCredentials, bool savePassword) const
{
uint32 tries = 0;
OSStatus status;
- Process &cltProc = Server::process();
- // Authorization preserves creator's UID in setuid processes
- uid_t cltUid = (cltProc.uid() != 0) ? cltProc.uid() : auth.creatorUid();
- secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc.pid(), cltUid);
+ Process &cltProc = Server::process();
+ // Authorization preserves creator's UID in setuid processes
+ uid_t cltUid = (cltProc.uid() != 0) ? cltProc.uid() : auth.creatorUid();
+ secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc.pid(), cltUid);
- {
- AgentMechanismEvaluator eval(cltUid, cltProc.session(), mEvalDef);
+ {
+ AgentMechanismEvaluator eval(cltUid, auth.session(), mEvalDef);
+ // For auditing within AuthorizationMechEval, pass the right name.
+ size_t rightNameSize = inRight->name() ? strlen(inRight->name()) : 0;
+ AuthorizationString rightName = inRight->name() ? inRight->name() : "";
+ // @@@ AuthValueRef's ctor ought to take a const void *
+ AuthValueRef rightValue(rightNameSize, const_cast<char *>(rightName));
+ AuthValueVector authValueVector;
+ authValueVector.push_back(rightValue);
- do
- {
- setAgentHints(inRight, inRule, environmentToClient, auth);
- AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
- environmentToClient.erase(triesHint); environmentToClient.insert(triesHint); // replace
-
- status = eval.run(AuthValueVector(), environmentToClient, auth.infoSet());
+ do
+ {
+ setAgentHints(inRight, inRule, environmentToClient, auth);
+ AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
+ environmentToClient.erase(triesHint); environmentToClient.insert(triesHint); // replace
- if ((status == errAuthorizationSuccess) ||
- (status == errAuthorizationCanceled)) // @@@ can only pass back sideband through context
- {
- secdebug("AuthEvalMech", "storing new context for authorization");
- auth.setInfoSet(eval.context());
- if (status == errAuthorizationSuccess)
- {
- outCredentials = makeCredentials(eval.context());
- }
- }
-
- tries++;
- }
- while ((status == errAuthorizationDenied) // only if we have an expected failure we continue
- && ((mTries == 0) // mTries == 0 means we try forever
- || ((mTries > 0) // mTries > 0 means we try up to mTries times
- && (tries < mTries))));
- }
-
- if (name() == "system.login.console")
- {
- QueryInvokeMechanism query(cltUid, cltProc.session());
- query.terminateAgent();
- }
+ status = eval.run(authValueVector, environmentToClient, auth);
+ if ((status == errAuthorizationSuccess) ||
+ (status == errAuthorizationCanceled)) // @@@ can only pass back sideband through context
+ {
+ secdebug("AuthEvalMech", "storing new context for authorization");
+ auth.setInfoSet(eval.context(), savePassword);
+ if (status == errAuthorizationSuccess)
+ {
+ // (try to) attach the authorizing UID to the least-priv cred
+ if (auth.operatesAsLeastPrivileged())
+ {
+ outCredentials.insert(Credential(rightName, mShared));
+ if (mShared)
+ outCredentials.insert(Credential(rightName, false));
+
+ RightAuthenticationLogger logger(auth.creatorAuditToken(), AUE_ssauthint);
+ logger.setRight(rightName);
+
+ AuthItem *uidItem = eval.context().find(AGENT_CONTEXT_UID);
+ if (uidItem)
+ {
+ uid_t authorizedUid;
+ memcpy(&authorizedUid, uidItem->value().data, sizeof(authorizedUid));
+ secdebug("AuthEvalMech", "generating least-privilege cred for '%s' authorized by UID %u", inRight->name(), authorizedUid);
+ logger.logLeastPrivilege(authorizedUid, true);
+ }
+ else // cltUid is better than nothing
+ {
+ secdebug("AuthEvalMech", "generating least-privilege cred for '%s' with process- or auth-UID %u", inRight->name(), cltUid);
+ logger.logLeastPrivilege(cltUid, false);
+ }
+ }
+
+ if (0 == strcmp(rightName, "system.login.console") && NULL == eval.context().find(AGENT_CONTEXT_AUTO_LOGIN)) {
+ secdebug("AuthEvalMech", "We logged in as the session owner.\n");
+ SessionAttributeBits flags = auth.session().attributes();
+ flags |= AU_SESSION_FLAG_HAS_AUTHENTICATED;
+ auth.session().setAttributes(flags);
+ }
+ CredentialSet newCredentials = makeCredentials(auth);
+ outCredentials.insert(newCredentials.begin(), newCredentials.end());
+ }
+ }
+
+ tries++;
+ }
+ while ((status == errAuthorizationDenied) // only if we have an expected failure we continue
+ && ((mTries == 0) // mTries == 0 means we try forever
+ || ((mTries > 0) // mTries > 0 means we try up to mTries times
+ && (tries < mTries))));
+ }
+
+ // HACK kill all hosts to free pages for low memory systems
+ // (XXX/gh there should be a #define for this right)
+ if (name() == "system.login.done")
+ {
+ // one case where we don't want to mark the agents as "busy"
+ QueryInvokeMechanism query(securityAgent, auth.session());
+ query.terminateAgent();
+ QueryInvokeMechanism query2(privilegedAuthHost, auth.session());
+ query2.terminateAgent();
+ }
return status;
}
OSStatus
-RuleImpl::evaluateRules(const AuthItemRef &inRight, const Rule &inRule,
- AuthItemSet &environmentToClient, AuthorizationFlags flags,
- CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials,
- AuthorizationToken &auth) const
+RuleImpl::evaluateRules(const AuthItemRef &inRight, const Rule &inRule, AuthItemSet &environmentToClient, AuthorizationFlags flags, CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials, AuthorizationToken &auth, SecurityAgent::Reason &reason, bool savePassword) const
{
// line up the rules to try
if (!mRuleDef.size())
return errAuthorizationSuccess;
// get a rule and try it
- status = (*it)->evaluate(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
+ status = (*it)->evaluate(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth, reason, savePassword);
// if status is cancel/internal error abort
if ((status == errAuthorizationCanceled) || (status == errAuthorizationInternal))
else
count++;
}
+
+ if ((mType == kKofN) && (status == errAuthorizationSuccess) && (count < mKofN))
+ status = errAuthorizationDenied;
return status; // return the last failure
}
OSStatus
-RuleImpl::evaluate(const AuthItemRef &inRight, const Rule &inRule,
- AuthItemSet &environmentToClient, AuthorizationFlags flags,
- CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials,
- AuthorizationToken &auth) const
+RuleImpl::evaluate(const AuthItemRef &inRight, const Rule &inRule, AuthItemSet &environmentToClient, AuthorizationFlags flags, CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials, AuthorizationToken &auth, SecurityAgent::Reason &reason, bool savePassword) const
{
switch (mType)
{
case kAllow:
- secdebug("autheval", "rule is always allow");
+ SECURITYD_AUTH_ALLOW(&auth, (char *)name().c_str());
return errAuthorizationSuccess;
case kDeny:
- secdebug("autheval", "rule is always deny");
+ SECURITYD_AUTH_DENY(&auth, (char *)name().c_str());
return errAuthorizationDenied;
case kUser:
- secdebug("autheval", "rule is user");
- return evaluateUser(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
+ SECURITYD_AUTH_USER(&auth, (char *)name().c_str());
+ return evaluateUser(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth, reason, savePassword);
case kRuleDelegation:
- secdebug("autheval", "rule evaluates rules");
- return evaluateRules(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
+ SECURITYD_AUTH_RULES(&auth, (char *)name().c_str());
+ return evaluateRules(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth, reason, savePassword);
case kKofN:
- secdebug("autheval", "rule evaluates k-of-n rules");
- return evaluateRules(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
- case kEvaluateMechanisms:
- secdebug("autheval", "rule evaluates mechanisms");
- return evaluateMechanismOnly(inRight, inRule, environmentToClient, auth, credentials);
- default:
- MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+ SECURITYD_AUTH_KOFN(&auth, (char *)name().c_str());
+ return evaluateRules(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth, reason, savePassword);
+ case kEvaluateMechanisms:
+ SECURITYD_AUTH_MECHRULE(&auth, (char *)name().c_str());
+ // if we had a SecurityAgent::Reason code for "mechanism denied,"
+ // it would make sense to pass down "reason"
+ return evaluateMechanismOnly(inRight, inRule, environmentToClient, auth, credentials, savePassword);
+ default:
+ Syslog::alert("Unrecognized rule type %d", mType);
+ MacOSError::throwMe(errAuthorizationInternal); // invalid rule
}
}