2  * Copyright (c) 2000-2015 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@ 
  25 // passphrases - canonical code to obtain passphrases 
  27 #define __STDC_WANT_LIB_EXT1__ 1 
  30 #include "agentquery.h" 
  31 #include "ccaudit_extensions.h" 
  33 #include <Security/AuthorizationTags.h> 
  34 #include <Security/AuthorizationTagsPriv.h> 
  35 #include <Security/checkpw.h> 
  36 #include <Security/Security.h> 
  37 #include <System/sys/fileport.h> 
  38 #include <bsm/audit.h> 
  39 #include <bsm/audit_uevents.h>      // AUE_ssauthint 
  40 #include <membership.h> 
  41 #include <membershipPriv.h> 
  42 #include <security_utilities/logging.h> 
  43 #include <security_utilities/mach++.h> 
  46 #include <xpc/private.h> 
  47 #include "securityd_service/securityd_service/securityd_service_client.h" 
  49 // Includes for <rdar://problem/34677969> Always require the user's password on keychain approval dialogs 
  52 #define SECURITYAGENT_BOOTSTRAP_NAME_BASE               "com.apple.security.agent" 
  53 #define SECURITYAGENT_LOGINWINDOW_BOOTSTRAP_NAME_BASE   "com.apple.security.agent.login" 
  55 #define AUTH_XPC_ITEM_NAME  "_item_name" 
  56 #define AUTH_XPC_ITEM_FLAGS "_item_flags" 
  57 #define AUTH_XPC_ITEM_VALUE "_item_value" 
  58 #define AUTH_XPC_ITEM_TYPE  "_item_type" 
  59 #define AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH "_item_sensitive_value_length" 
  61 #define AUTH_XPC_REQUEST_METHOD_KEY "_agent_request_key" 
  62 #define AUTH_XPC_REQUEST_METHOD_CREATE "_agent_request_create" 
  63 #define AUTH_XPC_REQUEST_METHOD_INVOKE "_agent_request_invoke" 
  64 #define AUTH_XPC_REQUEST_METHOD_DEACTIVATE "_agent_request_deactivate" 
  65 #define AUTH_XPC_REQUEST_METHOD_DESTROY "_agent_request_destroy" 
  66 #define AUTH_XPC_REPLY_METHOD_KEY "_agent_reply_key" 
  67 #define AUTH_XPC_REPLY_METHOD_RESULT "_agent_reply_result" 
  68 #define AUTH_XPC_REPLY_METHOD_INTERRUPT "_agent_reply_interrupt" 
  69 #define AUTH_XPC_REPLY_METHOD_CREATE "_agent_reply_create" 
  70 #define AUTH_XPC_REPLY_METHOD_DEACTIVATE "_agent_reply_deactivate" 
  71 #define AUTH_XPC_PLUGIN_NAME "_agent_plugin" 
  72 #define AUTH_XPC_MECHANISM_NAME "_agent_mechanism" 
  73 #define AUTH_XPC_HINTS_NAME "_agent_hints" 
  74 #define AUTH_XPC_CONTEXT_NAME "_agent_context" 
  75 #define AUTH_XPC_IMMUTABLE_HINTS_NAME "_agent_immutable_hints" 
  76 #define AUTH_XPC_REQUEST_INSTANCE "_agent_instance" 
  77 #define AUTH_XPC_REPLY_RESULT_VALUE "_agent_reply_result_value" 
  78 #define AUTH_XPC_AUDIT_SESSION_PORT "_agent_audit_session_port" 
  79 #define AUTH_XPC_BOOTSTRAP_PORT "_agent_bootstrap_port" 
  81 #define UUID_INITIALIZER_FROM_SESSIONID(sessionid) \ 
  82 { 0,0,0,0, 0,0,0,0, 0,0,0,0, (unsigned char)((0xff000000 & (sessionid))>>24), (unsigned char)((0x00ff0000 & (sessionid))>>16), (unsigned char)((0x0000ff00 & (sessionid))>>8),  (unsigned char)((0x000000ff & (sessionid))) } 
  85 // SecurityAgentXPCConnection 
  87 SecurityAgentXPCConnection::SecurityAgentXPCConnection(Session 
&session
) 
  88 : mHostInstance(session
.authhost()), 
  90 mConnection(&Server::connection()), 
  91 mAuditToken(Server::connection().auditToken()) 
  93         // this may take a while 
  94         Server::active().longTermActivity(); 
  95     secnotice("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this); 
  96     mXPCConnection 
= NULL
; 
  98     struct passwd 
*pw 
= getpwnam("nobody"); 
 100         mNobodyUID 
= pw
->pw_uid
; 
 104 SecurityAgentXPCConnection::~SecurityAgentXPCConnection() 
 106     secnotice("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this); 
 107         mConnection
->useAgent(NULL
); 
 109     // If a connection has been established, we need to tear it down. 
 110     if (NULL 
!= mXPCConnection
) { 
 111         // Tearing this down is a multi-step process. First, request a cancellation. 
 112         // This is safe even if the connection is already in the canceled state. 
 113         xpc_connection_cancel(mXPCConnection
); 
 115         // Then release the XPC connection 
 116         xpc_release(mXPCConnection
); 
 117         mXPCConnection 
= NULL
; 
 121 bool SecurityAgentXPCConnection::inDarkWake() 
 123         return mSession
.server().inDarkWake(); 
 127 SecurityAgentXPCConnection::activate(bool ignoreUid
) 
 129     secnotice("SecurityAgentConnection", "activate(%p)", this); 
 131         mConnection
->useAgent(this); 
 132     if (mXPCConnection 
!= NULL
) { 
 133         // If we already have an XPC connection, there's nothing to do. 
 138                 uuid_t sessionUUID 
= UUID_INITIALIZER_FROM_SESSIONID(mSession
.sessionId()); 
 140                 // Yes, these need to be throws, as we're still in securityd, and thus still have to do flow control with exceptions. 
 141                 if (!(mSession
.attributes() & sessionHasGraphicAccess
)) 
 142                         CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
); 
 144                         CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE
); 
 145                 uid_t targetUid 
= mHostInstance
->session().originatorUid(); 
 147                 secnotice("SecurityAgentXPCConnection","Retrieved UID %d for this session", targetUid
); 
 148                 if (!ignoreUid 
&& targetUid 
!= 0 && targetUid 
!= mNobodyUID
) { 
 149                         mXPCConnection 
= xpc_connection_create_mach_service(SECURITYAGENT_BOOTSTRAP_NAME_BASE
, NULL
, 0); 
 150                         xpc_connection_set_target_uid(mXPCConnection
, targetUid
); 
 151                         secnotice("SecurityAgentXPCConnection", "Creating a standard security agent"); 
 153                         mXPCConnection 
= xpc_connection_create_mach_service(SECURITYAGENT_LOGINWINDOW_BOOTSTRAP_NAME_BASE
, NULL
, 0); 
 154                         xpc_connection_set_instance(mXPCConnection
, sessionUUID
); 
 155                         secnotice("SecurityAgentXPCConnection", "Creating a loginwindow security agent"); 
 158                 xpc_connection_set_event_handler(mXPCConnection
, ^(xpc_object_t object
) { 
 159                         if (xpc_get_type(object
) == XPC_TYPE_ERROR
) { 
 160                                 secnotice("SecurityAgentXPCConnection", "error during xpc: %s", xpc_dictionary_get_string(object
, XPC_ERROR_KEY_DESCRIPTION
)); 
 163                 xpc_connection_resume(mXPCConnection
); 
 164                 secnotice("SecurityAgentXPCConnection", "%p activated", this); 
 166     catch (MacOSError 
&err
) { 
 167                 mConnection
->useAgent(NULL
);    // guess not 
 168         Syslog::error("SecurityAgentConnection: error activating SecurityAgent instance %p", this); 
 172     secnotice("SecurityAgentXPCConnection", "contact didn't throw (%p)", this); 
 176 SecurityAgentXPCConnection::terminate() 
 180     // @@@ This happens already in the destructor; presumably we do this to tear things down orderly 
 181         mConnection
->useAgent(NULL
); 
 185 using SecurityAgent::Reason
; 
 186 using namespace Authorization
; 
 188 ModuleNexus
<RecursiveMutex
> gAllXPCClientsMutex
; 
 189 ModuleNexus
<set
<SecurityAgentXPCQuery
*> > allXPCClients
; 
 192 SecurityAgentXPCQuery::killAllXPCClients() 
 194     // grab the lock for the client list -- we need to make sure no one modifies the structure while we are iterating it. 
 195     StLock
<Mutex
> _(gAllXPCClientsMutex()); 
 197     set
<SecurityAgentXPCQuery
*>::iterator clientIterator 
= allXPCClients().begin(); 
 198     while (clientIterator 
!= allXPCClients().end()) 
 200         set
<SecurityAgentXPCQuery
*>::iterator thisClient 
= clientIterator
++; 
 201         if ((*thisClient
)->getTerminateOnSleep()) 
 203             (*thisClient
)->terminate(); 
 209 SecurityAgentXPCQuery::SecurityAgentXPCQuery(Session 
&session
) 
 210 : SecurityAgentXPCConnection(session
), mAgentConnected(false), mTerminateOnSleep(false) 
 212     secnotice("SecurityAgentXPCQuery", "new SecurityAgentXPCQuery(%p)", this); 
 215 SecurityAgentXPCQuery::~SecurityAgentXPCQuery() 
 217     secnotice("SecurityAgentXPCQuery", "SecurityAgentXPCQuery(%p) dying", this); 
 218     if (mAgentConnected
) { 
 224 SecurityAgentXPCQuery::inferHints(Process 
&thisProcess
) 
 226     AuthItemSet clientHints
; 
 227     SecurityAgent::RequestorType type 
= SecurityAgent::bundle
; 
 228     pid_t clientPid 
= thisProcess
.pid(); 
 229     uid_t clientUid 
= thisProcess
.uid(); 
 230     string guestPath 
= thisProcess
.getPath(); 
 231     Boolean ignoreSession 
= TRUE
; 
 233         clientHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_TYPE
, AuthValueOverlay(sizeof(type
), &type
))); 
 234         clientHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_PATH
, AuthValueOverlay(guestPath
))); 
 235         clientHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_PID
, AuthValueOverlay(sizeof(clientPid
), &clientPid
))); 
 236         clientHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_UID
, AuthValueOverlay(sizeof(clientUid
), &clientUid
))); 
 239      * If its loginwindow that's asking, override the loginwindow shield detection 
 240      * up front so that it can trigger SecurityAgent dialogs (like password change) 
 241      * for when the OD password and keychain password is out of sync. 
 244     if (guestPath 
== "/System/Library/CoreServices/loginwindow.app") { 
 245         clientHints
.insert(AuthItemRef(AGENT_HINT_IGNORE_SESSION
, AuthValueOverlay(sizeof(ignoreSession
), &ignoreSession
))); 
 248         mClientHints
.insert(clientHints
.begin(), clientHints
.end()); 
 250     bool validSignature 
= thisProcess
.checkAppleSigned(); 
 251     AuthItemSet clientImmutableHints
; 
 253         clientImmutableHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_SIGNED
, AuthValueOverlay(sizeof(validSignature
), &validSignature
))); 
 255         mImmutableHints
