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