#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
// SecurityAgentConnection
-SecurityAgentConnection::SecurityAgentConnection(const AuthHostType type, Session &session)
- : mAuthHostType(type),
- mHostInstance(session.authhost(mAuthHostType)),
- mConnection(&Server::connection()),
- mAuditToken(Server::connection().auditToken())
+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();
mConnection->useAgent(NULL);
}
-void
+void
SecurityAgentConnection::activate()
{
secdebug("SecurityAgentConnection", "activate(%p)", this);
// send the the userPrefs to SecurityAgent
if (mAuthHostType == securityAgent || mAuthHostType == userAuthHost) {
CFRef<CFDataRef> userPrefs(mHostInstance->session().copyUserPrefs());
- if (NULL != userPrefs)
+ if (0 != userPrefs)
{
FILE *mbox = NULL;
int fd = 0;
- mbox = tmpfile();
+ mbox = tmpfile();
if (NULL != mbox)
{
fd = dup(fileno(mbox));
}
mConnection->useAgent(this);
- try
+ try
{
StLock<Mutex> _(*mHostInstance);
SecurityAgent::Client::activate(mPort);
secdebug("SecurityAgentConnection", "%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"
+ mAuthHostType == privilegedAuthHost
+ ? "authorizationhost"
: "SecurityAgent", this);
throw;
}
}
-// SecurityAgentTransaction
+// SecurityAgentConnection
-SecurityAgentTransaction::SecurityAgentTransaction(const AuthHostType type, Session &session, bool startNow)
- : SecurityAgentConnection(type, session),
- mStarted(false)
+SecurityAgentXPCConnection::SecurityAgentXPCConnection(const AuthHostType type, Session &session)
+: mAuthHostType(type),
+mHostInstance(session.authhost(mAuthHostType)),
+mSession(session),
+mConnection(&Server::connection()),
+mAuditToken(Server::connection().auditToken())
{
- secdebug("SecurityAgentTransaction", "New SecurityAgentTransaction(%p)", this);
- activate(); // start agent now, or other SAConnections will kill and spawn new agents
- if (startNow)
- start();
+ // 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;
+ }
}
-SecurityAgentTransaction::~SecurityAgentTransaction()
+SecurityAgentXPCConnection::~SecurityAgentXPCConnection()
{
- try { end(); } catch(...) {}
- secdebug("SecurityAgentTransaction", "Destroying %p", this);
+ 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;
+ }
+ }
}
-void
-SecurityAgentTransaction::start()
+bool SecurityAgentXPCConnection::inDarkWake()
{
- secdebug("SecurityAgentTransaction", "start(%p)", this);
- MacOSError::check(SecurityAgentQuery::Client::startTransaction(mPort));
- mStarted = true;
- secdebug("SecurityAgentTransaction", "started(%p)", this);
+ return mSession.server().inDarkWake();
}
-void
-SecurityAgentTransaction::end()
+void
+SecurityAgentXPCConnection::activate(bool ignoreUid)
{
- if (started())
- {
- MacOSError::check(SecurityAgentQuery::Client::endTransaction(mPort));
- mStarted = false;
+ secdebug("SecurityAgentConnection", "activate(%p)", this);
+
+ mConnection->useAgent(this);
+ if (mXPCConnection != NULL) {
+ // If we already have an XPC connection, there's nothing to do.
+ return;
}
- secdebug("SecurityAgentTransaction", "End SecurityAgentTransaction(%p)", this);
+ 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)
+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
-
+#endif
+
if (SecurityAgent::Client::state() != SecurityAgent::Client::dead)
- destroy();
+ destroy();
}
void
guestPath = codePath(clientCode);
}
AuthItemSet processHints = clientHints(SecurityAgent::bundle, guestPath,
- thisProcess.pid(), thisProcess.uid());
+ thisProcess.pid(), thisProcess.uid());
mClientHints.insert(processHints.begin(), processHints.end());
}
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::disconnect()
{
SecurityAgent::Client::destroy();
}
-
+
void
SecurityAgentQuery::terminate()
{
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
//
// 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)
{
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
{
// prepopulate with client hints
hints.insert(mClientHints.begin(), mClientHints.end());
- hints.insert(AuthItemRef(AGENT_HINT_APPLICATION_PATH, AuthValueOverlay(strlen(aclPath), const_cast<char*>(aclPath))));
-
+ hints.insert(AuthItemRef(AGENT_HINT_APPLICATION_PATH, AuthValueOverlay((uint32_t)strlen(aclPath), const_cast<char*>(aclPath))));
+
create("builtin", "code-identity", noSecuritySession);
setInput(hints, context);
// 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", 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:
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;
setInput(hints, context);
status = invoke();
checkResult();
- passwordItem = outContext().find(AGENT_PASSWORD);
+ passwordItem = mOutContext.find(AGENT_PASSWORD);
} while (!passwordItem);
#endif
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, dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated));
+ } while ((reason = accept(passphrase, dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated)));
return reason;
}
// prepopulate with client hints
inHints.insert(mClientHints.begin(), mClientHints.end());
- if (mAuthHostType == securityAgent) {
- if (Server::active().inDarkWake())
- CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE);
- }
+ if (Server::active().inDarkWake())
+ CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE);
setArguments(inArguments);
setInput(inHints, inContext);
// 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 ? strlen(prompt) : 0, const_cast<char*>(prompt))));
+ 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 ? 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))));
create("builtin", "confirm-access-user-password", noSecuritySession);
logger.logFailure();
throw;
}
- usernameItem = outContext().find(AGENT_USERNAME);
- passwordItem = outContext().find(AGENT_PASSWORD);
+ 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));
+ } while ((reason = accept(username, password)));
if (SecurityAgent::noReason == reason)
logger.logSuccess();