2 * Copyright (c) 2000-2015 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 <membership.h>
39 #include <membershipPriv.h>
40 #include <security_utilities/logging.h>
41 #include <security_utilities/mach++.h>
44 #include <xpc/private.h>
45 #include "securityd_service/securityd_service/securityd_service_client.h"
47 #define SECURITYAGENT_BOOTSTRAP_NAME_BASE "com.apple.security.agent"
48 #define SECURITYAGENT_LOGINWINDOW_BOOTSTRAP_NAME_BASE "com.apple.security.agent.login"
49 #define AUTHORIZATIONHOST_BOOTSTRAP_NAME_BASE "com.apple.security.authhost"
51 #define AUTH_XPC_ITEM_NAME "_item_name"
52 #define AUTH_XPC_ITEM_FLAGS "_item_flags"
53 #define AUTH_XPC_ITEM_VALUE "_item_value"
54 #define AUTH_XPC_ITEM_TYPE "_item_type"
56 #define AUTH_XPC_REQUEST_METHOD_KEY "_agent_request_key"
57 #define AUTH_XPC_REQUEST_METHOD_CREATE "_agent_request_create"
58 #define AUTH_XPC_REQUEST_METHOD_INVOKE "_agent_request_invoke"
59 #define AUTH_XPC_REQUEST_METHOD_DEACTIVATE "_agent_request_deactivate"
60 #define AUTH_XPC_REQUEST_METHOD_DESTROY "_agent_request_destroy"
61 #define AUTH_XPC_REPLY_METHOD_KEY "_agent_reply_key"
62 #define AUTH_XPC_REPLY_METHOD_RESULT "_agent_reply_result"
63 #define AUTH_XPC_REPLY_METHOD_INTERRUPT "_agent_reply_interrupt"
64 #define AUTH_XPC_REPLY_METHOD_CREATE "_agent_reply_create"
65 #define AUTH_XPC_REPLY_METHOD_DEACTIVATE "_agent_reply_deactivate"
66 #define AUTH_XPC_PLUGIN_NAME "_agent_plugin"
67 #define AUTH_XPC_MECHANISM_NAME "_agent_mechanism"
68 #define AUTH_XPC_HINTS_NAME "_agent_hints"
69 #define AUTH_XPC_CONTEXT_NAME "_agent_context"
70 #define AUTH_XPC_IMMUTABLE_HINTS_NAME "_agent_immutable_hints"
71 #define AUTH_XPC_REQUEST_INSTANCE "_agent_instance"
72 #define AUTH_XPC_REPLY_RESULT_VALUE "_agent_reply_result_value"
73 #define AUTH_XPC_AUDIT_SESSION_PORT "_agent_audit_session_port"
74 #define AUTH_XPC_BOOTSTRAP_PORT "_agent_bootstrap_port"
76 #define UUID_INITIALIZER_FROM_SESSIONID(sessionid) \
77 { 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))) }
80 // NOSA support functions. This is a test mode where the SecurityAgent
81 // is simulated via stdio in the client. Good for running automated tests
82 // of client programs. Only available if -DNOSA when compiling.
87 static void getNoSA(char *buffer
, size_t bufferSize
, const char *fmt
, ...)
92 vfprintf(stdout
, fmt
, args
);
96 memset(buffer
, 0, bufferSize
);
97 const char *nosa
= getenv("NOSA");
98 if (!strcmp(nosa
, "-")) {
99 if (fgets(buffer
, bufferSize
-1, stdin
) == NULL
)
100 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
101 buffer
[strlen(buffer
)-1] = '\0'; // remove trailing newline
102 if (!isatty(fileno(stdin
)))
103 printf("%s\n", buffer
); // echo to output if input not terminal
105 strncpy(buffer
, nosa
, bufferSize
-1);
106 printf("%s\n", buffer
);
108 if (buffer
[0] == '\0') // empty input -> cancellation
109 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED
);
114 // SecurityAgentXPCConnection
116 SecurityAgentXPCConnection::SecurityAgentXPCConnection(const AuthHostType type
, Session
&session
)
117 : mAuthHostType(type
),
118 mHostInstance(session
.authhost(mAuthHostType
)),
120 mConnection(&Server::connection()),
121 mAuditToken(Server::connection().auditToken())
123 // this may take a while
124 Server::active().longTermActivity();
125 secdebug("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
126 mXPCConnection
= NULL
;
128 struct passwd
*pw
= getpwnam("nobody");
130 mNobodyUID
= pw
->pw_uid
;
134 SecurityAgentXPCConnection::~SecurityAgentXPCConnection()
136 secdebug("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this);
137 mConnection
->useAgent(NULL
);
139 // If a connection has been established, we need to tear it down.
140 if (NULL
!= mXPCConnection
) {
141 // Tearing this down is a multi-step process. First, request a cancellation.
142 // This is safe even if the connection is already in the cancelled state.
143 xpc_connection_cancel(mXPCConnection
);
145 // Then release the XPC connection
146 xpc_release(mXPCConnection
);
147 mXPCConnection
= NULL
;
151 bool SecurityAgentXPCConnection::inDarkWake()
153 return mSession
.server().inDarkWake();
157 SecurityAgentXPCConnection::activate(bool ignoreUid
)
159 secdebug("SecurityAgentConnection", "activate(%p)", this);
161 mConnection
->useAgent(this);
162 if (mXPCConnection
!= NULL
) {
163 // If we already have an XPC connection, there's nothing to do.
168 uuid_t sessionUUID
= UUID_INITIALIZER_FROM_SESSIONID(mSession
.sessionId());
170 if (mAuthHostType
== securityAgent
) {
171 // Yes, these need to be throws, as we're still in securityd, and thus still have to do flow control with exceptions.
172 if (!(mSession
.attributes() & sessionHasGraphicAccess
))
173 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
175 CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE
);
176 uid_t targetUid
= mHostInstance
->session().originatorUid();
178 secdebug("SecurityAgentXPCConnection","Retrieved UID %d for this session", targetUid
);
179 if (!ignoreUid
&& targetUid
!= 0 && targetUid
!= mNobodyUID
) {
180 mXPCConnection
= xpc_connection_create_mach_service(SECURITYAGENT_BOOTSTRAP_NAME_BASE
, NULL
, 0);
181 xpc_connection_set_target_uid(mXPCConnection
, targetUid
);
182 secdebug("SecurityAgentXPCConnection", "Creating a standard security agent");
184 mXPCConnection
= xpc_connection_create_mach_service(SECURITYAGENT_LOGINWINDOW_BOOTSTRAP_NAME_BASE
, NULL
, 0);
185 xpc_connection_set_instance(mXPCConnection
, sessionUUID
);
186 secdebug("SecurityAgentXPCConnection", "Creating a loginwindow security agent");
189 mXPCConnection
= xpc_connection_create_mach_service(AUTHORIZATIONHOST_BOOTSTRAP_NAME_BASE
, NULL
, 0);
190 xpc_connection_set_instance(mXPCConnection
, sessionUUID
);
191 secdebug("SecurityAgentXPCConnection", "Creating a standard authhost");
194 xpc_connection_set_event_handler(mXPCConnection
, ^(xpc_object_t object
) {
195 if (xpc_get_type(object
) == XPC_TYPE_ERROR
) {
196 secdebug("SecurityAgentXPCConnection", "error during xpc: %s", xpc_dictionary_get_string(object
, XPC_ERROR_KEY_DESCRIPTION
));
199 xpc_connection_resume(mXPCConnection
);
200 secdebug("SecurityAgentXPCConnection", "%p activated", this);
202 catch (MacOSError
&err
) {
203 mConnection
->useAgent(NULL
); // guess not
204 Syslog::error("SecurityAgentConnection: error activating %s instance %p",
205 mAuthHostType
== privilegedAuthHost
206 ? "authorizationhost"
207 : "SecurityAgent", this);
211 secdebug("SecurityAgentXPCConnection", "contact didn't throw (%p)", this);
215 SecurityAgentXPCConnection::reconnect()
220 SecurityAgentXPCConnection::terminate()
224 // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
225 mConnection
->useAgent(NULL
);
229 using SecurityAgent::Reason
;
230 using namespace Authorization
;
232 ModuleNexus
<RecursiveMutex
> gAllXPCClientsMutex
;
233 ModuleNexus
<set
<SecurityAgentXPCQuery
*> > allXPCClients
;
236 SecurityAgentXPCQuery::killAllXPCClients()
238 // grab the lock for the client list -- we need to make sure no one modifies the structure while we are iterating it.
239 StLock
<Mutex
> _(gAllXPCClientsMutex());
241 set
<SecurityAgentXPCQuery
*>::iterator clientIterator
= allXPCClients().begin();
242 while (clientIterator
!= allXPCClients().end())
244 set
<SecurityAgentXPCQuery
*>::iterator thisClient
= clientIterator
++;
245 if ((*thisClient
)->getTerminateOnSleep())
247 (*thisClient
)->terminate();
253 SecurityAgentXPCQuery::SecurityAgentXPCQuery(const AuthHostType type
, Session
&session
)
254 : SecurityAgentXPCConnection(type
, session
), mAgentConnected(false), mTerminateOnSleep(false)
256 secdebug("SecurityAgentXPCQuery", "new SecurityAgentXPCQuery(%p)", this);
259 SecurityAgentXPCQuery::~SecurityAgentXPCQuery()
261 secdebug("SecurityAgentXPCQuery", "SecurityAgentXPCQuery(%p) dying", this);
262 if (mAgentConnected
) {
268 SecurityAgentXPCQuery::inferHints(Process
&thisProcess
)
271 if (SecCodeRef clientCode
= thisProcess
.currentGuest())
272 guestPath
= codePath(clientCode
);
274 AuthItemSet clientHints
;
275 SecurityAgent::RequestorType type
= SecurityAgent::bundle
;
276 pid_t clientPid
= thisProcess
.pid();
277 uid_t clientUid
= thisProcess
.uid();
279 clientHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_TYPE
, AuthValueOverlay(sizeof(type
), &type
)));
280 clientHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_PATH
, AuthValueOverlay(guestPath
)));
281 clientHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_PID
, AuthValueOverlay(sizeof(clientPid
), &clientPid
)));
282 clientHints
.insert(AuthItemRef(AGENT_HINT_CLIENT_UID
, AuthValueOverlay(sizeof(clientUid
), &clientUid
)));
285 mClientHints
.insert(clientHints
.begin(), clientHints
.end());
287 bool validSignature
= thisProcess
.checkAppleSigned();
288 AuthItemSet clientImmutableHints
;
290 clientImmutableHints
.insert(AuthItemRef(AGENT_HINT_PROCESS_SIGNED
, AuthValueOverlay(sizeof(validSignature
), &validSignature
)));
292 mImmutableHints
.insert(clientImmutableHints
.begin(), clientImmutableHints
.end());
295 void SecurityAgentXPCQuery::addHint(const char *name
, const void *value
, UInt32 valueLen
, UInt32 flags
)
297 AuthorizationItem item
= { name
, valueLen
, const_cast<void *>(value
), flags
};
298 mClientHints
.insert(AuthItemRef(item
));
303 SecurityAgentXPCQuery::readChoice()
308 AuthItem
*allowAction
= mOutContext
.find(AGENT_CONTEXT_ALLOW
);
312 if (allowAction
->getString(allowString
)
313 && (allowString
== "YES"))
317 AuthItem
*rememberAction
= mOutContext
.find(AGENT_CONTEXT_REMEMBER_ACTION
);
320 string rememberString
;
321 if (rememberAction
->getString(rememberString
)
322 && (rememberString
== "YES"))
328 SecurityAgentXPCQuery::disconnect()
330 if (NULL
!= mXPCConnection
) {
331 xpc_object_t requestObject
= xpc_dictionary_create(NULL
, NULL
, 0);
332 xpc_dictionary_set_string(requestObject
, AUTH_XPC_REQUEST_METHOD_KEY
, AUTH_XPC_REQUEST_METHOD_DESTROY
);
333 xpc_connection_send_message(mXPCConnection
, requestObject
);
334 xpc_release(requestObject
);
337 StLock
<Mutex
> _(gAllXPCClientsMutex());
338 allXPCClients().erase(this);
342 SecurityAgentXPCQuery::terminate()
347 static void xpcArrayToAuthItemSet(AuthItemSet
*setToBuild
, xpc_object_t input
) {
350 xpc_array_apply(input
, ^bool(size_t index
, xpc_object_t item
) {
351 const char *name
= xpc_dictionary_get_string(item
, AUTH_XPC_ITEM_NAME
);
354 const void *data
= xpc_dictionary_get_data(item
, AUTH_XPC_ITEM_VALUE
, &length
);
355 void *dataCopy
= malloc(length
);
356 memcpy(dataCopy
, data
, length
);
358 uint64_t flags
= xpc_dictionary_get_uint64(item
, AUTH_XPC_ITEM_FLAGS
);
359 AuthItemRef
nextItem(name
, AuthValueOverlay((uint32_t)length
, dataCopy
), (uint32_t)flags
);
360 setToBuild
->insert(nextItem
);
361 memset(dataCopy
, 0, length
); // The authorization items contain things like passwords, so wiping clean is important.
368 SecurityAgentXPCQuery::create(const char *pluginId
, const char *mechanismId
)
370 bool ignoreUid
= false;
375 mAgentConnected
= false;
377 xpc_object_t requestObject
= xpc_dictionary_create(NULL
, NULL
, 0);
378 xpc_dictionary_set_string(requestObject
, AUTH_XPC_REQUEST_METHOD_KEY
, AUTH_XPC_REQUEST_METHOD_CREATE
);
379 xpc_dictionary_set_string(requestObject
, AUTH_XPC_PLUGIN_NAME
, pluginId
);
380 xpc_dictionary_set_string(requestObject
, AUTH_XPC_MECHANISM_NAME
, mechanismId
);
382 uid_t targetUid
= Server::process().uid();
383 bool doSwitchAudit
= (ignoreUid
|| targetUid
== 0 || targetUid
== mNobodyUID
);
384 bool doSwitchBootstrap
= (ignoreUid
|| targetUid
== 0 || targetUid
== mNobodyUID
);
387 mach_port_name_t jobPort
;
388 if (0 == audit_session_port(mSession
.sessionId(), &jobPort
)) {
389 secdebug("SecurityAgentXPCQuery", "attaching an audit session port because the uid was %d", targetUid
);
390 xpc_dictionary_set_mach_send(requestObject
, AUTH_XPC_AUDIT_SESSION_PORT
, jobPort
);
391 if (mach_port_mod_refs(mach_task_self(), jobPort
, MACH_PORT_RIGHT_SEND
, -1) != KERN_SUCCESS
) {
392 secdebug("SecurityAgentXPCQuery", "unable to release send right for audit session, leaking");
397 if (doSwitchBootstrap
) {
398 secdebug("SecurityAgentXPCQuery", "attaching a bootstrap port because the uid was %d", targetUid
);
399 MachPlusPlus::Bootstrap processBootstrap
= Server::process().taskPort().bootstrap();
400 xpc_dictionary_set_mach_send(requestObject
, AUTH_XPC_BOOTSTRAP_PORT
, processBootstrap
);
403 xpc_object_t object
= xpc_connection_send_message_with_reply_sync(mXPCConnection
, requestObject
);
404 if (xpc_get_type(object
) == XPC_TYPE_DICTIONARY
) {
405 const char *replyType
= xpc_dictionary_get_string(object
, AUTH_XPC_REPLY_METHOD_KEY
);
406 if (0 == strcmp(replyType
, AUTH_XPC_REPLY_METHOD_CREATE
)) {
407 uint64_t status
= xpc_dictionary_get_uint64(object
, AUTH_XPC_REPLY_RESULT_VALUE
);
408 if (status
== kAuthorizationResultAllow
) {
409 mAgentConnected
= true;
411 secdebug("SecurityAgentXPCQuery", "plugin create failed in SecurityAgent");
412 MacOSError::throwMe(errAuthorizationInternal
);
415 } else if (xpc_get_type(object
) == XPC_TYPE_ERROR
) {
416 if (XPC_ERROR_CONNECTION_INVALID
== object
) {
417 // If we get an error before getting the create response, try again without the UID
419 secdebug("SecurityAgentXPCQuery", "failed to establish connection, no retries left");
421 MacOSError::throwMe(errAuthorizationInternal
);
423 secdebug("SecurityAgentXPCQuery", "failed to establish connection, retrying with no UID");
425 xpc_release(mXPCConnection
);
426 mXPCConnection
= NULL
;
428 } else if (XPC_ERROR_CONNECTION_INTERRUPTED
== object
) {
429 // If we get an error before getting the create response, try again
433 xpc_release(requestObject
);
434 } while (!mAgentConnected
);
436 StLock
<Mutex
> _(gAllXPCClientsMutex());
437 allXPCClients().insert(this);
440 static xpc_object_t
authItemSetToXPCArray(AuthItemSet input
) {
441 xpc_object_t outputArray
= xpc_array_create(NULL
, 0);
442 for (AuthItemSet::iterator i
= input
.begin(); i
!= input
.end(); i
++) {
443 AuthItemRef item
= *i
;
445 xpc_object_t xpc_data
= xpc_dictionary_create(NULL
, NULL
, 0);
446 xpc_dictionary_set_string(xpc_data
, AUTH_XPC_ITEM_NAME
, item
->name());
447 AuthorizationValue value
= item
->value();
448 if (value
.data
!= NULL
) {
449 xpc_dictionary_set_data(xpc_data
, AUTH_XPC_ITEM_VALUE
, value
.data
, value
.length
);
451 xpc_dictionary_set_uint64(xpc_data
, AUTH_XPC_ITEM_FLAGS
, item
->flags());
452 xpc_array_append_value(outputArray
, xpc_data
);
453 xpc_release(xpc_data
);
459 SecurityAgentXPCQuery::invoke() {
460 __block OSStatus status
= kAuthorizationResultUndefined
;
462 xpc_object_t hintsArray
= authItemSetToXPCArray(mInHints
);
463 xpc_object_t contextArray
= authItemSetToXPCArray(mInContext
);
464 xpc_object_t immutableHintsArray
= authItemSetToXPCArray(mImmutableHints
);
466 xpc_object_t requestObject
= xpc_dictionary_create(NULL
, NULL
, 0);
467 xpc_dictionary_set_string(requestObject
, AUTH_XPC_REQUEST_METHOD_KEY
, AUTH_XPC_REQUEST_METHOD_INVOKE
);
468 xpc_dictionary_set_value(requestObject
, AUTH_XPC_HINTS_NAME
, hintsArray
);
469 xpc_dictionary_set_value(requestObject
, AUTH_XPC_CONTEXT_NAME
, contextArray
);
470 xpc_dictionary_set_value(requestObject
, AUTH_XPC_IMMUTABLE_HINTS_NAME
, immutableHintsArray
);
472 xpc_object_t object
= xpc_connection_send_message_with_reply_sync(mXPCConnection
, requestObject
);
473 if (xpc_get_type(object
) == XPC_TYPE_DICTIONARY
) {
474 const char *replyType
= xpc_dictionary_get_string(object
, AUTH_XPC_REPLY_METHOD_KEY
);
475 if (0 == strcmp(replyType
, AUTH_XPC_REPLY_METHOD_RESULT
)) {
476 xpc_object_t xpcHints
= xpc_dictionary_get_value(object
, AUTH_XPC_HINTS_NAME
);
477 xpc_object_t xpcContext
= xpc_dictionary_get_value(object
, AUTH_XPC_CONTEXT_NAME
);
478 AuthItemSet tempHints
, tempContext
;
479 xpcArrayToAuthItemSet(&tempHints
, xpcHints
);
480 xpcArrayToAuthItemSet(&tempContext
, xpcContext
);
481 mOutHints
= tempHints
;
482 mOutContext
= tempContext
;
483 mLastResult
= xpc_dictionary_get_uint64(object
, AUTH_XPC_REPLY_RESULT_VALUE
);
485 } else if (xpc_get_type(object
) == XPC_TYPE_ERROR
) {
486 if (XPC_ERROR_CONNECTION_INVALID
== object
) {
487 // If the connection drops, return an "auth undefined" result, because we cannot continue
488 } else if (XPC_ERROR_CONNECTION_INTERRUPTED
== object
) {
489 // If the agent dies, return an "auth undefined" result, because we cannot continue
494 xpc_release(hintsArray
);
495 xpc_release(contextArray
);
496 xpc_release(immutableHintsArray
);
497 xpc_release(requestObject
);
502 void SecurityAgentXPCQuery::checkResult()
504 // now check the OSStatus return from the server side
505 switch (mLastResult
) {
506 case kAuthorizationResultAllow
: return;
507 case kAuthorizationResultDeny
:
508 case kAuthorizationResultUserCanceled
: CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED
);
509 default: MacOSError::throwMe(errAuthorizationInternal
);
514 // Perform the "rogue app" access query dialog
516 QueryKeychainUse::QueryKeychainUse(bool needPass
, const Database
*db
)
517 : mPassphraseCheck(NULL
)
519 // if passphrase checking requested, save KeychainDatabase reference
520 // (will quietly disable check if db isn't a keychain)
522 mPassphraseCheck
= dynamic_cast<const KeychainDatabase
*>(db
);
524 setTerminateOnSleep(true);
527 Reason
QueryKeychainUse::queryUser (const char *database
, const char *description
, AclAuthorization action
)
529 Reason reason
= SecurityAgent::noReason
;
532 AuthValueVector arguments
;
533 AuthItemSet hints
, context
;
536 if (getenv("NOSA")) {
537 char answer
[maxPassphraseLength
+10];
539 string applicationPath
;
540 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
541 if (applicationPathItem
)
542 applicationPathItem
->getString(applicationPath
);
544 getNoSA(answer
, sizeof(answer
), "Allow %s to do %d on %s in %s? [yn][g]%s ",
545 applicationPath
.c_str(), int(action
), (description
? description
: "[NULL item]"),
546 (database
? database
: "[NULL database]"),
547 mPassphraseCheck
? ":passphrase" : "");
548 // turn passphrase (no ':') into y:passphrase
549 if (mPassphraseCheck
&& !strchr(answer
, ':')) {
550 memmove(answer
+2, answer
, strlen(answer
)+1);
551 memcpy(answer
, "y:", 2);
554 allow
= answer
[0] == 'y';
555 remember
= answer
[1] == 'g';
556 return SecurityAgent::noReason
;
560 // prepopulate with client hints
561 hints
.insert(mClientHints
.begin(), mClientHints
.end());
563 // put action/operation (sint32) into hints
564 hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
))));
566 // item name into hints
568 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description
? (uint32_t)strlen(description
) : 0, const_cast<char*>(description
))));
570 // keychain name into hints
571 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
? (uint32_t)strlen(database
) : 0, const_cast<char*>(database
))));
573 if (mPassphraseCheck
)
575 create("builtin", "confirm-access-password");
577 CssmAutoData
data(Allocator::standard(Allocator::sensitive
));
582 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
583 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
585 if (retryCount
++ > kMaximumAuthorizationTries
)
587 reason
= SecurityAgent::tooManyTries
;
590 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
591 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
593 setInput(hints
, context
);
596 if (retryCount
> kMaximumAuthorizationTries
)
603 AuthItem
*passwordItem
= mOutContext
.find(kAuthorizationEnvironmentPassword
);
607 passwordItem
->getCssmData(data
);
609 while ((reason
= (const_cast<KeychainDatabase
*>(mPassphraseCheck
)->decode(data
) ? SecurityAgent::noReason
: SecurityAgent::invalidPassphrase
)));
613 create("builtin", "confirm-access");
614 setInput(hints
, context
);
624 // Perform code signature ACL access adjustment dialogs
626 bool QueryCodeCheck::operator () (const char *aclPath
)
629 AuthValueVector arguments
;
630 AuthItemSet hints
, context
;
633 if (getenv("NOSA")) {
636 string applicationPath
;
637 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
638 if (applicationPathItem
)
639 applicationPathItem
->getString(applicationPath
);
641 getNoSA(answer
, sizeof(answer
),
642 "Allow %s to match an ACL for %s [yn][g]? ",
643 applicationPath
.c_str(), aclPath
? aclPath
: "(unknown)");
644 allow
= answer
[0] == 'y';
645 remember
= answer
[1] == 'g';
650 // prepopulate with client hints
651 hints
.insert(mClientHints
.begin(), mClientHints
.end());
653 hints
.insert(AuthItemRef(AGENT_HINT_APPLICATION_PATH
, AuthValueOverlay((uint32_t)strlen(aclPath
), const_cast<char*>(aclPath
))));
655 create("builtin", "code-identity");
657 setInput(hints
, context
);
662 // MacOSError::check(status);
664 return kAuthorizationResultAllow
== mLastResult
;
669 // Obtain passphrases and submit them to the accept() method until it is accepted
670 // or we can't get another passphrase. Accept() should consume the passphrase
671 // if it is accepted. If no passphrase is acceptable, throw out of here.
673 Reason
QueryOld::query()
675 Reason reason
= SecurityAgent::noReason
;
677 AuthValueVector arguments
;
678 AuthItemSet hints
, context
;
679 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
683 // return the passphrase
684 if (getenv("NOSA")) {
685 char passphrase_
[maxPassphraseLength
];
686 getNoSA(passphrase
, maxPassphraseLength
, "Unlock %s [<CR> to cancel]: ", database
.dbName());
687 passphrase
.copy(passphrase_
, strlen(passphrase_
));
688 return database
.decode(passphrase
) ? SecurityAgent::noReason
: SecurityAgent::invalidPassphrase
;
692 // prepopulate with client hints
694 const char *keychainPath
= database
.dbName();
695 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay((uint32_t)strlen(keychainPath
), const_cast<char*>(keychainPath
))));
697 hints
.insert(mClientHints
.begin(), mClientHints
.end());
699 create("builtin", "unlock-keychain");
703 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
704 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
708 if (retryCount
> maxTries
)
710 reason
= SecurityAgent::tooManyTries
;
713 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
714 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
716 setInput(hints
, context
);
719 if (retryCount
> maxTries
)
726 AuthItem
*passwordItem
= mOutContext
.find(kAuthorizationEnvironmentPassword
);
730 passwordItem
->getCssmData(passphrase
);
733 while ((reason
= accept(passphrase
)));
735 return SecurityAgent::noReason
;
740 // Get existing passphrase (unlock) Query
742 Reason
QueryOld::operator () ()
749 // End-classes for old secrets
751 Reason
QueryUnlock::accept(CssmManagedData
&passphrase
)
753 if (safer_cast
<KeychainDatabase
&>(database
).decode(passphrase
))
754 return SecurityAgent::noReason
;
756 return SecurityAgent::invalidPassphrase
;
759 Reason
QueryUnlock::retrievePassword(CssmOwnedData
&passphrase
) {
760 CssmAutoData
pass(Allocator::standard(Allocator::sensitive
));
762 AuthItem
*passwordItem
= mOutContext
.find(kAuthorizationEnvironmentPassword
);
764 return SecurityAgent::invalidPassphrase
;
766 passwordItem
->getCssmData(pass
);
770 return SecurityAgent::noReason
;
773 QueryKeybagPassphrase::QueryKeybagPassphrase(Session
& session
, int32_t tries
) : mSession(session
), mContext(), mRetries(tries
)
775 setTerminateOnSleep(true);
776 mContext
= mSession
.get_current_service_context();
779 Reason
QueryKeybagPassphrase::query()
781 Reason reason
= SecurityAgent::noReason
;
783 AuthValueVector arguments
;
784 AuthItemSet hints
, context
;
785 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
788 // prepopulate with client hints
790 const char *keychainPath
= "iCloud";
791 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay((uint32_t)strlen(keychainPath
), const_cast<char*>(keychainPath
))));
793 hints
.insert(mClientHints
.begin(), mClientHints
.end());
795 create("builtin", "unlock-keychain");
800 currentTry
= retryCount
;
801 if (retryCount
> mRetries
)
803 return SecurityAgent::tooManyTries
;
807 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(currentTry
), ¤tTry
));
808 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
810 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
811 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
813 setInput(hints
, context
);
818 AuthItem
*passwordItem
= mOutContext
.find(kAuthorizationEnvironmentPassword
);
822 passwordItem
->getCssmData(passphrase
);
824 while ((reason
= accept(passphrase
)));
826 return SecurityAgent::noReason
;
829 Reason
QueryKeybagPassphrase::accept(Security::CssmManagedData
& password
)
831 if (service_client_kb_unlock(&mContext
, password
.data(), (int)password
.length()) == 0) {
832 mSession
.keybagSetState(session_keybag_unlocked
);
833 return SecurityAgent::noReason
;
835 return SecurityAgent::invalidPassphrase
;
838 QueryKeybagNewPassphrase::QueryKeybagNewPassphrase(Session
& session
) : QueryKeybagPassphrase(session
) {}
840 Reason
QueryKeybagNewPassphrase::query(CssmOwnedData
&oldPassphrase
, CssmOwnedData
&passphrase
)
842 CssmAutoData
pass(Allocator::standard(Allocator::sensitive
));
843 CssmAutoData
oldPass(Allocator::standard(Allocator::sensitive
));
844 Reason reason
= SecurityAgent::noReason
;
846 AuthValueVector arguments
;
847 AuthItemSet hints
, context
;
850 // prepopulate with client hints
852 const char *keychainPath
= "iCloud";
853 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay((uint32_t)strlen(keychainPath
), const_cast<char*>(keychainPath
))));
855 const char *showResetString
= "YES";
856 hints
.insert(AuthItemRef(AGENT_HINT_SHOW_RESET
, AuthValueOverlay((uint32_t)strlen(showResetString
), const_cast<char*>(showResetString
))));
858 hints
.insert(mClientHints
.begin(), mClientHints
.end());
860 create("builtin", "change-passphrase");
863 AuthItem
*resetPassword
= NULL
;
866 currentTry
= retryCount
;
867 if (retryCount
> mRetries
)
869 return SecurityAgent::tooManyTries
;
873 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(currentTry
), ¤tTry
));
874 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
876 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
877 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
879 setInput(hints
, context
);
884 resetPassword
= mOutContext
.find(AGENT_CONTEXT_RESET_PASSWORD
);
885 if (resetPassword
!= NULL
) {
886 return SecurityAgent::resettingPassword
;
889 AuthItem
*oldPasswordItem
= mOutContext
.find(AGENT_PASSWORD
);
890 if (!oldPasswordItem
)
893 oldPasswordItem
->getCssmData(oldPass
);
895 while ((reason
= accept(oldPass
)));
897 if (reason
== SecurityAgent::noReason
) {
898 AuthItem
*passwordItem
= mOutContext
.find(AGENT_CONTEXT_NEW_PASSWORD
);
900 return SecurityAgent::invalidPassphrase
;
902 passwordItem
->getCssmData(pass
);
904 oldPassphrase
= oldPass
;
908 return SecurityAgent::noReason
;
911 QueryPIN::QueryPIN(Database
&db
)
912 : QueryOld(db
), mPin(Allocator::standard())
914 this->inferHints(Server::process());
918 Reason
QueryPIN::accept(CssmManagedData
&pin
)
920 // no retries for now
922 return SecurityAgent::noReason
;
927 // Obtain passphrases and submit them to the accept() method until it is accepted
928 // or we can't get another passphrase. Accept() should consume the passphrase
929 // if it is accepted. If no passphrase is acceptable, throw out of here.
931 Reason
QueryNewPassphrase::query()
933 Reason reason
= initialReason
;
934 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
935 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
938 AuthValueVector arguments
;
939 AuthItemSet hints
, context
;
944 if (getenv("NOSA")) {
945 char passphrase_
[maxPassphraseLength
];
946 getNoSA(passphrase_
, maxPassphraseLength
,
947 "New passphrase for %s (reason %d) [<CR> to cancel]: ",
948 database
.dbName(), reason
);
949 return SecurityAgent::noReason
;
953 // prepopulate with client hints
954 hints
.insert(mClientHints
.begin(), mClientHints
.end());
956 // keychain name into hints
957 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
.dbName())));
959 switch (initialReason
)
961 case SecurityAgent::newDatabase
:
962 create("builtin", "new-passphrase");
964 case SecurityAgent::changePassphrase
:
965 create("builtin", "change-passphrase");
973 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
974 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
976 if (++retryCount
> maxTries
)
978 reason
= SecurityAgent::tooManyTries
;
981 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
982 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
984 setInput(hints
, context
);
987 if (retryCount
> maxTries
)
994 if (SecurityAgent::changePassphrase
== initialReason
)
996 AuthItem
*oldPasswordItem
= mOutContext
.find(AGENT_PASSWORD
);
997 if (!oldPasswordItem
)
1000 oldPasswordItem
->getCssmData(oldPassphrase
);
1003 AuthItem
*passwordItem
= mOutContext
.find(AGENT_CONTEXT_NEW_PASSWORD
);
1007 passwordItem
->getCssmData(passphrase
);
1010 while ((reason
= accept(passphrase
, (initialReason
== SecurityAgent::changePassphrase
) ? &oldPassphrase
.get() : NULL
)));
1012 return SecurityAgent::noReason
;
1017 // Get new passphrase Query
1019 Reason
QueryNewPassphrase::operator () (CssmOwnedData
&oldPassphrase
, CssmOwnedData
&passphrase
)
1021 if (Reason result
= query())
1022 return result
; // failed
1023 passphrase
= mPassphrase
;
1024 oldPassphrase
= mOldPassphrase
;
1025 return SecurityAgent::noReason
; // success
1028 Reason
QueryNewPassphrase::accept(CssmManagedData
&passphrase
, CssmData
*oldPassphrase
)
1030 //@@@ acceptance criteria are currently hardwired here
1031 //@@@ This validation presumes ASCII - UTF8 might be more lenient
1033 // if we have an old passphrase, check it
1034 if (oldPassphrase
&& !safer_cast
<KeychainDatabase
&>(database
).validatePassphrase(*oldPassphrase
))
1035 return SecurityAgent::oldPassphraseWrong
;
1037 // sanity check the new passphrase (but allow user override)
1038 if (!(mPassphraseValid
&& passphrase
.get() == mPassphrase
)) {
1039 mPassphrase
= passphrase
;
1040 if (oldPassphrase
) mOldPassphrase
= *oldPassphrase
;
1041 mPassphraseValid
= true;
1042 if (mPassphrase
.length() == 0)
1043 return SecurityAgent::passphraseIsNull
;
1044 if (mPassphrase
.length() < 6)
1045 return SecurityAgent::passphraseTooSimple
;
1049 return SecurityAgent::noReason
;
1053 // Get a passphrase for unspecified use
1055 Reason
QueryGenericPassphrase::operator () (const CssmData
*prompt
, bool verify
,
1058 return query(prompt
, verify
, passphrase
);
1061 Reason
QueryGenericPassphrase::query(const CssmData
*prompt
, bool verify
,
1064 Reason reason
= SecurityAgent::noReason
;
1065 OSStatus status
; // not really used; remove?
1066 AuthValueVector arguments
;
1067 AuthItemSet hints
, context
;
1070 if (getenv("NOSA")) {
1072 return SecurityAgent::noReason
;
1076 hints
.insert(mClientHints
.begin(), mClientHints
.end());
1077 hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt
? (UInt32
)prompt
->length() : 0, prompt
? prompt
->data() : NULL
)));
1078 // XXX/gh defined by dmitch but no analogous hint in
1079 // AuthorizationTagsPriv.h:
1080 // CSSM_ATTRIBUTE_ALERT_TITLE (optional alert panel title)
1082 if (false == verify
) { // import
1083 create("builtin", "generic-unlock");
1084 } else { // verify passphrase (export)
1085 // new-passphrase-generic works with the pre-4 June 2004 agent;
1086 // generic-new-passphrase is required for the new agent
1087 create("builtin", "generic-new-passphrase");
1090 AuthItem
*passwordItem
;
1093 setInput(hints
, context
);
1096 passwordItem
= mOutContext
.find(AGENT_PASSWORD
);
1098 } while (!passwordItem
);
1100 passwordItem
->getString(passphrase
);
1107 // Get a DB blob's passphrase--keychain synchronization
1109 Reason
QueryDBBlobSecret::operator () (DbHandle
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle
*dbHandleAuthenticated
)
1111 return query(dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
);
1114 Reason
QueryDBBlobSecret::query(DbHandle
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle
*dbHandleAuthenticated
)
1116 Reason reason
= SecurityAgent::noReason
;
1117 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
1118 OSStatus status
; // not really used; remove?
1119 AuthValueVector arguments
;
1120 AuthItemSet hints
/*NUKEME*/, context
;
1123 if (getenv("NOSA")) {
1124 // FIXME akin to 3690984
1125 return SecurityAgent::noReason
;
1129 hints
.insert(mClientHints
.begin(), mClientHints
.end());
1130 create("builtin", "generic-unlock-kcblob");
1132 AuthItem
*secretItem
;
1137 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
1138 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
1140 if (++retryCount
> maxTries
)
1142 reason
= SecurityAgent::tooManyTries
;
1145 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
1146 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
1148 setInput(hints
, context
);
1151 secretItem
= mOutContext
.find(AGENT_PASSWORD
);
1154 secretItem
->getCssmData(passphrase
);
1156 } while ((reason
= accept(passphrase
, dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
)));
1161 Reason
QueryDBBlobSecret::accept(CssmManagedData
&passphrase
,
1162 DbHandle
*dbHandlesToAuthenticate
, uint8 dbHandleCount
, DbHandle
*dbHandleAuthenticated
)
1164 DbHandle
*currHdl
= dbHandlesToAuthenticate
;
1166 Boolean authenticated
= false;
1167 for (index
=0; index
< dbHandleCount
&& !authenticated
; index
++)
1171 RefPointer
<KeychainDatabase
> dbToUnlock
= Server::keychain(*currHdl
);
1172 dbToUnlock
->unlockDb(passphrase
);
1173 authenticated
= true;
1174 *dbHandleAuthenticated
= *currHdl
; // return the DbHandle that 'passphrase' authenticated with.
1176 catch (const CommonError
&err
)
1178 currHdl
++; // we failed to authenticate with this one, onto the next one.
1181 if ( !authenticated
)
1182 return SecurityAgent::invalidPassphrase
;
1184 return SecurityAgent::noReason
;
1187 // @@@ no pluggable authentication possible!
1189 QueryKeychainAuth::operator () (const char *database
, const char *description
, AclAuthorization action
, const char *prompt
)
1191 Reason reason
= SecurityAgent::noReason
;
1192 AuthItemSet hints
, context
;
1193 AuthValueVector arguments
;
1198 using CommonCriteria::Securityd::KeychainAuthLogger
;
1199 KeychainAuthLogger
logger(mAuditToken
, AUE_ssauthint
, database
, description
);
1202 /* XXX/gh probably not complete; stolen verbatim from rogue-app query */
1203 if (getenv("NOSA")) {
1204 char answer
[maxPassphraseLength
+10];
1206 string applicationPath
;
1207 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
1208 if (applicationPathItem
)
1209 applicationPathItem
->getString(applicationPath
);
1211 getNoSA(answer
, sizeof(answer
), "Allow %s to do %d on %s in %s? [yn][g]%s ",
1212 applicationPath
.c_str(), int(action
), (description
? description
: "[NULL item]"),
1213 (database
? database
: "[NULL database]"),
1214 mPassphraseCheck
? ":passphrase" : "");
1215 // turn passphrase (no ':') into y:passphrase
1216 if (mPassphraseCheck
&& !strchr(answer
, ':')) {
1217 memmove(answer
+2, answer
, strlen(answer
)+1);
1218 memcpy(answer
, "y:", 2);
1221 allow
= answer
[0] == 'y';
1222 remember
= answer
[1] == 'g';
1223 return SecurityAgent::noReason
;
1227 hints
.insert(mClientHints
.begin(), mClientHints
.end());
1229 // put action/operation (sint32) into hints
1230 hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
))));
1232 hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt
? (uint32_t)strlen(prompt
) : 0, const_cast<char*>(prompt
))));
1234 // item name into hints
1235 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description
? (uint32_t)strlen(description
) : 0, const_cast<char*>(description
))));
1237 // keychain name into hints
1238 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
? (uint32_t)strlen(database
) : 0, const_cast<char*>(database
))));
1240 create("builtin", "confirm-access-user-password");
1242 AuthItem
*usernameItem
;
1243 AuthItem
*passwordItem
;
1247 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
1248 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
1250 if (++retryCount
> maxTries
)
1251 reason
= SecurityAgent::tooManyTries
;
1253 if (SecurityAgent::noReason
!= reason
)
1255 if (SecurityAgent::tooManyTries
== reason
)
1256 logger
.logFailure(NULL
, CommonCriteria::errTooManyTries
);
1258 logger
.logFailure();
1261 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
1262 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
1264 setInput(hints
, context
);
1270 catch (...) // user probably clicked "deny"
1272 logger
.logFailure();
1275 usernameItem
= mOutContext
.find(AGENT_USERNAME
);
1276 passwordItem
= mOutContext
.find(AGENT_PASSWORD
);
1277 if (!usernameItem
|| !passwordItem
)
1279 usernameItem
->getString(username
);
1280 passwordItem
->getString(password
);
1281 } while ((reason
= accept(username
, password
)));
1283 if (SecurityAgent::noReason
== reason
)
1284 logger
.logSuccess();
1285 // else we logged the denial in the loop
1291 QueryKeychainAuth::accept(string
&username
, string
&passphrase
)
1293 // Note: QueryKeychainAuth currently requires that the
1294 // specified user be in the admin group. If this requirement
1295 // ever needs to change, the group name should be passed as
1296 // a separate argument to this method.
1298 const char *user
= username
.c_str();
1299 const char *passwd
= passphrase
.c_str();
1300 int checkpw_status
= checkpw(user
, passwd
);
1302 if (checkpw_status
!= CHECKPW_SUCCESS
) {
1303 return SecurityAgent::invalidPassphrase
;
1306 const char *group
= "admin";
1309 uuid_t group_uuid
, user_uuid
;
1310 rc
= mbr_group_name_to_uuid(group
, group_uuid
);
1311 if (rc
) { return SecurityAgent::userNotInGroup
; }
1313 rc
= mbr_user_name_to_uuid(user
, user_uuid
);
1314 if (rc
) { return SecurityAgent::userNotInGroup
; }
1316 rc
= mbr_check_membership(user_uuid
, group_uuid
, &ismember
);
1317 if (rc
|| !ismember
) { return SecurityAgent::userNotInGroup
; }
1320 return SecurityAgent::noReason
;