]> git.saurik.com Git - apple/security.git/blame - securityd/src/agentquery.cpp
Security-57336.10.29.tar.gz
[apple/security.git] / securityd / src / agentquery.cpp
CommitLineData
d8f41ccd 1/*
5c19dc3a
A
2 * Copyright (c) 2000-2015 Apple Inc. All Rights Reserved.
3 *
d8f41ccd 4 * @APPLE_LICENSE_HEADER_START@
5c19dc3a 5 *
d8f41ccd
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
5c19dc3a 12 *
d8f41ccd
A
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.
5c19dc3a 20 *
d8f41ccd
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24//
25// passphrases - canonical code to obtain passphrases
26//
27#include "agentquery.h"
28#include "authority.h"
29#include "ccaudit_extensions.h"
30
31#include <Security/AuthorizationTags.h>
32#include <Security/AuthorizationTagsPriv.h>
33#include <Security/checkpw.h>
34#include <Security/Security.h>
35#include <System/sys/fileport.h>
36#include <bsm/audit.h>
37#include <bsm/audit_uevents.h> // AUE_ssauthint
5dd5f9ec
A
38#include <membership.h>
39#include <membershipPriv.h>
d8f41ccd
A
40#include <security_utilities/logging.h>
41#include <security_utilities/mach++.h>
42#include <stdlib.h>
43#include <xpc/xpc.h>
44#include <xpc/private.h>
45#include "securityd_service/securityd_service/securityd_service_client.h"
46
5c19dc3a
A
47#define SECURITYAGENT_BOOTSTRAP_NAME_BASE "com.apple.security.agent"
48#define SECURITYAGENT_LOGINWINDOW_BOOTSTRAP_NAME_BASE "com.apple.security.agent.login"
49#define AUTHORIZATIONHOST_BOOTSTRAP_NAME_BASE "com.apple.security.authhost"
d8f41ccd
A
50
51#define AUTH_XPC_ITEM_NAME "_item_name"
52#define AUTH_XPC_ITEM_FLAGS "_item_flags"
53#define AUTH_XPC_ITEM_VALUE "_item_value"
54#define AUTH_XPC_ITEM_TYPE "_item_type"
55
56#define AUTH_XPC_REQUEST_METHOD_KEY "_agent_request_key"
57#define AUTH_XPC_REQUEST_METHOD_CREATE "_agent_request_create"
58#define AUTH_XPC_REQUEST_METHOD_INVOKE "_agent_request_invoke"
59#define AUTH_XPC_REQUEST_METHOD_DEACTIVATE "_agent_request_deactivate"
60#define AUTH_XPC_REQUEST_METHOD_DESTROY "_agent_request_destroy"
61#define AUTH_XPC_REPLY_METHOD_KEY "_agent_reply_key"
62#define AUTH_XPC_REPLY_METHOD_RESULT "_agent_reply_result"
63#define AUTH_XPC_REPLY_METHOD_INTERRUPT "_agent_reply_interrupt"
64#define AUTH_XPC_REPLY_METHOD_CREATE "_agent_reply_create"
65#define AUTH_XPC_REPLY_METHOD_DEACTIVATE "_agent_reply_deactivate"
66#define AUTH_XPC_PLUGIN_NAME "_agent_plugin"
67#define AUTH_XPC_MECHANISM_NAME "_agent_mechanism"
68#define AUTH_XPC_HINTS_NAME "_agent_hints"
69#define AUTH_XPC_CONTEXT_NAME "_agent_context"
70#define AUTH_XPC_IMMUTABLE_HINTS_NAME "_agent_immutable_hints"
71#define AUTH_XPC_REQUEST_INSTANCE "_agent_instance"
72#define AUTH_XPC_REPLY_RESULT_VALUE "_agent_reply_result_value"
73#define AUTH_XPC_AUDIT_SESSION_PORT "_agent_audit_session_port"
74#define AUTH_XPC_BOOTSTRAP_PORT "_agent_bootstrap_port"
d8f41ccd
A
75
76#define UUID_INITIALIZER_FROM_SESSIONID(sessionid) \
77{ 0,0,0,0, 0,0,0,0, 0,0,0,0, (unsigned char)((0xff000000 & (sessionid))>>24), (unsigned char)((0x00ff0000 & (sessionid))>>16), (unsigned char)((0x0000ff00 & (sessionid))>>8), (unsigned char)((0x000000ff & (sessionid))) }
78
5c19dc3a 79
d8f41ccd
A
80// NOSA support functions. This is a test mode where the SecurityAgent
81// is simulated via stdio in the client. Good for running automated tests
82// of client programs. Only available if -DNOSA when compiling.
d8f41ccd 83
5c19dc3a 84#if defined(NOSA)
d8f41ccd
A
85#include <cstdarg>
86
87static void getNoSA(char *buffer, size_t bufferSize, const char *fmt, ...)
88{
89 // write prompt
90 va_list args;
91 va_start(args, fmt);
92 vfprintf(stdout, fmt, args);
93 va_end(args);
94
95 // read reply
96 memset(buffer, 0, bufferSize);
97 const char *nosa = getenv("NOSA");
98 if (!strcmp(nosa, "-")) {
99 if (fgets(buffer, bufferSize-1, stdin) == NULL)
100 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
101 buffer[strlen(buffer)-1] = '\0'; // remove trailing newline
102 if (!isatty(fileno(stdin)))
103 printf("%s\n", buffer); // echo to output if input not terminal
104 } else {
105 strncpy(buffer, nosa, bufferSize-1);
106 printf("%s\n", buffer);
107 }
108 if (buffer[0] == '\0') // empty input -> cancellation
109 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
110}
5c19dc3a 111#endif // NOSA
d8f41ccd
A
112
113
5c19dc3a 114// SecurityAgentXPCConnection
d8f41ccd
A
115
116SecurityAgentXPCConnection::SecurityAgentXPCConnection(const AuthHostType type, Session &session)
117: mAuthHostType(type),
118mHostInstance(session.authhost(mAuthHostType)),
119mSession(session),
120mConnection(&Server::connection()),
121mAuditToken(Server::connection().auditToken())
122{
123 // this may take a while
124 Server::active().longTermActivity();
125 secdebug("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this);
126 mXPCConnection = NULL;
d8f41ccd
A
127 mNobodyUID = -2;
128 struct passwd *pw = getpwnam("nobody");
129 if (NULL != pw) {
130 mNobodyUID = pw->pw_uid;
131 }
132}
133
134SecurityAgentXPCConnection::~SecurityAgentXPCConnection()
135{
136 secdebug("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this);
137 mConnection->useAgent(NULL);
5c19dc3a 138
d8f41ccd
A
139 // If a connection has been established, we need to tear it down.
140 if (NULL != mXPCConnection) {
141 // Tearing this down is a multi-step process. First, request a cancellation.
142 // This is safe even if the connection is already in the cancelled state.
143 xpc_connection_cancel(mXPCConnection);
5c19dc3a 144
d8f41ccd
A
145 // Then release the XPC connection
146 xpc_release(mXPCConnection);
147 mXPCConnection = NULL;
d8f41ccd
A
148 }
149}
150
151bool SecurityAgentXPCConnection::inDarkWake()
152{
153 return mSession.server().inDarkWake();
154}
155
156void
157SecurityAgentXPCConnection::activate(bool ignoreUid)
158{
159 secdebug("SecurityAgentConnection", "activate(%p)", this);
5c19dc3a 160
d8f41ccd
A
161 mConnection->useAgent(this);
162 if (mXPCConnection != NULL) {
163 // If we already have an XPC connection, there's nothing to do.
164 return;
165 }
5c19dc3a
A
166
167 try {
168 uuid_t sessionUUID = UUID_INITIALIZER_FROM_SESSIONID(mSession.sessionId());
169
d8f41ccd 170 if (mAuthHostType == securityAgent) {
d8f41ccd
A
171 // Yes, these need to be throws, as we're still in securityd, and thus still have to do flow control with exceptions.
172 if (!(mSession.attributes() & sessionHasGraphicAccess))
173 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
174 if (inDarkWake())
175 CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE);
176 uid_t targetUid = mHostInstance->session().originatorUid();
5c19dc3a 177
d8f41ccd 178 secdebug("SecurityAgentXPCConnection","Retrieved UID %d for this session", targetUid);
5c19dc3a
A
179 if (!ignoreUid && targetUid != 0 && targetUid != mNobodyUID) {
180 mXPCConnection = xpc_connection_create_mach_service(SECURITYAGENT_BOOTSTRAP_NAME_BASE, NULL, 0);
181 xpc_connection_set_target_uid(mXPCConnection, targetUid);
182 secdebug("SecurityAgentXPCConnection", "Creating a standard security agent");
183 } else {
184 mXPCConnection = xpc_connection_create_mach_service(SECURITYAGENT_LOGINWINDOW_BOOTSTRAP_NAME_BASE, NULL, 0);
185 xpc_connection_set_instance(mXPCConnection, sessionUUID);
186 secdebug("SecurityAgentXPCConnection", "Creating a loginwindow security agent");
d8f41ccd 187 }
d8f41ccd
A
188 } else {
189 mXPCConnection = xpc_connection_create_mach_service(AUTHORIZATIONHOST_BOOTSTRAP_NAME_BASE, NULL, 0);
5c19dc3a 190 xpc_connection_set_instance(mXPCConnection, sessionUUID);
d8f41ccd
A
191 secdebug("SecurityAgentXPCConnection", "Creating a standard authhost");
192 }
5c19dc3a
A
193
194 xpc_connection_set_event_handler(mXPCConnection, ^(xpc_object_t object) {
195 if (xpc_get_type(object) == XPC_TYPE_ERROR) {
196 secdebug("SecurityAgentXPCConnection", "error during xpc: %s", xpc_dictionary_get_string(object, XPC_ERROR_KEY_DESCRIPTION));
197 }
198 });
199 xpc_connection_resume(mXPCConnection);
200 secdebug("SecurityAgentXPCConnection", "%p activated", this);
d8f41ccd 201 }
5c19dc3a 202 catch (MacOSError &err) {
d8f41ccd
A
203 mConnection->useAgent(NULL); // guess not
204 Syslog::error("SecurityAgentConnection: error activating %s instance %p",
205 mAuthHostType == privilegedAuthHost
206 ? "authorizationhost"
207 : "SecurityAgent", this);
208 throw;
209 }
5c19dc3a 210
d8f41ccd
A
211 secdebug("SecurityAgentXPCConnection", "contact didn't throw (%p)", this);
212}
213
214void
215SecurityAgentXPCConnection::reconnect()
216{
217}
218
219void
220SecurityAgentXPCConnection::terminate()
221{
222 activate(false);
5c19dc3a 223
d8f41ccd
A
224 // @@@ This happens already in the destructor; presumably we do this to tear things down orderly
225 mConnection->useAgent(NULL);
226}
227
228
229using SecurityAgent::Reason;
230using namespace Authorization;
231
d8f41ccd
A
232ModuleNexus<RecursiveMutex> gAllXPCClientsMutex;
233ModuleNexus<set<SecurityAgentXPCQuery*> > allXPCClients;
234
235void
236SecurityAgentXPCQuery::killAllXPCClients()
237{
238 // grab the lock for the client list -- we need to make sure no one modifies the structure while we are iterating it.
239 StLock<Mutex> _(gAllXPCClientsMutex());
5c19dc3a 240
d8f41ccd
A
241 set<SecurityAgentXPCQuery*>::iterator clientIterator = allXPCClients().begin();
242 while (clientIterator != allXPCClients().end())
243 {
244 set<SecurityAgentXPCQuery*>::iterator thisClient = clientIterator++;
245 if ((*thisClient)->getTerminateOnSleep())
246 {
247 (*thisClient)->terminate();
248 }
249 }
250}
251
252
253SecurityAgentXPCQuery::SecurityAgentXPCQuery(const AuthHostType type, Session &session)
254: SecurityAgentXPCConnection(type, session), mAgentConnected(false), mTerminateOnSleep(false)
255{
256 secdebug("SecurityAgentXPCQuery", "new SecurityAgentXPCQuery(%p)", this);
257}
258
259SecurityAgentXPCQuery::~SecurityAgentXPCQuery()
260{
261 secdebug("SecurityAgentXPCQuery", "SecurityAgentXPCQuery(%p) dying", this);
262 if (mAgentConnected) {
263 this->disconnect();
264 }
265}
266
267void
268SecurityAgentXPCQuery::inferHints(Process &thisProcess)
269{
270 string guestPath;
271 if (SecCodeRef clientCode = thisProcess.currentGuest())
272 guestPath = codePath(clientCode);
273
274 AuthItemSet clientHints;
275 SecurityAgent::RequestorType type = SecurityAgent::bundle;
276 pid_t clientPid = thisProcess.pid();
277 uid_t clientUid = thisProcess.uid();
5c19dc3a 278
d8f41ccd
A
279 clientHints.insert(AuthItemRef(AGENT_HINT_CLIENT_TYPE, AuthValueOverlay(sizeof(type), &type)));
280 clientHints.insert(AuthItemRef(AGENT_HINT_CLIENT_PATH, AuthValueOverlay(guestPath)));
281 clientHints.insert(AuthItemRef(AGENT_HINT_CLIENT_PID, AuthValueOverlay(sizeof(clientPid), &clientPid)));
282 clientHints.insert(AuthItemRef(AGENT_HINT_CLIENT_UID, AuthValueOverlay(sizeof(clientUid), &clientUid)));
5c19dc3a 283
d8f41ccd
A
284
285 mClientHints.insert(clientHints.begin(), clientHints.end());
286
287 bool validSignature = thisProcess.checkAppleSigned();
288 AuthItemSet clientImmutableHints;
5c19dc3a 289
d8f41ccd
A
290 clientImmutableHints.insert(AuthItemRef(AGENT_HINT_PROCESS_SIGNED, AuthValueOverlay(sizeof(validSignature), &validSignature)));
291
292 mImmutableHints.insert(clientImmutableHints.begin(), clientImmutableHints.end());
293}
294
295void SecurityAgentXPCQuery::addHint(const char *name, const void *value, UInt32 valueLen, UInt32 flags)
296{
297 AuthorizationItem item = { name, valueLen, const_cast<void *>(value), flags };
298 mClientHints.insert(AuthItemRef(item));
299}
300
301
302void
303SecurityAgentXPCQuery::readChoice()
304{
305 allow = false;
306 remember = false;
5c19dc3a 307
d8f41ccd
A
308 AuthItem *allowAction = mOutContext.find(AGENT_CONTEXT_ALLOW);
309 if (allowAction)
310 {
311 string allowString;
312 if (allowAction->getString(allowString)
313 && (allowString == "YES"))
314 allow = true;
315 }
5c19dc3a 316
d8f41ccd
A
317 AuthItem *rememberAction = mOutContext.find(AGENT_CONTEXT_REMEMBER_ACTION);
318 if (rememberAction)
319 {
320 string rememberString;
321 if (rememberAction->getString(rememberString)
322 && (rememberString == "YES"))
323 remember = true;
324 }
325}
326
327void
328SecurityAgentXPCQuery::disconnect()
329{
330 if (NULL != mXPCConnection) {
331 xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
332 xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_DESTROY);
333 xpc_connection_send_message(mXPCConnection, requestObject);
334 xpc_release(requestObject);
335 }
336
337 StLock<Mutex> _(gAllXPCClientsMutex());
338 allXPCClients().erase(this);
339}
340
341void
342SecurityAgentXPCQuery::terminate()
343{
344 this->disconnect();
345}
346
347static void xpcArrayToAuthItemSet(AuthItemSet *setToBuild, xpc_object_t input) {
348 setToBuild->clear();
5c19dc3a 349
d8f41ccd
A
350 xpc_array_apply(input, ^bool(size_t index, xpc_object_t item) {
351 const char *name = xpc_dictionary_get_string(item, AUTH_XPC_ITEM_NAME);
5c19dc3a 352
d8f41ccd
A
353 size_t length;
354 const void *data = xpc_dictionary_get_data(item, AUTH_XPC_ITEM_VALUE, &length);
355 void *dataCopy = malloc(length);
356 memcpy(dataCopy, data, length);
5c19dc3a 357
d8f41ccd
A
358 uint64_t flags = xpc_dictionary_get_uint64(item, AUTH_XPC_ITEM_FLAGS);
359 AuthItemRef nextItem(name, AuthValueOverlay((uint32_t)length, dataCopy), (uint32_t)flags);
360 setToBuild->insert(nextItem);
361 memset(dataCopy, 0, length); // The authorization items contain things like passwords, so wiping clean is important.
362 free(dataCopy);
363 return true;
364 });
365}
366
367void
5c19dc3a 368SecurityAgentXPCQuery::create(const char *pluginId, const char *mechanismId)
d8f41ccd
A
369{
370 bool ignoreUid = false;
5c19dc3a 371
d8f41ccd
A
372 do {
373 activate(ignoreUid);
5c19dc3a 374
d8f41ccd 375 mAgentConnected = false;
5c19dc3a 376
d8f41ccd
A
377 xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
378 xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_CREATE);
379 xpc_dictionary_set_string(requestObject, AUTH_XPC_PLUGIN_NAME, pluginId);
380 xpc_dictionary_set_string(requestObject, AUTH_XPC_MECHANISM_NAME, mechanismId);
5c19dc3a 381
d8f41ccd 382 uid_t targetUid = Server::process().uid();
5c19dc3a
A
383 bool doSwitchAudit = (ignoreUid || targetUid == 0 || targetUid == mNobodyUID);
384 bool doSwitchBootstrap = (ignoreUid || targetUid == 0 || targetUid == mNobodyUID);
385
d8f41ccd
A
386 if (doSwitchAudit) {
387 mach_port_name_t jobPort;
388 if (0 == audit_session_port(mSession.sessionId(), &jobPort)) {
389 secdebug("SecurityAgentXPCQuery", "attaching an audit session port because the uid was %d", targetUid);
390 xpc_dictionary_set_mach_send(requestObject, AUTH_XPC_AUDIT_SESSION_PORT, jobPort);
391 if (mach_port_mod_refs(mach_task_self(), jobPort, MACH_PORT_RIGHT_SEND, -1) != KERN_SUCCESS) {
392 secdebug("SecurityAgentXPCQuery", "unable to release send right for audit session, leaking");
393 }
394 }
395 }
5c19dc3a 396
d8f41ccd
A
397 if (doSwitchBootstrap) {
398 secdebug("SecurityAgentXPCQuery", "attaching a bootstrap port because the uid was %d", targetUid);
399 MachPlusPlus::Bootstrap processBootstrap = Server::process().taskPort().bootstrap();
400 xpc_dictionary_set_mach_send(requestObject, AUTH_XPC_BOOTSTRAP_PORT, processBootstrap);
401 }
5c19dc3a 402
d8f41ccd
A
403 xpc_object_t object = xpc_connection_send_message_with_reply_sync(mXPCConnection, requestObject);
404 if (xpc_get_type(object) == XPC_TYPE_DICTIONARY) {
405 const char *replyType = xpc_dictionary_get_string(object, AUTH_XPC_REPLY_METHOD_KEY);
406 if (0 == strcmp(replyType, AUTH_XPC_REPLY_METHOD_CREATE)) {
407 uint64_t status = xpc_dictionary_get_uint64(object, AUTH_XPC_REPLY_RESULT_VALUE);
408 if (status == kAuthorizationResultAllow) {
409 mAgentConnected = true;
410 } else {
411 secdebug("SecurityAgentXPCQuery", "plugin create failed in SecurityAgent");
412 MacOSError::throwMe(errAuthorizationInternal);
413 }
414 }
415 } else if (xpc_get_type(object) == XPC_TYPE_ERROR) {
416 if (XPC_ERROR_CONNECTION_INVALID == object) {
417 // If we get an error before getting the create response, try again without the UID
418 if (ignoreUid) {
419 secdebug("SecurityAgentXPCQuery", "failed to establish connection, no retries left");
420 xpc_release(object);
421 MacOSError::throwMe(errAuthorizationInternal);
422 } else {
423 secdebug("SecurityAgentXPCQuery", "failed to establish connection, retrying with no UID");
424 ignoreUid = true;
425 xpc_release(mXPCConnection);
426 mXPCConnection = NULL;
427 }
428 } else if (XPC_ERROR_CONNECTION_INTERRUPTED == object) {
429 // If we get an error before getting the create response, try again
430 }
431 }
432 xpc_release(object);
433 xpc_release(requestObject);
434 } while (!mAgentConnected);
435
436 StLock<Mutex> _(gAllXPCClientsMutex());
437 allXPCClients().insert(this);
438}
439
440static xpc_object_t authItemSetToXPCArray(AuthItemSet input) {
441 xpc_object_t outputArray = xpc_array_create(NULL, 0);
442 for (AuthItemSet::iterator i = input.begin(); i != input.end(); i++) {
443 AuthItemRef item = *i;
5c19dc3a 444
d8f41ccd
A
445 xpc_object_t xpc_data = xpc_dictionary_create(NULL, NULL, 0);
446 xpc_dictionary_set_string(xpc_data, AUTH_XPC_ITEM_NAME, item->name());
447 AuthorizationValue value = item->value();
448 if (value.data != NULL) {
449 xpc_dictionary_set_data(xpc_data, AUTH_XPC_ITEM_VALUE, value.data, value.length);
450 }
451 xpc_dictionary_set_uint64(xpc_data, AUTH_XPC_ITEM_FLAGS, item->flags());
452 xpc_array_append_value(outputArray, xpc_data);
453 xpc_release(xpc_data);
454 }
455 return outputArray;
456}
457
458OSStatus
459SecurityAgentXPCQuery::invoke() {
460 __block OSStatus status = kAuthorizationResultUndefined;
5c19dc3a 461
d8f41ccd
A
462 xpc_object_t hintsArray = authItemSetToXPCArray(mInHints);
463 xpc_object_t contextArray = authItemSetToXPCArray(mInContext);
464 xpc_object_t immutableHintsArray = authItemSetToXPCArray(mImmutableHints);
5c19dc3a 465
d8f41ccd
A
466 xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
467 xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_INVOKE);
468 xpc_dictionary_set_value(requestObject, AUTH_XPC_HINTS_NAME, hintsArray);
469 xpc_dictionary_set_value(requestObject, AUTH_XPC_CONTEXT_NAME, contextArray);
470 xpc_dictionary_set_value(requestObject, AUTH_XPC_IMMUTABLE_HINTS_NAME, immutableHintsArray);
5c19dc3a 471
d8f41ccd
A
472 xpc_object_t object = xpc_connection_send_message_with_reply_sync(mXPCConnection, requestObject);
473 if (xpc_get_type(object) == XPC_TYPE_DICTIONARY) {
474 const char *replyType = xpc_dictionary_get_string(object, AUTH_XPC_REPLY_METHOD_KEY);
475 if (0 == strcmp(replyType, AUTH_XPC_REPLY_METHOD_RESULT)) {
476 xpc_object_t xpcHints = xpc_dictionary_get_value(object, AUTH_XPC_HINTS_NAME);
477 xpc_object_t xpcContext = xpc_dictionary_get_value(object, AUTH_XPC_CONTEXT_NAME);
478 AuthItemSet tempHints, tempContext;
479 xpcArrayToAuthItemSet(&tempHints, xpcHints);
480 xpcArrayToAuthItemSet(&tempContext, xpcContext);
481 mOutHints = tempHints;
482 mOutContext = tempContext;
483 mLastResult = xpc_dictionary_get_uint64(object, AUTH_XPC_REPLY_RESULT_VALUE);
484 }
485 } else if (xpc_get_type(object) == XPC_TYPE_ERROR) {
486 if (XPC_ERROR_CONNECTION_INVALID == object) {
487 // If the connection drops, return an "auth undefined" result, because we cannot continue
488 } else if (XPC_ERROR_CONNECTION_INTERRUPTED == object) {
489 // If the agent dies, return an "auth undefined" result, because we cannot continue
490 }
491 }
492 xpc_release(object);
5c19dc3a 493
d8f41ccd
A
494 xpc_release(hintsArray);
495 xpc_release(contextArray);
496 xpc_release(immutableHintsArray);
497 xpc_release(requestObject);
5c19dc3a 498
d8f41ccd
A
499 return status;
500}
501
502void SecurityAgentXPCQuery::checkResult()
503{
504 // now check the OSStatus return from the server side
505 switch (mLastResult) {
506 case kAuthorizationResultAllow: return;
507 case kAuthorizationResultDeny:
508 case kAuthorizationResultUserCanceled: CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
509 default: MacOSError::throwMe(errAuthorizationInternal);
510 }
511}
512
513//
514// Perform the "rogue app" access query dialog
515//
516QueryKeychainUse::QueryKeychainUse(bool needPass, const Database *db)
517 : mPassphraseCheck(NULL)
518{
519 // if passphrase checking requested, save KeychainDatabase reference
520 // (will quietly disable check if db isn't a keychain)
521 if (needPass)
522 mPassphraseCheck = dynamic_cast<const KeychainDatabase *>(db);
5c19dc3a 523
d8f41ccd
A
524 setTerminateOnSleep(true);
525}
526
527Reason QueryKeychainUse::queryUser (const char *database, const char *description, AclAuthorization action)
528{
529 Reason reason = SecurityAgent::noReason;
530 int retryCount = 0;
531 OSStatus status;
532 AuthValueVector arguments;
533 AuthItemSet hints, context;
534
535#if defined(NOSA)
536 if (getenv("NOSA")) {
537 char answer[maxPassphraseLength+10];
5c19dc3a 538
d8f41ccd
A
539 string applicationPath;
540 AuthItem *applicationPathItem = mClientHints.find(AGENT_HINT_APPLICATION_PATH);
541 if (applicationPathItem)
542 applicationPathItem->getString(applicationPath);
543
544 getNoSA(answer, sizeof(answer), "Allow %s to do %d on %s in %s? [yn][g]%s ",
545 applicationPath.c_str(), int(action), (description ? description : "[NULL item]"),
546 (database ? database : "[NULL database]"),
547 mPassphraseCheck ? ":passphrase" : "");
548 // turn passphrase (no ':') into y:passphrase
549 if (mPassphraseCheck && !strchr(answer, ':')) {
550 memmove(answer+2, answer, strlen(answer)+1);
551 memcpy(answer, "y:", 2);
552 }
553
554 allow = answer[0] == 'y';
555 remember = answer[1] == 'g';
556 return SecurityAgent::noReason;
557 }
558#endif
559
560 // prepopulate with client hints
561 hints.insert(mClientHints.begin(), mClientHints.end());
5c19dc3a 562
d8f41ccd
A
563 // put action/operation (sint32) into hints
564 hints.insert(AuthItemRef(AGENT_HINT_ACL_TAG, AuthValueOverlay(sizeof(action), static_cast<sint32*>(&action))));
5c19dc3a 565
d8f41ccd
A
566 // item name into hints
567
568 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME, AuthValueOverlay(description ? (uint32_t)strlen(description) : 0, const_cast<char*>(description))));
5c19dc3a 569
d8f41ccd
A
570 // keychain name into hints
571 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(database ? (uint32_t)strlen(database) : 0, const_cast<char*>(database))));
5c19dc3a 572
d8f41ccd
A
573 if (mPassphraseCheck)
574 {
5c19dc3a
A
575 create("builtin", "confirm-access-password");
576
d8f41ccd
A
577 CssmAutoData data(Allocator::standard(Allocator::sensitive));
578
5c19dc3a 579 do
d8f41ccd
A
580 {
581
582 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
583 hints.erase(triesHint); hints.insert(triesHint); // replace
5c19dc3a 584
d8f41ccd
A
585 if (retryCount++ > kMaximumAuthorizationTries)
586 {
587 reason = SecurityAgent::tooManyTries;
588 }
5c19dc3a 589
d8f41ccd
A
590 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
591 hints.erase(retryHint); hints.insert(retryHint); // replace
592
593 setInput(hints, context);
594 status = invoke();
595
596 if (retryCount > kMaximumAuthorizationTries)
597 {
598 return reason;
599 }
5c19dc3a 600
d8f41ccd 601 checkResult();
5c19dc3a 602
d8f41ccd
A
603 AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
604 if (!passwordItem)
605 continue;
5c19dc3a 606
d8f41ccd 607 passwordItem->getCssmData(data);
5c19dc3a 608 }
d8f41ccd
A
609 while ((reason = (const_cast<KeychainDatabase*>(mPassphraseCheck)->decode(data) ? SecurityAgent::noReason : SecurityAgent::invalidPassphrase)));
610 }
611 else
612 {
5c19dc3a 613 create("builtin", "confirm-access");
d8f41ccd
A
614 setInput(hints, context);
615 invoke();
616 }
5c19dc3a 617
d8f41ccd 618 readChoice();
5c19dc3a 619
d8f41ccd
A
620 return reason;
621}
622
623//
624// Perform code signature ACL access adjustment dialogs
625//
626bool QueryCodeCheck::operator () (const char *aclPath)
627{
628 OSStatus status;
629 AuthValueVector arguments;
630 AuthItemSet hints, context;
5c19dc3a 631
d8f41ccd
A
632#if defined(NOSA)
633 if (getenv("NOSA")) {
634 char answer[10];
5c19dc3a 635
d8f41ccd
A
636 string applicationPath;
637 AuthItem *applicationPathItem = mClientHints.find(AGENT_HINT_APPLICATION_PATH);
638 if (applicationPathItem)
639 applicationPathItem->getString(applicationPath);
640
641 getNoSA(answer, sizeof(answer),
642 "Allow %s to match an ACL for %s [yn][g]? ",
643 applicationPath.c_str(), aclPath ? aclPath : "(unknown)");
644 allow = answer[0] == 'y';
645 remember = answer[1] == 'g';
646 return;
647 }
648#endif
649
650 // prepopulate with client hints
651 hints.insert(mClientHints.begin(), mClientHints.end());
5c19dc3a 652
d8f41ccd 653 hints.insert(AuthItemRef(AGENT_HINT_APPLICATION_PATH, AuthValueOverlay((uint32_t)strlen(aclPath), const_cast<char*>(aclPath))));
5c19dc3a
A
654
655 create("builtin", "code-identity");
d8f41ccd
A
656
657 setInput(hints, context);
658 status = invoke();
5c19dc3a 659
d8f41ccd
A
660 checkResult();
661
662// MacOSError::check(status);
663
664 return kAuthorizationResultAllow == mLastResult;
665}
666
667
668//
669// Obtain passphrases and submit them to the accept() method until it is accepted
670// or we can't get another passphrase. Accept() should consume the passphrase
671// if it is accepted. If no passphrase is acceptable, throw out of here.
672//
673Reason QueryOld::query()
674{
675 Reason reason = SecurityAgent::noReason;
676 OSStatus status;
677 AuthValueVector arguments;
678 AuthItemSet hints, context;
679 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
680 int retryCount = 0;
5c19dc3a 681
d8f41ccd
A
682#if defined(NOSA)
683 // return the passphrase
684 if (getenv("NOSA")) {
685 char passphrase_[maxPassphraseLength];
686 getNoSA(passphrase, maxPassphraseLength, "Unlock %s [<CR> to cancel]: ", database.dbName());
687 passphrase.copy(passphrase_, strlen(passphrase_));
688 return database.decode(passphrase) ? SecurityAgent::noReason : SecurityAgent::invalidPassphrase;
689 }
690#endif
5c19dc3a 691
d8f41ccd
A
692 // prepopulate with client hints
693
694 const char *keychainPath = database.dbName();
695 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay((uint32_t)strlen(keychainPath), const_cast<char*>(keychainPath))));
696
697 hints.insert(mClientHints.begin(), mClientHints.end());
5c19dc3a
A
698
699 create("builtin", "unlock-keychain");
d8f41ccd
A
700
701 do
702 {
703 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
704 hints.erase(triesHint); hints.insert(triesHint); // replace
5c19dc3a 705
d8f41ccd 706 ++retryCount;
5c19dc3a 707
d8f41ccd
A
708 if (retryCount > maxTries)
709 {
710 reason = SecurityAgent::tooManyTries;
711 }
5c19dc3a 712
d8f41ccd
A
713 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
714 hints.erase(retryHint); hints.insert(retryHint); // replace
715
716 setInput(hints, context);
717 status = invoke();
718
719 if (retryCount > maxTries)
720 {
721 return reason;
722 }
723
724 checkResult();
5c19dc3a 725
d8f41ccd
A
726 AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
727 if (!passwordItem)
728 continue;
5c19dc3a 729
d8f41ccd 730 passwordItem->getCssmData(passphrase);
5c19dc3a 731
d8f41ccd
A
732 }
733 while ((reason = accept(passphrase)));
734
735 return SecurityAgent::noReason;
736}
737
738
739//
740// Get existing passphrase (unlock) Query
741//
742Reason QueryOld::operator () ()
743{
744 return query();
745}
746
747
748//
749// End-classes for old secrets
750//
751Reason QueryUnlock::accept(CssmManagedData &passphrase)
752{
753 if (safer_cast<KeychainDatabase &>(database).decode(passphrase))
754 return SecurityAgent::noReason;
755 else
756 return SecurityAgent::invalidPassphrase;
757}
758
759Reason QueryUnlock::retrievePassword(CssmOwnedData &passphrase) {
760 CssmAutoData pass(Allocator::standard(Allocator::sensitive));
761
762 AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
763 if (!passwordItem)
764 return SecurityAgent::invalidPassphrase;
5c19dc3a 765
d8f41ccd 766 passwordItem->getCssmData(pass);
5c19dc3a 767
d8f41ccd
A
768 passphrase = pass;
769
770 return SecurityAgent::noReason;
771}
772
773QueryKeybagPassphrase::QueryKeybagPassphrase(Session & session, int32_t tries) : mSession(session), mContext(), mRetries(tries)
774{
775 setTerminateOnSleep(true);
776 mContext = mSession.get_current_service_context();
777}
778
779Reason QueryKeybagPassphrase::query()
780{
781 Reason reason = SecurityAgent::noReason;
782 OSStatus status;
783 AuthValueVector arguments;
784 AuthItemSet hints, context;
785 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
786 int retryCount = 0;
787
788 // prepopulate with client hints
789
790 const char *keychainPath = "iCloud";
791 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay((uint32_t)strlen(keychainPath), const_cast<char*>(keychainPath))));
792
793 hints.insert(mClientHints.begin(), mClientHints.end());
794
5c19dc3a 795 create("builtin", "unlock-keychain");
d8f41ccd 796
5c19dc3a 797 int currentTry = 0;
d8f41ccd
A
798 do
799 {
5c19dc3a 800 currentTry = retryCount;
d8f41ccd
A
801 if (retryCount > mRetries)
802 {
803 return SecurityAgent::tooManyTries;
804 }
5c19dc3a 805 retryCount++;
d8f41ccd 806
5c19dc3a 807 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(currentTry), &currentTry));
d8f41ccd
A
808 hints.erase(triesHint); hints.insert(triesHint); // replace
809
810 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
811 hints.erase(retryHint); hints.insert(retryHint); // replace
812
813 setInput(hints, context);
814 status = invoke();
815
816 checkResult();
817
818 AuthItem *passwordItem = mOutContext.find(kAuthorizationEnvironmentPassword);
819 if (!passwordItem)
820 continue;
821
822 passwordItem->getCssmData(passphrase);
d8f41ccd
A
823 }
824 while ((reason = accept(passphrase)));
825
826 return SecurityAgent::noReason;
827}
828
829Reason QueryKeybagPassphrase::accept(Security::CssmManagedData & password)
830{
831 if (service_client_kb_unlock(&mContext, password.data(), (int)password.length()) == 0) {
832 mSession.keybagSetState(session_keybag_unlocked);
833 return SecurityAgent::noReason;
834 } else
835 return SecurityAgent::invalidPassphrase;
836}
837
838QueryKeybagNewPassphrase::QueryKeybagNewPassphrase(Session & session) : QueryKeybagPassphrase(session) {}
839
840Reason QueryKeybagNewPassphrase::query(CssmOwnedData &oldPassphrase, CssmOwnedData &passphrase)
841{
842 CssmAutoData pass(Allocator::standard(Allocator::sensitive));
843 CssmAutoData oldPass(Allocator::standard(Allocator::sensitive));
844 Reason reason = SecurityAgent::noReason;
845 OSStatus status;
846 AuthValueVector arguments;
847 AuthItemSet hints, context;
848 int retryCount = 0;
849
850 // prepopulate with client hints
851
852 const char *keychainPath = "iCloud";
853 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay((uint32_t)strlen(keychainPath), const_cast<char*>(keychainPath))));
854
855 const char *showResetString = "YES";
856 hints.insert(AuthItemRef(AGENT_HINT_SHOW_RESET, AuthValueOverlay((uint32_t)strlen(showResetString), const_cast<char*>(showResetString))));
857
858 hints.insert(mClientHints.begin(), mClientHints.end());
859
5c19dc3a 860 create("builtin", "change-passphrase");
d8f41ccd 861
5c19dc3a 862 int currentTry = 0;
d8f41ccd
A
863 AuthItem *resetPassword = NULL;
864 do
865 {
5c19dc3a 866 currentTry = retryCount;
d8f41ccd
A
867 if (retryCount > mRetries)
868 {
869 return SecurityAgent::tooManyTries;
870 }
5c19dc3a 871 retryCount++;
d8f41ccd 872
5c19dc3a 873 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(currentTry), &currentTry));
d8f41ccd
A
874 hints.erase(triesHint); hints.insert(triesHint); // replace
875
876 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
877 hints.erase(retryHint); hints.insert(retryHint); // replace
878
879 setInput(hints, context);
880 status = invoke();
881
882 checkResult();
883
884 resetPassword = mOutContext.find(AGENT_CONTEXT_RESET_PASSWORD);
885 if (resetPassword != NULL) {
886 return SecurityAgent::resettingPassword;
887 }
5c19dc3a 888
d8f41ccd
A
889 AuthItem *oldPasswordItem = mOutContext.find(AGENT_PASSWORD);
890 if (!oldPasswordItem)
891 continue;
892
893 oldPasswordItem->getCssmData(oldPass);
d8f41ccd
A
894 }
895 while ((reason = accept(oldPass)));
896
897 if (reason == SecurityAgent::noReason) {
898 AuthItem *passwordItem = mOutContext.find(AGENT_CONTEXT_NEW_PASSWORD);
899 if (!passwordItem)
900 return SecurityAgent::invalidPassphrase;
901
902 passwordItem->getCssmData(pass);
903
904 oldPassphrase = oldPass;
905 passphrase = pass;
906 }
907
908 return SecurityAgent::noReason;
909}
910
911QueryPIN::QueryPIN(Database &db)
912 : QueryOld(db), mPin(Allocator::standard())
913{
914 this->inferHints(Server::process());
915}
916
917
918Reason QueryPIN::accept(CssmManagedData &pin)
919{
920 // no retries for now
921 mPin = pin;
922 return SecurityAgent::noReason;
923}
924
925
926//
927// Obtain passphrases and submit them to the accept() method until it is accepted
928// or we can't get another passphrase. Accept() should consume the passphrase
929// if it is accepted. If no passphrase is acceptable, throw out of here.
930//
931Reason QueryNewPassphrase::query()
932{
933 Reason reason = initialReason;
934 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
935 CssmAutoData oldPassphrase(Allocator::standard(Allocator::sensitive));
936
937 OSStatus status;
938 AuthValueVector arguments;
939 AuthItemSet hints, context;
5c19dc3a 940
d8f41ccd
A
941 int retryCount = 0;
942
943#if defined(NOSA)
944 if (getenv("NOSA")) {
945 char passphrase_[maxPassphraseLength];
946 getNoSA(passphrase_, maxPassphraseLength,
947 "New passphrase for %s (reason %d) [<CR> to cancel]: ",
948 database.dbName(), reason);
949 return SecurityAgent::noReason;
950 }
951#endif
952
953 // prepopulate with client hints
954 hints.insert(mClientHints.begin(), mClientHints.end());
955
956 // keychain name into hints
957 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(database.dbName())));
5c19dc3a 958
d8f41ccd
A
959 switch (initialReason)
960 {
5c19dc3a
A
961 case SecurityAgent::newDatabase:
962 create("builtin", "new-passphrase");
d8f41ccd
A
963 break;
964 case SecurityAgent::changePassphrase:
5c19dc3a 965 create("builtin", "change-passphrase");
d8f41ccd
A
966 break;
967 default:
968 assert(false);
969 }
970
971 do
972 {
973 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
974 hints.erase(triesHint); hints.insert(triesHint); // replace
975
976 if (++retryCount > maxTries)
977 {
978 reason = SecurityAgent::tooManyTries;
979 }
980
981 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
982 hints.erase(retryHint); hints.insert(retryHint); // replace
983
984 setInput(hints, context);
985 status = invoke();
986
987 if (retryCount > maxTries)
988 {
989 return reason;
990 }
5c19dc3a 991
d8f41ccd
A
992 checkResult();
993
994 if (SecurityAgent::changePassphrase == initialReason)
995 {
996 AuthItem *oldPasswordItem = mOutContext.find(AGENT_PASSWORD);
997 if (!oldPasswordItem)
998 continue;
5c19dc3a 999
d8f41ccd
A
1000 oldPasswordItem->getCssmData(oldPassphrase);
1001 }
5c19dc3a 1002
d8f41ccd
A
1003 AuthItem *passwordItem = mOutContext.find(AGENT_CONTEXT_NEW_PASSWORD);
1004 if (!passwordItem)
1005 continue;
5c19dc3a 1006
d8f41ccd
A
1007 passwordItem->getCssmData(passphrase);
1008
1009 }
1010 while ((reason = accept(passphrase, (initialReason == SecurityAgent::changePassphrase) ? &oldPassphrase.get() : NULL)));
5c19dc3a 1011
d8f41ccd
A
1012 return SecurityAgent::noReason;
1013}
1014
1015
1016//
1017// Get new passphrase Query
1018//
1019Reason QueryNewPassphrase::operator () (CssmOwnedData &oldPassphrase, CssmOwnedData &passphrase)
1020{
1021 if (Reason result = query())
1022 return result; // failed
1023 passphrase = mPassphrase;
1024 oldPassphrase = mOldPassphrase;
1025 return SecurityAgent::noReason; // success
1026}
1027
1028Reason QueryNewPassphrase::accept(CssmManagedData &passphrase, CssmData *oldPassphrase)
1029{
1030 //@@@ acceptance criteria are currently hardwired here
1031 //@@@ This validation presumes ASCII - UTF8 might be more lenient
5c19dc3a 1032
d8f41ccd
A
1033 // if we have an old passphrase, check it
1034 if (oldPassphrase && !safer_cast<KeychainDatabase&>(database).validatePassphrase(*oldPassphrase))
1035 return SecurityAgent::oldPassphraseWrong;
5c19dc3a 1036
d8f41ccd
A
1037 // sanity check the new passphrase (but allow user override)
1038 if (!(mPassphraseValid && passphrase.get() == mPassphrase)) {
1039 mPassphrase = passphrase;
1040 if (oldPassphrase) mOldPassphrase = *oldPassphrase;
1041 mPassphraseValid = true;
1042 if (mPassphrase.length() == 0)
1043 return SecurityAgent::passphraseIsNull;
1044 if (mPassphrase.length() < 6)
1045 return SecurityAgent::passphraseTooSimple;
1046 }
5c19dc3a 1047
d8f41ccd
A
1048 // accept this
1049 return SecurityAgent::noReason;
1050}
1051
5c19dc3a 1052//
d8f41ccd 1053// Get a passphrase for unspecified use
5c19dc3a 1054//
d8f41ccd
A
1055Reason QueryGenericPassphrase::operator () (const CssmData *prompt, bool verify,
1056 string &passphrase)
1057{
1058 return query(prompt, verify, passphrase);
1059}
1060
1061Reason QueryGenericPassphrase::query(const CssmData *prompt, bool verify,
1062 string &passphrase)
1063{
1064 Reason reason = SecurityAgent::noReason;
5c19dc3a 1065 OSStatus status; // not really used; remove?
d8f41ccd
A
1066 AuthValueVector arguments;
1067 AuthItemSet hints, context;
5c19dc3a 1068
d8f41ccd
A
1069#if defined(NOSA)
1070 if (getenv("NOSA")) {
1071 // FIXME 3690984
1072 return SecurityAgent::noReason;
1073 }
1074#endif
5c19dc3a 1075
d8f41ccd
A
1076 hints.insert(mClientHints.begin(), mClientHints.end());
1077 hints.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT, AuthValueOverlay(prompt ? (UInt32)prompt->length() : 0, prompt ? prompt->data() : NULL)));
1078 // XXX/gh defined by dmitch but no analogous hint in
1079 // AuthorizationTagsPriv.h:
1080 // CSSM_ATTRIBUTE_ALERT_TITLE (optional alert panel title)
5c19dc3a 1081
d8f41ccd 1082 if (false == verify) { // import
5c19dc3a 1083 create("builtin", "generic-unlock");
d8f41ccd 1084 } else { // verify passphrase (export)
5c19dc3a 1085 // new-passphrase-generic works with the pre-4 June 2004 agent;
d8f41ccd 1086 // generic-new-passphrase is required for the new agent
5c19dc3a 1087 create("builtin", "generic-new-passphrase");
d8f41ccd 1088 }
5c19dc3a 1089
d8f41ccd 1090 AuthItem *passwordItem;
5c19dc3a 1091
d8f41ccd
A
1092 do {
1093 setInput(hints, context);
1094 status = invoke();
1095 checkResult();
1096 passwordItem = mOutContext.find(AGENT_PASSWORD);
5c19dc3a 1097
d8f41ccd 1098 } while (!passwordItem);
5c19dc3a 1099
d8f41ccd 1100 passwordItem->getString(passphrase);
5c19dc3a 1101
d8f41ccd
A
1102 return reason;
1103}
1104
1105
5c19dc3a 1106//
d8f41ccd 1107// Get a DB blob's passphrase--keychain synchronization
5c19dc3a 1108//
d8f41ccd
A
1109Reason QueryDBBlobSecret::operator () (DbHandle *dbHandleArray, uint8 dbHandleArrayCount, DbHandle *dbHandleAuthenticated)
1110{
1111 return query(dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated);
1112}
1113
1114Reason QueryDBBlobSecret::query(DbHandle *dbHandleArray, uint8 dbHandleArrayCount, DbHandle *dbHandleAuthenticated)
1115{
1116 Reason reason = SecurityAgent::noReason;
1117 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
5c19dc3a 1118 OSStatus status; // not really used; remove?
d8f41ccd
A
1119 AuthValueVector arguments;
1120 AuthItemSet hints/*NUKEME*/, context;
5c19dc3a 1121
d8f41ccd
A
1122#if defined(NOSA)
1123 if (getenv("NOSA")) {
1124 // FIXME akin to 3690984
1125 return SecurityAgent::noReason;
1126 }
1127#endif
1128
1129 hints.insert(mClientHints.begin(), mClientHints.end());
5c19dc3a
A
1130 create("builtin", "generic-unlock-kcblob");
1131
d8f41ccd 1132 AuthItem *secretItem;
5c19dc3a 1133
d8f41ccd 1134 int retryCount = 0;
5c19dc3a 1135
d8f41ccd
A
1136 do {
1137 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
1138 hints.erase(triesHint); hints.insert(triesHint); // replace
1139
1140 if (++retryCount > maxTries)
1141 {
1142 reason = SecurityAgent::tooManyTries;
5c19dc3a
A
1143 }
1144
d8f41ccd
A
1145 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
1146 hints.erase(retryHint); hints.insert(retryHint); // replace
5c19dc3a 1147
d8f41ccd
A
1148 setInput(hints, context);
1149 status = invoke();
1150 checkResult();
1151 secretItem = mOutContext.find(AGENT_PASSWORD);
1152 if (!secretItem)
1153 continue;
1154 secretItem->getCssmData(passphrase);
5c19dc3a 1155
d8f41ccd 1156 } while ((reason = accept(passphrase, dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated)));
5c19dc3a 1157
d8f41ccd
A
1158 return reason;
1159}
1160
5c19dc3a 1161Reason QueryDBBlobSecret::accept(CssmManagedData &passphrase,
d8f41ccd
A
1162 DbHandle *dbHandlesToAuthenticate, uint8 dbHandleCount, DbHandle *dbHandleAuthenticated)
1163{
1164 DbHandle *currHdl = dbHandlesToAuthenticate;
1165 short index;
1166 Boolean authenticated = false;
1167 for (index=0; index < dbHandleCount && !authenticated; index++)
1168 {
5c19dc3a 1169 try
d8f41ccd
A
1170 {
1171 RefPointer<KeychainDatabase> dbToUnlock = Server::keychain(*currHdl);
1172 dbToUnlock->unlockDb(passphrase);
1173 authenticated = true;
1174 *dbHandleAuthenticated = *currHdl; // return the DbHandle that 'passphrase' authenticated with.
5c19dc3a
A
1175 }
1176 catch (const CommonError &err)
d8f41ccd 1177 {
5c19dc3a 1178 currHdl++; // we failed to authenticate with this one, onto the next one.
d8f41ccd
A
1179 }
1180 }
1181 if ( !authenticated )
1182 return SecurityAgent::invalidPassphrase;
d8f41ccd 1183
5c19dc3a 1184 return SecurityAgent::noReason;
d8f41ccd
A
1185}
1186
5c19dc3a 1187// @@@ no pluggable authentication possible!
d8f41ccd
A
1188Reason
1189QueryKeychainAuth::operator () (const char *database, const char *description, AclAuthorization action, const char *prompt)
1190{
1191 Reason reason = SecurityAgent::noReason;
1192 AuthItemSet hints, context;
1193 AuthValueVector arguments;
1194 int retryCount = 0;
1195 string username;
1196 string password;
5c19dc3a 1197
d8f41ccd
A
1198 using CommonCriteria::Securityd::KeychainAuthLogger;
1199 KeychainAuthLogger logger(mAuditToken, AUE_ssauthint, database, description);
5c19dc3a 1200
d8f41ccd
A
1201#if defined(NOSA)
1202 /* XXX/gh probably not complete; stolen verbatim from rogue-app query */
1203 if (getenv("NOSA")) {
1204 char answer[maxPassphraseLength+10];
5c19dc3a 1205
d8f41ccd
A
1206 string applicationPath;
1207 AuthItem *applicationPathItem = mClientHints.find(AGENT_HINT_APPLICATION_PATH);
1208 if (applicationPathItem)
1209 applicationPathItem->getString(applicationPath);
1210
1211 getNoSA(answer, sizeof(answer), "Allow %s to do %d on %s in %s? [yn][g]%s ",
1212 applicationPath.c_str(), int(action), (description ? description : "[NULL item]"),
1213 (database ? database : "[NULL database]"),
1214 mPassphraseCheck ? ":passphrase" : "");
1215 // turn passphrase (no ':') into y:passphrase
1216 if (mPassphraseCheck && !strchr(answer, ':')) {
1217 memmove(answer+2, answer, strlen(answer)+1);
1218 memcpy(answer, "y:", 2);
1219 }
1220
1221 allow = answer[0] == 'y';
1222 remember = answer[1] == 'g';
1223 return SecurityAgent::noReason;
1224 }
1225#endif
5c19dc3a 1226
d8f41ccd
A
1227 hints.insert(mClientHints.begin(), mClientHints.end());
1228
1229 // put action/operation (sint32) into hints
1230 hints.insert(AuthItemRef(AGENT_HINT_ACL_TAG, AuthValueOverlay(sizeof(action), static_cast<sint32*>(&action))));
1231
1232 hints.insert(AuthItemRef(AGENT_HINT_CUSTOM_PROMPT, AuthValueOverlay(prompt ? (uint32_t)strlen(prompt) : 0, const_cast<char*>(prompt))));
5c19dc3a 1233
d8f41ccd
A
1234 // item name into hints
1235 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_ITEM_NAME, AuthValueOverlay(description ? (uint32_t)strlen(description) : 0, const_cast<char*>(description))));
5c19dc3a 1236
d8f41ccd
A
1237 // keychain name into hints
1238 hints.insert(AuthItemRef(AGENT_HINT_KEYCHAIN_PATH, AuthValueOverlay(database ? (uint32_t)strlen(database) : 0, const_cast<char*>(database))));
5c19dc3a
A
1239
1240 create("builtin", "confirm-access-user-password");
1241
d8f41ccd
A
1242 AuthItem *usernameItem;
1243 AuthItem *passwordItem;
5c19dc3a 1244
d8f41ccd
A
1245 do {
1246
1247 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(retryCount), &retryCount));
1248 hints.erase(triesHint); hints.insert(triesHint); // replace
5c19dc3a 1249
d8f41ccd
A
1250 if (++retryCount > maxTries)
1251 reason = SecurityAgent::tooManyTries;
5c19dc3a 1252
d8f41ccd
A
1253 if (SecurityAgent::noReason != reason)
1254 {
1255 if (SecurityAgent::tooManyTries == reason)
1256 logger.logFailure(NULL, CommonCriteria::errTooManyTries);
1257 else
1258 logger.logFailure();
1259 }
1260
1261 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
1262 hints.erase(retryHint); hints.insert(retryHint); // replace
5c19dc3a 1263
d8f41ccd
A
1264 setInput(hints, context);
1265 try
1266 {
1267 invoke();
1268 checkResult();
1269 }
1270 catch (...) // user probably clicked "deny"
1271 {
1272 logger.logFailure();
1273 throw;
1274 }
1275 usernameItem = mOutContext.find(AGENT_USERNAME);
1276 passwordItem = mOutContext.find(AGENT_PASSWORD);
1277 if (!usernameItem || !passwordItem)
1278 continue;
1279 usernameItem->getString(username);
1280 passwordItem->getString(password);
1281 } while ((reason = accept(username, password)));
1282
1283 if (SecurityAgent::noReason == reason)
1284 logger.logSuccess();
1285 // else we logged the denial in the loop
5c19dc3a 1286
d8f41ccd
A
1287 return reason;
1288}
1289
5c19dc3a 1290Reason
d8f41ccd
A
1291QueryKeychainAuth::accept(string &username, string &passphrase)
1292{
5dd5f9ec
A
1293 // Note: QueryKeychainAuth currently requires that the
1294 // specified user be in the admin group. If this requirement
1295 // ever needs to change, the group name should be passed as
1296 // a separate argument to this method.
1297
1298 const char *user = username.c_str();
1299 const char *passwd = passphrase.c_str();
1300 int checkpw_status = checkpw(user, passwd);
1301
1302 if (checkpw_status != CHECKPW_SUCCESS) {
d8f41ccd 1303 return SecurityAgent::invalidPassphrase;
5dd5f9ec
A
1304 }
1305
1306 const char *group = "admin";
1307 if (group) {
1308 int rc, ismember;
1309 uuid_t group_uuid, user_uuid;
1310 rc = mbr_group_name_to_uuid(group, group_uuid);
1311 if (rc) { return SecurityAgent::userNotInGroup; }
1312
1313 rc = mbr_user_name_to_uuid(user, user_uuid);
1314 if (rc) { return SecurityAgent::userNotInGroup; }
1315
1316 rc = mbr_check_membership(user_uuid, group_uuid, &ismember);
1317 if (rc || !ismember) { return SecurityAgent::userNotInGroup; }
1318 }
d8f41ccd
A
1319
1320 return SecurityAgent::noReason;
1321}
1322