/ securityd / src / agentquery.cpp
agentquery.cpp
   1  /*
   2   * Copyright (c) 2000-2015 Apple Inc. All Rights Reserved.
   3   *
   4   * @APPLE_LICENSE_HEADER_START@
   5   *
   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
  11   * file.
  12   *
  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.
  20   *
  21   * @APPLE_LICENSE_HEADER_END@
  22   */
  23  
  24  //
  25  // passphrases - canonical code to obtain passphrases
  26  //
  27  #define __STDC_WANT_LIB_EXT1__ 1
  28  #include <string.h>
  29  
  30  #include "agentquery.h"
  31  #include "ccaudit_extensions.h"
  32  
  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>
  44  #include <stdlib.h>
  45  #include <xpc/xpc.h>
  46  #include <xpc/private.h>
  47  #include "securityd_service/securityd_service/securityd_service_client.h"
  48  
  49  // Includes for <rdar://problem/34677969> Always require the user's password on keychain approval dialogs
  50  #include "server.h"
  51  
  52  #define SECURITYAGENT_BOOTSTRAP_NAME_BASE               "com.apple.security.agent"
  53  #define SECURITYAGENT_LOGINWINDOW_BOOTSTRAP_NAME_BASE   "com.apple.security.agent.login"
  54  
  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"
  60  
  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"
  80  
  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))) }
  83  
  84  
  85  // SecurityAgentXPCConnection
  86  
  87  SecurityAgentXPCConnection::SecurityAgentXPCConnection(Session &session)
  88  : mHostInstance(session.authhost()),
  89  mSession(session),
  90  mConnection(&Server::connection()),
  91  mAuditToken(Server::connection().auditToken())
  92  {
  93  	// this may take a while
  94  	Server::active().longTermActivity();
  95      secnotice("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
  96      mXPCConnection = NULL;
  97      mNobodyUID = -2;
  98      struct passwd *pw = getpwnam("nobody");
  99      if (NULL != pw) {
 100          mNobodyUID = pw->pw_uid;
 101      }
 102  }
 103  
 104  SecurityAgentXPCConnection::~SecurityAgentXPCConnection()
 105  {
 106      secnotice("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this);
 107  	mConnection->useAgent(NULL);
 108  
 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);
 114  
 115          // Then release the XPC connection
 116          xpc_release(mXPCConnection);
 117          mXPCConnection = NULL;
 118      }
 119  }
 120  
 121  bool SecurityAgentXPCConnection::inDarkWake()
 122  {
 123  	return mSession.server().inDarkWake();
 124  }
 125  
 126  void
 127  SecurityAgentXPCConnection::activate(bool ignoreUid)
 128  {
 129      secnotice("SecurityAgentConnection", "activate(%p)", this);
 130  
 131  	mConnection->useAgent(this);
 132      if (mXPCConnection != NULL) {
 133          // If we already have an XPC connection, there's nothing to do.
 134          return;
 135      }
 136  
 137  	try {
 138  		uuid_t sessionUUID = UUID_INITIALIZER_FROM_SESSIONID(mSession.sessionId());
 139  
 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);
 143  		if (inDarkWake())
 144  			CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE);
 145  		uid_t targetUid = mHostInstance->session().originatorUid();
 146  
 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");
 152  		} else {
 153  			mXPCConnection = xpc_connection_create_mach_service(SECURITYAGENT_LOGINWINDOW_BOOTSTRAP_NAME_BASE, NULL,
 154                                                                  XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
 155  			xpc_connection_set_instance(mXPCConnection, sessionUUID);
 156  			secnotice("SecurityAgentXPCConnection", "Creating a loginwindow security agent");
 157  		}
 158  
 159  		xpc_connection_set_event_handler(mXPCConnection, ^(xpc_object_t object) {
 160  			if (xpc_get_type(object) == XPC_TYPE_ERROR) {
 161  				secnotice("SecurityAgentXPCConnection", "error during xpc: %s", xpc_dictionary_get_string(object, XPC_ERROR_KEY_DESCRIPTION));
 162  			}
 163  		});
 164  		xpc_connection_resume(mXPCConnection);
 165  		secnotice("SecurityAgentXPCConnection", "%p activated", this);
 166  	}
 167      catch (MacOSError &err) {
 168  		mConnection->useAgent(NULL);	// guess not
 169          Syslog::error("SecurityAgentConnection: error activating SecurityAgent instance %p", this);
 170  		throw;
 171  	}
 172  
 173      secnotice("SecurityAgentXPCConnection", "contact didn't throw (%p)", this);
 174  }
 175  
 176  void
 177  SecurityAgentXPCConnection::terminate()
 178  {
 179  	activate(false);
 180  
 181      // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
 182  	mConnection->useAgent(NULL);
 183  }
 184  
 185  
 186  using SecurityAgent::Reason;
 187  using namespace Authorization;
 188  
 189  ModuleNexus<RecursiveMutex> gAllXPCClientsMutex;
 190  ModuleNexus<set<SecurityAgentXPCQuery*> > allXPCClients;
 191  
 192  void
 193  SecurityAgentXPCQuery::killAllXPCClients()
 194  {
 195      // grab the lock for the client list -- we need to make sure no one modifies the structure while we are iterating it.
 196      StLock<Mutex> _(gAllXPCClientsMutex());
 197  
 198      set<SecurityAgentXPCQuery*>::iterator clientIterator = allXPCClients().begin();
 199      while (clientIterator != allXPCClients().end())
 200      {
 201          set<SecurityAgentXPCQuery*>::iterator thisClient = clientIterator++;
 202          if ((*thisClient)->getTerminateOnSleep())
 203          {
 204              (*thisClient)->terminate();
 205          }
 206      }
 207  }
 208  
 209  
 210  SecurityAgentXPCQuery::SecurityAgentXPCQuery(Session &session)
 211  : SecurityAgentXPCConnection(session), mAgentConnected(false), mTerminateOnSleep(false)
 212  {
 213      secnotice("SecurityAgentXPCQuery", "new SecurityAgentXPCQuery(%p)", this);
 214  }
 215  
 216  SecurityAgentXPCQuery::~SecurityAgentXPCQuery()
 217  {
 218      secnotice("SecurityAgentXPCQuery", "SecurityAgentXPCQuery(%p) dying", this);
 219      if (mAgentConnected) {
 220          this->disconnect();
 221      }
 222  }
 223  
 224  void
 225  SecurityAgentXPCQuery::inferHints(Process &thisProcess)
 226  {
 227      AuthItemSet clientHints;
 228      SecurityAgent::RequestorType type = SecurityAgent::bundle;
 229      pid_t clientPid = thisProcess.pid();
 230      uid_t clientUid = thisProcess.uid();
 231      string guestPath = thisProcess.getPath();
 232      Boolean ignoreSession = TRUE;
 233  
 234  	clientHints.insert(AuthItemRef(AGENT_HINT_CLIENT_TYPE, AuthValueOverlay(sizeof(type), &type)));
 235  	clientHints.insert(AuthItemRef(AGENT_HINT_CLIENT_PATH, AuthValueOverlay(guestPath)));
 236  	clientHints.insert(AuthItemRef(AGENT_HINT_CLIENT_PID, AuthValueOverlay(sizeof(clientPid), &clientPid)));
 237  	clientHints.insert(AuthItemRef(AGENT_HINT_CLIENT_UID, AuthValueOverlay(sizeof(clientUid), &clientUid)));
 238  
 239      /*
 240       * If its loginwindow that's asking, override the loginwindow shield detection
 241       * up front so that it can trigger SecurityAgent dialogs (like password change)
 242       * for when the OD password and keychain password is out of sync.
 243       */
 244  
 245      if (guestPath == "/System/Library/CoreServices/loginwindow.app") {
 246          clientHints.insert(AuthItemRef(AGENT_HINT_IGNORE_SESSION, AuthValueOverlay(sizeof(ignoreSession), &ignoreSession)));
 247      }
 248  
 249  	mClientHints.insert(clientHints.begin(), clientHints.end());
 250  
 251      bool validSignature = thisProcess.checkAppleSigned();
 252      AuthItemSet clientImmutableHints;
 253  
 254  	clientImmutableHints.insert(AuthItemRef(AGENT_HINT_CLIENT_SIGNED, AuthValueOverlay(sizeof(validSignature), &validSignature)));
 255  
 256  	mImmutableHints.insert(clientImmutableHints.begin(), clientImmutableHints.end());
 257  }
 258  
 259  void SecurityAgentXPCQuery::addHint(const char *name, const void *value, UInt32 valueLen, UInt32 flags)
 260  {
 261      AuthorizationItem item = { name, valueLen, const_cast<void *>(value), flags };
 262      mClientHints.insert(AuthItemRef(item));
 263  }
 264  
 265  
 266  void
 267  SecurityAgentXPCQuery::readChoice()
 268  {
 269      allow = false;
 270      remember = false;
 271  
 272  	AuthItem *allowAction = mOutContext.find(AGENT_CONTEXT_ALLOW);
 273  	if (allowAction)
 274  	{
 275          string allowString;
 276  		if (allowAction->getString(allowString)
 277              && (allowString == "YES"))
 278              allow = true;
 279  	}
 280  
 281  	AuthItem *rememberAction = mOutContext.find(AGENT_CONTEXT_REMEMBER_ACTION);
 282  	if (rememberAction)
 283  	{
 284          string rememberString;
 285          if (rememberAction->getString(rememberString)
 286              && (rememberString == "YES"))
 287              remember = true;
 288  	}
 289  }
 290  
 291  void
 292  SecurityAgentXPCQuery::disconnect()
 293  {
 294      if (NULL != mXPCConnection) {
 295          xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
 296          xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_DESTROY);
 297          xpc_connection_send_message(mXPCConnection, requestObject);
 298          xpc_release(requestObject);
 299      }
 300  
 301      StLock<Mutex> _(gAllXPCClientsMutex());
 302      allXPCClients().erase(this);
 303  }
 304  
 305  void
 306  SecurityAgentXPCQuery::terminate()
 307  {
 308      this->disconnect();
 309  }
 310  
 311  static void xpcArrayToAuthItemSet(AuthItemSet *setToBuild, xpc_object_t input) {
 312      setToBuild->clear();
 313  
 314      xpc_array_apply(input,  ^bool(size_t index, xpc_object_t item) {
 315          const char *name = xpc_dictionary_get_string(item, AUTH_XPC_ITEM_NAME);
 316  
 317          size_t length;
 318          const void *data = xpc_dictionary_get_data(item, AUTH_XPC_ITEM_VALUE, &length);
 319          void *dataCopy = 0;
 320  
 321          // <rdar://problem/13033889> authd is holding on to multiple copies of my password in the clear
 322          bool sensitive = xpc_dictionary_get_value(item, AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH);
 323          if (sensitive) {
 324              size_t sensitiveLength = (size_t)xpc_dictionary_get_uint64(item, AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH);
 325              if (sensitiveLength > length) {
 326                  secnotice("SecurityAgentXPCQuery", "Sensitive data len %zu is not valid", sensitiveLength);
 327                  return true;
 328              }
 329              dataCopy = malloc(sensitiveLength);
 330              memcpy(dataCopy, data, sensitiveLength);
 331              memset_s((void *)data, length, 0, sensitiveLength); // clear the sensitive data, memset_s is never optimized away
 332              length = sensitiveLength;
 333          } else {
 334              dataCopy = malloc(length);
 335              memcpy(dataCopy, data, length);
 336          }
 337  
 338          uint64_t flags = xpc_dictionary_get_uint64(item, AUTH_XPC_ITEM_FLAGS);
 339          AuthItemRef nextItem(name, AuthValueOverlay((uint32_t)length, dataCopy), (uint32_t)flags);
 340          setToBuild->insert(nextItem);
 341          memset(dataCopy, 0, length); // The authorization items contain things like passwords, so wiping clean is important.
 342          free(dataCopy);
 343          return true;
 344      });
 345  }
 346  
 347  void
 348  SecurityAgentXPCQuery::create(const char *pluginId, const char *mechanismId)
 349  {
 350      bool ignoreUid = false;
 351  
 352      do {
 353          activate(ignoreUid);
 354  
 355          mAgentConnected = false;
 356  
 357          xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
 358          xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_CREATE);
 359          xpc_dictionary_set_string(requestObject, AUTH_XPC_PLUGIN_NAME, pluginId);
 360          xpc_dictionary_set_string(requestObject, AUTH_XPC_MECHANISM_NAME, mechanismId);
 361  
 362          uid_t targetUid = Server::process().uid();
 363  		bool doSwitchAudit     = (ignoreUid || targetUid == 0 || targetUid == mNobodyUID);
 364  		bool doSwitchBootstrap = (ignoreUid || targetUid == 0 || targetUid == mNobodyUID);
 365  
 366          if (doSwitchAudit) {
 367              mach_port_name_t jobPort;
 368              if (0 == audit_session_port(mSession.sessionId(), &jobPort)) {
 369                  secnotice("SecurityAgentXPCQuery", "attaching an audit session port because the uid was %d", targetUid);
 370                  xpc_dictionary_set_mach_send(requestObject, AUTH_XPC_AUDIT_SESSION_PORT, jobPort);
 371                  if (mach_port_mod_refs(mach_task_self(), jobPort, MACH_PORT_RIGHT_SEND, -1) != KERN_SUCCESS) {
 372                      secnotice("SecurityAgentXPCQuery", "unable to release send right for audit session, leaking");
 373                  }
 374              }
 375          }
 376  
 377          if (doSwitchBootstrap) {
 378              secnotice("SecurityAgentXPCQuery", "attaching a bootstrap port because the uid was %d", targetUid);
 379              MachPlusPlus::Bootstrap processBootstrap = Server::process().taskPort().bootstrap();
 380              xpc_dictionary_set_mach_send(requestObject, AUTH_XPC_BOOTSTRAP_PORT, processBootstrap);
 381          }
 382  
 383          xpc_object_t object = xpc_connection_send_message_with_reply_sync(mXPCConnection, requestObject);
 384          if (xpc_get_type(object) == XPC_TYPE_DICTIONARY) {
 385              const char *replyType = xpc_dictionary_get_string(object, AUTH_XPC_REPLY_METHOD_KEY);
 386              if (0 == strcmp(replyType, AUTH_XPC_REPLY_METHOD_CREATE)) {
 387                  uint64_t status = xpc_dictionary_get_uint64(object, AUTH_XPC_REPLY_RESULT_VALUE);
 388                  if (status == kAuthorizationResultAllow) {
 389                      mAgentConnected = true;
 390                  } else {
 391                      secnotice("SecurityAgentXPCQuery", "plugin create failed in SecurityAgent");
 392                      MacOSError::throwMe(errAuthorizationInternal);
 393                  }
 394              }
 395          } else if (xpc_get_type(object) == XPC_TYPE_ERROR) {
 396              if (XPC_ERROR_CONNECTION_INVALID == object) {
 397                  // If we get an error before getting the create response, try again without the UID
 398                  if (ignoreUid) {
 399                      secnotice("SecurityAgentXPCQuery", "failed to establish connection, no retries left");
 400                      xpc_release(object);
 401                      MacOSError::throwMe(errAuthorizationInternal);
 402                  } else {
 403                      secnotice("SecurityAgentXPCQuery", "failed to establish connection, retrying with no UID");
 404                      ignoreUid = true;
 405                      xpc_release(mXPCConnection);
 406                      mXPCConnection = NULL;
 407                  }
 408              } else if (XPC_ERROR_CONNECTION_INTERRUPTED == object) {
 409                  // If we get an error before getting the create response, try again
 410              }
 411          }
 412          xpc_release(object);
 413          xpc_release(requestObject);
 414      } while (!mAgentConnected);
 415  
 416      StLock<Mutex> _(gAllXPCClientsMutex());
 417      allXPCClients().insert(this);
 418  }
 419  
 420  static xpc_object_t authItemSetToXPCArray(AuthItemSet input) {
 421      xpc_object_t outputArray = xpc_array_create(NULL, 0);
 422      for (AuthItemSet::iterator i = input.begin(); i != input.end(); i++) {
 423          AuthItemRef item = *i;
 424  
 425          xpc_object_t xpc_data = xpc_dictionary_create(NULL, NULL, 0);
 426          xpc_dictionary_set_string(xpc_data, AUTH_XPC_ITEM_NAME, item->name());
 427          AuthorizationValue value = item->value();
 428          if (value.data != NULL) {
 429              xpc_dictionary_set_data(xpc_data, AUTH_XPC_ITEM_VALUE, value.data, value.length);
 430          }
 431          xpc_dictionary_set_uint64(xpc_data, AUTH_XPC_ITEM_FLAGS, item->flags());
 432          xpc_array_append_value(outputArray, xpc_data);
 433          xpc_release(xpc_data);
 434      }
 435      return outputArray;
 436  }
 437  
 438  void
 439  SecurityAgentXPCQuery::invoke() {
 440      xpc_object_t hintsArray = authItemSetToXPCArray(mInHints);
 441      xpc_object_t contextArray = authItemSetToXPCArray(mInContext);
 442      xpc_object_t immutableHintsArray = authItemSetToXPCArray(mImmutableHints);
 443  
 444      xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
 445      xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_INVOKE);
 446      xpc_dictionary_set_value(requestObject, AUTH_XPC_HINTS_NAME, hintsArray);
 447      xpc_dictionary_set_value(requestObject, AUTH_XPC_CONTEXT_NAME, contextArray);
 448      xpc_dictionary_set_value(requestObject, AUTH_XPC_IMMUTABLE_HINTS_NAME, immutableHintsArray);
 449  
 450      xpc_object_t object = xpc_connection_send_message_with_reply_sync(mXPCConnection, requestObject);
 451      if (xpc_get_type(object) == XPC_TYPE_DICTIONARY) {
 452          const char *replyType = xpc_dictionary_get_string(object, AUTH_XPC_REPLY_METHOD_KEY);
 453          if (0 == strcmp(replyType, AUTH_XPC_REPLY_METHOD_RESULT)) {
 454              xpc_object_t xpcHints = xpc_dictionary_get_value(object, AUTH_XPC_HINTS_NAME);
 455              xpc_object_t xpcContext = xpc_dictionary_get_value(object, AUTH_XPC_CONTEXT_NAME);
 456              AuthItemSet tempHints, tempContext;
 457              xpcArrayToAuthItemSet(&tempHints, xpcHints);
 458              xpcArrayToAuthItemSet(&tempContext, xpcContext);
 459              mOutHints = tempHints;
 460              mOutContext = tempContext;
 461              mLastResult = xpc_dictionary_get_uint64(object, AUTH_XPC_REPLY_RESULT_VALUE);
 462          }
 463      } else if (xpc_get_type(object) == XPC_TYPE_ERROR) {
 464          if (XPC_ERROR_CONNECTION_INVALID == object) {
 465              // If the connection drops, return an "auth undefined" result, because we cannot continue
 466          } else if (XPC_ERROR_CONNECTION_INTERRUPTED == object) {
 467              // If the agent dies, return an "auth undefined" result, because we cannot continue
 468          }
 469      }
 470      xpc_release(object);
 471  
 472      xpc_release(hintsArray);
 473      xpc_release(contextArray);
 474      xpc_release(immutableHintsArray);
 475      xpc_release(requestObject);
 476  }
 477  
 478  void SecurityAgentXPCQuery::checkResult()
 479  {
 480      // now check the OSStatus return from the server side
 481      switch (mLastResult) {
 482          case kAuthorizationResultAllow: return;
 483          case kAuthorizationResultDeny:
 484          case kAuthorizationResultUserCanceled: CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
 485          default: MacOSError::throwMe(errAuthorizationInternal);
 486      }
 487  }
 488  
 489  //
 490  // Perform the "rogue app" access query dialog
 491  //
 492  QueryKeychainUse::QueryKeychainUse(bool needPass, const Database *db)
 493  	: mPassphraseCheck(NULL)
 494  {
 495  	// if passphrase checking requested, save KeychainDatabase reference
 496  	// (will quietly disable check if db isn't a keychain)
 497  
 498      // Always require password due to <rdar://problem/34677969>
 499      mPassphraseCheck = dynamic_cast<const KeychainDatabase *>(db);
 500  
 501      setTerminateOnSleep(true);
 502  }
 503  
 504  // Callers to this function must hold the common lock
 505  Reason QueryKeychainUse::queryUser (const char *database, const char *description, AclAuthorization action)
 506  {
 507      Reason reason = SecurityAgent::noReason;
 508  	uint32_t retryCount = 0;
 509  	AuthItemSet hints, context;
 510  
 511  	// prepopulate with client hints
 512  	hints.insert(mClientHints.begin(), mClientHints.end());
 513  
 514  	// put action/operation (sint32) into hints
 515  	hints.insert(AuthItemRef(AGENT_HINT_ACL_TAG, AuthValueOverlay(sizeof(action), static_cast<sint32*>(&action))));
 516  
 517  	// item name into hints
 518  
 519      hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME, AuthValueOverlay(description ? (uint32_t)strlen(description) : 0, const_cast<char*>(description))));
 520  
 521  	// keychain name into hints
 522  	hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(database ? (uint32_t)strlen(database) : 0, const_cast<char*>(database))));
 523  
 524  	if (mPassphraseCheck)
 525  	{
 526  		create("builtin", "confirm-access-password");
 527  
 528  		CssmAutoData data(Allocator::standard(Allocator::sensitive));
 529  
 530  		do
 531  		{
 532  
 533              AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
 534              hints.erase(triesHint); hints.insert(triesHint); // replace
 535  
 536              if (retryCount++ > kMaximumAuthorizationTries)
 537  			{
 538                  reason = SecurityAgent::tooManyTries;
 539  			}
 540  
 541              AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
 542              hints.erase(retryHint); hints.insert(retryHint); // replace
 543  
 544              setInput(hints, context);
 545  
 546              {
 547                  // Must drop the common lock while showing UI.
 548                  StSyncLock<Mutex, Mutex> syncLock(const_cast<KeychainDatabase*>(mPassphraseCheck)->common().uiLock(), const_cast<KeychainDatabase*>(mPassphraseCheck)->common());
 549                  invoke();
 550              }
 551  
 552              if (retryCount > kMaximumAuthorizationTries)
 553  			{
 554                  return reason;
 555  			}
 556  
 557  			checkResult();
 558  
 559  			AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
 560  			if (!passwordItem)
 561  				continue;
 562  
 563  			passwordItem->getCssmData(data);
 564  
 565              // decode() replaces the master key, so do this only if we know the passphrase is correct.
 566              // I suspect decode() is redundant but something might rely on its side effects so let's keep it.
 567              if (const_cast<KeychainDatabase*>(mPassphraseCheck)->validatePassphrase(data) && const_cast<KeychainDatabase*>(mPassphraseCheck)->decode(data)) {
 568                  reason = SecurityAgent::noReason;
 569              } else {
 570                  reason = SecurityAgent::invalidPassphrase;
 571              }
 572  		}
 573          while (reason != SecurityAgent::noReason);
 574          
 575          readChoice();
 576  	}
 577  	else
 578  	{
 579  //        create("builtin", "confirm-access");
 580  //        setInput(hints, context);
 581  //        invoke();
 582          
 583          // This is a hack to support <rdar://problem/34677969>, we can never simply prompt for confirmation
 584          secerror("ACL validation fallback case! Must ask user for account password because we have no database");
 585          Session &session = Server::session();
 586          try{
 587              session.verifyKeyStorePassphrase(1, true, description);
 588          } catch (...) {
 589              return SecurityAgent::invalidPassphrase;
 590          }
 591          SecurityAgentXPCQuery::allow = true;
 592  	}
 593  
 594  	return reason;
 595  }
 596  
 597  //
 598  // Obtain passphrases and submit them to the accept() method until it is accepted
 599  // or we can't get another passphrase. Accept() should consume the passphrase
 600  // if it is accepted. If no passphrase is acceptable, throw out of here.
 601  //
 602  Reason QueryOld::query()
 603  {
 604  	Reason reason = SecurityAgent::noReason;
 605  	AuthItemSet hints, context;
 606  	CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
 607  	int retryCount = 0;
 608  
 609  	// prepopulate with client hints
 610  
 611      const char *keychainPath = database.dbName();
 612      hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay((uint32_t)strlen(keychainPath), const_cast<char*>(keychainPath))));
 613  
 614  	hints.insert(mClientHints.begin(), mClientHints.end());
 615  
 616  	create("builtin", "unlock-keychain");
 617  
 618  	do
 619  	{
 620          AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
 621          hints.erase(triesHint); hints.insert(triesHint); // replace
 622  
 623          ++retryCount;
 624  
 625          if (retryCount > maxTries)
 626  		{
 627  			reason = SecurityAgent::tooManyTries;
 628          }
 629  
 630          AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
 631          hints.erase(retryHint); hints.insert(retryHint); // replace
 632  
 633          setInput(hints, context);
 634          invoke();
 635  
 636          if (retryCount > maxTries)
 637          {
 638  			return reason;
 639  		}
 640  
 641          checkResult();
 642  
 643  		AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
 644  		if (!passwordItem)
 645  			continue;
 646  
 647  		passwordItem->getCssmData(passphrase);
 648  
 649  	}
 650  	while ((reason = accept(passphrase)));
 651  
 652  	return SecurityAgent::noReason;
 653  }
 654  
 655  
 656  //
 657  // Get existing passphrase (unlock) Query
 658  //
 659  Reason QueryOld::operator () ()
 660  {
 661  	return query();
 662  }
 663  
 664  
 665  //
 666  // End-classes for old secrets
 667  //
 668  Reason QueryUnlock::accept(CssmManagedData &passphrase)
 669  {
 670      // Must hold the 'common' lock to call decode; otherwise there's a data corruption issue
 671      StLock<Mutex> _(safer_cast<KeychainDatabase &>(database).common());
 672  
 673      // Calling validatePassphrase here throws when trying to constitute a key.
 674      // Unsure why but since this is for the KC unlock path and not a validation path the wrong password won't make things worse.
 675  	if (safer_cast<KeychainDatabase &>(database).decode(passphrase))
 676  		return SecurityAgent::noReason;
 677  	else
 678  		return SecurityAgent::invalidPassphrase;
 679  }
 680  
 681  Reason QueryUnlock::retrievePassword(CssmOwnedData &passphrase) {
 682      CssmAutoData pass(Allocator::standard(Allocator::sensitive));
 683  
 684      AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
 685      if (!passwordItem)
 686          return SecurityAgent::invalidPassphrase;
 687  
 688      passwordItem->getCssmData(pass);
 689  
 690      passphrase = pass;
 691  
 692     return SecurityAgent::noReason;
 693  }
 694  
 695  QueryKeybagPassphrase::QueryKeybagPassphrase(Session & session, int32_t tries) : mSession(session), mContext(), mRetries(tries)
 696  {
 697      setTerminateOnSleep(true);
 698      mContext = mSession.get_current_service_context();
 699  }
 700  
 701  Reason QueryKeybagPassphrase::query()
 702  {
 703  	Reason reason = SecurityAgent::noReason;
 704  	AuthItemSet hints, context;
 705  	CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
 706  	int retryCount = 0;
 707  
 708  	// prepopulate with client hints
 709  
 710      const char *keychainPath = "iCloud";
 711      hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay((uint32_t)strlen(keychainPath), const_cast<char*>(keychainPath))));
 712  
 713  	hints.insert(mClientHints.begin(), mClientHints.end());
 714  
 715  	create("builtin", "unlock-keychain");
 716  
 717      int currentTry = 0;
 718  	do
 719  	{
 720          currentTry = retryCount;
 721          if (retryCount > mRetries)
 722  		{
 723  			return SecurityAgent::tooManyTries;
 724          }
 725          retryCount++;
 726  
 727          AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(currentTry), &currentTry));
 728          hints.erase(triesHint); hints.insert(triesHint); // replace
 729  
 730          AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
 731          hints.erase(retryHint); hints.insert(retryHint); // replace
 732  
 733          setInput(hints, context);
 734          invoke();
 735  
 736          checkResult();
 737  
 738  		AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
 739  		if (!passwordItem)
 740  			continue;
 741  
 742  		passwordItem->getCssmData(passphrase);
 743  	}
 744  	while ((reason = accept(passphrase)));
 745  
 746  	return SecurityAgent::noReason;
 747  }
 748  
 749  Reason QueryKeybagPassphrase::accept(Security::CssmManagedData & password)
 750  {
 751  	if (service_client_kb_unlock(&mContext, password.data(), (int)password.length()) == 0) {
 752  		mSession.keybagSetState(session_keybag_unlocked);
 753          return SecurityAgent::noReason;
 754      } else
 755  		return SecurityAgent::invalidPassphrase;
 756  }
 757  
 758  QueryKeybagNewPassphrase::QueryKeybagNewPassphrase(Session & session) : QueryKeybagPassphrase(session) {}
 759  
 760  Reason QueryKeybagNewPassphrase::query(CssmOwnedData &oldPassphrase, CssmOwnedData &passphrase)
 761  {
 762      CssmAutoData pass(Allocator::standard(Allocator::sensitive));
 763      CssmAutoData oldPass(Allocator::standard(Allocator::sensitive));
 764      Reason reason = SecurityAgent::noReason;
 765  	AuthItemSet hints, context;
 766  	int retryCount = 0;
 767  
 768  	// prepopulate with client hints
 769  
 770      const char *keychainPath = "iCloud";
 771      hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay((uint32_t)strlen(keychainPath), const_cast<char*>(keychainPath))));
 772  
 773      const char *showResetString = "YES";
 774      hints.insert(AuthItemRef(AGENT_HINT_SHOW_RESET, AuthValueOverlay((uint32_t)strlen(showResetString), const_cast<char*>(showResetString))));
 775  
 776  	hints.insert(mClientHints.begin(), mClientHints.end());
 777  
 778  	create("builtin", "change-passphrase");
 779  
 780      int currentTry = 0;
 781      AuthItem *resetPassword = NULL;
 782  	do
 783  	{
 784          currentTry = retryCount;
 785          if (retryCount > mRetries)
 786  		{
 787  			return SecurityAgent::tooManyTries;
 788          }
 789          retryCount++;
 790  
 791          AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(currentTry), &currentTry));
 792          hints.erase(triesHint); hints.insert(triesHint); // replace
 793  
 794          AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
 795          hints.erase(retryHint); hints.insert(retryHint); // replace
 796  
 797          setInput(hints, context);
 798          invoke();
 799  
 800          checkResult();
 801  
 802          resetPassword = mOutContext.find(AGENT_CONTEXT_RESET_PASSWORD);
 803          if (resetPassword != NULL) {
 804              return SecurityAgent::resettingPassword;
 805          }
 806  
 807          AuthItem *oldPasswordItem = mOutContext.find(AGENT_PASSWORD);
 808          if (!oldPasswordItem)
 809              continue;
 810  
 811          oldPasswordItem->getCssmData(oldPass);
 812  	}
 813  	while ((reason = accept(oldPass)));
 814  
 815      if (reason == SecurityAgent::noReason) {
 816  		AuthItem *passwordItem = mOutContext.find(AGENT_CONTEXT_NEW_PASSWORD);
 817  		if (!passwordItem)
 818              return SecurityAgent::invalidPassphrase;
 819  
 820  		passwordItem->getCssmData(pass);
 821  
 822          oldPassphrase = oldPass;
 823          passphrase = pass;
 824      }
 825  
 826  	return SecurityAgent::noReason;
 827  }
 828  
 829  QueryPIN::QueryPIN(Database &db)
 830  	: QueryOld(db), mPin(Allocator::standard())
 831  {
 832  	this->inferHints(Server::process());
 833  }
 834  
 835  
 836  Reason QueryPIN::accept(CssmManagedData &pin)
 837  {
 838  	// no retries for now
 839  	mPin = pin;
 840  	return SecurityAgent::noReason;
 841  }
 842  
 843  
 844  //
 845  // Obtain passphrases and submit them to the accept() method until it is accepted
 846  // or we can't get another passphrase. Accept() should consume the passphrase
 847  // if it is accepted. If no passphrase is acceptable, throw out of here.
 848  //
 849  Reason QueryNewPassphrase::query()
 850  {
 851  	Reason reason = initialReason;
 852  	CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
 853  	CssmAutoData oldPassphrase(Allocator::standard(Allocator::sensitive));
 854  
 855  	AuthItemSet hints, context;
 856  
 857  	int retryCount = 0;
 858  
 859  	// prepopulate with client hints
 860  	hints.insert(mClientHints.begin(), mClientHints.end());
 861  
 862  	// keychain name into hints
 863  	hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(database.dbName())));
 864  
 865      switch (initialReason)
 866      {
 867          case SecurityAgent::newDatabase:
 868              create("builtin", "new-passphrase");
 869              break;
 870          case SecurityAgent::changePassphrase:
 871              create("builtin", "change-passphrase");
 872              break;
 873          default:
 874              assert(false);
 875      }
 876  
 877  	do
 878  	{
 879          AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
 880          hints.erase(triesHint); hints.insert(triesHint); // replace
 881  
 882  		if (++retryCount > maxTries)
 883  		{
 884  			reason = SecurityAgent::tooManyTries;
 885  		}
 886  
 887          AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
 888          hints.erase(retryHint); hints.insert(retryHint); // replace
 889  
 890          setInput(hints, context);
 891  		invoke();
 892  
 893  		if (retryCount > maxTries)
 894  		{
 895              return reason;
 896          }
 897  
 898          checkResult();
 899  
 900  		if (SecurityAgent::changePassphrase == initialReason)
 901          {
 902              AuthItem *oldPasswordItem = mOutContext.find(AGENT_PASSWORD);
 903              if (!oldPasswordItem)
 904                  continue;
 905  
 906              oldPasswordItem->getCssmData(oldPassphrase);
 907          }
 908  
 909  		AuthItem *passwordItem = mOutContext.find(AGENT_CONTEXT_NEW_PASSWORD);
 910  		if (!passwordItem)
 911  			continue;
 912  
 913  		passwordItem->getCssmData(passphrase);
 914  
 915      }
 916  	while ((reason = accept(passphrase, (initialReason == SecurityAgent::changePassphrase) ? &oldPassphrase.get() : NULL)));
 917  
 918  	return SecurityAgent::noReason;
 919  }
 920  
 921  
 922  //
 923  // Get new passphrase Query
 924  //
 925  Reason QueryNewPassphrase::operator () (CssmOwnedData &oldPassphrase, CssmOwnedData &passphrase)
 926  {
 927  	if (Reason result = query())
 928  		return result;	// failed
 929  	passphrase = mPassphrase;
 930      oldPassphrase = mOldPassphrase;
 931  	return SecurityAgent::noReason;	// success
 932  }
 933  
 934  Reason QueryNewPassphrase::accept(CssmManagedData &passphrase, CssmData *oldPassphrase)
 935  {
 936  	//@@@ acceptance criteria are currently hardwired here
 937  	//@@@ This validation presumes ASCII - UTF8 might be more lenient
 938  
 939  	// if we have an old passphrase, check it
 940  	if (oldPassphrase && !safer_cast<KeychainDatabase&>(database).validatePassphrase(*oldPassphrase))
 941  		return SecurityAgent::oldPassphraseWrong;
 942  
 943  	// sanity check the new passphrase (but allow user override)
 944  	if (!(mPassphraseValid && passphrase.get() == mPassphrase)) {
 945  		mPassphrase = passphrase;
 946          if (oldPassphrase) mOldPassphrase = *oldPassphrase;
 947  		mPassphraseValid = true;
 948  		if (mPassphrase.length() == 0)
 949  			return SecurityAgent::passphraseIsNull;
 950  		if (mPassphrase.length() < 6)
 951  			return SecurityAgent::passphraseTooSimple;
 952  	}
 953  
 954  	// accept this
 955  	return SecurityAgent::noReason;
 956  }
 957  
 958  //
 959  // Get a passphrase for unspecified use
 960  //
 961  Reason QueryGenericPassphrase::operator () (const CssmData *prompt, bool verify,
 962                                              string &passphrase)
 963  {
 964      return query(prompt, verify, passphrase);
 965  }
 966  
 967  Reason QueryGenericPassphrase::query(const CssmData *prompt, bool verify,
 968                                       string &passphrase)
 969  {
 970      Reason reason = SecurityAgent::noReason;
 971      AuthItemSet hints, context;
 972  
 973      hints.insert(mClientHints.begin(), mClientHints.end());
 974      hints.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT, AuthValueOverlay(prompt ? (UInt32)prompt->length() : 0, prompt ? prompt->data() : NULL)));
 975      // XXX/gh  defined by dmitch but no analogous hint in
 976      // AuthorizationTagsPriv.h:
 977      // CSSM_ATTRIBUTE_ALERT_TITLE (optional alert panel title)
 978  
 979      if (false == verify) {  // import
 980  		create("builtin", "generic-unlock");
 981      } else {		// verify passphrase (export)
 982  		create("builtin", "generic-new-passphrase");
 983      }
 984  
 985      AuthItem *passwordItem;
 986  
 987      do {
 988          setInput(hints, context);
 989  		invoke();
 990  		checkResult();
 991  		passwordItem = mOutContext.find(AGENT_PASSWORD);
 992  
 993      } while (!passwordItem);
 994  
 995      passwordItem->getString(passphrase);
 996  
 997      return reason;
 998  }
 999  
