2  *  Copyright (c) 2003-2004 Apple Computer, Inc. All Rights Reserved. 
   4  *  @APPLE_LICENSE_HEADER_START@ 
   6  *  This file contains Original Code and/or Modifications of Original Code 
   7  *  as defined in and that are subject to the Apple Public Source License 
   8  *  Version 2.0 (the 'License'). You may not use this file except in 
   9  *  compliance with the License. Please obtain a copy of the License at 
  10  *  http://www.opensource.apple.com/apsl/ and read it before using this 
  13  *  The Original Code and all software distributed under the License are 
  14  *  distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  15  *  EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  16  *  INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  17  *  FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  18  *  Please see the License for the specific language governing rights and 
  19  *  limitations under the License. 
  21  *  @APPLE_LICENSE_HEADER_END@ 
  23  *  AuthorizationRule.cpp 
  28 #include "AuthorizationRule.h" 
  29 #include <Security/AuthorizationTags.h> 
  30 #include <Security/AuthorizationTagsPriv.h> 
  31 #include <Security/AuthorizationDB.h> 
  32 #include <Security/AuthorizationPriv.h> 
  33 #include <security_utilities/logging.h> 
  34 #include <security_utilities/ccaudit.h> 
  35 #include <bsm/audit_uevents.h> 
  36 #include "authority.h" 
  39 #include "agentquery.h" 
  40 #include "AuthorizationMechEval.h" 
  45 #include <membership.h> 
  48 #include <membershipPriv.h> 
  54 namespace Authorization 
{ 
  56 CFStringRef 
RuleImpl::kUserGroupID 
= CFSTR(kAuthorizationRuleParameterGroup
); 
  57 CFStringRef 
RuleImpl::kTimeoutID 
= CFSTR(kAuthorizationRuleParameterCredentialTimeout
); 
  58 CFStringRef 
RuleImpl::kSharedID 
= CFSTR(kAuthorizationRuleParameterCredentialShared
); 
  59 CFStringRef 
RuleImpl::kAllowRootID 
= CFSTR(kAuthorizationRuleParameterAllowRoot
); 
  60 CFStringRef 
RuleImpl::kMechanismsID 
= CFSTR(kAuthorizationRuleParameterMechanisms
); 
  61 CFStringRef 
RuleImpl::kSessionOwnerID 
= CFSTR(kAuthorizationRuleParameterCredentialSessionOwner
); 
  62 CFStringRef 
RuleImpl::kKofNID 
= CFSTR(kAuthorizationRuleParameterKofN
); 
  63 CFStringRef 
RuleImpl::kPromptID 
= CFSTR(kAuthorizationRuleParameterDefaultPrompt
); 
  64 CFStringRef 
RuleImpl::kTriesID 
= CFSTR("tries"); // XXX/cs move to AuthorizationTagsPriv.h 
  66 CFStringRef 
RuleImpl::kRuleClassID 
= CFSTR(kAuthorizationRuleClass
); 
  67 CFStringRef 
RuleImpl::kRuleAllowID 
= CFSTR(kAuthorizationRuleClassAllow
); 
  68 CFStringRef 
RuleImpl::kRuleDenyID 
= CFSTR(kAuthorizationRuleClassDeny
); 
  69 CFStringRef 
RuleImpl::kRuleUserID 
= CFSTR(kAuthorizationRuleClassUser
); 
  70 CFStringRef 
RuleImpl::kRuleDelegateID 
= CFSTR(kAuthorizationRightRule
); 
  71 CFStringRef 
RuleImpl::kRuleMechanismsID 
= CFSTR(kAuthorizationRuleClassMechanisms
); 
  72 CFStringRef 
RuleImpl::kRuleAuthenticateUserID 
= CFSTR(kAuthorizationRuleParameterAuthenticateUser
); 
  76 RuleImpl::Attribute::getString(CFDictionaryRef config
, CFStringRef key
, bool required 
= false, char *defaultValue 
= "") 
  78         CFTypeRef value 
= CFDictionaryGetValue(config
, key
); 
  79         if (value 
&& (CFGetTypeID(value
) == CFStringGetTypeID())) 
  81                 CFStringRef stringValue 
= reinterpret_cast<CFStringRef
>(value
); 
  83                 const char *ptr 
= CFStringGetCStringPtr(stringValue
, kCFStringEncodingUTF8
); 
  86                         if (CFStringGetCString(stringValue
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
)) 
  89                                 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule 
  96                         return string(defaultValue
); 
  98                         MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule 
 102 RuleImpl::Attribute::getDouble(CFDictionaryRef config
, CFStringRef key
, bool required 
= false, double defaultValue 
= 0.0) 
 104         double doubleValue 
= 0; 
 106         CFTypeRef value 
= CFDictionaryGetValue(config
, key
); 
 107         if (value 
&& (CFGetTypeID(value
) == CFNumberGetTypeID())) 
 109                 CFNumberGetValue(reinterpret_cast<CFNumberRef
>(value
), kCFNumberDoubleType
, &doubleValue
); 
 115                         MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule 
 121 RuleImpl::Attribute::getBool(CFDictionaryRef config
, CFStringRef key
, bool required 
= false, bool defaultValue 
= false) 
 123         bool boolValue 
= false; 
 124         CFTypeRef value 
= CFDictionaryGetValue(config
, key
); 
 126         if (value 
&& (CFGetTypeID(value
) == CFBooleanGetTypeID())) 
 128                 boolValue 
= CFBooleanGetValue(reinterpret_cast<CFBooleanRef
>(value
)); 
 134                         MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule 
 140 RuleImpl::Attribute::getVector(CFDictionaryRef config
, CFStringRef key
, bool required 
= false) 
 142         vector
<string
> valueArray
; 
 144         CFTypeRef value 
= CFDictionaryGetValue(config
, key
); 
 145         if (value 
&& (CFGetTypeID(value
) == CFArrayGetTypeID())) 
 147                 CFArrayRef evalArray 
= reinterpret_cast<CFArrayRef
>(value
); 
 149                 for (int index
=0; index 
< CFArrayGetCount(evalArray
); index
++) 
 151                         CFTypeRef arrayValue 
= CFArrayGetValueAtIndex(evalArray
, index
); 
 152                         if (arrayValue 
&& (CFGetTypeID(arrayValue
) == CFStringGetTypeID())) 
 154                                 CFStringRef stringValue 
= reinterpret_cast<CFStringRef
>(arrayValue
); 
 156                                 const char *ptr 
= CFStringGetCStringPtr(stringValue
, kCFStringEncodingUTF8
); 
 159                                         if (CFStringGetCString(stringValue
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
)) 
 162                                                 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule 
 164                                 valueArray
.push_back(string(ptr
)); 
 170                         MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule 
 176 bool RuleImpl::Attribute::getLocalizedPrompts(CFDictionaryRef config
, map
<string
,string
> &localizedPrompts
) 
 178         CFIndex numberOfPrompts 
= 0; 
 179         CFDictionaryRef promptsDict
; 
 180         if (CFDictionaryContainsKey(config
, kPromptID
)) 
 182                 promptsDict 
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(config
, kPromptID
)); 
 183                 if (promptsDict 
&& (CFGetTypeID(promptsDict
) == CFDictionaryGetTypeID())) 
 184                         numberOfPrompts 
