]> git.saurik.com Git - apple/securityd.git/blobdiff - src/AuthorizationRule.cpp
securityd-55199.3.tar.gz
[apple/securityd.git] / src / AuthorizationRule.cpp
index 1fc8290fb07eaa70f84f84546d8df39a8b7af67f..451618f51c13adbdcf880d429f9abf4a8c042dfb 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- *  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@
  *  
  *
  *  @APPLE_LICENSE_HEADER_START@
  *  
 #include <Security/AuthorizationDB.h>
 #include <Security/AuthorizationPriv.h>
 #include <security_utilities/logging.h>
 #include <Security/AuthorizationDB.h>
 #include <Security/AuthorizationPriv.h>
 #include <security_utilities/logging.h>
-#include <security_utilities/ccaudit.h>
 #include <bsm/audit_uevents.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 "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 <pwd.h>
 #include <grp.h>
 #include <unistd.h>
@@ -48,6 +49,8 @@ extern "C" {
 #include <membershipPriv.h>
 }
 
 #include <membershipPriv.h>
 }
 
+using namespace CommonCriteria::Securityd;
+    
 //
 // Rule class
 //
 //
 // Rule class
 //
@@ -61,7 +64,9 @@ CFStringRef RuleImpl::kMechanismsID = CFSTR(kAuthorizationRuleParameterMechanism
 CFStringRef RuleImpl::kSessionOwnerID = CFSTR(kAuthorizationRuleParameterCredentialSessionOwner);
 CFStringRef RuleImpl::kKofNID = CFSTR(kAuthorizationRuleParameterKofN);
 CFStringRef RuleImpl::kPromptID = CFSTR(kAuthorizationRuleParameterDefaultPrompt);
 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::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::kRuleClassID = CFSTR(kAuthorizationRuleClass);
 CFStringRef RuleImpl::kRuleAllowID = CFSTR(kAuthorizationRuleClassAllow);
@@ -73,7 +78,7 @@ CFStringRef RuleImpl::kRuleAuthenticateUserID = CFSTR(kAuthorizationRuleParamete
 
 
 string
 
 
 string
-RuleImpl::Attribute::getString(CFDictionaryRef config, CFStringRef key, bool required = false, char *defaultValue = "")
+RuleImpl::Attribute::getString(CFDictionaryRef config, CFStringRef key, bool required = false, const char *defaultValue = "")
 {
        CFTypeRef value = CFDictionaryGetValue(config, key);
        if (value && (CFGetTypeID(value) == CFStringGetTypeID()))
 {
        CFTypeRef value = CFDictionaryGetValue(config, key);
        if (value && (CFGetTypeID(value) == CFStringGetTypeID()))
@@ -86,7 +91,10 @@ RuleImpl::Attribute::getString(CFDictionaryRef config, CFStringRef key, bool req
                        if (CFStringGetCString(stringValue, buffer, sizeof(buffer), kCFStringEncodingUTF8))
                                ptr = buffer;
                        else
                        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);
                }
 
                return string(ptr);
@@ -95,7 +103,10 @@ RuleImpl::Attribute::getString(CFDictionaryRef config, CFStringRef key, bool req
                if (!required)
                        return string(defaultValue);
                else
                if (!required)
                        return string(defaultValue);
                else
-                       MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+               {
+                       Syslog::alert("Failed to get rule string");
+                       MacOSError::throwMe(errAuthorizationInternal);
+               }
 }                      
 
 double
 }                      
 
 double
@@ -112,7 +123,10 @@ RuleImpl::Attribute::getDouble(CFDictionaryRef config, CFStringRef key, bool req
                if (!required)
                        return defaultValue;
                else
                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;
 }
                        
        return doubleValue;
 }
@@ -131,7 +145,10 @@ RuleImpl::Attribute::getBool(CFDictionaryRef config, CFStringRef key, bool requi
                if (!required)
                        return defaultValue;
                else
                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;
 }
        
        return boolValue;
 }
