2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 // SecurityAgentClient - client interface to SecurityAgent
22 // This file changes behavior depending on two environment variables.
23 // AGENTNAME/AGENTPATH: if defined, is the name and path to
24 // the SecurityAgent binary to autolaunch. If undefined, SecurityAgent must be running.
25 // NOSA: If set, check for NOSA environment variable and if set, simulate the Agent
26 // using stdio in the client.
28 // A note on message flow: the combined send/receive operation at the heart of each
29 // secagent_client_* call can receive three types of message:
30 // (o) SecurityAgent reply -- ok, process
31 // (o) Dead port notification -- agent died, translated to NO_USER_INTERACTION error thrown
32 // (o) Cancel message -- will come out as INVALID_ID error and throw
34 // @@@ SA keepalive option.
36 #include "SecurityAgentClient.h"
38 #include <Security/utilities.h>
39 #include <Security/Authorization.h>
42 #include <mach/mach_error.h>
44 #include <Security/debugging.h>
46 #include <sys/syslimits.h>
49 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
50 #include <Security/ktracecodes.h>
52 #include <sys/types.h>
55 // @@@ Should be in <time.h> but it isn't as of Puma5F22
56 extern "C" int nanosleep(const struct timespec
*rqtp
, struct timespec
*rmtp
);
59 namespace SecurityAgent
{
62 using namespace Security
;
63 using namespace MachPlusPlus
;
66 // pass structured arguments in/out of IPC calls. See "data walkers" for details
67 #define COPY(copy) copy, copy.length(), copy
68 #define COPY_OUT(copy) ©, ©##Length, ©##Base
69 #define COPY_OUT_DECL(type,name) type *name, *name##Base; mach_msg_type_number_t name##Length
77 Requestor(const OSXCode
*code
) { if (code
) extForm
= code
->encode(); }
78 operator const char * () const { return extForm
.c_str(); }
80 // use this for debugging only
81 const char *c_str() const { return extForm
.empty() ? "(unknown)" : extForm
.c_str(); }
89 // Check a return from a MIG client call
91 void Client::check(kern_return_t error
)
93 // first check the Mach IPC return code
95 case KERN_SUCCESS
: // peachy
97 case MIG_SERVER_DIED
: // explicit can't-send-it's-dead
99 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
100 default: // some random Mach error
102 MachPlusPlus::Error::throwMe(error
);
105 // now check the OSStatus return from the server side
108 case errAuthorizationDenied
:
110 case userCanceledErr
:
112 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED
);
115 MacOSError::throwMe(status
);
119 void Client::unstage()
121 if (stage
!= mainStage
) {
122 mStagePort
.deallocate();
129 // NOSA support functions. This is a test mode where the SecurityAgent
130 // is simulated via stdio in the client. Good for running automated tests
131 // of client programs. Only available if -DNOSA when compiling.
137 static void getNoSA(char *buffer
, size_t bufferSize
, const char *fmt
, ...)
142 vfprintf(stdout
, fmt
, args
);
146 memset(buffer
, 0, bufferSize
);
147 const char *nosa
= getenv("NOSA");
148 if (!strcmp(nosa
, "-")) {
149 if (fgets(buffer
, bufferSize
-1, stdin
) == NULL
)
150 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
151 buffer
[strlen(buffer
)-1] = '\0'; // remove trailing newline
152 if (!isatty(fileno(stdin
)))
153 printf("%s\n", buffer
); // echo to output if input not terminal
155 strncpy(buffer
, nosa
, bufferSize
-1);
156 printf("%s\n", buffer
);
158 if (buffer
[0] == '\0') // empty input -> cancellation
159 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED
);
166 // Initialize our CSSM interface
168 Client::Client() : mActive(false), desktopUid(0), mKeepAlive(false), stage(mainStage
), mAgentName("com.apple.SecurityAgent")
173 * The new, preferred way to activate the Security Agent. The Security
174 * Server will take advantage of this interface; the old constructor is
175 * kept around for compatibility with the only other client, DiskCopy.
176 * DiskCopy needs to be fixed to use the Security Server itself rather
179 Client::Client(uid_t clientUID
, Bootstrap clientBootstrap
, const char *agentName
) :
180 mActive(false), desktopUid(clientUID
),
181 mClientBootstrap(clientBootstrap
), mKeepAlive(false), stage(mainStage
), mAgentName(agentName
)
192 // Activate a session
194 void Client::activate()
201 mClientPort
.allocate(MACH_PORT_RIGHT_RECEIVE
);
202 mClientPort
.insertRight(MACH_MSG_TYPE_MAKE_SEND
);
204 // get notified if the server dies (shouldn't happen, but...)
205 mServerPort
.requestNotify(mClientPort
, MACH_NOTIFY_DEAD_NAME
, true);
212 void Client::terminate()
215 mServerPort
.deallocate();
216 mClientPort
.destroy();
223 // Cancel a client call.
224 // This actually sends a reply message to the thread waiting for a reply,
225 // thereby unblocking it.
226 // @@@ Theoretically we should thread-lock this so only one cancel message
227 // ever gets sent. But right now, this is only used to completely tear down
228 // a client session, so duplicate replies don't bother us.
230 void Client::cancel()
232 // this is the common prefix of SecurityAgent client call replies
234 mach_msg_header_t Head
;
236 kern_return_t result
;
240 request
.Head
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND
, 0);
241 request
.Head
.msgh_remote_port
= mClientPort
;
242 request
.Head
.msgh_local_port
= MACH_PORT_NULL
;
243 request
.Head
.msgh_id
= cancelMessagePseudoID
;
244 request
.NDR
= NDR_record
;
246 // set call succeeded, no error status
247 request
.result
= KERN_SUCCESS
;
248 request
.status
= noErr
;
250 // send it (do not receive a reply). Use zero timeout to avoid hangs
251 MachPlusPlus::check(mach_msg_overwrite(&request
.Head
, MACH_SEND_MSG
|MACH_SEND_TIMEOUT
,
252 sizeof(request
), 0, MACH_PORT_NULL
, MACH_MSG_TIMEOUT_NONE
,
253 MACH_PORT_NULL
, (mach_msg_header_t
*) NULL
, 0));
258 // Get the port for the SecurityAgent.
259 // Start it if necessary (and possible). Throw an exception if we can't get to it.
260 // Sets mServerPort on success.
262 void Client::establishServer()
264 // if the server is already running, we're done
265 if (mServerPort
= mClientBootstrap
.lookupOptional(mAgentName
.c_str()))
268 #if defined(AGENTNAME) && defined(AGENTPATH)
269 // switch the bootstrap port to that of the logged-in user
271 StBootstrap
bootSaver(mClientBootstrap
);
273 // try to start the agent
274 switch (pid_t pid
= fork()) {
277 // Setup the environment for the SecurityAgent
282 // tell agent which name to register
283 setenv("AGENTNAME", mAgentName
.c_str(), 1);
285 if (desktopUid
) // if the user is running as root, or we're not told what uid to use, we stick with what we are
287 struct group
*grent
= getgrnam("nobody");
288 gid_t desktopGid
= grent
? grent
->gr_gid
: unsigned(-2); //@@@ questionable
290 secdebug("SAclnt", "setgid(%d)", desktopGid
);
291 setgid(desktopGid
); // switch to login-user gid
292 secdebug("SAclnt", "setuid(%d)", desktopUid
);
293 // Must be setuid and not seteuid since we do not want the agent to be able
294 // to call seteuid(0) successfully.
295 setuid(desktopUid
); // switch to login-user uid
297 // close down any files that might have been open at this point
298 int maxDescriptors
= getdtablesize ();
301 for (i
= 3; i
< maxDescriptors
; ++i
)
306 // construct path to SecurityAgent
307 char agentExecutable
[PATH_MAX
+ 1];
308 const char *path
= getenv("SECURITYAGENT");
311 snprintf(agentExecutable
, sizeof(agentExecutable
), "%s/Contents/MacOS/" AGENTNAME
, path
);
312 secdebug("SAclnt", "execl(%s)", agentExecutable
);
313 execl(agentExecutable
, agentExecutable
, NULL
);
314 secdebug("SAclnt", "execl of SecurityAgent failed, errno=%d", errno
);
316 // Unconditional suicide follows.
317 // See comments below on why we can't use abort()
321 // NOTE: OS X abort() is implemented as kill(getuid()), which fails
322 // for a setuid-root process that has setuid'd. Go back to root to die...
327 case -1: // error (in parent)
328 UnixError::throwMe();
331 static const int timeout
= 300;
333 secdebug("SAclnt", "Starting security agent (%d seconds timeout)", timeout
);
334 struct timespec rqtp
;
335 memset(&rqtp
, 0, sizeof(rqtp
));
336 rqtp
.tv_nsec
= 100000000; /* 10^8 nanaseconds = 1/10th of a second */
337 for (int n
= timeout
; n
> 0; nanosleep(&rqtp
, NULL
), n
--) {
338 if (mServerPort
= mClientBootstrap
.lookupOptional(mAgentName
.c_str()))
341 switch (IFDEBUG(pid_t rc
=) waitpid(pid
, &status
, WNOHANG
)) {
342 case 0: // child still running
347 case EAGAIN
: // transient
349 case ECHILD
: // no such child (dead; already reaped elsewhere)
350 secdebug("SAclnt", "child is dead (reaped elsewhere)");
351 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
353 secdebug("SAclnt", "waitpid failed: errno=%d", errno
);
354 UnixError::throwMe();
358 secdebug("SAclnt", "child died without claiming the SecurityAgent port");
359 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
363 if (mServerPort
== 0) { // couldn't contact Security Agent
364 secdebug("SAclnt", "Autolaunch failed");
365 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
367 secdebug("SAclnt", "SecurityAgent located");
373 // well, this didn't work. Too bad
374 secdebug("SAclnt", "Cannot contact SecurityAgent");
375 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
); //@@@ or INTERNAL_ERROR?
380 // Staged query maintainance
382 void Client::finishStagedQuery()
384 if (stage
== mainStage
)
385 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
387 if (getenv("NOSA")) {
388 printf(" [query done]\n");
393 check(secagent_client_finishStagedQuery(mStagePort
, mClientPort
, &status
));
398 void Client::cancelStagedQuery(Reason reason
)
400 if (stage
== mainStage
)
401 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
403 if (getenv("NOSA")) {
404 printf(" [query canceled; reason=%d]\n", reason
);
409 check(secagent_client_cancelStagedQuery(mStagePort
, mClientPort
, &status
, reason
));
416 // Query the user to unlock a keychain. This is a staged protocol with a private side-port.
418 void Client::queryUnlockDatabase(const OSXCode
*requestor
, pid_t requestPid
,
419 const char *database
, char passphrase
[maxPassphraseLength
])
421 Requestor
req(requestor
);
424 if (getenv("NOSA")) {
425 getNoSA(passphrase
, maxPassphraseLength
, "Unlock %s [<CR> to cancel]: ", database
);
431 check(secagent_client_unlockDatabase(mServerPort
, mClientPort
,
432 &status
, req
, requestPid
, database
, &mStagePort
.port(), passphrase
));
436 void Client::retryUnlockDatabase(Reason reason
, char passphrase
[maxPassphraseLength
])
438 if (stage
!= unlockStage
)
439 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
441 if (getenv("NOSA")) {
442 getNoSA(passphrase
, maxPassphraseLength
, "Retry unlock [<CR> to cancel]: ");
446 check(secagent_client_retryUnlockDatabase(mStagePort
, mClientPort
,
447 &status
, reason
, passphrase
));
452 // Ask for a (new) password for something.
454 void Client::queryNewPassphrase(const OSXCode
*requestor
, pid_t requestPid
,
455 const char *database
, Reason reason
, char passphrase
[maxPassphraseLength
], char oldPassphrase
[maxPassphraseLength
])
457 Requestor
req(requestor
);
460 if (getenv("NOSA")) {
461 getNoSA(passphrase
, maxPassphraseLength
,
462 "New passphrase for %s (reason %d) [<CR> to cancel]: ",
463 (database
? database
: "[NULL database]"), reason
);
464 stage
= newPassphraseStage
;
469 check(secagent_client_queryNewPassphrase(mServerPort
, mClientPort
,
470 &status
, req
, requestPid
, database
, reason
,
471 &mStagePort
.port(), passphrase
, oldPassphrase
));
472 stage
= newPassphraseStage
;
475 void Client::retryNewPassphrase(Reason reason
, char passphrase
[maxPassphraseLength
], char oldPassphrase
[maxPassphraseLength
])
477 if (stage
!= newPassphraseStage
)
478 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
480 if (getenv("NOSA")) {
481 getNoSA(passphrase
, maxPassphraseLength
,
482 "retry new passphrase (reason %d) [<CR> to cancel]: ", reason
);
486 check(secagent_client_retryNewPassphrase(mStagePort
, mClientPort
,
487 &status
, reason
, passphrase
, oldPassphrase
));
492 // Ask the user permission to use an item.
493 // This is used by the keychain-style ACL subject type (only).
495 void Client::queryKeychainAccess(const OSXCode
*requestor
, pid_t requestPid
,
496 const char *database
, const char *itemName
, AclAuthorization action
,
497 bool needPassphrase
, KeychainChoice
&choice
)
499 Debug::trace (kSecTraceSecurityServerQueryKeychainAccess
);
501 Requestor
req(requestor
);
504 if (getenv("NOSA")) {
505 char answer
[maxPassphraseLength
+10];
506 getNoSA(answer
, sizeof(answer
), "Allow %s to do %d on %s in %s? [yn][g]%s ",
507 req
.c_str(), int(action
), (itemName
? itemName
: "[NULL item]"),
508 (database
? database
: "[NULL database]"),
509 needPassphrase
? ":passphrase" : "");
510 // turn passphrase (no ':') into y:passphrase
511 if (needPassphrase
&& !strchr(answer
, ':')) {
512 memmove(answer
+2, answer
, strlen(answer
)+1);
513 memcpy(answer
, "y:", 2);
515 choice
.allowAccess
= answer
[0] == 'y';
516 choice
.continueGrantingToCaller
= answer
[1] == 'g';
517 if (const char *colon
= strchr(answer
, ':'))
518 strncpy(choice
.passphrase
, colon
+1, maxPassphraseLength
);
520 choice
.passphrase
[0] = '\0';
525 check(secagent_client_queryKeychainAccess(mServerPort
, mClientPort
,
526 &status
, req
, requestPid
, (database
? database
: ""), itemName
, action
,
527 needPassphrase
, &mStagePort
.port(), &choice
));
529 stage
= queryKeychainAccessStage
;
533 void Client::retryQueryKeychainAccess (Reason reason
, Choice
&choice
)
535 if (stage
!= queryKeychainAccessStage
)
536 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
538 check(secagent_client_retryQueryKeychainAccess (mStagePort
, mClientPort
, &status
, reason
, &choice
));
544 // Ask the user whether a somewhat (but not cleanly) matching code identity
545 // should be accepted for access control purposes.
547 void Client::queryCodeIdentity(const OSXCode
*requestor
, pid_t requestPid
,
548 const char *aclPath
, KeychainChoice
&choice
)
550 Requestor
req(requestor
);
553 if (getenv("NOSA")) {
555 getNoSA(answer
, sizeof(answer
),
556 "Allow %s to match an ACL for %s [yn][g]? ",
557 req
.c_str(), aclPath
? aclPath
: "(unknown)");
558 choice
.allowAccess
= answer
[0] == 'y';
559 choice
.continueGrantingToCaller
= answer
[1] == 'g';
564 check(secagent_client_queryCodeIdentity(mServerPort
, mClientPort
,
565 &status
, req
, requestPid
, aclPath
, &choice
));
571 // Query the user for a generic existing passphrase, with selectable prompt.
573 void Client::queryOldGenericPassphrase(const OSXCode
*requestor
, pid_t requestPid
,
575 KeychainBox
&addToKeychain
, char passphrase
[maxPassphraseLength
])
577 Requestor
req(requestor
);
580 if (getenv("NOSA")) {
581 getNoSA(passphrase
, maxPassphraseLength
,
582 "Old passphrase (\"%s\") [<CR> to cancel]: ", prompt
);
583 // @@@ addToKeychain not hooked up; stays unchanged
584 stage
= oldGenericPassphraseStage
;
589 MigBoolean addBox
= addToKeychain
.setting
;
590 check(secagent_client_queryOldGenericPassphrase(mServerPort
, mClientPort
,
591 &status
, req
, requestPid
, prompt
, &mStagePort
.port(),
592 addToKeychain
.show
, &addBox
, passphrase
));
593 addToKeychain
.setting
= addBox
;
594 stage
= oldGenericPassphraseStage
;
597 void Client::retryOldGenericPassphrase(Reason reason
,
598 bool &addToKeychain
, char passphrase
[maxPassphraseLength
])
600 if (stage
!= oldGenericPassphraseStage
)
601 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
603 if (getenv("NOSA")) {
604 getNoSA(passphrase
, maxPassphraseLength
,
605 "Retry old passphrase [<CR> to cancel]: ");
610 check(secagent_client_retryOldGenericPassphrase(mStagePort
, mClientPort
,
611 &status
, reason
, &addBox
, passphrase
));
612 addToKeychain
= addBox
;
617 // Ask for a new passphrase for something (with selectable prompt).
619 void Client::queryNewGenericPassphrase(const OSXCode
*requestor
, pid_t requestPid
,
620 const char *prompt
, Reason reason
,
621 KeychainBox
&addToKeychain
, char passphrase
[maxPassphraseLength
])
623 Requestor
req(requestor
);
626 if (getenv("NOSA")) {
627 getNoSA(passphrase
, maxPassphraseLength
,
628 "New passphrase (\"%s\") (reason %d) [<CR> to cancel]: ",
630 // @@@ addToKeychain not hooked up; stays unchanged
631 stage
= newGenericPassphraseStage
;
636 MigBoolean addBox
= addToKeychain
.setting
;
637 check(secagent_client_queryNewGenericPassphrase(mServerPort
, mClientPort
,
638 &status
, req
, requestPid
, prompt
, reason
,
639 &mStagePort
.port(), addToKeychain
.show
, &addBox
, passphrase
));
640 addToKeychain
.setting
= addBox
;
641 stage
= newGenericPassphraseStage
;
644 void Client::retryNewGenericPassphrase(Reason reason
,
645 bool &addToKeychain
, char passphrase
[maxPassphraseLength
])
647 if (stage
!= newGenericPassphraseStage
)
648 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
650 if (getenv("NOSA")) {
651 getNoSA(passphrase
, maxPassphraseLength
,
652 "retry new passphrase (reason %d) [<CR> to cancel]: ", reason
);
657 check(secagent_client_retryNewGenericPassphrase(mStagePort
, mClientPort
,
658 &status
, reason
, &addBox
, passphrase
));
659 addToKeychain
= addBox
;
664 // Authorization by authentication
666 bool Client::authorizationAuthenticate(const OSXCode
*requestor
, pid_t requestPid
,
667 const char *neededGroup
, const char *candidateUser
,
668 char user
[maxUsernameLength
], char passphrase
[maxPassphraseLength
])
670 Requestor
req(requestor
);
673 if (getenv("NOSA")) {
674 getNoSA(user
, maxUsernameLength
,
675 "User to authenticate for group %s (try \"%s\" [\"-\" to deny]): ",
676 neededGroup
, (candidateUser
? candidateUser
: "[NULL]"));
677 if (strcmp(user
, "-"))
678 getNoSA(passphrase
, maxPassphraseLength
,
679 "Passphrase for user %s: ", user
);
680 stage
= authorizeStage
;
681 return strcmp(user
, "-");
685 check(secagent_client_authorizationAuthenticate(mServerPort
, mClientPort
,
686 &status
, req
, requestPid
, neededGroup
, candidateUser
, &mStagePort
.port(), user
, passphrase
));
687 stage
= authorizeStage
;
688 return status
== noErr
;
691 bool Client::retryAuthorizationAuthenticate(Reason reason
, char user
[maxUsernameLength
],
692 char passphrase
[maxPassphraseLength
])
694 if (stage
!= authorizeStage
)
695 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
697 if (getenv("NOSA")) {
698 getNoSA(user
, maxUsernameLength
,
699 "Retry authenticate (reason=%d) ([\"-\" to deny again]): ", reason
);
700 if (strcmp(user
, "-"))
701 getNoSA(passphrase
, maxPassphraseLength
,
702 "Passphrase for user %s: ", user
);
703 return strcmp(user
, "-");
706 check(secagent_client_retryAuthorizationAuthenticate(mStagePort
, mClientPort
,
707 &status
, reason
, user
, passphrase
));
708 return status
== noErr
;
712 // invokeMechanism old style
714 bool Client::invokeMechanism(const string
&inPluginId
, const string
&inMechanismId
, const AuthValueVector
&inArguments
, AuthItemSet
&inHints
, AuthItemSet
&inContext
, AuthorizationResult
*outResult
)
716 AuthorizationValueVector
*inArgumentVector
;
717 AuthorizationItemSet
*inHintsSet
, *inContextSet
;
718 size_t inArgumentVectorLength
, inHintsSetLength
, inContextSetLength
;
720 CssmAllocator
&alloc
= CssmAllocator::standard();
721 inArguments
.copy(&inArgumentVector
, &inArgumentVectorLength
);
722 CssmAutoPtr
<AuthorizationValueVector
> argGuard(alloc
, inArgumentVector
);
723 inHints
.copy(inHintsSet
, inHintsSetLength
, alloc
);
724 CssmAutoPtr
<AuthorizationItemSet
> hintGuard(alloc
, inHintsSet
);
725 inContext
.copy(inContextSet
, inContextSetLength
, alloc
);
726 CssmAutoPtr
<AuthorizationItemSet
> contextGuard(alloc
, inContextSet
);
728 COPY_OUT_DECL(AuthorizationItemSet
, outHintsSet
);
729 COPY_OUT_DECL(AuthorizationItemSet
, outContextSet
);
733 // either noErr (user cancel, allow) or throws authInternal
735 check(secagent_client_invokeMechanism(mServerPort
, mClientPort
,
736 &status
, &mStagePort
.port(), inPluginId
.c_str(), inMechanismId
.c_str(),
737 inArgumentVector
, inArgumentVectorLength
, inArgumentVector
,
738 inHintsSet
, inHintsSetLength
, inHintsSet
,
739 inContextSet
, inContextSetLength
, inContextSet
,
741 COPY_OUT(outHintsSet
),
742 COPY_OUT(outContextSet
)));
744 VMGuard
_(outHintsSet
, outHintsSetLength
);
745 VMGuard
_2(outContextSet
, outContextSetLength
);
747 if (status
!= errAuthorizationDenied
)
749 relocate(outHintsSet
, outHintsSetBase
);
750 inHints
= *outHintsSet
;
751 relocate(outContextSet
, outContextSetBase
);
752 inContext
= *outContextSet
;
755 return (status
== noErr
);
759 void Client::terminateAgent()
761 // If the agent is (still) running, kill it
762 if (mClientBootstrap
.lookupOptional(mAgentName
.c_str()))
765 check(secagent_client_terminate(mServerPort
, mClientPort
));
769 } // end namespace SecurityAgent
770 } // end namespace Security