= CFDictionaryGetCount(promptsDict
); 
 186         if (numberOfPrompts 
== 0) 
 189         const void *keys
[numberOfPrompts
+1]; 
 190         const void *values
[numberOfPrompts
+1]; 
 191         CFDictionaryGetKeysAndValues(promptsDict
, &keys
[0], &values
[0]); 
 193         while (numberOfPrompts
-- > 0) 
 195                 CFStringRef keyRef 
= reinterpret_cast<CFStringRef
>(keys
[numberOfPrompts
]); 
 196                 CFStringRef valueRef 
= reinterpret_cast<CFStringRef
>(values
[numberOfPrompts
]); 
 197                 if (!keyRef 
|| (CFGetTypeID(keyRef
) != CFStringGetTypeID())) 
 199                 if (!valueRef 
|| (CFGetTypeID(valueRef
) != CFStringGetTypeID())) 
 201                 string key 
= cfString(keyRef
); 
 202                 string value 
= cfString(valueRef
); 
 203                 localizedPrompts
[kAuthorizationRuleParameterDescription
+key
] = value
; 
 211 RuleImpl::RuleImpl() : 
 212 mType(kUser
), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false), mSessionOwner(false), mTries(0), mAuthenticateUser(true) 
 214         // XXX/cs read default descriptions from somewhere 
 215         // @@@ Default rule is shared admin group with 5 minute timeout 
 218 // return rule built from rule definition; throw if invalid. 
 219 RuleImpl::RuleImpl(const string 
&inRightName
, CFDictionaryRef cfRight
, CFDictionaryRef cfRules
) : mRightName(inRightName
) 
 221         // @@@ make sure cfRight is non mutable and never used that way 
 223         if (CFGetTypeID(cfRight
) != CFDictionaryGetTypeID()) 
 224                 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule 
 228         string classTag 
= Attribute::getString(cfRight
, kRuleClassID
, false, ""); 
 230         if (classTag
.length()) 
 232                 if (classTag 
== kAuthorizationRuleClassAllow
) 
 234                         secdebug("authrule", "%s : rule allow", inRightName
.c_str()); 
 237                 else if (classTag 
== kAuthorizationRuleClassDeny
) 
 239                         secdebug("authrule", "%s : rule deny", inRightName
.c_str()); 
 242                 else if (classTag 
== kAuthorizationRuleClassUser
) 
 245                         mGroupName 
= Attribute::getString(cfRight
, kUserGroupID
); 
 246                         // grab other user-in-group attributes 
 247                         mMaxCredentialAge 
= Attribute::getDouble(cfRight
, kTimeoutID
, false, DBL_MAX
); 
 248                         mShared 
= Attribute::getBool(cfRight
, kSharedID
); 
 249                         mAllowRoot 