@@ -146,7 +163,8 @@ RuleImpl::Attribute::getVector(CFDictionaryRef config, CFStringRef key, bool req
        {
                CFArrayRef evalArray = reinterpret_cast<CFArrayRef>(value);
 
        {
                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()))
                {
                        CFTypeRef arrayValue = CFArrayGetValueAtIndex(evalArray, index);
                        if (arrayValue && (CFGetTypeID(arrayValue) == CFStringGetTypeID()))
@@ -159,7 +177,10 @@ RuleImpl::Attribute::getVector(CFDictionaryRef config, CFStringRef key, bool req
                                        if (CFStringGetCString(stringValue, buffer, sizeof(buffer), kCFStringEncodingUTF8))
                                                ptr = buffer;
                                        else
                                        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));
                        }
                                }
                                valueArray.push_back(string(ptr));
                        }
@@ -167,19 +188,22 @@ RuleImpl::Attribute::getVector(CFDictionaryRef config, CFStringRef key, bool req
        }
        else
                if (required)
        }
        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;
 }
 
 
                        
        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;
 {
        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);
        }
                if (promptsDict && (CFGetTypeID(promptsDict) == CFDictionaryGetTypeID()))
                        numberOfPrompts = CFDictionaryGetCount(promptsDict);
        }
@@ -194,13 +218,15 @@ bool RuleImpl::Attribute::getLocalizedPrompts(CFDictionaryRef config, map<string
        {
                CFStringRef keyRef = reinterpret_cast<CFStringRef>(keys[numberOfPrompts]);
                CFStringRef valueRef = reinterpret_cast<CFStringRef>(values[numberOfPrompts]);
        {
                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;
                        continue;
-               if (!valueRef || (CFGetTypeID(valueRef) != CFStringGetTypeID()))
+               }
+               if (!valueRef || (CFGetTypeID(valueRef) != CFStringGetTypeID())) {
                        continue;
                        continue;
+               }
                string key = cfString(keyRef);
                string value = cfString(valueRef);
                string key = cfString(keyRef);
                string value = cfString(valueRef);
-               localizedPrompts[kAuthorizationRuleParameterDescription+key] = value;
+               localizedPrompts[descriptionKey + key] = value;
        }
 
        return true;
        }
 
        return true;
