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
);
343 setTerminateOnSleep(true);
346 Reason
QueryKeychainUse::queryUser (const char *database
, const char *description
, AclAuthorization action
)
348 Reason reason
= SecurityAgent::noReason
;
351 AuthValueVector arguments
;
352 AuthItemSet hints
, context
;
355 if (getenv("NOSA")) {
356 char answer
[maxPassphraseLength
+10];
358 string applicationPath
;
359 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
360 if (applicationPathItem
)
361 applicationPathItem
->getString(applicationPath
);
363 getNoSA(answer
, sizeof(answer
), "Allow %s to do %d on %s in %s? [yn][g]%s ",
364 applicationPath
.c_str(), int(action
), (description
? description
: "[NULL item]"),
365 (database
? database
: "[NULL database]"),
366 mPassphraseCheck
? ":passphrase" : "");
367 // turn passphrase (no ':') into y:passphrase
368 if (mPassphraseCheck
&& !strchr(answer
, ':')) {
369 memmove(answer
+2, answer
, strlen(answer
)+1);
370 memcpy(answer
, "y:", 2);
373 allow
= answer
[0] == 'y';
374 remember
= answer
[1] == 'g';
375 return SecurityAgent::noReason
;
379 // prepopulate with client hints
380 hints
.insert(mClientHints
.begin(), mClientHints
.end());
382 // put action/operation (sint32) into hints
383 hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
))));
385 // item name into hints
387 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description
? strlen(description
) : 0, const_cast<char*>(description
))));
389 // keychain name into hints
390 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
? strlen(database
) : 0, const_cast<char*>(database
))));
393 if (mPassphraseCheck
)
395 create("builtin", "confirm-access-password", noSecuritySession
);
397 CssmAutoData
data(Allocator::standard(Allocator::sensitive
));
402 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
403 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
405 if (retryCount
++ > kMaximumAuthorizationTries
)
407 reason
= SecurityAgent::tooManyTries
;
410 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
411 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
413 setInput(hints
, context
);
416 if (retryCount
> kMaximumAuthorizationTries
)
423 AuthItem
*passwordItem
= outContext().find(kAuthorizationEnvironmentPassword
);
427 passwordItem
->getCssmData(data
);
429 while (reason
= (const_cast<KeychainDatabase
*>(mPassphraseCheck
)->decode(data
) ? SecurityAgent::noReason
: SecurityAgent::invalidPassphrase
));
433 create("builtin", "confirm-access", noSecuritySession
);
434 setInput(hints
, context
);
444 // Perform code signature ACL access adjustment dialogs
446 bool QueryCodeCheck::operator () (const char *aclPath
)
449 AuthValueVector arguments
;
450 AuthItemSet hints
, context
;
453 if (getenv("NOSA")) {
456 string applicationPath
;
457 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
458 if (applicationPathItem
)
459 applicationPathItem
->getString(applicationPath
);
461 getNoSA(answer
, sizeof(answer
),
462 "Allow %s to match an ACL for %s [yn][g]? ",
463 applicationPath
.c_str(), aclPath
? aclPath
: "(unknown)");
464 allow
= answer
[0] == 'y';
465 remember
= answer
[1] == 'g';
470 // prepopulate with client hints
471 hints
.insert(mClientHints
.begin(), mClientHints
.end());
473 hints
.insert(AuthItemRef(AGENT_HINT_APPLICATION_PATH
, AuthValueOverlay(strlen(aclPath
), const_cast<char*>(aclPath
))));
475 create("builtin", "code-identity", noSecuritySession
);
477 setInput(hints
, context
);
482 // MacOSError::check(status);
484 return kAuthorizationResultAllow
== result();
489 // Obtain passphrases and submit them to the accept() method until it is accepted
490 // or we can't get another passphrase. Accept() should consume the passphrase
491 // if it is accepted. If no passphrase is acceptable, throw out of here.
493 Reason
QueryOld::query()
495 Reason reason
= SecurityAgent::noReason
;
497 AuthValueVector arguments
;
498 AuthItemSet hints
, context
;
499 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
503 // return the passphrase
504 if (getenv("NOSA")) {
505 char passphrase_
[maxPassphraseLength
];
506 getNoSA(passphrase
, maxPassphraseLength
, "Unlock %s [<CR> to cancel]: ", database
.dbName());
507 passphrase
.copy(passphrase_
, strlen(passphrase_
));
508 return database
.decode(passphrase
) ? SecurityAgent::noReason
: SecurityAgent::invalidPassphrase
;
512 // prepopulate with client hints
514 const char *keychainPath
= database
.dbName();
515 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(strlen(keychainPath
), const_cast<char*>(keychainPath
))));
517 hints
.insert(mClientHints
.begin(), mClientHints
.end());
519 create("builtin", "unlock-keychain", noSecuritySession
);
523 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
524 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
528 if (retryCount
> maxTries
)
530 reason
= SecurityAgent::tooManyTries
;
533 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
534 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
536 setInput(hints
, context
);
539 if (retryCount
> maxTries
)
546 AuthItem
*passwordItem
= outContext().find(kAuthorizationEnvironmentPassword
);
550 passwordItem
->getCssmData(passphrase
);
553 while (reason
= accept(passphrase
));
555 return SecurityAgent::noReason
;
560 // Get existing passphrase (unlock) Query
562 Reason
QueryOld::operator () ()
569 // End-classes for old secrets
571 Reason
QueryUnlock::accept(CssmManagedData
&passphrase
)
573 if (safer_cast
<KeychainDatabase
&>(database
).decode(passphrase
))
574 return SecurityAgent::noReason
;
576 return SecurityAgent::invalidPassphrase
;
580 QueryPIN::QueryPIN(Database
&db
)
581 : QueryOld(db
), mPin(Allocator::standard())
583 this->inferHints(Server::process());
587 Reason
QueryPIN::accept(CssmManagedData
&pin
)
589 // no retries for now
591 return SecurityAgent::noReason
;
596 // Obtain passphrases and submit them to the accept() method until it is accepted
597 // or we can't get another passphrase. Accept() should consume the passphrase
598 // if it is accepted. If no passphrase is acceptable, throw out of here.
600 Reason
QueryNewPassphrase::query()
602 Reason reason
= initialReason
;
603 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
604 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
607 AuthValueVector arguments
;
608 AuthItemSet hints
, context
;
613 if (getenv("NOSA")) {
614 char passphrase_
[maxPassphraseLength
];
615 getNoSA(passphrase_
, maxPassphraseLength
,
616 "New passphrase for %s (reason %d) [<CR> to cancel]: ",
617 database
.dbName(), reason
);
618 return SecurityAgent::noReason
;
622 // prepopulate with client hints
623 hints
.insert(mClientHints
.begin(), mClientHints
.end());
625 // keychain name into hints
626 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
.dbName())));
628 switch (initialReason
)
630 case SecurityAgent::newDatabase
:
631 create("builtin", "new-passphrase", noSecuritySession
);
633 case SecurityAgent::changePassphrase
:
634 create("builtin", "change-passphrase", noSecuritySession
);
642 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
643 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
645 if (++retryCount
> maxTries
)
647 reason
= SecurityAgent::tooManyTries
;
650 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
651 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
653 setInput(hints
, context
);
656 if (retryCount
> maxTries
)
663 if (SecurityAgent::changePassphrase
== initialReason
)
665 AuthItem
*oldPasswordItem
= outContext().find(AGENT_PASSWORD
);
666 if (!oldPasswordItem
)
669 oldPasswordItem
->getCssmData(oldPassphrase
);
672 AuthItem
*passwordItem
= outContext().find(AGENT_CONTEXT_NEW_PASSWORD
);
676 passwordItem
->getCssmData(passphrase
);
679 while (reason
= accept(passphrase
, (initialReason
== SecurityAgent::changePassphrase
) ? &oldPassphrase
.get() : NULL
));
681 return SecurityAgent::noReason
;
686 // Get new passphrase Query
688 Reason
QueryNewPassphrase::operator () (CssmOwnedData
&passphrase
)
690 if (Reason result
= query())
691 return result
; // failed
692 passphrase
= mPassphrase
;
693 return SecurityAgent::noReason
; // success
696 Reason
QueryNewPassphrase::accept(CssmManagedData
&passphrase
, CssmData
*oldPassphrase
)
698 //@@@ acceptance criteria are currently hardwired here
699 //@@@ This validation presumes ASCII - UTF8 might be more lenient
701 // if we have an old passphrase, check it
702 if (oldPassphrase
&& !safer_cast
<KeychainDatabase
&>(database
).validatePassphrase(*oldPassphrase
))
703 return SecurityAgent::oldPassphraseWrong
;
705 // sanity check the new passphrase (but allow user override)
706 if (!(mPassphraseValid
&& passphrase
.get() == mPassphrase
)) {
707 mPassphrase
= passphrase
;
708 mPassphraseValid
= true;
709 if (mPassphrase
.length() == 0)
710 return SecurityAgent::passphraseIsNull
;
711 if (mPassphrase
.length() < 6)
712 return SecurityAgent::passphraseTooSimple
;
716 return SecurityAgent::noReason
;
720 // Get a passphrase for unspecified use
722 Reason
QueryGenericPassphrase::operator () (const CssmData
*prompt
, bool verify
,
725 return query(prompt
, verify
, passphrase
);
728 Reason
QueryGenericPassphrase::query(const CssmData
*prompt
, bool verify
,
731 Reason reason
= SecurityAgent::noReason
;
732 OSStatus status
; // not really used; remove?
733 AuthValueVector arguments
;
734 AuthItemSet hints
, context
;
737 if (getenv("NOSA")) {
739 return SecurityAgent::noReason
;
743 hints
.insert(mClientHints
.begin(), mClientHints
.end());
744 hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt
? (UInt32
)prompt
->length() : 0, prompt
? prompt
->data() : NULL
)));
745 // XXX/gh defined by dmitch but no analogous hint in
746 // AuthorizationTagsPriv.h:
747 // CSSM_ATTRIBUTE_ALERT_TITLE (optional alert panel title)
749 if (false == verify
) { // import
750 create("builtin", "generic-unlock", noSecuritySession
);
751 } else { // verify passphrase (export)
752 // new-passphrase-generic works with the pre-4 June 2004 agent;
753 // generic-new-passphrase is required for the new agent
754 create("builtin", "generic-new-passphrase", noSecuritySession
);
757 AuthItem
*passwordItem
;
760 setInput(hints
, context
);
763 passwordItem
= outContext().find(AGENT_PASSWORD
);
765 } while (!passwordItem
);
767 passwordItem
->getString(passphrase
);
774 // Get a DB blob's passphrase--keychain synchronization
776 Reason
QueryDBBlobSecret::operator () (DbHandle
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle
*dbHandleAuthenticated
)
778 return query(dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
);
781 Reason
QueryDBBlobSecret::query(DbHandle
*dbHandleArray
, uint8 dbHandleArrayCount
, DbHandle
*dbHandleAuthenticated
)
783 Reason reason
= SecurityAgent::noReason
;
784 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
785 OSStatus status
; // not really used; remove?
786 AuthValueVector arguments
;
787 AuthItemSet hints
/*NUKEME*/, context
;
790 if (getenv("NOSA")) {
791 // FIXME akin to 3690984
792 return SecurityAgent::noReason
;
796 hints
.insert(mClientHints
.begin(), mClientHints
.end());
798 create("builtin", "generic-unlock-kcblob", noSecuritySession
);
800 AuthItem
*secretItem
;
805 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
806 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
808 if (++retryCount
> maxTries
)
810 reason
= SecurityAgent::tooManyTries
;
813 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
814 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
816 setInput(hints
, context
);
819 secretItem
= outContext().find(AGENT_PASSWORD
);
822 secretItem
->getCssmData(passphrase
);
824 } while (reason
= accept(passphrase
, dbHandleArray
, dbHandleArrayCount
, dbHandleAuthenticated
));
829 Reason
QueryDBBlobSecret::accept(CssmManagedData
&passphrase
,
830 DbHandle
*dbHandlesToAuthenticate
, uint8 dbHandleCount
, DbHandle
*dbHandleAuthenticated
)
832 DbHandle
*currHdl
= dbHandlesToAuthenticate
;
834 Boolean authenticated
= false;
835 for (index
=0; index
< dbHandleCount
&& !authenticated
; index
++)
839 RefPointer
<KeychainDatabase
> dbToUnlock
= Server::keychain(*currHdl
);
840 dbToUnlock
->unlockDb(passphrase
);
841 authenticated
= true;
842 *dbHandleAuthenticated
= *currHdl
; // return the DbHandle that 'passphrase' authenticated with.
844 catch (const CommonError
&err
)
846 currHdl
++; // we failed to authenticate with this one, onto the next one.
849 if ( !authenticated
)
850 return SecurityAgent::invalidPassphrase
;
852 return SecurityAgent::noReason
;
855 QueryInvokeMechanism::QueryInvokeMechanism(const AuthHostType type
, Session
&session
) :
856 SecurityAgentQuery(type
, session
) { }
858 void QueryInvokeMechanism::initialize(const string
&inPluginId
, const string
&inMechanismId
, const AuthValueVector
&inArguments
, const SessionId inSessionId
)
860 if (SecurityAgent::Client::init
== SecurityAgent::Client::state())
862 create(inPluginId
.c_str(), inMechanismId
.c_str(), inSessionId
);
863 mArguments
= inArguments
;
867 // XXX/cs should return AuthorizationResult
868 void QueryInvokeMechanism::run(const AuthValueVector
&inArguments
, AuthItemSet
&inHints
, AuthItemSet
&inContext
, AuthorizationResult
*outResult
)
870 // prepopulate with client hints
871 inHints
.insert(mClientHints
.begin(), mClientHints
.end());
873 if (Server::active().inDarkWake())
874 CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE
);
876 setArguments(inArguments
);
877 setInput(inHints
, inContext
);
878 MacOSError::check(invoke());
880 if (outResult
) *outResult
= result();
882 inHints
= outHints();
883 inContext
= outContext();
886 void QueryInvokeMechanism::terminateAgent()
891 // @@@ no pluggable authentication possible!
893 QueryKeychainAuth::operator () (const char *database
, const char *description
, AclAuthorization action
, const char *prompt
)
895 Reason reason
= SecurityAgent::noReason
;
896 AuthItemSet hints
, context
;
897 AuthValueVector arguments
;
902 using CommonCriteria::Securityd::KeychainAuthLogger
;
903 KeychainAuthLogger
logger(mAuditToken
, AUE_ssauthint
, database
, description
);
906 /* XXX/gh probably not complete; stolen verbatim from rogue-app query */
907 if (getenv("NOSA")) {
908 char answer
[maxPassphraseLength
+10];
910 string applicationPath
;
911 AuthItem
*applicationPathItem
= mClientHints
.find(AGENT_HINT_APPLICATION_PATH
);
912 if (applicationPathItem
)
913 applicationPathItem
->getString(applicationPath
);
915 getNoSA(answer
, sizeof(answer
), "Allow %s to do %d on %s in %s? [yn][g]%s ",
916 applicationPath
.c_str(), int(action
), (description
? description
: "[NULL item]"),
917 (database
? database
: "[NULL database]"),
918 mPassphraseCheck
? ":passphrase" : "");
919 // turn passphrase (no ':') into y:passphrase
920 if (mPassphraseCheck
&& !strchr(answer
, ':')) {
921 memmove(answer
+2, answer
, strlen(answer
)+1);
922 memcpy(answer
, "y:", 2);
925 allow
= answer
[0] == 'y';
926 remember
= answer
[1] == 'g';
927 return SecurityAgent::noReason
;
931 hints
.insert(mClientHints
.begin(), mClientHints
.end());
933 // put action/operation (sint32) into hints
934 hints
.insert(AuthItemRef(AGENT_HINT_ACL_TAG
, AuthValueOverlay(sizeof(action
), static_cast<sint32
*>(&action
))));
936 hints
.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT
, AuthValueOverlay(prompt
? strlen(prompt
) : 0, const_cast<char*>(prompt
))));
938 // item name into hints
939 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME
, AuthValueOverlay(description
? strlen(description
) : 0, const_cast<char*>(description
))));
941 // keychain name into hints
942 hints
.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH
, AuthValueOverlay(database
? strlen(database
) : 0, const_cast<char*>(database
))));
944 create("builtin", "confirm-access-user-password", noSecuritySession
);
946 AuthItem
*usernameItem
;
947 AuthItem
*passwordItem
;
951 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(retryCount
), &retryCount
));
952 hints
.erase(triesHint
); hints
.insert(triesHint
); // replace
954 if (++retryCount
> maxTries
)
955 reason
= SecurityAgent::tooManyTries
;
957 if (SecurityAgent::noReason
!= reason
)
959 if (SecurityAgent::tooManyTries
== reason
)
960 logger
.logFailure(NULL
, CommonCriteria::errTooManyTries
);
965 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
966 hints
.erase(retryHint
); hints
.insert(retryHint
); // replace
968 setInput(hints
, context
);
974 catch (...) // user probably clicked "deny"
979 usernameItem
= outContext().find(AGENT_USERNAME
);
980 passwordItem
= outContext().find(AGENT_PASSWORD
);
981 if (!usernameItem
|| !passwordItem
)
983 usernameItem
->getString(username
);
984 passwordItem
->getString(password
);
985 } while (reason
= accept(username
, password
));
987 if (SecurityAgent::noReason
== reason
)
989 // else we logged the denial in the loop
995 QueryKeychainAuth::accept(string
&username
, string
&passphrase
)
997 const char *user
= username
.c_str();
998 const char *passwd
= passphrase
.c_str();
999 int checkpw_status
= checkpw(user
, passwd
);
1001 if (checkpw_status
!= CHECKPW_SUCCESS
)
1002 return SecurityAgent::invalidPassphrase
;
1004 return SecurityAgent::noReason
;