.insert(clientImmutableHints
.begin(), clientImmutableHints
.end()); 
 258 void SecurityAgentXPCQuery::addHint(const char *name
, const void *value
, UInt32 valueLen
, UInt32 flags
) 
 260     AuthorizationItem item 
= { name
, valueLen
, const_cast<void *>(value
), flags 
}; 
 261     mClientHints
.insert(AuthItemRef(item
)); 
 266 SecurityAgentXPCQuery::readChoice() 
 271         AuthItem 
*allowAction 
= mOutContext
.find(AGENT_CONTEXT_ALLOW
); 
 275                 if (allowAction
->getString(allowString
) 
 276             && (allowString 
== "YES")) 
 280         AuthItem 
*rememberAction 
= mOutContext
.find(AGENT_CONTEXT_REMEMBER_ACTION
); 
 283         string rememberString
; 
 284         if (rememberAction
->getString(rememberString
) 
 285             && (rememberString 
== "YES")) 
 291 SecurityAgentXPCQuery::disconnect() 
 293     if (NULL 
!= mXPCConnection
) { 
 294         xpc_object_t requestObject 
= xpc_dictionary_create(NULL
, NULL
, 0); 
 295         xpc_dictionary_set_string(requestObject
, AUTH_XPC_REQUEST_METHOD_KEY
, AUTH_XPC_REQUEST_METHOD_DESTROY
); 
 296         xpc_connection_send_message(mXPCConnection
, requestObject
); 
 297         xpc_release(requestObject
); 
 300     StLock
<Mutex
> _(gAllXPCClientsMutex()); 
 301     allXPCClients().erase(this); 
 305 SecurityAgentXPCQuery::terminate() 
 310 static void xpcArrayToAuthItemSet(AuthItemSet 
*setToBuild
, xpc_object_t input
) { 
 313     xpc_array_apply(input
,  ^bool(size_t index
, xpc_object_t item
) { 
 314         const char *name 
= xpc_dictionary_get_string(item
, AUTH_XPC_ITEM_NAME
); 
 317         const void *data 
= xpc_dictionary_get_data(item
, AUTH_XPC_ITEM_VALUE
, &length
); 
 320         // <rdar://problem/13033889> authd is holding on to multiple copies of my password in the clear 
 321         bool sensitive 
= xpc_dictionary_get_value(item
, AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH
); 
 323             size_t sensitiveLength 
= (size_t)xpc_dictionary_get_uint64(item
, AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH
); 
 324             dataCopy 
= malloc(sensitiveLength
); 
 325             memcpy(dataCopy
, data
, sensitiveLength
); 
 326             memset_s((void *)data
, length
, 0, sensitiveLength
); // clear the sensitive data, memset_s is never optimized away 
 327             length 
= sensitiveLength
; 
 329             dataCopy 
= malloc(length
); 
 330             memcpy(dataCopy
, data
, length
); 
 333         uint64_t flags 
= xpc_dictionary_get_uint64(item
, AUTH_XPC_ITEM_FLAGS
); 
 334         AuthItemRef 
nextItem(name
, AuthValueOverlay((uint32_t)length
, dataCopy
), (uint32_t)flags
); 
 335         setToBuild
->insert(nextItem
); 
 336         memset(dataCopy
, 0, length
); // The authorization items contain things like passwords, so wiping clean is important. 
 343 SecurityAgentXPCQuery::create(const char *pluginId
, const char *mechanismId
) 
 345     bool ignoreUid 
= false; 
 350         mAgentConnected 
= false; 
 352         xpc_object_t requestObject 
= xpc_dictionary_create(NULL
, NULL
, 0); 
 353         xpc_dictionary_set_string(requestObject
, AUTH_XPC_REQUEST_METHOD_KEY
, AUTH_XPC_REQUEST_METHOD_CREATE
); 
 354         xpc_dictionary_set_string(requestObject
, AUTH_XPC_PLUGIN_NAME
, pluginId
); 
 355         xpc_dictionary_set_string(requestObject
, AUTH_XPC_MECHANISM_NAME
, mechanismId
); 
 357         uid_t targetUid 
= Server::process().uid(); 
 358                 bool doSwitchAudit     
= (ignoreUid 
|| targetUid 
== 0 || targetUid 
== mNobodyUID
); 
 359                 bool doSwitchBootstrap 
= (ignoreUid 
|| targetUid 
== 0 || targetUid 
== mNobodyUID
); 
 362             mach_port_name_t jobPort
; 
 363             if (0 == audit_session_port(mSession
.sessionId(), &jobPort
)) { 
 364                 secnotice("SecurityAgentXPCQuery", "attaching an audit session port because the uid was %d", targetUid
); 
 365                 xpc_dictionary_set_mach_send(requestObject
, AUTH_XPC_AUDIT_SESSION_PORT
, jobPort
); 
 366                 if (mach_port_mod_refs(mach_task_self(), jobPort
, MACH_PORT_RIGHT_SEND
, -1) != KERN_SUCCESS
) { 
 367                     secnotice("SecurityAgentXPCQuery", "unable to release send right for audit session, leaking"); 
 372         if (doSwitchBootstrap
) { 
 373             secnotice("SecurityAgentXPCQuery", "attaching a bootstrap port because the uid was %d", targetUid
); 
 374             MachPlusPlus::Bootstrap processBootstrap 
= Server::process().taskPort().bootstrap(); 
 375             xpc_dictionary_set_mach_send(requestObject
, AUTH_XPC_BOOTSTRAP_PORT
, processBootstrap
); 
 378         xpc_object_t object 
= xpc_connection_send_message_with_reply_sync(mXPCConnection
, requestObject
); 
 379         if (xpc_get_type(object
) == XPC_TYPE_DICTIONARY
) { 
 380             const char *replyType 
= xpc_dictionary_get_string(object
, AUTH_XPC_REPLY_METHOD_KEY
); 
 381             if (0 == strcmp(replyType
, AUTH_XPC_REPLY_METHOD_CREATE
)) { 
 382                 uint64_t status 
= xpc_dictionary_get_uint64(object
, AUTH_XPC_REPLY_RESULT_VALUE
); 
 383                 if (status 
== kAuthorizationResultAllow
) { 
 384                     mAgentConnected 
= true; 
 386                     secnotice("SecurityAgentXPCQuery", "plugin create failed in SecurityAgent"); 
 387                     MacOSError::throwMe(errAuthorizationInternal
); 
 390         } else if (xpc_get_type(object
) == XPC_TYPE_ERROR
) { 
 391             if (XPC_ERROR_CONNECTION_INVALID 
== object
) { 
 392                 // If we get an error before getting the create response, try again without the UID 
 394                     secnotice("SecurityAgentXPCQuery", "failed to establish connection, no retries left"); 
 396                     MacOSError::throwMe(errAuthorizationInternal
); 
 398                     secnotice("SecurityAgentXPCQuery", "failed to establish connection, retrying with no UID"); 
 400                     xpc_release(mXPCConnection
); 
 401                     mXPCConnection 
= NULL
; 
 403             } else if (XPC_ERROR_CONNECTION_INTERRUPTED 
== object
) { 
 404                 // If we get an error before getting the create response, try again 
 408         xpc_release(requestObject
); 
 409     } while (!mAgentConnected
); 
 411     StLock
<Mutex
> _(gAllXPCClientsMutex()); 
 412     allXPCClients().insert(this); 
 415 static xpc_object_t 
authItemSetToXPCArray(AuthItemSet input
) { 
 416     xpc_object_t outputArray 
= xpc_array_create(NULL
, 0); 
 417     for (AuthItemSet::iterator i 
= input
.begin(); i 
!= input
.end(); i
++) { 
 418         AuthItemRef item 
= *i
; 
 420         xpc_object_t xpc_data 
= xpc_dictionary_create(NULL
, NULL
, 0); 
 421         xpc_dictionary_set_string(xpc_data
, AUTH_XPC_ITEM_NAME
, item
->name()); 
 422         AuthorizationValue value 
= item
->value(); 
 423         if (value
.data 
!= NULL
) { 
 424             xpc_dictionary_set_data(xpc_data
, AUTH_XPC_ITEM_VALUE
, value
.data
, value
.length
); 
 426         xpc_dictionary_set_uint64(xpc_data
, AUTH_XPC_ITEM_FLAGS
, item
->flags()); 
 427         xpc_array_append_value(outputArray
, xpc_data
); 
 428         xpc_release(xpc_data
); 
 434 SecurityAgentXPCQuery::invoke() { 
 435     xpc_object_t hintsArray 
= authItemSetToXPCArray(mInHints
); 
 436     xpc_object_t contextArray 
= authItemSetToXPCArray(mInContext
); 
 437     xpc_object_t immutableHintsArray 
= authItemSetToXPCArray(mImmutableHints
); 
 439     xpc_object_t requestObject 
= xpc_dictionary_create(NULL
, NULL
, 0); 
 440     xpc_dictionary_set_string(requestObject
, AUTH_XPC_REQUEST_METHOD_KEY
, AUTH_XPC_REQUEST_METHOD_INVOKE
); 
 441     xpc_dictionary_set_value(requestObject
, AUTH_XPC_HINTS_NAME
, hintsArray
); 
 442     xpc_dictionary_set_value(requestObject
, AUTH_XPC_CONTEXT_NAME
, contextArray
); 
 443     xpc_dictionary_set_value(requestObject
, AUTH_XPC_IMMUTABLE_HINTS_NAME
, immutableHintsArray
); 
 445     xpc_object_t object 
= xpc_connection_send_message_with_reply_sync(mXPCConnection
, requestObject
); 
 446     if (xpc_get_type(object
) == XPC_TYPE_DICTIONARY
) { 
 447         const char *replyType 
= xpc_dictionary_get_string(object
, AUTH_XPC_REPLY_METHOD_KEY
); 
 448         if (0 == strcmp(replyType
, AUTH_XPC_REPLY_METHOD_RESULT
)) { 
 449             xpc_object_t xpcHints 
= xpc_dictionary_get_value(object
, AUTH_XPC_HINTS_NAME
); 
 450             xpc_object_t xpcContext 
= xpc_dictionary_get_value(object
, AUTH_XPC_CONTEXT_NAME
); 
 451             AuthItemSet tempHints
, tempContext
; 
 452             xpcArrayToAuthItemSet(&tempHints
, xpcHints
); 
 453             xpcArrayToAuthItemSet(&tempContext
, xpcContext
); 
 454             mOutHints 
= tempHints
; 
 455             mOutContext 
= tempContext
; 
 456             mLastResult 
= xpc_dictionary_get_uint64(object
, AUTH_XPC_REPLY_RESULT_VALUE
); 
 458     } else if (xpc_get_type(object
) == XPC_TYPE_ERROR
) { 
 459         if (XPC_ERROR_CONNECTION_INVALID 
== object
) { 
 460             // If the connection drops, return an "auth undefined" result, because we cannot continue 
 461         } else if (XPC_ERROR_CONNECTION_INTERRUPTED 
== object
) { 
 462             // If the agent dies, return an "auth undefined" result, because we cannot continue 
 467     xpc_release(hintsArray
); 
 468     xpc_release(contextArray
); 
 469     xpc_release(immutableHintsArray
); 
 470     xpc_release(requestObject
); 
 473 void SecurityAgentXPCQuery::checkResult() 
 475     // now check the OSStatus return from the server side 
 476     switch (mLastResult
) { 
 477         case kAuthorizationResultAllow
: return; 
 478         case kAuthorizationResultDeny
: 
 479         case kAuthorizationResultUserCanceled
: CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED
); 
 480         default: MacOSError::throwMe(errAuthorizationInternal
); 
 485 // Perform the "rogue app" access query dialog 
 487 QueryKeychainUse::QueryKeychainUse(bool needPass
, const Database 
*db
) 
 488         : mPassphraseCheck(NULL
) 
 490         // if passphrase checking requested, save KeychainDatabase reference 
 491         // (will quietly disable check if db isn't a keychain) 
 493     // Always require password due to <rdar://problem/34677969> 
 494     mPassphraseCheck 
= dynamic_cast<const KeychainDatabase 
*>(db
); 
 496     setTerminateOnSleep(true); 
 499 // Callers to this function must hold the common lock 
 500 Reason 
QueryKeychainUse::queryUser (const char *database
, const char *description
, AclAuthorization action
) 
 502     Reason reason 
= SecurityAgent::noReason
; 
 503         uint32_t retryCount 
= 0; 
 504         AuthItemSet hints
, context
; 
 506         // prepopulate with client hints 
 507         hints
.insert(mClientHints
.begin(), mClientHints
.end()); 
 509         // put action/operation (sint32) into hints 
 510         hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
)))); 
 512         // item name into hints 
 514     hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description 
? (uint32_t)strlen(description
) : 0, const_cast<char*>(description
)))); 
 516         // keychain name into hints 
 517         hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database 
? (uint32_t)strlen(database
) : 0, const_cast<char*>(database
)))); 
 519         if (mPassphraseCheck
) 
 521                 create("builtin", "confirm-access-password"); 
 523                 CssmAutoData 
data(Allocator::standard(Allocator::sensitive
)); 
 528             AuthItemRef 
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
)); 
 529             hints
.erase(triesHint
); hints
.insert(triesHint
); // replace 
 531             if (retryCount
++ > kMaximumAuthorizationTries
) 
 533                 reason 
= SecurityAgent::tooManyTries
; 
 536             AuthItemRef 
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
)); 
 537             hints
.erase(retryHint
); hints
.insert(retryHint
); // replace 
 539             setInput(hints
, context
); 
 542                 // Must drop the common lock while showing UI. 
 543                 StSyncLock
<Mutex
, Mutex
> syncLock(const_cast<KeychainDatabase
*>(mPassphraseCheck
)->common().uiLock(), const_cast<KeychainDatabase
*>(mPassphraseCheck
)->common()); 
 547             if (retryCount 
> kMaximumAuthorizationTries
) 
 554                         AuthItem 
*passwordItem 
= mOutContext
.find(kAuthorizationEnvironmentPassword
); 
 558                         passwordItem
->getCssmData(data
); 
 560             reason 
= (const_cast<KeychainDatabase
*>(mPassphraseCheck
)->decode(data
) ? SecurityAgent::noReason 
: SecurityAgent::invalidPassphrase
); 
 562         while (reason 
!= SecurityAgent::noReason
); 
 568 //        create("builtin", "confirm-access"); 
 569 //        setInput(hints, context); 
 572         // This is a hack to support <rdar://problem/34677969>, we can never simply prompt for confirmation 
 573         secerror("ACL validation fallback case! Must ask user for account password because we have no database"); 
 574         Session 
&session 
= Server::session(); 
 576             session
.verifyKeyStorePassphrase(1, true, description
); 
 578             return SecurityAgent::invalidPassphrase
; 
 580         SecurityAgentXPCQuery::allow 
= true; 
 587 // Obtain passphrases and submit them to the accept() method until it is accepted 
 588 // or we can't get another passphrase. Accept() should consume the passphrase 
 589 // if it is accepted. If no passphrase is acceptable, throw out of here. 
 591 Reason 
QueryOld::query() 
 593         Reason reason 
= SecurityAgent::noReason
; 
 594         AuthItemSet hints
, context
; 
 595         CssmAutoData 
passphrase(Allocator::standard(Allocator::sensitive
)); 
 598         // prepopulate with client hints 
 600     const char *keychainPath 
= database
.dbName(); 
 601     hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay((uint32_t)strlen(keychainPath
), const_cast<char*>(keychainPath
)))); 
 603         hints
.insert(mClientHints
.begin(), mClientHints
.end()); 
 605         create("builtin", "unlock-keychain"); 
 609         AuthItemRef 
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
)); 
 610         hints
