]> git.saurik.com Git - apple/security.git/blobdiff - securityd/src/agentquery.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / securityd / src / agentquery.cpp
index 9694585b211ac233943c994d3f51fa714aeed4a7..3c813f4ccf6dd6d41effa20219984ad9a79d6d5f 100644 (file)
@@ -1,15 +1,15 @@
 /*
- * Copyright (c) 2000-2013 Apple Inc. All Rights Reserved.
- * 
+ * Copyright (c) 2000-2015 Apple Inc. All Rights Reserved.
+ *
  * @APPLE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * compliance with the License. Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this
  * file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_LICENSE_HEADER_END@
  */
 
 //
 // passphrases - canonical code to obtain passphrases
 //
+#define __STDC_WANT_LIB_EXT1__ 1
+#include <string.h>
+
 #include "agentquery.h"
-#include "authority.h"
 #include "ccaudit_extensions.h"
 
 #include <Security/AuthorizationTags.h>
 #include <xpc/private.h>
 #include "securityd_service/securityd_service/securityd_service_client.h"
 
-#define SECURITYAGENT_BOOTSTRAP_NAME_BASE       "com.apple.security.agentMain"
-#define SECURITYAGENT_STUB_BOOTSTRAP_NAME_BASE       "com.apple.security.agentStub"
-#define AUTHORIZATIONHOST_BOOTSTRAP_NAME_BASE   "com.apple.security.authhost"
+// Includes for <rdar://problem/34677969> Always require the user's password on keychain approval dialogs
+#include "server.h"
+
+#define SECURITYAGENT_BOOTSTRAP_NAME_BASE               "com.apple.security.agent"
+#define SECURITYAGENT_LOGINWINDOW_BOOTSTRAP_NAME_BASE   "com.apple.security.agent.login"
 
 #define AUTH_XPC_ITEM_NAME  "_item_name"
 #define AUTH_XPC_ITEM_FLAGS "_item_flags"
 #define AUTH_XPC_ITEM_VALUE "_item_value"
 #define AUTH_XPC_ITEM_TYPE  "_item_type"
+#define AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH "_item_sensitive_value_length"
 
 #define AUTH_XPC_REQUEST_METHOD_KEY "_agent_request_key"
 #define AUTH_XPC_REQUEST_METHOD_CREATE "_agent_request_create"
 #define AUTH_XPC_REPLY_RESULT_VALUE "_agent_reply_result_value"
 #define AUTH_XPC_AUDIT_SESSION_PORT "_agent_audit_session_port"
 #define AUTH_XPC_BOOTSTRAP_PORT "_agent_bootstrap_port"
-#define AUTH_XPC_SESSION_UUID "_agent_session_uuid"
-#define AUTH_XPC_SESSION_PREFS "_agent_session_prefs"
-#define AUTH_XPC_SESSION_INPUT_METHOD "_agent_session_inputMethod"
 
 #define UUID_INITIALIZER_FROM_SESSIONID(sessionid) \
 { 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))) }
 