@@ -209,19 +235,22 @@ bool RuleImpl::Attribute::getLocalizedPrompts(CFDictionaryRef config, map<string
 
 // default rule
 RuleImpl::RuleImpl() :
 
 // default rule
 RuleImpl::RuleImpl() :
-mType(kUser), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false), mSessionOwner(false), mTries(0), mAuthenticateUser(true)
+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.
 {
        // 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())
 {
        // @@@ 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;
 
                        
        mTries = 0;
 
@@ -256,8 +285,9 @@ RuleImpl::RuleImpl(const string &inRightName, CFDictionaryRef cfRight, CFDiction
                                if (cfRuleDef && CFGetTypeID(cfRuleDef) == CFDictionaryGetTypeID())
                                        mEvalDef = Attribute::getVector(cfRuleDef, kMechanismsID);
                        }
                                if (cfRuleDef && CFGetTypeID(cfRuleDef) == CFDictionaryGetTypeID())
                                        mEvalDef = Attribute::getVector(cfRuleDef, kMechanismsID);
                        }
-                       mTries = int(Attribute::getDouble(cfRight, kTriesID, false, 3.0)); // XXX/cs double(kAuthorizationMaxTries)
+                       mTries = int(Attribute::getDouble(cfRight, kTriesID, false, double(kMaximumAuthorizationTries)));
                        mAuthenticateUser = Attribute::getBool(cfRight, kRuleAuthenticateUserID, false, true);
                        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(),
 
                        secdebug("authrule", "%s : rule user in group \"%s\" timeout %g%s%s",
                                inRightName.c_str(),
@@ -273,6 +303,7 @@ RuleImpl::RuleImpl(const string &inRightName, CFDictionaryRef cfRight, CFDiction
                        mEvalDef = Attribute::getVector(cfRight, kMechanismsID, true);
                        mTries = int(Attribute::getDouble(cfRight, kTriesID, false, 0.0)); // "forever"
                        mShared = Attribute::getBool(cfRight, kSharedID, false, true);
                        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)
                {
                }
                else if (classTag == kAuthorizationRightRule)
                {
@@ -289,7 +320,10 @@ RuleImpl::RuleImpl(const string &inRightName, CFDictionaryRef cfRight, CFDiction
                                if (ruleDefRef)
                                        CFRelease(ruleDefRef);
                                if (!cfRuleDef || CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID())
                                if (ruleDefRef)
                                        CFRelease(ruleDefRef);
                                if (!cfRuleDef || CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID())
-                                       MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+                               {
+                                       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
                                mRuleDef.push_back(Rule(ruleDefString, cfRuleDef, cfRules));
                        }
                        else // array
@@ -302,7 +336,10 @@ RuleImpl::RuleImpl(const string &inRightName, CFDictionaryRef cfRight, CFDiction
                                        if (ruleNameRef)
                                                CFRelease(ruleNameRef);
                                        if (!cfRuleDef || (CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID()))
                                        if (ruleNameRef)
                                                CFRelease(ruleNameRef);
                                        if (!cfRuleDef || (CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID()))
-                                               MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+                                       {
+                                               Syslog::alert("Invalid rule '%s'in rule set", it->c_str());
+                                               MacOSError::throwMe(errAuthorizationInternal);
+                                       }
                                        mRuleDef.push_back(Rule(*it, cfRuleDef, cfRules));
                                }
                        }
                                        mRuleDef.push_back(Rule(*it, cfRuleDef, cfRules));
                                }
                        }
@@ -314,8 +351,9 @@ RuleImpl::RuleImpl(const string &inRightName, CFDictionaryRef cfRight, CFDiction
                }
                else
                {
                }
                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
                }
        }
        else
@@ -332,11 +370,15 @@ RuleImpl::RuleImpl(const string &inRightName, CFDictionaryRef cfRight, CFDiction
                if (ruleNameRef)
                        CFRelease(ruleNameRef);
                if (!cfRuleDef || CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID())
                if (ruleNameRef)
                        CFRelease(ruleNameRef);
                if (!cfRuleDef || CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID())
-                       MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+               {
+                       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));
        }
 
                mRuleDef.push_back(Rule(ruleName, cfRuleDef, cfRules));
        }
 