.erase(triesHint
); hints
.insert(triesHint
); // replace 
 614         if (retryCount 
> maxTries
) 
 616                         reason 
= SecurityAgent::tooManyTries
; 
 619         AuthItemRef 
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
)); 
 620         hints
.erase(retryHint
); hints
.insert(retryHint
); // replace 
 622         setInput(hints
, context
); 
 625         if (retryCount 
> maxTries
) 
 632                 AuthItem 
*passwordItem 
= mOutContext
.find(kAuthorizationEnvironmentPassword
); 
 636                 passwordItem
->getCssmData(passphrase
); 
 639         while ((reason 
= accept(passphrase
))); 
 641         return SecurityAgent::noReason
; 
 646 // Get existing passphrase (unlock) Query 
 648 Reason 
QueryOld::operator () () 
 655 // End-classes for old secrets 
 657 Reason 
QueryUnlock::accept(CssmManagedData 
&passphrase
) 
 659     // Must hold the 'common' lock to call decode; otherwise there's a data corruption issue 
 660     StLock
<Mutex
> _(safer_cast
<KeychainDatabase 
&>(database
).common()); 
 662         if (safer_cast
<KeychainDatabase 
&>(database
).decode(passphrase
)) 
 663                 return SecurityAgent::noReason
; 
 665                 return SecurityAgent::invalidPassphrase
