AuthorizationRule.cpp [plain text]
#include "AuthorizationRule.h"
#include "AuthorizationTags.h"
#include "AuthorizationDB.h"
#include "AuthorizationPriv.h"
#include "authority.h"
#include "server.h"
#include "process.h"
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
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::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);
string
RuleImpl::Attribute::getString(CFDictionaryRef config, CFStringRef key, bool required = false, char *defaultValue = NULL)
{
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
MacOSError::throwMe(errAuthorizationInternal); }
return string(ptr);
}
else
if (!required)
return string(defaultValue);
else
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
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
MacOSError::throwMe(errAuthorizationInternal);
return boolValue;
}
void
RuleImpl::Attribute::setString(CFMutableDictionaryRef config, CFStringRef key, string &value)
{
CFStringRef cfstringValue = CFStringCreateWithCString(NULL , value.c_str(), kCFStringEncodingUTF8);
if (cfstringValue)
{
CFDictionarySetValue(config, key, cfstringValue);
CFRelease(cfstringValue);
}
else
MacOSError::throwMe(errAuthorizationInternal); }
void
RuleImpl::Attribute::setDouble(CFMutableDictionaryRef config, CFStringRef key, double value)
{
CFNumberRef doubleValue = CFNumberCreate(NULL , kCFNumberDoubleType, doubleValue);
if (doubleValue)
{
CFDictionarySetValue(config, key, doubleValue);
CFRelease(doubleValue);
}
else
MacOSError::throwMe(errAuthorizationInternal); }
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)
{
vector<string> valueArray;
CFTypeRef value = CFDictionaryGetValue(config, key);
if (value && (CFGetTypeID(value) == CFArrayGetTypeID()))
{
CFArrayRef evalArray = reinterpret_cast<CFArrayRef>(value);
for (int index=0; index < CFArrayGetCount(evalArray); 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
MacOSError::throwMe(errAuthorizationInternal); }
valueArray.push_back(string(ptr));
}
}
}
else
if (required)
MacOSError::throwMe(errAuthorizationInternal);
return valueArray;
}
bool RuleImpl::Attribute::getLocalizedPrompts(CFDictionaryRef config, map<string,string> &localizedPrompts)
{
CFIndex numberOfPrompts = 0;
CFDictionaryRef promptsDict;
if (CFDictionaryContainsKey(config, kPromptID))
{
promptsDict = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(config, kPromptID));
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["description"+key] = value;
}
return true;
}
RuleImpl::RuleImpl() :
mType(kUser), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false), mSessionOwner(false), mTries(0)
{
}
RuleImpl::RuleImpl(const string &inRightName, CFDictionaryRef cfRight, CFDictionaryRef cfRules) : mRightName(inRightName)
{
if (CFGetTypeID(cfRight) != CFDictionaryGetTypeID())
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);
mTries = 3;
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);
}
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())
MacOSError::throwMe(errAuthorizationInternal); mRuleDef.push_back(Rule(ruleDefString, cfRuleDef, NULL));
}
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()))
MacOSError::throwMe(errAuthorizationInternal); mRuleDef.push_back(Rule(*it, cfRuleDef, NULL));
}
}
mKofN = int(Attribute::getDouble(cfRight, kKofNID, false, 0.0));
if (mKofN)
mType = kKofN;
}
else
{
secdebug("authrule", "%s : rule class unknown %s.", inRightName.c_str(), classTag.c_str());
MacOSError::throwMe(errAuthorizationInternal); }
}
else
{
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());
CFStringRef ruleNameRef = makeCFString(ruleName);
CFDictionaryRef cfRuleDef = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(cfRules, ruleNameRef));
if (ruleNameRef)
CFRelease(ruleNameRef);
if (!cfRuleDef || CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID())
MacOSError::throwMe(errAuthorizationInternal); mRuleDef.push_back(Rule(ruleName, cfRuleDef, NULL));
}
Attribute::getLocalizedPrompts(cfRight, mLocalizedPrompts);
}
void
RuleImpl::setAgentHints(const AuthItemRef &inRight, const Rule &inTopLevelRule, AuthItemSet &environmentToClient, AuthorizationToken &auth) const
{
string authorizeString(inRight->name());
environmentToClient.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT, AuthValueOverlay(authorizeString)));
pid_t cPid = Server::connection().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.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]; string bundlePath = osxcode->canonicalPath();
environmentToClient.insert(AuthItemRef(AGENT_HINT_CLIENT_TYPE, AuthValueOverlay(sizeof(bundleType), &bundleType)));
environmentToClient.insert(AuthItemRef(AGENT_HINT_CLIENT_PATH, AuthValueOverlay(bundlePath)));
}
map<string,string> defaultPrompts = inTopLevelRule->localizedPrompts();
if (defaultPrompts.empty())
defaultPrompts = localizedPrompts();
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)));
}
}
string ruleName = name();
environmentToClient.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE, AuthValueOverlay(ruleName)));
}
string
RuleImpl::agentNameForAuth(const AuthorizationToken &auth) 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);
}
catch (CssmError &e)
{
secdebug("auth", "digesting authref failed (%lu)", e.cssmError());
return string("SecurityAgentMechanism");
}
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;
}
return string(static_cast<char *>(hashedData.data()), hashedData.length());
}
OSStatus
RuleImpl::evaluateMechanism(const AuthItemRef &inRight, const AuthItemSet &environment, AuthorizationToken &auth, CredentialSet &outCredentials) const
{
string agentName = agentNameForAuth(auth);
AuthValueVector arguments;
AuthItemSet context = auth.infoSet();
AuthItemSet hints = environment;
AuthorizationResult result = kAuthorizationResultAllow;
vector<string>::const_iterator currentMechanism = mEvalDef.begin();
while ( (result == kAuthorizationResultAllow) &&
(currentMechanism != mEvalDef.end()) ) {
string::size_type extPlugin = currentMechanism->find(':');
if (extPlugin != string::npos)
{
string pluginIn(currentMechanism->substr(0, extPlugin));
string mechanismIn(currentMechanism->substr(extPlugin + 1));
secdebug("SSevalMech", "external mech %s:%s", pluginIn.c_str(), mechanismIn.c_str());
bool mechExecOk = false;
Process &cltProc = Server::active().connection().process;
uid_t cltUid = (cltProc.uid() != 0) ? cltProc.uid() : auth.creatorUid();
secdebug("SSevalMech", "Mechanism invocation by process %d (UID %d)", cltProc.pid(), cltUid);
QueryInvokeMechanism client(cltUid, auth, agentName.c_str());
try
{
mechExecOk = client(pluginIn, mechanismIn, arguments, hints, context, &result);
}
catch (...) {
secdebug("SSevalMech", "exception from mech eval or client death");
if (mechExecOk != true)
result = kAuthorizationResultUndefined;
}
secdebug("SSevalMech", "evaluate(plugin: %s, mechanism: %s) %s, result: %lu.", pluginIn.c_str(), mechanismIn.c_str(), (mechExecOk == true) ? "succeeded" : "failed", result);
}
else
{
if (*currentMechanism == "authinternal")
{
secdebug("SSevalMech", "evaluate authinternal");
result = kAuthorizationResultDeny;
do {
AuthItemSet::iterator found = find_if(context.begin(), context.end(), FindAuthItemByRightName(kAuthorizationEnvironmentUsername) );
if (found == context.end())
break;
string username(static_cast<const char *>((*found)->value().data), (*found)->value().length);
secdebug("SSevalMech", "found username");
found = find_if(context.begin(), context.end(), FindAuthItemByRightName(kAuthorizationEnvironmentPassword) );
if (found == context.end())
break;
string password(static_cast<const char *>((*found)->value().data), (*found)->value().length);
secdebug("SSevalMech", "found password");
Credential newCredential(username, password, true);
if (newCredential->isValid())
Syslog::info("authinternal authenticated user %s (uid %lu) for right %s.", newCredential->username().c_str(), newCredential->uid(), inRight->name());
else
Syslog::error("authinternal failed to authenticate user %s for right %s.", newCredential->username().c_str(), inRight->name());
if (newCredential->isValid())
{
outCredentials.clear(); secdebug("SSevalMech", "inserting new credential");
outCredentials.insert(newCredential);
result = kAuthorizationResultAllow;
} else
result = kAuthorizationResultDeny;
} while (0);
}
else
if (*currentMechanism == "push_hints_to_context")
{
secdebug("SSevalMech", "evaluate push_hints_to_context");
mTries = 1; result = kAuthorizationResultAllow; context = hints;
}
else
if (*currentMechanism == "switch_to_user")
{
Process &cltProc = Server::active().connection().process;
uid_t cltUid = (cltProc.uid() != 0) ? cltProc.uid() : auth.creatorUid();
secdebug("SSevalMech", "terminating agent at request of process %d (UID %d)\n", cltProc.pid(), cltUid);
QueryInvokeMechanism client(cltUid, auth, agentName.c_str());
try {
client.terminateAgent();
} catch (...) {
}
result = kAuthorizationResultAllow;
}
}
switch(result)
{
case kAuthorizationResultAllow:
secdebug("SSevalMech", "result allow");
currentMechanism++;
break;
case kAuthorizationResultDeny:
secdebug("SSevalMech", "result deny");
break;
case kAuthorizationResultUndefined:
secdebug("SSevalMech", "result undefined");
break; case kAuthorizationResultUserCanceled:
secdebug("SSevalMech", "result canceled");
break; default:
break; }
}
if ((result == kAuthorizationResultAllow) ||
(result == kAuthorizationResultUserCanceled)) {
secdebug("SSevalMech", "storing new context for authorization");
auth.setInfoSet(context);
}
switch(result)
{
case kAuthorizationResultDeny:
return errAuthorizationDenied;
case kAuthorizationResultUserCanceled:
return errAuthorizationCanceled;
case kAuthorizationResultAllow:
return errAuthorizationSuccess;
default:
return errAuthorizationInternal;
}
}
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;
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 = evaluateMechanism(inRight, environmentToClient, auth, credentials);
if (status == errAuthorizationSuccess)
{
status = errAuthorizationDenied;
AuthItemSet inContext = auth.infoSet();
CredentialSet newCredentials = makeCredentials(inContext);
for (CredentialSet::const_iterator it = newCredentials.begin(); it != newCredentials.end(); ++it)
{
const Credential& newCredential = *it;
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
Syslog::error("uid %lu failed to authenticate as user %s for right %s.", auth.creatorUid(), newCredential->username().c_str(), inRight->name());
if (!newCredential->isValid())
{
reason = SecurityAgent::invalidPassphrase; continue;
}
status = evaluateCredentialForRight(inRight, inRule, environmentToClient, now, newCredential, true);
if (status == errAuthorizationSuccess)
{
credentials.erase(newCredential); credentials.insert(newCredential);
secdebug("SSevalMech", "added valid credential for user %s", newCredential->username().c_str());
status = errAuthorizationSuccess;
break;
}
else
reason = SecurityAgent::userNotInGroup; }
if (status == errAuthorizationSuccess)
break;
}
else
if ((status == errAuthorizationCanceled) ||
(status == errAuthorizationInternal))
break;
}
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); evaluateMechanism(inRight, environmentToClient, auth, credentials);
}
Process &cltProc = Server::active().connection().process;
uid_t cltUid = (cltProc.uid() != 0) ? cltProc.uid() : auth.creatorUid();
secdebug("SSevalMech", "terminating agent at request of process %d (UID %d)\n", cltProc.pid(), cltUid);
string agentName = agentNameForAuth(auth);
QueryInvokeMechanism client(cltUid, auth, agentName.c_str());
try {
client.terminateAgent();
} catch (...) {
}
return status;
}
CredentialSet
RuleImpl::makeCredentials(const AuthItemSet &context) 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("SSevalMech", "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("SSevalMech", "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("SSevalMech", "found gid");
}
if (username.length() && uid && gid)
{
newCredentials.insert(Credential(username, *uid, *gid, mShared));
}
else
{
found = find_if(context.begin(), context.end(), FindAuthItemByRightName(kAuthorizationEnvironmentPassword) );
if (found != context.end())
{
secdebug("SSevalMech", "found password");
string password = (**found).stringValue();
secdebug("SSevalMech", "falling back on username/password credential if valid");
newCredentials.insert(Credential(username, password, mShared));
}
}
} while(0);
return newCredentials;
}
OSStatus
RuleImpl::evaluateSessionOwner(const AuthItemRef &inRight, const Rule &inRule,
const AuthItemSet &environment,
const CFAbsoluteTime now,
const AuthorizationToken &auth,
string& usernamehint) const
{
OSStatus status = noErr;
uid_t uid = auth.creatorUid();
Server::active().longTermActivity();
struct passwd *pw = getpwuid(uid);
if (pw != NULL)
{
if ( (pw->pw_passwd == NULL) ||
strcmp(pw->pw_passwd, "*") ) {
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;
} endpwent();
}
return status;
}
OSStatus
RuleImpl::evaluateCredentialForRight(const AuthItemRef &inRight, const Rule &inRule, const AuthItemSet &environment, CFAbsoluteTime now, const Credential &credential, bool ignoreShared) const
{
assert(mType == kUser);
const char *user = credential->username().c_str();
if (!credential->isValid())
{
secdebug("autheval", "credential for user %s is invalid, denying right %s", user, inRight->name());
return errAuthorizationDenied;
}
if (now - credential->creationTime() > mMaxCredentialAge)
{
secdebug("autheval", "credential for user %s has expired, denying right %s", user, inRight->name());
return errAuthorizationDenied;
}
if (!ignoreShared && !mShared && credential->isShared())
{
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)
{
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 (mGroupName.length())
{
const char *groupname = mGroupName.c_str();
Server::active().longTermActivity();
struct group *gr = getgrnam(groupname);
if (!gr)
return errAuthorizationDenied;
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)
{
if (!strcmp(*group, user))
{
secdebug("autheval", "user %s is a member of group %s, granting right %s",
user, groupname, inRight->name());
endgrent();
return errAuthorizationSuccess;
}
}
secdebug("autheval", "user %s is not a member of group %s, denying right %s",
user, groupname, inRight->name());
endgrent();
}
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
{
if (mAllowRoot && auth.creatorUid() == 0)
{
secdebug("autheval", "creator of authorization has uid == 0 granting right %s",
inRight->name());
return errAuthorizationSuccess;
}
if (name() == kAuthorizationRuleIsAdmin)
{
string username;
if (!evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, username))
return errAuthorizationSuccess;
}
for (CredentialSet::const_iterator it = credentials.begin(); it != credentials.end(); ++it)
{
OSStatus status = evaluateCredentialForRight(inRight, inRule, environmentToClient, now, *it, true);
if (status != errAuthorizationDenied)
{
auth.setCredentialInfo(*it);
return status;
}
}
if (inCredentials)
{
for (CredentialSet::const_iterator it = inCredentials->begin(); it != inCredentials->end(); ++it)
{
OSStatus status = evaluateCredentialForRight(inRight, inRule, environmentToClient, now, *it, false);
if (status == errAuthorizationSuccess)
{
credentials.erase(*it); credentials.insert(*it);
auth.setCredentialInfo(*it);
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);
if (mEvalDef.size() == 0)
return evaluateAuthorizationOld(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
else
return evaluateAuthorization(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
}
OSStatus
RuleImpl::evaluateMechanismOnly(const AuthItemRef &inRight, const Rule &inRule, AuthItemSet &environmentToClient, AuthorizationToken &auth, CredentialSet &outCredentials) const
{
uint32 tries = 0;
OSStatus status;
do
{
setAgentHints(inRight, inRule, environmentToClient, auth);
AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
environmentToClient.erase(triesHint); environmentToClient.insert(triesHint);
status = evaluateMechanism(inRight, environmentToClient, auth, outCredentials);
tries++;
}
while ((status == errAuthorizationDenied) && ((mTries == 0) || ((mTries > 0) && (tries < mTries))));
if (name() != "system.login.console")
{
string agentName = agentNameForAuth(auth);
Process &cltProc = Server::active().connection().process;
uid_t cltUid = (cltProc.uid() != 0) ? cltProc.uid() : auth.creatorUid();
secdebug("SSevalMech", "terminating agent at request of process %d (UID %d)\n", cltProc.pid(), cltUid);
QueryInvokeMechanism client(cltUid, auth, agentName.c_str());
try
{
client.terminateAgent();
} catch (...) {
}
}
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
{
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);
if ((status == errAuthorizationCanceled) || (status == errAuthorizationInternal))
return status;
if (status != errAuthorizationSuccess)
{
if (mType == kKofN)
continue;
break;
}
else
count++;
}
return status; }
OSStatus
RuleImpl::evaluate(const AuthItemRef &inRight, const Rule &inRule,
AuthItemSet &environmentToClient, AuthorizationFlags flags,
CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials,
AuthorizationToken &auth) const
{
switch (mType)
{
case kAllow:
secdebug("autheval", "rule is always allow");
return errAuthorizationSuccess;
case kDeny:
secdebug("autheval", "rule is always deny");
return errAuthorizationDenied;
case kUser:
secdebug("autheval", "rule is user");
return evaluateUser(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
case kRuleDelegation:
secdebug("autheval", "rule evaluates rules");
return evaluateRules(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
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); }
}
OSStatus
RuleImpl::evaluateAuthorizationOld(const AuthItemRef &inRight, const Rule &inRule,
AuthItemSet &environmentToClient,
AuthorizationFlags flags, CFAbsoluteTime now,
const CredentialSet *inCredentials,
CredentialSet &credentials, AuthorizationToken &auth) const
{
Process &cltProc = Server::active().connection().process;
uid_t cltUid = (cltProc.uid() != 0) ? cltProc.uid() : auth.creatorUid();
secdebug("autheval", "Auth query from process %d (UID %d)", cltProc.pid(), cltUid);
QueryAuthorizeByGroup query(cltUid, auth);
string usernamehint;
evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, usernamehint);
Credential newCredential;
SecurityAgent::Reason reason = SecurityAgent::userNotInGroup;
for (uint32_t tryCount = 0; tryCount < mTries; ++tryCount)
{
OSStatus status = obtainCredential(query, inRight, environmentToClient, usernamehint.c_str(), newCredential, reason);
if (status)
return status;
if (!newCredential->isValid())
reason = SecurityAgent::invalidPassphrase;
else {
status = evaluateCredentialForRight(inRight, inRule, environmentToClient, now, newCredential, true);
if (status == errAuthorizationSuccess)
{
credentials.erase(newCredential); credentials.insert(newCredential);
query.done();
auth.setCredentialInfo(newCredential);
return errAuthorizationSuccess;
}
else if (status != errAuthorizationDenied)
return status;
}
reason = SecurityAgent::userNotInGroup;
}
query.cancel(SecurityAgent::tooManyTries);
return errAuthorizationDenied;
}
OSStatus
RuleImpl::obtainCredential(QueryAuthorizeByGroup &query, const AuthItemRef &inRight,
AuthItemSet &environmentToClient, const char *usernameHint, Credential &outCredential, SecurityAgent::Reason reason) const
{
char nameBuffer[SecurityAgent::maxUsernameLength];
char passphraseBuffer[SecurityAgent::maxPassphraseLength];
OSStatus status = errAuthorizationDenied;
try {
if (query(mGroupName.c_str(), usernameHint, nameBuffer, passphraseBuffer, reason))
status = noErr;
} catch (const CssmCommonError &err) {
status = err.osStatus();
} catch (...) {
status = errAuthorizationInternal;
}
if (status == CSSM_ERRCODE_USER_CANCELED)
{
secdebug("auth", "canceled obtaining credential for user in group %s", mGroupName.c_str());
return errAuthorizationCanceled;
}
if (status == CSSM_ERRCODE_NO_USER_INTERACTION)
{
secdebug("auth", "user interaction not possible obtaining credential for user in group %s", mGroupName.c_str());
return errAuthorizationInteractionNotAllowed;
}
if (status != noErr)
{
secdebug("auth", "failed obtaining credential for user in group %s", mGroupName.c_str());
return status;
}
secdebug("auth", "obtained credential for user %s", nameBuffer);
string username(nameBuffer);
string password(passphraseBuffer);
outCredential = Credential(username, password, mShared);
return errAuthorizationSuccess;
}
Rule::Rule() : RefPointer<RuleImpl>(new RuleImpl()) {}
Rule::Rule(const string &inRightName, CFDictionaryRef cfRight, CFDictionaryRef cfRules) : RefPointer<RuleImpl>(new RuleImpl(inRightName, cfRight, cfRules)) {}
}