= Attribute::getBool(cfRight
, kAllowRootID
); 
 250                         mSessionOwner 
= Attribute::getBool(cfRight
, kSessionOwnerID
); 
 251                         // authorization tags can have eval now too 
 252                         mEvalDef 
= Attribute::getVector(cfRight
, kMechanismsID
); 
 253                         if (mEvalDef
.size() == 0 && cfRules 
/*only rights default see appserver-admin*/) 
 255                                 CFDictionaryRef cfRuleDef 
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, CFSTR("authenticate"))); 
 256                                 if (cfRuleDef 
&& CFGetTypeID(cfRuleDef
) == CFDictionaryGetTypeID()) 
 257                                         mEvalDef 
= Attribute::getVector(cfRuleDef
, kMechanismsID
); 
 259                         mTries 
= int(Attribute::getDouble(cfRight
, kTriesID
, false, 3.0)); // XXX/cs double(kAuthorizationMaxTries) 
 260                         mAuthenticateUser 
= Attribute::getBool(cfRight
, kRuleAuthenticateUserID
, false, true); 
 262                         secdebug("authrule", "%s : rule user in group \"%s\" timeout %g%s%s", 
 264                                 mGroupName
.c_str(), mMaxCredentialAge
, mShared 
? " shared" : "", 
 265                                 mAllowRoot 
? " allow-root" : ""); 
 268                 else if (classTag 
== kAuthorizationRuleClassMechanisms
) 
 270                         secdebug("authrule", "%s : rule evaluate mechanisms", inRightName
.c_str()); 
 271                         mType 
= kEvaluateMechanisms
; 
 272                         // mechanisms to evaluate 
 273                         mEvalDef 
= Attribute::getVector(cfRight
, kMechanismsID
, true); 
 274                         mTries 
= int(Attribute::getDouble(cfRight
, kTriesID
, false, 0.0)); // "forever" 
 275                         mShared 
= Attribute::getBool(cfRight
, kSharedID
, false, true); 
 277                 else if (classTag 
== kAuthorizationRightRule
) 
 279                         assert(cfRules
); // rules can't delegate to other rules 
 280                         secdebug("authrule", "%s : rule delegate rule", inRightName
.c_str()); 
 281                         mType 
= kRuleDelegation
; 
 284                         string ruleDefString 
= Attribute::getString(cfRight
, kRuleDelegateID
, false, ""); 
 285                         if (ruleDefString
.length()) 
 287                                 CFStringRef ruleDefRef 
= makeCFString(ruleDefString
); 
 288                                 CFDictionaryRef cfRuleDef 
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleDefRef
)); 
 290                                         CFRelease(ruleDefRef
); 
 291                                 if (!cfRuleDef 
|| CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID()) 
 292                                         MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule 
 293                                 mRuleDef
.push_back(Rule(ruleDefString
, cfRuleDef
, cfRules
)); 
 297                                 vector
<string
> ruleDef 
= Attribute::getVector(cfRight
, kRuleDelegateID
, true); 
 298                                 for (vector
<string
>::const_iterator it 
= ruleDef
.begin(); it 
!= ruleDef
.end(); it
++) 
 300                                         CFStringRef ruleNameRef 
= makeCFString(*it
); 
 301                                         CFDictionaryRef cfRuleDef 
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleNameRef
)); 
 303                                                 CFRelease(ruleNameRef
); 
 304                                         if (!cfRuleDef 
|| (CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID())) 
 305                                                 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule 
 306                                         mRuleDef
.push_back(Rule(*it
, cfRuleDef
, cfRules
)); 
 310                         mKofN 
= int(Attribute::getDouble(cfRight
, kKofNID
, false, 0.0)); 
 317                         secdebug("authrule", "%s : rule class unknown %s.", inRightName
.c_str(), classTag
.c_str()); 
 318                         MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule 
 323                 // no class tag means, this is the abbreviated specification from the API 
 324                 // it _must_ have a definition for "rule" which will be used as a delegate 
 325                 // it may have a comment (not extracted here) 
 326                 // it may have a default prompt, or a whole dictionary of languages (not extracted here) 
 327                 mType 
= kRuleDelegation
; 
 328                 string ruleName 
= Attribute::getString(cfRight
, kRuleDelegateID
, true); 
 329                 secdebug("authrule", "%s : rule delegate rule (1): %s", inRightName
.c_str(), ruleName
.c_str()); 
 330                 CFStringRef ruleNameRef 
= makeCFString(ruleName
); 
 331                 CFDictionaryRef cfRuleDef 
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleNameRef
)); 
 333                         CFRelease(ruleNameRef
); 
 334                 if (!cfRuleDef 
|| CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID()) 
 335                         MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule 
 336                 mRuleDef
.push_back(Rule(ruleName
, cfRuleDef
, cfRules
)); 
 339         Attribute::getLocalizedPrompts(cfRight
, mLocalizedPrompts
); 
 349 RuleImpl::setAgentHints(const AuthItemRef 
&inRight
, const Rule 
&inTopLevelRule
, AuthItemSet 
&environmentToClient
, AuthorizationToken 
&auth
) const 
 351         string 