; 
 668 Reason 
QueryUnlock::retrievePassword(CssmOwnedData 
&passphrase
) { 
 669     CssmAutoData 
pass(Allocator::standard(Allocator::sensitive
)); 
 671     AuthItem 
*passwordItem 
= mOutContext
.find(kAuthorizationEnvironmentPassword
); 
 673         return SecurityAgent::invalidPassphrase
; 
 675     passwordItem
->getCssmData(pass
); 
 679    return SecurityAgent::noReason
; 
 682 QueryKeybagPassphrase::QueryKeybagPassphrase(Session 
& session
, int32_t tries
) : mSession(session
), mContext(), mRetries(tries
) 
 684     setTerminateOnSleep(true); 
 685     mContext 
= mSession
.get_current_service_context(); 
 688 Reason 
QueryKeybagPassphrase::query() 
 690         Reason reason 
= SecurityAgent::noReason
; 
 691         AuthItemSet hints
, context
; 
 692         CssmAutoData 
passphrase(Allocator::standard(Allocator::sensitive
)); 
 695         // prepopulate with client hints 
 697     const char *keychainPath 
= "iCloud"; 
 698     hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay((uint32_t)strlen(keychainPath
), const_cast<char*>(keychainPath
)))); 
 700         hints
.insert(mClientHints
.begin(), mClientHints
.end()); 
 702         create("builtin", "unlock-keychain"); 
 707         currentTry 