-       Attribute::getLocalizedPrompts(cfRight, mLocalizedPrompts);
+       Attribute::getLocalizedText(cfRight, mLocalizedPrompts, kPromptID, kAuthorizationRuleParameterDescription);
+       Attribute::getLocalizedText(cfRight, mLocalizedButtons, kButtonID, kAuthorizationRuleParameterButton);
 }
 
 /*
 }
 
 /*
@@ -356,6 +398,10 @@ RuleImpl::setAgentHints(const AuthItemRef &inRight, const Rule &inTopLevelRule,
        environmentToClient.erase(AuthItemRef(AGENT_HINT_CREATOR_PID)); 
        environmentToClient.insert(AuthItemRef(AGENT_HINT_CREATOR_PID, AuthValueOverlay(sizeof(pid_t), &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())
        Process &thisProcess = Server::process();
        string bundlePath;
        if (SecStaticCodeRef clientCode = auth.creatorCode())
@@ -369,9 +415,12 @@ RuleImpl::setAgentHints(const AuthItemRef &inRight, const Rule &inTopLevelRule,
        environmentToClient.insert(processHints.begin(), processHints.end());
 
        map<string,string> defaultPrompts = inTopLevelRule->localizedPrompts();
        environmentToClient.insert(processHints.begin(), processHints.end());
 
        map<string,string> defaultPrompts = inTopLevelRule->localizedPrompts();
+       map<string,string> defaultButtons = inTopLevelRule->localizedButtons();
 
        if (defaultPrompts.empty())
                defaultPrompts = localizedPrompts();
 
        if (defaultPrompts.empty())
                defaultPrompts = localizedPrompts();
+       if (defaultButtons.empty())
+               defaultButtons = localizedButtons();
                
        if (!defaultPrompts.empty())
        {
                
        if (!defaultPrompts.empty())
        {
@@ -383,6 +432,16 @@ RuleImpl::setAgentHints(const AuthItemRef &inRight, const Rule &inTopLevelRule,
                        environmentToClient.insert(AuthItemRef(key.c_str(), AuthValueOverlay(value)));
                }
        }
                        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();
 
        // add rulename as a hint
        string ruleName = name();
@@ -394,12 +453,12 @@ RuleImpl::setAgentHints(const AuthItemRef &inRight, const Rule &inTopLevelRule,
 // we'll run that and validate the credentials from there.
 // we fall back on a default configuration from the authenticate rule
 OSStatus
 // 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) const
+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;
 {
        OSStatus status = errAuthorizationDenied;
 
        Credential hintCredential;
-       if (errAuthorizationSuccess == evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, 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())
                if (hintCredential->name().length())
                        environmentToClient.insert(AuthItemRef(AGENT_HINT_SUGGESTED_USER, AuthValueOverlay(hintCredential->name())));
                if (hintCredential->realname().length())
@@ -410,13 +469,51 @@ RuleImpl::evaluateAuthentication(const AuthItemRef &inRight, const Rule &inRule,
                environmentToClient.insert(AuthItemRef(AGENT_HINT_REQUIRE_USER_IN_GROUP, AuthValueOverlay(mGroupName)));
 
        uint32 tries;
                environmentToClient.insert(AuthItemRef(AGENT_HINT_REQUIRE_USER_IN_GROUP, AuthValueOverlay(mGroupName)));
 
        uint32 tries;
-       SecurityAgent::Reason reason = SecurityAgent::noReason;
+       reason = SecurityAgent::noReason;
 
        Process &cltProc = Server::process();
        // Authorization preserves creator's UID in setuid processes
 
        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);
  
        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;
+       }
+       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++)
        AgentMechanismEvaluator eval(cltUid, auth.session(), mEvalDef);
 
        for (tries = 0; tries < mTries; tries++)
@@ -426,89 +523,93 @@ RuleImpl::evaluateAuthentication(const AuthItemRef &inRight, const Rule &inRule,
                AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
                environmentToClient.erase(triesHint); environmentToClient.insert(triesHint); // replace
 
                AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
                environmentToClient.erase(triesHint); environmentToClient.insert(triesHint); // replace
 
-               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;
-                       
-                       CredentialSet newCredentials = makeCredentials(auth);
-                       // clear context after extracting credentials
-                       auth.scrubInfoSet(); 
-                       
-                       CommonCriteria::AuditRecord auditrec(auth.creatorAuditToken());
-                       for (CredentialSet::const_iterator it = newCredentials.begin(); it != newCredentials.end(); ++it)
-                       {
-                               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->name().c_str(), newCredential->uid(), inRight->name());
-                                       auditrec.submit(AUE_ssauthint, CommonCriteria::errNone, 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->name().c_str(), inRight->name());
-                                       auditrec.submit(AUE_ssauthint, CommonCriteria::errInvalidCredential, inRight->name());
-                               }
-                               
-                               if (!newCredential->isValid())
-                               {
-                                       reason = SecurityAgent::invalidPassphrase; //invalidPassphrase;
-                                       continue;
-                               }
-
-                               // verify that this credential authorizes right
-                               status = evaluateUserCredentialForRight(auth, inRight, inRule, environmentToClient, now, newCredential, true);
-                               
-                               if (status == errAuthorizationSuccess)
-                               {
-                                       if (auth.operatesAsLeastPrivileged()) {
-                                               Credential rightCredential(inRight->name(), mShared);
-                                               credentials.erase(rightCredential); credentials.insert(rightCredential);
-                                               if (mShared)
-                                                       credentials.insert(Credential(inRight->name(), false));
-                                       } else {
-                                               // 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);
-                                       secdebug("SSevalMech", "added valid credential for user %s", newCredential->name().c_str());
-                                       status = errAuthorizationSuccess;
-                                       break;
-                               }
-                               else
-                                       reason = SecurityAgent::userNotInGroup; //unacceptableUser; // userNotInGroup
-                       }
+            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);
+            }
+
+            // 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);
+                
+                for (CredentialSet::const_iterator it = newCredentials.begin(); it != newCredentials.end(); ++it)
+                {
+                    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;
+                    }
+                }
 
                        if (status == errAuthorizationSuccess)
                                break;
                }
                else
 
                        if (status == errAuthorizationSuccess)
                                break;
                }
                else
-                       if ((status == errAuthorizationCanceled) ||
-               (status == errAuthorizationInternal))
+                       if ((status == errAuthorizationCanceled) || (status == errAuthorizationInternal))
                        {
                        {
-                               auth.scrubInfoSet();
+                               auth.scrubInfoSet(false);
                                break;
                        }
                        else // last mechanism is now authentication - fail
                                if (status == errAuthorizationDenied)
                                        reason = SecurityAgent::invalidPassphrase;
                                break;
                        }
                        else // last mechanism is now authentication - fail
                                if (status == errAuthorizationDenied)
                                        reason = SecurityAgent::invalidPassphrase;
-}
+        }
 
        // If we fell out of the loop because of too many tries, notify user
        if (tries == mTries)
 
        // If we fell out of the loop because of too many tries, notify user
        if (tries == mTries)
@@ -518,12 +619,11 @@ RuleImpl::evaluateAuthentication(const AuthItemRef &inRight, const Rule &inRule,
                environmentToClient.erase(retryHint); environmentToClient.insert(retryHint); // replace
                AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
                environmentToClient.erase(triesHint); environmentToClient.insert(triesHint); // replace
                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);
+            eval.run(AuthValueVector(), environmentToClient, auth);
                // XXX/cs is this still necessary?
                // XXX/cs is this still necessary?
-               auth.scrubInfoSet();
+               auth.scrubInfoSet(false);
                
                
-               CommonCriteria::AuditRecord auditrec(auth.creatorAuditToken());
-               auditrec.submit(AUE_ssauthorize, CommonCriteria::errTooManyTries, inRight->name());
+        rightAuthLogger.logFailure(NULL, CommonCriteria::errTooManyTries);
        }
 
        return status;
        }
 
        return status;
@@ -565,36 +665,28 @@ RuleImpl::makeCredentials(const AuthorizationToken &auth) const
 
 // evaluate whether a good credential of the current session owner would authorize a right
 OSStatus
 
 // 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, Credential &credential) 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
        // @@@ 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.
        
 {
        // 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;
-       Session &session = auth.session();
        Credential sessionCredential;
        Credential sessionCredential;
-       if (session.haveOriginatorUid()) {
-               // preflight session credential as if it were a fresh copy
-               const Credential &cred = session.originatorCredential();
-               sessionCredential = Credential(cred->uid(), cred->name(), cred->realname(), mShared/*ignored*/);
-       } else {
-               uid = auth.creatorUid();
-               Server::active().longTermActivity();
-               struct passwd *pw = getpwuid(uid);
-               if (pw != NULL) {
-                       // avoid hinting a locked account
-                       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
-                               secdebug("AuthEvalMech", "preflight credential from current user, result follows:");
-                               sessionCredential = Credential(pw->pw_uid, pw->pw_name, pw->pw_gecos, mShared/*ignored*/);
-                       } //fi
-                       endpwent();
-               }
+       uid_t uid = auth.session().originatorUid();
+       Server::active().longTermActivity();
+       struct passwd *pw = getpwuid(uid);
+       if (pw != NULL) {
+               // avoid hinting a locked account
+               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
+                       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);
+       OSStatus status = evaluateUserCredentialForRight(auth, inRight, inRule, environment, now, sessionCredential, true, reason);
        if (errAuthorizationSuccess == status)
                credential = sessionCredential;
 
        if (errAuthorizationSuccess == status)
                credential = sessionCredential;
 