authorizeString(inRight
->name()); 
 352         environmentToClient
.erase(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT
));  
 353         environmentToClient
.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT
, AuthValueOverlay(authorizeString
))); 
 355         pid_t creatorPid 
= auth
.creatorPid(); 
 356         environmentToClient
.erase(AuthItemRef(AGENT_HINT_CREATOR_PID
));  
 357         environmentToClient
.insert(AuthItemRef(AGENT_HINT_CREATOR_PID
, AuthValueOverlay(sizeof(pid_t
), &creatorPid
))); 
 359         Process 
&thisProcess 
= Server::process(); 
 361         if (SecStaticCodeRef clientCode 
= auth
.creatorCode()) 
 362                 bundlePath 
= codePath(clientCode
); 
 363         AuthItemSet processHints 
= SecurityAgent::Client::clientHints( 
 364                 SecurityAgent::bundle
, bundlePath
, thisProcess
.pid(), thisProcess
.uid()); 
 365         environmentToClient
.erase(AuthItemRef(AGENT_HINT_CLIENT_TYPE
)); 
 366         environmentToClient
.erase(AuthItemRef(AGENT_HINT_CLIENT_PATH
)); 
 367         environmentToClient
.erase(AuthItemRef(AGENT_HINT_CLIENT_PID
)); 
 368         environmentToClient
.erase(AuthItemRef(AGENT_HINT_CLIENT_UID
)); 
 369         environmentToClient
.insert(processHints
.begin(), processHints
.end()); 
 371         map
<string
,string
> defaultPrompts 
= inTopLevelRule
->localizedPrompts(); 
 373         if (defaultPrompts
.empty()) 
 374                 defaultPrompts 
= localizedPrompts(); 
 376         if (!defaultPrompts
.empty()) 
 378                 map
<string
,string
>::const_iterator it
; 
 379                 for (it 
= defaultPrompts
.begin(); it 
!= defaultPrompts
.end(); it
++) 
 381                         const string 
&key 
= it
->first
; 
 382                         const string 
&value 
= it
->second
; 
 383                         environmentToClient
.insert(AuthItemRef(key
.c_str(), AuthValueOverlay(value
))); 
 387         // add rulename as a hint 
 388         string ruleName 
= name(); 
 389     environmentToClient
.erase(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE
)); 
 390         environmentToClient
.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE
, AuthValueOverlay(ruleName
))); 
 393 // If a different evaluation for getting a credential is prescribed, 
 394 // we'll run that and validate the credentials from there. 
 395 // we fall back on a default configuration from the authenticate rule 
 397 RuleImpl::evaluateAuthentication(const AuthItemRef 
&inRight
, const Rule 
&inRule
,AuthItemSet 
&environmentToClient
, AuthorizationFlags flags
, CFAbsoluteTime now
, const CredentialSet 
*inCredentials
, CredentialSet 
&credentials
, AuthorizationToken 
&auth
) const 
 399         OSStatus status 
= errAuthorizationDenied
; 
 401         Credential hintCredential
; 
 402         if (errAuthorizationSuccess 
== evaluateSessionOwner(inRight
, inRule
, environmentToClient
, now
, auth
, hintCredential
)) { 
 403                 if (hintCredential
->name().length()) 
 404                         environmentToClient
.insert(AuthItemRef(AGENT_HINT_SUGGESTED_USER
, AuthValueOverlay(hintCredential
->name()))); 
 405                 if (hintCredential
->realname().length()) 
 406                         environmentToClient
.insert(AuthItemRef(AGENT_HINT_SUGGESTED_USER_LONG
, AuthValueOverlay(hintCredential
->realname()))); 
 409         if ((mType 
== kUser
) && (mGroupName
.length())) 
 410                 environmentToClient
.insert(AuthItemRef(AGENT_HINT_REQUIRE_USER_IN_GROUP
, AuthValueOverlay(mGroupName
))); 
 413         SecurityAgent::Reason reason 
= SecurityAgent::noReason
; 
 415         Process 
&cltProc 
= Server::process(); 
 416         // Authorization preserves creator's UID in setuid processes 
 417         uid_t cltUid 
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid(); 
 418         secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc
.pid(), cltUid
); 
 420         AgentMechanismEvaluator 
eval(cltUid
, auth
.session(), mEvalDef
); 
 422         for (tries 
= 0; tries 
< mTries
; tries
++) 
 424                 AuthItemRef 
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
)); 
 425                 environmentToClient
.erase(retryHint
); environmentToClient
.insert(retryHint
); // replace 
 426                 AuthItemRef 
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
)); 
 427                 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace 
 429                 status 
= eval
.run(AuthValueVector(), environmentToClient
, auth
); 
 431                 if ((status 
== errAuthorizationSuccess
) || 
 432                         (status 
== errAuthorizationCanceled
)) // @@@ can only pass back sideband through context 
 434                         secdebug("AuthEvalMech", "storing new context for authorization"); 
 435                         auth
