AuthorizationRule.cpp [plain text]
#include "AuthorizationRule.h"
#include <Security/AuthorizationTags.h>
#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;
namespace Authorization {
CFStringRef RuleImpl::kUserGroupID = CFSTR(kAuthorizationRuleParameterGroup);
CFStringRef RuleImpl::kTimeoutID = CFSTR(kAuthorizationRuleParameterCredentialTimeout);
CFStringRef RuleImpl::kSharedID = CFSTR(kAuthorizationRuleParameterCredentialShared);
CFStringRef RuleImpl::kAllowRootID = CFSTR(kAuthorizationRuleParameterAllowRoot);
CFStringRef RuleImpl::kMechanismsID = CFSTR(kAuthorizationRuleParameterMechanisms);
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"); CFStringRef RuleImpl::kExtractPasswordID = CFSTR(kAuthorizationRuleParameterExtractPassword);
CFStringRef RuleImpl::kRuleClassID = CFSTR(kAuthorizationRuleClass);
CFStringRef RuleImpl::kRuleAllowID = CFSTR(kAuthorizationRuleClassAllow);
CFStringRef RuleImpl::kRuleDenyID = CFSTR(kAuthorizationRuleClassDeny);
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, const char *defaultValue = "")
{
CFTypeRef value = CFDictionaryGetValue(config, key);
if (value && (CFGetTypeID(value) == CFStringGetTypeID()))
{
CFStringRef stringValue = reinterpret_cast<CFStringRef>(value);
char buffer[512];
const char *ptr = CFStringGetCStringPtr(stringValue, kCFStringEncodingUTF8);
if (ptr == NULL)
{
if (CFStringGetCString(stringValue, buffer, sizeof(buffer), kCFStringEncodingUTF8))
ptr = buffer;
else
{
Syslog::alert("Could not convert CFString to C string");
MacOSError::throwMe(errAuthorizationInternal);
}
}
return string(ptr);
}
else
if (!required)
return string(defaultValue);
else
{
Syslog::alert("Failed to get rule string");
MacOSError::throwMe(errAuthorizationInternal);
}
}
double
RuleImpl::Attribute::getDouble(CFDictionaryRef config, CFStringRef key, bool required = false, double defaultValue = 0.0)
{
double doubleValue = 0;
CFTypeRef value = CFDictionaryGetValue(config, key);
if (value && (CFGetTypeID(value) == CFNumberGetTypeID()))
{
CFNumberGetValue(reinterpret_cast<CFNumberRef>(value), kCFNumberDoubleType, &doubleValue);
}
else
if (!required)
return defaultValue;
else
{
Syslog::alert("Failed to get rule double value");
MacOSError::throwMe(errAuthorizationInternal);
}
return doubleValue;
}
bool
RuleImpl::Attribute::getBool(CFDictionaryRef config, CFStringRef key, bool required = false, bool defaultValue = false)
{
bool boolValue = false;
CFTypeRef value = CFDictionaryGetValue(config, key);
if (value && (CFGetTypeID(value) == CFBooleanGetTypeID()))
{
boolValue = CFBooleanGetValue(reinterpret_cast<CFBooleanRef>(value));
}
else
if (!required)
return defaultValue;
else
{
Syslog::alert("Failed to get rule bool value");
MacOSError::throwMe(errAuthorizationInternal);
}
return boolValue;
}
vector<string>
RuleImpl::Attribute::getVector(CFDictionaryRef config, CFStringRef key, bool required = false)
{
vector<string> valueArray;
CFTypeRef value = CFDictionaryGetValue(config, key);
if (value && (CFGetTypeID(value) == CFArrayGetTypeID()))
{
CFArrayRef evalArray = reinterpret_cast<CFArrayRef>(value);
CFIndex numItems = CFArrayGetCount(evalArray);
for (CFIndex index=0; index < numItems; index++)
{
CFTypeRef arrayValue = CFArrayGetValueAtIndex(evalArray, index);
if (arrayValue && (CFGetTypeID(arrayValue) == CFStringGetTypeID()))
{
CFStringRef stringValue = reinterpret_cast<CFStringRef>(arrayValue);
char buffer[512];
const char *ptr = CFStringGetCStringPtr(stringValue, kCFStringEncodingUTF8);
if (ptr == NULL)
{
if (CFStringGetCString(stringValue, buffer, sizeof(buffer), kCFStringEncodingUTF8))
ptr = buffer;
else
{
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)
{
Syslog::alert("Value for key either not present or not a CFArray");
MacOSError::throwMe(errAuthorizationInternal);
}
return valueArray;
}
bool RuleImpl::Attribute::getLocalizedText(CFDictionaryRef config, map<string,string> &localizedPrompts, CFStringRef dictKey, const char *descriptionKey)
{
CFIndex numberOfPrompts = 0;
CFDictionaryRef promptsDict;
if (CFDictionaryContainsKey(config, dictKey))
{
promptsDict = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(config, dictKey));
if (promptsDict && (CFGetTypeID(promptsDict) == CFDictionaryGetTypeID()))
numberOfPrompts = CFDictionaryGetCount(promptsDict);
}
if (numberOfPrompts == 0)
return false;
const void *keys[numberOfPrompts+1];
const void *values[numberOfPrompts+1];
CFDictionaryGetKeysAndValues(promptsDict, &keys[0], &values[0]);
while (numberOfPrompts-- > 0)
{
CFStringRef keyRef = reinterpret_cast<CFStringRef>(keys[numberOfPrompts]);
CFStringRef valueRef = reinterpret_cast<CFStringRef>(values[numberOfPrompts]);
if (!keyRef || (CFGetTypeID(keyRef) != CFStringGetTypeID())) {
continue;
}
if (!valueRef || (CFGetTypeID(valueRef) != CFStringGetTypeID())) {
continue;
}
string key = cfString(keyRef);
string value = cfString(valueRef);
localizedPrompts[descriptionKey + key] = value;
}
return true;
}
RuleImpl::RuleImpl() :
mType(kUser), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false), mSessionOwner(false), mTries(0), mAuthenticateUser(true), mExtractPassword(false)
{
}
RuleImpl::RuleImpl(const string &inRightName, CFDictionaryRef cfRight, CFDictionaryRef cfRules) : mRightName(inRightName), mExtractPassword(false)
{
if (CFGetTypeID(cfRight) != CFDictionaryGetTypeID())
{
Syslog::alert("Invalid rights set");
MacOSError::throwMe(errAuthorizationInternal);
}
mTries = 0;
string classTag = Attribute::getString(cfRight, kRuleClassID, false, "");
if (classTag.length())
{
if (classTag == kAuthorizationRuleClassAllow)
{
secdebug("authrule", "%s : rule allow", inRightName.c_str());
mType = kAllow;
}
else if (classTag == kAuthorizationRuleClassDeny)
{
secdebug("authrule", "%s : rule deny", inRightName.c_str());
mType = kDeny;
}
else if (classTag == kAuthorizationRuleClassUser)
{
mType = kUser;
mGroupName = Attribute::getString(cfRight, kUserGroupID);
mMaxCredentialAge = Attribute::getDouble(cfRight, kTimeoutID, false, DBL_MAX);
mShared = Attribute::getBool(cfRight, kSharedID);
mAllowRoot = Attribute::getBool(cfRight, kAllowRootID);
mSessionOwner = Attribute::getBool(cfRight, kSessionOwnerID);
mEvalDef = Attribute::getVector(cfRight, kMechanismsID);
if (mEvalDef.size() == 0 && cfRules )
{
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(),
mGroupName.c_str(), mMaxCredentialAge, mShared ? " shared" : "",
mAllowRoot ? " allow-root" : "");
}
else if (classTag == kAuthorizationRuleClassMechanisms)
{
secdebug("authrule", "%s : rule evaluate mechanisms", inRightName.c_str());
mType = kEvaluateMechanisms;
mEvalDef = Attribute::getVector(cfRight, kMechanismsID, true);
mTries = int(Attribute::getDouble(cfRight, kTriesID, false, 0.0)); mShared = Attribute::getBool(cfRight, kSharedID, false, true);
mExtractPassword = Attribute::getBool(cfRight, kExtractPasswordID, false, false);
}
else if (classTag == kAuthorizationRightRule)
{
assert(cfRules); secdebug("authrule", "%s : rule delegate rule", inRightName.c_str());
mType = kRuleDelegation;
string ruleDefString = Attribute::getString(cfRight, kRuleDelegateID, false, "");
if (ruleDefString.length())
{
CFStringRef ruleDefRef = makeCFString(ruleDefString);
CFDictionaryRef cfRuleDef = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(cfRules, ruleDefRef));
if (ruleDefRef)
CFRelease(ruleDefRef);
if (!cfRuleDef || CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID())
{
Syslog::alert("'%s' does not name a built-in rule", ruleDefString.c_str());
MacOSError::throwMe(errAuthorizationInternal);
}
mRuleDef.push_back(Rule(ruleDefString, cfRuleDef, cfRules));
}
else {
vector<string> ruleDef = Attribute::getVector(cfRight, kRuleDelegateID, true);
for (vector<string>::const_iterator it = ruleDef.begin(); it != ruleDef.end(); it++)
{
CFStringRef ruleNameRef = makeCFString(*it);
CFDictionaryRef cfRuleDef = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(cfRules, ruleNameRef));
if (ruleNameRef)
CFRelease(ruleNameRef);
if (!cfRuleDef || (CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID()))
{
Syslog::alert("Invalid rule '%s'in rule set", it->c_str());
MacOSError::throwMe(errAuthorizationInternal);
}
mRuleDef.push_back(Rule(*it, cfRuleDef, cfRules));
}
}
mKofN = int(Attribute::getDouble(cfRight, kKofNID, false, 0.0));
if (mKofN)
mType = kKofN;
}
else
{
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
{
mType = kRuleDelegation;
string ruleName = Attribute::getString(cfRight, kRuleDelegateID, true);
secdebug("authrule", "%s : rule delegate rule (1): %s", inRightName.c_str(), ruleName.c_str());
CFStringRef ruleNameRef = makeCFString(ruleName);
CFDictionaryRef cfRuleDef = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(cfRules, ruleNameRef));
if (ruleNameRef)
CFRelease(ruleNameRef);
if (!cfRuleDef || CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID())
{
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::getLocalizedText(cfRight, mLocalizedPrompts, kPromptID, kAuthorizationRuleParameterDescription);
Attribute::getLocalizedText(cfRight, mLocalizedButtons, kButtonID, kAuthorizationRuleParameterButton);
}
void
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)));
pid_t creatorPid = auth.creatorPid();
environmentToClient.erase(AuthItemRef(AGENT_HINT_CREATOR_PID));
environmentToClient.insert(AuthItemRef(AGENT_HINT_CREATOR_PID, AuthValueOverlay(sizeof(pid_t), &creatorPid)));
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())
{
map<string,string>::const_iterator it;
for (it = defaultPrompts.begin(); it != defaultPrompts.end(); it++)
{
const string &key = it->first;
const string &value = it->second;
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)));
}
}
string ruleName = name();
environmentToClient.erase(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE));
environmentToClient.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE, AuthValueOverlay(ruleName)));
}
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
{
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())));
}
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();
uid_t cltUid = (cltProc.uid() != 0) ? cltProc.uid() : auth.creatorUid();
secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc.pid(), cltUid);
size_t rightNameSize = inRight->name() ? strlen(inRight->name()) : 0;
AuthorizationString rightName = inRight->name() ? inRight->name() : "";
AuthValueRef rightValue(rightNameSize, const_cast<char *>(rightName));
AuthValueVector authValueVector;
authValueVector.push_back(rightValue);
RightAuthenticationLogger rightAuthLogger(auth.creatorAuditToken(), AUE_ssauthint);
rightAuthLogger.setRight(rightName);
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);
}
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);
}
AgentMechanismEvaluator eval(cltUid, auth.session(), mEvalDef);
for (tries = 0; tries < mTries; tries++)
{
AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
environmentToClient.erase(retryHint); environmentToClient.insert(retryHint); AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
environmentToClient.erase(triesHint); environmentToClient.insert(triesHint);
status = eval.run(authValueVector, environmentToClient, auth);
if ((status == errAuthorizationSuccess) ||
(status == errAuthorizationCanceled)) {
secdebug("AuthEvalMech", "storing new context for authorization");
auth.setInfoSet(eval.context(), savePassword);
}
if (status == errAuthorizationSuccess)
{
status = errAuthorizationDenied;
CredentialSet newCredentials = makeCredentials(auth);
auth.scrubInfoSet(savePassword);
for (CredentialSet::const_iterator it = newCredentials.begin(); it != newCredentials.end(); ++it)
{
const Credential& newCredential = *it;
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 {
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;
}
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));
}
credentials.erase(newCredential); credentials.insert(newCredential);
if (mShared)
credentials.insert(Credential(newCredential->uid(), newCredential->name(), newCredential->realname(), false));
auth.setCredentialInfo(newCredential, savePassword);
secdebug("SSevalMech", "added valid credential for user %s", newCredential->name().c_str());
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;
}
}
if (status == errAuthorizationSuccess)
break;
}
else
if ((status == errAuthorizationCanceled) || (status == errAuthorizationInternal))
{
auth.scrubInfoSet(false);
break;
}
else if (status == errAuthorizationDenied)
reason = SecurityAgent::invalidPassphrase;
}
if (tries == mTries)
{
reason = SecurityAgent::tooManyTries;
AuthItemRef retryHint (AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
environmentToClient.erase(retryHint); environmentToClient.insert(retryHint); AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
environmentToClient.erase(triesHint); environmentToClient.insert(triesHint); eval.run(AuthValueVector(), environmentToClient, auth);
auth.scrubInfoSet(false);
rightAuthLogger.logFailure(NULL, CommonCriteria::errTooManyTries);
}
return status;
}
CredentialSet
RuleImpl::makeCredentials(const AuthorizationToken &auth) const
{
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)
{
newCredentials.insert(Credential(*uid, username, "", mShared));
}
} while(0);
return newCredentials;
}
OSStatus
RuleImpl::evaluateSessionOwner(const AuthItemRef &inRight, const Rule &inRule, const AuthItemSet &environment, const CFAbsoluteTime now, const AuthorizationToken &auth, Credential &credential, SecurityAgent::Reason &reason) const
{
Credential sessionCredential;
uid_t uid = auth.session().originatorUid();
Server::active().longTermActivity();
struct passwd *pw = getpwuid(uid);
if (pw != NULL) {
if ( (pw->pw_passwd == NULL) ||
strcmp(pw->pw_passwd, "*") ) {
secdebug("AuthEvalMech", "preflight credential from current user, result follows:");
sessionCredential = Credential(pw->pw_uid, pw->pw_name, pw->pw_gecos, mShared);
} 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())
{
reason = SecurityAgent::unknownReason;
secdebug("autheval", "shared credential cannot be used, denying right %s", inRight->name());
return errAuthorizationDenied;
} else {
return errAuthorizationSuccess;
}
} else {
reason = SecurityAgent::unknownReason;
return errAuthorizationDenied;
}
} else
return evaluateUserCredentialForRight(auth, inRight, inRule, environment, now, credential, false, reason);
}
OSStatus
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);
const char *user = credential->name().c_str();
if (!credential->isValid())
{
reason = SecurityAgent::invalidPassphrase;
secdebug("autheval", "credential for user %s is invalid, denying right %s", user, inRight->name());
return errAuthorizationDenied;
}
if (now - credential->creationTime() > mMaxCredentialAge)
{
reason = SecurityAgent::unknownReason;
secdebug("autheval", "credential for user %s has expired, denying right %s", user, inRight->name());
return errAuthorizationDenied;
}
if (!ignoreShared && !mShared && credential->isShared())
{
reason = SecurityAgent::unknownReason;
secdebug("autheval", "shared credential for user %s cannot be used, denying right %s", user, inRight->name());
return errAuthorizationDenied;
}
if (credential->uid() == 0)
{
secdebug("autheval", "user %s has uid 0, granting right %s", user, inRight->name());
return errAuthorizationSuccess;
}
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;
}
}
else
{
reason = SecurityAgent::unknownReason;
secdebug("autheval", "session-owner check failed.");
}
if (mGroupName.length())
{
const char *groupname = mGroupName.c_str();
Server::active().longTermActivity();
if (!groupname)
return errAuthorizationDenied;
do
{
uuid_t group_uuid, user_uuid;
int is_member;
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());
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());
}
else if (mSessionOwner) {
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, SecurityAgent::Reason &reason, bool savePassword) const
{
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 (!mAuthenticateUser)
{
Credential hintCredential;
OSStatus status = evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, hintCredential, reason);
if (!status)
{
SECURITYD_AUTH_USER_ALLOWSESSIONOWNER(&auth);
return errAuthorizationSuccess;
}
return errAuthorizationDenied;
}
for (CredentialSet::const_iterator it = credentials.begin(); it != credentials.end(); ++it)
{
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;
}
}
OSStatus status = evaluateCredentialForRight(auth, inRight, inRule, environmentToClient, now, *it, false, reason);
if (status != errAuthorizationDenied) {
auth.setCredentialInfo(*it, savePassword);
return status;
}
}
if (inCredentials)
{
for (CredentialSet::const_iterator it = inCredentials->begin(); it != inCredentials->end(); ++it)
{
OSStatus status = evaluateCredentialForRight(auth, inRight, inRule, environmentToClient, now, *it, false, reason);
if (status == errAuthorizationSuccess)
{
credentials.erase(*it); credentials.insert(*it);
auth.setCredentialInfo(*it, savePassword);
return status;
}
else if (status != errAuthorizationDenied)
return status;
}
}
if (!(flags & kAuthorizationFlagExtendRights))
return errAuthorizationDenied;
if ((flags & kAuthorizationFlagPreAuthorize) &&
(mMaxCredentialAge == 0.0))
{
inRight->setFlags(inRight->flags() | kAuthorizationFlagCanNotPreAuthorize);
return errAuthorizationSuccess;
}
if (!(flags & kAuthorizationFlagInteractionAllowed))
return errAuthorizationInteractionNotAllowed;
setAgentHints(inRight, inRule, environmentToClient, 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, bool savePassword) const
{
uint32 tries = 0;
OSStatus status;
Process &cltProc = Server::process();
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, auth.session(), mEvalDef);
size_t rightNameSize = inRight->name() ? strlen(inRight->name()) : 0;
AuthorizationString rightName = inRight->name() ? inRight->name() : "";
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);
status = eval.run(authValueVector, environmentToClient, auth);
if ((status == errAuthorizationSuccess) ||
(status == errAuthorizationCanceled)) {
secdebug("AuthEvalMech", "storing new context for authorization");
auth.setInfoSet(eval.context(), savePassword);
if (status == errAuthorizationSuccess)
{
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 {
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) && ((mTries == 0) || ((mTries > 0) && (tries < mTries))));
}
if (name() == "system.login.done")
{
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, SecurityAgent::Reason &reason, bool savePassword) const
{
if (!mRuleDef.size())
return errAuthorizationSuccess;
uint32_t count = 0;
OSStatus status = errAuthorizationSuccess;
vector<Rule>::const_iterator it;
for (it = mRuleDef.begin();it != mRuleDef.end(); it++)
{
if ((mType == kKofN) && (count == mKofN))
return errAuthorizationSuccess;
status = (*it)->evaluate(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth, reason, savePassword);
if ((status == errAuthorizationCanceled) || (status == errAuthorizationInternal))
return status;
if (status != errAuthorizationSuccess)
{
if (mType == kKofN)
continue;
break;
}
else
count++;
}
if ((mType == kKofN) && (status == errAuthorizationSuccess) && (count < mKofN))
status = errAuthorizationDenied;
return status; }
OSStatus
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:
SECURITYD_AUTH_ALLOW(&auth, (char *)name().c_str());
return errAuthorizationSuccess;
case kDeny:
SECURITYD_AUTH_DENY(&auth, (char *)name().c_str());
return errAuthorizationDenied;
case kUser:
SECURITYD_AUTH_USER(&auth, (char *)name().c_str());
return evaluateUser(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth, reason, savePassword);
case kRuleDelegation:
SECURITYD_AUTH_RULES(&auth, (char *)name().c_str());
return evaluateRules(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth, reason, savePassword);
case kKofN:
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());
return evaluateMechanismOnly(inRight, inRule, environmentToClient, auth, credentials, savePassword);
default:
Syslog::alert("Unrecognized rule type %d", mType);
MacOSError::throwMe(errAuthorizationInternal); }
}
Rule::Rule() : RefPointer<RuleImpl>(new RuleImpl()) {}
Rule::Rule(const string &inRightName, CFDictionaryRef cfRight, CFDictionaryRef cfRules) : RefPointer<RuleImpl>(new RuleImpl(inRightName, cfRight, cfRules)) {}
}