@@ -603,43 +695,66 @@ RuleImpl::evaluateSessionOwner(const AuthItemRef &inRight, const Rule &inRule, c
 
 
 OSStatus
 
 
 OSStatus
-RuleImpl::evaluateCredentialForRight(const AuthorizationToken &auth, const AuthItemRef &inRight, const Rule &inRule, const AuthItemSet &environment, CFAbsoluteTime now, const Credential &credential, bool ignoreShared) const
+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 (auth.operatesAsLeastPrivileged()) {
-               if (credential->isRight() && credential->isValid() && (inRight->name() == credential->name()))
-                       return errAuthorizationSuccess;
-               else
-                       return errAuthorizationDenied;
+        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
        } else
-               return evaluateUserCredentialForRight(auth, inRight, inRule, environment, now, credential, false);
+               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
 }
 
 // Return errAuthorizationSuccess if this rule allows access based on the specified credential,
 // return errAuthorizationDenied otherwise.
 OSStatus
-RuleImpl::evaluateUserCredentialForRight(const AuthorizationToken &auth, 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);
 
 {
        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->name().c_str();
 
        // Get the username from the credential
        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())
        {
        // 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)
        {
                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())
        {
                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;
        }
                secdebug("autheval", "shared credential for user %s cannot be used, denying right %s", user, inRight->name());
                return errAuthorizationDenied;
        }