.setInfoSet(eval
.context()); 
 438                 // successfully ran mechanisms to obtain credential 
 439                 if (status 
== errAuthorizationSuccess
) 
 441                         // deny is the default 
 442                         status 
= errAuthorizationDenied
; 
 444                         CredentialSet newCredentials 
= makeCredentials(auth
); 
 445                         // clear context after extracting credentials 
 448                         CommonCriteria::AuditRecord 
auditrec(auth
.creatorAuditToken()); 
 449                         for (CredentialSet::const_iterator it 
= newCredentials
.begin(); it 
!= newCredentials
.end(); ++it
) 
 451                                 const Credential
& newCredential 
= *it
; 
 453                                 // @@@ we log the uid a process was running under when it created the authref, which is misleading in the case of loginwindow 
 454                                 if (newCredential
->isValid()) { 
 455                                         Syslog::info("uid %lu succeeded authenticating as user %s (uid %lu) for right %s.", auth
.creatorUid(), newCredential
->name().c_str(), newCredential
->uid(), inRight
->name()); 
 456                                         auditrec
.submit(AUE_ssauthint
, CommonCriteria::errNone
, inRight
->name()); 
 458                                         // we can't be sure that the user actually exists so inhibit logging of uid 
 459                                         Syslog::error("uid %lu failed to authenticate as user %s for right %s.", auth
.creatorUid(), newCredential
->name().c_str(), inRight
->name()); 
 460                                         auditrec
.submit(AUE_ssauthint
, CommonCriteria::errInvalidCredential
, inRight
->name()); 
 463                                 if (!newCredential
->isValid()) 
 465                                         reason 
= SecurityAgent::invalidPassphrase
; //invalidPassphrase; 
 469                                 // verify that this credential authorizes right 
 470                                 status 
= evaluateUserCredentialForRight(auth
, inRight
, inRule
, environmentToClient
, now
, newCredential
, true); 
 472                                 if (status 
== errAuthorizationSuccess
) 
 474                                         if (auth
.operatesAsLeastPrivileged()) { 
 475                                                 Credential 
rightCredential(inRight
->name(), mShared
); 
 476                                                 credentials
.erase(rightCredential
); credentials
.insert(rightCredential
); 
 478                                                         credentials
.insert(Credential(inRight
->name(), false)); 
 480                                                 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent 
 481                                                 credentials
.erase(newCredential
); credentials
.insert(newCredential
); 
 482                                            // just got a new credential - if it's shared also add a non-shared one that to stick in the authorizationref local cache 
 484                                                            credentials
.insert(Credential(newCredential
->uid(), newCredential
->name(), newCredential
->realname(), false)); 
 487                                         // use valid credential to set context info 
 488                                         // XXX/cs keeping this for now, such that the uid is passed back 
 489                                         auth
.setCredentialInfo(newCredential
); 
 490                                         secdebug("SSevalMech", "added valid credential for user %s", newCredential
->name().c_str()); 
 491                                         status 
= errAuthorizationSuccess
; 
 495                                         reason 
= SecurityAgent::userNotInGroup
; //unacceptableUser; // userNotInGroup 
 498                         if (status 
== errAuthorizationSuccess
) 
 502                         if ((status 
== errAuthorizationCanceled
) || 
 503                 (status 
== errAuthorizationInternal
)) 
 508                         else // last mechanism is now authentication - fail 
 509                                 if (status 
== errAuthorizationDenied
) 
 510                                         reason 
= SecurityAgent::invalidPassphrase
; 
 513         // If we fell out of the loop because of too many tries, notify user 
 516                 reason 
= SecurityAgent::tooManyTries
; 
 517                 AuthItemRef 
retryHint (AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
)); 
 518                 environmentToClient
.erase(retryHint
); environmentToClient
.insert(retryHint
); // replace 
 519                 AuthItemRef 
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
)); 
 520                 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace 
 521                 eval
.run(AuthValueVector(), environmentToClient
, auth
); 
 522                 // XXX/cs is this still necessary? 
 525                 CommonCriteria::AuditRecord 
auditrec(auth
.creatorAuditToken()); 
 526                 auditrec
.submit(AUE_ssauthorize
, CommonCriteria::errTooManyTries
, inRight
->name()); 
 532 // create externally verified credentials on the basis of  
 533 // mechanism-provided information 
 535 RuleImpl::makeCredentials(const AuthorizationToken 
&auth
) const 
 537         // fetch context and construct a credential to be tested 
 538         const AuthItemSet 
&context 
= const_cast<AuthorizationToken 
&>(auth
).infoSet(); 
 539         CredentialSet newCredentials
; 
 542                 AuthItemSet::const_iterator found 
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName(kAuthorizationEnvironmentUsername
) ); 
 543                 if (found 
== context
.end()) 
 545                 string username 
= (**found
).stringValue(); 
 546                 secdebug("AuthEvalMech", "found username"); 
 548                 const uid_t 
*uid 
= NULL
; 
 549                 found 
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName("uid") ); 
 550                 if (found 
!= context
.end()) 
 552                         uid 
