2  * Copyright (c) 2000-2004,2009 Apple 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@ 
  24 #include "AuthorizationEngine.h" 
  25 #include <security_cdsa_utilities/AuthorizationWalkers.h> 
  26 #include <Security/AuthorizationPriv.h> 
  27 #include <Security/AuthorizationDB.h> 
  29 #include "authority.h" 
  31 #include <Security/AuthorizationTags.h> 
  32 #include <Security/AuthorizationTagsPriv.h> 
  33 #include <security_utilities/logging.h> 
  34 #include <security_utilities/cfutilities.h> 
  35 #include <security_utilities/debugging.h> 
  38 #include <CoreFoundation/CFData.h> 
  39 #include <CoreFoundation/CFNumber.h> 
  40 #include <CoreFoundation/CFPropertyList.h> 
  47 #include <bsm/audit_uevents.h>      // AUE_ssauth* 
  48 #include "ccaudit_extensions.h" 
  50 namespace Authorization 
{ 
  52 using namespace CommonCriteria::Securityd
; 
  56 // Errors to be thrown 
  58 Error::Error(int err
) : error(err
) 
  62 const char *Error::what() const throw() 
  63 { return "Authorization error"; } 
  65 int Error::unixError() const throw() 
  66 { return error
; }       // @@@ eventually... 
  68 OSStatus 
Error::osStatus() const throw() 
  71 void Error::throwMe(int err
) { throw Error(err
); } 
  76 Engine::Engine(const char *configFile
) : mAuthdb(configFile
) 
  86         @function AuthorizationEngine::authorize 
  90         @param inRights (input) List of rights being requested for authorization. 
  91         @param environment (optional/input) Environment containing information to be used during evaluation. 
  92         @param flags (input) Optional flags @@@ see AuthorizationCreate for a description. 
  93         @param inCredentials (input) Credentials already held by the caller. 
  94         @param outCredentials (output/optional) Credentials obtained, used or refreshed during this call to authorize the requested rights. 
  95         @param outRights (output/optional) Subset of inRights which were actually authorized. 
  97         @results Returns errAuthorizationSuccess if all rights requested are authorized, or if the kAuthorizationFlagPartialRights flag was specified.  Might return other status values like errAuthorizationDenied, errAuthorizationCanceled or errAuthorizationInteractionNotAllowed  
 100 Engine::authorize(const AuthItemSet 
&inRights
, const AuthItemSet 
&environment
, 
 101         AuthorizationFlags flags
, const CredentialSet 
*inCredentials
, CredentialSet 
*outCredentials
, 
 102         AuthItemSet 
&outRights
, AuthorizationToken 
&auth
) 
 104         CredentialSet credentials
; 
 105         OSStatus status 
= errAuthorizationSuccess
; 
 106     SecurityAgent::Reason reason 
= SecurityAgent::noReason
; 
 108         // Get current time of day. 
 109         CFAbsoluteTime now 
= CFAbsoluteTimeGetCurrent(); 
 111         // Update rules from database if needed 
 114         // Check if a credential was passed into the environment and we were asked to extend the rights 
 115         if (flags 
& kAuthorizationFlagExtendRights
) 
 117                 string username
, password
; 
 119                 for (AuthItemSet::iterator item 
= environment
.begin(); item 
!= environment
.end(); item 
++) 
 121                         if (!strcmp((*item
)->name(), kAuthorizationEnvironmentUsername
)) 
 122                                 username 
= (*item
)->stringValue(); 
 123                         else if (!strcmp((*item
)->name(), kAuthorizationEnvironmentPassword
)) 
 124                                 password 
= (*item
)->stringValue(); 
 125                         else if (!strcmp((*item
)->name(), kAuthorizationEnvironmentShared
)) 
 129                 if (username
.length()) 
 131                         // Let's create a credential from the passed in username and password. 
 132                         Credential 
newCredential(username
, password
, shared
); 
 133                         // If it's valid insert it into the credentials list.  Normally this is 
 134                         // only done if it actually authorizes a requested right, but for this 
 135                         // special case (environment) we do it even when no rights are being requested. 
 136                         if (newCredential
->isValid()) 
 137                                 credentials
.insert(newCredential
); 
 141         // generate hints for every authorization 
 142     AuthItemSet environmentToClient 
= environment
; 
 144     RightAuthenticationLogger 
logger(auth
.creatorAuditToken(), AUE_ssauthorize
); 
 146         // create a vector with the first right first 
 147         std::vector
<AuthItemRef
>                tempRights
; 
 148         for (AuthItemSet::const_iterator it 
= inRights
.begin(); it 
!= inRights
.end(); ++it
) { 
 149                 if (inRights
.firstItemName 
!= NULL 
&& strcmp((*it
)->name(), inRights
.firstItemName
) == 0) 
 150                         tempRights
.insert(tempRights
.begin(), *it
); 
 152                         tempRights
.push_back(*it
); 
 155         bool authExtractPassword 
= false; 
 156         std::vector
<AuthItemRef
>::const_iterator end 
= tempRights
.end(); 
 157         for (std::vector
<AuthItemRef
>::const_iterator it 
= tempRights
.begin(); it 
!= end
; ++it
) 
 159                 // Get the rule for each right we are trying to obtain. 
 160                 const Rule 
&toplevelRule 
= mAuthdb
.getRule(*it
); 
 162                 if (false == authExtractPassword
) 
 163                         authExtractPassword 
= toplevelRule
->extractPassword(); 
 165         string processName 
= "unknown"; 
 166         string authCreatorName 
= "unknown"; 
 167         if (SecCodeRef code 
= Server::process().currentGuest()) { 
 168             CFRef
<CFURLRef
> path
; 
 169             if (!SecCodeCopyPath(code
, kSecCSDefaultFlags
, &path
.aref())) 
 170                 processName 
= cfString(path
); 
 172         if (SecStaticCodeRef code 
= auth
.creatorCode()) { 
 173             CFRef
<CFURLRef
> path
; 
 174             if (!SecCodeCopyPath(code
, kSecCSDefaultFlags
, &path
.aref())) 
 175                 authCreatorName 
= cfString(path
); 
 178         if (sandbox_check(Server::process().pid(), "authorization-right-obtain", SANDBOX_FILTER_RIGHT_NAME
, (*it
)->name())) { 
 179             Syslog::error("Sandbox denied authorizing right '%s' by client '%s' [%d]", (*it
)->name(), processName
.c_str(), Server::process().pid()); 
 180             return errAuthorizationDenied
; 
 182         if (auth
.creatorSandboxed() && sandbox_check(auth
.creatorPid(), "authorization-right-obtain", SANDBOX_FILTER_RIGHT_NAME
, (*it
)->name())) { 
 183             Syslog::error("Sandbox denied authorizing right '%s' for authorization created by '%s' [%d]", (*it
)->name(), authCreatorName
.c_str(), auth
.creatorPid()); 
 184             return errAuthorizationDenied
; 
 187                 OSStatus result 
= toplevelRule
->evaluate(*it
, toplevelRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
, reason
, authExtractPassword
); 
 188                 secdebug("autheval", "evaluate rule %s for right %s returned %d.", toplevelRule
->name().c_str(), (*it
)->name(), int(result
)); 
 189         SECURITYD_AUTH_EVALRIGHT(&auth
, (char *)(*it
)->name(), result
); 
 191         logger
.setRight((*it
)->name()); 
 192         logger
.logAuthorizationResult(processName
.c_str(), authCreatorName
.c_str(), result
); 
 194         if (result 
== errAuthorizationSuccess
) 
 196             outRights
.insert(*it
); 
 197             Syslog::info("Succeeded authorizing right '%s' by client '%s' [%d] for authorization created by '%s' [%d]", (*it
)->name(), processName
.c_str(), Server::process().pid(), authCreatorName
.c_str(), auth
.creatorPid()); 
 199         else if (result 
== errAuthorizationDenied 
|| result 
== errAuthorizationInteractionNotAllowed
)  
 201             if (result 
== errAuthorizationDenied
) 
 203                  Syslog::notice("Failed to authorize right '%s' by client '%s' [%d] for authorization created by '%s' [%d]", (*it
)->name(), processName
.c_str(), Server::process().pid(), authCreatorName
.c_str(), auth
.creatorPid()); 
 206             // add creator pid to authorization token 
 207             if (!(flags 
& kAuthorizationFlagPartialRights
)) 
 213         else if (result 
== errAuthorizationCanceled
) 
 220             Syslog::error("Engine::authorize: Rule::evaluate returned %ld returning errAuthorizationInternal", result
); 
 221             status 
= errAuthorizationInternal
; 
 227                 outCredentials
->swap(credentials
); 
 233 Engine::verifyModification(string inRightName
, bool remove
, 
 234         const CredentialSet 
*inCredentials
, CredentialSet 
*outCredentials
, AuthorizationToken 
&auth
) 
 238         // meta rights are constructed as follows: 
 239         // we don't allow setting of wildcard rights, so you can only be more specific 
 240         // note that you should never restrict things with a wildcard right without disallowing 
 241         // changes to the entire domain.  ie.  
 242         //              system.privilege.               -> never 
 243         //              config.add.system.privilege.    -> never 
 244         //              config.modify.system.privilege. -> never 
 245         //              config.delete.system.privilege. -> never 
 246         // For now we don't allow any configuration of configuration rules 
 247         //              config.config. -> never 
 249         string rightnameToCheck
; 
 251         // @@@ verify right name is is not NULL or zero length 
 252         if (inRightName
.length() == 0) 
 253                 return errAuthorizationDenied
; 
 255         // @@@ make sure it isn't a wildcard right by checking trailing "." 
 256         if ( *(inRightName
.rbegin()) == '.') 
 257                 return errAuthorizationDenied
; 
 259         // @@@ make sure it isn't a configure right by checking it doesn't start with config. 
 260         if (inRightName
.find(kConfigRight
, 0) != string::npos
) 
 262                 // special handling of meta right change: 
 263                 // config.add. config.modify. config.remove. config.{}. 
 264                 // check for config.<right> (which always starts with config.config.) 
 265                 rightnameToCheck 
= string(kConfigRight
) + inRightName
; 
 269                 // regular check of rights 
 270                 bool existingRule 
= mAuthdb
.existRule(inRightName
); 
 274                                 rightnameToCheck 
= string(kAuthorizationConfigRightModify
) + inRightName
; 
 276                                 rightnameToCheck 
= string(kAuthorizationConfigRightAdd
) + inRightName
; 
 281                                 rightnameToCheck 
= string(kAuthorizationConfigRightRemove
) + inRightName
; 
 284                                 secdebug("engine", "rule %s doesn't exist.", inRightName
.c_str()); 
 285                                 return errAuthorizationSuccess
; // doesn't exist, done 
 291         AuthItemSet rights
, environment
, outRights
; 
 292         rights
.insert(AuthItemRef(rightnameToCheck
.c_str())); 
 293         secdebug("engine", "authorizing %s for db modification.", rightnameToCheck
.c_str()); 
 294         return authorize(rights
, environment
, kAuthorizationFlagDefaults 
| kAuthorizationFlagInteractionAllowed 
| kAuthorizationFlagExtendRights
, inCredentials
, outCredentials
, outRights
, auth
); 
 298 Engine::getRule(string 
&inRightName
, CFDictionaryRef 
*outRuleDefinition
) 
 300         // Get current time of day. 
 301         CFAbsoluteTime now 
= CFAbsoluteTimeGetCurrent(); 
 303         // Update rules from database if needed 
 306         CFDictionaryRef definition 
= mAuthdb
.getRuleDefinition(inRightName
); 
 309                 if (outRuleDefinition
) 
 310                         *outRuleDefinition 
= definition
; 
 312                         CFRelease(definition
); 
 314                 return errAuthorizationSuccess
; 
 317         return errAuthorizationDenied
; 
 321 Engine::setRule(const char *inRightName
, CFDictionaryRef inRuleDefinition
, const CredentialSet 
*inCredentials
, CredentialSet 
*outCredentials
, AuthorizationToken 
&auth
) 
 323         // Validate rule by constructing it from the passed dictionary 
 324         if (!mAuthdb
.validateRule(inRightName
, inRuleDefinition
)) 
 325                 return errAuthorizationDenied
; // @@@ separate error for this? 
 327         OSStatus result 
= verifyModification(inRightName
, false /*setting, not removing*/, inCredentials
, outCredentials
, auth
); 
 328         if (result 
!= errAuthorizationSuccess
) 
 331         // set the rule for the right and save the database 
 332         mAuthdb
.setRule(inRightName
, inRuleDefinition
); 
 334         return errAuthorizationSuccess
; 
 338 Engine::removeRule(const char *inRightName
, const CredentialSet 
*inCredentials
, CredentialSet 
*outCredentials
, AuthorizationToken 
&auth
) 
 340         // Get current time of day. 
 341         CFAbsoluteTime now 
= CFAbsoluteTimeGetCurrent(); 
 343         // Update rules from database if needed 
 346         OSStatus result 
= verifyModification(inRightName
, true /*removing*/, inCredentials
, outCredentials
, auth
); 
 347         if (result 
!= errAuthorizationSuccess
) 
 350         // set the rule for the right and save the database 
 351         mAuthdb
.removeRule(inRightName
); 
 353         return errAuthorizationSuccess
; 
 356 }       // end namespace Authorization