@@ -654,18 +769,20 @@ RuleImpl::evaluateUserCredentialForRight(const AuthorizationToken &auth, const A
        if (mSessionOwner)
        {
                Session &session = auth.session();
        if (mSessionOwner)
        {
                Session &session = auth.session();
-               if (session.haveOriginatorUid())
-               {
-                       uid_t console_user = session.originatorUid();
+               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;
-                       }
+               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.");
+               // 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())
        }
        
        if (mGroupName.length())
@@ -680,12 +797,21 @@ RuleImpl::evaluateUserCredentialForRight(const AuthorizationToken &auth, const A
                {
                        uuid_t group_uuid, user_uuid;
                        int is_member;
                {
                        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))
                        if (mbr_group_name_to_uuid(groupname, group_uuid))
                                break;
                                
                        if (mbr_uid_to_uuid(credential->uid(), user_uuid))
-                               break;
+                       {
+                               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 (mbr_check_membership(user_uuid, group_uuid, &is_member))
                                break;
@@ -699,10 +825,15 @@ RuleImpl::evaluateUserCredentialForRight(const AuthorizationToken &auth, const A
                                
                }
                while (0);
                                
                }
                while (0);
-
+        
+        reason = SecurityAgent::userNotInGroup;
                secdebug("autheval", "user %s is not a member of group %s, denying right %s",
                        user, groupname, inRight->name());
        }
                secdebug("autheval", "user %s is not a member of group %s, denying right %s",
                        user, groupname, inRight->name());
        }
+    else if (mSessionOwner) // rule asks only if user is the session owner
+    {
+        reason = SecurityAgent::unacceptableUser;
+    }
        
        return errAuthorizationDenied;
 }
        
        return errAuthorizationDenied;
 }
