/*
- * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
+ * Copyright (c) 2000-2004,2008-2009 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
//
#include "agentquery.h"
#include "authority.h"
+#include "ccaudit_extensions.h"
#include <Security/AuthorizationTags.h>
#include <Security/AuthorizationTagsPriv.h>
+#include <Security/checkpw.h>
+#include <Security/Security.h>
+#include <System/sys/fileport.h>
+#include <bsm/audit.h>
+#include <bsm/audit_uevents.h> // AUE_ssauthint
+#include <security_utilities/logging.h>
+#include <security_utilities/mach++.h>
+#include <stdlib.h>
+#include <xpc/xpc.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"
+
+#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_REQUEST_METHOD_KEY "_agent_request_key"
+#define AUTH_XPC_REQUEST_METHOD_CREATE "_agent_request_create"
+#define AUTH_XPC_REQUEST_METHOD_INVOKE "_agent_request_invoke"
+#define AUTH_XPC_REQUEST_METHOD_DEACTIVATE "_agent_request_deactivate"
+#define AUTH_XPC_REQUEST_METHOD_DESTROY "_agent_request_destroy"
+#define AUTH_XPC_REPLY_METHOD_KEY "_agent_reply_key"
+#define AUTH_XPC_REPLY_METHOD_RESULT "_agent_reply_result"
+#define AUTH_XPC_REPLY_METHOD_INTERRUPT "_agent_reply_interrupt"
+#define AUTH_XPC_REPLY_METHOD_CREATE "_agent_reply_create"
+#define AUTH_XPC_REPLY_METHOD_DEACTIVATE "_agent_reply_deactivate"
+#define AUTH_XPC_PLUGIN_NAME "_agent_plugin"
+#define AUTH_XPC_MECHANISM_NAME "_agent_mechanism"
+#define AUTH_XPC_HINTS_NAME "_agent_hints"
+#define AUTH_XPC_CONTEXT_NAME "_agent_context"
+#define AUTH_XPC_IMMUTABLE_HINTS_NAME "_agent_immutable_hints"
+#define AUTH_XPC_REQUEST_INSTANCE "_agent_instance"
+#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
#endif //NOSA
-using SecurityAgent::Reason;
-using namespace Authorization;
+// SecurityAgentConnection
-SecurityAgentQuery::SecurityAgentQuery(const AuthHostType type, Session &session) : mAuthHostType(type), mHostInstance(session.authhost(mAuthHostType)), mConnection(&Server::connection())
+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("SecurityAgentQuery", "new SecurityAgentQuery(%p)", this);
+ secdebug("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
}
-SecurityAgentQuery::~SecurityAgentQuery()
+SecurityAgentConnection::~SecurityAgentConnection()
{
- secdebug("SecurityAgentQuery", "SecurityAgentQuery(%p) dying", this);
+ secdebug("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this);
mConnection->useAgent(NULL);
+}
-#if defined(NOSA)
- if (getenv("NOSA")) {
- printf(" [query done]\n");
- return;
+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);
}
-#endif
+ 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);
+}
- if (SecurityAgent::Client::state() != SecurityAgent::Client::dead)
- destroy();
+void
+SecurityAgentConnection::reconnect()
+{
+ // if !mHostInstance throw()?
+ if (mHostInstance)
+ {
+ activate();
+ }
}
void
-SecurityAgentQuery::activate()
+SecurityAgentConnection::terminate()
{
- mConnection->useAgent(this);
+ activate();
+
+ // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
+ mConnection->useAgent(NULL);
+}
- try {
- SecurityAgent::Client::activate(mHostInstance->activate());
- } catch (...) {
+
+// SecurityAgentConnection
+
+SecurityAgentXPCConnection::SecurityAgentXPCConnection(const AuthHostType type, Session &session)
+: mAuthHostType(type),
+mHostInstance(session.authhost(mAuthHostType)),
+mSession(session),
+mConnection(&Server::connection()),
+mAuditToken(Server::connection().auditToken())
+{
+ // this may take a while
+ Server::active().longTermActivity();
+ secdebug("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
+ mXPCConnection = NULL;
+ mNobodyUID = -2;
+ struct passwd *pw = getpwnam("nobody");
+ if (NULL != pw) {
+ mNobodyUID = pw->pw_uid;
+ }
+}
+
+SecurityAgentXPCConnection::~SecurityAgentXPCConnection()
+{
+ secdebug("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.
+ 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;
+ }
+ }
+}
+
+bool SecurityAgentXPCConnection::inDarkWake()
+{
+ return mSession.server().inDarkWake();
+}
+
+void
+SecurityAgentXPCConnection::activate(bool ignoreUid)
+{
+ secdebug("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);
+ }
+ catch (MacOSError &err)
+ {
mConnection->useAgent(NULL); // guess not
+ Syslog::error("SecurityAgentConnection: error activating %s instance %p",
+ mAuthHostType == privilegedAuthHost
+ ? "authorizationhost"
+ : "SecurityAgent", this);
throw;
}
+
+ secdebug("SecurityAgentXPCConnection", "contact didn't throw (%p)", this);
+}
+
+void
+SecurityAgentXPCConnection::reconnect()
+{
+}
+
+void
+SecurityAgentXPCConnection::terminate()
+{
+ activate(false);
+
+ // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
+ mConnection->useAgent(NULL);
+}
+
+
+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)
{
- RefPointer<OSXCode> clientCode = thisProcess.clientCode();
- SecurityAgent::RequestorType requestorType = SecurityAgent::unknown;
- string bundlePath;
-
- if (clientCode)
+ string guestPath;
{
- string encodedBundle = clientCode->encode();
- char bundleType = (encodedBundle.c_str())[0]; // yay, no accessor
- switch(bundleType)
- {
- case 'b': requestorType = SecurityAgent::bundle; break;
- case 't': requestorType = SecurityAgent::tool; break;
- }
- bundlePath = clientCode->canonicalPath();
+ 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());
+}
- AuthItemSet processHints = clientHints(requestorType, bundlePath, 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()
{
AuthItem *allowAction = outContext().find(AGENT_CONTEXT_ALLOW);
if (allowAction)
{
- string allowString;
- if (allowAction->getString(allowString)
- && (allowString == "YES"))
- allow = true;
+ 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;
+ string rememberString;
+ if (rememberAction->getString(rememberString)
+ && (rememberString == "YES"))
+ remember = true;
}
-}
+}
void
-SecurityAgentQuery::terminate()
+SecurityAgentQuery::disconnect()
{
- activate();
+ SecurityAgent::Client::destroy();
+}
- // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
- mConnection->useAgent(NULL);
-
+void
+SecurityAgentQuery::terminate()
+{
+ // you might think these are called in the wrong order, but you'd be wrong
+ SecurityAgentConnection::terminate();
SecurityAgent::Client::terminate();
}
if (status)
{
secdebug("SecurityAgentQuery", "agent went walkabout, restarting");
- Session &session = mHostInstance->session();
- mHostInstance = session.authhost(mAuthHostType, true);
- activate();
+ reconnect();
status = SecurityAgent::Client::create(pluginId, mechanismId, inSessionId);
}
if (status) MacOSError::throwMe(status);
}
+ModuleNexus<RecursiveMutex> gAllXPCClientsMutex;
+ModuleNexus<set<SecurityAgentXPCQuery*> > allXPCClients;
+
+void
+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())
+ {
+ set<SecurityAgentXPCQuery*>::iterator thisClient = clientIterator++;
+ if ((*thisClient)->getTerminateOnSleep())
+ {
+ (*thisClient)->terminate();
+ }
+ }
+}
+
+
+SecurityAgentXPCQuery::SecurityAgentXPCQuery(const AuthHostType type, Session &session)
+: SecurityAgentXPCConnection(type, session), mAgentConnected(false), mTerminateOnSleep(false)
+{
+ secdebug("SecurityAgentXPCQuery", "new SecurityAgentXPCQuery(%p)", this);
+}
+
+SecurityAgentXPCQuery::~SecurityAgentXPCQuery()
+{
+ secdebug("SecurityAgentXPCQuery", "SecurityAgentXPCQuery(%p) dying", this);
+ if (mAgentConnected) {
+ this->disconnect();
+ }
+}
+
+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();
+
+ 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)));
+
+
+ mClientHints.insert(clientHints.begin(), clientHints.end());
+
+ bool validSignature = thisProcess.checkAppleSigned();
+ AuthItemSet clientImmutableHints;
+
+ clientImmutableHints.insert(AuthItemRef(AGENT_HINT_PROCESS_SIGNED, AuthValueOverlay(sizeof(validSignature), &validSignature)));
+
+ mImmutableHints.insert(clientImmutableHints.begin(), clientImmutableHints.end());
+}
+
+void SecurityAgentXPCQuery::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
+SecurityAgentXPCQuery::readChoice()
+{
+ allow = false;
+ remember = false;
+
+ AuthItem *allowAction = mOutContext.find(AGENT_CONTEXT_ALLOW);
+ if (allowAction)
+ {
+ string allowString;
+ if (allowAction->getString(allowString)
+ && (allowString == "YES"))
+ allow = true;
+ }
+
+ AuthItem *rememberAction = mOutContext.find(AGENT_CONTEXT_REMEMBER_ACTION);
+ if (rememberAction)
+ {
+ string rememberString;
+ if (rememberAction->getString(rememberString)
+ && (rememberString == "YES"))
+ remember = true;
+ }
+}
+
+void
+SecurityAgentXPCQuery::disconnect()
+{
+ if (NULL != mXPCConnection) {
+ xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
+ xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_DESTROY);
+ xpc_connection_send_message(mXPCConnection, requestObject);
+ xpc_release(requestObject);
+ }
+
+ StLock<Mutex> _(gAllXPCClientsMutex());
+ allXPCClients().erase(this);
+}
+
+void
+SecurityAgentXPCQuery::terminate()
+{
+ this->disconnect();
+}
+
+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);
+
+ 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);
+ memset(dataCopy, 0, length); // The authorization items contain things like passwords, so wiping clean is important.
+ free(dataCopy);
+ return true;
+ });
+}
+
+void
+SecurityAgentXPCQuery::create(const char *pluginId, const char *mechanismId, const SessionId inSessionId)
+{
+ 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));
+
+ 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);
+ xpc_dictionary_set_mach_send(requestObject, AUTH_XPC_AUDIT_SESSION_PORT, jobPort);
+ }
+ }
+
+ if (doSwitchBootstrap) {
+ secdebug("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);
+ if (0 == strcmp(replyType, AUTH_XPC_REPLY_METHOD_CREATE)) {
+ uint64_t status = xpc_dictionary_get_uint64(object, AUTH_XPC_REPLY_RESULT_VALUE);
+ if (status == kAuthorizationResultAllow) {
+ mAgentConnected = true;
+ } else {
+ secdebug("SecurityAgentXPCQuery", "plugin create failed in SecurityAgent");
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
+ }
+ } else if (xpc_get_type(object) == XPC_TYPE_ERROR) {
+ 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");
+ xpc_release(object);
+ MacOSError::throwMe(errAuthorizationInternal);
+ } else {
+ secdebug("SecurityAgentXPCQuery", "failed to establish connection, retrying with no UID");
+ ignoreUid = true;
+ xpc_release(mXPCConnection);
+ mXPCConnection = NULL;
+ }
+ } else if (XPC_ERROR_CONNECTION_INTERRUPTED == object) {
+ // If we get an error before getting the create response, try again
+ }
+ }
+ xpc_release(object);
+ xpc_release(requestObject);
+ } while (!mAgentConnected);
+
+ StLock<Mutex> _(gAllXPCClientsMutex());
+ allXPCClients().insert(this);
+}
+
+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();
+ if (value.data != NULL) {
+ xpc_dictionary_set_data(xpc_data, AUTH_XPC_ITEM_VALUE, value.data, value.length);
+ }
+ xpc_dictionary_set_uint64(xpc_data, AUTH_XPC_ITEM_FLAGS, item->flags());
+ xpc_array_append_value(outputArray, xpc_data);
+ xpc_release(xpc_data);
+ }
+ return outputArray;
+}
+
+OSStatus
+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);
+ if (0 == strcmp(replyType, AUTH_XPC_REPLY_METHOD_RESULT)) {
+ xpc_object_t xpcHints = xpc_dictionary_get_value(object, AUTH_XPC_HINTS_NAME);
+ xpc_object_t xpcContext = xpc_dictionary_get_value(object, AUTH_XPC_CONTEXT_NAME);
+ AuthItemSet tempHints, tempContext;
+ xpcArrayToAuthItemSet(&tempHints, xpcHints);
+ xpcArrayToAuthItemSet(&tempContext, xpcContext);
+ mOutHints = tempHints;
+ mOutContext = tempContext;
+ mLastResult = xpc_dictionary_get_uint64(object, AUTH_XPC_REPLY_RESULT_VALUE);
+ }
+ } else if (xpc_get_type(object) == XPC_TYPE_ERROR) {
+ if (XPC_ERROR_CONNECTION_INVALID == object) {
+ // If the connection drops, return an "auth undefined" result, because we cannot continue
+ } else if (XPC_ERROR_CONNECTION_INTERRUPTED == object) {
+ // If the agent dies, return an "auth undefined" result, because we cannot continue
+ }
+ }
+ xpc_release(object);
+
+ xpc_release(hintsArray);
+ xpc_release(contextArray);
+ xpc_release(immutableHintsArray);
+ xpc_release(requestObject);
+
+ return status;
+}
+
+void SecurityAgentXPCQuery::checkResult()
+{
+ // now check the OSStatus return from the server side
+ switch (mLastResult) {
+ case kAuthorizationResultAllow: return;
+ case kAuthorizationResultDeny:
+ case kAuthorizationResultUserCanceled: CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
+ default: MacOSError::throwMe(errAuthorizationInternal);
+ }
+}
+
//
// Perform the "rogue app" access query dialog
//
// (will quietly disable check if db isn't a keychain)
if (needPass)
mPassphraseCheck = dynamic_cast<const KeychainDatabase *>(db);
+
+ setTerminateOnSleep(true);
}
Reason QueryKeychainUse::queryUser (const char *database, const char *description, AclAuthorization action)
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_ACL_TAG, AuthValueOverlay(sizeof(action), static_cast<sint32*>(&action))));
// item name into hints
- hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME, AuthValueOverlay(description ? 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 ? strlen(database) : 0, const_cast<char*>(database))));
-
+ 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", NULL);
+ create("builtin", "confirm-access-password", noSecuritySession);
CssmAutoData data(Allocator::standard(Allocator::sensitive));
checkResult();
- AuthItem *passwordItem = outContext().find(kAuthorizationEnvironmentPassword);
+ AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
if (!passwordItem)
continue;
passwordItem->getCssmData(data);
}
- while (reason = (const_cast<KeychainDatabase*>(mPassphraseCheck)->decode(data) ? SecurityAgent::noReason : SecurityAgent::invalidPassphrase));
+ while ((reason = (const_cast<KeychainDatabase*>(mPassphraseCheck)->decode(data) ? SecurityAgent::noReason : SecurityAgent::invalidPassphrase)));
}
else
{
- create("builtin", "confirm-access", NULL);
+ create("builtin", "confirm-access", noSecuritySession);
setInput(hints, context);
invoke();
}
// prepopulate with client hints
hints.insert(mClientHints.begin(), mClientHints.end());
- hints.insert(AuthItemRef(AGENT_HINT_APPLICATION_PATH, AuthValueOverlay(strlen(aclPath), const_cast<char*>(aclPath))));
-
- create("builtin", "code-identity", NULL);
+ 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();
// MacOSError::check(status);
- return kAuthorizationResultAllow == result();
+ return kAuthorizationResultAllow == mLastResult;
}
// prepopulate with client hints
const char *keychainPath = database.dbName();
- hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(strlen(keychainPath), const_cast<char*>(keychainPath))));
+ 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", NULL);
+
+ create("builtin", "unlock-keychain", noSecuritySession);
do
{
checkResult();
- AuthItem *passwordItem = outContext().find(kAuthorizationEnvironmentPassword);
+ AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
if (!passwordItem)
continue;
passwordItem->getCssmData(passphrase);
}
- while (reason = accept(passphrase));
+ while ((reason = accept(passphrase)));
return SecurityAgent::noReason;
}
return SecurityAgent::invalidPassphrase;
}
+Reason QueryUnlock::retrievePassword(CssmOwnedData &passphrase) {
+ CssmAutoData pass(Allocator::standard(Allocator::sensitive));
+
+ AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
+ if (!passwordItem)
+ return SecurityAgent::invalidPassphrase;
+
+ passwordItem->getCssmData(pass);
+
+ passphrase = pass;
+
+ return SecurityAgent::noReason;
+}
+
+QueryKeybagPassphrase::QueryKeybagPassphrase(Session & session, int32_t tries) : mSession(session), mContext(), mRetries(tries)
+{
+ setTerminateOnSleep(true);
+ mContext = mSession.get_current_service_context();
+}
+
+Reason QueryKeybagPassphrase::query()
+{
+ Reason reason = SecurityAgent::noReason;
+ OSStatus status;
+ AuthValueVector arguments;
+ AuthItemSet hints, context;
+ CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
+ int retryCount = 0;
+
+ // prepopulate with client hints
+
+ const char *keychainPath = "iCloud";
+ 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);
+
+ do
+ {
+ if (retryCount > mRetries)
+ {
+ return SecurityAgent::tooManyTries;
+ }
+
+ AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
+ 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();
+
+ checkResult();
+
+ AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
+ if (!passwordItem)
+ continue;
+
+ passwordItem->getCssmData(passphrase);
+
+ ++retryCount;
+ }
+ while ((reason = accept(passphrase)));
+
+ return SecurityAgent::noReason;
+}
+
+Reason QueryKeybagPassphrase::accept(Security::CssmManagedData & password)
+{
+ if (service_client_kb_unlock(&mContext, password.data(), (int)password.length()) == 0) {
+ mSession.keybagSetState(session_keybag_unlocked);
+ return SecurityAgent::noReason;
+ } else
+ return SecurityAgent::invalidPassphrase;
+}
+
+QueryKeybagNewPassphrase::QueryKeybagNewPassphrase(Session & session) : QueryKeybagPassphrase(session) {}
+
+Reason QueryKeybagNewPassphrase::query(CssmOwnedData &oldPassphrase, CssmOwnedData &passphrase)
+{
+ 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;
+
+ // prepopulate with client hints
+
+ const char *keychainPath = "iCloud";
+ hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay((uint32_t)strlen(keychainPath), const_cast<char*>(keychainPath))));
+
+ const char *showResetString = "YES";
+ hints.insert(AuthItemRef(AGENT_HINT_SHOW_RESET, AuthValueOverlay((uint32_t)strlen(showResetString), const_cast<char*>(showResetString))));
+
+ hints.insert(mClientHints.begin(), mClientHints.end());
+
+ create("builtin", "change-passphrase", noSecuritySession);
+
+ AuthItem *resetPassword = NULL;
+ do
+ {
+ if (retryCount > mRetries)
+ {
+ return SecurityAgent::tooManyTries;
+ }
+
+ AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
+ 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();
+
+ checkResult();
+
+ resetPassword = mOutContext.find(AGENT_CONTEXT_RESET_PASSWORD);
+ if (resetPassword != NULL) {
+ return SecurityAgent::resettingPassword;
+ }
+
+ AuthItem *oldPasswordItem = mOutContext.find(AGENT_PASSWORD);
+ if (!oldPasswordItem)
+ continue;
+
+ oldPasswordItem->getCssmData(oldPass);
+
+ ++retryCount;
+ }
+ while ((reason = accept(oldPass)));
+
+ if (reason == SecurityAgent::noReason) {
+ AuthItem *passwordItem = mOutContext.find(AGENT_CONTEXT_NEW_PASSWORD);
+ if (!passwordItem)
+ return SecurityAgent::invalidPassphrase;
+
+ passwordItem->getCssmData(pass);
+
+ oldPassphrase = oldPass;
+ passphrase = pass;
+ }
+
+ return SecurityAgent::noReason;
+}
QueryPIN::QueryPIN(Database &db)
: QueryOld(db), mPin(Allocator::standard())
// keychain name into hints
hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(database.dbName())));
-
+
switch (initialReason)
{
case SecurityAgent::newDatabase:
- create("builtin", "new-passphrase", NULL);
+ create("builtin", "new-passphrase", noSecuritySession);
break;
case SecurityAgent::changePassphrase:
- create("builtin", "change-passphrase", NULL);
+ create("builtin", "change-passphrase", noSecuritySession);
break;
default:
assert(false);
if (SecurityAgent::changePassphrase == initialReason)
{
- AuthItem *oldPasswordItem = outContext().find(AGENT_PASSWORD);
+ AuthItem *oldPasswordItem = mOutContext.find(AGENT_PASSWORD);
if (!oldPasswordItem)
continue;
oldPasswordItem->getCssmData(oldPassphrase);
}
- AuthItem *passwordItem = outContext().find(AGENT_CONTEXT_NEW_PASSWORD);
+ AuthItem *passwordItem = mOutContext.find(AGENT_CONTEXT_NEW_PASSWORD);
if (!passwordItem)
continue;
passwordItem->getCssmData(passphrase);
}
- while (reason = accept(passphrase, (initialReason == SecurityAgent::changePassphrase) ? &oldPassphrase.get() : NULL));
+ while ((reason = accept(passphrase, (initialReason == SecurityAgent::changePassphrase) ? &oldPassphrase.get() : NULL)));
return SecurityAgent::noReason;
}
//
// Get new passphrase Query
//
-Reason QueryNewPassphrase::operator () (CssmOwnedData &passphrase)
+Reason QueryNewPassphrase::operator () (CssmOwnedData &oldPassphrase, CssmOwnedData &passphrase)
{
if (Reason result = query())
return result; // failed
passphrase = mPassphrase;
+ oldPassphrase = mOldPassphrase;
return SecurityAgent::noReason; // success
}
// sanity check the new passphrase (but allow user override)
if (!(mPassphraseValid && passphrase.get() == mPassphrase)) {
mPassphrase = passphrase;
+ if (oldPassphrase) mOldPassphrase = *oldPassphrase;
mPassphraseValid = true;
if (mPassphrase.length() == 0)
return SecurityAgent::passphraseIsNull;
//
// Get a passphrase for unspecified use
//
-Reason QueryGenericPassphrase::operator () (const char *prompt, bool verify,
+Reason QueryGenericPassphrase::operator () (const CssmData *prompt, bool verify,
string &passphrase)
{
return query(prompt, verify, passphrase);
}
-Reason QueryGenericPassphrase::query(const char *prompt, bool verify,
+Reason QueryGenericPassphrase::query(const CssmData *prompt, bool verify,
string &passphrase)
{
Reason reason = SecurityAgent::noReason;
#endif
hints.insert(mClientHints.begin(), mClientHints.end());
- hints.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT, AuthValueOverlay(prompt ? strlen(prompt) : 0, const_cast<char*>(prompt))));
+ 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", NULL);
+ create("builtin", "generic-unlock", noSecuritySession);
} 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", NULL);
+ create("builtin", "generic-new-passphrase", noSecuritySession);
}
AuthItem *passwordItem;
setInput(hints, context);
status = invoke();
checkResult();
- passwordItem = outContext().find(AGENT_PASSWORD);
+ passwordItem = mOutContext.find(AGENT_PASSWORD);
} while (!passwordItem);
//
// Get a DB blob's passphrase--keychain synchronization
//
-
-void QueryDBBlobSecret::addHint(const char *name, const void *value, UInt32 valueLen, UInt32 flags)
+Reason QueryDBBlobSecret::operator () (DbHandle *dbHandleArray, uint8 dbHandleArrayCount, DbHandle *dbHandleAuthenticated)
{
- AuthorizationItem item = { name, valueLen, const_cast<void *>(value), flags };
- mClientHints.insert(AuthItemRef(item));
+ return query(dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated);
}
-Reason QueryDBBlobSecret::operator () (DatabaseCryptoCore &dbCore, const DbBlob *secretsBlob)
-{
- return query(dbCore, secretsBlob);
-}
-
-Reason QueryDBBlobSecret::query(DatabaseCryptoCore &dbCore, const DbBlob *secretsBlob)
+Reason QueryDBBlobSecret::query(DbHandle *dbHandleArray, uint8 dbHandleArrayCount, DbHandle *dbHandleAuthenticated)
{
Reason reason = SecurityAgent::noReason;
CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
return SecurityAgent::noReason;
}
#endif
-
- hints.insert(mClientHints.begin(), mClientHints.end());
-
- create("builtin", "generic-unlock-kcblob", NULL);
+
+ hints.insert(mClientHints.begin(), mClientHints.end());
+ create("builtin", "generic-unlock-kcblob", noSecuritySession);
AuthItem *secretItem;
setInput(hints, context);
status = invoke();
checkResult();
- secretItem = outContext().find(AGENT_PASSWORD);
+ secretItem = mOutContext.find(AGENT_PASSWORD);
if (!secretItem)
continue;
secretItem->getCssmData(passphrase);
- } while (reason = accept(passphrase, dbCore, secretsBlob));
+ } while ((reason = accept(passphrase, dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated)));
return reason;
}
Reason QueryDBBlobSecret::accept(CssmManagedData &passphrase,
- DatabaseCryptoCore &dbCore,
- const DbBlob *secretsBlob)
-{
- try {
- dbCore.setup(secretsBlob, passphrase);
- dbCore.decodeCore(secretsBlob, NULL);
- } catch (const CommonError &err) {
- // XXX/gh Are there errors other than this?
- return SecurityAgent::invalidPassphrase;
+ DbHandle *dbHandlesToAuthenticate, uint8 dbHandleCount, DbHandle *dbHandleAuthenticated)
+{
+ DbHandle *currHdl = dbHandlesToAuthenticate;
+ short index;
+ Boolean authenticated = false;
+ for (index=0; index < dbHandleCount && !authenticated; index++)
+ {
+ try
+ {
+ RefPointer<KeychainDatabase> dbToUnlock = Server::keychain(*currHdl);
+ dbToUnlock->unlockDb(passphrase);
+ authenticated = true;
+ *dbHandleAuthenticated = *currHdl; // return the DbHandle that 'passphrase' authenticated with.
+ }
+ catch (const CommonError &err)
+ {
+ currHdl++; // we failed to authenticate with this one, onto the next one.
+ }
}
+ if ( !authenticated )
+ return SecurityAgent::invalidPassphrase;
+
return SecurityAgent::noReason;
}
// 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());
{
terminate();
}
+
+// @@@ no pluggable authentication possible!
+Reason
+QueryKeychainAuth::operator () (const char *database, 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);
+ }
+
+ 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);
+
+ AuthItem *usernameItem;
+ AuthItem *passwordItem;
+
+ 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)
+ logger.logFailure(NULL, CommonCriteria::errTooManyTries);
+ else
+ logger.logFailure();
+ }
+
+ AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
+ hints.erase(retryHint); hints.insert(retryHint); // replace
+
+ setInput(hints, context);
+ try
+ {
+ invoke();
+ checkResult();
+ }
+ catch (...) // user probably clicked "deny"
+ {
+ logger.logFailure();
+ throw;
+ }
+ usernameItem = mOutContext.find(AGENT_USERNAME);
+ passwordItem = mOutContext.find(AGENT_PASSWORD);
+ if (!usernameItem || !passwordItem)
+ continue;
+ usernameItem->getString(username);
+ passwordItem->getString(password);
+ } while ((reason = accept(username, password)));
+
+ if (SecurityAgent::noReason == reason)
+ logger.logSuccess();
+ // else we logged the denial in the loop
+
+ return reason;
+}
+
+Reason
+QueryKeychainAuth::accept(string &username, string &passphrase)
+{
+ const char *user = username.c_str();
+ const char *passwd = passphrase.c_str();
+ int checkpw_status = checkpw(user, passwd);
+
+ if (checkpw_status != CHECKPW_SUCCESS)
+ return SecurityAgent::invalidPassphrase;
+
+ return SecurityAgent::noReason;
+}
+