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>
51 // @@@ Should be in <time.h> but it isn't as of Puma5F22
52 extern "C" int nanosleep(const struct timespec
*rqtp
, struct timespec
*rmtp
);
55 namespace SecurityAgent
{
58 using namespace Security
;
59 using namespace MachPlusPlus
;
62 // pass structured arguments in/out of IPC calls. See "data walkers" for details
63 #define COPY(copy) copy, copy.length(), copy
64 #define COPY_OUT(copy) ©, ©##Length, ©##Base
65 #define COPY_OUT_DECL(type,name) type *name, *name##Base; mach_msg_type_number_t name##Length
73 Requestor(const OSXCode
*code
) { if (code
) extForm
= code
->encode(); }
74 operator const char * () const { return extForm
.c_str(); }
82 // Check a return from a MIG client call
84 void Client::check(kern_return_t error
)
86 // first check the Mach IPC return code
88 case KERN_SUCCESS
: // peachy
90 case MIG_SERVER_DIED
: // explicit can't-send-it's-dead
92 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
93 default: // some random Mach error
95 MachPlusPlus::Error::throwMe(error
);
98 // now check the OSStatus return from the server side
101 case errAuthorizationDenied
:
103 case userCanceledErr
:
105 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED
);
108 MacOSError::throwMe(status
);
112 void Client::unstage()
114 if (stage
!= mainStage
) {
115 mStagePort
.deallocate();
122 // NOSA support functions. This is a test mode where the SecurityAgent
123 // is simulated via stdio in the client. Good for running automated tests
124 // of client programs. Only available if -DNOSA when compiling.
130 static void getNoSA(char *buffer
, size_t bufferSize
, const char *fmt
, ...)
135 vfprintf(stdout
, fmt
, args
);
139 memset(buffer
, 0, bufferSize
);
140 const char *nosa
= getenv("NOSA");
141 if (!strcmp(nosa
, "-")) {
142 if (fgets(buffer
, bufferSize
-1, stdin
) == NULL
)
143 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
144 buffer
[strlen(buffer
)-1] = '\0'; // remove trailing newline
145 if (!isatty(fileno(stdin
)))
146 printf("%s\n", buffer
); // echo to output if input not terminal
148 strncpy(buffer
, nosa
, bufferSize
-1);
149 printf("%s\n", buffer
);
151 if (buffer
[0] == '\0') // empty input -> cancellation
152 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED
);
159 // Initialize our CSSM interface
161 Client::Client() : mActive(false), mUsePBS(true), mKeepAlive(false), stage(mainStage
)
166 * The new, preferred way to activate the Security Agent. The Security
167 * Server will take advantage of this interface; the old constructor is
168 * kept around for compatibility with the only other client, DiskCopy.
169 * DiskCopy needs to be fixed to use the Security Server itself rather
172 Client::Client(uid_t clientUID
, Bootstrap clientBootstrap
) :
173 mActive(false), desktopUid(clientUID
), mUsePBS(false),
174 mClientBootstrap(clientBootstrap
), mKeepAlive(false), stage(mainStage
)
177 debug("SAclnt", "Desktop: uid %d, gid %d", desktopUid
, desktopGid
);
187 // Activate a session
189 void Client::activate(const char *name
)
192 establishServer(name
? name
: "SecurityAgent");
195 mClientPort
.allocate(MACH_PORT_RIGHT_RECEIVE
);
196 mClientPort
.insertRight(MACH_MSG_TYPE_MAKE_SEND
);
198 // get notified if the server dies (shouldn't happen, but...)
199 mServerPort
.requestNotify(mClientPort
, MACH_NOTIFY_DEAD_NAME
, true);
206 void Client::terminate()
209 mServerPort
.deallocate();
210 mClientPort
.destroy();
217 // Cancel a client call.
218 // This actually sends a reply message to the thread waiting for a reply,
219 // thereby unblocking it.
220 // @@@ Theoretically we should thread-lock this so only one cancel message
221 // ever gets sent. But right now, this is only used to completely tear down
222 // a client session, so duplicate replies don't bother us.
224 void Client::cancel()
226 // this is the common prefix of SecurityAgent client call replies
228 mach_msg_header_t Head
;
230 kern_return_t result
;
234 request
.Head
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND
, 0);
235 request
.Head
.msgh_remote_port
= mClientPort
;
236 request
.Head
.msgh_local_port
= MACH_PORT_NULL
;
237 request
.Head
.msgh_id
= cancelMessagePseudoID
;
238 request
.NDR
= NDR_record
;
240 // set call succeeded, no error status
241 request
.result
= KERN_SUCCESS
;
242 request
.status
= noErr
;
244 // send it (do not receive a reply). Use zero timeout to avoid hangs
245 MachPlusPlus::check(mach_msg_overwrite(&request
.Head
, MACH_SEND_MSG
|MACH_SEND_TIMEOUT
,
246 sizeof(request
), 0, MACH_PORT_NULL
, MACH_MSG_TIMEOUT_NONE
,
247 MACH_PORT_NULL
, (mach_msg_header_t
*) NULL
, 0));
252 // Get the port for the SecurityAgent.
253 // Start it if necessary (and possible). Throw an exception if we can't get to it.
254 // Sets mServerPort on success.
256 void Client::establishServer(const char *name
)
259 * Once we wean ourselves off PBS we can eliminate "bootstrap" and use
260 * mClientBootstrap directly.
265 pbsBootstrap
= mClientBootstrap
;
267 // If the userids don't match, that means you can't do user interaction
268 // @@@ Check session so we don't pop up UI in a non-UI context
269 // @@@ Expose this to caller so it can implement its own idea of getuid()!
270 if (desktopUid
!= getuid() && getuid() != 0)
271 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
273 // if the server is already running, we're done
274 Bootstrap
bootstrap(pbsBootstrap
);
275 if (mServerPort
= bootstrap
.lookupOptional(name
))
278 #if defined(AGENTNAME) && defined(AGENTPATH)
279 // switch the bootstrap port to that of the logged-in user
280 StBootstrap
bootSaver(pbsBootstrap
);
282 // try to start the agent
283 switch (pid_t pid
= fork()) {
286 // Setup the environment for the SecurityAgent
291 debug("SAclnt", "setgid(%d)", desktopGid
);
292 setgid(desktopGid
); // switch to login-user gid
293 debug("SAclnt", "setuid(%d)", desktopUid
);
294 // Must be setuid and not seteuid since we do not want the agent to be able
295 // to call seteuid(0) successfully.
296 setuid(desktopUid
); // switch to login-user uid
298 // close down any files that might have been open at this point
299 int maxDescriptors
= getdtablesize ();
302 for (i
= 3; i
< maxDescriptors
; ++i
)
307 // construct path to SecurityAgent
308 char agentExecutable
[PATH_MAX
+ 1];
309 const char *path
= getenv("SECURITYAGENT");
312 snprintf(agentExecutable
, sizeof(agentExecutable
), "%s/Contents/MacOS/" AGENTNAME
, path
);
313 debug("SAclnt", "execl(%s)", agentExecutable
);
314 execl(agentExecutable
, agentExecutable
, NULL
);
315 debug("SAclnt", "execl of SecurityAgent failed, errno=%d", errno
);
317 // Unconditional suicide follows.
318 // See comments below on why we can't use abort()
322 // NOTE: OS X abort() is implemented as kill(getuid()), which fails
323 // for a setuid-root process that has setuid'd. Go back to root to die...
328 case -1: // error (in parent)
329 UnixError::throwMe();
332 static const int timeout
= 300;
334 debug("SAclnt", "Starting security agent (%d seconds timeout)", timeout
);
335 struct timespec rqtp
;
336 memset(&rqtp
, 0, sizeof(rqtp
));
337 rqtp
.tv_nsec
= 100000000; /* 10^8 nanaseconds = 1/10th of a second */
338 for (int n
= timeout
; n
> 0; nanosleep(&rqtp
, NULL
), n
--) {
339 if (mServerPort
= bootstrap
.lookupOptional(name
))
342 switch (pid_t rc
= waitpid(pid
, &status
, WNOHANG
)) {
343 case 0: // child still running
348 case EAGAIN
: // transient
350 case ECHILD
: // no such child (dead; already reaped elsewhere)
351 debug("SAclnt", "child is dead (reaped elsewhere)");
352 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
354 debug("SAclnt", "waitpid failed: errno=%d", errno
);
355 UnixError::throwMe();
359 debug("SAclnt", "child died without claiming the SecurityAgent port");
360 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
364 if (mServerPort
== 0) { // couldn't contact Security Agent
365 debug("SAclnt", "Autolaunch failed");
366 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
368 debug("SAclnt", "SecurityAgent located");
374 // well, this didn't work. Too bad
375 debug("SAclnt", "Cannot contact SecurityAgent");
376 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
); //@@@ or INTERNAL_ERROR?
381 // Staged query maintainance
383 void Client::finishStagedQuery()
385 if (stage
== mainStage
)
386 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
388 if (getenv("NOSA")) {
389 printf(" [query done]\n");
394 check(secagent_client_finishStagedQuery(mStagePort
, mClientPort
, &status
));
399 void Client::cancelStagedQuery(Reason reason
)
401 if (stage
== mainStage
)
402 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
404 if (getenv("NOSA")) {
405 printf(" [query canceled; reason=%d]\n", reason
);
410 check(secagent_client_cancelStagedQuery(mStagePort
, mClientPort
, &status
, reason
));
417 // Query the user to unlock a keychain. This is a staged protocol with a private side-port.
419 void Client::queryUnlockDatabase(const OSXCode
*requestor
, pid_t requestPid
,
420 const char *database
, char passphrase
[maxPassphraseLength
])
422 Requestor
req(requestor
);
425 if (getenv("NOSA")) {
426 getNoSA(passphrase
, maxPassphraseLength
, "Unlock %s [<CR> to cancel]: ", database
);
432 check(secagent_client_unlockDatabase(mServerPort
, mClientPort
,
433 &status
, req
, requestPid
, database
, &mStagePort
.port(), passphrase
));
437 void Client::retryUnlockDatabase(Reason reason
, char passphrase
[maxPassphraseLength
])
439 if (stage
!= unlockStage
)
440 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
442 if (getenv("NOSA")) {
443 getNoSA(passphrase
, maxPassphraseLength
, "Retry unlock [<CR> to cancel]: ");
447 check(secagent_client_retryUnlockDatabase(mStagePort
, mClientPort
,
448 &status
, reason
, passphrase
));
453 // Ask for a (new) password for something.
455 void Client::queryNewPassphrase(const OSXCode
*requestor
, pid_t requestPid
,
456 const char *database
, Reason reason
, char passphrase
[maxPassphraseLength
])
458 Requestor
req(requestor
);
461 if (getenv("NOSA")) {
462 getNoSA(passphrase
, maxPassphraseLength
,
463 "New passphrase for %s (reason %d) [<CR> to cancel]: ",
464 (database
? database
: "[NULL database]"), reason
);
465 stage
= newPassphraseStage
;
470 check(secagent_client_queryNewPassphrase(mServerPort
, mClientPort
,
471 &status
, req
, requestPid
, database
, reason
,
472 &mStagePort
.port(), passphrase
));
473 stage
= newPassphraseStage
;
476 void Client::retryNewPassphrase(Reason reason
, char passphrase
[maxPassphraseLength
])
478 if (stage
!= newPassphraseStage
)
479 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
481 if (getenv("NOSA")) {
482 getNoSA(passphrase
, maxPassphraseLength
,
483 "retry new passphrase (reason %d) [<CR> to cancel]: ", reason
);
487 check(secagent_client_retryNewPassphrase(mStagePort
, mClientPort
,
488 &status
, reason
, passphrase
));
493 // Ask the user permission to use an item.
494 // This is used by the keychain-style ACL subject type (only).
496 void Client::queryKeychainAccess(const OSXCode
*requestor
, pid_t requestPid
,
497 const char *database
, const char *itemName
, AclAuthorization action
,
498 bool needPassphrase
, KeychainChoice
&choice
)
500 Requestor
req(requestor
);
503 if (getenv("NOSA")) {
504 char answer
[maxPassphraseLength
+10];
505 getNoSA(answer
, sizeof(answer
), "Allow [someone] to do %d on %s in %s? [yn][g]%s ",
506 int(action
), (itemName
? itemName
: "[NULL item]"),
507 (database
? database
: "[NULL database]"),
508 needPassphrase
? ":passphrase" : "");
509 // turn passphrase (no ':') into y:passphrase
510 if (needPassphrase
&& !strchr(answer
, ':')) {
511 memmove(answer
+2, answer
, strlen(answer
)+1);
512 memcpy(answer
, "y:", 2);
514 choice
.allowAccess
= answer
[0] == 'y';
515 choice
.continueGrantingToCaller
= answer
[1] == 'g';
516 if (const char *colon
= strchr(answer
, ':'))
517 strncpy(choice
.passphrase
, colon
+1, maxPassphraseLength
);
519 choice
.passphrase
[0] = '\0';
524 check(secagent_client_queryKeychainAccess(mServerPort
, mClientPort
,
525 &status
, req
, requestPid
, (database
? database
: ""), itemName
, action
,
526 needPassphrase
, &choice
));
532 // Query the user for a generic existing passphrase, with selectable prompt.
534 void Client::queryOldGenericPassphrase(const OSXCode
*requestor
, pid_t requestPid
,
536 KeychainBox
&addToKeychain
, char passphrase
[maxPassphraseLength
])
538 Requestor
req(requestor
);
541 if (getenv("NOSA")) {
542 getNoSA(passphrase
, maxPassphraseLength
,
543 "Old passphrase (\"%s\") [<CR> to cancel]: ", prompt
);
544 // @@@ addToKeychain not hooked up; stays unchanged
545 stage
= oldGenericPassphraseStage
;
550 MigBoolean addBox
= addToKeychain
.setting
;
551 check(secagent_client_queryOldGenericPassphrase(mServerPort
, mClientPort
,
552 &status
, req
, requestPid
, prompt
, &mStagePort
.port(),
553 addToKeychain
.show
, &addBox
, passphrase
));
554 addToKeychain
.setting
= addBox
;
555 stage
= oldGenericPassphraseStage
;
558 void Client::retryOldGenericPassphrase(Reason reason
,
559 bool &addToKeychain
, char passphrase
[maxPassphraseLength
])
561 if (stage
!= oldGenericPassphraseStage
)
562 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
564 if (getenv("NOSA")) {
565 getNoSA(passphrase
, maxPassphraseLength
,
566 "Retry old passphrase [<CR> to cancel]: ");
571 check(secagent_client_retryOldGenericPassphrase(mStagePort
, mClientPort
,
572 &status
, reason
, &addBox
, passphrase
));
573 addToKeychain
= addBox
;
578 // Ask for a new passphrase for something (with selectable prompt).
580 void Client::queryNewGenericPassphrase(const OSXCode
*requestor
, pid_t requestPid
,
581 const char *prompt
, Reason reason
,
582 KeychainBox
&addToKeychain
, char passphrase
[maxPassphraseLength
])
584 Requestor
req(requestor
);
587 if (getenv("NOSA")) {
588 getNoSA(passphrase
, maxPassphraseLength
,
589 "New passphrase (\"%s\") (reason %d) [<CR> to cancel]: ",
591 // @@@ addToKeychain not hooked up; stays unchanged
592 stage
= newGenericPassphraseStage
;
597 MigBoolean addBox
= addToKeychain
.setting
;
598 check(secagent_client_queryNewGenericPassphrase(mServerPort
, mClientPort
,
599 &status
, req
, requestPid
, prompt
, reason
,
600 &mStagePort
.port(), addToKeychain
.show
, &addBox
, passphrase
));
601 addToKeychain
.setting
= addBox
;
602 stage
= newGenericPassphraseStage
;
605 void Client::retryNewGenericPassphrase(Reason reason
,
606 bool &addToKeychain
, char passphrase
[maxPassphraseLength
])
608 if (stage
!= newGenericPassphraseStage
)
609 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
611 if (getenv("NOSA")) {
612 getNoSA(passphrase
, maxPassphraseLength
,
613 "retry new passphrase (reason %d) [<CR> to cancel]: ", reason
);
618 check(secagent_client_retryNewGenericPassphrase(mStagePort
, mClientPort
,
619 &status
, reason
, &addBox
, passphrase
));
620 addToKeychain
= addBox
;
625 // Authorization by authentication
627 bool Client::authorizationAuthenticate(const OSXCode
*requestor
, pid_t requestPid
,
628 const char *neededGroup
, const char *candidateUser
,
629 char user
[maxUsernameLength
], char passphrase
[maxPassphraseLength
])
631 Requestor
req(requestor
);
634 if (getenv("NOSA")) {
635 getNoSA(user
, maxUsernameLength
,
636 "User to authenticate for group %s (try \"%s\" [\"-\" to deny]): ",
637 neededGroup
, (candidateUser
? candidateUser
: "[NULL]"));
638 if (strcmp(user
, "-"))
639 getNoSA(passphrase
, maxPassphraseLength
,
640 "Passphrase for user %s: ", user
);
641 stage
= authorizeStage
;
642 return strcmp(user
, "-");
646 check(secagent_client_authorizationAuthenticate(mServerPort
, mClientPort
,
647 &status
, req
, requestPid
, neededGroup
, candidateUser
, &mStagePort
.port(), user
, passphrase
));
648 stage
= authorizeStage
;
649 return status
== noErr
;
652 bool Client::retryAuthorizationAuthenticate(Reason reason
, char user
[maxUsernameLength
],
653 char passphrase
[maxPassphraseLength
])
655 if (stage
!= authorizeStage
)
656 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ invent a "state mismatch error"?
658 if (getenv("NOSA")) {
659 getNoSA(user
, maxUsernameLength
,
660 "Retry authenticate (reason=%d) ([\"-\" to deny again]): ", reason
);
661 if (strcmp(user
, "-"))
662 getNoSA(passphrase
, maxPassphraseLength
,
663 "Passphrase for user %s: ", user
);
664 return strcmp(user
, "-");
667 check(secagent_client_retryAuthorizationAuthenticate(mStagePort
, mClientPort
,
668 &status
, reason
, user
, passphrase
));
669 return status
== noErr
;
673 // invokeMechanism old style
675 bool Client::invokeMechanism(const string
&inPluginId
, const string
&inMechanismId
, const AuthorizationValueVector
*inArguments
, const AuthorizationItemSet
*inHints
, const AuthorizationItemSet
*inContext
, AuthorizationResult
*outResult
, AuthorizationItemSet
*&outHintsPtr
, AuthorizationItemSet
*&outContextPtr
)
677 Copier
<AuthorizationValueVector
> inArgumentVector(inArguments
);
678 Copier
<AuthorizationItemSet
> inHintsSet(inHints
);
679 Copier
<AuthorizationItemSet
> inContextSet(inContext
);
681 COPY_OUT_DECL(AuthorizationItemSet
, outHintsSet
);
682 COPY_OUT_DECL(AuthorizationItemSet
, outContextSet
);
686 // either noErr (user cancel, allow) or throws authInternal
687 check(secagent_client_invokeMechanism(mServerPort
, mClientPort
,
688 &status
, &mStagePort
.port(),
690 inMechanismId
.c_str(),
691 COPY(inArgumentVector
),
695 COPY_OUT(outHintsSet
),
696 COPY_OUT(outContextSet
)));
698 if (status
!= errAuthorizationDenied
)
700 relocate(outHintsSet
, outHintsSetBase
);
701 Copier
<AuthorizationItemSet
> copyHints(outHintsSet
);
702 // the auth engine releases this when done
703 outHintsPtr
= copyHints
.keep();
704 relocate(outContextSet
, outContextSetBase
);
705 Copier
<AuthorizationItemSet
> copyContext(outContextSet
);
706 // the auth engine releases this when done
707 outContextPtr
= copyContext
.keep();
710 return (status
== noErr
);
714 void Client::terminateAgent()
717 // find the right place to look
720 // make sure we're doing this for the right user
721 // @@@ Check session as well!
722 if (desktopUid
!= getuid() && getuid() != 0)
723 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
725 // if the server is already running, it's time to kill it
726 bool agentRunning
= false;
729 Bootstrap
bootstrap(pbsBootstrap
);
730 if (mServerPort
= bootstrap
.lookupOptional("SecurityAgent"))
735 if (mServerPort
= mClientBootstrap
.lookupOptional("SecurityAgent"))
741 check(secagent_client_terminate(mServerPort
, mClientPort
));
745 #include <sys/types.h>
748 void Client::setClientGroupID(const char *grpName
)
751 * desktopGid is unsigned so the compiler warns about the conversion
754 struct group
*grent
= getgrnam(grpName
? grpName
: "nobody");
755 desktopGid
= grent
? grent
->gr_gid
: -2;
759 // Locate and identify the current desktop.
760 // This is moderately atrocious code. There really ought to be a way to identify
761 // the logged-in (graphics console) user (and whether there is one). As it stands,
762 // we locate the "pbs" (pasteboard server) process and obtain its uid. No pbs, no
763 // user interaction. (By all accounts, a dead pbs is a death sentence anyway.)
765 #include <sys/sysctl.h>
766 #include <mach/mach_error.h>
768 void Client::locateDesktop()
770 int mib
[3] = { CTL_KERN
, KERN_PROC
, KERN_PROC_ALL
};
772 struct kinfo_proc
*procBuf
;
774 if (sysctl(mib
, 3, NULL
, &bufSize
, NULL
, 0) < 0) {
779 procBuf
= (struct kinfo_proc
*)malloc(bufSize
); //@@@ which allocator?
780 if (sysctl(mib
, 3, procBuf
, &bufSize
, NULL
, 0))
781 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
);
782 int count
= bufSize
/ sizeof(struct kinfo_proc
);
783 struct kinfo_proc
*pbsProc
= NULL
;
784 for (struct kinfo_proc
*proc
= procBuf
; proc
< procBuf
+ count
; proc
++) {
785 if (!strncmp(proc
->kp_proc
.p_comm
, "pbs", MAXCOMLEN
)) {
791 if (!pbsProc
) { // no pasteboard server -- user not logged in
792 debug("SAclnt", "No pasteboard server - no user logged in");
793 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
796 desktopUid
= pbsProc
->kp_eproc
.e_ucred
.cr_uid
;
797 desktopGid
= pbsProc
->kp_eproc
.e_ucred
.cr_gid
;
798 pid_t pbsPid
= pbsProc
->kp_proc
.p_pid
;
800 debug("SAclnt", "Desktop has uid %d", desktopUid
);
803 kern_return_t result
;
804 mach_port_t pbsTaskPort
;
805 result
= task_for_pid(mach_task_self(), pbsPid
, &pbsTaskPort
);
808 mach_error("task_for_pid(pbs)", result
);
809 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
812 result
= task_get_bootstrap_port(pbsTaskPort
, &pbsBootstrap
);
815 mach_error("task_get_bootstrap_port(pbs)", result
);
816 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
820 } // end namespace SecurityAgent
821 } // end namespace Security