= retryCount
; 
 708         if (retryCount 
> mRetries
) 
 710                         return SecurityAgent::tooManyTries
; 
 714         AuthItemRef 
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(currentTry
), ¤tTry
)); 
 715         hints
.erase(triesHint
); hints
.insert(triesHint
); // replace 
 717         AuthItemRef 
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
)); 
 718         hints
.erase(retryHint
); hints
.insert(retryHint
); // replace 
 720         setInput(hints
, context
); 
 725                 AuthItem 
*passwordItem 
= mOutContext
.find(kAuthorizationEnvironmentPassword
); 
 729                 passwordItem
->getCssmData(passphrase
); 
 731         while ((reason 
= accept(passphrase
))); 
 733         return SecurityAgent::noReason
; 
 736 Reason 
QueryKeybagPassphrase::accept(Security::CssmManagedData 
& password
) 
 738         if (service_client_kb_unlock(&mContext
, password
.data(), (int)password
.length()) == 0) { 
 739                 mSession
.keybagSetState(session_keybag_unlocked
); 
 740         return SecurityAgent::noReason
; 
 742                 return SecurityAgent::invalidPassphrase
; 
 745 QueryKeybagNewPassphrase::QueryKeybagNewPassphrase(Session 
& session
) : QueryKeybagPassphrase(session
) {} 
 747 Reason 
QueryKeybagNewPassphrase::query(CssmOwnedData 
&oldPassphrase
, CssmOwnedData 
&passphrase
) 
 749     CssmAutoData 
pass(Allocator::standard(Allocator::sensitive
)); 
 750     CssmAutoData 
oldPass(Allocator::standard(Allocator::sensitive
)); 
 751     Reason reason 
= SecurityAgent::noReason
; 
 752         AuthItemSet hints
, context
; 
 755         // prepopulate with client hints 
 757     const char *keychainPath 
= "iCloud"; 
 758     hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay((uint32_t)strlen(keychainPath
), const_cast<char*>(keychainPath
)))); 
 760     const char *showResetString 
