]> git.saurik.com Git - apple/securityd.git/blob - src/agentquery.cpp
securityd-55137.5.tar.gz
[apple/securityd.git] / src / agentquery.cpp
1 /*
2 * Copyright (c) 2000-2004,2008-2009 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 //
25 // passphrases - canonical code to obtain passphrases
26 //
27 #include "agentquery.h"
28 #include "authority.h"
29 #include "ccaudit_extensions.h"
30
31 #include <Security/AuthorizationTags.h>
32 #include <Security/AuthorizationTagsPriv.h>
33 #include <Security/checkpw.h>
34 #include <System/sys/fileport.h>
35 #include <bsm/audit.h>
36 #include <bsm/audit_uevents.h> // AUE_ssauthint
37 #include <security_utilities/logging.h>
38 #include <security_utilities/mach++.h>
39 #include <stdlib.h>
40
41 //
42 // NOSA support functions. This is a test mode where the SecurityAgent
43 // is simulated via stdio in the client. Good for running automated tests
44 // of client programs. Only available if -DNOSA when compiling.
45 //
46 #if defined(NOSA)
47
48 #include <cstdarg>
49
50 static void getNoSA(char *buffer, size_t bufferSize, const char *fmt, ...)
51 {
52 // write prompt
53 va_list args;
54 va_start(args, fmt);
55 vfprintf(stdout, fmt, args);
56 va_end(args);
57
58 // read reply
59 memset(buffer, 0, bufferSize);
60 const char *nosa = getenv("NOSA");
61 if (!strcmp(nosa, "-")) {
62 if (fgets(buffer, bufferSize-1, stdin) == NULL)
63 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
64 buffer[strlen(buffer)-1] = '\0'; // remove trailing newline
65 if (!isatty(fileno(stdin)))
66 printf("%s\n", buffer); // echo to output if input not terminal
67 } else {
68 strncpy(buffer, nosa, bufferSize-1);
69 printf("%s\n", buffer);
70 }
71 if (buffer[0] == '\0') // empty input -> cancellation
72 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
73 }
74
75 #endif //NOSA
76
77
78 // SecurityAgentConnection
79
80 SecurityAgentConnection::SecurityAgentConnection(const AuthHostType type, Session &session)
81 : mAuthHostType(type),
82 mHostInstance(session.authhost(mAuthHostType)),
83 mConnection(&Server::connection()),
84 mAuditToken(Server::connection().auditToken())
85 {
86 // this may take a while
87 Server::active().longTermActivity();
88 secdebug("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
89 }
90
91 SecurityAgentConnection::~SecurityAgentConnection()
92 {
93 secdebug("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this);
94 mConnection->useAgent(NULL);
95 }
96
97 void
98 SecurityAgentConnection::activate()
99 {
100 secdebug("SecurityAgentConnection", "activate(%p)", this);
101
102 Session &session = mHostInstance->session();
103 SessionId targetSessionId = session.sessionId();
104 MachPlusPlus::Bootstrap processBootstrap = Server::process().taskPort().bootstrap();
105 fileport_t userPrefsFP = MACH_PORT_NULL;
106
107 // send the the userPrefs to SecurityAgent
108 if (mAuthHostType == securityAgent || mAuthHostType == userAuthHost) {
109 CFRef<CFDataRef> userPrefs(mHostInstance->session().copyUserPrefs());
110 if (NULL != userPrefs)
111 {
112 FILE *mbox = NULL;
113 int fd = 0;
114 mbox = tmpfile();
115 if (NULL != mbox)
116 {
117 fd = dup(fileno(mbox));
118 fclose(mbox);
119 if (fd != -1)
120 {
121 CFIndex length = CFDataGetLength(userPrefs);
122 if (write(fd, CFDataGetBytePtr(userPrefs), length) != length)
123 Syslog::error("could not write userPrefs");
124 else
125 {
126 if (0 == fileport_makeport(fd, &userPrefsFP))
127 secdebug("SecurityAgentConnection", "stashed the userPrefs file descriptor");
128 else
129 Syslog::error("failed to stash the userPrefs file descriptor");
130 }
131 close(fd);
132 }
133 }
134 }
135 if (MACH_PORT_NULL == userPrefsFP)
136 {
137 secdebug("SecurityAgentConnection", "could not read userPrefs");
138 }
139 }
140
141 mConnection->useAgent(this);
142 try
143 {
144 StLock<Mutex> _(*mHostInstance);
145
146 mach_port_t lookupPort = mHostInstance->lookup(targetSessionId);
147 if (MACH_PORT_NULL == lookupPort)
148 {
149 Syslog::error("could not find real service, bailing");
150 MacOSError::throwMe(CSSM_ERRCODE_SERVICE_NOT_AVAILABLE);
151 }
152 // reset Client contact info
153 mPort = lookupPort;
154 SecurityAgent::Client::activate(mPort);
155
156 secdebug("SecurityAgentConnection", "%p activated", this);
157 }
158 catch (MacOSError &err)
159 {
160 mConnection->useAgent(NULL); // guess not
161 Syslog::error("SecurityAgentConnection: error activating %s instance %p",
162 mAuthHostType == privilegedAuthHost
163 ? "authorizationhost"
164 : "SecurityAgent", this);
165 throw;
166 }
167
168 secdebug("SecurityAgentConnection", "contacting service (%p)", this);
169 mach_port_name_t jobPort;
170 if (0 > audit_session_port(session.sessionId(), &jobPort))
171 Syslog::error("audit_session_port failed: %m");
172 MacOSError::check(SecurityAgent::Client::contact(jobPort, processBootstrap, userPrefsFP));
173 secdebug("SecurityAgentConnection", "contact didn't throw (%p)", this);
174
175 if (userPrefsFP != MACH_PORT_NULL)
176 mach_port_deallocate(mach_task_self(), userPrefsFP);
177 }
178
179 void
180 SecurityAgentConnection::reconnect()
181 {
182 // if !mHostInstance throw()?
183 if (mHostInstance)
184 {
185 activate();
186 }
187 }
188
189 void
190 SecurityAgentConnection::terminate()
191 {
192 activate();
193
194 // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
195 mConnection->useAgent(NULL);
196 }
197
198
199 // SecurityAgentTransaction
200
201 SecurityAgentTransaction::SecurityAgentTransaction(const AuthHostType type, Session &session, bool startNow)
202 : SecurityAgentConnection(type, session),
203 mStarted(false)
204 {
205 secdebug("SecurityAgentTransaction", "New SecurityAgentTransaction(%p)", this);
206 activate(); // start agent now, or other SAConnections will kill and spawn new agents
207 if (startNow)
208 start();
209 }
210
211 SecurityAgentTransaction::~SecurityAgentTransaction()
212 {
213 try { end(); } catch(...) {}
214 secdebug("SecurityAgentTransaction", "Destroying %p", this);
215 }
216
217 void
218 SecurityAgentTransaction::start()
219 {
220 secdebug("SecurityAgentTransaction", "start(%p)", this);
221 MacOSError::check(SecurityAgentQuery::Client::startTransaction(mPort));
222 mStarted = true;
223 secdebug("SecurityAgentTransaction", "started(%p)", this);
224 }
225
226 void
227 SecurityAgentTransaction::end()
228 {
229 if (started())
230 {
231 MacOSError::check(SecurityAgentQuery::Client::endTransaction(mPort));
232 mStarted = false;
233 }
234 secdebug("SecurityAgentTransaction", "End SecurityAgentTransaction(%p)", this);
235 }
236
237 using SecurityAgent::Reason;
238 using namespace Authorization;
239
240 SecurityAgentQuery::SecurityAgentQuery(const AuthHostType type, Session &session)
241 : SecurityAgentConnection(type, session)
242 {
243 secdebug("SecurityAgentQuery", "new SecurityAgentQuery(%p)", this);
244 }
245
246 SecurityAgentQuery::~SecurityAgentQuery()
247 {
248 secdebug("SecurityAgentQuery", "SecurityAgentQuery(%p) dying", this);
249
250 #if defined(NOSA)
251 if (getenv("NOSA")) {
252 printf(" [query done]\n");
253 return;
254 }
255 #endif
256
257 if (SecurityAgent::Client::state() != SecurityAgent::Client::dead)
258 destroy();
259 }
260
261 void
262 SecurityAgentQuery::inferHints(Process &thisProcess)
263 {
264 string guestPath;
265 {
266 StLock<Mutex> _(thisProcess);
267 if (SecCodeRef clientCode = thisProcess.currentGuest())
268 guestPath = codePath(clientCode);
269 }
270 AuthItemSet processHints = clientHints(SecurityAgent::bundle, guestPath,
271 thisProcess.pid(), thisProcess.uid());
272 mClientHints.insert(processHints.begin(), processHints.end());
273 }
274
275 void SecurityAgentQuery::addHint(const char *name, const void *value, UInt32 valueLen, UInt32 flags)
276 {
277 AuthorizationItem item = { name, valueLen, const_cast<void *>(value), flags };
278 mClientHints.insert(AuthItemRef(item));
279 }
280
281
282 void
283 SecurityAgentQuery::readChoice()
284 {
285 allow = false;
286 remember = false;
287
288 AuthItem *allowAction = outContext().find(AGENT_CONTEXT_ALLOW);
289 if (allowAction)
290 {
291 string allowString;
292 if (allowAction->getString(allowString)
293 && (allowString == "YES"))
294 allow = true;
295 }
296
297 AuthItem *rememberAction = outContext().find(AGENT_CONTEXT_REMEMBER_ACTION);
298 if (rememberAction)
299 {
300 string rememberString;
301 if (rememberAction->getString(rememberString)
302 && (rememberString == "YES"))
303 remember = true;
304 }
305 }
306
307 void
308 SecurityAgentQuery::disconnect()
309 {
310 SecurityAgent::Client::destroy();
311 }
312
313 void
314 SecurityAgentQuery::terminate()
315 {
316 // you might think these are called in the wrong order, but you'd be wrong
317 SecurityAgentConnection::terminate();
318 SecurityAgent::Client::terminate();
319 }
320
321 void
322 SecurityAgentQuery::create(const char *pluginId, const char *mechanismId, const SessionId inSessionId)
323 {
324 activate();
325 OSStatus status = SecurityAgent::Client::create(pluginId, mechanismId, inSessionId);
326 if (status)
327 {
328 secdebug("SecurityAgentQuery", "agent went walkabout, restarting");
329 reconnect();
330 status = SecurityAgent::Client::create(pluginId, mechanismId, inSessionId);
331 }
332 if (status) MacOSError::throwMe(status);
333 }
334
335 //
336 // Perform the "rogue app" access query dialog
337 //
338 QueryKeychainUse::QueryKeychainUse(bool needPass, const Database *db)
339 : mPassphraseCheck(NULL)
340 {
341 // if passphrase checking requested, save KeychainDatabase reference
342 // (will quietly disable check if db isn't a keychain)
343 if (needPass)
344 mPassphraseCheck = dynamic_cast<const KeychainDatabase *>(db);
345
346 setTerminateOnSleep(true);
347 }
348
349 Reason QueryKeychainUse::queryUser (const char *database, const char *description, AclAuthorization action)
350 {
351 Reason reason = SecurityAgent::noReason;
352 int retryCount = 0;
353 OSStatus status;
354 AuthValueVector arguments;
355 AuthItemSet hints, context;
356
357 #if defined(NOSA)
358 if (getenv("NOSA")) {
359 char answer[maxPassphraseLength+10];
360
361 string applicationPath;
362 AuthItem *applicationPathItem = mClientHints.find(AGENT_HINT_APPLICATION_PATH);
363 if (applicationPathItem)
364 applicationPathItem->getString(applicationPath);
365
366 getNoSA(answer, sizeof(answer), "Allow %s to do %d on %s in %s? [yn][g]%s ",
367 applicationPath.c_str(), int(action), (description ? description : "[NULL item]"),
368 (database ? database : "[NULL database]"),
369 mPassphraseCheck ? ":passphrase" : "");
370 // turn passphrase (no ':') into y:passphrase
371 if (mPassphraseCheck && !strchr(answer, ':')) {
372 memmove(answer+2, answer, strlen(answer)+1);
373 memcpy(answer, "y:", 2);
374 }
375
376 allow = answer[0] == 'y';
377 remember = answer[1] == 'g';
378 return SecurityAgent::noReason;
379 }
380 #endif
381
382 // prepopulate with client hints
383 hints.insert(mClientHints.begin(), mClientHints.end());
384
385 // put action/operation (sint32) into hints
386 hints.insert(AuthItemRef(AGENT_HINT_ACL_TAG, AuthValueOverlay(sizeof(action), static_cast<sint32*>(&action))));
387
388 // item name into hints
389
390 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME, AuthValueOverlay(description ? strlen(description) : 0, const_cast<char*>(description))));
391
392 // keychain name into hints
393 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(database ? strlen(database) : 0, const_cast<char*>(database))));
394
395
396 if (mPassphraseCheck)
397 {
398 create("builtin", "confirm-access-password", noSecuritySession);
399
400 CssmAutoData data(Allocator::standard(Allocator::sensitive));
401
402 do
403 {
404
405 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
406 hints.erase(triesHint); hints.insert(triesHint); // replace
407
408 if (retryCount++ > kMaximumAuthorizationTries)
409 {
410 reason = SecurityAgent::tooManyTries;
411 }
412
413 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
414 hints.erase(retryHint); hints.insert(retryHint); // replace
415
416 setInput(hints, context);
417 status = invoke();
418
419 if (retryCount > kMaximumAuthorizationTries)
420 {
421 return reason;
422 }
423
424 checkResult();
425
426 AuthItem *passwordItem = outContext().find(kAuthorizationEnvironmentPassword);
427 if (!passwordItem)
428 continue;
429
430 passwordItem->getCssmData(data);
431 }
432 while (reason = (const_cast<KeychainDatabase*>(mPassphraseCheck)->decode(data) ? SecurityAgent::noReason : SecurityAgent::invalidPassphrase));
433 }
434 else
435 {
436 create("builtin", "confirm-access", noSecuritySession);
437 setInput(hints, context);
438 invoke();
439 }
440
441 readChoice();
442
443 return reason;
444 }
445
446 //
447 // Perform code signature ACL access adjustment dialogs
448 //
449 bool QueryCodeCheck::operator () (const char *aclPath)
450 {
451 OSStatus status;
452 AuthValueVector arguments;
453 AuthItemSet hints, context;
454
455 #if defined(NOSA)
456 if (getenv("NOSA")) {
457 char answer[10];
458
459 string applicationPath;
460 AuthItem *applicationPathItem = mClientHints.find(AGENT_HINT_APPLICATION_PATH);
461 if (applicationPathItem)
462 applicationPathItem->getString(applicationPath);
463
464 getNoSA(answer, sizeof(answer),
465 "Allow %s to match an ACL for %s [yn][g]? ",
466 applicationPath.c_str(), aclPath ? aclPath : "(unknown)");
467 allow = answer[0] == 'y';
468 remember = answer[1] == 'g';
469 return;
470 }
471 #endif
472
473 // prepopulate with client hints
474 hints.insert(mClientHints.begin(), mClientHints.end());
475
476 hints.insert(AuthItemRef(AGENT_HINT_APPLICATION_PATH, AuthValueOverlay(strlen(aclPath), const_cast<char*>(aclPath))));
477
478 create("builtin", "code-identity", noSecuritySession);
479
480 setInput(hints, context);
481 status = invoke();
482
483 checkResult();
484
485 // MacOSError::check(status);
486
487 return kAuthorizationResultAllow == result();
488 }
489
490
491 //
492 // Obtain passphrases and submit them to the accept() method until it is accepted
493 // or we can't get another passphrase. Accept() should consume the passphrase
494 // if it is accepted. If no passphrase is acceptable, throw out of here.
495 //
496 Reason QueryOld::query()
497 {
498 Reason reason = SecurityAgent::noReason;
499 OSStatus status;
500 AuthValueVector arguments;
501 AuthItemSet hints, context;
502 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
503 int retryCount = 0;
504
505 #if defined(NOSA)
506 // return the passphrase
507 if (getenv("NOSA")) {
508 char passphrase_[maxPassphraseLength];
509 getNoSA(passphrase, maxPassphraseLength, "Unlock %s [<CR> to cancel]: ", database.dbName());
510 passphrase.copy(passphrase_, strlen(passphrase_));
511 return database.decode(passphrase) ? SecurityAgent::noReason : SecurityAgent::invalidPassphrase;
512 }
513 #endif
514
515 // prepopulate with client hints
516
517 const char *keychainPath = database.dbName();
518 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(strlen(keychainPath), const_cast<char*>(keychainPath))));
519
520 hints.insert(mClientHints.begin(), mClientHints.end());
521
522 create("builtin", "unlock-keychain", noSecuritySession);
523
524 do
525 {
526 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
527 hints.erase(triesHint); hints.insert(triesHint); // replace
528
529 ++retryCount;
530
531 if (retryCount > maxTries)
532 {
533 reason = SecurityAgent::tooManyTries;
534 }
535
536 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
537 hints.erase(retryHint); hints.insert(retryHint); // replace
538
539 setInput(hints, context);
540 status = invoke();
541
542 if (retryCount > maxTries)
543 {
544 return reason;
545 }
546
547 checkResult();
548
549 AuthItem *passwordItem = outContext().find(kAuthorizationEnvironmentPassword);
550 if (!passwordItem)
551 continue;
552
553 passwordItem->getCssmData(passphrase);
554
555 }
556 while (reason = accept(passphrase));
557
558 return SecurityAgent::noReason;
559 }
560
561
562 //
563 // Get existing passphrase (unlock) Query
564 //
565 Reason QueryOld::operator () ()
566 {
567 return query();
568 }
569
570
571 //
572 // End-classes for old secrets
573 //
574 Reason QueryUnlock::accept(CssmManagedData &passphrase)
575 {
576 if (safer_cast<KeychainDatabase &>(database).decode(passphrase))
577 return SecurityAgent::noReason;
578 else
579 return SecurityAgent::invalidPassphrase;
580 }
581
582
583 QueryPIN::QueryPIN(Database &db)
584 : QueryOld(db), mPin(Allocator::standard())
585 {
586 this->inferHints(Server::process());
587 }
588
589
590 Reason QueryPIN::accept(CssmManagedData &pin)
591 {
592 // no retries for now
593 mPin = pin;
594 return SecurityAgent::noReason;
595 }
596
597
598 //
599 // Obtain passphrases and submit them to the accept() method until it is accepted
600 // or we can't get another passphrase. Accept() should consume the passphrase
601 // if it is accepted. If no passphrase is acceptable, throw out of here.
602 //
603 Reason QueryNewPassphrase::query()
604 {
605 Reason reason = initialReason;
606 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
607 CssmAutoData oldPassphrase(Allocator::standard(Allocator::sensitive));
608
609 OSStatus status;
610 AuthValueVector arguments;
611 AuthItemSet hints, context;
612
613 int retryCount = 0;
614
615 #if defined(NOSA)
616 if (getenv("NOSA")) {
617 char passphrase_[maxPassphraseLength];
618 getNoSA(passphrase_, maxPassphraseLength,
619 "New passphrase for %s (reason %d) [<CR> to cancel]: ",
620 database.dbName(), reason);
621 return SecurityAgent::noReason;
622 }
623 #endif
624
625 // prepopulate with client hints
626 hints.insert(mClientHints.begin(), mClientHints.end());
627
628 // keychain name into hints
629 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(database.dbName())));
630
631 switch (initialReason)
632 {
633 case SecurityAgent::newDatabase:
634 create("builtin", "new-passphrase", noSecuritySession);
635 break;
636 case SecurityAgent::changePassphrase:
637 create("builtin", "change-passphrase", noSecuritySession);
638 break;
639 default:
640 assert(false);
641 }
642
643 do
644 {
645 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
646 hints.erase(triesHint); hints.insert(triesHint); // replace
647
648 if (++retryCount > maxTries)
649 {
650 reason = SecurityAgent::tooManyTries;
651 }
652
653 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
654 hints.erase(retryHint); hints.insert(retryHint); // replace
655
656 setInput(hints, context);
657 status = invoke();
658
659 if (retryCount > maxTries)
660 {
661 return reason;
662 }
663
664 checkResult();
665
666 if (SecurityAgent::changePassphrase == initialReason)
667 {
668 AuthItem *oldPasswordItem = outContext().find(AGENT_PASSWORD);
669 if (!oldPasswordItem)
670 continue;
671
672 oldPasswordItem->getCssmData(oldPassphrase);
673 }
674
675 AuthItem *passwordItem = outContext().find(AGENT_CONTEXT_NEW_PASSWORD);
676 if (!passwordItem)
677 continue;
678
679 passwordItem->getCssmData(passphrase);
680
681 }
682 while (reason = accept(passphrase, (initialReason == SecurityAgent::changePassphrase) ? &oldPassphrase.get() : NULL));
683
684 return SecurityAgent::noReason;
685 }
686
687
688 //
689 // Get new passphrase Query
690 //
691 Reason QueryNewPassphrase::operator () (CssmOwnedData &passphrase)
692 {
693 if (Reason result = query())
694 return result; // failed
695 passphrase = mPassphrase;
696 return SecurityAgent::noReason; // success
697 }
698
699 Reason QueryNewPassphrase::accept(CssmManagedData &passphrase, CssmData *oldPassphrase)
700 {
701 //@@@ acceptance criteria are currently hardwired here
702 //@@@ This validation presumes ASCII - UTF8 might be more lenient
703
704 // if we have an old passphrase, check it
705 if (oldPassphrase && !safer_cast<KeychainDatabase&>(database).validatePassphrase(*oldPassphrase))
706 return SecurityAgent::oldPassphraseWrong;
707
708 // sanity check the new passphrase (but allow user override)
709 if (!(mPassphraseValid && passphrase.get() == mPassphrase)) {
710 mPassphrase = passphrase;
711 mPassphraseValid = true;
712 if (mPassphrase.length() == 0)
713 return SecurityAgent::passphraseIsNull;
714 if (mPassphrase.length() < 6)
715 return SecurityAgent::passphraseTooSimple;
716 }
717
718 // accept this
719 return SecurityAgent::noReason;
720 }
721
722 //
723 // Get a passphrase for unspecified use
724 //
725 Reason QueryGenericPassphrase::operator () (const CssmData *prompt, bool verify,
726 string &passphrase)
727 {
728 return query(prompt, verify, passphrase);
729 }
730
731 Reason QueryGenericPassphrase::query(const CssmData *prompt, bool verify,
732 string &passphrase)
733 {
734 Reason reason = SecurityAgent::noReason;
735 OSStatus status; // not really used; remove?
736 AuthValueVector arguments;
737 AuthItemSet hints, context;
738
739 #if defined(NOSA)
740 if (getenv("NOSA")) {
741 // FIXME 3690984
742 return SecurityAgent::noReason;
743 }
744 #endif
745
746 hints.insert(mClientHints.begin(), mClientHints.end());
747 hints.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT, AuthValueOverlay(prompt ? (UInt32)prompt->length() : 0, prompt ? prompt->data() : NULL)));
748 // XXX/gh defined by dmitch but no analogous hint in
749 // AuthorizationTagsPriv.h:
750 // CSSM_ATTRIBUTE_ALERT_TITLE (optional alert panel title)
751
752 if (false == verify) { // import
753 create("builtin", "generic-unlock", noSecuritySession);
754 } else { // verify passphrase (export)
755 // new-passphrase-generic works with the pre-4 June 2004 agent;
756 // generic-new-passphrase is required for the new agent
757 create("builtin", "generic-new-passphrase", noSecuritySession);
758 }
759
760 AuthItem *passwordItem;
761
762 do {
763 setInput(hints, context);
764 status = invoke();
765 checkResult();
766 passwordItem = outContext().find(AGENT_PASSWORD);
767
768 } while (!passwordItem);
769
770 passwordItem->getString(passphrase);
771
772 return reason;
773 }
774
775
776 //
777 // Get a DB blob's passphrase--keychain synchronization
778 //
779 Reason QueryDBBlobSecret::operator () (DbHandle *dbHandleArray, uint8 dbHandleArrayCount, DbHandle *dbHandleAuthenticated)
780 {
781 return query(dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated);
782 }
783
784 Reason QueryDBBlobSecret::query(DbHandle *dbHandleArray, uint8 dbHandleArrayCount, DbHandle *dbHandleAuthenticated)
785 {
786 Reason reason = SecurityAgent::noReason;
787 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
788 OSStatus status; // not really used; remove?
789 AuthValueVector arguments;
790 AuthItemSet hints/*NUKEME*/, context;
791
792 #if defined(NOSA)
793 if (getenv("NOSA")) {
794 // FIXME akin to 3690984
795 return SecurityAgent::noReason;
796 }
797 #endif
798
799 hints.insert(mClientHints.begin(), mClientHints.end());
800
801 create("builtin", "generic-unlock-kcblob", noSecuritySession);
802
803 AuthItem *secretItem;
804
805 int retryCount = 0;
806
807 do {
808 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
809 hints.erase(triesHint); hints.insert(triesHint); // replace
810
811 if (++retryCount > maxTries)
812 {
813 reason = SecurityAgent::tooManyTries;
814 }
815
816 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
817 hints.erase(retryHint); hints.insert(retryHint); // replace
818
819 setInput(hints, context);
820 status = invoke();
821 checkResult();
822 secretItem = outContext().find(AGENT_PASSWORD);
823 if (!secretItem)
824 continue;
825 secretItem->getCssmData(passphrase);
826
827 } while (reason = accept(passphrase, dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated));
828
829 return reason;
830 }
831
832 Reason QueryDBBlobSecret::accept(CssmManagedData &passphrase,
833 DbHandle *dbHandlesToAuthenticate, uint8 dbHandleCount, DbHandle *dbHandleAuthenticated)
834 {
835 DbHandle *currHdl = dbHandlesToAuthenticate;
836 short index;
837 Boolean authenticated = false;
838 for (index=0; index < dbHandleCount && !authenticated; index++)
839 {
840 try
841 {
842 RefPointer<KeychainDatabase> dbToUnlock = Server::keychain(*currHdl);
843 dbToUnlock->unlockDb(passphrase);
844 authenticated = true;
845 *dbHandleAuthenticated = *currHdl; // return the DbHandle that 'passphrase' authenticated with.
846 }
847 catch (const CommonError &err)
848 {
849 currHdl++; // we failed to authenticate with this one, onto the next one.
850 }
851 }
852 if ( !authenticated )
853 return SecurityAgent::invalidPassphrase;
854
855 return SecurityAgent::noReason;
856 }
857
858 QueryInvokeMechanism::QueryInvokeMechanism(const AuthHostType type, Session &session) :
859 SecurityAgentQuery(type, session) { }
860
861 void QueryInvokeMechanism::initialize(const string &inPluginId, const string &inMechanismId, const AuthValueVector &inArguments, const SessionId inSessionId)
862 {
863 if (SecurityAgent::Client::init == SecurityAgent::Client::state())
864 {
865 create(inPluginId.c_str(), inMechanismId.c_str(), inSessionId);
866 mArguments = inArguments;
867 }
868 }
869
870 // XXX/cs should return AuthorizationResult
871 void QueryInvokeMechanism::run(const AuthValueVector &inArguments, AuthItemSet &inHints, AuthItemSet &inContext, AuthorizationResult *outResult)
872 {
873 // prepopulate with client hints
874 inHints.insert(mClientHints.begin(), mClientHints.end());
875
876 if (mAuthHostType == securityAgent) {
877 if (Server::active().inDarkWake())
878 CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE);
879 }
880
881 setArguments(inArguments);
882 setInput(inHints, inContext);
883 MacOSError::check(invoke());
884
885 if (outResult) *outResult = result();
886
887 inHints = outHints();
888 inContext = outContext();
889 }
890
891 void QueryInvokeMechanism::terminateAgent()
892 {
893 terminate();
894 }
895
896 // @@@ no pluggable authentication possible!
897 Reason
898 QueryKeychainAuth::operator () (const char *database, const char *description, AclAuthorization action, const char *prompt)
899 {
900 Reason reason = SecurityAgent::noReason;
901 AuthItemSet hints, context;
902 AuthValueVector arguments;
903 int retryCount = 0;
904 string username;
905 string password;
906
907 using CommonCriteria::Securityd::KeychainAuthLogger;
908 KeychainAuthLogger logger(mAuditToken, AUE_ssauthint, database, description);
909
910 #if defined(NOSA)
911 /* XXX/gh probably not complete; stolen verbatim from rogue-app query */
912 if (getenv("NOSA")) {
913 char answer[maxPassphraseLength+10];
914
915 string applicationPath;
916 AuthItem *applicationPathItem = mClientHints.find(AGENT_HINT_APPLICATION_PATH);
917 if (applicationPathItem)
918 applicationPathItem->getString(applicationPath);
919
920 getNoSA(answer, sizeof(answer), "Allow %s to do %d on %s in %s? [yn][g]%s ",
921 applicationPath.c_str(), int(action), (description ? description : "[NULL item]"),
922 (database ? database : "[NULL database]"),
923 mPassphraseCheck ? ":passphrase" : "");
924 // turn passphrase (no ':') into y:passphrase
925 if (mPassphraseCheck && !strchr(answer, ':')) {
926 memmove(answer+2, answer, strlen(answer)+1);
927 memcpy(answer, "y:", 2);
928 }
929
930 allow = answer[0] == 'y';
931 remember = answer[1] == 'g';
932 return SecurityAgent::noReason;
933 }
934 #endif
935
936 hints.insert(mClientHints.begin(), mClientHints.end());
937
938 // put action/operation (sint32) into hints
939 hints.insert(AuthItemRef(AGENT_HINT_ACL_TAG, AuthValueOverlay(sizeof(action), static_cast<sint32*>(&action))));
940
941 hints.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT, AuthValueOverlay(prompt ? strlen(prompt) : 0, const_cast<char*>(prompt))));
942
943 // item name into hints
944 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME, AuthValueOverlay(description ? strlen(description) : 0, const_cast<char*>(description))));
945
946 // keychain name into hints
947 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(database ? strlen(database) : 0, const_cast<char*>(database))));
948
949 create("builtin", "confirm-access-user-password", noSecuritySession);
950
951 AuthItem *usernameItem;
952 AuthItem *passwordItem;
953
954 do {
955
956 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
957 hints.erase(triesHint); hints.insert(triesHint); // replace
958
959 if (++retryCount > maxTries)
960 reason = SecurityAgent::tooManyTries;
961
962 if (SecurityAgent::noReason != reason)
963 {
964 if (SecurityAgent::tooManyTries == reason)
965 logger.logFailure(NULL, CommonCriteria::errTooManyTries);
966 else
967 logger.logFailure();
968 }
969
970 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
971 hints.erase(retryHint); hints.insert(retryHint); // replace
972
973 setInput(hints, context);
974 try
975 {
976 invoke();
977 checkResult();
978 }
979 catch (...) // user probably clicked "deny"
980 {
981 logger.logFailure();
982 throw;
983 }
984 usernameItem = outContext().find(AGENT_USERNAME);
985 passwordItem = outContext().find(AGENT_PASSWORD);
986 if (!usernameItem || !passwordItem)
987 continue;
988 usernameItem->getString(username);
989 passwordItem->getString(password);
990 } while (reason = accept(username, password));
991
992 if (SecurityAgent::noReason == reason)
993 logger.logSuccess();
994 // else we logged the denial in the loop
995
996 return reason;
997 }
998
999 Reason
1000 QueryKeychainAuth::accept(string &username, string &passphrase)
1001 {
1002 const char *user = username.c_str();
1003 const char *passwd = passphrase.c_str();
1004 int checkpw_status = checkpw(user, passwd);
1005
1006 if (checkpw_status != CHECKPW_SUCCESS)
1007 return SecurityAgent::invalidPassphrase;
1008
1009 return SecurityAgent::noReason;
1010 }
1011