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
)
266 StLock
<Mutex
> _(thisProcess
);
267 if (SecCodeRef clientCode
= thisProcess
.currentGuest())
268 guestPath
= codePath(clientCode
);
270 AuthItemSet processHints
= clientHints(SecurityAgent::bundle
, guestPath
,
271 thisProcess
.pid(), thisProcess
.uid());
272 mClientHints
.insert(processHints
.begin(), processHints
.end());
275 void SecurityAgentQuery::addHint(const char *name
, const void *value
, UInt32 valueLen
, UInt32 flags
)
277 AuthorizationItem item
= { name
, valueLen
, const_cast<void *>(value
), flags
};
278 mClientHints
.insert(AuthItemRef(item
));
283 SecurityAgentQuery::readChoice()
288 AuthItem
*allowAction
= outContext().find(AGENT_CONTEXT_ALLOW
);
292 if (allowAction
->getString(allowString
)
293 && (allowString
== "YES"))
297 AuthItem
*rememberAction
= outContext().find(AGENT_CONTEXT_REMEMBER_ACTION
);
300 string rememberString
;
301 if (rememberAction
->getString(rememberString
)
302 && (rememberString
== "YES"))
308 SecurityAgentQuery::disconnect()
310 SecurityAgent::Client::destroy();
314 SecurityAgentQuery::terminate()
316 // you might think these are called in the wrong order, but you'd be wrong
317 SecurityAgentConnection::terminate();
318 SecurityAgent::Client::terminate();
322 SecurityAgentQuery::create(const char *pluginId
, const char *mechanismId
, const SessionId inSessionId
)
325 OSStatus status
= SecurityAgent::Client::create(pluginId
, mechanismId
, inSessionId
);
328 secdebug("SecurityAgentQuery", "agent went walkabout, restarting");
330 status
= SecurityAgent::Client::create(pluginId
, mechanismId
, inSessionId
);
332 if (status
) MacOSError::throwMe(status
);
336 // Perform the "rogue app" access query dialog
338 QueryKeychainUse::QueryKeychainUse(bool needPass
, const Database
*db
)
339 : mPassphraseCheck(NULL
)
341 // if passphrase checking requested, save KeychainDatabase reference
342 // (will quietly disable check if db isn't a keychain)
344 mPassphraseCheck
= dynamic_cast<const KeychainDatabase
*>(db
);
346 setTerminateOnSleep(true);
349 Reason
QueryKeychainUse::queryUser (const char *database
, const char *description
, AclAuthorization action
)
351 Reason reason
= SecurityAgent::noReason
;
354 AuthValueVector arguments
;
355 AuthItemSet hints
, context
;
358 if (getenv("NOSA")) {
359 char answer
[maxPassphraseLength
+10];
361 string applicationPath
;
362 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
363 if (applicationPathItem
)
364 applicationPathItem
->getString(applicationPath
);
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);
376 allow
= answer
[0] == 'y';
377 remember
= answer
[1] == 'g';
378 return SecurityAgent::noReason
;
382 // prepopulate with client hints
383 hints
.insert(mClientHints
.begin(), mClientHints
.end());
385 // put action/operation (sint32) into hints
386 hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
))));
388 // item name into hints
390 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description
? strlen(description
) : 0, const_cast<char*>(description
))));
392 // keychain name into hints
393 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
? strlen(database
) : 0, const_cast<char*>(database
))));
396 if (mPassphraseCheck
)
398 create("builtin", "confirm-access-password", noSecuritySession
);
400 CssmAutoData
data(Allocator::standard(Allocator::sensitive
));
405 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
406 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
408 if (retryCount
++ > kMaximumAuthorizationTries
)
410 reason
= SecurityAgent::tooManyTries
;
413 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
414 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
416 setInput(hints
, context
);
419 if (retryCount
> kMaximumAuthorizationTries
)
426 AuthItem
*passwordItem
= outContext().find(kAuthorizationEnvironmentPassword
);
430 passwordItem
->getCssmData(data
);
432 while (reason
= (const_cast<KeychainDatabase
*>(mPassphraseCheck
)->decode(data
) ? SecurityAgent::noReason
: SecurityAgent::invalidPassphrase
));
436 create("builtin", "confirm-access", noSecuritySession
);
437 setInput(hints
, context
);
447 // Perform code signature ACL access adjustment dialogs
449 bool QueryCodeCheck::operator () (const char *aclPath
)
452 AuthValueVector arguments
;
453 AuthItemSet hints
, context
;
456 if (getenv("NOSA")) {
459 string applicationPath
;
460 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
461 if (applicationPathItem
)
462 applicationPathItem
->getString(applicationPath
);
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';
473 // prepopulate with client hints
474 hints
.insert(mClientHints
.begin(), mClientHints
.end());
476 hints
.insert(AuthItemRef(AGENT_HINT_APPLICATION_PATH
, AuthValueOverlay(strlen(aclPath
), const_cast<char*>(aclPath
))));
478 create("builtin", "code-identity", noSecuritySession
);
480 setInput(hints
, context
);
485 // MacOSError::check(status);
487 return kAuthorizationResultAllow
== result();
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.
496 Reason
QueryOld::query()
498 Reason reason
= SecurityAgent::noReason
;
500 AuthValueVector arguments
;
501 AuthItemSet hints
, context
;
502 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
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
;
515 // prepopulate with client hints
517 const char *keychainPath
= database
.dbName();
518 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(strlen(keychainPath
), const_cast<char*>(keychainPath
))));
520 hints
.insert(mClientHints
.begin(), mClientHints
.end());
522 create("builtin", "unlock-keychain", noSecuritySession
);
526 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
527 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
531 if (retryCount
> maxTries
)
533 reason
= SecurityAgent::tooManyTries
;
536 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
537 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
539 setInput(hints
, context
);
542 if (retryCount
> maxTries
)
549 AuthItem
*passwordItem
= outContext().find(kAuthorizationEnvironmentPassword
);
553 passwordItem
->getCssmData(passphrase
);
556 while (reason
= accept(passphrase
));
558 return SecurityAgent::noReason
;
563 // Get existing passphrase (unlock) Query
565 Reason
QueryOld::operator () ()
572 // End-classes for old secrets
574 Reason
QueryUnlock::accept(CssmManagedData
&passphrase
)
576 if (safer_cast
<KeychainDatabase
&>(database
).decode(passphrase
))
577 return SecurityAgent::noReason
;
579 return SecurityAgent::invalidPassphrase
;
583 QueryPIN::QueryPIN(Database
&db
)
584 : QueryOld(db
), mPin(Allocator::standard())
586 this->inferHints(Server::process());
590 Reason
QueryPIN::accept(CssmManagedData
&pin
)
592 // no retries for now
594 return SecurityAgent::noReason
;
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.
603 Reason
QueryNewPassphrase::query()
605 Reason reason
= initialReason
;
606 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
607 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
610 AuthValueVector arguments
;
611 AuthItemSet hints
, context
;
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
;
625 // prepopulate with client hints
626 hints
.insert(mClientHints
.begin(), mClientHints
.end());
628 // keychain name into hints
629 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
.dbName())));
631 switch (initialReason
)
633 case SecurityAgent::newDatabase
:
634 create("builtin", "new-passphrase", noSecuritySession
);
636 case SecurityAgent::changePassphrase
:
637 create("builtin", "change-passphrase", noSecuritySession
);
645 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
646 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
648 if (++retryCount
> maxTries
)
650 reason
= SecurityAgent::tooManyTries
;
653 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
654 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
656 setInput(hints
, context
);
659 if (retryCount
> maxTries
)
666 if (SecurityAgent::changePassphrase
== initialReason
)
668 AuthItem
*oldPasswordItem
= outContext().find(AGENT_PASSWORD
);
669 if (!oldPasswordItem
)
672 oldPasswordItem
->getCssmData(oldPassphrase
);
675 AuthItem
*passwordItem
= outContext().find(AGENT_CONTEXT_NEW_PASSWORD
);
679 passwordItem
->getCssmData(passphrase
);
682 while (reason
= accept(passphrase
, (initialReason
== SecurityAgent::changePassphrase
) ? &oldPassphrase
.get() : NULL
));
684 return SecurityAgent::noReason
;
689 // Get new passphrase Query
691 Reason
QueryNewPassphrase::operator () (CssmOwnedData
&passphrase
)
693 if (Reason result
= query())
694 return result
; // failed
695 passphrase
= mPassphrase
;
696 return SecurityAgent::noReason
; // success
699 Reason
QueryNewPassphrase::accept(CssmManagedData
&passphrase
, CssmData
*oldPassphrase
)
701 //@@@ acceptance criteria are currently hardwired here
702 //@@@ This validation presumes ASCII - UTF8 might be more lenient
704 // if we have an old passphrase, check it
705 if (oldPassphrase
&& !safer_cast
<KeychainDatabase
&>(database
).validatePassphrase(*oldPassphrase
))
706 return SecurityAgent::oldPassphraseWrong
;
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
;
719 return SecurityAgent::noReason
;
723 // Get a passphrase for unspecified use
725 Reason
QueryGenericPassphrase::operator () (const CssmData
*prompt
, bool verify
,
728 return query(prompt
, verify
, passphrase
);
731 Reason
QueryGenericPassphrase::query(const CssmData
*prompt
, bool verify
,
734 Reason reason
= SecurityAgent::noReason
;
735 OSStatus status
; // not really used; remove?
736 AuthValueVector arguments
;
737 AuthItemSet hints
, context
;
740 if (getenv("NOSA")) {
742 return SecurityAgent::noReason
;
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)
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
);
760 AuthItem
*passwordItem
;
763 setInput(hints
, context
);
766 passwordItem
= outContext().find(AGENT_PASSWORD
);
768 } while (!passwordItem
);
770 passwordItem
->getString(passphrase
);
777 // Get a DB blob's passphrase--keychain synchronization
779 Reason
QueryDBBlobSecret::operator () (DbHandle
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle
*dbHandleAuthenticated
)
781 return query(dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
);
784 Reason
QueryDBBlobSecret::query(DbHandle
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle
*dbHandleAuthenticated
)
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
;
793 if (getenv("NOSA")) {
794 // FIXME akin to 3690984
795 return SecurityAgent::noReason
;
799 hints
.insert(mClientHints
.begin(), mClientHints
.end());
801 create("builtin", "generic-unlock-kcblob", noSecuritySession
);
803 AuthItem
*secretItem
;
808 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
809 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
811 if (++retryCount
> maxTries
)
813 reason
= SecurityAgent::tooManyTries
;
816 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
817 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
819 setInput(hints
, context
);
822 secretItem
= outContext().find(AGENT_PASSWORD
);
825 secretItem
->getCssmData(passphrase
);
827 } while (reason
= accept(passphrase
, dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
));
832 Reason
QueryDBBlobSecret::accept(CssmManagedData
&passphrase
,
833 DbHandle
*dbHandlesToAuthenticate
, uint8 dbHandleCount
, DbHandle
*dbHandleAuthenticated
)
835 DbHandle
*currHdl
= dbHandlesToAuthenticate
;
837 Boolean authenticated
= false;
838 for (index
=0; index
< dbHandleCount
&& !authenticated
; index
++)
842 RefPointer
<KeychainDatabase
> dbToUnlock
= Server::keychain(*currHdl
);
843 dbToUnlock
->unlockDb(passphrase
);
844 authenticated
= true;
845 *dbHandleAuthenticated
= *currHdl
; // return the DbHandle that 'passphrase' authenticated with.
847 catch (const CommonError
&err
)
849 currHdl
++; // we failed to authenticate with this one, onto the next one.
852 if ( !authenticated
)
853 return SecurityAgent::invalidPassphrase
;
855 return SecurityAgent::noReason
;
858 QueryInvokeMechanism::QueryInvokeMechanism(const AuthHostType type
, Session
&session
) :
859 SecurityAgentQuery(type
, session
) { }
861 void QueryInvokeMechanism::initialize(const string
&inPluginId
, const string
&inMechanismId
, const AuthValueVector
&inArguments
, const SessionId inSessionId
)
863 if (SecurityAgent::Client::init
== SecurityAgent::Client::state())
865 create(inPluginId
.c_str(), inMechanismId
.c_str(), inSessionId
);
866 mArguments
= inArguments
;
870 // XXX/cs should return AuthorizationResult
871 void QueryInvokeMechanism::run(const AuthValueVector
&inArguments
, AuthItemSet
&inHints
, AuthItemSet
&inContext
, AuthorizationResult
*outResult
)
873 // prepopulate with client hints
874 inHints
.insert(mClientHints
.begin(), mClientHints
.end());
876 if (mAuthHostType
== securityAgent
) {
877 if (Server::active().inDarkWake())
878 CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE
);
881 setArguments(inArguments
);
882 setInput(inHints
, inContext
);
883 MacOSError::check(invoke());
885 if (outResult
) *outResult
= result();
887 inHints
= outHints();
888 inContext
= outContext();
891 void QueryInvokeMechanism::terminateAgent()
896 // @@@ no pluggable authentication possible!
898 QueryKeychainAuth::operator () (const char *database
, const char *description
, AclAuthorization action
, const char *prompt
)
900 Reason reason
= SecurityAgent::noReason
;
901 AuthItemSet hints
, context
;
902 AuthValueVector arguments
;
907 using CommonCriteria::Securityd::KeychainAuthLogger
;
908 KeychainAuthLogger
logger(mAuditToken
, AUE_ssauthint
, database
, description
);
911 /* XXX/gh probably not complete; stolen verbatim from rogue-app query */
912 if (getenv("NOSA")) {
913 char answer
[maxPassphraseLength
+10];
915 string applicationPath
;
916 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
917 if (applicationPathItem
)
918 applicationPathItem
->getString(applicationPath
);
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);
930 allow
= answer
[0] == 'y';
931 remember
= answer
[1] == 'g';
932 return SecurityAgent::noReason
;
936 hints
.insert(mClientHints
.begin(), mClientHints
.end());
938 // put action/operation (sint32) into hints
939 hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
))));
941 hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt
? strlen(prompt
) : 0, const_cast<char*>(prompt
))));
943 // item name into hints
944 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description
? strlen(description
) : 0, const_cast<char*>(description
))));
946 // keychain name into hints
947 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
? strlen(database
) : 0, const_cast<char*>(database
))));
949 create("builtin", "confirm-access-user-password", noSecuritySession
);
951 AuthItem
*usernameItem
;
952 AuthItem
*passwordItem
;
956 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
957 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
959 if (++retryCount
> maxTries
)
960 reason
= SecurityAgent::tooManyTries
;
962 if (SecurityAgent::noReason
!= reason
)
964 if (SecurityAgent::tooManyTries
== reason
)
965 logger
.logFailure(NULL
, CommonCriteria::errTooManyTries
);
970 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
971 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
973 setInput(hints
, context
);
979 catch (...) // user probably clicked "deny"
984 usernameItem
= outContext().find(AGENT_USERNAME
);
985 passwordItem
= outContext().find(AGENT_PASSWORD
);
986 if (!usernameItem
|| !passwordItem
)
988 usernameItem
->getString(username
);
989 passwordItem
->getString(password
);
990 } while (reason
= accept(username
, password
));
992 if (SecurityAgent::noReason
== reason
)
994 // else we logged the denial in the loop
1000 QueryKeychainAuth::accept(string
&username
, string
&passphrase
)
1002 const char *user
= username
.c_str();
1003 const char *passwd
= passphrase
.c_str();
1004 int checkpw_status
= checkpw(user
, passwd
);
1006 if (checkpw_status
!= CHECKPW_SUCCESS
)
1007 return SecurityAgent::invalidPassphrase
;
1009 return SecurityAgent::noReason
;