1000  
1001  //
1002  // Get a DB blob's passphrase--keychain synchronization
1003  //
1004  Reason QueryDBBlobSecret::operator () (DbHandle *dbHandleArray, uint8 dbHandleArrayCount, DbHandle *dbHandleAuthenticated)
1005  {
1006      return query(dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated);
1007  }
1008  
1009  Reason QueryDBBlobSecret::query(DbHandle *dbHandleArray, uint8 dbHandleArrayCount, DbHandle *dbHandleAuthenticated)
1010  {
1011      Reason reason = SecurityAgent::noReason;
1012  	CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
1013      AuthItemSet hints/*NUKEME*/, context;
1014  
1015  	hints.insert(mClientHints.begin(), mClientHints.end());
1016  	create("builtin", "generic-unlock-kcblob");
1017  
1018      AuthItem *secretItem;
1019  
1020  	int retryCount = 0;
1021  
1022      do {
1023          AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
1024          hints.erase(triesHint); hints.insert(triesHint); // replace
1025  
1026  		if (++retryCount > maxTries)
1027  		{
1028  			reason = SecurityAgent::tooManyTries;
1029  		}
1030  
1031          AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
1032          hints.erase(retryHint); hints.insert(retryHint); // replace
1033  
1034          setInput(hints, context);
1035  		invoke();
1036  		checkResult();
1037  		secretItem = mOutContext.find(AGENT_PASSWORD);
1038  		if (!secretItem)
1039  			continue;
1040  		secretItem->getCssmData(passphrase);
1041  
1042      } while ((reason = accept(passphrase, dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated)));
1043  
1044      return reason;
1045  }
1046  
1047  Reason QueryDBBlobSecret::accept(CssmManagedData &passphrase,
1048  								 DbHandle *dbHandlesToAuthenticate, uint8 dbHandleCount, DbHandle *dbHandleAuthenticated)
1049  {
1050  	DbHandle *currHdl = dbHandlesToAuthenticate;
1051  	short index;
1052  	Boolean authenticated = false;
1053  	for (index=0; index < dbHandleCount && !authenticated; index++)
1054  	{
1055  		try
1056  		{
1057  			RefPointer<KeychainDatabase> dbToUnlock = Server::keychain(*currHdl);
1058  			dbToUnlock->unlockDb(passphrase, false);
1059  			authenticated = true;
1060  			*dbHandleAuthenticated = *currHdl; // return the DbHandle that 'passphrase' authenticated with.
1061  		}
1062  		catch (const CommonError &err)
1063  		{
1064  			currHdl++; // we failed to authenticate with this one, onto the next one.
1065  		}
1066  	}
1067  	if ( !authenticated )
1068  		return SecurityAgent::invalidPassphrase;
1069  
1070  	return SecurityAgent::noReason;
1071  }
1072  
1073  // @@@  no pluggable authentication possible!
1074  Reason
1075  QueryKeychainAuth::performQuery(const KeychainDatabase& db, const char *description, AclAuthorization action, const char *prompt)
1076  {
1077      Reason reason = SecurityAgent::noReason;
1078      AuthItemSet hints, context;
1079  	int retryCount = 0;
1080  	string username;
1081  	string password;
1082  
1083      using CommonCriteria::Securityd::KeychainAuthLogger;
1084      KeychainAuthLogger logger(mAuditToken, (short)AUE_ssauthint, db.dbName(), description);
1085  
1086      hints.insert(mClientHints.begin(), mClientHints.end());
1087  
1088  	// put action/operation (sint32) into hints
1089  	hints.insert(AuthItemRef(AGENT_HINT_ACL_TAG, AuthValueOverlay(sizeof(action), static_cast<sint32*>(&action))));
1090  
1091      hints.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT, AuthValueOverlay(prompt ? (uint32_t)strlen(prompt) : 0, const_cast<char*>(prompt))));
1092  
1093  	// item name into hints
1094  	hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME, AuthValueOverlay(description ? (uint32_t)strlen(description) : 0, const_cast<char*>(description))));
1095  
1096  	// keychain name into hints
1097  	hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(db.dbName() ? (uint32_t)strlen(db.dbName()) : 0, const_cast<char*>(db.dbName()))));
1098  
1099      create("builtin", "confirm-access-user-password");
1100  
1101      AuthItem *usernameItem;
1102      AuthItem *passwordItem;
1103  
1104      // This entire do..while requires the UI lock because we do accept() in the condition, which in some cases is reentrant
1105      StSyncLock<Mutex, Mutex> syncLock(db.common().uiLock(), db.common());
1106      do {
1107          AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
1108          hints.erase(triesHint); hints.insert(triesHint); // replace
1109  
1110  		if (++retryCount > maxTries)
1111  			reason = SecurityAgent::tooManyTries;
1112  
1113          if (SecurityAgent::noReason != reason)
1114          {
1115              if (SecurityAgent::tooManyTries == reason)
1116                  logger.logFailure(NULL,  CommonCriteria::errTooManyTries);
1117              else
1118                  logger.logFailure();
1119          }
1120  
1121          AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
1122          hints.erase(retryHint); hints.insert(retryHint); // replace
1123  
1124          setInput(hints, context);
1125          try
1126          {
1127              invoke();
1128              checkResult();
1129          }
1130          catch (...)     // user probably clicked "deny"
1131          {
1132              logger.logFailure();
1133              throw;
1134          }
1135          usernameItem = mOutContext.find(AGENT_USERNAME);
1136  		passwordItem = mOutContext.find(AGENT_PASSWORD);
1137  		if (!usernameItem || !passwordItem)
1138  			continue;
1139          usernameItem->getString(username);
1140          passwordItem->getString(password);
1141      } while ((reason = accept(username, password)));
1142      syncLock.unlock();
1143  
1144      if (SecurityAgent::noReason == reason)
1145          logger.logSuccess();
1146      // else we logged the denial in the loop
1147  
1148      return reason;
1149  }
1150  
1151  Reason
1152  QueryKeychainAuth::accept(string &username, string &passphrase)
1153  {
1154  	// Note: QueryKeychainAuth currently requires that the
1155  	// specified user be in the admin group. If this requirement
1156  	// ever needs to change, the group name should be passed as
1157  	// a separate argument to this method.
1158  
1159  	const char *user = username.c_str();
1160  	const char *passwd = passphrase.c_str();
1161  	int checkpw_status = checkpw(user, passwd);
1162  
1163  	if (checkpw_status != CHECKPW_SUCCESS) {
1164  		return SecurityAgent::invalidPassphrase;
1165  	}
1166  
1167  	const char *group = "admin";
1168  	if (group) {
1169  		int rc, ismember;
1170  		uuid_t group_uuid, user_uuid;
1171  		rc = mbr_group_name_to_uuid(group, group_uuid);
1172  		if (rc) { return SecurityAgent::userNotInGroup; }
1173  
1174  		rc = mbr_user_name_to_uuid(user, user_uuid);
1175  		if (rc) { return SecurityAgent::userNotInGroup; }
1176  
1177  		rc = mbr_check_membership(user_uuid, group_uuid, &ismember);
1178  		if (rc || !ismember) { return SecurityAgent::userNotInGroup; }
1179  	}
1180  
1181  	return SecurityAgent::noReason;
1182  }
1183