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