+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);
+ }
+}
+