]> git.saurik.com Git - apple/security.git/blob - SecurityServer/SecurityAgentClient.cpp
Security-163.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 #include <signal.h>
49 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
50 #include <Security/ktracecodes.h>
51
52 #include <sys/types.h>
53 #include <grp.h>
54
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);
57
58 namespace Security {
59 namespace SecurityAgent {
60
61
62 using namespace Security;
63 using namespace MachPlusPlus;
64
65
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) &copy, &copy##Length, &copy##Base
69 #define COPY_OUT_DECL(type,name) type *name, *name##Base; mach_msg_type_number_t name##Length
70
71
72 //
73 // Encode a requestor
74 //
75 class Requestor {
76 public:
77 Requestor(const OSXCode *code) { if (code) extForm = code->encode(); }
78 operator const char * () const { return extForm.c_str(); }
79
80 // use this for debugging only
81 const char *c_str() const { return extForm.empty() ? "(unknown)" : extForm.c_str(); }
82
83 private:
84 string extForm;
85 };
86
87
88 //
89 // Check a return from a MIG client call
90 //
91 void Client::check(kern_return_t error)
92 {
93 // first check the Mach IPC return code
94 switch (error) {
95 case KERN_SUCCESS: // peachy
96 break;
97 case MIG_SERVER_DIED: // explicit can't-send-it's-dead
98 stage = mainStage;
99 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
100 default: // some random Mach error
101 stage = mainStage;
102 MachPlusPlus::Error::throwMe(error);
103 }
104
105 // now check the OSStatus return from the server side
106 switch (status) {
107 case noErr:
108 case errAuthorizationDenied:
109 break;
110 case userCanceledErr:
111 unstage();
112 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
113 default:
114 unstage();
115 MacOSError::throwMe(status);
116 }
117 }
118
119 void Client::unstage()
120 {
121 if (stage != mainStage) {
122 mStagePort.deallocate();
123 stage = mainStage;
124 }
125 }
126
127
128 //
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.
132 //
133 #if defined(NOSA)
134
135 #include <cstdarg>
136
137 static void getNoSA(char *buffer, size_t bufferSize, const char *fmt, ...)
138 {
139 // write prompt
140 va_list args;
141 va_start(args, fmt);
142 vfprintf(stdout, fmt, args);
143 va_end(args);
144
145 // read reply
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
154 } else {
155 strncpy(buffer, nosa, bufferSize-1);
156 printf("%s\n", buffer);
157 }
158 if (buffer[0] == '\0') // empty input -> cancellation
159 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
160 }
161
162 #endif //NOSA
163
164
165 //
166 // Initialize our CSSM interface
167 //
168 Client::Client() : mActive(false), desktopUid(0), mKeepAlive(false), stage(mainStage), mAgentName("com.apple.SecurityAgent")
169 {
170 }
171
172 /*
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
177 * than this library.
178 */
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)
182 {
183 }
184
185 Client::~Client()
186 {
187 terminate();
188 }
189
190
191 //
192 // Activate a session
193 //
194 void Client::activate()
195 {
196 if (!mActive) {
197
198 establishServer();
199
200 // create reply port
201 mClientPort.allocate(MACH_PORT_RIGHT_RECEIVE);
202 mClientPort.insertRight(MACH_MSG_TYPE_MAKE_SEND);
203
204 // get notified if the server dies (shouldn't happen, but...)
205 mServerPort.requestNotify(mClientPort, MACH_NOTIFY_DEAD_NAME, true);
206
207 // ready
208 mActive = true;
209 }
210 }
211
212 void Client::terminate()
213 {
214 if (mActive) {
215 mServerPort.deallocate();
216 mClientPort.destroy();
217 mActive = false;
218 }
219 }
220
221
222 //
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.
229 //
230 void Client::cancel()
231 {
232 // this is the common prefix of SecurityAgent client call replies
233 struct {
234 mach_msg_header_t Head;
235 NDR_record_t NDR;
236 kern_return_t result;
237 OSStatus status;
238 } request;
239
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;
245
246 // set call succeeded, no error status
247 request.result = KERN_SUCCESS;
248 request.status = noErr;
249
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));
254 }
255
256
257 //
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.
261 //
262 void Client::establishServer()
263 {
264 // if the server is already running, we're done
265 if (mServerPort = mClientBootstrap.lookupOptional(mAgentName.c_str()))
266 return;
267
268 #if defined(AGENTNAME) && defined(AGENTPATH)
269 // switch the bootstrap port to that of the logged-in user
270
271 StBootstrap bootSaver(mClientBootstrap);
272
273 // try to start the agent
274 switch (pid_t pid = fork()) {
275 case 0: // child
276 {
277 // Setup the environment for the SecurityAgent
278 unsetenv("USER");
279 unsetenv("LOGNAME");
280 unsetenv("HOME");
281
282 // tell agent which name to register
283 setenv("AGENTNAME", mAgentName.c_str(), 1);
284
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
286 {
287 struct group *grent = getgrnam("nobody");
288 gid_t desktopGid = grent ? grent->gr_gid : unsigned(-2); //@@@ questionable
289 endgrent();
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
296 }
297 // close down any files that might have been open at this point
298 int maxDescriptors = getdtablesize ();
299 int i;
300
301 for (i = 3; i < maxDescriptors; ++i)
302 {
303 close (i);
304 }
305
306 // construct path to SecurityAgent
307 char agentExecutable[PATH_MAX + 1];
308 const char *path = getenv("SECURITYAGENT");
309 if (!path)
310 path = AGENTPATH;
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);
315
316 // Unconditional suicide follows.
317 // See comments below on why we can't use abort()
318 #if 1
319 _exit(1);
320 #else
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...
323 setuid(0);
324 abort();
325 #endif
326 }
327 case -1: // error (in parent)
328 UnixError::throwMe();
329 default: // parent
330 {
331 static const int timeout = 300;
332
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()))
339 break;
340 int status;
341 switch (IFDEBUG(pid_t rc =) waitpid(pid, &status, WNOHANG)) {
342 case 0: // child still running
343 continue;
344 case -1: // error
345 switch (errno) {
346 case EINTR:
347 case EAGAIN: // transient
348 continue;
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);
352 default:
353 secdebug("SAclnt", "waitpid failed: errno=%d", errno);
354 UnixError::throwMe();
355 }
356 default:
357 assert(rc == pid);
358 secdebug("SAclnt", "child died without claiming the SecurityAgent port");
359 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
360 }
361 }
362
363 if (mServerPort == 0) { // couldn't contact Security Agent
364 secdebug("SAclnt", "Autolaunch failed");
365 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
366 }
367 secdebug("SAclnt", "SecurityAgent located");
368 return;
369 }
370 }
371 #endif
372
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?
376 }
377
378
379 //
380 // Staged query maintainance
381 //
382 void Client::finishStagedQuery()
383 {
384 if (stage == mainStage)
385 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
386 #if defined(NOSA)
387 if (getenv("NOSA")) {
388 printf(" [query done]\n");
389 stage = mainStage;
390 return;
391 }
392 #endif
393 check(secagent_client_finishStagedQuery(mStagePort, mClientPort, &status));
394 unstage();
395 terminate();
396 }
397
398 void Client::cancelStagedQuery(Reason reason)
399 {
400 if (stage == mainStage)
401 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
402 #if defined(NOSA)
403 if (getenv("NOSA")) {
404 printf(" [query canceled; reason=%d]\n", reason);
405 stage = mainStage;
406 return;
407 }
408 #endif
409 check(secagent_client_cancelStagedQuery(mStagePort, mClientPort, &status, reason));
410 unstage();
411 terminate();
412 }
413
414
415 //
416 // Query the user to unlock a keychain. This is a staged protocol with a private side-port.
417 //
418 void Client::queryUnlockDatabase(const OSXCode *requestor, pid_t requestPid,
419 const char *database, char passphrase[maxPassphraseLength])
420 {
421 Requestor req(requestor);
422
423 #if defined(NOSA)
424 if (getenv("NOSA")) {
425 getNoSA(passphrase, maxPassphraseLength, "Unlock %s [<CR> to cancel]: ", database);
426 stage = unlockStage;
427 return;
428 }
429 #endif
430 activate();
431 check(secagent_client_unlockDatabase(mServerPort, mClientPort,
432 &status, req, requestPid, database, &mStagePort.port(), passphrase));
433 stage = unlockStage;
434 }
435
436 void Client::retryUnlockDatabase(Reason reason, char passphrase[maxPassphraseLength])
437 {
438 if (stage != unlockStage)
439 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
440 #if defined(NOSA)
441 if (getenv("NOSA")) {
442 getNoSA(passphrase, maxPassphraseLength, "Retry unlock [<CR> to cancel]: ");
443 return;
444 }
445 #endif
446 check(secagent_client_retryUnlockDatabase(mStagePort, mClientPort,
447 &status, reason, passphrase));
448 }
449
450
451 //
452 // Ask for a (new) password for something.
453 //
454 void Client::queryNewPassphrase(const OSXCode *requestor, pid_t requestPid,
455 const char *database, Reason reason, char passphrase[maxPassphraseLength], char oldPassphrase[maxPassphraseLength])
456 {
457 Requestor req(requestor);
458
459 #if defined(NOSA)
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;
465 return;
466 }
467 #endif
468 activate();
469 check(secagent_client_queryNewPassphrase(mServerPort, mClientPort,
470 &status, req, requestPid, database, reason,
471 &mStagePort.port(), passphrase, oldPassphrase));
472 stage = newPassphraseStage;
473 }
474
475 void Client::retryNewPassphrase(Reason reason, char passphrase[maxPassphraseLength], char oldPassphrase[maxPassphraseLength])
476 {
477 if (stage != newPassphraseStage)
478 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
479 #if defined(NOSA)
480 if (getenv("NOSA")) {
481 getNoSA(passphrase, maxPassphraseLength,
482 "retry new passphrase (reason %d) [<CR> to cancel]: ", reason);
483 return;
484 }
485 #endif
486 check(secagent_client_retryNewPassphrase(mStagePort, mClientPort,
487 &status, reason, passphrase, oldPassphrase));
488 }
489
490
491 //
492 // Ask the user permission to use an item.
493 // This is used by the keychain-style ACL subject type (only).
494 //
495 void Client::queryKeychainAccess(const OSXCode *requestor, pid_t requestPid,
496 const char *database, const char *itemName, AclAuthorization action,
497 bool needPassphrase, KeychainChoice &choice)
498 {
499 Debug::trace (kSecTraceSecurityServerQueryKeychainAccess);
500
501 Requestor req(requestor);
502
503 #if defined(NOSA)
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);
514 }
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);
519 else
520 choice.passphrase[0] = '\0';
521 return;
522 }
523 #endif
524 activate();
525 check(secagent_client_queryKeychainAccess(mServerPort, mClientPort,
526 &status, req, requestPid, (database ? database : ""), itemName, action,
527 needPassphrase, &mStagePort.port(), &choice));
528
529 stage = queryKeychainAccessStage;
530 }
531
532
533 void Client::retryQueryKeychainAccess (Reason reason, Choice &choice)
534 {
535 if (stage != queryKeychainAccessStage)
536 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
537
538 check(secagent_client_retryQueryKeychainAccess (mStagePort, mClientPort, &status, reason, &choice));
539 }
540
541
542
543 //
544 // Ask the user whether a somewhat (but not cleanly) matching code identity
545 // should be accepted for access control purposes.
546 //
547 void Client::queryCodeIdentity(const OSXCode *requestor, pid_t requestPid,
548 const char *aclPath, KeychainChoice &choice)
549 {
550 Requestor req(requestor);
551
552 #if defined(NOSA)
553 if (getenv("NOSA")) {
554 char answer[10];
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';
560 return;
561 }
562 #endif
563 activate();
564 check(secagent_client_queryCodeIdentity(mServerPort, mClientPort,
565 &status, req, requestPid, aclPath, &choice));
566 terminate();
567 }
568
569
570 //
571 // Query the user for a generic existing passphrase, with selectable prompt.
572 //
573 void Client::queryOldGenericPassphrase(const OSXCode *requestor, pid_t requestPid,
574 const char *prompt,
575 KeychainBox &addToKeychain, char passphrase[maxPassphraseLength])
576 {
577 Requestor req(requestor);
578
579 #if defined(NOSA)
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;
585 return;
586 }
587 #endif
588 activate();
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;
595 }
596
597 void Client::retryOldGenericPassphrase(Reason reason,
598 bool &addToKeychain, char passphrase[maxPassphraseLength])
599 {
600 if (stage != oldGenericPassphraseStage)
601 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
602 #if defined(NOSA)
603 if (getenv("NOSA")) {
604 getNoSA(passphrase, maxPassphraseLength,
605 "Retry old passphrase [<CR> to cancel]: ");
606 return;
607 }
608 #endif
609 MigBoolean addBox;
610 check(secagent_client_retryOldGenericPassphrase(mStagePort, mClientPort,
611 &status, reason, &addBox, passphrase));
612 addToKeychain = addBox;
613 }
614
615
616 //
617 // Ask for a new passphrase for something (with selectable prompt).
618 //
619 void Client::queryNewGenericPassphrase(const OSXCode *requestor, pid_t requestPid,
620 const char *prompt, Reason reason,
621 KeychainBox &addToKeychain, char passphrase[maxPassphraseLength])
622 {
623 Requestor req(requestor);
624
625 #if defined(NOSA)
626 if (getenv("NOSA")) {
627 getNoSA(passphrase, maxPassphraseLength,
628 "New passphrase (\"%s\") (reason %d) [<CR> to cancel]: ",
629 prompt, reason);
630 // @@@ addToKeychain not hooked up; stays unchanged
631 stage = newGenericPassphraseStage;
632 return;
633 }
634 #endif
635 activate();
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;
642 }
643
644 void Client::retryNewGenericPassphrase(Reason reason,
645 bool &addToKeychain, char passphrase[maxPassphraseLength])
646 {
647 if (stage != newGenericPassphraseStage)
648 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
649 #if defined(NOSA)
650 if (getenv("NOSA")) {
651 getNoSA(passphrase, maxPassphraseLength,
652 "retry new passphrase (reason %d) [<CR> to cancel]: ", reason);
653 return;
654 }
655 #endif
656 MigBoolean addBox;
657 check(secagent_client_retryNewGenericPassphrase(mStagePort, mClientPort,
658 &status, reason, &addBox, passphrase));
659 addToKeychain = addBox;
660 }
661
662
663 //
664 // Authorization by authentication
665 //
666 bool Client::authorizationAuthenticate(const OSXCode *requestor, pid_t requestPid,
667 const char *neededGroup, const char *candidateUser,
668 char user[maxUsernameLength], char passphrase[maxPassphraseLength])
669 {
670 Requestor req(requestor);
671
672 #if defined(NOSA)
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, "-");
682 }
683 #endif
684 activate();
685 check(secagent_client_authorizationAuthenticate(mServerPort, mClientPort,
686 &status, req, requestPid, neededGroup, candidateUser, &mStagePort.port(), user, passphrase));
687 stage = authorizeStage;
688 return status == noErr;
689 }
690
691 bool Client::retryAuthorizationAuthenticate(Reason reason, char user[maxUsernameLength],
692 char passphrase[maxPassphraseLength])
693 {
694 if (stage != authorizeStage)
695 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ invent a "state mismatch error"?
696 #if defined(NOSA)
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, "-");
704 }
705 #endif
706 check(secagent_client_retryAuthorizationAuthenticate(mStagePort, mClientPort,
707 &status, reason, user, passphrase));
708 return status == noErr;
709 }
710
711 //
712 // invokeMechanism old style
713 //
714 bool Client::invokeMechanism(const string &inPluginId, const string &inMechanismId, const AuthValueVector &inArguments, AuthItemSet &inHints, AuthItemSet &inContext, AuthorizationResult *outResult)
715 {
716 AuthorizationValueVector *inArgumentVector;
717 AuthorizationItemSet *inHintsSet, *inContextSet;
718 size_t inArgumentVectorLength, inHintsSetLength, inContextSetLength;
719
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);
727
728 COPY_OUT_DECL(AuthorizationItemSet, outHintsSet);
729 COPY_OUT_DECL(AuthorizationItemSet, outContextSet);
730
731 activate();
732
733 // either noErr (user cancel, allow) or throws authInternal
734
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,
740 outResult,
741 COPY_OUT(outHintsSet),
742 COPY_OUT(outContextSet)));
743
744 VMGuard _(outHintsSet, outHintsSetLength);
745 VMGuard _2(outContextSet, outContextSetLength);
746
747 if (status != errAuthorizationDenied)
748 {
749 relocate(outHintsSet, outHintsSetBase);
750 inHints = *outHintsSet;
751 relocate(outContextSet, outContextSetBase);
752 inContext = *outContextSet;
753 }
754
755 return (status == noErr);
756 }
757
758
759 void Client::terminateAgent()
760 {
761 // If the agent is (still) running, kill it
762 if (mClientBootstrap.lookupOptional(mAgentName.c_str()))
763 {
764 activate();
765 check(secagent_client_terminate(mServerPort, mClientPort));
766 }
767 }
768
769 } // end namespace SecurityAgent
770 } // end namespace Security