= static_cast<const uid_t 
*>((**found
).value().data
); 
 553                         secdebug("AuthEvalMech", "found uid"); 
 556                 if (username
.length() && uid
) 
 558                         // credential is valid because mechanism says so 
 559                         newCredentials
.insert(Credential(*uid
, username
, "", mShared
)); 
 563         return newCredentials
; 
 566 // evaluate whether a good credential of the current session owner would authorize a right 
 568 RuleImpl::evaluateSessionOwner(const AuthItemRef 
&inRight
, const Rule 
&inRule
, const AuthItemSet 
&environment
, const CFAbsoluteTime now
, const AuthorizationToken 
&auth
, Credential 
&credential
) const 
 570         // username hint is taken from the user who created the authorization, unless it's clearly ineligible 
 571         // @@@ we have no access to current requester uid here and the process uid is only taken when the authorization is created 
 572         // meaning that a process like loginwindow that drops privs later is screwed. 
 575         Session 
&session 
= auth
.session(); 
 576         Credential sessionCredential
; 
 577         if (session
.haveOriginatorUid()) { 
 578                 // preflight session credential as if it were a fresh copy 
 579                 const Credential 
&cred 
= session
.originatorCredential(); 
 580                 sessionCredential 
= Credential(cred
->uid(), cred
->name(), cred
->realname(), mShared
/*ignored*/); 
 582                 uid 
= auth
.creatorUid(); 
 583                 Server::active().longTermActivity(); 
 584                 struct passwd 
*pw 
= getpwuid(uid
); 
 586                         // avoid hinting a locked account 
 587                         if ( (pw
->pw_passwd 
== NULL
) || 
 588                                 strcmp(pw
->pw_passwd
, "*") ) { 
 589                                 // Check if username will authorize the request and set username to 
 590                                 // be used as a hint to the user if so 
 591                                 secdebug("AuthEvalMech", "preflight credential from current user, result follows:"); 
 592                                 sessionCredential 
= Credential(pw
->pw_uid
, pw
->pw_name
, pw
->pw_gecos
, mShared
/*ignored*/); 
 597         OSStatus status 
= evaluateUserCredentialForRight(auth
, inRight
, inRule
, environment
, now
, sessionCredential
, true); 
 598         if (errAuthorizationSuccess 
== status
) 
 599                 credential 
= sessionCredential
; 
 606 RuleImpl::evaluateCredentialForRight(const AuthorizationToken 
&auth
, const AuthItemRef 
&inRight
, const Rule 
&inRule
, const AuthItemSet 
&environment
, CFAbsoluteTime now
, const Credential 
&credential
, bool ignoreShared
) const 
 608         if (auth
.operatesAsLeastPrivileged()) { 
 609                 if (credential
->isRight() && credential
->isValid() && (inRight
->name() == credential
->name())) 
 610                         return errAuthorizationSuccess
; 
 612                         return errAuthorizationDenied
; 
 614                 return evaluateUserCredentialForRight(auth
, inRight
, inRule
, environment
, now
, credential
, false); 
 617 // Return errAuthorizationSuccess if this rule allows access based on the specified credential, 
 618 // return errAuthorizationDenied otherwise. 
 620 RuleImpl::evaluateUserCredentialForRight(const AuthorizationToken 
&auth
, const AuthItemRef 
&inRight
, const Rule 
&inRule
, const AuthItemSet 
&environment
, CFAbsoluteTime now
, const Credential 
&credential
, bool ignoreShared
) const 
 622         assert(mType 
== kUser
); 
 624         // Get the username from the credential 
 625         const char *user 
= credential
->name().c_str(); 
 627         // If the credential is not valid or it's age is more than the allowed maximum age 
 628         // for a credential, deny. 
 629         if (!credential
->isValid()) 
 631                 secdebug("autheval", "credential for user %s is invalid, denying right %s", user
, inRight
->name()); 
 632                 return errAuthorizationDenied
; 
 635         if (now 
- credential
->creationTime() > mMaxCredentialAge
) 
 637                 secdebug("autheval", "credential for user %s has expired, denying right %s", user
, inRight
->name()); 
 638                 return errAuthorizationDenied
; 
 641         if (!ignoreShared 
&& !mShared 
&& credential
->isShared()) 
 643                 secdebug("autheval", "shared credential for user %s cannot be used, denying right %s", user
, inRight
->name()); 
 644                 return errAuthorizationDenied
; 
 647         // A root (uid == 0) user can do anything 
 648         if (credential
->uid() == 0) 
 650                 secdebug("autheval", "user %s has uid 0, granting right %s", user
, inRight
->name()); 
 651                 return errAuthorizationSuccess
; 
 656                 Session 
&session 
= auth
.session(); 
 657                 if (session
.haveOriginatorUid()) 
 659                         uid_t console_user 
= session
.originatorUid(); 
 661                         if (credential
->uid() == console_user
) 
 663                                 secdebug("autheval", "user %s is session-owner(uid: %d), granting right %s", user
, console_user
, inRight
->name()); 
 664                                 return errAuthorizationSuccess
; 
 668                         secdebug("autheval", "session-owner check failed."); 
 671         if (mGroupName
.length()) 
 673                 const char *groupname 
= mGroupName
.c_str(); 
 674                 Server::active().longTermActivity(); 
 677                         return errAuthorizationDenied
; 
 681                         uuid_t group_uuid
, user_uuid
; 
 684                         if (mbr_group_name_to_uuid(groupname
, group_uuid
)) 
 687                         if (mbr_uid_to_uuid(credential
->uid(), user_uuid
)) 
 690                         if (mbr_check_membership(user_uuid
, group_uuid
, &is_member
)) 
 695                                 secdebug("autheval", "user %s is a member of group %s, granting right %s", 
 696                                         user
, groupname
, inRight
->name()); 
 697                                 return errAuthorizationSuccess
; 
 703                 secdebug("autheval", "user %s is not a member of group %s, denying right %s", 
 704                         user
, groupname
, inRight
->name()); 
 707         return errAuthorizationDenied
; 
 713 RuleImpl::evaluateUser(const AuthItemRef 
&inRight
, const Rule 
&inRule
, AuthItemSet 
&environmentToClient
, AuthorizationFlags flags
, CFAbsoluteTime now
, const CredentialSet 
*inCredentials
, CredentialSet 
&credentials
, AuthorizationToken 
&auth
) const 
 715         // If we got here, this is a kUser type rule, let's start looking for a 
 716         // credential that is satisfactory 
 718         // Zeroth -- Here is an extra special saucy ugly hack to allow authorizations 
 719         // created by a proccess running as root to automatically get a right. 
 720         if (mAllowRoot 
&& auth
.creatorUid() == 0) 
 722                 secdebug("autheval", "creator of authorization has uid == 0 granting right %s", 
 724                 return errAuthorizationSuccess
; 
 727         // if we're not supposed to authenticate evaluate the session-owner against the group 
 728         if (!mAuthenticateUser
) 
 730                 Credential hintCredential
; 
 731                 OSStatus status 
= evaluateSessionOwner(inRight
, inRule
, environmentToClient
, now
, auth
, hintCredential
); 
 734                         return errAuthorizationSuccess
; 
 736                 return errAuthorizationDenied
; 
 739         // First -- go though the credentials we either already used or obtained during this authorize operation. 
 740         for (CredentialSet::const_iterator it 
= credentials
.begin(); it 
!= credentials
.end(); ++it
) 
 742                 // Passed in user credentials are allowed for least privileged mode 
 743                 if (auth
.operatesAsLeastPrivileged() && !(*it
)->isRight() && (*it
)->isValid())  
 745                         OSStatus status 
= evaluateUserCredentialForRight(auth
, inRight
, inRule
, environmentToClient
, now
, *it
, false); 
 746                         if (errAuthorizationSuccess 
== status
) { 
 747                                 Credential 
rightCredential(inRight
->name(), mShared
); 
 748                                 credentials
.erase(rightCredential
); credentials
.insert(rightCredential
); 
 750                                         credentials
.insert(Credential(inRight
->name(), false)); 
 755                 // if this is least privileged, this will function differently: match credential to requested right 
 756                 OSStatus status 
= evaluateCredentialForRight(auth
, inRight
, inRule
, environmentToClient
, now
, *it
, false); 
 758                 if (status 
!= errAuthorizationDenied
) { 
 759                         // add credential to authinfo 
 760                         auth
.setCredentialInfo(*it
); 
 766         // Second -- go though the credentials passed in to this authorize operation by the state management layer. 
 769                 for (CredentialSet::const_iterator it 
= inCredentials
->begin(); it 
!= inCredentials
->end(); ++it
) 
 771                         // if this is least privileged, this will function differently: match credential to requested right 
 772                         OSStatus status 
= evaluateCredentialForRight(auth
, inRight
, inRule
, environmentToClient
, now
, *it
, false); 
 774                         if (status 
== errAuthorizationSuccess
) 
 776                                 // Add the credential we used to the output set. 
 777                                 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent 
 778                                 credentials
.erase(*it
); credentials
.insert(*it
); 
 779                                 // add credential to authinfo 
 780                                 auth
.setCredentialInfo(*it
); 
 784                         else if (status 
!= errAuthorizationDenied
) 
 789         // Finally -- We didn't find the credential in our passed in credential lists.  Obtain a new credential if our flags let us do so. 
 790         if (!(flags 
& kAuthorizationFlagExtendRights
)) 
 791                 return errAuthorizationDenied
; 
 793         // authorizations that timeout immediately cannot be preauthorized 
 794         if ((flags 
& kAuthorizationFlagPreAuthorize
) &&  
 795                 (mMaxCredentialAge 
== 0.0)) 
 797                 inRight
->setFlags(inRight
->flags() | kAuthorizationFlagCanNotPreAuthorize
); 
 798                 return errAuthorizationSuccess
; 
 801         if (!(flags 
& kAuthorizationFlagInteractionAllowed
)) 
 802                 return errAuthorizationInteractionNotAllowed
; 
 804         setAgentHints(inRight
, inRule
, environmentToClient
, auth
); 
 806         return evaluateAuthentication(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
); 
 810 RuleImpl::evaluateMechanismOnly(const AuthItemRef 
&inRight
, const Rule 
&inRule
, AuthItemSet 
&environmentToClient
, AuthorizationToken 
&auth
, CredentialSet 
&outCredentials
) const 
 815         Process 
&cltProc 
= Server::process(); 
 816         // Authorization preserves creator's UID in setuid processes 
 817         uid_t cltUid 
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid(); 
 818         secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc
.pid(), cltUid
); 
 821                 AgentMechanismEvaluator 
eval(cltUid
, auth
.session(), mEvalDef
); 
 825                         setAgentHints(inRight
, inRule
, environmentToClient
, auth
); 
 826                         AuthItemRef 
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
)); 
 827                         environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace 
 829                         status 
= eval
.run(AuthValueVector(), environmentToClient
, auth
); 
 831                         if ((status 
== errAuthorizationSuccess
) || 
 832                                 (status 
== errAuthorizationCanceled
)) // @@@ can only pass back sideband through context 
 834                                 secdebug("AuthEvalMech", "storing new context for authorization"); 
 835                                 auth
.setInfoSet(eval
.context()); 
 836                                 if (status 
== errAuthorizationSuccess
) 
 838                                         if (auth
.operatesAsLeastPrivileged()) 
 839                                                 outCredentials
.insert(Credential(inRight
->name(), mShared
)); 
 841                                                 outCredentials 
= makeCredentials(auth
); 
 847                 while ((status 
== errAuthorizationDenied
) // only if we have an expected failure we continue 
 848                                         && ((mTries 
== 0)                               // mTries == 0 means we try forever 
 849                                         || ((mTries 
> 0)                        // mTries > 0 means we try up to mTries times 
 850                                         && (tries 
< mTries
)))); 
 853         // HACK kill all hosts to free pages for low memory systems 
 854         if (name() == "system.login.done") 
 856                 QueryInvokeMechanism 
query(securityAgent
, auth
.session()); 
 857                 query
.terminateAgent(); 
 858                 QueryInvokeMechanism 
query2(privilegedAuthHost
, auth
.session()); 
 859                 query2
.terminateAgent(); 
 866 RuleImpl::evaluateRules(const AuthItemRef 
&inRight
, const Rule 
&inRule
, AuthItemSet 
&environmentToClient
, AuthorizationFlags flags
, CFAbsoluteTime now
, const CredentialSet 
*inCredentials
, CredentialSet 
&credentials
, AuthorizationToken 
&auth
) const 
 868         // line up the rules to try 
 869         if (!mRuleDef
.size()) 
 870                 return errAuthorizationSuccess
; 
 873         OSStatus status 
= errAuthorizationSuccess
; 
 874         vector
<Rule
>::const_iterator it
; 
 876         for (it 
= mRuleDef
.begin();it 
!= mRuleDef
.end(); it
++) 
 879                 if ((mType 
== kKofN
) && (count 
== mKofN
)) 
 880                         return errAuthorizationSuccess
; 
 882                 // get a rule and try it 
 883                 status 
= (*it
)->evaluate(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
); 
 885                 // if status is cancel/internal error abort 
 886                 if ((status 
== errAuthorizationCanceled
) || (status 
== errAuthorizationInternal
)) 
 889                 if (status 
!= errAuthorizationSuccess
) 
 891                         // continue if we're only looking for k of n 
 901         return status
; // return the last failure 
 906 RuleImpl::evaluate(const AuthItemRef 
&inRight
, const Rule 
&inRule
, AuthItemSet 
&environmentToClient
, AuthorizationFlags flags
, CFAbsoluteTime now
, const CredentialSet 
*inCredentials
, CredentialSet 
&credentials
, AuthorizationToken 
&auth
) const 
 911                 secdebug("autheval", "rule is always allow"); 
 912                 return errAuthorizationSuccess
; 
 914                 secdebug("autheval", "rule is always deny"); 
 915                 return errAuthorizationDenied
; 
 917                 secdebug("autheval", "rule is user"); 
 918                 return evaluateUser(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
); 
 919         case kRuleDelegation
: 
 920                 secdebug("autheval", "rule evaluates rules"); 
 921                 return evaluateRules(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
); 
 923                 secdebug("autheval", "rule evaluates k-of-n rules"); 
 924                 return evaluateRules(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
); 
 925         case kEvaluateMechanisms
: 
 926                 secdebug("autheval", "rule evaluates mechanisms"); 
 927                 return evaluateMechanismOnly(inRight
, inRule
, environmentToClient
, auth
, credentials
); 
 929                 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule 
 933 Rule::Rule() : RefPointer
<RuleImpl
>(new RuleImpl()) {} 
 934 Rule::Rule(const string 
&inRightName
, CFDictionaryRef cfRight
, CFDictionaryRef cfRules
) : RefPointer
<RuleImpl
>(new RuleImpl(inRightName
, cfRight
, cfRules
)) {} 
 938 } // end namespace Authorization