@@ -710,15 +841,17 @@ RuleImpl::evaluateUserCredentialForRight(const AuthorizationToken &auth, const A
 
 
 OSStatus
 
 
 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)
        {
        // 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;
                secdebug("autheval", "creator of authorization has uid == 0 granting right %s",
                        inRight->name());
                return errAuthorizationSuccess;
@@ -728,10 +861,13 @@ RuleImpl::evaluateUser(const AuthItemRef &inRight, const Rule &inRule, AuthItemS
        if (!mAuthenticateUser)
        {
                Credential hintCredential;
        if (!mAuthenticateUser)
        {
                Credential hintCredential;
-               OSStatus status = evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, hintCredential);
+               OSStatus status = evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, hintCredential, reason);
 
                if (!status)
 
                if (!status)
+        {
+            SECURITYD_AUTH_USER_ALLOWSESSIONOWNER(&auth);
                        return errAuthorizationSuccess;
                        return errAuthorizationSuccess;
+        }
 
                return errAuthorizationDenied;
        }
 
                return errAuthorizationDenied;
        }
@@ -739,10 +875,10 @@ RuleImpl::evaluateUser(const AuthItemRef &inRight, const Rule &inRule, AuthItemS
        // 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)
        {
        // 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)
        {
-               // Passed in user credentials are allowed for least privileged mode
+               // Passed-in user credentials are allowed for least-privileged mode
                if (auth.operatesAsLeastPrivileged() && !(*it)->isRight() && (*it)->isValid()) 
                {
                if (auth.operatesAsLeastPrivileged() && !(*it)->isRight() && (*it)->isValid()) 
                {
-                       OSStatus status = evaluateUserCredentialForRight(auth, inRight, inRule, environmentToClient, now, *it, false);
+                       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 (errAuthorizationSuccess == status) {
                                Credential rightCredential(inRight->name(), mShared);
                                credentials.erase(rightCredential); credentials.insert(rightCredential);
@@ -753,11 +889,11 @@ RuleImpl::evaluateUser(const AuthItemRef &inRight, const Rule &inRule, AuthItemS
                }
 
                // if this is least privileged, this will function differently: match credential to requested right
                }
 
                // if this is least privileged, this will function differently: match credential to requested right
-               OSStatus status = evaluateCredentialForRight(auth, inRight, inRule, environmentToClient, now, *it, false);
+               OSStatus status = evaluateCredentialForRight(auth, inRight, inRule, environmentToClient, now, *it, false, reason);
                        
                if (status != errAuthorizationDenied) {
                        // add credential to authinfo
                        
                if (status != errAuthorizationDenied) {
                        // add credential to authinfo
-                       auth.setCredentialInfo(*it);
+                       auth.setCredentialInfo(*it, savePassword);
                        return status;
                }
 
                        return status;
                }
 
@@ -769,7 +905,7 @@ RuleImpl::evaluateUser(const AuthItemRef &inRight, const Rule &inRule, AuthItemS
                for (CredentialSet::const_iterator it = inCredentials->begin(); it != inCredentials->end(); ++it)
                {
                        // if this is least privileged, this will function differently: match credential to requested right
                for (CredentialSet::const_iterator it = inCredentials->begin(); it != inCredentials->end(); ++it)
                {
                        // if this is least privileged, this will function differently: match credential to requested right
-                       OSStatus status = evaluateCredentialForRight(auth, inRight, inRule, environmentToClient, now, *it, false);
+                       OSStatus status = evaluateCredentialForRight(auth, inRight, inRule, environmentToClient, now, *it, false, reason);
 
                        if (status == errAuthorizationSuccess)
                        {
 
                        if (status == errAuthorizationSuccess)
                        {
@@ -777,7 +913,7 @@ RuleImpl::evaluateUser(const AuthItemRef &inRight, const Rule &inRule, AuthItemS
                                // 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
                                // 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);
+                               auth.setCredentialInfo(*it, savePassword);
 
                                return status;
                        }
 
                                return status;
                        }
@@ -803,11 +939,11 @@ RuleImpl::evaluateUser(const AuthItemRef &inRight, const Rule &inRule, AuthItemS
 
        setAgentHints(inRight, inRule, environmentToClient, auth);
 
 
        setAgentHints(inRight, inRule, environmentToClient, auth);
 
-       return evaluateAuthentication(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
+       return evaluateAuthentication(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth, reason, savePassword);
 }
 
 OSStatus
 }
 
 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;
 {
        uint32 tries = 0; 
        OSStatus status;
@@ -819,26 +955,61 @@ RuleImpl::evaluateMechanismOnly(const AuthItemRef &inRight, const Rule &inRule,
 
        {
                AgentMechanismEvaluator eval(cltUid, auth.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
                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);
-                       
+            
+            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");
                        if ((status == errAuthorizationSuccess) ||
                                (status == errAuthorizationCanceled)) // @@@ can only pass back sideband through context
                        {
                                secdebug("AuthEvalMech", "storing new context for authorization");
-                               auth.setInfoSet(eval.context());
+                               auth.setInfoSet(eval.context(), savePassword);
                                if (status == errAuthorizationSuccess)
                                {
                                if (status == errAuthorizationSuccess)
                                {
+                    // (try to) attach the authorizing UID to the least-priv cred
                                        if (auth.operatesAsLeastPrivileged())
                                        if (auth.operatesAsLeastPrivileged())
-                                               outCredentials.insert(Credential(inRight->name(), mShared));
-                                       else
-                                               outCredentials = makeCredentials(auth);
+                    {
+                        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());
                                }
                        }
 
                                }
                        }
 
@@ -851,8 +1022,10 @@ RuleImpl::evaluateMechanismOnly(const AuthItemRef &inRight, const Rule &inRule,
        }
        
        // HACK kill all hosts to free pages for low memory systems
        }
        
        // 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")
        {
        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());
                QueryInvokeMechanism query(securityAgent, auth.session());
                query.terminateAgent();
                QueryInvokeMechanism query2(privilegedAuthHost, auth.session());
@@ -863,7 +1036,7 @@ RuleImpl::evaluateMechanismOnly(const AuthItemRef &inRight, const Rule &inRule,
 }
 
 OSStatus
 }
 
 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())
 {
        // line up the rules to try
        if (!mRuleDef.size())
@@ -880,7 +1053,7 @@ RuleImpl::evaluateRules(const AuthItemRef &inRight, const Rule &inRule, AuthItem
                        return errAuthorizationSuccess;
 
                // get a rule and try it
                        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))
 
                // if status is cancel/internal error abort
                if ((status == errAuthorizationCanceled) || (status == errAuthorizationInternal))
@@ -897,36 +1070,42 @@ RuleImpl::evaluateRules(const AuthItemRef &inRight, const Rule &inRule, AuthItem
                else
                        count++;
        }
                else
                        count++;
        }
+
+       if ((mType == kKofN) && (status == errAuthorizationSuccess) && (count < mKofN))
+               status = errAuthorizationDenied;
        
        return status; // return the last failure
 }
 
 
 OSStatus
        
        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:
 {
        switch (mType)
        {
        case kAllow:
-               secdebug("autheval", "rule is always allow");
+        SECURITYD_AUTH_ALLOW(&auth, (char *)name().c_str());
                return errAuthorizationSuccess;
        case kDeny:
                return errAuthorizationSuccess;
        case kDeny:
-               secdebug("autheval", "rule is always deny");
+        SECURITYD_AUTH_DENY(&auth, (char *)name().c_str());
                return errAuthorizationDenied;
        case kUser:
                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:
        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:
        case kKofN:
-               secdebug("autheval", "rule evaluates k-of-n rules");
-               return evaluateRules(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
+        SECURITYD_AUTH_KOFN(&auth, (char *)name().c_str());
+               return evaluateRules(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth, reason, savePassword);
        case kEvaluateMechanisms:
        case kEvaluateMechanisms:
-               secdebug("autheval", "rule evaluates mechanisms");
-               return evaluateMechanismOnly(inRight, inRule, environmentToClient, auth, credentials);
+        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:
        default:
-               MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
+               Syslog::alert("Unrecognized rule type %d", mType);
+               MacOSError::throwMe(errAuthorizationInternal); // invalid rule
        }
 }
 
        }
 }