-//
-// NOSA support functions. This is a test mode where the SecurityAgent
-// is simulated via stdio in the client. Good for running automated tests
-// of client programs. Only available if -DNOSA when compiling.
-//
-#if defined(NOSA)
-
-#include <cstdarg>
-
-static void getNoSA(char *buffer, size_t bufferSize, const char *fmt, ...)
-{
-       // write prompt
-       va_list args;
-       va_start(args, fmt);
-       vfprintf(stdout, fmt, args);
-       va_end(args);
-
-       // read reply
-       memset(buffer, 0, bufferSize);
-       const char *nosa = getenv("NOSA");
-       if (!strcmp(nosa, "-")) {
-               if (fgets(buffer, bufferSize-1, stdin) == NULL)
-                       CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
-               buffer[strlen(buffer)-1] = '\0';        // remove trailing newline
-               if (!isatty(fileno(stdin)))
-                       printf("%s\n", buffer);                 // echo to output if input not terminal
-       } else {
-               strncpy(buffer, nosa, bufferSize-1);
-               printf("%s\n", buffer);
-       }
-       if (buffer[0] == '\0')                          // empty input -> cancellation
-               CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
-}
-
-#endif //NOSA
-
-
-// SecurityAgentConnection
-
-SecurityAgentConnection::SecurityAgentConnection(const AuthHostType type, Session &session)
-: mAuthHostType(type),
-mHostInstance(session.authhost(mAuthHostType)),
-mConnection(&Server::connection()),
-mAuditToken(Server::connection().auditToken())
-{
-       // this may take a while
-       Server::active().longTermActivity();
-    secdebug("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
-}
-
-SecurityAgentConnection::~SecurityAgentConnection()
-{
-    secdebug("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this);
-       mConnection->useAgent(NULL);
-}
-
-void
-SecurityAgentConnection::activate()
-{
-    secdebug("SecurityAgentConnection", "activate(%p)", this);
-       
-    Session &session = mHostInstance->session();
-    SessionId targetSessionId = session.sessionId();
-    MachPlusPlus::Bootstrap processBootstrap = Server::process().taskPort().bootstrap();
-    fileport_t userPrefsFP = MACH_PORT_NULL;
-       
-    // send the the userPrefs to SecurityAgent
-    if (mAuthHostType == securityAgent || mAuthHostType == userAuthHost) {
-               CFRef<CFDataRef> userPrefs(mHostInstance->session().copyUserPrefs());
-               if (0 != userPrefs)
-               {
-                       FILE *mbox = NULL;
-                       int fd = 0;
-                       mbox = tmpfile();
-                       if (NULL != mbox)
-                       {
-                               fd = dup(fileno(mbox));
-                               fclose(mbox);
-                               if (fd != -1)
-                               {
-                                       CFIndex length = CFDataGetLength(userPrefs);
-                                       if (write(fd, CFDataGetBytePtr(userPrefs), length) != length)
-                                               Syslog::error("could not write userPrefs");
-                                       else
-                                       {
-                                               if (0 == fileport_makeport(fd, &userPrefsFP))
-                                                       secdebug("SecurityAgentConnection", "stashed the userPrefs file descriptor");
-                                               else
-                                                       Syslog::error("failed to stash the userPrefs file descriptor");
-                                       }
-                                       close(fd);
-                               }
-                       }
-               }
-               if (MACH_PORT_NULL == userPrefsFP)
-               {
-                       secdebug("SecurityAgentConnection", "could not read userPrefs");
-               }
-    }
-    
-       mConnection->useAgent(this);
-       try
-    {
-        StLock<Mutex> _(*mHostInstance);
-               
-        mach_port_t lookupPort = mHostInstance->lookup(targetSessionId);
-        if (MACH_PORT_NULL == lookupPort)
-        {
-                       Syslog::error("could not find real service, bailing");
-                       MacOSError::throwMe(CSSM_ERRCODE_SERVICE_NOT_AVAILABLE);
-        }
-        // reset Client contact info
-        mPort = lookupPort;
-        SecurityAgent::Client::activate(mPort);
-        
-        secdebug("SecurityAgentConnection", "%p activated", this);
-       }
-    catch (MacOSError &err)
-    {
-               mConnection->useAgent(NULL);    // guess not
-        Syslog::error("SecurityAgentConnection: error activating %s instance %p",
-                      mAuthHostType == privilegedAuthHost
-                      ? "authorizationhost"
-                      : "SecurityAgent", this);
-               throw;
-       }
-       
-    secdebug("SecurityAgentConnection", "contacting service (%p)", this);
-       mach_port_name_t jobPort;
-       if (0 > audit_session_port(session.sessionId(), &jobPort))
-               Syslog::error("audit_session_port failed: %m");
-    MacOSError::check(SecurityAgent::Client::contact(jobPort, processBootstrap, userPrefsFP));
-    secdebug("SecurityAgentConnection", "contact didn't throw (%p)", this);
-       
-    if (userPrefsFP != MACH_PORT_NULL)
-        mach_port_deallocate(mach_task_self(), userPrefsFP);
-}
-
-void
-SecurityAgentConnection::reconnect()
-{
-    // if !mHostInstance throw()?
-    if (mHostInstance)
-    {
-        activate();
-    }
-}
-
-void
-SecurityAgentConnection::terminate()
-{
-       activate();
-    
-    // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
-       mConnection->useAgent(NULL);
-}
-
 
-// SecurityAgentConnection
+// SecurityAgentXPCConnection
 
-SecurityAgentXPCConnection::SecurityAgentXPCConnection(const AuthHostType type, Session &session)
-: mAuthHostType(type),
-mHostInstance(session.authhost(mAuthHostType)),
+SecurityAgentXPCConnection::SecurityAgentXPCConnection(Session &session)
+: mHostInstance(session.authhost()),
 mSession(session),
 mConnection(&Server::connection()),
 mAuditToken(Server::connection().auditToken())
 {
        // this may take a while
        Server::active().longTermActivity();
-    secdebug("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
+    secnotice("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
     mXPCConnection = NULL;
-    mXPCStubConnection = NULL;
     mNobodyUID = -2;
     struct passwd *pw = getpwnam("nobody");
     if (NULL != pw) {
@@ -260,24 +103,18 @@ mAuditToken(Server::connection().auditToken())
 
 SecurityAgentXPCConnection::~SecurityAgentXPCConnection()
 {
-    secdebug("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this);
+    secnotice("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this);
        mConnection->useAgent(NULL);
-    
+
     // If a connection has been established, we need to tear it down.
     if (NULL != mXPCConnection) {
         // Tearing this down is a multi-step process. First, request a cancellation.
-        // This is safe even if the connection is already in the cancelled state.
+        // This is safe even if the connection is already in the canceled state.
         xpc_connection_cancel(mXPCConnection);
-        
+
         // Then release the XPC connection
         xpc_release(mXPCConnection);
         mXPCConnection = NULL;
-
-        if (NULL != mXPCStubConnection) {
-            // We may or may not have one of these
-            xpc_release(mXPCStubConnection);
-            mXPCStubConnection = NULL;
-        }
     }
 }
 
@@ -289,84 +126,58 @@ bool SecurityAgentXPCConnection::inDarkWake()
 void
 SecurityAgentXPCConnection::activate(bool ignoreUid)
 {
-    secdebug("SecurityAgentConnection", "activate(%p)", this);
-    
+    secnotice("SecurityAgentConnection", "activate(%p)", this);
+
        mConnection->useAgent(this);
     if (mXPCConnection != NULL) {
         // If we already have an XPC connection, there's nothing to do.
         return;
     }
-       try
-    {
-        if (mAuthHostType == securityAgent) {
-            uuid_t sessionUUID = UUID_INITIALIZER_FROM_SESSIONID(mSession.sessionId());
-            // Yes, these need to be throws, as we're still in securityd, and thus still have to do flow control with exceptions.
-            if (!(mSession.attributes() & sessionHasGraphicAccess))
-                CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
-            if (inDarkWake())
-                CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE);
-            uid_t targetUid = mHostInstance->session().originatorUid();
-            secdebug("SecurityAgentXPCConnection","Retrieved UID %d for this session", targetUid);
-            if ((int32_t)targetUid != -1) {
-                mXPCStubConnection = xpc_connection_create_mach_service(SECURITYAGENT_STUB_BOOTSTRAP_NAME_BASE, NULL, 0);
-                xpc_connection_set_target_uid(mXPCStubConnection, targetUid);
-                secdebug("SecurityAgentXPCConnection", "Creating a security agent stub");
-                xpc_connection_set_event_handler(mXPCStubConnection, ^(xpc_object_t object){}); // Yes, this is a dummy handler, we never ever care about any responses from the stub. It can die in a fire for all I care.
-                xpc_connection_resume(mXPCStubConnection);
-                
-                xpc_object_t wakeupMessage = xpc_dictionary_create(NULL, NULL, 0);
-                xpc_dictionary_set_data(wakeupMessage, AUTH_XPC_SESSION_UUID, sessionUUID, sizeof(uuid_t));
-                xpc_object_t responseMessage = xpc_connection_send_message_with_reply_sync(mXPCStubConnection, wakeupMessage);
-                if (xpc_get_type(responseMessage) == XPC_TYPE_DICTIONARY) {
-                    secdebug("SecurityAgentXPCConnection", "Valid response received from stub");
-                } else {
-                    secdebug("SecurityAgentXPCConnection", "Error response received from stub");
-                }
-                xpc_release(wakeupMessage);
-                xpc_release(responseMessage);
-            }
-            
-            mXPCConnection = xpc_connection_create_mach_service(SECURITYAGENT_BOOTSTRAP_NAME_BASE, NULL,0);
-            xpc_connection_set_instance(mXPCConnection, sessionUUID);
-            secdebug("SecurityAgentXPCConnection", "Creating a security agent");
-        } else {
-            mXPCConnection = xpc_connection_create_mach_service(AUTHORIZATIONHOST_BOOTSTRAP_NAME_BASE, NULL, 0);
-            secdebug("SecurityAgentXPCConnection", "Creating a standard authhost");
-        }
-        
-        xpc_connection_set_event_handler(mXPCConnection, ^(xpc_object_t object) {
-            if (xpc_get_type(object) == XPC_TYPE_ERROR) {
-                secdebug("SecurityAgentXPCConnection", "error during xpc: %s", xpc_dictionary_get_string(object, XPC_ERROR_KEY_DESCRIPTION));
-            }
-        });
-        
-        xpc_connection_resume(mXPCConnection);
-        
-        secdebug("SecurityAgentXPCConnection", "%p activated", this);
+
+       try {
+               uuid_t sessionUUID = UUID_INITIALIZER_FROM_SESSIONID(mSession.sessionId());
+
+               // Yes, these need to be throws, as we're still in securityd, and thus still have to do flow control with exceptions.
+               if (!(mSession.attributes() & sessionHasGraphicAccess))
+                       CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
+               if (inDarkWake())
+                       CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE);
+               uid_t targetUid = mHostInstance->session().originatorUid();
+
+               secnotice("SecurityAgentXPCConnection","Retrieved UID %d for this session", targetUid);
+               if (!ignoreUid && targetUid != 0 && targetUid != mNobodyUID) {
+                       mXPCConnection = xpc_connection_create_mach_service(SECURITYAGENT_BOOTSTRAP_NAME_BASE, NULL, 0);
+                       xpc_connection_set_target_uid(mXPCConnection, targetUid);
+                       secnotice("SecurityAgentXPCConnection", "Creating a standard security agent");
+               } else {
+                       mXPCConnection = xpc_connection_create_mach_service(SECURITYAGENT_LOGINWINDOW_BOOTSTRAP_NAME_BASE, NULL,
+                                                                XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
+                       xpc_connection_set_instance(mXPCConnection, sessionUUID);
+                       secnotice("SecurityAgentXPCConnection", "Creating a loginwindow security agent");
+               }
+
+               xpc_connection_set_event_handler(mXPCConnection, ^(xpc_object_t object) {
+                       if (xpc_get_type(object) == XPC_TYPE_ERROR) {
+                               secnotice("SecurityAgentXPCConnection", "error during xpc: %s", xpc_dictionary_get_string(object, XPC_ERROR_KEY_DESCRIPTION));
+                       }
+               });
+               xpc_connection_resume(mXPCConnection);
+               secnotice("SecurityAgentXPCConnection", "%p activated", this);
        }
-    catch (MacOSError &err)
-    {
+    catch (MacOSError &err) {
                mConnection->useAgent(NULL);    // guess not
-        Syslog::error("SecurityAgentConnection: error activating %s instance %p",
-                      mAuthHostType == privilegedAuthHost
-                      ? "authorizationhost"
-                      : "SecurityAgent", this);
+        Syslog::error("SecurityAgentConnection: error activating SecurityAgent instance %p", this);
                throw;
        }
-       
-    secdebug("SecurityAgentXPCConnection", "contact didn't throw (%p)", this);
-}
 
-void
-SecurityAgentXPCConnection::reconnect()
-{
+    secnotice("SecurityAgentXPCConnection", "contact didn't throw (%p)", this);
 }
 
 void
 SecurityAgentXPCConnection::terminate()
 {
        activate(false);
-    
+
     // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
        mConnection->useAgent(NULL);
 }
@@ -375,101 +186,6 @@ SecurityAgentXPCConnection::terminate()
 using SecurityAgent::Reason;
 using namespace Authorization;
 
-SecurityAgentQuery::SecurityAgentQuery(const AuthHostType type, Session &session)
-: SecurityAgentConnection(type, session)
-{
-    secdebug("SecurityAgentQuery", "new SecurityAgentQuery(%p)", this);
-}
-
-SecurityAgentQuery::~SecurityAgentQuery()
-{
-    secdebug("SecurityAgentQuery", "SecurityAgentQuery(%p) dying", this);
-    
-#if defined(NOSA)
-       if (getenv("NOSA")) {
-               printf(" [query done]\n");
-               return;
-       }
-#endif
-    
-    if (SecurityAgent::Client::state() != SecurityAgent::Client::dead)
-        destroy();
-}
-
-void
-SecurityAgentQuery::inferHints(Process &thisProcess)
-{
-    string guestPath;
-       {
-               StLock<Mutex> _(thisProcess);
-               if (SecCodeRef clientCode = thisProcess.currentGuest())
-                       guestPath = codePath(clientCode);
-       }
-       AuthItemSet processHints = clientHints(SecurityAgent::bundle, guestPath,
-                                           thisProcess.pid(), thisProcess.uid());
-       mClientHints.insert(processHints.begin(), processHints.end());
-}
-
-void SecurityAgentQuery::addHint(const char *name, const void *value, UInt32 valueLen, UInt32 flags)
-{
-    AuthorizationItem item = { name, valueLen, const_cast<void *>(value), flags };
-    mClientHints.insert(AuthItemRef(item));
-}
-
-
-void
-SecurityAgentQuery::readChoice()
-{
-    allow = false;
-    remember = false;
-    
-       AuthItem *allowAction = outContext().find(AGENT_CONTEXT_ALLOW);
-       if (allowAction)
-       {
-        string allowString;
-               if (allowAction->getString(allowString)
-            && (allowString == "YES"))
-            allow = true;
-       }
-       
-       AuthItem *rememberAction = outContext().find(AGENT_CONTEXT_REMEMBER_ACTION);
-       if (rememberAction)
-       {
-        string rememberString;
-        if (rememberAction->getString(rememberString)
-            && (rememberString == "YES"))
-            remember = true;
-       }
-}
-
-void
-SecurityAgentQuery::disconnect()
-{
-    SecurityAgent::Client::destroy();
-}
-
-void
-SecurityAgentQuery::terminate()
-{
-    // you might think these are called in the wrong order, but you'd be wrong
-    SecurityAgentConnection::terminate();
-       SecurityAgent::Client::terminate();
-}
-
-void
-SecurityAgentQuery::create(const char *pluginId, const char *mechanismId, const SessionId inSessionId)
-{
-       activate();
-       OSStatus status = SecurityAgent::Client::create(pluginId, mechanismId, inSessionId);
-       if (status)
-       {
-               secdebug("SecurityAgentQuery", "agent went walkabout, restarting");
-        reconnect();
-               status = SecurityAgent::Client::create(pluginId, mechanismId, inSessionId);
-       }
-       if (status) MacOSError::throwMe(status);
-}
-
 ModuleNexus<RecursiveMutex> gAllXPCClientsMutex;
 ModuleNexus<set<SecurityAgentXPCQuery*> > allXPCClients;
 
@@ -478,7 +194,7 @@ SecurityAgentXPCQuery::killAllXPCClients()
 {
     // grab the lock for the client list -- we need to make sure no one modifies the structure while we are iterating it.
     StLock<Mutex> _(gAllXPCClientsMutex());
-    
+
     set<SecurityAgentXPCQuery*>::iterator clientIterator = allXPCClients().begin();
     while (clientIterator != allXPCClients().end())
     {
@@ -491,15 +207,15 @@ SecurityAgentXPCQuery::killAllXPCClients()
 }
 
 
-SecurityAgentXPCQuery::SecurityAgentXPCQuery(const AuthHostType type, Session &session)
-: SecurityAgentXPCConnection(type, session), mAgentConnected(false), mTerminateOnSleep(false)
+SecurityAgentXPCQuery::SecurityAgentXPCQuery(Session &session)
+: SecurityAgentXPCConnection(session), mAgentConnected(false), mTerminateOnSleep(false)
 {
-    secdebug("SecurityAgentXPCQuery", "new SecurityAgentXPCQuery(%p)", this);
+    secnotice("SecurityAgentXPCQuery", "new SecurityAgentXPCQuery(%p)", this);
 }
 
 SecurityAgentXPCQuery::~SecurityAgentXPCQuery()
 {
-    secdebug("SecurityAgentXPCQuery", "SecurityAgentXPCQuery(%p) dying", this);
+    secnotice("SecurityAgentXPCQuery", "SecurityAgentXPCQuery(%p) dying", this);
     if (mAgentConnected) {
         this->disconnect();
     }
@@ -508,27 +224,34 @@ SecurityAgentXPCQuery::~SecurityAgentXPCQuery()
 void
 SecurityAgentXPCQuery::inferHints(Process &thisProcess)
 {
-    string guestPath;
-       if (SecCodeRef clientCode = thisProcess.currentGuest())
-               guestPath = codePath(clientCode);
-
     AuthItemSet clientHints;
     SecurityAgent::RequestorType type = SecurityAgent::bundle;
     pid_t clientPid = thisProcess.pid();
     uid_t clientUid = thisProcess.uid();
-    
+    string guestPath = thisProcess.getPath();
+    Boolean ignoreSession = TRUE;
+
        clientHints.insert(AuthItemRef(AGENT_HINT_CLIENT_TYPE, AuthValueOverlay(sizeof(type), &type)));
        clientHints.insert(AuthItemRef(AGENT_HINT_CLIENT_PATH, AuthValueOverlay(guestPath)));
        clientHints.insert(AuthItemRef(AGENT_HINT_CLIENT_PID, AuthValueOverlay(sizeof(clientPid), &clientPid)));
        clientHints.insert(AuthItemRef(AGENT_HINT_CLIENT_UID, AuthValueOverlay(sizeof(clientUid), &clientUid)));
-    
+
+    /*
+     * If its loginwindow that's asking, override the loginwindow shield detection
+     * up front so that it can trigger SecurityAgent dialogs (like password change)
+     * for when the OD password and keychain password is out of sync.
+     */
+
+    if (guestPath == "/System/Library/CoreServices/loginwindow.app") {
+        clientHints.insert(AuthItemRef(AGENT_HINT_IGNORE_SESSION, AuthValueOverlay(sizeof(ignoreSession), &ignoreSession)));
+    }
 
        mClientHints.insert(clientHints.begin(), clientHints.end());
 
     bool validSignature = thisProcess.checkAppleSigned();
     AuthItemSet clientImmutableHints;
-    
-       clientImmutableHints.insert(AuthItemRef(AGENT_HINT_PROCESS_SIGNED, AuthValueOverlay(sizeof(validSignature), &validSignature)));
+
+       clientImmutableHints.insert(AuthItemRef(AGENT_HINT_CLIENT_SIGNED, AuthValueOverlay(sizeof(validSignature), &validSignature)));
 
        mImmutableHints.insert(clientImmutableHints.begin(), clientImmutableHints.end());
 }
@@ -545,7 +268,7 @@ SecurityAgentXPCQuery::readChoice()
 {
     allow = false;
     remember = false;
-    
+
        AuthItem *allowAction = mOutContext.find(AGENT_CONTEXT_ALLOW);
        if (allowAction)
        {
@@ -554,7 +277,7 @@ SecurityAgentXPCQuery::readChoice()
             && (allowString == "YES"))
             allow = true;
        }
-       
+
        AuthItem *rememberAction = mOutContext.find(AGENT_CONTEXT_REMEMBER_ACTION);
        if (rememberAction)
        {
@@ -587,15 +310,31 @@ SecurityAgentXPCQuery::terminate()
 
 static void xpcArrayToAuthItemSet(AuthItemSet *setToBuild, xpc_object_t input) {
     setToBuild->clear();
-    
+
     xpc_array_apply(input,  ^bool(size_t index, xpc_object_t item) {
         const char *name = xpc_dictionary_get_string(item, AUTH_XPC_ITEM_NAME);
-        
+
         size_t length;
         const void *data = xpc_dictionary_get_data(item, AUTH_XPC_ITEM_VALUE, &length);
-        void *dataCopy = malloc(length);
-        memcpy(dataCopy, data, length);
-        
+        void *dataCopy = 0;
+
+        // <rdar://problem/13033889> authd is holding on to multiple copies of my password in the clear
+        bool sensitive = xpc_dictionary_get_value(item, AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH);
+        if (sensitive) {
+            size_t sensitiveLength = (size_t)xpc_dictionary_get_uint64(item, AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH);
+            if (sensitiveLength > length) {
+                secnotice("SecurityAgentXPCQuery", "Sensitive data len %zu is not valid", sensitiveLength);
+                return true;
+            }
+            dataCopy = malloc(sensitiveLength);
+            memcpy(dataCopy, data, sensitiveLength);
+            memset_s((void *)data, length, 0, sensitiveLength); // clear the sensitive data, memset_s is never optimized away
+            length = sensitiveLength;
+        } else {
+            dataCopy = malloc(length);
+            memcpy(dataCopy, data, length);
+        }
+
         uint64_t flags = xpc_dictionary_get_uint64(item, AUTH_XPC_ITEM_FLAGS);
         AuthItemRef nextItem(name, AuthValueOverlay((uint32_t)length, dataCopy), (uint32_t)flags);
         setToBuild->insert(nextItem);
@@ -606,41 +345,41 @@ static void xpcArrayToAuthItemSet(AuthItemSet *setToBuild, xpc_object_t input) {
 }
 
 void
-SecurityAgentXPCQuery::create(const char *pluginId, const char *mechanismId, const SessionId inSessionId)
+SecurityAgentXPCQuery::create(const char *pluginId, const char *mechanismId)
 {
     bool ignoreUid = false;
-    
+
     do {
         activate(ignoreUid);
-        
+
         mAgentConnected = false;
-        
+
         xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
         xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_CREATE);
         xpc_dictionary_set_string(requestObject, AUTH_XPC_PLUGIN_NAME, pluginId);
         xpc_dictionary_set_string(requestObject, AUTH_XPC_MECHANISM_NAME, mechanismId);
-        
+
         uid_t targetUid = Server::process().uid();
-        bool doSwitchAudit =  true; // (ignoreUid) || ((targetUid == 0) || (targetUid == mNobodyUID));
-        bool doSwitchBootstrap = true; // (ignoreUid) || ((targetUid == 0) || (targetUid == mNobodyUID));
-        
+               bool doSwitchAudit     = (ignoreUid || targetUid == 0 || targetUid == mNobodyUID);
+               bool doSwitchBootstrap = (ignoreUid || targetUid == 0 || targetUid == mNobodyUID);
+
         if (doSwitchAudit) {
             mach_port_name_t jobPort;
             if (0 == audit_session_port(mSession.sessionId(), &jobPort)) {
-                secdebug("SecurityAgentXPCQuery", "attaching an audit session port because the uid was %d", targetUid);
+                secnotice("SecurityAgentXPCQuery", "attaching an audit session port because the uid was %d", targetUid);
                 xpc_dictionary_set_mach_send(requestObject, AUTH_XPC_AUDIT_SESSION_PORT, jobPort);
                 if (mach_port_mod_refs(mach_task_self(), jobPort, MACH_PORT_RIGHT_SEND, -1) != KERN_SUCCESS) {
-                    secdebug("SecurityAgentXPCQuery", "unable to release send right for audit session, leaking");
+                    secnotice("SecurityAgentXPCQuery", "unable to release send right for audit session, leaking");
                 }
             }
         }
-        
+
         if (doSwitchBootstrap) {
-            secdebug("SecurityAgentXPCQuery", "attaching a bootstrap port because the uid was %d", targetUid);
+            secnotice("SecurityAgentXPCQuery", "attaching a bootstrap port because the uid was %d", targetUid);
             MachPlusPlus::Bootstrap processBootstrap = Server::process().taskPort().bootstrap();
             xpc_dictionary_set_mach_send(requestObject, AUTH_XPC_BOOTSTRAP_PORT, processBootstrap);
         }
-        
+
         xpc_object_t object = xpc_connection_send_message_with_reply_sync(mXPCConnection, requestObject);
         if (xpc_get_type(object) == XPC_TYPE_DICTIONARY) {
             const char *replyType = xpc_dictionary_get_string(object, AUTH_XPC_REPLY_METHOD_KEY);
@@ -649,7 +388,7 @@ SecurityAgentXPCQuery::create(const char *pluginId, const char *mechanismId, con
                 if (status == kAuthorizationResultAllow) {
                     mAgentConnected = true;
                 } else {
-                    secdebug("SecurityAgentXPCQuery", "plugin create failed in SecurityAgent");
+                    secnotice("SecurityAgentXPCQuery", "plugin create failed in SecurityAgent");
                     MacOSError::throwMe(errAuthorizationInternal);
                 }
             }
@@ -657,11 +396,11 @@ SecurityAgentXPCQuery::create(const char *pluginId, const char *mechanismId, con
             if (XPC_ERROR_CONNECTION_INVALID == object) {
                 // If we get an error before getting the create response, try again without the UID
                 if (ignoreUid) {
-                    secdebug("SecurityAgentXPCQuery", "failed to establish connection, no retries left");
+                    secnotice("SecurityAgentXPCQuery", "failed to establish connection, no retries left");
                     xpc_release(object);
                     MacOSError::throwMe(errAuthorizationInternal);
                 } else {
-                    secdebug("SecurityAgentXPCQuery", "failed to establish connection, retrying with no UID");
+                    secnotice("SecurityAgentXPCQuery", "failed to establish connection, retrying with no UID");
                     ignoreUid = true;
                     xpc_release(mXPCConnection);
                     mXPCConnection = NULL;
@@ -682,7 +421,7 @@ static xpc_object_t authItemSetToXPCArray(AuthItemSet input) {
     xpc_object_t outputArray = xpc_array_create(NULL, 0);
     for (AuthItemSet::iterator i = input.begin(); i != input.end(); i++) {
         AuthItemRef item = *i;
-        
+
         xpc_object_t xpc_data = xpc_dictionary_create(NULL, NULL, 0);
         xpc_dictionary_set_string(xpc_data, AUTH_XPC_ITEM_NAME, item->name());
         AuthorizationValue value = item->value();
@@ -696,20 +435,18 @@ static xpc_object_t authItemSetToXPCArray(AuthItemSet input) {
     return outputArray;
 }
 
-OSStatus
+void
 SecurityAgentXPCQuery::invoke() {
-    __block OSStatus status = kAuthorizationResultUndefined;
-    
     xpc_object_t hintsArray = authItemSetToXPCArray(mInHints);
     xpc_object_t contextArray = authItemSetToXPCArray(mInContext);
     xpc_object_t immutableHintsArray = authItemSetToXPCArray(mImmutableHints);
-    
+
     xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
     xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_INVOKE);
     xpc_dictionary_set_value(requestObject, AUTH_XPC_HINTS_NAME, hintsArray);
     xpc_dictionary_set_value(requestObject, AUTH_XPC_CONTEXT_NAME, contextArray);
     xpc_dictionary_set_value(requestObject, AUTH_XPC_IMMUTABLE_HINTS_NAME, immutableHintsArray);
-    
+
     xpc_object_t object = xpc_connection_send_message_with_reply_sync(mXPCConnection, requestObject);
     if (xpc_get_type(object) == XPC_TYPE_DICTIONARY) {
         const char *replyType = xpc_dictionary_get_string(object, AUTH_XPC_REPLY_METHOD_KEY);
@@ -731,13 +468,11 @@ SecurityAgentXPCQuery::invoke() {
         }
     }
     xpc_release(object);
-    
+
     xpc_release(hintsArray);
     xpc_release(contextArray);
     xpc_release(immutableHintsArray);
     xpc_release(requestObject);
-    
-    return status;
 }
 
 void SecurityAgentXPCQuery::checkResult()
@@ -759,153 +494,106 @@ QueryKeychainUse::QueryKeychainUse(bool needPass, const Database *db)
 {
        // if passphrase checking requested, save KeychainDatabase reference
        // (will quietly disable check if db isn't a keychain)
-       if (needPass)
-               mPassphraseCheck = dynamic_cast<const KeychainDatabase *>(db);
-    
+
+    // Always require password due to <rdar://problem/34677969>
+    mPassphraseCheck = dynamic_cast<const KeychainDatabase *>(db);
+
     setTerminateOnSleep(true);
 }
 
+// Callers to this function must hold the common lock
 Reason QueryKeychainUse::queryUser (const char *database, const char *description, AclAuthorization action)
 {
     Reason reason = SecurityAgent::noReason;
-    int retryCount = 0;
-       OSStatus status;
-       AuthValueVector arguments;
+       uint32_t retryCount = 0;
        AuthItemSet hints, context;
 
-#if defined(NOSA)
-       if (getenv("NOSA")) {
-               char answer[maxPassphraseLength+10];
-               
-        string applicationPath;
-        AuthItem *applicationPathItem = mClientHints.find(AGENT_HINT_APPLICATION_PATH);
-               if (applicationPathItem)
-                 applicationPathItem->getString(applicationPath);
-
-               getNoSA(answer, sizeof(answer), "Allow %s to do %d on %s in %s? [yn][g]%s ",
-                       applicationPath.c_str(), int(action), (description ? description : "[NULL item]"),
-                       (database ? database : "[NULL database]"),
-                       mPassphraseCheck ? ":passphrase" : "");
-               // turn passphrase (no ':') into y:passphrase
-               if (mPassphraseCheck && !strchr(answer, ':')) {
-                       memmove(answer+2, answer, strlen(answer)+1);
-                       memcpy(answer, "y:", 2);
-               }
-
-               allow = answer[0] == 'y';
-               remember = answer[1] == 'g';
-               return SecurityAgent::noReason;
-       }
-#endif
-
        // prepopulate with client hints
        hints.insert(mClientHints.begin(), mClientHints.end());
-       
+
        // put action/operation (sint32) into hints
        hints.insert(AuthItemRef(AGENT_HINT_ACL_TAG, AuthValueOverlay(sizeof(action), static_cast<sint32*>(&action))));
-       
+
        // item name into hints
 
-       hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME, AuthValueOverlay(description ? (uint32_t)strlen(description) : 0, const_cast<char*>(description))));
-       
+    hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME, AuthValueOverlay(description ? (uint32_t)strlen(description) : 0, const_cast<char*>(description))));
+
        // keychain name into hints
        hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(database ? (uint32_t)strlen(database) : 0, const_cast<char*>(database))));
-       
+
        if (mPassphraseCheck)
        {
-               create("builtin", "confirm-access-password", noSecuritySession);
-               
+               create("builtin", "confirm-access-password");
+
                CssmAutoData data(Allocator::standard(Allocator::sensitive));
 
-               do 
+               do
                {
 
             AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
             hints.erase(triesHint); hints.insert(triesHint); // replace
-            
+
             if (retryCount++ > kMaximumAuthorizationTries)
                        {
                 reason = SecurityAgent::tooManyTries;
                        }
-                               
+
             AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
             hints.erase(retryHint); hints.insert(retryHint); // replace
 
             setInput(hints, context);
-                       status = invoke();
+
+            {
+                // Must drop the common lock while showing UI.
+                StSyncLock<Mutex, Mutex> syncLock(const_cast<KeychainDatabase*>(mPassphraseCheck)->common().uiLock(), const_cast<KeychainDatabase*>(mPassphraseCheck)->common());
+                invoke();
+            }
 
             if (retryCount > kMaximumAuthorizationTries)
                        {
                 return reason;
                        }
-                       
+
                        checkResult();
-                       
+
                        AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
                        if (!passwordItem)
                                continue;
-                                               
+
                        passwordItem->getCssmData(data);
-               } 
-               while ((reason = (const_cast<KeychainDatabase*>(mPassphraseCheck)->decode(data) ? SecurityAgent::noReason : SecurityAgent::invalidPassphrase)));
+
+            // decode() replaces the master key, so do this only if we know the passphrase is correct.
+            // I suspect decode() is redundant but something might rely on its side effects so let's keep it.
+            if (const_cast<KeychainDatabase*>(mPassphraseCheck)->validatePassphrase(data) && const_cast<KeychainDatabase*>(mPassphraseCheck)->decode(data)) {
+                reason = SecurityAgent::noReason;
+            } else {
+                reason = SecurityAgent::invalidPassphrase;
+            }
+               }
+        while (reason != SecurityAgent::noReason);
+        
+        readChoice();
        }
        else
        {
-               create("builtin", "confirm-access", noSecuritySession);
-        setInput(hints, context);
-               invoke();
-       }
-       
-    readChoice();
-    
-       return reason;
-}
-
-//
-// Perform code signature ACL access adjustment dialogs
-//
-bool QueryCodeCheck::operator () (const char *aclPath)
-{
-       OSStatus status;
-       AuthValueVector arguments;
-       AuthItemSet hints, context;
-       
-#if defined(NOSA)
-       if (getenv("NOSA")) {
-               char answer[10];
-               
-        string applicationPath;
-        AuthItem *applicationPathItem = mClientHints.find(AGENT_HINT_APPLICATION_PATH);
-               if (applicationPathItem)
-                 applicationPathItem->getString(applicationPath);
-
-               getNoSA(answer, sizeof(answer),
-                               "Allow %s to match an ACL for %s [yn][g]? ",
-                               applicationPath.c_str(), aclPath ? aclPath : "(unknown)");
-               allow = answer[0] == 'y';
-               remember = answer[1] == 'g';
-               return;
+//        create("builtin", "confirm-access");
+//        setInput(hints, context);
+//        invoke();
+        
+        // This is a hack to support <rdar://problem/34677969>, we can never simply prompt for confirmation
+        secerror("ACL validation fallback case! Must ask user for account password because we have no database");
+        Session &session = Server::session();
+        try{
+            session.verifyKeyStorePassphrase(1, true, description);
+        } catch (...) {
+            return SecurityAgent::invalidPassphrase;
+        }
+        SecurityAgentXPCQuery::allow = true;
        }
-#endif
-
-       // prepopulate with client hints
-       hints.insert(mClientHints.begin(), mClientHints.end());
-       
-       hints.insert(AuthItemRef(AGENT_HINT_APPLICATION_PATH, AuthValueOverlay((uint32_t)strlen(aclPath), const_cast<char*>(aclPath))));
-    
-       create("builtin", "code-identity", noSecuritySession);
 
-    setInput(hints, context);
-       status = invoke();
-               
-    checkResult();
-
-//     MacOSError::check(status);
-
-    return kAuthorizationResultAllow == mLastResult;
+       return reason;
 }
 
-
 //
 // Obtain passphrases and submit them to the accept() method until it is accepted
 // or we can't get another passphrase. Accept() should consume the passphrase
@@ -914,48 +602,36 @@ bool QueryCodeCheck::operator () (const char *aclPath)
 Reason QueryOld::query()
 {
        Reason reason = SecurityAgent::noReason;
-       OSStatus status;
-       AuthValueVector arguments;
        AuthItemSet hints, context;
        CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
        int retryCount = 0;
-       
-#if defined(NOSA)
-    // return the passphrase
-       if (getenv("NOSA")) {
-        char passphrase_[maxPassphraseLength];
-               getNoSA(passphrase, maxPassphraseLength, "Unlock %s [<CR> to cancel]: ", database.dbName());
-       passphrase.copy(passphrase_, strlen(passphrase_));
-       return database.decode(passphrase) ? SecurityAgent::noReason : SecurityAgent::invalidPassphrase;
-       }
-#endif
-       
+
        // prepopulate with client hints
 
     const char *keychainPath = database.dbName();
     hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay((uint32_t)strlen(keychainPath), const_cast<char*>(keychainPath))));
 
        hints.insert(mClientHints.begin(), mClientHints.end());
-    
-       create("builtin", "unlock-keychain", noSecuritySession);
+
+       create("builtin", "unlock-keychain");
 
        do
        {
         AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
         hints.erase(triesHint); hints.insert(triesHint); // replace
-               
+
         ++retryCount;
-            
+
         if (retryCount > maxTries)
                {
                        reason = SecurityAgent::tooManyTries;
         }
-            
+
         AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
         hints.erase(retryHint); hints.insert(retryHint); // replace
 
         setInput(hints, context);
-        status = invoke();
+        invoke();
 
         if (retryCount > maxTries)
         {
@@ -963,13 +639,13 @@ Reason QueryOld::query()
                }
 
         checkResult();
-               
+
                AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
                if (!passwordItem)
                        continue;
-               
+
                passwordItem->getCssmData(passphrase);
-               
+
        }
        while ((reason = accept(passphrase)));
 
@@ -991,6 +667,11 @@ Reason QueryOld::operator () ()
 //
 Reason QueryUnlock::accept(CssmManagedData &passphrase)
 {
+    // Must hold the 'common' lock to call decode; otherwise there's a data corruption issue
+    StLock<Mutex> _(safer_cast<KeychainDatabase &>(database).common());
+
+    // Calling validatePassphrase here throws when trying to constitute a key.
+    // Unsure why but since this is for the KC unlock path and not a validation path the wrong password won't make things worse.
        if (safer_cast<KeychainDatabase &>(database).decode(passphrase))
                return SecurityAgent::noReason;
        else
@@ -1003,9 +684,9 @@ Reason QueryUnlock::retrievePassword(CssmOwnedData &passphrase) {
     AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
     if (!passwordItem)
         return SecurityAgent::invalidPassphrase;
-    
+
     passwordItem->getCssmData(pass);
-    
+
     passphrase = pass;
 
    return SecurityAgent::noReason;
@@ -1020,8 +701,6 @@ QueryKeybagPassphrase::QueryKeybagPassphrase(Session & session, int32_t tries) :
 Reason QueryKeybagPassphrase::query()
 {
        Reason reason = SecurityAgent::noReason;
-       OSStatus status;
-       AuthValueVector arguments;
        AuthItemSet hints, context;
        CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
        int retryCount = 0;
@@ -1033,23 +712,26 @@ Reason QueryKeybagPassphrase::query()
 
        hints.insert(mClientHints.begin(), mClientHints.end());
 
-       create("builtin", "unlock-keychain", noSecuritySession);
+       create("builtin", "unlock-keychain");
 
+    int currentTry = 0;
        do
        {
+        currentTry = retryCount;
         if (retryCount > mRetries)
                {
                        return SecurityAgent::tooManyTries;
         }
+        retryCount++;
 
-        AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
+        AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(currentTry), &currentTry));
         hints.erase(triesHint); hints.insert(triesHint); // replace
 
         AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
         hints.erase(retryHint); hints.insert(retryHint); // replace
 
         setInput(hints, context);
-        status = invoke();
+        invoke();
 
         checkResult();
 
@@ -1058,8 +740,6 @@ Reason QueryKeybagPassphrase::query()
                        continue;
 
                passwordItem->getCssmData(passphrase);
-
-        ++retryCount;
        }
        while ((reason = accept(passphrase)));
 
@@ -1082,8 +762,6 @@ Reason QueryKeybagNewPassphrase::query(CssmOwnedData &oldPassphrase, CssmOwnedDa
     CssmAutoData pass(Allocator::standard(Allocator::sensitive));
     CssmAutoData oldPass(Allocator::standard(Allocator::sensitive));
     Reason reason = SecurityAgent::noReason;
-       OSStatus status;
-       AuthValueVector arguments;
        AuthItemSet hints, context;
        int retryCount = 0;
 
@@ -1097,24 +775,27 @@ Reason QueryKeybagNewPassphrase::query(CssmOwnedData &oldPassphrase, CssmOwnedDa
 
        hints.insert(mClientHints.begin(), mClientHints.end());
 
-       create("builtin", "change-passphrase", noSecuritySession);
+       create("builtin", "change-passphrase");
 
+    int currentTry = 0;
     AuthItem *resetPassword = NULL;
        do
        {
+        currentTry = retryCount;
         if (retryCount > mRetries)
                {
                        return SecurityAgent::tooManyTries;
         }
+        retryCount++;
 
-        AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
+        AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(currentTry), &currentTry));
         hints.erase(triesHint); hints.insert(triesHint); // replace
 
         AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
         hints.erase(retryHint); hints.insert(retryHint); // replace
 
         setInput(hints, context);
-        status = invoke();
+        invoke();
 
         checkResult();
 
@@ -1122,14 +803,12 @@ Reason QueryKeybagNewPassphrase::query(CssmOwnedData &oldPassphrase, CssmOwnedDa
         if (resetPassword != NULL) {
             return SecurityAgent::resettingPassword;
         }
-        
+
         AuthItem *oldPasswordItem = mOutContext.find(AGENT_PASSWORD);
         if (!oldPasswordItem)
             continue;
 
         oldPasswordItem->getCssmData(oldPass);
-
-        ++retryCount;
        }
        while ((reason = accept(oldPass)));
 
@@ -1173,35 +852,23 @@ Reason QueryNewPassphrase::query()
        CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
        CssmAutoData oldPassphrase(Allocator::standard(Allocator::sensitive));
 
-    OSStatus status;
-       AuthValueVector arguments;
        AuthItemSet hints, context;
-    
-       int retryCount = 0;
 
-#if defined(NOSA)
-       if (getenv("NOSA")) {
-        char passphrase_[maxPassphraseLength];
-               getNoSA(passphrase_, maxPassphraseLength,
-                       "New passphrase for %s (reason %d) [<CR> to cancel]: ",
-                       database.dbName(), reason);
-               return SecurityAgent::noReason;
-       }
-#endif
+       int retryCount = 0;
 
        // prepopulate with client hints
        hints.insert(mClientHints.begin(), mClientHints.end());
 
        // keychain name into hints
        hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(database.dbName())));
-    
+
     switch (initialReason)
     {
-        case SecurityAgent::newDatabase: 
-            create("builtin", "new-passphrase", noSecuritySession);
+        case SecurityAgent::newDatabase:
+            create("builtin", "new-passphrase");
             break;
         case SecurityAgent::changePassphrase:
-            create("builtin", "change-passphrase", noSecuritySession);
+            create("builtin", "change-passphrase");
             break;
         default:
             assert(false);
@@ -1221,13 +888,13 @@ Reason QueryNewPassphrase::query()
         hints.erase(retryHint); hints.insert(retryHint); // replace
 
         setInput(hints, context);
-               status = invoke();
+               invoke();
 
                if (retryCount > maxTries)
                {
             return reason;
         }
-        
+
         checkResult();
 
                if (SecurityAgent::changePassphrase == initialReason)
@@ -1235,19 +902,19 @@ Reason QueryNewPassphrase::query()
             AuthItem *oldPasswordItem = mOutContext.find(AGENT_PASSWORD);
             if (!oldPasswordItem)
                 continue;
-            
+
             oldPasswordItem->getCssmData(oldPassphrase);
         }
-        
+
                AuthItem *passwordItem = mOutContext.find(AGENT_CONTEXT_NEW_PASSWORD);
                if (!passwordItem)
                        continue;
-               
+
                passwordItem->getCssmData(passphrase);
 
     }
        while ((reason = accept(passphrase, (initialReason == SecurityAgent::changePassphrase) ? &oldPassphrase.get() : NULL)));
-    
+
        return SecurityAgent::noReason;
 }
 
@@ -1268,11 +935,11 @@ Reason QueryNewPassphrase::accept(CssmManagedData &passphrase, CssmData *oldPass
 {
        //@@@ acceptance criteria are currently hardwired here
        //@@@ This validation presumes ASCII - UTF8 might be more lenient
-       
+
        // if we have an old passphrase, check it
        if (oldPassphrase && !safer_cast<KeychainDatabase&>(database).validatePassphrase(*oldPassphrase))
                return SecurityAgent::oldPassphraseWrong;
-       
+
        // sanity check the new passphrase (but allow user override)
        if (!(mPassphraseValid && passphrase.get() == mPassphrase)) {
                mPassphrase = passphrase;
@@ -1283,14 +950,14 @@ Reason QueryNewPassphrase::accept(CssmManagedData &passphrase, CssmData *oldPass
                if (mPassphrase.length() < 6)
                        return SecurityAgent::passphraseTooSimple;
        }
-       
+
        // accept this
        return SecurityAgent::noReason;
 }
 
-// 
+//
 // Get a passphrase for unspecified use
-// 
+//
 Reason QueryGenericPassphrase::operator () (const CssmData *prompt, bool verify,
                                             string &passphrase)
 {
@@ -1301,50 +968,39 @@ Reason QueryGenericPassphrase::query(const CssmData *prompt, bool verify,
                                      string &passphrase)
 {
     Reason reason = SecurityAgent::noReason;
-    OSStatus status;    // not really used; remove?  
-    AuthValueVector arguments;
     AuthItemSet hints, context;
-       
-#if defined(NOSA)
-    if (getenv("NOSA")) {
-               // FIXME  3690984
-               return SecurityAgent::noReason;
-    }
-#endif
-       
+
     hints.insert(mClientHints.begin(), mClientHints.end());
     hints.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT, AuthValueOverlay(prompt ? (UInt32)prompt->length() : 0, prompt ? prompt->data() : NULL)));
     // XXX/gh  defined by dmitch but no analogous hint in
     // AuthorizationTagsPriv.h:
     // CSSM_ATTRIBUTE_ALERT_TITLE (optional alert panel title)
-       
+
     if (false == verify) {  // import
-               create("builtin", "generic-unlock", noSecuritySession);
+               create("builtin", "generic-unlock");
     } else {           // verify passphrase (export)
-                                       // new-passphrase-generic works with the pre-4 June 2004 agent; 
-                                       // generic-new-passphrase is required for the new agent
-               create("builtin", "generic-new-passphrase", noSecuritySession);
+               create("builtin", "generic-new-passphrase");
     }
-    
+
     AuthItem *passwordItem;
-    
+
     do {
         setInput(hints, context);
-               status = invoke();
+               invoke();
                checkResult();
                passwordItem = mOutContext.find(AGENT_PASSWORD);
-               
+
     } while (!passwordItem);
-       
+
     passwordItem->getString(passphrase);
-    
+
     return reason;
 }
 
 
-// 
+//
 // Get a DB blob's passphrase--keychain synchronization
-// 
+//
 Reason QueryDBBlobSecret::operator () (DbHandle *dbHandleArray, uint8 dbHandleArrayCount, DbHandle *dbHandleAuthenticated)
 {
     return query(dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated);
@@ -1354,24 +1010,15 @@ Reason QueryDBBlobSecret::query(DbHandle *dbHandleArray, uint8 dbHandleArrayCoun
 {
     Reason reason = SecurityAgent::noReason;
        CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
-    OSStatus status;    // not really used; remove?  
-    AuthValueVector arguments;
     AuthItemSet hints/*NUKEME*/, context;
-       
-#if defined(NOSA)
-    if (getenv("NOSA")) {
-               // FIXME  akin to 3690984
-               return SecurityAgent::noReason;
-    }
-#endif
 
        hints.insert(mClientHints.begin(), mClientHints.end());
-       create("builtin", "generic-unlock-kcblob", noSecuritySession);
-    
+       create("builtin", "generic-unlock-kcblob");
+
     AuthItem *secretItem;
-    
+
        int retryCount = 0;
-       
+
     do {
         AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
         hints.erase(triesHint); hints.insert(triesHint); // replace
@@ -1379,25 +1026,25 @@ Reason QueryDBBlobSecret::query(DbHandle *dbHandleArray, uint8 dbHandleArrayCoun
                if (++retryCount > maxTries)
                {
                        reason = SecurityAgent::tooManyTries;
-               }               
-               
+               }
+
         AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
         hints.erase(retryHint); hints.insert(retryHint); // replace
-               
+
         setInput(hints, context);
-               status = invoke();
+               invoke();
                checkResult();
                secretItem = mOutContext.find(AGENT_PASSWORD);
                if (!secretItem)
                        continue;
                secretItem->getCssmData(passphrase);
-               
+
     } while ((reason = accept(passphrase, dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated)));
-           
+
     return reason;
 }
 
-Reason QueryDBBlobSecret::accept(CssmManagedData &passphrase, 
+Reason QueryDBBlobSecret::accept(CssmManagedData &passphrase,
                                                                 DbHandle *dbHandlesToAuthenticate, uint8 dbHandleCount, DbHandle *dbHandleAuthenticated)
 {
        DbHandle *currHdl = dbHandlesToAuthenticate;
@@ -1405,126 +1052,64 @@ Reason QueryDBBlobSecret::accept(CssmManagedData &passphrase,
        Boolean authenticated = false;
        for (index=0; index < dbHandleCount && !authenticated; index++)
        {
-               try 
+               try
                {
                        RefPointer<KeychainDatabase> dbToUnlock = Server::keychain(*currHdl);
-                       dbToUnlock->unlockDb(passphrase);
+                       dbToUnlock->unlockDb(passphrase, false);
                        authenticated = true;
                        *dbHandleAuthenticated = *currHdl; // return the DbHandle that 'passphrase' authenticated with.
-               } 
-               catch (const CommonError &err) 
+               }
+               catch (const CommonError &err)
                {
-                       currHdl++; // we failed to authenticate with this one, onto the next one.  
+                       currHdl++; // we failed to authenticate with this one, onto the next one.
                }
        }
        if ( !authenticated )
                return SecurityAgent::invalidPassphrase;
-       
-       return SecurityAgent::noReason;
-}
-
-QueryInvokeMechanism::QueryInvokeMechanism(const AuthHostType type, Session &session) :
-    SecurityAgentQuery(type, session) { }
-
-void QueryInvokeMechanism::initialize(const string &inPluginId, const string &inMechanismId, const AuthValueVector &inArguments, const SessionId inSessionId)
-{
-    if (SecurityAgent::Client::init == SecurityAgent::Client::state())
-    {
-        create(inPluginId.c_str(), inMechanismId.c_str(), inSessionId);
-        mArguments = inArguments;
-    }
-}
-
-// XXX/cs should return AuthorizationResult
-void QueryInvokeMechanism::run(const AuthValueVector &inArguments, AuthItemSet &inHints, AuthItemSet &inContext, AuthorizationResult *outResult)
-{
-    // prepopulate with client hints
-       inHints.insert(mClientHints.begin(), mClientHints.end());
-
-       if (Server::active().inDarkWake())
-               CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE);
-
-    setArguments(inArguments);
-    setInput(inHints, inContext);
-    MacOSError::check(invoke());
-    
-       if (outResult) *outResult = result();
-    
-    inHints = outHints();
-    inContext = outContext();
-}
 
-void QueryInvokeMechanism::terminateAgent()
-{
-    terminate();
+       return SecurityAgent::noReason;
 }
 
-// @@@  no pluggable authentication possible!  
+// @@@  no pluggable authentication possible!
 Reason
-QueryKeychainAuth::operator () (const char *database, const char *description, AclAuthorization action, const char *prompt)
+QueryKeychainAuth::performQuery(const KeychainDatabase& db, const char *description, AclAuthorization action, const char *prompt)
 {
     Reason reason = SecurityAgent::noReason;
     AuthItemSet hints, context;
-       AuthValueVector arguments;
        int retryCount = 0;
        string username;
        string password;
-    
+
     using CommonCriteria::Securityd::KeychainAuthLogger;
-    KeychainAuthLogger logger(mAuditToken, AUE_ssauthint, database, description);
-       
-#if defined(NOSA)
-    /* XXX/gh  probably not complete; stolen verbatim from rogue-app query */
-    if (getenv("NOSA")) {
-               char answer[maxPassphraseLength+10];
-               
-        string applicationPath;
-        AuthItem *applicationPathItem = mClientHints.find(AGENT_HINT_APPLICATION_PATH);
-               if (applicationPathItem)
-                 applicationPathItem->getString(applicationPath);
-
-               getNoSA(answer, sizeof(answer), "Allow %s to do %d on %s in %s? [yn][g]%s ",
-                       applicationPath.c_str(), int(action), (description ? description : "[NULL item]"),
-                       (database ? database : "[NULL database]"),
-                       mPassphraseCheck ? ":passphrase" : "");
-               // turn passphrase (no ':') into y:passphrase
-               if (mPassphraseCheck && !strchr(answer, ':')) {
-                       memmove(answer+2, answer, strlen(answer)+1);
-                       memcpy(answer, "y:", 2);
-               }
+    KeychainAuthLogger logger(mAuditToken, (short)AUE_ssauthint, db.dbName(), description);
 
-               allow = answer[0] == 'y';
-               remember = answer[1] == 'g';
-               return SecurityAgent::noReason;
-    }
-#endif
-       
     hints.insert(mClientHints.begin(), mClientHints.end());
 
        // put action/operation (sint32) into hints
        hints.insert(AuthItemRef(AGENT_HINT_ACL_TAG, AuthValueOverlay(sizeof(action), static_cast<sint32*>(&action))));
 
     hints.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT, AuthValueOverlay(prompt ? (uint32_t)strlen(prompt) : 0, const_cast<char*>(prompt))));
-       
+
        // item name into hints
        hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME, AuthValueOverlay(description ? (uint32_t)strlen(description) : 0, const_cast<char*>(description))));
-       
+
        // keychain name into hints
-       hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(database ? (uint32_t)strlen(database) : 0, const_cast<char*>(database))));
-       
-    create("builtin", "confirm-access-user-password", noSecuritySession);
-    
+       hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(db.dbName() ? (uint32_t)strlen(db.dbName()) : 0, const_cast<char*>(db.dbName()))));
+
+    create("builtin", "confirm-access-user-password");
+
     AuthItem *usernameItem;
     AuthItem *passwordItem;
-    
-    do {
 
+    // This entire do..while requires the UI lock because we do accept() in the condition, which in some cases is reentrant
+    StSyncLock<Mutex, Mutex> syncLock(db.common().uiLock(), db.common());
+    do {
         AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
         hints.erase(triesHint); hints.insert(triesHint); // replace
-        
+
                if (++retryCount > maxTries)
                        reason = SecurityAgent::tooManyTries;
-               
+
         if (SecurityAgent::noReason != reason)
         {
             if (SecurityAgent::tooManyTries == reason)
@@ -1535,7 +1120,7 @@ QueryKeychainAuth::operator () (const char *database, const char *description, A
 
         AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
         hints.erase(retryHint); hints.insert(retryHint); // replace
-               
+
         setInput(hints, context);
         try
         {
@@ -1554,15 +1139,16 @@ QueryKeychainAuth::operator () (const char *database, const char *description, A
         usernameItem->getString(username);
         passwordItem->getString(password);
     } while ((reason = accept(username, password)));
+    syncLock.unlock();
 
     if (SecurityAgent::noReason == reason)
         logger.logSuccess();
     // else we logged the denial in the loop
-    
+
     return reason;
 }
 
-Reason 
+Reason
 QueryKeychainAuth::accept(string &username, string &passphrase)
 {
        // Note: QueryKeychainAuth currently requires that the