]> git.saurik.com Git - apple/security.git/blob - SecurityServer/SecurityAgentClient.cpp
Security-28.tar.gz
[apple/security.git] / SecurityServer / SecurityAgentClient.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 //
20 // SecurityAgentClient - client interface to SecurityAgent
21 //
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.
27 //
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
33 //
34 // @@@ SA keepalive option.
35 //
36 #include "SecurityAgentClient.h"
37 #include "secagent.h"
38 #include <Security/utilities.h>
39 #include <Security/Authorization.h>
40 #include <cstdio>
41 #include <unistd.h>
42 #include <mach/mach_error.h>
43 #include <mach/ndr.h>
44 #include <Security/debugging.h>
45 #include <sys/wait.h>
46 #include <sys/syslimits.h>
47 #include <time.h>
48
49 // @@@ Should be in <time.h> but it isn't as of Puma5F22
50 extern "C" int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
51
52 namespace Security {
53 namespace SecurityAgent {
54
55
56 using namespace Security;
57 using namespace MachPlusPlus;
58
59
60 //
61 // Encode a requestor
62 //
63 class Requestor {
64 public:
65 Requestor(const OSXCode *code) { if (code) extForm = code->encode(); }
66 operator const char * () const { return extForm.c_str(); }
67
68 private:
69 string extForm;
70 };
71
72
73 //
74 // Check a return from a MIG client call
75 //
76 void Client::check(kern_return_t error)
77 {
78 // first check the Mach IPC return code
79 switch (error) {
80 case KERN_SUCCESS: // peachy
81 break;
82 case MIG_SERVER_DIED: // explicit can't-send-it's-dead
83 stage = mainStage;
84 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
85 default: // some random Mach error
86 stage = mainStage;
87 MachPlusPlus::Error::throwMe(error);
88 }
89
90 // now check the OSStatus return from the server side
91 switch (status) {
92 case noErr:
93 case errAuthorizationDenied:
94 break;
95 case userCanceledErr:
96 unstage();
97 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
98 default:
99 unstage();
100 MacOSError::throwMe(status);
101 }
102 }
103
104 void Client::unstage()
105 {
106 if (stage != mainStage) {
107 mStagePort.deallocate();
108 stage = mainStage;
109 }
110 }
111
112
113 //
114 // NOSA support functions. This is a test mode where the SecurityAgent
115 // is simulated via stdio in the client. Good for running automated tests
116 // of client programs. Only available if -DNOSA when compiling.
117 //
118 #if defined(NOSA)
119
120 #include <cstdarg>
121
122 static void getNoSA(char *buffer, size_t bufferSize, const char *fmt, ...)
123 {
124 // write prompt
125 va_list args;
126 va_start(args, fmt);
127 vfprintf(stdout, fmt, args);
128 va_end(args);
129
130 // read reply
131 memset(buffer, 0, bufferSize);
132 const char *nosa = getenv("NOSA");
133 if (!strcmp(nosa, "-")) {
134 if (fgets(buffer, bufferSize-1, stdin) == NULL)
135 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
136 buffer[strlen(buffer)-1] = '\0'; // remove trailing newline
137 if (!isatty(fileno(stdin)))
138 printf("%s\n", buffer); // echo to output if input not terminal
139 } else {
140 strncpy(buffer, nosa, bufferSize-1);
141 printf("%s\n", buffer);
142 }
143 if (buffer[0] == '\0') // empty input -> cancellation
144 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
145 }
146
147 #endif //NOSA
148
149
150 //
151 // Initialize our CSSM interface
152 //
153 Client::Client() : mActive(false), mKeepAlive(false), stage(mainStage)
154 {
155 }
156
157 Client::~Client()
158 {
159 terminate();
160 }
161
162
163 //
164 // Activate a session
165 //
166 void Client::activate(const char *name)
167 {
168 if (!mActive) {
169 establishServer(name ? name : "SecurityAgent");
170
171 // create reply port
172 mClientPort.allocate(MACH_PORT_RIGHT_RECEIVE);
173 mClientPort.insertRight(MACH_MSG_TYPE_MAKE_SEND);
174
175 // get notified if the server dies (shouldn't happen, but...)
176 mServerPort.requestNotify(mClientPort, MACH_NOTIFY_DEAD_NAME, true);
177
178 // ready
179 mActive = true;
180 }
181 }
182
183 void Client::terminate()
184 {
185 if (mActive) {
186 mServerPort.deallocate();
187 mClientPort.destroy();
188 mActive = false;
189 }
190 }
191
192
193 //
194 // Cancel a client call.
195 // This actually sends a reply message to the thread waiting for a reply,
196 // thereby unblocking it.
197 // @@@ Theoretically we should thread-lock this so only one cancel message
198 // ever gets sent. But right now, this is only used to completely tear down
199 // a client session, so duplicate replies don't bother us.
200 //
201 void Client::cancel()
202 {
203 // this is the common prefix of SecurityAgent client call replies
204 struct {
205 mach_msg_header_t Head;
206 NDR_record_t NDR;
207 kern_return_t result;
208 OSStatus status;
209 } request;
210
211 request.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
212 request.Head.msgh_remote_port = mClientPort;
213 request.Head.msgh_local_port = MACH_PORT_NULL;
214 request.Head.msgh_id = cancelMessagePseudoID;
215 request.NDR = NDR_record;
216
217 // set call succeeded, no error status
218 request.result = KERN_SUCCESS;
219 request.status = noErr;
220
221 // send it (do not receive a reply). Use zero timeout to avoid hangs
222 MachPlusPlus::check(mach_msg_overwrite(&request.Head, MACH_SEND_MSG|MACH_SEND_TIMEOUT,
223 sizeof(request), 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
224 MACH_PORT_NULL, (mach_msg_header_t *) NULL, 0));
225 }
226
227
228 //
229 // Get the port for the SecurityAgent.
230 // Start it if necessary (and possible). Throw an exception if we can't get to it.
231 // Sets mServerPort on success.
232 //
233 void Client::establishServer(const char *name)
234 {
235 locateDesktop();
236
237 // If the userids don't match, that means you can't do user interaction
238 // @@@ Expose this to caller so it can implement its own idea of getuid()!
239 if (desktopUid != getuid() && getuid() != 0)
240 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
241
242 // if the server is already running, we're done
243 Bootstrap bootstrap(pbsBootstrap);
244 if (mServerPort = bootstrap.lookupOptional(name))
245 return;
246
247 #if defined(AGENTNAME) && defined(AGENTPATH)
248 // switch the bootstrap port to that of the logged-in user
249 StBootstrap bootSaver(pbsBootstrap);
250
251 // try to start the agent
252 switch (pid_t pid = fork()) {
253 case 0: // child
254 {
255 // Setup the environment for the SecurityAgent
256 unsetenv("USER");
257 unsetenv("LOGNAME");
258 unsetenv("HOME");
259
260 debug("SAclnt", "setgid(%d)", desktopGid);
261 setgid(desktopGid); // switch to login-user gid
262 debug("SAclnt", "setuid(%d)", desktopUid);
263 // Must be setuid and not seteuid since we do not want the agent to be able
264 // to call seteuid(0) successfully.
265 setuid(desktopUid); // switch to login-user uid
266
267 // construct path to SecurityAgent
268 char agentExecutable[PATH_MAX + 1];
269 const char *path = getenv("SECURITYAGENT");
270 if (!path)
271 path = AGENTPATH;
272 snprintf(agentExecutable, sizeof(agentExecutable), "%s/Contents/MacOS/" AGENTNAME, path);
273 debug("SAclnt", "execl(%s)", agentExecutable);
274 execl(agentExecutable, agentExecutable, NULL);
275 debug("SAclnt", "execl of SecurityAgent failed, errno=%d", errno);
276
277 // Unconditional suicide follows.
278 // See comments below on why we can't use abort()
279 #if 1
280 _exit(1);
281 #else
282 // NOTE: OS X abort() is implemented as kill(getuid()), which fails
283 // for a setuid-root process that has setuid'd. Go back to root to die...
284 setuid(0);
285 abort();
286 #endif
287 }
288 case -1: // error (in parent)
289 UnixError::throwMe();
290 default: // parent
291 {
292 static const int timeout = 300;
293
294 debug("SAclnt", "Starting security agent (%d seconds timeout)", timeout);
295 struct timespec rqtp;
296 memset(&rqtp, 0, sizeof(rqtp));
297 rqtp.tv_nsec = 100000000; /* 10^8 nanaseconds = 1/10th of a second */
298 for (int n = timeout; n > 0; nanosleep(&rqtp, NULL), n--) {
299 if (mServerPort = bootstrap.lookupOptional(name))
300 break;
301 int status;
302 switch (pid_t rc = waitpid(pid, &status, WNOHANG)) {
303 case 0: // child still running
304 continue;
305 case -1: // error
306 switch (errno) {
307 case EINTR:
308 case EAGAIN: // transient
309 continue;
310 case ECHILD: // no such child (dead; already reaped elsewhere)
311 debug("SAclnt", "child is dead (reaped elsewhere)");
312 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
313 default:
314 debug("SAclnt", "waitpid failed: errno=%d", errno);
315 UnixError::throwMe();
316 }
317 default:
318 assert(rc == pid);
319 debug("SAclnt", "child died without claiming the SecurityAgent port");
320 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
321 }
322 }
323
324 if (mServerPort == 0) { // couldn't contact Security Agent
325 debug("SAclnt", "Autolaunch failed");
326 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
327 }
328 debug("SAclnt", "SecurityAgent located");
329 return;
330 }
331 }
332 #endif
333
334 // well, this didn't work. Too bad
335 debug("SAclnt", "Cannot contact SecurityAgent");
336 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION); //@@@ or INTERNAL_ERROR?
337 }
338
339
340 //
341 // Staged query maintainance
342 //
343 void Client::finishStagedQuery()
344 {
345 if (stage == mainStage)
346 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
347 #if defined(NOSA)
348 if (getenv("NOSA")) {
349 printf(" [query done]\n");
350 stage = mainStage;
351 return;
352 }
353 #endif
354 check(secagent_client_finishStagedQuery(mStagePort, mClientPort, &status));
355 unstage();
356 terminate();
357 }
358
359 void Client::cancelStagedQuery(Reason reason)
360 {
361 if (stage == mainStage)
362 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
363 #if defined(NOSA)
364 if (getenv("NOSA")) {
365 printf(" [query canceled; reason=%d]\n", reason);
366 stage = mainStage;
367 return;
368 }
369 #endif
370 check(secagent_client_cancelStagedQuery(mStagePort, mClientPort, &status, reason));
371 unstage();
372 terminate();
373 }
374
375
376 //
377 // Query the user to unlock a keychain. This is a staged protocol with a private side-port.
378 //
379 void Client::queryUnlockDatabase(const OSXCode *requestor, pid_t requestPid,
380 const char *database, char passphrase[maxPassphraseLength])
381 {
382 Requestor req(requestor);
383
384 #if defined(NOSA)
385 if (getenv("NOSA")) {
386 getNoSA(passphrase, maxPassphraseLength, "Unlock %s [<CR> to cancel]: ", database);
387 stage = unlockStage;
388 return;
389 }
390 #endif
391 activate();
392 check(secagent_client_unlockDatabase(mServerPort, mClientPort,
393 &status, req, requestPid, database, &mStagePort.port(), passphrase));
394 stage = unlockStage;
395 }
396
397 void Client::retryUnlockDatabase(Reason reason, char passphrase[maxPassphraseLength])
398 {
399 if (stage != unlockStage)
400 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
401 #if defined(NOSA)
402 if (getenv("NOSA")) {
403 getNoSA(passphrase, maxPassphraseLength, "Retry unlock [<CR> to cancel]: ");
404 return;
405 }
406 #endif
407 check(secagent_client_retryUnlockDatabase(mStagePort, mClientPort,
408 &status, reason, passphrase));
409 }
410
411
412 //
413 // Ask for a (new) password for something.
414 //
415 void Client::queryNewPassphrase(const OSXCode *requestor, pid_t requestPid,
416 const char *database, Reason reason, char passphrase[maxPassphraseLength])
417 {
418 Requestor req(requestor);
419
420 #if defined(NOSA)
421 if (getenv("NOSA")) {
422 getNoSA(passphrase, maxPassphraseLength,
423 "New passphrase for %s (reason %d) [<CR> to cancel]: ",
424 (database ? database : "[NULL database]"), reason);
425 stage = newPassphraseStage;
426 return;
427 }
428 #endif
429 activate();
430 check(secagent_client_queryNewPassphrase(mServerPort, mClientPort,
431 &status, req, requestPid, database, reason,
432 &mStagePort.port(), passphrase));
433 stage = newPassphraseStage;
434 }
435
436 void Client::retryNewPassphrase(Reason reason, char passphrase[maxPassphraseLength])
437 {
438 if (stage != newPassphraseStage)
439 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
440 #if defined(NOSA)
441 if (getenv("NOSA")) {
442 getNoSA(passphrase, maxPassphraseLength,
443 "retry new passphrase (reason %d) [<CR> to cancel]: ", reason);
444 return;
445 }
446 #endif
447 check(secagent_client_retryNewPassphrase(mStagePort, mClientPort,
448 &status, reason, passphrase));
449 }
450
451
452 //
453 // Ask the user permission to use an item.
454 // This is used by the keychain-style ACL subject type (only).
455 //
456 void Client::queryKeychainAccess(const OSXCode *requestor, pid_t requestPid,
457 const char *database, const char *itemName, AclAuthorization action,
458 Client::KeychainChoice &choice)
459 {
460 Requestor req(requestor);
461
462 #if defined(NOSA)
463 if (getenv("NOSA")) {
464 char answer[10];
465 getNoSA(answer, sizeof(answer), "Allow [someone] to do %d on %s in %s? ",
466 int(action), (itemName ? itemName : "[NULL item]"),
467 (database ? database : "[NULL database]"));
468 choice.allowAccess = answer[0] == 'y';
469 choice.continueGrantingToCaller = answer[1] == 'g';
470 return;
471 }
472 #endif
473 activate();
474 check(secagent_client_queryKeychainAccess(mServerPort, mClientPort,
475 &status, req, requestPid, (database ? database : ""), itemName, action, &choice));
476 terminate();
477 }
478
479
480 //
481 // Query the user for a generic existing passphrase, with selectable prompt.
482 //
483 void Client::queryOldGenericPassphrase(const OSXCode *requestor, pid_t requestPid,
484 const char *prompt,
485 KeychainBox &addToKeychain, char passphrase[maxPassphraseLength])
486 {
487 Requestor req(requestor);
488
489 #if defined(NOSA)
490 if (getenv("NOSA")) {
491 getNoSA(passphrase, maxPassphraseLength,
492 "Old passphrase (\"%s\") [<CR> to cancel]: ", prompt);
493 // @@@ addToKeychain not hooked up; stays unchanged
494 stage = oldGenericPassphraseStage;
495 return;
496 }
497 #endif
498 activate();
499 MigBoolean addBox = addToKeychain.setting;
500 check(secagent_client_queryOldGenericPassphrase(mServerPort, mClientPort,
501 &status, req, requestPid, prompt, &mStagePort.port(),
502 addToKeychain.show, &addBox, passphrase));
503 addToKeychain.setting = addBox;
504 stage = oldGenericPassphraseStage;
505 }
506
507 void Client::retryOldGenericPassphrase(Reason reason,
508 bool &addToKeychain, char passphrase[maxPassphraseLength])
509 {
510 if (stage != oldGenericPassphraseStage)
511 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
512 #if defined(NOSA)
513 if (getenv("NOSA")) {
514 getNoSA(passphrase, maxPassphraseLength,
515 "Retry old passphrase [<CR> to cancel]: ");
516 return;
517 }
518 #endif
519 MigBoolean addBox;
520 check(secagent_client_retryOldGenericPassphrase(mStagePort, mClientPort,
521 &status, reason, &addBox, passphrase));
522 addToKeychain = addBox;
523 }
524
525
526 //
527 // Ask for a new passphrase for something (with selectable prompt).
528 //
529 void Client::queryNewGenericPassphrase(const OSXCode *requestor, pid_t requestPid,
530 const char *prompt, Reason reason,
531 KeychainBox &addToKeychain, char passphrase[maxPassphraseLength])
532 {
533 Requestor req(requestor);
534
535 #if defined(NOSA)
536 if (getenv("NOSA")) {
537 getNoSA(passphrase, maxPassphraseLength,
538 "New passphrase (\"%s\") (reason %d) [<CR> to cancel]: ",
539 prompt, reason);
540 // @@@ addToKeychain not hooked up; stays unchanged
541 stage = newGenericPassphraseStage;
542 return;
543 }
544 #endif
545 activate();
546 MigBoolean addBox = addToKeychain.setting;
547 check(secagent_client_queryNewGenericPassphrase(mServerPort, mClientPort,
548 &status, req, requestPid, prompt, reason,
549 &mStagePort.port(), addToKeychain.show, &addBox, passphrase));
550 addToKeychain.setting = addBox;
551 stage = newGenericPassphraseStage;
552 }
553
554 void Client::retryNewGenericPassphrase(Reason reason,
555 bool &addToKeychain, char passphrase[maxPassphraseLength])
556 {
557 if (stage != newGenericPassphraseStage)
558 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
559 #if defined(NOSA)
560 if (getenv("NOSA")) {
561 getNoSA(passphrase, maxPassphraseLength,
562 "retry new passphrase (reason %d) [<CR> to cancel]: ", reason);
563 return;
564 }
565 #endif
566 MigBoolean addBox;
567 check(secagent_client_retryNewGenericPassphrase(mStagePort, mClientPort,
568 &status, reason, &addBox, passphrase));
569 addToKeychain = addBox;
570 }
571
572
573 //
574 // Authorization by authentication
575 //
576 bool Client::authorizationAuthenticate(const OSXCode *requestor, pid_t requestPid,
577 const char *neededGroup, const char *candidateUser,
578 char user[maxUsernameLength], char passphrase[maxPassphraseLength])
579 {
580 Requestor req(requestor);
581
582 #if defined(NOSA)
583 if (getenv("NOSA")) {
584 getNoSA(user, maxUsernameLength,
585 "User to authenticate for group %s (try \"%s\" [\"-\" to deny]): ",
586 neededGroup, (candidateUser ? candidateUser : "[NULL]"));
587 if (strcmp(user, "-"))
588 getNoSA(passphrase, maxPassphraseLength,
589 "Passphrase for user %s: ", user);
590 stage = authorizeStage;
591 return strcmp(user, "-");
592 }
593 #endif
594 activate();
595 check(secagent_client_authorizationAuthenticate(mServerPort, mClientPort,
596 &status, req, requestPid, neededGroup, candidateUser, &mStagePort.port(), user, passphrase));
597 stage = authorizeStage;
598 return status == noErr;
599 }
600
601 bool Client::retryAuthorizationAuthenticate(Reason reason, char user[maxUsernameLength],
602 char passphrase[maxPassphraseLength])
603 {
604 if (stage != authorizeStage)
605 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
606 #if defined(NOSA)
607 if (getenv("NOSA")) {
608 getNoSA(user, maxUsernameLength,
609 "Retry authenticate (reason=%d) ([\"-\" to deny again]): ", reason);
610 if (strcmp(user, "-"))
611 getNoSA(passphrase, maxPassphraseLength,
612 "Passphrase for user %s: ", user);
613 return strcmp(user, "-");
614 }
615 #endif
616 check(secagent_client_retryAuthorizationAuthenticate(mStagePort, mClientPort,
617 &status, reason, user, passphrase));
618 return status == noErr;
619 }
620
621
622 //
623 // Locate and identify the current desktop.
624 // This is moderately atrocious code. There really ought to be a way to identify
625 // the logged-in (graphics console) user (and whether there is one). As it stands,
626 // we locate the "pbs" (pasteboard server) process and obtain its uid. No pbs, no
627 // user interaction. (By all accounts, a dead pbs is a death sentence anyway.)
628 //
629 #include <sys/sysctl.h>
630 #include <mach/mach_error.h>
631
632 void Client::locateDesktop()
633 {
634 int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL};
635 size_t bufSize;
636 struct kinfo_proc *procBuf;
637
638 if (sysctl(mib, 3, NULL, &bufSize, NULL, 0) < 0) {
639 perror("sysctl");
640 abort();
641 }
642
643 procBuf = (struct kinfo_proc *)malloc(bufSize); //@@@ which allocator?
644 if (sysctl(mib, 3, procBuf, &bufSize, NULL, 0))
645 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
646 int count = bufSize / sizeof(struct kinfo_proc);
647 struct kinfo_proc *pbsProc = NULL;
648 for (struct kinfo_proc *proc = procBuf; proc < procBuf + count; proc++) {
649 if (!strncmp(proc->kp_proc.p_comm, "pbs", MAXCOMLEN)) {
650 pbsProc = proc;
651 break;
652 }
653 }
654
655 if (!pbsProc) { // no pasteboard server -- user not logged in
656 debug("SAclnt", "No pasteboard server - no user logged in");
657 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
658 }
659
660 desktopUid = pbsProc->kp_eproc.e_ucred.cr_uid;
661 desktopGid = pbsProc->kp_eproc.e_ucred.cr_gid;
662 pid_t pbsPid = pbsProc->kp_proc.p_pid;
663
664 debug("SAclnt", "Desktop has uid %d", desktopUid);
665 free(procBuf);
666
667 kern_return_t result;
668 mach_port_t pbsTaskPort;
669 result = task_for_pid(mach_task_self(), pbsPid, &pbsTaskPort);
670 if (result)
671 {
672 mach_error("task_for_pid(pbs)", result);
673 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
674 }
675
676 result = task_get_bootstrap_port(pbsTaskPort, &pbsBootstrap);
677 if (result)
678 {
679 mach_error("task_get_bootstrap_port(pbs)", result);
680 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
681 }
682 }
683
684 } // end namespace SecurityAgent
685 } // end namespace Security