= "YES"; 
 761     hints
.insert(AuthItemRef(AGENT_HINT_SHOW_RESET
, AuthValueOverlay((uint32_t)strlen(showResetString
), const_cast<char*>(showResetString
)))); 
 763         hints
.insert(mClientHints
.begin(), mClientHints
.end()); 
 765         create("builtin", "change-passphrase"); 
 768     AuthItem 
*resetPassword 
= NULL
; 
 771         currentTry 
= retryCount
; 
 772         if (retryCount 
> mRetries
) 
 774                         return SecurityAgent::tooManyTries
; 
 778         AuthItemRef 
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(currentTry
), ¤tTry
)); 
 779         hints
.erase(triesHint
); hints
.insert(triesHint
); // replace 
 781         AuthItemRef 
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
)); 
 782         hints
.erase(retryHint
); hints
.insert(retryHint
); // replace 
 784         setInput(hints
, context
); 
 789         resetPassword 
= mOutContext
.find(AGENT_CONTEXT_RESET_PASSWORD
); 
 790         if (resetPassword 
!= NULL
) { 
 791             return SecurityAgent::resettingPassword
; 
 794         AuthItem 
*oldPasswordItem 
= mOutContext
.find(AGENT_PASSWORD
); 
 795         if (!oldPasswordItem
) 
 798         oldPasswordItem
->getCssmData(oldPass
); 
 800         while ((reason 
= accept(oldPass
))); 
 802     if (reason 
== SecurityAgent::noReason
) { 
 803                 AuthItem 
*passwordItem 
= mOutContext
.find(AGENT_CONTEXT_NEW_PASSWORD
); 
 805             return SecurityAgent::invalidPassphrase
; 
 807                 passwordItem
->getCssmData(pass
); 
 809         oldPassphrase 
= oldPass
; 
 813         return SecurityAgent::noReason
; 
 816 QueryPIN::QueryPIN(Database 
&db
) 
 817         : QueryOld(db
), mPin(Allocator::standard()) 
 819         this->inferHints(Server::process()); 
 823 Reason 
QueryPIN::accept(CssmManagedData 
&pin
) 
 825         // no retries for now 
 827         return SecurityAgent::noReason
; 
 832 // Obtain passphrases and submit them to the accept() method until it is accepted 
 833 // or we can't get another passphrase. Accept() should consume the passphrase 
 834 // if it is accepted. If no passphrase is acceptable, throw out of here. 
 836 Reason 
QueryNewPassphrase::query() 
 838         Reason reason 
= initialReason
; 
 839         CssmAutoData 
passphrase(Allocator::standard(Allocator::sensitive
)); 
 840         CssmAutoData 
oldPassphrase(Allocator::standard(Allocator::sensitive
)); 
 842         AuthItemSet hints
, context
; 
 846         // prepopulate with client hints 
 847         hints
.insert(mClientHints
.begin(), mClientHints
.end()); 
 849         // keychain name into hints 
 850         hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
.dbName()))); 
 852     switch (initialReason
) 
 854         case SecurityAgent::newDatabase
