2 * Copyright (c) 2000-2013 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 // passphrases - canonical code to obtain passphrases
27 #include "agentquery.h"
28 #include "authority.h"
29 #include "ccaudit_extensions.h"
31 #include <Security/AuthorizationTags.h>
32 #include <Security/AuthorizationTagsPriv.h>
33 #include <Security/checkpw.h>
34 #include <Security/Security.h>
35 #include <System/sys/fileport.h>
36 #include <bsm/audit.h>
37 #include <bsm/audit_uevents.h> // AUE_ssauthint
38 #include <security_utilities/logging.h>
39 #include <security_utilities/mach++.h>
42 #include <xpc/private.h>
43 #include "securityd_service/securityd_service/securityd_service_client.h"
45 #define SECURITYAGENT_BOOTSTRAP_NAME_BASE "com.apple.security.agentMain"
46 #define SECURITYAGENT_STUB_BOOTSTRAP_NAME_BASE "com.apple.security.agentStub"
47 #define AUTHORIZATIONHOST_BOOTSTRAP_NAME_BASE "com.apple.security.authhost"
49 #define AUTH_XPC_ITEM_NAME "_item_name"
50 #define AUTH_XPC_ITEM_FLAGS "_item_flags"
51 #define AUTH_XPC_ITEM_VALUE "_item_value"
52 #define AUTH_XPC_ITEM_TYPE "_item_type"
54 #define AUTH_XPC_REQUEST_METHOD_KEY "_agent_request_key"
55 #define AUTH_XPC_REQUEST_METHOD_CREATE "_agent_request_create"
56 #define AUTH_XPC_REQUEST_METHOD_INVOKE "_agent_request_invoke"
57 #define AUTH_XPC_REQUEST_METHOD_DEACTIVATE "_agent_request_deactivate"
58 #define AUTH_XPC_REQUEST_METHOD_DESTROY "_agent_request_destroy"
59 #define AUTH_XPC_REPLY_METHOD_KEY "_agent_reply_key"
60 #define AUTH_XPC_REPLY_METHOD_RESULT "_agent_reply_result"
61 #define AUTH_XPC_REPLY_METHOD_INTERRUPT "_agent_reply_interrupt"
62 #define AUTH_XPC_REPLY_METHOD_CREATE "_agent_reply_create"
63 #define AUTH_XPC_REPLY_METHOD_DEACTIVATE "_agent_reply_deactivate"
64 #define AUTH_XPC_PLUGIN_NAME "_agent_plugin"
65 #define AUTH_XPC_MECHANISM_NAME "_agent_mechanism"
66 #define AUTH_XPC_HINTS_NAME "_agent_hints"
67 #define AUTH_XPC_CONTEXT_NAME "_agent_context"
68 #define AUTH_XPC_IMMUTABLE_HINTS_NAME "_agent_immutable_hints"
69 #define AUTH_XPC_REQUEST_INSTANCE "_agent_instance"
70 #define AUTH_XPC_REPLY_RESULT_VALUE "_agent_reply_result_value"
71 #define AUTH_XPC_AUDIT_SESSION_PORT "_agent_audit_session_port"
72 #define AUTH_XPC_BOOTSTRAP_PORT "_agent_bootstrap_port"
73 #define AUTH_XPC_SESSION_UUID "_agent_session_uuid"
74 #define AUTH_XPC_SESSION_PREFS "_agent_session_prefs"
75 #define AUTH_XPC_SESSION_INPUT_METHOD "_agent_session_inputMethod"
77 #define UUID_INITIALIZER_FROM_SESSIONID(sessionid) \
78 { 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))) }
81 // NOSA support functions. This is a test mode where the SecurityAgent
82 // is simulated via stdio in the client. Good for running automated tests
83 // of client programs. Only available if -DNOSA when compiling.
89 static void getNoSA(char *buffer
, size_t bufferSize
, const char *fmt
, ...)
94 vfprintf(stdout
, fmt
, args
);
98 memset(buffer
, 0, bufferSize
);
99 const char *nosa
= getenv("NOSA");
100 if (!strcmp(nosa
, "-")) {
101 if (fgets(buffer
, bufferSize
-1, stdin
) == NULL
)
102 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
103 buffer
[strlen(buffer
)-1] = '\0'; // remove trailing newline
104 if (!isatty(fileno(stdin
)))
105 printf("%s\n", buffer
); // echo to output if input not terminal
107 strncpy(buffer
, nosa
, bufferSize
-1);
108 printf("%s\n", buffer
);
110 if (buffer
[0] == '\0') // empty input -> cancellation
111 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED
);
117 // SecurityAgentConnection
119 SecurityAgentConnection::SecurityAgentConnection(const AuthHostType type
, Session
&session
)
120 : mAuthHostType(type
),
121 mHostInstance(session
.authhost(mAuthHostType
)),
122 mConnection(&Server::connection()),
123 mAuditToken(Server::connection().auditToken())
125 // this may take a while
126 Server::active().longTermActivity();
127 secdebug("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
130 SecurityAgentConnection::~SecurityAgentConnection()
132 secdebug("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this);
133 mConnection
->useAgent(NULL
);
137 SecurityAgentConnection::activate()
139 secdebug("SecurityAgentConnection", "activate(%p)", this);
141 Session
&session
= mHostInstance
->session();
142 SessionId targetSessionId
= session
.sessionId();
143 MachPlusPlus::Bootstrap processBootstrap
= Server::process().taskPort().bootstrap();
144 fileport_t userPrefsFP
= MACH_PORT_NULL
;
146 // send the the userPrefs to SecurityAgent
147 if (mAuthHostType
== securityAgent
|| mAuthHostType
== userAuthHost
) {
148 CFRef
<CFDataRef
> userPrefs(mHostInstance
->session().copyUserPrefs());
156 fd
= dup(fileno(mbox
));
160 CFIndex length
= CFDataGetLength(userPrefs
);
161 if (write(fd
, CFDataGetBytePtr(userPrefs
), length
) != length
)
162 Syslog::error("could not write userPrefs");
165 if (0 == fileport_makeport(fd
, &userPrefsFP
))
166 secdebug("SecurityAgentConnection", "stashed the userPrefs file descriptor");
168 Syslog::error("failed to stash the userPrefs file descriptor");
174 if (MACH_PORT_NULL
== userPrefsFP
)
176 secdebug("SecurityAgentConnection", "could not read userPrefs");
180 mConnection
->useAgent(this);
183 StLock
<Mutex
> _(*mHostInstance
);
185 mach_port_t lookupPort
= mHostInstance
->lookup(targetSessionId
);
186 if (MACH_PORT_NULL
== lookupPort
)
188 Syslog::error("could not find real service, bailing");
189 MacOSError::throwMe(CSSM_ERRCODE_SERVICE_NOT_AVAILABLE
);
191 // reset Client contact info
193 SecurityAgent::Client::activate(mPort
);
195 secdebug("SecurityAgentConnection", "%p activated", this);
197 catch (MacOSError
&err
)
199 mConnection
->useAgent(NULL
); // guess not
200 Syslog::error("SecurityAgentConnection: error activating %s instance %p",
201 mAuthHostType
== privilegedAuthHost
202 ? "authorizationhost"
203 : "SecurityAgent", this);
207 secdebug("SecurityAgentConnection", "contacting service (%p)", this);
208 mach_port_name_t jobPort
;
209 if (0 > audit_session_port(session
.sessionId(), &jobPort
))
210 Syslog::error("audit_session_port failed: %m");
211 MacOSError::check(SecurityAgent::Client::contact(jobPort
, processBootstrap
, userPrefsFP
));
212 secdebug("SecurityAgentConnection", "contact didn't throw (%p)", this);
214 if (userPrefsFP
!= MACH_PORT_NULL
)
215 mach_port_deallocate(mach_task_self(), userPrefsFP
);
219 SecurityAgentConnection::reconnect()
221 // if !mHostInstance throw()?
229 SecurityAgentConnection::terminate()
233 // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
234 mConnection
->useAgent(NULL
);
238 // SecurityAgentConnection
240 SecurityAgentXPCConnection::SecurityAgentXPCConnection(const AuthHostType type
, Session
&session
)
241 : mAuthHostType(type
),
242 mHostInstance(session
.authhost(mAuthHostType
)),
244 mConnection(&Server::connection()),
245 mAuditToken(Server::connection().auditToken())
247 // this may take a while
248 Server::active().longTermActivity();
249 secdebug("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
250 mXPCConnection
= NULL
;
251 mXPCStubConnection
= NULL
;
253 struct passwd
*pw
= getpwnam("nobody");
255 mNobodyUID
= pw
->pw_uid
;
259 SecurityAgentXPCConnection::~SecurityAgentXPCConnection()
261 secdebug("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this);
262 mConnection
->useAgent(NULL
);
264 // If a connection has been established, we need to tear it down.
265 if (NULL
!= mXPCConnection
) {
266 // Tearing this down is a multi-step process. First, request a cancellation.
267 // This is safe even if the connection is already in the cancelled state.
268 xpc_connection_cancel(mXPCConnection
);
270 // Then release the XPC connection
271 xpc_release(mXPCConnection
);
272 mXPCConnection
= NULL
;
274 if (NULL
!= mXPCStubConnection
) {
275 // We may or may not have one of these
276 xpc_release(mXPCStubConnection
);
277 mXPCStubConnection
= NULL
;
282 bool SecurityAgentXPCConnection::inDarkWake()
284 return mSession
.server().inDarkWake();
288 SecurityAgentXPCConnection::activate(bool ignoreUid
)
290 secdebug("SecurityAgentConnection", "activate(%p)", this);
292 mConnection
->useAgent(this);
293 if (mXPCConnection
!= NULL
) {
294 // If we already have an XPC connection, there's nothing to do.
299 if (mAuthHostType
== securityAgent
) {
300 uuid_t sessionUUID
= UUID_INITIALIZER_FROM_SESSIONID(mSession
.sessionId());
301 // Yes, these need to be throws, as we're still in securityd, and thus still have to do flow control with exceptions.
302 if (!(mSession
.attributes() & sessionHasGraphicAccess
))
303 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
305 CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE
);
306 uid_t targetUid
= mHostInstance
->session().originatorUid();
307 secdebug("SecurityAgentXPCConnection","Retrieved UID %d for this session", targetUid
);
308 if ((int32_t)targetUid
!= -1) {
309 mXPCStubConnection
= xpc_connection_create_mach_service(SECURITYAGENT_STUB_BOOTSTRAP_NAME_BASE
, NULL
, 0);
310 xpc_connection_set_target_uid(mXPCStubConnection
, targetUid
);
311 secdebug("SecurityAgentXPCConnection", "Creating a security agent stub");
312 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.
313 xpc_connection_resume(mXPCStubConnection
);
315 xpc_object_t wakeupMessage
= xpc_dictionary_create(NULL
, NULL
, 0);
316 xpc_dictionary_set_data(wakeupMessage
, AUTH_XPC_SESSION_UUID
, sessionUUID
, sizeof(uuid_t
));
317 xpc_object_t responseMessage
= xpc_connection_send_message_with_reply_sync(mXPCStubConnection
, wakeupMessage
);
318 if (xpc_get_type(responseMessage
) == XPC_TYPE_DICTIONARY
) {
319 secdebug("SecurityAgentXPCConnection", "Valid response received from stub");
321 secdebug("SecurityAgentXPCConnection", "Error response received from stub");
323 xpc_release(wakeupMessage
);
324 xpc_release(responseMessage
);
327 mXPCConnection
= xpc_connection_create_mach_service(SECURITYAGENT_BOOTSTRAP_NAME_BASE
, NULL
,0);
328 xpc_connection_set_instance(mXPCConnection
, sessionUUID
);
329 secdebug("SecurityAgentXPCConnection", "Creating a security agent");
331 mXPCConnection
= xpc_connection_create_mach_service(AUTHORIZATIONHOST_BOOTSTRAP_NAME_BASE
, NULL
, 0);
332 secdebug("SecurityAgentXPCConnection", "Creating a standard authhost");
335 xpc_connection_set_event_handler(mXPCConnection
, ^(xpc_object_t object
) {
336 if (xpc_get_type(object
) == XPC_TYPE_ERROR
) {
337 secdebug("SecurityAgentXPCConnection", "error during xpc: %s", xpc_dictionary_get_string(object
, XPC_ERROR_KEY_DESCRIPTION
));
341 xpc_connection_resume(mXPCConnection
);
343 secdebug("SecurityAgentXPCConnection", "%p activated", this);
345 catch (MacOSError
&err
)
347 mConnection
->useAgent(NULL
); // guess not
348 Syslog::error("SecurityAgentConnection: error activating %s instance %p",
349 mAuthHostType
== privilegedAuthHost
350 ? "authorizationhost"
351 : "SecurityAgent", this);
355 secdebug("SecurityAgentXPCConnection", "contact didn't throw (%p)", this);
359 SecurityAgentXPCConnection::reconnect()
364 SecurityAgentXPCConnection::terminate()
368 // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
369 mConnection
->useAgent(NULL
);
373 using SecurityAgent::Reason
;
374 using namespace Authorization
;
376 SecurityAgentQuery::SecurityAgentQuery(const AuthHostType type
, Session
&session
)
377 : SecurityAgentConnection(type
, session
)
379 secdebug("SecurityAgentQuery", "new SecurityAgentQuery(%p)", this);
382 SecurityAgentQuery::~SecurityAgentQuery()
384 secdebug("SecurityAgentQuery", "SecurityAgentQuery(%p) dying", this);
387 if (getenv("NOSA")) {
388 printf(" [query done]\n");
393 if (SecurityAgent::Client::state() != SecurityAgent::Client::dead
)
398 SecurityAgentQuery::inferHints(Process
&thisProcess
)
402 StLock
<Mutex
> _(thisProcess
);
403 if (SecCodeRef clientCode
= thisProcess
.currentGuest())
404 guestPath
= codePath(clientCode
);
406 AuthItemSet processHints
= clientHints(SecurityAgent::bundle
, guestPath
,
407 thisProcess
.pid(), thisProcess
.uid());
408 mClientHints
.insert(processHints
.begin(), processHints
.end());
411 void SecurityAgentQuery::addHint(const char *name
, const void *value
, UInt32 valueLen
, UInt32 flags
)
413 AuthorizationItem item
= { name
, valueLen
, const_cast<void *>(value
), flags
};
414 mClientHints
.insert(AuthItemRef(item
));
419 SecurityAgentQuery::readChoice()
424 AuthItem
*allowAction
= outContext().find(AGENT_CONTEXT_ALLOW
);
428 if (allowAction
->getString(allowString
)
429 && (allowString
== "YES"))
433 AuthItem
*rememberAction
= outContext().find(AGENT_CONTEXT_REMEMBER_ACTION
);
436 string rememberString
;
437 if (rememberAction
->getString(rememberString
)
438 && (rememberString
== "YES"))
444 SecurityAgentQuery::disconnect()
446 SecurityAgent::Client::destroy();
450 SecurityAgentQuery::terminate()
452 // you might think these are called in the wrong order, but you'd be wrong
453 SecurityAgentConnection::terminate();
454 SecurityAgent::Client::terminate();
458 SecurityAgentQuery::create(const char *pluginId
, const char *mechanismId
, const SessionId inSessionId
)
461 OSStatus status
= SecurityAgent::Client::create(pluginId
, mechanismId
, inSessionId
);
464 secdebug("SecurityAgentQuery", "agent went walkabout, restarting");
466 status
= SecurityAgent::Client::create(pluginId
, mechanismId
, inSessionId
);
468 if (status
) MacOSError::throwMe(status
);
471 ModuleNexus
<RecursiveMutex
> gAllXPCClientsMutex
;
472 ModuleNexus
<set
<SecurityAgentXPCQuery
*> > allXPCClients
;
475 SecurityAgentXPCQuery::killAllXPCClients()
477 // grab the lock for the client list -- we need to make sure no one modifies the structure while we are iterating it.
478 StLock
<Mutex
> _(gAllXPCClientsMutex());
480 set
<SecurityAgentXPCQuery
*>::iterator clientIterator
= allXPCClients().begin();
481 while (clientIterator
!= allXPCClients().end())
483 set
<SecurityAgentXPCQuery
*>::iterator thisClient
= clientIterator
++;
484 if ((*thisClient
)->getTerminateOnSleep())
486 (*thisClient
)->terminate();
492 SecurityAgentXPCQuery::SecurityAgentXPCQuery(const AuthHostType type
, Session
&session
)
493 : SecurityAgentXPCConnection(type
, session
), mAgentConnected(false), mTerminateOnSleep(false)
495 secdebug("SecurityAgentXPCQuery", "new SecurityAgentXPCQuery(%p)", this);
498 SecurityAgentXPCQuery::~SecurityAgentXPCQuery()
500 secdebug("SecurityAgentXPCQuery", "SecurityAgentXPCQuery(%p) dying", this);
501 if (mAgentConnected
) {
507 SecurityAgentXPCQuery::inferHints(Process
&thisProcess
)
510 if (SecCodeRef clientCode
= thisProcess
.currentGuest())
511 guestPath
= codePath(clientCode
);
513 AuthItemSet clientHints
;
514 SecurityAgent::RequestorType type
= SecurityAgent::bundle
;
515 pid_t clientPid
= thisProcess
.pid();
516 uid_t clientUid
= thisProcess
.uid();
518 clientHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_TYPE
, AuthValueOverlay(sizeof(type
), &type
)));
519 clientHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_PATH
, AuthValueOverlay(guestPath
)));
520 clientHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_PID
, AuthValueOverlay(sizeof(clientPid
), &clientPid
)));
521 clientHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_UID
, AuthValueOverlay(sizeof(clientUid
), &clientUid
)));
524 mClientHints
.insert(clientHints
.begin(), clientHints
.end());
526 bool validSignature
= thisProcess
.checkAppleSigned();
527 AuthItemSet clientImmutableHints
;
529 clientImmutableHints
.insert(AuthItemRef(AGENT_HINT_PROCESS_SIGNED
, AuthValueOverlay(sizeof(validSignature
), &validSignature
)));
531 mImmutableHints
.insert(clientImmutableHints
.begin(), clientImmutableHints
.end());
534 void SecurityAgentXPCQuery::addHint(const char *name
, const void *value
, UInt32 valueLen
, UInt32 flags
)
536 AuthorizationItem item
= { name
, valueLen
, const_cast<void *>(value
), flags
};
537 mClientHints
.insert(AuthItemRef(item
));
542 SecurityAgentXPCQuery::readChoice()
547 AuthItem
*allowAction
= mOutContext
.find(AGENT_CONTEXT_ALLOW
);
551 if (allowAction
->getString(allowString
)
552 && (allowString
== "YES"))
556 AuthItem
*rememberAction
= mOutContext
.find(AGENT_CONTEXT_REMEMBER_ACTION
);
559 string rememberString
;
560 if (rememberAction
->getString(rememberString
)
561 && (rememberString
== "YES"))
567 SecurityAgentXPCQuery::disconnect()
569 if (NULL
!= mXPCConnection
) {
570 xpc_object_t requestObject
= xpc_dictionary_create(NULL
, NULL
, 0);
571 xpc_dictionary_set_string(requestObject
, AUTH_XPC_REQUEST_METHOD_KEY
, AUTH_XPC_REQUEST_METHOD_DESTROY
);
572 xpc_connection_send_message(mXPCConnection
, requestObject
);
573 xpc_release(requestObject
);
576 StLock
<Mutex
> _(gAllXPCClientsMutex());
577 allXPCClients().erase(this);
581 SecurityAgentXPCQuery::terminate()
586 static void xpcArrayToAuthItemSet(AuthItemSet
*setToBuild
, xpc_object_t input
) {
589 xpc_array_apply(input
, ^bool(size_t index
, xpc_object_t item
) {
590 const char *name
= xpc_dictionary_get_string(item
, AUTH_XPC_ITEM_NAME
);
593 const void *data
= xpc_dictionary_get_data(item
, AUTH_XPC_ITEM_VALUE
, &length
);
594 void *dataCopy
= malloc(length
);
595 memcpy(dataCopy
, data
, length
);
597 uint64_t flags
= xpc_dictionary_get_uint64(item
, AUTH_XPC_ITEM_FLAGS
);
598 AuthItemRef
nextItem(name
, AuthValueOverlay((uint32_t)length
, dataCopy
), (uint32_t)flags
);
599 setToBuild
->insert(nextItem
);
600 memset(dataCopy
, 0, length
); // The authorization items contain things like passwords, so wiping clean is important.
607 SecurityAgentXPCQuery::create(const char *pluginId
, const char *mechanismId
, const SessionId inSessionId
)
609 bool ignoreUid
= false;
614 mAgentConnected
= false;
616 xpc_object_t requestObject
= xpc_dictionary_create(NULL
, NULL
, 0);
617 xpc_dictionary_set_string(requestObject
, AUTH_XPC_REQUEST_METHOD_KEY
, AUTH_XPC_REQUEST_METHOD_CREATE
);
618 xpc_dictionary_set_string(requestObject
, AUTH_XPC_PLUGIN_NAME
, pluginId
);
619 xpc_dictionary_set_string(requestObject
, AUTH_XPC_MECHANISM_NAME
, mechanismId
);
621 uid_t targetUid
= Server::process().uid();
622 bool doSwitchAudit
= true; // (ignoreUid) || ((targetUid == 0) || (targetUid == mNobodyUID));
623 bool doSwitchBootstrap
= true; // (ignoreUid) || ((targetUid == 0) || (targetUid == mNobodyUID));
626 mach_port_name_t jobPort
;
627 if (0 == audit_session_port(mSession
.sessionId(), &jobPort
)) {
628 secdebug("SecurityAgentXPCQuery", "attaching an audit session port because the uid was %d", targetUid
);
629 xpc_dictionary_set_mach_send(requestObject
, AUTH_XPC_AUDIT_SESSION_PORT
, jobPort
);
630 if (mach_port_mod_refs(mach_task_self(), jobPort
, MACH_PORT_RIGHT_SEND
, -1) != KERN_SUCCESS
) {
631 secdebug("SecurityAgentXPCQuery", "unable to release send right for audit session, leaking");
636 if (doSwitchBootstrap
) {
637 secdebug("SecurityAgentXPCQuery", "attaching a bootstrap port because the uid was %d", targetUid
);
638 MachPlusPlus::Bootstrap processBootstrap
= Server::process().taskPort().bootstrap();
639 xpc_dictionary_set_mach_send(requestObject
, AUTH_XPC_BOOTSTRAP_PORT
, processBootstrap
);
642 xpc_object_t object
= xpc_connection_send_message_with_reply_sync(mXPCConnection
, requestObject
);
643 if (xpc_get_type(object
) == XPC_TYPE_DICTIONARY
) {
644 const char *replyType
= xpc_dictionary_get_string(object
, AUTH_XPC_REPLY_METHOD_KEY
);
645 if (0 == strcmp(replyType
, AUTH_XPC_REPLY_METHOD_CREATE
)) {
646 uint64_t status
= xpc_dictionary_get_uint64(object
, AUTH_XPC_REPLY_RESULT_VALUE
);
647 if (status
== kAuthorizationResultAllow
) {
648 mAgentConnected
= true;
650 secdebug("SecurityAgentXPCQuery", "plugin create failed in SecurityAgent");
651 MacOSError::throwMe(errAuthorizationInternal
);
654 } else if (xpc_get_type(object
) == XPC_TYPE_ERROR
) {
655 if (XPC_ERROR_CONNECTION_INVALID
== object
) {
656 // If we get an error before getting the create response, try again without the UID
658 secdebug("SecurityAgentXPCQuery", "failed to establish connection, no retries left");
660 MacOSError::throwMe(errAuthorizationInternal
);
662 secdebug("SecurityAgentXPCQuery", "failed to establish connection, retrying with no UID");
664 xpc_release(mXPCConnection
);
665 mXPCConnection
= NULL
;
667 } else if (XPC_ERROR_CONNECTION_INTERRUPTED
== object
) {
668 // If we get an error before getting the create response, try again
672 xpc_release(requestObject
);
673 } while (!mAgentConnected
);
675 StLock
<Mutex
> _(gAllXPCClientsMutex());
676 allXPCClients().insert(this);
679 static xpc_object_t
authItemSetToXPCArray(AuthItemSet input
) {
680 xpc_object_t outputArray
= xpc_array_create(NULL
, 0);
681 for (AuthItemSet::iterator i
= input
.begin(); i
!= input
.end(); i
++) {
682 AuthItemRef item
= *i
;
684 xpc_object_t xpc_data
= xpc_dictionary_create(NULL
, NULL
, 0);
685 xpc_dictionary_set_string(xpc_data
, AUTH_XPC_ITEM_NAME
, item
->name());
686 AuthorizationValue value
= item
->value();
687 if (value
.data
!= NULL
) {
688 xpc_dictionary_set_data(xpc_data
, AUTH_XPC_ITEM_VALUE
, value
.data
, value
.length
);
690 xpc_dictionary_set_uint64(xpc_data
, AUTH_XPC_ITEM_FLAGS
, item
->flags());
691 xpc_array_append_value(outputArray
, xpc_data
);
692 xpc_release(xpc_data
);
698 SecurityAgentXPCQuery::invoke() {
699 __block OSStatus status
= kAuthorizationResultUndefined
;
701 xpc_object_t hintsArray
= authItemSetToXPCArray(mInHints
);
702 xpc_object_t contextArray
= authItemSetToXPCArray(mInContext
);
703 xpc_object_t immutableHintsArray
= authItemSetToXPCArray(mImmutableHints
);
705 xpc_object_t requestObject
= xpc_dictionary_create(NULL
, NULL
, 0);
706 xpc_dictionary_set_string(requestObject
, AUTH_XPC_REQUEST_METHOD_KEY
, AUTH_XPC_REQUEST_METHOD_INVOKE
);
707 xpc_dictionary_set_value(requestObject
, AUTH_XPC_HINTS_NAME
, hintsArray
);
708 xpc_dictionary_set_value(requestObject
, AUTH_XPC_CONTEXT_NAME
, contextArray
);
709 xpc_dictionary_set_value(requestObject
, AUTH_XPC_IMMUTABLE_HINTS_NAME
, immutableHintsArray
);
711 xpc_object_t object
= xpc_connection_send_message_with_reply_sync(mXPCConnection
, requestObject
);
712 if (xpc_get_type(object
) == XPC_TYPE_DICTIONARY
) {
713 const char *replyType
= xpc_dictionary_get_string(object
, AUTH_XPC_REPLY_METHOD_KEY
);
714 if (0 == strcmp(replyType
, AUTH_XPC_REPLY_METHOD_RESULT
)) {
715 xpc_object_t xpcHints
= xpc_dictionary_get_value(object
, AUTH_XPC_HINTS_NAME
);
716 xpc_object_t xpcContext
= xpc_dictionary_get_value(object
, AUTH_XPC_CONTEXT_NAME
);
717 AuthItemSet tempHints
, tempContext
;
718 xpcArrayToAuthItemSet(&tempHints
, xpcHints
);
719 xpcArrayToAuthItemSet(&tempContext
, xpcContext
);
720 mOutHints
= tempHints
;
721 mOutContext
= tempContext
;
722 mLastResult
= xpc_dictionary_get_uint64(object
, AUTH_XPC_REPLY_RESULT_VALUE
);
724 } else if (xpc_get_type(object
) == XPC_TYPE_ERROR
) {
725 if (XPC_ERROR_CONNECTION_INVALID
== object
) {
726 // If the connection drops, return an "auth undefined" result, because we cannot continue
727 } else if (XPC_ERROR_CONNECTION_INTERRUPTED
== object
) {
728 // If the agent dies, return an "auth undefined" result, because we cannot continue
733 xpc_release(hintsArray
);
734 xpc_release(contextArray
);
735 xpc_release(immutableHintsArray
);
736 xpc_release(requestObject
);
741 void SecurityAgentXPCQuery::checkResult()
743 // now check the OSStatus return from the server side
744 switch (mLastResult
) {
745 case kAuthorizationResultAllow
: return;
746 case kAuthorizationResultDeny
:
747 case kAuthorizationResultUserCanceled
: CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED
);
748 default: MacOSError::throwMe(errAuthorizationInternal
);
753 // Perform the "rogue app" access query dialog
755 QueryKeychainUse::QueryKeychainUse(bool needPass
, const Database
*db
)
756 : mPassphraseCheck(NULL
)
758 // if passphrase checking requested, save KeychainDatabase reference
759 // (will quietly disable check if db isn't a keychain)
761 mPassphraseCheck
= dynamic_cast<const KeychainDatabase
*>(db
);
763 setTerminateOnSleep(true);
766 Reason
QueryKeychainUse::queryUser (const char *database
, const char *description
, AclAuthorization action
)
768 Reason reason
= SecurityAgent::noReason
;
771 AuthValueVector arguments
;
772 AuthItemSet hints
, context
;
775 if (getenv("NOSA")) {
776 char answer
[maxPassphraseLength
+10];
778 string applicationPath
;
779 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
780 if (applicationPathItem
)
781 applicationPathItem
->getString(applicationPath
);
783 getNoSA(answer
, sizeof(answer
), "Allow %s to do %d on %s in %s? [yn][g]%s ",
784 applicationPath
.c_str(), int(action
), (description
? description
: "[NULL item]"),
785 (database
? database
: "[NULL database]"),
786 mPassphraseCheck
? ":passphrase" : "");
787 // turn passphrase (no ':') into y:passphrase
788 if (mPassphraseCheck
&& !strchr(answer
, ':')) {
789 memmove(answer
+2, answer
, strlen(answer
)+1);
790 memcpy(answer
, "y:", 2);
793 allow
= answer
[0] == 'y';
794 remember
= answer
[1] == 'g';
795 return SecurityAgent::noReason
;
799 // prepopulate with client hints
800 hints
.insert(mClientHints
.begin(), mClientHints
.end());
802 // put action/operation (sint32) into hints
803 hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
))));
805 // item name into hints
807 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description
? (uint32_t)strlen(description
) : 0, const_cast<char*>(description
))));
809 // keychain name into hints
810 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
? (uint32_t)strlen(database
) : 0, const_cast<char*>(database
))));
812 if (mPassphraseCheck
)
814 create("builtin", "confirm-access-password", noSecuritySession
);
816 CssmAutoData
data(Allocator::standard(Allocator::sensitive
));
821 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
822 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
824 if (retryCount
++ > kMaximumAuthorizationTries
)
826 reason
= SecurityAgent::tooManyTries
;
829 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
830 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
832 setInput(hints
, context
);
835 if (retryCount
> kMaximumAuthorizationTries
)
842 AuthItem
*passwordItem
= mOutContext
.find(kAuthorizationEnvironmentPassword
);
846 passwordItem
->getCssmData(data
);
848 while ((reason
= (const_cast<KeychainDatabase
*>(mPassphraseCheck
)->decode(data
) ? SecurityAgent::noReason
: SecurityAgent::invalidPassphrase
)));
852 create("builtin", "confirm-access", noSecuritySession
);
853 setInput(hints
, context
);
863 // Perform code signature ACL access adjustment dialogs
865 bool QueryCodeCheck::operator () (const char *aclPath
)
868 AuthValueVector arguments
;
869 AuthItemSet hints
, context
;
872 if (getenv("NOSA")) {
875 string applicationPath
;
876 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
877 if (applicationPathItem
)
878 applicationPathItem
->getString(applicationPath
);
880 getNoSA(answer
, sizeof(answer
),
881 "Allow %s to match an ACL for %s [yn][g]? ",
882 applicationPath
.c_str(), aclPath
? aclPath
: "(unknown)");
883 allow
= answer
[0] == 'y';
884 remember
= answer
[1] == 'g';
889 // prepopulate with client hints
890 hints
.insert(mClientHints
.begin(), mClientHints
.end());
892 hints
.insert(AuthItemRef(AGENT_HINT_APPLICATION_PATH
, AuthValueOverlay((uint32_t)strlen(aclPath
), const_cast<char*>(aclPath
))));
894 create("builtin", "code-identity", noSecuritySession
);
896 setInput(hints
, context
);
901 // MacOSError::check(status);
903 return kAuthorizationResultAllow
== mLastResult
;
908 // Obtain passphrases and submit them to the accept() method until it is accepted
909 // or we can't get another passphrase. Accept() should consume the passphrase
910 // if it is accepted. If no passphrase is acceptable, throw out of here.
912 Reason
QueryOld::query()
914 Reason reason
= SecurityAgent::noReason
;
916 AuthValueVector arguments
;
917 AuthItemSet hints
, context
;
918 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
922 // return the passphrase
923 if (getenv("NOSA")) {
924 char passphrase_
[maxPassphraseLength
];
925 getNoSA(passphrase
, maxPassphraseLength
, "Unlock %s [<CR> to cancel]: ", database
.dbName());
926 passphrase
.copy(passphrase_
, strlen(passphrase_
));
927 return database
.decode(passphrase
) ? SecurityAgent::noReason
: SecurityAgent::invalidPassphrase
;
931 // prepopulate with client hints
933 const char *keychainPath
= database
.dbName();
934 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay((uint32_t)strlen(keychainPath
), const_cast<char*>(keychainPath
))));
936 hints
.insert(mClientHints
.begin(), mClientHints
.end());
938 create("builtin", "unlock-keychain", noSecuritySession
);
942 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
943 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
947 if (retryCount
> maxTries
)
949 reason
= SecurityAgent::tooManyTries
;
952 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
953 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
955 setInput(hints
, context
);
958 if (retryCount
> maxTries
)
965 AuthItem
*passwordItem
= mOutContext
.find(kAuthorizationEnvironmentPassword
);
969 passwordItem
->getCssmData(passphrase
);
972 while ((reason
= accept(passphrase
)));
974 return SecurityAgent::noReason
;
979 // Get existing passphrase (unlock) Query
981 Reason
QueryOld::operator () ()
988 // End-classes for old secrets
990 Reason
QueryUnlock::accept(CssmManagedData
&passphrase
)
992 if (safer_cast
<KeychainDatabase
&>(database
).decode(passphrase
))
993 return SecurityAgent::noReason
;
995 return SecurityAgent::invalidPassphrase
;
998 Reason
QueryUnlock::retrievePassword(CssmOwnedData
&passphrase
) {
999 CssmAutoData
pass(Allocator::standard(Allocator::sensitive
));
1001 AuthItem
*passwordItem
= mOutContext
.find(kAuthorizationEnvironmentPassword
);
1003 return SecurityAgent::invalidPassphrase
;
1005 passwordItem
->getCssmData(pass
);
1009 return SecurityAgent::noReason
;
1012 QueryKeybagPassphrase::QueryKeybagPassphrase(Session
& session
, int32_t tries
) : mSession(session
), mContext(), mRetries(tries
)
1014 setTerminateOnSleep(true);
1015 mContext
= mSession
.get_current_service_context();
1018 Reason
QueryKeybagPassphrase::query()
1020 Reason reason
= SecurityAgent::noReason
;
1022 AuthValueVector arguments
;
1023 AuthItemSet hints
, context
;
1024 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
1027 // prepopulate with client hints
1029 const char *keychainPath
= "iCloud";
1030 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay((uint32_t)strlen(keychainPath
), const_cast<char*>(keychainPath
))));
1032 hints
.insert(mClientHints
.begin(), mClientHints
.end());
1034 create("builtin", "unlock-keychain", noSecuritySession
);
1038 if (retryCount
> mRetries
)
1040 return SecurityAgent::tooManyTries
;
1043 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
1044 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
1046 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
1047 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
1049 setInput(hints
, context
);
1054 AuthItem
*passwordItem
= mOutContext
.find(kAuthorizationEnvironmentPassword
);
1058 passwordItem
->getCssmData(passphrase
);
1062 while ((reason
= accept(passphrase
)));
1064 return SecurityAgent::noReason
;
1067 Reason
QueryKeybagPassphrase::accept(Security::CssmManagedData
& password
)
1069 if (service_client_kb_unlock(&mContext
, password
.data(), (int)password
.length()) == 0) {
1070 mSession
.keybagSetState(session_keybag_unlocked
);
1071 return SecurityAgent::noReason
;
1073 return SecurityAgent::invalidPassphrase
;
1076 QueryKeybagNewPassphrase::QueryKeybagNewPassphrase(Session
& session
) : QueryKeybagPassphrase(session
) {}
1078 Reason
QueryKeybagNewPassphrase::query(CssmOwnedData
&oldPassphrase
, CssmOwnedData
&passphrase
)
1080 CssmAutoData
pass(Allocator::standard(Allocator::sensitive
));
1081 CssmAutoData
oldPass(Allocator::standard(Allocator::sensitive
));
1082 Reason reason
= SecurityAgent::noReason
;
1084 AuthValueVector arguments
;
1085 AuthItemSet hints
, context
;
1088 // prepopulate with client hints
1090 const char *keychainPath
= "iCloud";
1091 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay((uint32_t)strlen(keychainPath
), const_cast<char*>(keychainPath
))));
1093 const char *showResetString
= "YES";
1094 hints
.insert(AuthItemRef(AGENT_HINT_SHOW_RESET
, AuthValueOverlay((uint32_t)strlen(showResetString
), const_cast<char*>(showResetString
))));
1096 hints
.insert(mClientHints
.begin(), mClientHints
.end());
1098 create("builtin", "change-passphrase", noSecuritySession
);
1100 AuthItem
*resetPassword
= NULL
;
1103 if (retryCount
> mRetries
)
1105 return SecurityAgent::tooManyTries
;
1108 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
1109 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
1111 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
1112 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
1114 setInput(hints
, context
);
1119 resetPassword
= mOutContext
.find(AGENT_CONTEXT_RESET_PASSWORD
);
1120 if (resetPassword
!= NULL
) {
1121 return SecurityAgent::resettingPassword
;
1124 AuthItem
*oldPasswordItem
= mOutContext
.find(AGENT_PASSWORD
);
1125 if (!oldPasswordItem
)
1128 oldPasswordItem
->getCssmData(oldPass
);
1132 while ((reason
= accept(oldPass
)));
1134 if (reason
== SecurityAgent::noReason
) {
1135 AuthItem
*passwordItem
= mOutContext
.find(AGENT_CONTEXT_NEW_PASSWORD
);
1137 return SecurityAgent::invalidPassphrase
;
1139 passwordItem
->getCssmData(pass
);
1141 oldPassphrase
= oldPass
;
1145 return SecurityAgent::noReason
;
1148 QueryPIN::QueryPIN(Database
&db
)
1149 : QueryOld(db
), mPin(Allocator::standard())
1151 this->inferHints(Server::process());
1155 Reason
QueryPIN::accept(CssmManagedData
&pin
)
1157 // no retries for now
1159 return SecurityAgent::noReason
;
1164 // Obtain passphrases and submit them to the accept() method until it is accepted
1165 // or we can't get another passphrase. Accept() should consume the passphrase
1166 // if it is accepted. If no passphrase is acceptable, throw out of here.
1168 Reason
QueryNewPassphrase::query()
1170 Reason reason
= initialReason
;
1171 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
1172 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
1175 AuthValueVector arguments
;
1176 AuthItemSet hints
, context
;
1181 if (getenv("NOSA")) {
1182 char passphrase_
[maxPassphraseLength
];
1183 getNoSA(passphrase_
, maxPassphraseLength
,
1184 "New passphrase for %s (reason %d) [<CR> to cancel]: ",
1185 database
.dbName(), reason
);
1186 return SecurityAgent::noReason
;
1190 // prepopulate with client hints
1191 hints
.insert(mClientHints
.begin(), mClientHints
.end());
1193 // keychain name into hints
1194 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
.dbName())));
1196 switch (initialReason
)
1198 case SecurityAgent::newDatabase
:
1199 create("builtin", "new-passphrase", noSecuritySession
);
1201 case SecurityAgent::changePassphrase
:
1202 create("builtin", "change-passphrase", noSecuritySession
);
1210 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
1211 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
1213 if (++retryCount
> maxTries
)
1215 reason
= SecurityAgent::tooManyTries
;
1218 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
1219 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
1221 setInput(hints
, context
);
1224 if (retryCount
> maxTries
)
1231 if (SecurityAgent::changePassphrase
== initialReason
)
1233 AuthItem
*oldPasswordItem
= mOutContext
.find(AGENT_PASSWORD
);
1234 if (!oldPasswordItem
)
1237 oldPasswordItem
->getCssmData(oldPassphrase
);
1240 AuthItem
*passwordItem
= mOutContext
.find(AGENT_CONTEXT_NEW_PASSWORD
);
1244 passwordItem
->getCssmData(passphrase
);
1247 while ((reason
= accept(passphrase
, (initialReason
== SecurityAgent::changePassphrase
) ? &oldPassphrase
.get() : NULL
)));
1249 return SecurityAgent::noReason
;
1254 // Get new passphrase Query
1256 Reason
QueryNewPassphrase::operator () (CssmOwnedData
&oldPassphrase
, CssmOwnedData
&passphrase
)
1258 if (Reason result
= query())
1259 return result
; // failed
1260 passphrase
= mPassphrase
;
1261 oldPassphrase
= mOldPassphrase
;
1262 return SecurityAgent::noReason
; // success
1265 Reason
QueryNewPassphrase::accept(CssmManagedData
&passphrase
, CssmData
*oldPassphrase
)
1267 //@@@ acceptance criteria are currently hardwired here
1268 //@@@ This validation presumes ASCII - UTF8 might be more lenient
1270 // if we have an old passphrase, check it
1271 if (oldPassphrase
&& !safer_cast
<KeychainDatabase
&>(database
).validatePassphrase(*oldPassphrase
))
1272 return SecurityAgent::oldPassphraseWrong
;
1274 // sanity check the new passphrase (but allow user override)
1275 if (!(mPassphraseValid
&& passphrase
.get() == mPassphrase
)) {
1276 mPassphrase
= passphrase
;
1277 if (oldPassphrase
) mOldPassphrase
= *oldPassphrase
;
1278 mPassphraseValid
= true;
1279 if (mPassphrase
.length() == 0)
1280 return SecurityAgent::passphraseIsNull
;
1281 if (mPassphrase
.length() < 6)
1282 return SecurityAgent::passphraseTooSimple
;
1286 return SecurityAgent::noReason
;
1290 // Get a passphrase for unspecified use
1292 Reason
QueryGenericPassphrase::operator () (const CssmData
*prompt
, bool verify
,
1295 return query(prompt
, verify
, passphrase
);
1298 Reason
QueryGenericPassphrase::query(const CssmData
*prompt
, bool verify
,
1301 Reason reason
= SecurityAgent::noReason
;
1302 OSStatus status
; // not really used; remove?
1303 AuthValueVector arguments
;
1304 AuthItemSet hints
, context
;
1307 if (getenv("NOSA")) {
1309 return SecurityAgent::noReason
;
1313 hints
.insert(mClientHints
.begin(), mClientHints
.end());
1314 hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt
? (UInt32
)prompt
->length() : 0, prompt
? prompt
->data() : NULL
)));
1315 // XXX/gh defined by dmitch but no analogous hint in
1316 // AuthorizationTagsPriv.h:
1317 // CSSM_ATTRIBUTE_ALERT_TITLE (optional alert panel title)
1319 if (false == verify
) { // import
1320 create("builtin", "generic-unlock", noSecuritySession
);
1321 } else { // verify passphrase (export)
1322 // new-passphrase-generic works with the pre-4 June 2004 agent;
1323 // generic-new-passphrase is required for the new agent
1324 create("builtin", "generic-new-passphrase", noSecuritySession
);
1327 AuthItem
*passwordItem
;
1330 setInput(hints
, context
);
1333 passwordItem
= mOutContext
.find(AGENT_PASSWORD
);
1335 } while (!passwordItem
);
1337 passwordItem
->getString(passphrase
);
1344 // Get a DB blob's passphrase--keychain synchronization
1346 Reason
QueryDBBlobSecret::operator () (DbHandle
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle
*dbHandleAuthenticated
)
1348 return query(dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
);
1351 Reason
QueryDBBlobSecret::query(DbHandle
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle
*dbHandleAuthenticated
)
1353 Reason reason
= SecurityAgent::noReason
;
1354 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
1355 OSStatus status
; // not really used; remove?
1356 AuthValueVector arguments
;
1357 AuthItemSet hints
/*NUKEME*/, context
;
1360 if (getenv("NOSA")) {
1361 // FIXME akin to 3690984
1362 return SecurityAgent::noReason
;
1366 hints
.insert(mClientHints
.begin(), mClientHints
.end());
1367 create("builtin", "generic-unlock-kcblob", noSecuritySession
);
1369 AuthItem
*secretItem
;
1374 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
1375 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
1377 if (++retryCount
> maxTries
)
1379 reason
= SecurityAgent::tooManyTries
;
1382 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
1383 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
1385 setInput(hints
, context
);
1388 secretItem
= mOutContext
.find(AGENT_PASSWORD
);
1391 secretItem
->getCssmData(passphrase
);
1393 } while ((reason
= accept(passphrase
, dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
)));
1398 Reason
QueryDBBlobSecret::accept(CssmManagedData
&passphrase
,
1399 DbHandle
*dbHandlesToAuthenticate
, uint8 dbHandleCount
, DbHandle
*dbHandleAuthenticated
)
1401 DbHandle
*currHdl
= dbHandlesToAuthenticate
;
1403 Boolean authenticated
= false;
1404 for (index
=0; index
< dbHandleCount
&& !authenticated
; index
++)
1408 RefPointer
<KeychainDatabase
> dbToUnlock
= Server::keychain(*currHdl
);
1409 dbToUnlock
->unlockDb(passphrase
);
1410 authenticated
= true;
1411 *dbHandleAuthenticated
= *currHdl
; // return the DbHandle that 'passphrase' authenticated with.
1413 catch (const CommonError
&err
)
1415 currHdl
++; // we failed to authenticate with this one, onto the next one.
1418 if ( !authenticated
)
1419 return SecurityAgent::invalidPassphrase
;
1421 return SecurityAgent::noReason
;
1424 QueryInvokeMechanism::QueryInvokeMechanism(const AuthHostType type
, Session
&session
) :
1425 SecurityAgentQuery(type
, session
) { }
1427 void QueryInvokeMechanism::initialize(const string
&inPluginId
, const string
&inMechanismId
, const AuthValueVector
&inArguments
, const SessionId inSessionId
)
1429 if (SecurityAgent::Client::init
== SecurityAgent::Client::state())
1431 create(inPluginId
.c_str(), inMechanismId
.c_str(), inSessionId
);
1432 mArguments
= inArguments
;
1436 // XXX/cs should return AuthorizationResult
1437 void QueryInvokeMechanism::run(const AuthValueVector
&inArguments
, AuthItemSet
&inHints
, AuthItemSet
&inContext
, AuthorizationResult
*outResult
)
1439 // prepopulate with client hints
1440 inHints
.insert(mClientHints
.begin(), mClientHints
.end());
1442 if (Server::active().inDarkWake())
1443 CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE
);
1445 setArguments(inArguments
);
1446 setInput(inHints
, inContext
);
1447 MacOSError::check(invoke());
1449 if (outResult
) *outResult
= result();
1451 inHints
= outHints();
1452 inContext
= outContext();
1455 void QueryInvokeMechanism::terminateAgent()
1460 // @@@ no pluggable authentication possible!
1462 QueryKeychainAuth::operator () (const char *database
, const char *description
, AclAuthorization action
, const char *prompt
)
1464 Reason reason
= SecurityAgent::noReason
;
1465 AuthItemSet hints
, context
;
1466 AuthValueVector arguments
;
1471 using CommonCriteria::Securityd::KeychainAuthLogger
;
1472 KeychainAuthLogger
logger(mAuditToken
, AUE_ssauthint
, database
, description
);
1475 /* XXX/gh probably not complete; stolen verbatim from rogue-app query */
1476 if (getenv("NOSA")) {
1477 char answer
[maxPassphraseLength
+10];
1479 string applicationPath
;
1480 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
1481 if (applicationPathItem
)
1482 applicationPathItem
->getString(applicationPath
);
1484 getNoSA(answer
, sizeof(answer
), "Allow %s to do %d on %s in %s? [yn][g]%s ",
1485 applicationPath
.c_str(), int(action
), (description
? description
: "[NULL item]"),
1486 (database
? database
: "[NULL database]"),
1487 mPassphraseCheck
? ":passphrase" : "");
1488 // turn passphrase (no ':') into y:passphrase
1489 if (mPassphraseCheck
&& !strchr(answer
, ':')) {
1490 memmove(answer
+2, answer
, strlen(answer
)+1);
1491 memcpy(answer
, "y:", 2);
1494 allow
= answer
[0] == 'y';
1495 remember
= answer
[1] == 'g';
1496 return SecurityAgent::noReason
;
1500 hints
.insert(mClientHints
.begin(), mClientHints
.end());
1502 // put action/operation (sint32) into hints
1503 hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
))));
1505 hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt
? (uint32_t)strlen(prompt
) : 0, const_cast<char*>(prompt
))));
1507 // item name into hints
1508 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description
? (uint32_t)strlen(description
) : 0, const_cast<char*>(description
))));
1510 // keychain name into hints
1511 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
? (uint32_t)strlen(database
) : 0, const_cast<char*>(database
))));
1513 create("builtin", "confirm-access-user-password", noSecuritySession
);
1515 AuthItem
*usernameItem
;
1516 AuthItem
*passwordItem
;
1520 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
1521 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
1523 if (++retryCount
> maxTries
)
1524 reason
= SecurityAgent::tooManyTries
;
1526 if (SecurityAgent::noReason
!= reason
)
1528 if (SecurityAgent::tooManyTries
== reason
)
1529 logger
.logFailure(NULL
, CommonCriteria::errTooManyTries
);
1531 logger
.logFailure();
1534 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
1535 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
1537 setInput(hints
, context
);
1543 catch (...) // user probably clicked "deny"
1545 logger
.logFailure();
1548 usernameItem
= mOutContext
.find(AGENT_USERNAME
);
1549 passwordItem
= mOutContext
.find(AGENT_PASSWORD
);
1550 if (!usernameItem
|| !passwordItem
)
1552 usernameItem
->getString(username
);
1553 passwordItem
->getString(password
);
1554 } while ((reason
= accept(username
, password
)));
1556 if (SecurityAgent::noReason
== reason
)
1557 logger
.logSuccess();
1558 // else we logged the denial in the loop
1564 QueryKeychainAuth::accept(string
&username
, string
&passphrase
)
1566 const char *user
= username
.c_str();
1567 const char *passwd
= passphrase
.c_str();
1568 int checkpw_status
= checkpw(user
, passwd
);
1570 if (checkpw_status
!= CHECKPW_SUCCESS
)
1571 return SecurityAgent::invalidPassphrase
;
1573 return SecurityAgent::noReason
;