2 * Copyright (c) 2000-2004,2008-2009 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 // passphrases - canonical code to obtain passphrases
27 #include "agentquery.h"
28 #include "authority.h"
29 #include "ccaudit_extensions.h"
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>
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.
50 static void getNoSA(char *buffer
, size_t bufferSize
, const char *fmt
, ...)
55 vfprintf(stdout
, fmt
, args
);
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
68 strncpy(buffer
, nosa
, bufferSize
-1);
69 printf("%s\n", buffer
);
71 if (buffer
[0] == '\0') // empty input -> cancellation
72 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED
);
78 // SecurityAgentConnection
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())
86 // this may take a while
87 Server::active().longTermActivity();
88 secdebug("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
91 SecurityAgentConnection::~SecurityAgentConnection()
93 secdebug("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this);
94 mConnection
->useAgent(NULL
);
98 SecurityAgentConnection::activate()
100 secdebug("SecurityAgentConnection", "activate(%p)", this);
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
;
107 // send the the userPrefs to SecurityAgent
108 if (mAuthHostType
== securityAgent
|| mAuthHostType
== userAuthHost
) {
109 CFRef
<CFDataRef
> userPrefs(mHostInstance
->session().copyUserPrefs());
110 if (NULL
!= userPrefs
)
117 fd
= dup(fileno(mbox
));
121 CFIndex length
= CFDataGetLength(userPrefs
);
122 if (write(fd
, CFDataGetBytePtr(userPrefs
), length
) != length
)
123 Syslog::error("could not write userPrefs");
126 if (0 == fileport_makeport(fd
, &userPrefsFP
))
127 secdebug("SecurityAgentConnection", "stashed the userPrefs file descriptor");
129 Syslog::error("failed to stash the userPrefs file descriptor");
135 if (MACH_PORT_NULL
== userPrefsFP
)
137 secdebug("SecurityAgentConnection", "could not read userPrefs");
141 mConnection
->useAgent(this);
144 StLock
<Mutex
> _(*mHostInstance
);
146 mach_port_t lookupPort
= mHostInstance
->lookup(targetSessionId
);
147 if (MACH_PORT_NULL
== lookupPort
)
149 Syslog::error("could not find real service, bailing");
150 MacOSError::throwMe(CSSM_ERRCODE_SERVICE_NOT_AVAILABLE
);
152 // reset Client contact info
154 SecurityAgent::Client::activate(mPort
);
156 secdebug("SecurityAgentConnection", "%p activated", this);
158 catch (MacOSError
&err
)
160 mConnection
->useAgent(NULL
); // guess not
161 Syslog::error("SecurityAgentConnection: error activating %s instance %p",
162 mAuthHostType
== privilegedAuthHost
163 ? "authorizationhost"
164 : "SecurityAgent", this);
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);
175 if (userPrefsFP
!= MACH_PORT_NULL
)
176 mach_port_deallocate(mach_task_self(), userPrefsFP
);
180 SecurityAgentConnection::reconnect()
182 // if !mHostInstance throw()?
190 SecurityAgentConnection::terminate()
194 // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
195 mConnection
->useAgent(NULL
);
199 // SecurityAgentTransaction
201 SecurityAgentTransaction::SecurityAgentTransaction(const AuthHostType type
, Session
&session
, bool startNow
)
202 : SecurityAgentConnection(type
, session
),
205 secdebug("SecurityAgentTransaction", "New SecurityAgentTransaction(%p)", this);
206 activate(); // start agent now, or other SAConnections will kill and spawn new agents
211 SecurityAgentTransaction::~SecurityAgentTransaction()
213 try { end(); } catch(...) {}
214 secdebug("SecurityAgentTransaction", "Destroying %p", this);
218 SecurityAgentTransaction::start()
220 secdebug("SecurityAgentTransaction", "start(%p)", this);
221 MacOSError::check(SecurityAgentQuery::Client::startTransaction(mPort
));
223 secdebug("SecurityAgentTransaction", "started(%p)", this);
227 SecurityAgentTransaction::end()
231 MacOSError::check(SecurityAgentQuery::Client::endTransaction(mPort
));
234 secdebug("SecurityAgentTransaction", "End SecurityAgentTransaction(%p)", this);
237 using SecurityAgent::Reason
;
238 using namespace Authorization
;
240 SecurityAgentQuery::SecurityAgentQuery(const AuthHostType type
, Session
&session
)
241 : SecurityAgentConnection(type
, session
)
243 secdebug("SecurityAgentQuery", "new SecurityAgentQuery(%p)", this);
246 SecurityAgentQuery::~SecurityAgentQuery()
248 secdebug("SecurityAgentQuery", "SecurityAgentQuery(%p) dying", this);
251 if (getenv("NOSA")) {
252 printf(" [query done]\n");
257 if (SecurityAgent::Client::state() != SecurityAgent::Client::dead
)
262 SecurityAgentQuery::inferHints(Process
&thisProcess
)
265 if (SecCodeRef clientCode
= thisProcess
.currentGuest())
266 guestPath
= codePath(clientCode
);
267 AuthItemSet processHints
= clientHints(SecurityAgent::bundle
, guestPath
,
268 thisProcess
.pid(), thisProcess
.uid());
269 mClientHints
.insert(processHints
.begin(), processHints
.end());
272 void SecurityAgentQuery::addHint(const char *name
, const void *value
, UInt32 valueLen
, UInt32 flags
)
274 AuthorizationItem item
= { name
, valueLen
, const_cast<void *>(value
), flags
};
275 mClientHints
.insert(AuthItemRef(item
));
280 SecurityAgentQuery::readChoice()
285 AuthItem
*allowAction
= outContext().find(AGENT_CONTEXT_ALLOW
);
289 if (allowAction
->getString(allowString
)
290 && (allowString
== "YES"))
294 AuthItem
*rememberAction
= outContext().find(AGENT_CONTEXT_REMEMBER_ACTION
);
297 string rememberString
;
298 if (rememberAction
->getString(rememberString
)
299 && (rememberString
== "YES"))
305 SecurityAgentQuery::disconnect()
307 SecurityAgent::Client::destroy();
311 SecurityAgentQuery::terminate()
313 // you might think these are called in the wrong order, but you'd be wrong
314 SecurityAgentConnection::terminate();
315 SecurityAgent::Client::terminate();
319 SecurityAgentQuery::create(const char *pluginId
, const char *mechanismId
, const SessionId inSessionId
)
322 OSStatus status
= SecurityAgent::Client::create(pluginId
, mechanismId
, inSessionId
);
325 secdebug("SecurityAgentQuery", "agent went walkabout, restarting");
327 status
= SecurityAgent::Client::create(pluginId
, mechanismId
, inSessionId
);
329 if (status
) MacOSError::throwMe(status
);
333 // Perform the "rogue app" access query dialog
335 QueryKeychainUse::QueryKeychainUse(bool needPass
, const Database
*db
)
336 : mPassphraseCheck(NULL
)
338 // if passphrase checking requested, save KeychainDatabase reference
339 // (will quietly disable check if db isn't a keychain)
341 mPassphraseCheck
= dynamic_cast<const KeychainDatabase
*>(db
);
344 Reason
QueryKeychainUse::queryUser (const char *database
, const char *description
, AclAuthorization action
)
346 Reason reason
= SecurityAgent::noReason
;
349 AuthValueVector arguments
;
350 AuthItemSet hints
, context
;
353 if (getenv("NOSA")) {
354 char answer
[maxPassphraseLength
+10];
356 string applicationPath
;
357 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
358 if (applicationPathItem
)
359 applicationPathItem
->getString(applicationPath
);
361 getNoSA(answer
, sizeof(answer
), "Allow %s to do %d on %s in %s? [yn][g]%s ",
362 applicationPath
.c_str(), int(action
), (description
? description
: "[NULL item]"),
363 (database
? database
: "[NULL database]"),
364 mPassphraseCheck
? ":passphrase" : "");
365 // turn passphrase (no ':') into y:passphrase
366 if (mPassphraseCheck
&& !strchr(answer
, ':')) {
367 memmove(answer
+2, answer
, strlen(answer
)+1);
368 memcpy(answer
, "y:", 2);
371 allow
= answer
[0] == 'y';
372 remember
= answer
[1] == 'g';
373 return SecurityAgent::noReason
;
377 // prepopulate with client hints
378 hints
.insert(mClientHints
.begin(), mClientHints
.end());
380 // put action/operation (sint32) into hints
381 hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
))));
383 // item name into hints
385 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description
? strlen(description
) : 0, const_cast<char*>(description
))));
387 // keychain name into hints
388 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
? strlen(database
) : 0, const_cast<char*>(database
))));
391 if (mPassphraseCheck
)
393 create("builtin", "confirm-access-password", noSecuritySession
);
395 CssmAutoData
data(Allocator::standard(Allocator::sensitive
));
400 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
401 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
403 if (retryCount
++ > kMaximumAuthorizationTries
)
405 reason
= SecurityAgent::tooManyTries
;
408 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
409 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
411 setInput(hints
, context
);
414 if (retryCount
> kMaximumAuthorizationTries
)
421 AuthItem
*passwordItem
= outContext().find(kAuthorizationEnvironmentPassword
);
425 passwordItem
->getCssmData(data
);
427 while (reason
= (const_cast<KeychainDatabase
*>(mPassphraseCheck
)->decode(data
) ? SecurityAgent::noReason
: SecurityAgent::invalidPassphrase
));
431 create("builtin", "confirm-access", noSecuritySession
);
432 setInput(hints
, context
);
442 // Perform code signature ACL access adjustment dialogs
444 bool QueryCodeCheck::operator () (const char *aclPath
)
447 AuthValueVector arguments
;
448 AuthItemSet hints
, context
;
451 if (getenv("NOSA")) {
454 string applicationPath
;
455 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
456 if (applicationPathItem
)
457 applicationPathItem
->getString(applicationPath
);
459 getNoSA(answer
, sizeof(answer
),
460 "Allow %s to match an ACL for %s [yn][g]? ",
461 applicationPath
.c_str(), aclPath
? aclPath
: "(unknown)");
462 allow
= answer
[0] == 'y';
463 remember
= answer
[1] == 'g';
468 // prepopulate with client hints
469 hints
.insert(mClientHints
.begin(), mClientHints
.end());
471 hints
.insert(AuthItemRef(AGENT_HINT_APPLICATION_PATH
, AuthValueOverlay(strlen(aclPath
), const_cast<char*>(aclPath
))));
473 create("builtin", "code-identity", noSecuritySession
);
475 setInput(hints
, context
);
480 // MacOSError::check(status);
482 return kAuthorizationResultAllow
== result();
487 // Obtain passphrases and submit them to the accept() method until it is accepted
488 // or we can't get another passphrase. Accept() should consume the passphrase
489 // if it is accepted. If no passphrase is acceptable, throw out of here.
491 Reason
QueryOld::query()
493 Reason reason
= SecurityAgent::noReason
;
495 AuthValueVector arguments
;
496 AuthItemSet hints
, context
;
497 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
501 // return the passphrase
502 if (getenv("NOSA")) {
503 char passphrase_
[maxPassphraseLength
];
504 getNoSA(passphrase
, maxPassphraseLength
, "Unlock %s [<CR> to cancel]: ", database
.dbName());
505 passphrase
.copy(passphrase_
, strlen(passphrase_
));
506 return database
.decode(passphrase
) ? SecurityAgent::noReason
: SecurityAgent::invalidPassphrase
;
510 // prepopulate with client hints
512 const char *keychainPath
= database
.dbName();
513 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(strlen(keychainPath
), const_cast<char*>(keychainPath
))));
515 hints
.insert(mClientHints
.begin(), mClientHints
.end());
517 create("builtin", "unlock-keychain", noSecuritySession
);
521 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
522 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
526 if (retryCount
> maxTries
)
528 reason
= SecurityAgent::tooManyTries
;
531 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
532 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
534 setInput(hints
, context
);
537 if (retryCount
> maxTries
)
544 AuthItem
*passwordItem
= outContext().find(kAuthorizationEnvironmentPassword
);
548 passwordItem
->getCssmData(passphrase
);
551 while (reason
= accept(passphrase
));
553 return SecurityAgent::noReason
;
558 // Get existing passphrase (unlock) Query
560 Reason
QueryOld::operator () ()
567 // End-classes for old secrets
569 Reason
QueryUnlock::accept(CssmManagedData
&passphrase
)
571 if (safer_cast
<KeychainDatabase
&>(database
).decode(passphrase
))
572 return SecurityAgent::noReason
;
574 return SecurityAgent::invalidPassphrase
;
578 QueryPIN::QueryPIN(Database
&db
)
579 : QueryOld(db
), mPin(Allocator::standard())
581 this->inferHints(Server::process());
585 Reason
QueryPIN::accept(CssmManagedData
&pin
)
587 // no retries for now
589 return SecurityAgent::noReason
;
594 // Obtain passphrases and submit them to the accept() method until it is accepted
595 // or we can't get another passphrase. Accept() should consume the passphrase
596 // if it is accepted. If no passphrase is acceptable, throw out of here.
598 Reason
QueryNewPassphrase::query()
600 Reason reason
= initialReason
;
601 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
602 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
605 AuthValueVector arguments
;
606 AuthItemSet hints
, context
;
611 if (getenv("NOSA")) {
612 char passphrase_
[maxPassphraseLength
];
613 getNoSA(passphrase_
, maxPassphraseLength
,
614 "New passphrase for %s (reason %d) [<CR> to cancel]: ",
615 database
.dbName(), reason
);
616 return SecurityAgent::noReason
;
620 // prepopulate with client hints
621 hints
.insert(mClientHints
.begin(), mClientHints
.end());
623 // keychain name into hints
624 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
.dbName())));
626 switch (initialReason
)
628 case SecurityAgent::newDatabase
:
629 create("builtin", "new-passphrase", noSecuritySession
);
631 case SecurityAgent::changePassphrase
:
632 create("builtin", "change-passphrase", noSecuritySession
);
640 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
641 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
643 if (++retryCount
> maxTries
)
645 reason
= SecurityAgent::tooManyTries
;
648 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
649 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
651 setInput(hints
, context
);
654 if (retryCount
> maxTries
)
661 if (SecurityAgent::changePassphrase
== initialReason
)
663 AuthItem
*oldPasswordItem
= outContext().find(AGENT_PASSWORD
);
664 if (!oldPasswordItem
)
667 oldPasswordItem
->getCssmData(oldPassphrase
);
670 AuthItem
*passwordItem
= outContext().find(AGENT_CONTEXT_NEW_PASSWORD
);
674 passwordItem
->getCssmData(passphrase
);
677 while (reason
= accept(passphrase
, (initialReason
== SecurityAgent::changePassphrase
) ? &oldPassphrase
.get() : NULL
));
679 return SecurityAgent::noReason
;
684 // Get new passphrase Query
686 Reason
QueryNewPassphrase::operator () (CssmOwnedData
&passphrase
)
688 if (Reason result
= query())
689 return result
; // failed
690 passphrase
= mPassphrase
;
691 return SecurityAgent::noReason
; // success
694 Reason
QueryNewPassphrase::accept(CssmManagedData
&passphrase
, CssmData
*oldPassphrase
)
696 //@@@ acceptance criteria are currently hardwired here
697 //@@@ This validation presumes ASCII - UTF8 might be more lenient
699 // if we have an old passphrase, check it
700 if (oldPassphrase
&& !safer_cast
<KeychainDatabase
&>(database
).validatePassphrase(*oldPassphrase
))
701 return SecurityAgent::oldPassphraseWrong
;
703 // sanity check the new passphrase (but allow user override)
704 if (!(mPassphraseValid
&& passphrase
.get() == mPassphrase
)) {
705 mPassphrase
= passphrase
;
706 mPassphraseValid
= true;
707 if (mPassphrase
.length() == 0)
708 return SecurityAgent::passphraseIsNull
;
709 if (mPassphrase
.length() < 6)
710 return SecurityAgent::passphraseTooSimple
;
714 return SecurityAgent::noReason
;
718 // Get a passphrase for unspecified use
720 Reason
QueryGenericPassphrase::operator () (const char *prompt
, bool verify
,
723 return query(prompt
, verify
, passphrase
);
726 Reason
QueryGenericPassphrase::query(const char *prompt
, bool verify
,
729 Reason reason
= SecurityAgent::noReason
;
730 OSStatus status
; // not really used; remove?
731 AuthValueVector arguments
;
732 AuthItemSet hints
, context
;
735 if (getenv("NOSA")) {
737 return SecurityAgent::noReason
;
741 hints
.insert(mClientHints
.begin(), mClientHints
.end());
742 hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt
? strlen(prompt
) : 0, const_cast<char*>(prompt
))));
743 // XXX/gh defined by dmitch but no analogous hint in
744 // AuthorizationTagsPriv.h:
745 // CSSM_ATTRIBUTE_ALERT_TITLE (optional alert panel title)
747 if (false == verify
) { // import
748 create("builtin", "generic-unlock", noSecuritySession
);
749 } else { // verify passphrase (export)
750 // new-passphrase-generic works with the pre-4 June 2004 agent;
751 // generic-new-passphrase is required for the new agent
752 create("builtin", "generic-new-passphrase", noSecuritySession
);
755 AuthItem
*passwordItem
;
758 setInput(hints
, context
);
761 passwordItem
= outContext().find(AGENT_PASSWORD
);
763 } while (!passwordItem
);
765 passwordItem
->getString(passphrase
);
772 // Get a DB blob's passphrase--keychain synchronization
774 Reason
QueryDBBlobSecret::operator () (DbHandle
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle
*dbHandleAuthenticated
)
776 return query(dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
);
779 Reason
QueryDBBlobSecret::query(DbHandle
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle
*dbHandleAuthenticated
)
781 Reason reason
= SecurityAgent::noReason
;
782 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
783 OSStatus status
; // not really used; remove?
784 AuthValueVector arguments
;
785 AuthItemSet hints
/*NUKEME*/, context
;
788 if (getenv("NOSA")) {
789 // FIXME akin to 3690984
790 return SecurityAgent::noReason
;
794 hints
.insert(mClientHints
.begin(), mClientHints
.end());
796 create("builtin", "generic-unlock-kcblob", noSecuritySession
);
798 AuthItem
*secretItem
;
803 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
804 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
806 if (++retryCount
> maxTries
)
808 reason
= SecurityAgent::tooManyTries
;
811 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
812 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
814 setInput(hints
, context
);
817 secretItem
= outContext().find(AGENT_PASSWORD
);
820 secretItem
->getCssmData(passphrase
);
822 } while (reason
= accept(passphrase
, dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
));
827 Reason
QueryDBBlobSecret::accept(CssmManagedData
&passphrase
,
828 DbHandle
*dbHandlesToAuthenticate
, uint8 dbHandleCount
, DbHandle
*dbHandleAuthenticated
)
830 DbHandle
*currHdl
= dbHandlesToAuthenticate
;
832 Boolean authenticated
= false;
833 for (index
=0; index
< dbHandleCount
&& !authenticated
; index
++)
837 RefPointer
<KeychainDatabase
> dbToUnlock
= Server::keychain(*currHdl
);
838 dbToUnlock
->unlockDb(passphrase
);
839 authenticated
= true;
840 *dbHandleAuthenticated
= *currHdl
; // return the DbHandle that 'passphrase' authenticated with.
842 catch (const CommonError
&err
)
844 currHdl
++; // we failed to authenticate with this one, onto the next one.
847 if ( !authenticated
)
848 return SecurityAgent::invalidPassphrase
;
850 return SecurityAgent::noReason
;
853 QueryInvokeMechanism::QueryInvokeMechanism(const AuthHostType type
, Session
&session
) :
854 SecurityAgentQuery(type
, session
) { }
856 void QueryInvokeMechanism::initialize(const string
&inPluginId
, const string
&inMechanismId
, const AuthValueVector
&inArguments
, const SessionId inSessionId
)
858 if (SecurityAgent::Client::init
== SecurityAgent::Client::state())
860 create(inPluginId
.c_str(), inMechanismId
.c_str(), inSessionId
);
861 mArguments
= inArguments
;
865 // XXX/cs should return AuthorizationResult
866 void QueryInvokeMechanism::run(const AuthValueVector
&inArguments
, AuthItemSet
&inHints
, AuthItemSet
&inContext
, AuthorizationResult
*outResult
)
868 // prepopulate with client hints
869 inHints
.insert(mClientHints
.begin(), mClientHints
.end());
871 setArguments(inArguments
);
872 setInput(inHints
, inContext
);
873 MacOSError::check(invoke());
875 if (outResult
) *outResult
= result();
877 inHints
= outHints();
878 inContext
= outContext();
881 void QueryInvokeMechanism::terminateAgent()
886 // @@@ no pluggable authentication possible!
888 QueryKeychainAuth::operator () (const char *database
, const char *description
, AclAuthorization action
, const char *prompt
)
890 Reason reason
= SecurityAgent::noReason
;
891 AuthItemSet hints
, context
;
892 AuthValueVector arguments
;
897 using CommonCriteria::Securityd::KeychainAuthLogger
;
898 KeychainAuthLogger
logger(mAuditToken
, AUE_ssauthint
, database
, description
);
901 /* XXX/gh probably not complete; stolen verbatim from rogue-app query */
902 if (getenv("NOSA")) {
903 char answer
[maxPassphraseLength
+10];
905 string applicationPath
;
906 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
907 if (applicationPathItem
)
908 applicationPathItem
->getString(applicationPath
);
910 getNoSA(answer
, sizeof(answer
), "Allow %s to do %d on %s in %s? [yn][g]%s ",
911 applicationPath
.c_str(), int(action
), (description
? description
: "[NULL item]"),
912 (database
? database
: "[NULL database]"),
913 mPassphraseCheck
? ":passphrase" : "");
914 // turn passphrase (no ':') into y:passphrase
915 if (mPassphraseCheck
&& !strchr(answer
, ':')) {
916 memmove(answer
+2, answer
, strlen(answer
)+1);
917 memcpy(answer
, "y:", 2);
920 allow
= answer
[0] == 'y';
921 remember
= answer
[1] == 'g';
922 return SecurityAgent::noReason
;
926 hints
.insert(mClientHints
.begin(), mClientHints
.end());
928 // put action/operation (sint32) into hints
929 hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
))));
931 hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt
? strlen(prompt
) : 0, const_cast<char*>(prompt
))));
933 // item name into hints
934 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description
? strlen(description
) : 0, const_cast<char*>(description
))));
936 // keychain name into hints
937 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
? strlen(database
) : 0, const_cast<char*>(database
))));
939 create("builtin", "confirm-access-user-password", noSecuritySession
);
941 AuthItem
*usernameItem
;
942 AuthItem
*passwordItem
;
946 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
947 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
949 if (++retryCount
> maxTries
)
950 reason
= SecurityAgent::tooManyTries
;
952 if (SecurityAgent::noReason
!= reason
)
954 if (SecurityAgent::tooManyTries
== reason
)
955 logger
.logFailure(NULL
, CommonCriteria::errTooManyTries
);
960 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
961 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
963 setInput(hints
, context
);
969 catch (...) // user probably clicked "deny"
974 usernameItem
= outContext().find(AGENT_USERNAME
);
975 passwordItem
= outContext().find(AGENT_PASSWORD
);
976 if (!usernameItem
|| !passwordItem
)
978 usernameItem
->getString(username
);
979 passwordItem
->getString(password
);
980 } while (reason
= accept(username
, password
));
982 if (SecurityAgent::noReason
== reason
)
984 // else we logged the denial in the loop
990 QueryKeychainAuth::accept(string
&username
, string
&passphrase
)
992 const char *user
= username
.c_str();
993 const char *passwd
= passphrase
.c_str();
994 int checkpw_status
= checkpw(user
, passwd
);
996 if (checkpw_status
!= CHECKPW_SUCCESS
)
997 return SecurityAgent::invalidPassphrase
;
999 return SecurityAgent::noReason
;