: 
 855             create("builtin", "new-passphrase"); 
 857         case SecurityAgent::changePassphrase
: 
 858             create("builtin", "change-passphrase"); 
 866         AuthItemRef 
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
)); 
 867         hints
.erase(triesHint
); hints
.insert(triesHint
); // replace 
 869                 if (++retryCount 
> maxTries
) 
 871                         reason 
= SecurityAgent::tooManyTries
; 
 874         AuthItemRef 
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
)); 
 875         hints
.erase(retryHint
); hints
.insert(retryHint
); // replace 
 877         setInput(hints
, context
); 
 880                 if (retryCount 
> maxTries
) 
 887                 if (SecurityAgent::changePassphrase 
== initialReason
) 
 889             AuthItem 
*oldPasswordItem 
= mOutContext
.find(AGENT_PASSWORD
); 
 890             if (!oldPasswordItem
) 
 893             oldPasswordItem
->getCssmData(oldPassphrase
); 
 896                 AuthItem 
*passwordItem 
= mOutContext
.find(AGENT_CONTEXT_NEW_PASSWORD
); 
 900                 passwordItem
->getCssmData(passphrase
); 
 903         while ((reason 
= accept(passphrase
, (initialReason 
== SecurityAgent::changePassphrase
) ? &oldPassphrase
.get() : NULL
))); 
 905         return SecurityAgent::noReason
; 
 910 // Get new passphrase Query 
 912 Reason 
QueryNewPassphrase::operator () (CssmOwnedData 
&oldPassphrase
, CssmOwnedData 
&passphrase
) 
 914         if (Reason result 
= query()) 
 915                 return result
;  // failed 
 916         passphrase 
= mPassphrase
; 
 917     oldPassphrase 
= mOldPassphrase
; 
 918         return SecurityAgent::noReason
; // success 
 921 Reason 
QueryNewPassphrase::accept(CssmManagedData 
&passphrase
, CssmData 
*oldPassphrase
) 
 923         //@@@ acceptance criteria are currently hardwired here 
 924         //@@@ This validation presumes ASCII - UTF8 might be more lenient 
 926         // if we have an old passphrase, check it 
 927         if (oldPassphrase 
&& !safer_cast
<KeychainDatabase
&>(database
).validatePassphrase(*oldPassphrase
)) 
 928                 return SecurityAgent::oldPassphraseWrong
; 
 930         // sanity check the new passphrase (but allow user override) 
 931         if (!(mPassphraseValid 
&& passphrase
.get() == mPassphrase
)) { 
 932                 mPassphrase 
= passphrase
; 
 933         if (oldPassphrase
) mOldPassphrase 
= *oldPassphrase
; 
 934                 mPassphraseValid 
= true; 
 935                 if (mPassphrase
.length() == 0) 
 936                         return SecurityAgent::passphraseIsNull
; 
 937                 if (mPassphrase
.length() < 6) 
 938                         return SecurityAgent::passphraseTooSimple
; 
 942         return SecurityAgent::noReason
; 
 946 // Get a passphrase for unspecified use 
 948 Reason 
QueryGenericPassphrase::operator () (const CssmData 
*prompt
, bool verify
, 
 951     return query(prompt
, verify
, passphrase
); 
 954 Reason 
QueryGenericPassphrase::query(const CssmData 
*prompt
, bool verify
, 
 957     Reason reason 
= SecurityAgent::noReason
; 
 958     AuthItemSet hints
, context
; 
 960     hints
.insert(mClientHints
.begin(), mClientHints
.end()); 
 961     hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt 
? (UInt32
)prompt
->length() : 0, prompt 
? prompt
->data() : NULL
))); 
 962     // XXX/gh  defined by dmitch but no analogous hint in 
 963     // AuthorizationTagsPriv.h: 
 964     // CSSM_ATTRIBUTE_ALERT_TITLE (optional alert panel title) 
 966     if (false == verify
) {  // import 
 967                 create("builtin", "generic-unlock"); 
 968     } else {            // verify passphrase (export) 
 969                 create("builtin", "generic-new-passphrase"); 
 972     AuthItem 
*passwordItem
; 
 975         setInput(hints
, context
); 
 978                 passwordItem 
= mOutContext
.find(AGENT_PASSWORD
); 
 980     } while (!passwordItem
); 
 982     passwordItem
->getString(passphrase
); 
 989 // Get a DB blob's passphrase--keychain synchronization 
 991 Reason 
QueryDBBlobSecret::operator () (DbHandle 
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle 
*dbHandleAuthenticated
) 
 993     return query(dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
); 
 996 Reason 
QueryDBBlobSecret::query(DbHandle 
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle 
*dbHandleAuthenticated
) 
 998     Reason reason 
= SecurityAgent::noReason
; 
 999         CssmAutoData 
