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 <bsm/audit_uevents.h> // AUE_ssauthint
37 // NOSA support functions. This is a test mode where the SecurityAgent
38 // is simulated via stdio in the client. Good for running automated tests
39 // of client programs. Only available if -DNOSA when compiling.
45 static void getNoSA(char *buffer
, size_t bufferSize
, const char *fmt
, ...)
50 vfprintf(stdout
, fmt
, args
);
54 memset(buffer
, 0, bufferSize
);
55 const char *nosa
= getenv("NOSA");
56 if (!strcmp(nosa
, "-")) {
57 if (fgets(buffer
, bufferSize
-1, stdin
) == NULL
)
58 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
59 buffer
[strlen(buffer
)-1] = '\0'; // remove trailing newline
60 if (!isatty(fileno(stdin
)))
61 printf("%s\n", buffer
); // echo to output if input not terminal
63 strncpy(buffer
, nosa
, bufferSize
-1);
64 printf("%s\n", buffer
);
66 if (buffer
[0] == '\0') // empty input -> cancellation
67 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED
);
73 // SecurityAgentConnection
75 SecurityAgentConnection::SecurityAgentConnection(const AuthHostType type
, Session
&session
)
76 : mAuthHostType(type
),
77 mHostInstance(session
.authhost(mAuthHostType
)),
78 mConnection(&Server::connection()),
79 mAuditToken(Server::connection().auditToken())
81 // this may take a while
82 Server::active().longTermActivity();
83 secdebug("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
86 SecurityAgentConnection::~SecurityAgentConnection()
88 secdebug("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this);
89 mConnection
->useAgent(NULL
);
93 SecurityAgentConnection::activate()
95 secdebug("SecurityAgentConnection", "activate(%p)", this);
96 mConnection
->useAgent(this);
98 mPort
= mHostInstance
->activate();
99 secdebug("SecurityAgentConnection", "%p activated", this);
101 mConnection
->useAgent(NULL
); // guess not
102 secdebug("SecurityAgentConnection", "error activating %p", this);
108 SecurityAgentConnection::reconnect()
110 // if !mHostInstance throw()?
113 Session
&session
= mHostInstance
->session();
114 mHostInstance
= session
.authhost(mAuthHostType
, true);
120 SecurityAgentConnection::terminate()
124 // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
125 mConnection
->useAgent(NULL
);
129 // SecurityAgentTransaction
131 SecurityAgentTransaction::SecurityAgentTransaction(const AuthHostType type
, Session
&session
, bool startNow
)
132 : SecurityAgentConnection(type
, session
),
135 secdebug("SecurityAgentTransaction", "New SecurityAgentTransaction(%p)", this);
136 activate(); // start agent now, or other SAConnections will kill and spawn new agents
141 SecurityAgentTransaction::~SecurityAgentTransaction()
143 try { end(); } catch(...) {}
144 secdebug("SecurityAgentTransaction", "Destroying %p", this);
148 SecurityAgentTransaction::start()
150 secdebug("SecurityAgentTransaction", "start(%p)", this);
151 MacOSError::check(SecurityAgentQuery::Client::startTransaction(mPort
));
153 secdebug("SecurityAgentTransaction", "started(%p)", this);
157 SecurityAgentTransaction::end()
161 MacOSError::check(SecurityAgentQuery::Client::endTransaction(mPort
));
164 secdebug("SecurityAgentTransaction", "End SecurityAgentTransaction(%p)", this);
167 using SecurityAgent::Reason
;
168 using namespace Authorization
;
170 SecurityAgentQuery::SecurityAgentQuery(const AuthHostType type
, Session
&session
)
171 : SecurityAgentConnection(type
, session
)
173 secdebug("SecurityAgentQuery", "new SecurityAgentQuery(%p)", this);
176 SecurityAgentQuery::~SecurityAgentQuery()
178 secdebug("SecurityAgentQuery", "SecurityAgentQuery(%p) dying", this);
181 if (getenv("NOSA")) {
182 printf(" [query done]\n");
187 if (SecurityAgent::Client::state() != SecurityAgent::Client::dead
)
192 SecurityAgentQuery::activate()
194 SecurityAgentConnection::activate();
195 SecurityAgent::Client::activate(mPort
);
196 secdebug("SecurityAgentQuery", "activate(%p)", this);
200 SecurityAgentQuery::reconnect()
202 SecurityAgentConnection::reconnect();
203 SecurityAgent::Client::activate(mPort
);
204 secdebug("SecurityAgentQuery", "reconnect(%p)", this);
208 SecurityAgentQuery::inferHints(Process
&thisProcess
)
211 if (SecCodeRef clientCode
= thisProcess
.currentGuest())
212 guestPath
= codePath(clientCode
);
213 AuthItemSet processHints
= clientHints(SecurityAgent::bundle
, guestPath
,
214 thisProcess
.pid(), thisProcess
.uid());
215 mClientHints
.insert(processHints
.begin(), processHints
.end());
218 void SecurityAgentQuery::addHint(const char *name
, const void *value
, UInt32 valueLen
, UInt32 flags
)
220 AuthorizationItem item
= { name
, valueLen
, const_cast<void *>(value
), flags
};
221 mClientHints
.insert(AuthItemRef(item
));
226 SecurityAgentQuery::readChoice()
231 AuthItem
*allowAction
= outContext().find(AGENT_CONTEXT_ALLOW
);
235 if (allowAction
->getString(allowString
)
236 && (allowString
== "YES"))
240 AuthItem
*rememberAction
= outContext().find(AGENT_CONTEXT_REMEMBER_ACTION
);
243 string rememberString
;
244 if (rememberAction
->getString(rememberString
)
245 && (rememberString
== "YES"))
251 SecurityAgentQuery::disconnect()
253 SecurityAgent::Client::destroy();
257 SecurityAgentQuery::terminate()
259 // you might think these are called in the wrong order, but you'd be wrong
260 SecurityAgentConnection::terminate();
261 SecurityAgent::Client::terminate();
265 SecurityAgentQuery::create(const char *pluginId
, const char *mechanismId
, const SessionId inSessionId
)
268 OSStatus status
= SecurityAgent::Client::create(pluginId
, mechanismId
, inSessionId
);
271 secdebug("SecurityAgentQuery", "agent went walkabout, restarting");
273 status
= SecurityAgent::Client::create(pluginId
, mechanismId
, inSessionId
);
275 if (status
) MacOSError::throwMe(status
);
279 // Perform the "rogue app" access query dialog
281 QueryKeychainUse::QueryKeychainUse(bool needPass
, const Database
*db
)
282 : mPassphraseCheck(NULL
)
284 // if passphrase checking requested, save KeychainDatabase reference
285 // (will quietly disable check if db isn't a keychain)
287 mPassphraseCheck
= dynamic_cast<const KeychainDatabase
*>(db
);
290 Reason
QueryKeychainUse::queryUser (const char *database
, const char *description
, AclAuthorization action
)
292 Reason reason
= SecurityAgent::noReason
;
295 AuthValueVector arguments
;
296 AuthItemSet hints
, context
;
299 if (getenv("NOSA")) {
300 char answer
[maxPassphraseLength
+10];
302 string applicationPath
;
303 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
304 if (applicationPathItem
)
305 applicationPathItem
->getString(applicationPath
);
307 getNoSA(answer
, sizeof(answer
), "Allow %s to do %d on %s in %s? [yn][g]%s ",
308 applicationPath
.c_str(), int(action
), (description
? description
: "[NULL item]"),
309 (database
? database
: "[NULL database]"),
310 mPassphraseCheck
? ":passphrase" : "");
311 // turn passphrase (no ':') into y:passphrase
312 if (mPassphraseCheck
&& !strchr(answer
, ':')) {
313 memmove(answer
+2, answer
, strlen(answer
)+1);
314 memcpy(answer
, "y:", 2);
317 allow
= answer
[0] == 'y';
318 remember
= answer
[1] == 'g';
319 return SecurityAgent::noReason
;
323 // prepopulate with client hints
324 hints
.insert(mClientHints
.begin(), mClientHints
.end());
326 // put action/operation (sint32) into hints
327 hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
))));
329 // item name into hints
331 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description
? strlen(description
) : 0, const_cast<char*>(description
))));
333 // keychain name into hints
334 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
? strlen(database
) : 0, const_cast<char*>(database
))));
337 if (mPassphraseCheck
)
339 create("builtin", "confirm-access-password", noSecuritySession
);
341 CssmAutoData
data(Allocator::standard(Allocator::sensitive
));
346 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
347 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
349 if (retryCount
++ > kMaximumAuthorizationTries
)
351 reason
= SecurityAgent::tooManyTries
;
354 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
355 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
357 setInput(hints
, context
);
360 if (retryCount
> kMaximumAuthorizationTries
)
367 AuthItem
*passwordItem
= outContext().find(kAuthorizationEnvironmentPassword
);
371 passwordItem
->getCssmData(data
);
373 while (reason
= (const_cast<KeychainDatabase
*>(mPassphraseCheck
)->decode(data
) ? SecurityAgent::noReason
: SecurityAgent::invalidPassphrase
));
377 create("builtin", "confirm-access", noSecuritySession
);
378 setInput(hints
, context
);
388 // Perform code signature ACL access adjustment dialogs
390 bool QueryCodeCheck::operator () (const char *aclPath
)
393 AuthValueVector arguments
;
394 AuthItemSet hints
, context
;
397 if (getenv("NOSA")) {
400 string applicationPath
;
401 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
402 if (applicationPathItem
)
403 applicationPathItem
->getString(applicationPath
);
405 getNoSA(answer
, sizeof(answer
),
406 "Allow %s to match an ACL for %s [yn][g]? ",
407 applicationPath
.c_str(), aclPath
? aclPath
: "(unknown)");
408 allow
= answer
[0] == 'y';
409 remember
= answer
[1] == 'g';
414 // prepopulate with client hints
415 hints
.insert(mClientHints
.begin(), mClientHints
.end());
417 hints
.insert(AuthItemRef(AGENT_HINT_APPLICATION_PATH
, AuthValueOverlay(strlen(aclPath
), const_cast<char*>(aclPath
))));
419 create("builtin", "code-identity", noSecuritySession
);
421 setInput(hints
, context
);
426 // MacOSError::check(status);
428 return kAuthorizationResultAllow
== result();
433 // Obtain passphrases and submit them to the accept() method until it is accepted
434 // or we can't get another passphrase. Accept() should consume the passphrase
435 // if it is accepted. If no passphrase is acceptable, throw out of here.
437 Reason
QueryOld::query()
439 Reason reason
= SecurityAgent::noReason
;
441 AuthValueVector arguments
;
442 AuthItemSet hints
, context
;
443 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
447 // return the passphrase
448 if (getenv("NOSA")) {
449 char passphrase_
[maxPassphraseLength
];
450 getNoSA(passphrase
, maxPassphraseLength
, "Unlock %s [<CR> to cancel]: ", database
.dbName());
451 passphrase
.copy(passphrase_
, strlen(passphrase_
));
452 return database
.decode(passphrase
) ? SecurityAgent::noReason
: SecurityAgent::invalidPassphrase
;
456 // prepopulate with client hints
458 const char *keychainPath
= database
.dbName();
459 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(strlen(keychainPath
), const_cast<char*>(keychainPath
))));
461 hints
.insert(mClientHints
.begin(), mClientHints
.end());
463 create("builtin", "unlock-keychain", noSecuritySession
);
467 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
468 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
472 if (retryCount
> maxTries
)
474 reason
= SecurityAgent::tooManyTries
;
477 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
478 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
480 setInput(hints
, context
);
483 if (retryCount
> maxTries
)
490 AuthItem
*passwordItem
= outContext().find(kAuthorizationEnvironmentPassword
);
494 passwordItem
->getCssmData(passphrase
);
497 while (reason
= accept(passphrase
));
499 return SecurityAgent::noReason
;
504 // Get existing passphrase (unlock) Query
506 Reason
QueryOld::operator () ()
513 // End-classes for old secrets
515 Reason
QueryUnlock::accept(CssmManagedData
&passphrase
)
517 if (safer_cast
<KeychainDatabase
&>(database
).decode(passphrase
))
518 return SecurityAgent::noReason
;
520 return SecurityAgent::invalidPassphrase
;
524 QueryPIN::QueryPIN(Database
&db
)
525 : QueryOld(db
), mPin(Allocator::standard())
527 this->inferHints(Server::process());
531 Reason
QueryPIN::accept(CssmManagedData
&pin
)
533 // no retries for now
535 return SecurityAgent::noReason
;
540 // Obtain passphrases and submit them to the accept() method until it is accepted
541 // or we can't get another passphrase. Accept() should consume the passphrase
542 // if it is accepted. If no passphrase is acceptable, throw out of here.
544 Reason
QueryNewPassphrase::query()
546 Reason reason
= initialReason
;
547 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
548 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
551 AuthValueVector arguments
;
552 AuthItemSet hints
, context
;
557 if (getenv("NOSA")) {
558 char passphrase_
[maxPassphraseLength
];
559 getNoSA(passphrase_
, maxPassphraseLength
,
560 "New passphrase for %s (reason %d) [<CR> to cancel]: ",
561 database
.dbName(), reason
);
562 return SecurityAgent::noReason
;
566 // prepopulate with client hints
567 hints
.insert(mClientHints
.begin(), mClientHints
.end());
569 // keychain name into hints
570 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
.dbName())));
572 switch (initialReason
)
574 case SecurityAgent::newDatabase
:
575 create("builtin", "new-passphrase", noSecuritySession
);
577 case SecurityAgent::changePassphrase
:
578 create("builtin", "change-passphrase", noSecuritySession
);
586 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
587 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
589 if (++retryCount
> maxTries
)
591 reason
= SecurityAgent::tooManyTries
;
594 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
595 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
597 setInput(hints
, context
);
600 if (retryCount
> maxTries
)
607 if (SecurityAgent::changePassphrase
== initialReason
)
609 AuthItem
*oldPasswordItem
= outContext().find(AGENT_PASSWORD
);
610 if (!oldPasswordItem
)
613 oldPasswordItem
->getCssmData(oldPassphrase
);
616 AuthItem
*passwordItem
= outContext().find(AGENT_CONTEXT_NEW_PASSWORD
);
620 passwordItem
->getCssmData(passphrase
);
623 while (reason
= accept(passphrase
, (initialReason
== SecurityAgent::changePassphrase
) ? &oldPassphrase
.get() : NULL
));
625 return SecurityAgent::noReason
;
630 // Get new passphrase Query
632 Reason
QueryNewPassphrase::operator () (CssmOwnedData
&passphrase
)
634 if (Reason result
= query())
635 return result
; // failed
636 passphrase
= mPassphrase
;
637 return SecurityAgent::noReason
; // success
640 Reason
QueryNewPassphrase::accept(CssmManagedData
&passphrase
, CssmData
*oldPassphrase
)
642 //@@@ acceptance criteria are currently hardwired here
643 //@@@ This validation presumes ASCII - UTF8 might be more lenient
645 // if we have an old passphrase, check it
646 if (oldPassphrase
&& !safer_cast
<KeychainDatabase
&>(database
).validatePassphrase(*oldPassphrase
))
647 return SecurityAgent::oldPassphraseWrong
;
649 // sanity check the new passphrase (but allow user override)
650 if (!(mPassphraseValid
&& passphrase
.get() == mPassphrase
)) {
651 mPassphrase
= passphrase
;
652 mPassphraseValid
= true;
653 if (mPassphrase
.length() == 0)
654 return SecurityAgent::passphraseIsNull
;
655 if (mPassphrase
.length() < 6)
656 return SecurityAgent::passphraseTooSimple
;
660 return SecurityAgent::noReason
;
664 // Get a passphrase for unspecified use
666 Reason
QueryGenericPassphrase::operator () (const char *prompt
, bool verify
,
669 return query(prompt
, verify
, passphrase
);
672 Reason
QueryGenericPassphrase::query(const char *prompt
, bool verify
,
675 Reason reason
= SecurityAgent::noReason
;
676 OSStatus status
; // not really used; remove?
677 AuthValueVector arguments
;
678 AuthItemSet hints
, context
;
681 if (getenv("NOSA")) {
683 return SecurityAgent::noReason
;
687 hints
.insert(mClientHints
.begin(), mClientHints
.end());
688 hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt
? strlen(prompt
) : 0, const_cast<char*>(prompt
))));
689 // XXX/gh defined by dmitch but no analogous hint in
690 // AuthorizationTagsPriv.h:
691 // CSSM_ATTRIBUTE_ALERT_TITLE (optional alert panel title)
693 if (false == verify
) { // import
694 create("builtin", "generic-unlock", noSecuritySession
);
695 } else { // verify passphrase (export)
696 // new-passphrase-generic works with the pre-4 June 2004 agent;
697 // generic-new-passphrase is required for the new agent
698 create("builtin", "generic-new-passphrase", noSecuritySession
);
701 AuthItem
*passwordItem
;
704 setInput(hints
, context
);
707 passwordItem
= outContext().find(AGENT_PASSWORD
);
709 } while (!passwordItem
);
711 passwordItem
->getString(passphrase
);
718 // Get a DB blob's passphrase--keychain synchronization
720 Reason
QueryDBBlobSecret::operator () (DbHandle
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle
*dbHandleAuthenticated
)
722 return query(dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
);
725 Reason
QueryDBBlobSecret::query(DbHandle
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle
*dbHandleAuthenticated
)
727 Reason reason
= SecurityAgent::noReason
;
728 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
729 OSStatus status
; // not really used; remove?
730 AuthValueVector arguments
;
731 AuthItemSet hints
/*NUKEME*/, context
;
734 if (getenv("NOSA")) {
735 // FIXME akin to 3690984
736 return SecurityAgent::noReason
;
740 hints
.insert(mClientHints
.begin(), mClientHints
.end());
742 create("builtin", "generic-unlock-kcblob", noSecuritySession
);
744 AuthItem
*secretItem
;
749 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
750 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
752 if (++retryCount
> maxTries
)
754 reason
= SecurityAgent::tooManyTries
;
757 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
758 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
760 setInput(hints
, context
);
763 secretItem
= outContext().find(AGENT_PASSWORD
);
766 secretItem
->getCssmData(passphrase
);
768 } while (reason
= accept(passphrase
, dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
));
773 Reason
QueryDBBlobSecret::accept(CssmManagedData
&passphrase
,
774 DbHandle
*dbHandlesToAuthenticate
, uint8 dbHandleCount
, DbHandle
*dbHandleAuthenticated
)
776 DbHandle
*currHdl
= dbHandlesToAuthenticate
;
778 Boolean authenticated
= false;
779 for (index
=0; index
< dbHandleCount
&& !authenticated
; index
++)
783 RefPointer
<KeychainDatabase
> dbToUnlock
= Server::keychain(*currHdl
);
784 dbToUnlock
->unlockDb(passphrase
);
785 authenticated
= true;
786 *dbHandleAuthenticated
= *currHdl
; // return the DbHandle that 'passphrase' authenticated with.
788 catch (const CommonError
&err
)
790 currHdl
++; // we failed to authenticate with this one, onto the next one.
793 if ( !authenticated
)
794 return SecurityAgent::invalidPassphrase
;
796 return SecurityAgent::noReason
;
799 QueryInvokeMechanism::QueryInvokeMechanism(const AuthHostType type
, Session
&session
) :
800 SecurityAgentQuery(type
, session
) { }
802 void QueryInvokeMechanism::initialize(const string
&inPluginId
, const string
&inMechanismId
, const AuthValueVector
&inArguments
, const SessionId inSessionId
)
804 if (SecurityAgent::Client::init
== SecurityAgent::Client::state())
806 create(inPluginId
.c_str(), inMechanismId
.c_str(), inSessionId
);
807 mArguments
= inArguments
;
811 // XXX/cs should return AuthorizationResult
812 void QueryInvokeMechanism::run(const AuthValueVector
&inArguments
, AuthItemSet
&inHints
, AuthItemSet
&inContext
, AuthorizationResult
*outResult
)
814 // prepopulate with client hints
815 inHints
.insert(mClientHints
.begin(), mClientHints
.end());
817 setArguments(inArguments
);
818 setInput(inHints
, inContext
);
819 MacOSError::check(invoke());
821 if (outResult
) *outResult
= result();
823 inHints
= outHints();
824 inContext
= outContext();
827 void QueryInvokeMechanism::terminateAgent()
832 // @@@ no pluggable authentication possible!
834 QueryKeychainAuth::operator () (const char *database
, const char *description
, AclAuthorization action
, const char *prompt
)
836 Reason reason
= SecurityAgent::noReason
;
837 AuthItemSet hints
, context
;
838 AuthValueVector arguments
;
843 using CommonCriteria::Securityd::KeychainAuthLogger
;
844 KeychainAuthLogger
logger(mAuditToken
, AUE_ssauthint
, database
, description
);
847 /* XXX/gh probably not complete; stolen verbatim from rogue-app query */
848 if (getenv("NOSA")) {
849 char answer
[maxPassphraseLength
+10];
851 string applicationPath
;
852 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
853 if (applicationPathItem
)
854 applicationPathItem
->getString(applicationPath
);
856 getNoSA(answer
, sizeof(answer
), "Allow %s to do %d on %s in %s? [yn][g]%s ",
857 applicationPath
.c_str(), int(action
), (description
? description
: "[NULL item]"),
858 (database
? database
: "[NULL database]"),
859 mPassphraseCheck
? ":passphrase" : "");
860 // turn passphrase (no ':') into y:passphrase
861 if (mPassphraseCheck
&& !strchr(answer
, ':')) {
862 memmove(answer
+2, answer
, strlen(answer
)+1);
863 memcpy(answer
, "y:", 2);
866 allow
= answer
[0] == 'y';
867 remember
= answer
[1] == 'g';
868 return SecurityAgent::noReason
;
872 hints
.insert(mClientHints
.begin(), mClientHints
.end());
874 // put action/operation (sint32) into hints
875 hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
))));
877 hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt
? strlen(prompt
) : 0, const_cast<char*>(prompt
))));
879 // item name into hints
880 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description
? strlen(description
) : 0, const_cast<char*>(description
))));
882 // keychain name into hints
883 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
? strlen(database
) : 0, const_cast<char*>(database
))));
885 create("builtin", "confirm-access-user-password", noSecuritySession
);
887 AuthItem
*usernameItem
;
888 AuthItem
*passwordItem
;
892 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
893 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
895 if (++retryCount
> maxTries
)
896 reason
= SecurityAgent::tooManyTries
;
898 if (SecurityAgent::noReason
!= reason
)
900 if (SecurityAgent::tooManyTries
== reason
)
901 logger
.logFailure(NULL
, CommonCriteria::errTooManyTries
);
906 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
907 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
909 setInput(hints
, context
);
915 catch (...) // user probably clicked "deny"
920 usernameItem
= outContext().find(AGENT_USERNAME
);
921 passwordItem
= outContext().find(AGENT_PASSWORD
);
922 if (!usernameItem
|| !passwordItem
)
924 usernameItem
->getString(username
);
925 passwordItem
->getString(password
);
926 } while (reason
= accept(username
, password
));
928 if (SecurityAgent::noReason
== reason
)
930 // else we logged the denial in the loop
936 QueryKeychainAuth::accept(string
&username
, string
&passphrase
)
938 const char *user
= username
.c_str();
939 const char *passwd
= passphrase
.c_str();
940 int checkpw_status
= checkpw(user
, passwd
);
942 if (checkpw_status
!= CHECKPW_SUCCESS
)
943 return SecurityAgent::invalidPassphrase
;
945 return SecurityAgent::noReason
;