/* * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the * specific language governing rights and limitations under the License. */ /* * AuthorizationEngine.cpp * Authorization * * Created by Michael Brouwer on Thu Oct 12 2000. * Copyright (c) 2000 Apple Computer Inc. All rights reserved. * */ #include "AuthorizationEngine.h" #include "server.h" #include "authority.h" #include #include #include #include #include #include #include #include #include #include #include #include // for longname lookup #include // private header (lu_utils.h from lookup project) extern "C" { int lookupd_query(ni_proplist *l, ni_proplist ***out); ni_proplist *lookupd_make_query(char *cat, char *fmt, ...); int _lu_running(void); } using namespace Authorization; // // Errors to be thrown // Error::Error(int err) : error(err) { } const char *Error::what() const { return "Authorization error"; } CSSM_RETURN Error::cssmError() const { return error; } // @@@ eventually... OSStatus Error::osStatus() const { return error; } void Error::throwMe(int err) { throw Error(err); } // // CredentialImpl class // // only for testing whether this credential is usable CredentialImpl::CredentialImpl(const string &username, const uid_t uid, const gid_t gid, bool shared) : mUsername(username), mShared(shared), mUid(uid), mGid(gid), mCreationTime(CFAbsoluteTimeGetCurrent()), mValid(true) { } // credential with validity based on username/password combination. CredentialImpl::CredentialImpl(const string &username, const string &password, bool shared) : mShared(shared), mCreationTime(CFAbsoluteTimeGetCurrent()), mValid(false) { // try short name first const char *user = username.c_str(); struct passwd *pw = getpwnam(user); do { if ( !pw && _lu_running() ) { // try lookup query to find passed username as a long name (realname in NI-speak) ni_proplist **out = NULL; // query "user" records. "k" specifies position of keys in varargs ni_proplist *in = lookupd_make_query("user", "kv", "realname", user); if (!in) break; int results = lookupd_query(in, &out); ni_proplist_free(in); if (!out) break; // Find the first, if any, name value in returned records, getpwnam, and dispose of them for (int i=0; ini_proplist_len; j++) { if ( !strcmp(nipl->ni_proplist_val[j].nip_name, "name") && (nipl->ni_proplist_val[j].nip_val.ni_namelist_len > 0) ) pw = getpwnam( *(nipl->ni_proplist_val[j].nip_val.ni_namelist_val) ); } ni_proplist_free(nipl); } free(out); } if (!pw) { debug("autheval", "user %s not found, creating invalid credential", user); break; } if (pw->pw_passwd != NULL && pw->pw_passwd[0]) { const char *passwd = password.c_str(); if (strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd)) { debug("autheval", "password for user %s is invalid, creating invalid credential", user); break; } } debug("autheval", "password for user %s is ok, creating%s credential", user, mShared ? " shared" : ""); mUsername = string ( pw->pw_name ); mUid = pw->pw_uid; mGid = pw->pw_gid; mValid = true; } while (0); if (pw) endpwent(); } CredentialImpl::~CredentialImpl() { } bool CredentialImpl::operator < (const CredentialImpl &other) const { if (!mShared && other.mShared) return true; if (!other.mShared && mShared) return false; return mUsername < other.mUsername; } // Returns true if this CredentialImpl should be shared. bool CredentialImpl::isShared() const { return mShared; } // Merge with other void CredentialImpl::merge(const CredentialImpl &other) { assert(mUsername == other.mUsername); if (other.mValid && (!mValid || mCreationTime < other.mCreationTime)) { mCreationTime = other.mCreationTime; mUid = other.mUid; mGid = other.mGid; mValid = true; } } // The time at which this credential was obtained. CFAbsoluteTime CredentialImpl::creationTime() const { return mCreationTime; } // Return true iff this credential is valid. bool CredentialImpl::isValid() const { return mValid; } void CredentialImpl::invalidate() { mValid = false; } // // Credential class // Credential::Credential() : RefPointer(NULL) { } Credential::Credential(CredentialImpl *impl) : RefPointer(impl) { } Credential::Credential(const string &username, const uid_t uid, const gid_t gid, bool shared) : RefPointer(new CredentialImpl(username, uid, gid, shared)) { } Credential::Credential(const string &username, const string &password, bool shared) : RefPointer(new CredentialImpl(username, password, shared)) { } Credential::~Credential() { } bool Credential::operator < (const Credential &other) const { if (!*this) return other; if (!other) return false; return (**this) < (*other); } // // Right class // Right & Right::overlay(AuthorizationItem &item) { return static_cast(item); } Right * Right::overlay(AuthorizationItem *item) { return static_cast(item); } Right::Right() { name = ""; valueLength = 0; value = NULL; flags = 0; } Right::Right(AuthorizationString inName, size_t inValueLength, const void *inValue) { name = inName; valueLength = inValueLength; value = const_cast(inValue); } Right::~Right() { } bool Right::operator < (const Right &other) const { return strcmp(name, other.name) < 0; } // // RightSet class // const AuthorizationRights RightSet::gEmptyRights = { 0, NULL }; RightSet::RightSet(const AuthorizationRights *rights) : mRights(const_cast(rights ? rights : &gEmptyRights)) { } RightSet::RightSet(const RightSet &other) { mRights = other.mRights; } RightSet::~RightSet() { } RightSet::const_reference RightSet::back() const { // @@@ Should this if empty::throwMe()? return static_cast(mRights->items[size() - 1]); } // // MutableRightSet class // MutableRightSet::MutableRightSet(size_t count, const Right &element) : mCapacity(count) { mRights = new AuthorizationRights(); mRights->items = reinterpret_cast(malloc(sizeof(Right) * mCapacity)); if (!mRights->items) { delete mRights; throw std::bad_alloc(); } mRights->count = count; for (size_type ix = 0; ix < count; ++ix) mRights->items[ix] = element; } MutableRightSet::MutableRightSet(const RightSet &other) { size_type count = other.size(); mCapacity = count; mRights = new AuthorizationRights(); mRights->items = reinterpret_cast(malloc(sizeof(Right) * mCapacity)); if (!mRights->items) { delete mRights; throw std::bad_alloc(); } mRights->count = count; for (size_type ix = 0; ix < count; ++ix) mRights->items[ix] = other.mRights->items[ix]; } MutableRightSet::~MutableRightSet() { free(mRights->items); delete mRights; } MutableRightSet & MutableRightSet::operator = (const RightSet &other) { size_type count = other.size(); if (capacity() < count) grow(count); mRights->count = count; for (size_type ix = 0; ix < count; ++ix) mRights->items[ix] = other.mRights->items[ix]; return *this; } void MutableRightSet::swap(MutableRightSet &other) { AuthorizationRights *rights = mRights; size_t capacity = mCapacity; mRights = other.mRights; mCapacity = other.mCapacity; other.mRights = rights; other.mCapacity = capacity; } MutableRightSet::reference MutableRightSet::back() { // @@@ Should this if empty::throwMe()? return static_cast(mRights->items[size() - 1]); } void MutableRightSet::push_back(const_reference right) { if (size() >= capacity()) grow(capacity() + 1); mRights->items[mRights->count] = right; mRights->count++; } void MutableRightSet::pop_back() { // @@@ Should this if empty::throwMe()? if (!empty()) mRights->count--; } void MutableRightSet::grow(size_type min_capacity) { size_type newCapacity = mCapacity * mCapacity; if (newCapacity < min_capacity) newCapacity = min_capacity; void *newItems = realloc(mRights->items, sizeof(*mRights->items) * newCapacity); if (!newItems) throw std::bad_alloc(); mRights->items = reinterpret_cast(newItems); mCapacity = newCapacity; } // // Rule class // CFStringRef Rule::kUserInGroupID = CFSTR("group"); CFStringRef Rule::kTimeoutID = CFSTR("timeout"); CFStringRef Rule::kSharedID = CFSTR("shared"); CFStringRef Rule::kAllowRootID = CFSTR("allow-root"); CFStringRef Rule::kDenyID = CFSTR("deny"); CFStringRef Rule::kAllowID = CFSTR("allow"); Rule::Rule() : mType(kUserInGroup), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false) { // @@@ Default rule is shared admin group with 5 minute timeout } Rule::Rule(CFTypeRef cfRule) { // @@@ This code is ugly. Serves me right for using CF. if (CFGetTypeID(cfRule) == CFStringGetTypeID()) { CFStringRef tag = reinterpret_cast(cfRule); if (CFEqual(kAllowID, tag)) { debug("authrule", "rule always allow"); mType = kAllow; } else if (CFEqual(kDenyID, tag)) { debug("authrule", "rule always deny"); mType = kDeny; } else Error::throwMe(); } else if (CFGetTypeID(cfRule) == CFDictionaryGetTypeID()) { mType = kUserInGroup; CFDictionaryRef dict = reinterpret_cast(cfRule); CFTypeRef groupTag = CFDictionaryGetValue(dict, kUserInGroupID); if (!groupTag || CFGetTypeID(groupTag) != CFStringGetTypeID()) Error::throwMe(); CFStringRef group = reinterpret_cast(groupTag); char buffer[512]; const char *ptr = CFStringGetCStringPtr(group, kCFStringEncodingUTF8); if (ptr == NULL) { if (CFStringGetCString(group, buffer, 512, kCFStringEncodingUTF8)) ptr = buffer; else Error::throwMe(); } mGroupName = string(ptr); mMaxCredentialAge = DBL_MAX; CFTypeRef timeoutTag = CFDictionaryGetValue(dict, kTimeoutID); if (timeoutTag) { if (CFGetTypeID(timeoutTag) != CFNumberGetTypeID()) Error::throwMe(); CFNumberGetValue(reinterpret_cast(timeoutTag), kCFNumberDoubleType, &mMaxCredentialAge); } CFTypeRef sharedTag = CFDictionaryGetValue(dict, kSharedID); mShared = false; if (sharedTag) { if (CFGetTypeID(sharedTag) != CFBooleanGetTypeID()) Error::throwMe(); mShared = CFBooleanGetValue(reinterpret_cast(sharedTag)); } CFTypeRef allowRootTag = CFDictionaryGetValue(dict, kAllowRootID); mAllowRoot = false; if (allowRootTag) { if (CFGetTypeID(allowRootTag) != CFBooleanGetTypeID()) Error::throwMe(); mAllowRoot = CFBooleanGetValue(reinterpret_cast(allowRootTag)); } debug("authrule", "rule user in group \"%s\" timeout %g%s%s", mGroupName.c_str(), mMaxCredentialAge, mShared ? " shared" : "", mAllowRoot ? " allow-root" : ""); } } Rule::Rule(const Rule &other) : mType(other.mType), mGroupName(other.mGroupName), mMaxCredentialAge(other.mMaxCredentialAge), mShared(other.mShared), mAllowRoot(other.mAllowRoot) { } Rule & Rule::operator = (const Rule &other) { mType = other.mType; mGroupName = other.mGroupName; mMaxCredentialAge = other.mMaxCredentialAge; mShared = other.mShared; mAllowRoot = other.mAllowRoot; return *this; } Rule::~Rule() { } OSStatus Rule::evaluate(const Right &inRight, const AuthorizationEnvironment *environment, AuthorizationFlags flags, CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials, const AuthorizationToken &auth) { switch (mType) { case kAllow: debug("autheval", "rule is always allow"); return errAuthorizationSuccess; case kDeny: debug("autheval", "rule is always deny"); return errAuthorizationDenied; case kUserInGroup: debug("autheval", "rule is user in group"); break; default: Error::throwMe(); } // If we got here, this is a kUserInGroup 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) { debug("autheval", "creator of authorization has uid == 0 granting right %s", inRight.rightName()); return errAuthorizationSuccess; } // 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 = evaluate(inRight, environment, now, *it, true); if (status != errAuthorizationDenied) return status; } // Second -- go though the credentials passed in to this authorize operation by the state management layer. if (inCredentials) { for (CredentialSet::const_iterator it = inCredentials->begin(); it != inCredentials->end(); ++it) { OSStatus status = evaluate(inRight, environment, now, *it, false); if (status == errAuthorizationSuccess) { // Add the credential we used to the output set. // @@@ Deal with potential credential merges. credentials.insert(*it); return status; } else if (status != errAuthorizationDenied) 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. if (!(flags & kAuthorizationFlagExtendRights)) return errAuthorizationDenied; if (!(flags & kAuthorizationFlagInteractionAllowed)) return errAuthorizationInteractionNotAllowed; QueryAuthorizeByGroup query; string usernamehint; // @@@ This should really be the loginname of the proccess that originally created the AuthorizationRef. // For now we get the pw_name of the user with the uid of the calling process. uid_t uid = query.uid(); if (uid) { struct passwd *pw = getpwuid(uid); if (pw != NULL) { // avoid hinting a locked account (ie. root) if ( (pw->pw_passwd == NULL) || 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 if (evaluate(inRight, environment, now, Credential(pw->pw_name, pw->pw_uid, pw->pw_gid, mShared), true) == errAuthorizationSuccess) { // user long name as hint usernamehint = string( pw->pw_gecos ); // minus other gecos crud size_t comma = usernamehint.find(','); if (comma) usernamehint = usernamehint.substr(0, comma); // or fallback to short username if (usernamehint.size() == 0) usernamehint = string( pw->pw_name ); } //fi } //fi endpwent(); } } Credential newCredential; // @@@ Keep the default reason the same, so the agent only gets userNotInGroup or invalidPassphrase SecurityAgent::Reason reason = SecurityAgent::userNotInGroup; // @@@ Hardcoded 3 tries to avoid infinite loops. for (int tryCount = 0; tryCount < 3; ++tryCount) { // Obtain a new credential. Anything but success is considered an error. OSStatus status = obtainCredential(query, inRight, environment, usernamehint.c_str(), newCredential, reason); if (status) return status; // Now we have successfully obtained a credential we need to make sure it authorizes the requested right if (!newCredential->isValid()) reason = SecurityAgent::invalidPassphrase; else { status = evaluate(inRight, environment, now, newCredential, true); if (status == errAuthorizationSuccess) { // Add the new credential we obtained to the output set. // @@@ Deal with potential credential merges. credentials.insert(newCredential); query.done(); return errAuthorizationSuccess; } else if (status != errAuthorizationDenied) return status; reason = SecurityAgent::userNotInGroup; } } query.cancel(SecurityAgent::tooManyTries); return errAuthorizationDenied; } // Return errAuthorizationSuccess if this rule allows access based on the specified credential, // return errAuthorizationDenied otherwise. OSStatus Rule::evaluate(const Right &inRight, const AuthorizationEnvironment *environment, CFAbsoluteTime now, const Credential &credential, bool ignoreShared) { assert(mType == kUserInGroup); // Get the username from the credential const char *user = credential->username().c_str(); // If the credential is not valid or it's age is more than the allowed maximum age // for a credential, deny. if (!credential->isValid()) { debug("autheval", "credential for user %s is invalid, denying right %s", user, inRight.rightName()); return errAuthorizationDenied; } if (now - credential->creationTime() > mMaxCredentialAge) { debug("autheval", "credential for user %s has expired, denying right %s", user, inRight.rightName()); return errAuthorizationDenied; } if (!ignoreShared && !mShared && credential->isShared()) { debug("autheval", "shared credential for user %s cannot be used, denying right %s", user, inRight.rightName()); return errAuthorizationDenied; } // A root (uid == 0) user can do anything if (credential->uid() == 0) { debug("autheval", "user %s has uid 0, granting right %s", user, inRight.rightName()); return errAuthorizationSuccess; } const char *groupname = mGroupName.c_str(); struct group *gr = getgrnam(groupname); if (!gr) return errAuthorizationDenied; // Is this the default group of this user? // declares gr_gid int, as opposed to advertised (getgrent(3)) gid_t if (credential->gid() == gr->gr_gid) { debug("autheval", "user %s has group %s(%d) as default group, granting right %s", user, groupname, gr->gr_gid, inRight.rightName()); endgrent(); return errAuthorizationSuccess; } for (char **group = gr->gr_mem; *group; ++group) { if (!strcmp(*group, user)) { debug("autheval", "user %s is a member of group %s, granting right %s", user, groupname, inRight.rightName()); endgrent(); return errAuthorizationSuccess; } } debug("autheval", "user %s is not a member of group %s, denying right %s", user, groupname, inRight.rightName()); endgrent(); return errAuthorizationDenied; } OSStatus Rule::obtainCredential(QueryAuthorizeByGroup &query, const Right &inRight, const AuthorizationEnvironment *environment, const char *usernameHint, Credential &outCredential, SecurityAgent::Reason reason) { 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) { debug("auth", "canceled obtaining credential for user in group %s", mGroupName.c_str()); return errAuthorizationCanceled; } if (status == CSSM_ERRCODE_NO_USER_INTERACTION) { debug("auth", "user interaction not possible obtaining credential for user in group %s", mGroupName.c_str()); return errAuthorizationInteractionNotAllowed; } if (status != noErr) { debug("auth", "failed obtaining credential for user in group %s", mGroupName.c_str()); return status; } debug("auth", "obtained credential for user %s", nameBuffer); string username(nameBuffer); string password(passphraseBuffer); outCredential = Credential(username, password, mShared); return errAuthorizationSuccess; } // // Engine class // Engine::Engine(const char *configFile) : mLastChecked(DBL_MIN) { mRulesFileName = new char[strlen(configFile) + 1]; strcpy(mRulesFileName, configFile); memset(&mRulesFileMtimespec, 0, sizeof(mRulesFileMtimespec)); } Engine::~Engine() { delete[] mRulesFileName; } void Engine::updateRules(CFAbsoluteTime now) { if (mRules.empty()) readRules(); else { // Don't do anything if we checked the timestamp less than 5 seconds ago if (mLastChecked > now - 5.0) return; struct stat st; if (stat(mRulesFileName, &st)) { Syslog::error("Stating rules file \"%s\": %s", mRulesFileName, strerror(errno)); /* @@@ No rules file found, use defaults: admin group for everything. */ //UnixError::throwMe(errno); } else { // @@@ Make sure this is the right way to compare 2 struct timespec thingies // Technically we should check st_dev and st_ino as well since if either of those change // we are looking at a different file too. if (memcmp(&st.st_mtimespec, &mRulesFileMtimespec, sizeof(mRulesFileMtimespec))) readRules(); } } mLastChecked = now; } void Engine::readRules() { // Make an entry in the mRules map that matches every right to the default Rule. mRules.clear(); mRules.insert(RuleMap::value_type(string(), Rule())); int fd = open(mRulesFileName, O_RDONLY, 0); if (fd == -1) { Syslog::error("Opening rules file \"%s\": %s", mRulesFileName, strerror(errno)); return; } try { struct stat st; if (fstat(fd, &st)) UnixError::throwMe(errno); mRulesFileMtimespec = st.st_mtimespec; off_t fileSize = st.st_size; CFRef xmlData(CFDataCreateMutable(NULL, fileSize)); CFDataSetLength(xmlData, fileSize); void *buffer = CFDataGetMutableBytePtr(xmlData); size_t bytesRead = read(fd, buffer, fileSize); if (bytesRead != fileSize) { if (bytesRead == static_cast(-1)) { Syslog::error("Reading rules file \"%s\": %s", mRulesFileName, strerror(errno)); return; } Syslog::error("Could only read %ul out of %ul bytes from rules file \"%s\"", bytesRead, fileSize, mRulesFileName); return; } CFStringRef errorString; CFRef newRoot(reinterpret_cast (CFPropertyListCreateFromXMLData(NULL, xmlData, kCFPropertyListImmutable, &errorString))); if (!newRoot) { char buffer[512]; const char *error = CFStringGetCStringPtr(errorString, kCFStringEncodingUTF8); if (error == NULL) { if (CFStringGetCString(errorString, buffer, 512, kCFStringEncodingUTF8)) error = buffer; } Syslog::error("Parsing rules file \"%s\": %s", mRulesFileName, error); return; } if (CFGetTypeID(newRoot) != CFDictionaryGetTypeID()) { Syslog::error("Rules file \"%s\": is not a dictionary", mRulesFileName); return; } parseRules(newRoot); } catch(...) { close(fd); } close(fd); } void Engine::parseRules(CFDictionaryRef rules) { CFDictionaryApplyFunction(rules, parseRuleCallback, this); } void Engine::parseRuleCallback(const void *key, const void *value, void *context) { Engine *engine = reinterpret_cast(context); if (CFGetTypeID(key) != CFStringGetTypeID()) return; CFStringRef right = reinterpret_cast(key); engine->parseRule(right, reinterpret_cast(value)); } void Engine::parseRule(CFStringRef cfRight, CFTypeRef cfRule) { char buffer[512]; const char *ptr = CFStringGetCStringPtr(cfRight, kCFStringEncodingUTF8); if (ptr == NULL) { if (CFStringGetCString(cfRight, buffer, 512, kCFStringEncodingUTF8)) ptr = buffer; } string right(ptr); try { mRules[right] = Rule(cfRule); debug("authrule", "added rule for right \"%s\"", right.c_str()); } catch (...) { Syslog::error("Rules file \"%s\" right \"%s\": rule is invalid", mRulesFileName, ptr); } } /*! @function AuthorizationEngine::getRule Look up the Rule for a given right. @param inRight (input) the right for which we want a rule. @results The Rule for right */ Rule Engine::getRule(const Right &inRight) const { string key(inRight.rightName()); for (;;) { RuleMap::const_iterator it = mRules.find(key); if (it != mRules.end()) { debug("authrule", "right \"%s\" using right expression \"%s\"", inRight.rightName(), key.c_str()); return it->second; } // no default rule assert (key.size()); // any reduction of a combination of two chars is futile if (key.size() > 2) { // find last dot with exception of possible dot at end string::size_type index = key.rfind('.', key.size() - 2); // cut right after found dot, or make it match default rule key = key.substr(0, index == string::npos ? 0 : index + 1); } else key.erase(); } } /*! @function AuthorizationEngine::authorize @@@. @param inRights (input) List of rights being requested for authorization. @param environment (optional/input) Environment containing information to be used during evaluation. @param flags (input) Optional flags @@@ see AuthorizationCreate for a description. @param inCredentials (input) Credentials already held by the caller. @param outCredentials (output/optional) Credentials obtained, used or refreshed during this call to authorize the requested rights. @param outRights (output/optional) Subset of inRights which were actually authorized. @results Returns errAuthorizationSuccess if all rights requested are authorized, or if the kAuthorizationFlagPartialRights flag was specified. Might return other status values like errAuthorizationDenied, errAuthorizationCanceled or errAuthorizationInteractionNotAllowed */ OSStatus Engine::authorize(const RightSet &inRights, const AuthorizationEnvironment *environment, AuthorizationFlags flags, const CredentialSet *inCredentials, CredentialSet *outCredentials, MutableRightSet *outRights, const AuthorizationToken &auth) { CredentialSet credentials; MutableRightSet rights; OSStatus status = errAuthorizationSuccess; // Get current time of day. CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); // Update rules from database if needed updateRules(now); // Check if a credential was passed into the environment and we were asked to extend the rights if (environment && (flags & kAuthorizationFlagExtendRights)) { const AuthorizationItem *username = NULL, *password = NULL; bool shared = false; for (UInt32 ix = 0; ix < environment->count; ++ix) { const AuthorizationItem &item = environment->items[ix]; if (!strcmp(item.name, kAuthorizationEnvironmentUsername)) username = &item; if (!strcmp(item.name, kAuthorizationEnvironmentPassword)) password = &item; if (!strcmp(item.name, kAuthorizationEnvironmentShared)) shared = true; } if (username && password) { // Let's create a credential from the passed in username and password. Credential newCredential(string(reinterpret_cast(username->value), username->valueLength), string(reinterpret_cast(password->value), password->valueLength), shared); // If it's valid insert it into the credentials list. Normally this is // only done if it actually authorizes a requested right, but for this // special case (environment) we do it even when no rights are being requested. if (newCredential->isValid()) credentials.insert(newCredential); } } RightSet::const_iterator end = inRights.end(); for (RightSet::const_iterator it = inRights.begin(); it != end; ++it) { // Get the rule for each right we are trying to obtain. OSStatus result = getRule(*it).evaluate(*it, environment, flags, now, inCredentials, credentials, auth); if (result == errAuthorizationSuccess) rights.push_back(*it); else if (result == errAuthorizationDenied || result == errAuthorizationInteractionNotAllowed) { if (!(flags & kAuthorizationFlagPartialRights)) { status = result; break; } } else if (result == errAuthorizationCanceled) { status = result; break; } else { Syslog::error("Engine::authorize: Rule::evaluate returned %ld returning errAuthorizationInternal", result); status = errAuthorizationInternal; break; } } if (outCredentials) outCredentials->swap(credentials); if (outRights) outRights->swap(rights); return status; }