passphrase(Allocator::standard(Allocator::sensitive
)); 
1000     AuthItemSet hints
/*NUKEME*/, context
; 
1002         hints
.insert(mClientHints
.begin(), mClientHints
.end()); 
1003         create("builtin", "generic-unlock-kcblob"); 
1005     AuthItem 
*secretItem
; 
1010         AuthItemRef 
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
)); 
1011         hints
.erase(triesHint
); hints
.insert(triesHint
); // replace 
1013                 if (++retryCount 
> maxTries
) 
1015                         reason 
= SecurityAgent::tooManyTries
; 
1018         AuthItemRef 
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
)); 
1019         hints
.erase(retryHint
); hints
.insert(retryHint
); // replace 
1021         setInput(hints
, context
); 
1024                 secretItem 
= mOutContext
.find(AGENT_PASSWORD
); 
1027                 secretItem
->getCssmData(passphrase
); 
1029     } while ((reason 
= accept(passphrase
, dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
))); 
1034 Reason 
QueryDBBlobSecret::accept(CssmManagedData 
&passphrase
, 
1035                                                                  DbHandle 
*dbHandlesToAuthenticate
, uint8 dbHandleCount
, DbHandle 
*dbHandleAuthenticated
) 
1037         DbHandle 
*currHdl 
= dbHandlesToAuthenticate
; 
1039         Boolean authenticated 
= false; 
1040         for (index
=0; index 
< dbHandleCount 
&& !authenticated
; index
++) 
1044                         RefPointer
<KeychainDatabase
> dbToUnlock 
= Server::keychain(*currHdl
); 
1045                         dbToUnlock
->unlockDb(passphrase
, false); 
1046                         authenticated 
= true; 
1047                         *dbHandleAuthenticated 
= *currHdl
; // return the DbHandle that 'passphrase' authenticated with. 
1049                 catch (const CommonError 
&err
) 
1051                         currHdl
++; // we failed to authenticate with this one, onto the next one. 
1054         if ( !authenticated 
) 
1055                 return SecurityAgent::invalidPassphrase
; 
1057         return SecurityAgent::noReason
; 
1060 // @@@  no pluggable authentication possible! 
1062 QueryKeychainAuth::operator () (const char *database
, const char *description
, AclAuthorization action
, const char *prompt
) 
1064     Reason reason 
= SecurityAgent::noReason
; 
1065     AuthItemSet hints
, context
; 
1070     using CommonCriteria::Securityd::KeychainAuthLogger
; 
1071     KeychainAuthLogger 
logger(mAuditToken
, (short)AUE_ssauthint
, database
, description
); 
1073     hints
.insert(mClientHints
.begin(), mClientHints
.end()); 
1075         // put action/operation (sint32) into hints 
1076         hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
)))); 
1078     hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt 
? (uint32_t)strlen(prompt
) : 0, const_cast<char*>(prompt
)))); 
1080         // item name into hints 
1081         hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description 
? (uint32_t)strlen(description
) : 0, const_cast<char*>(description
)))); 
1083         // keychain name into hints 
1084         hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database 
? (uint32_t)strlen(database
) : 0, const_cast<char*>(database
)))); 
1086     create("builtin", "confirm-access-user-password"); 
1088     AuthItem 
*usernameItem
; 
1089     AuthItem 
*passwordItem
; 
1093         AuthItemRef 
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
)); 
1094         hints
.erase(triesHint
); hints
.insert(triesHint
); // replace 
1096                 if (++retryCount 
> maxTries
) 
1097                         reason 
= SecurityAgent::tooManyTries
; 
1099         if (SecurityAgent::noReason 
!= reason
) 
1101             if (SecurityAgent::tooManyTries 
== reason
) 
1102                 logger
.logFailure(NULL
,  CommonCriteria::errTooManyTries
); 
1104                 logger
.logFailure(); 
1107         AuthItemRef 
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
)); 
1108         hints
.erase(retryHint
); hints
.insert(retryHint
); // replace 
1110         setInput(hints
, context
); 
1116         catch (...)     // user probably clicked "deny" 
1118             logger
.logFailure(); 
1121         usernameItem 
= mOutContext
.find(AGENT_USERNAME
); 
1122                 passwordItem 
= mOutContext
.find(AGENT_PASSWORD
); 
1123                 if (!usernameItem 
|| !passwordItem
) 
1125         usernameItem
->getString(username
); 
1126         passwordItem
->getString(password
); 
1127     } while ((reason 
= accept(username
, password
))); 
1129     if (SecurityAgent::noReason 
== reason
) 
1130         logger
.logSuccess(); 
1131     // else we logged the denial in the loop 
1137 QueryKeychainAuth::accept(string 
&username
, string 
&passphrase
) 
1139         // Note: QueryKeychainAuth currently requires that the 
1140         // specified user be in the admin group. If this requirement 
1141         // ever needs to change, the group name should be passed as 
1142         // a separate argument to this method. 
1144         const char *user 
= username
.c_str(); 
1145         const char *passwd 
= passphrase
.c_str(); 
1146         int checkpw_status 
= checkpw(user
, passwd
); 
1148         if (checkpw_status 
!= CHECKPW_SUCCESS
) { 
1149                 return SecurityAgent::invalidPassphrase
; 
1152         const char *group 
= "admin"; 
1155                 uuid_t group_uuid
, user_uuid
; 
1156                 rc 
= mbr_group_name_to_uuid(group
, group_uuid
); 
1157                 if (rc
) { return SecurityAgent::userNotInGroup
; } 
1159                 rc 
= mbr_user_name_to_uuid(user
, user_uuid
); 
1160                 if (rc
) { return SecurityAgent::userNotInGroup
; } 
1162                 rc 
= mbr_check_membership(user_uuid
, group_uuid
, &ismember
); 
1163                 if (rc 
|| !ismember
) { return SecurityAgent::userNotInGroup
; } 
1166         return SecurityAgent::noReason
;