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 89static 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 119SecurityAgentConnection::SecurityAgentConnection(const AuthHostType type, Session &session) 120: mAuthHostType(type), 121mHostInstance(session.authhost(mAuthHostType)), 122mConnection(&Server::connection()), 123mAuditToken(Server::connection().auditToken()) 124{ 125 // this may take a while 126 Server::active().longTermActivity(); 127 secdebug("SecurityAgentConnection", "new SecurityAgentConnection(%p)", this); 128} 129 130SecurityAgentConnection::~SecurityAgentConnection() 131{ 132 secdebug("SecurityAgentConnection", "SecurityAgentConnection(%p) dying", this); 133 mConnection->useAgent(NULL); 134} 135 136void 137SecurityAgentConnection::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 218void 219SecurityAgentConnection::reconnect() 220{ 221 // if !mHostInstance throw()? 222 if (mHostInstance) 223 { 224 activate(); 225 } 226} 227 228void 229SecurityAgentConnection::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 240SecurityAgentXPCConnection::SecurityAgentXPCConnection(const AuthHostType type, Session &session) 241: mAuthHostType(type), 242mHostInstance(session.authhost(mAuthHostType)), 243mSession(session), 244mConnection(&Server::connection()), 245mAuditToken(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 259SecurityAgentXPCConnection::~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 282bool SecurityAgentXPCConnection::inDarkWake() 283{ 284 return mSession.server().inDarkWake(); 285} 286 287void 288SecurityAgentXPCConnection::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 358void 359SecurityAgentXPCConnection::reconnect() 360{ 361} 362 363void 364SecurityAgentXPCConnection::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 373using SecurityAgent::Reason; 374using namespace Authorization; 375 376SecurityAgentQuery::SecurityAgentQuery(const AuthHostType type, Session &session) 377: SecurityAgentConnection(type, session) 378{ 379 secdebug("SecurityAgentQuery", "new SecurityAgentQuery(%p)", this); 380} 381 382SecurityAgentQuery::~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 397void 398SecurityAgentQuery::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 411void 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 418void 419SecurityAgentQuery::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 443void 444SecurityAgentQuery::disconnect() 445{ 446 SecurityAgent::Client::destroy(); 447} 448 449void 450SecurityAgentQuery::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 457void 458SecurityAgentQuery::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 471ModuleNexus<RecursiveMutex> gAllXPCClientsMutex; 472ModuleNexus<set<SecurityAgentXPCQuery*> > allXPCClients; 473 474void 475SecurityAgentXPCQuery::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 492SecurityAgentXPCQuery::SecurityAgentXPCQuery(const AuthHostType type, Session &session) 493: SecurityAgentXPCConnection(type, session), mAgentConnected(false), mTerminateOnSleep(false) 494{ 495 secdebug("SecurityAgentXPCQuery", "new SecurityAgentXPCQuery(%p)", this); 496} 497 498SecurityAgentXPCQuery::~SecurityAgentXPCQuery() 499{ 500 secdebug("SecurityAgentXPCQuery", "SecurityAgentXPCQuery(%p) dying", this); 501 if (mAgentConnected) { 502 this->disconnect(); 503 } 504} 505 506void 507SecurityAgentXPCQuery::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 534void 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 541void 542SecurityAgentXPCQuery::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 566void 567SecurityAgentXPCQuery::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 580void 581SecurityAgentXPCQuery::terminate() 582{ 583 this->disconnect(); 584} 585 586static 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 606void 607SecurityAgentXPCQuery::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 679static 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 697OSStatus 698SecurityAgentXPCQuery::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 741void 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// 755QueryKeychainUse::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 766Reason 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// 865bool 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// 912Reason 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// 981Reason QueryOld::operator () () 982{ 983 return query(); 984} 985 986 987// 988// End-classes for old secrets 989// 990Reason 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 998Reason 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 1012QueryKeybagPassphrase::QueryKeybagPassphrase(Session & session, int32_t tries) : mSession(session), mContext(), mRetries(tries) 1013{ 1014 setTerminateOnSleep(true); 1015 mContext = mSession.get_current_service_context(); 1016} 1017 1018Reason 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 1067Reason 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 1076QueryKeybagNewPassphrase::QueryKeybagNewPassphrase(Session & session) : QueryKeybagPassphrase(session) {} 1077 1078Reason 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 1148QueryPIN::QueryPIN(Database &db) 1149 : QueryOld(db), mPin(Allocator::standard()) 1150{ 1151 this->inferHints(Server::process()); 1152} 1153 1154 1155Reason 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// 1168Reason 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// 1256Reason 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 1265Reason 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// 1292Reason QueryGenericPassphrase::operator () (const CssmData *prompt, bool verify, 1293 string &passphrase) 1294{ 1295 return query(prompt, verify, passphrase); 1296} 1297 1298Reason 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// 1346Reason QueryDBBlobSecret::operator () (DbHandle *dbHandleArray, uint8 dbHandleArrayCount, DbHandle *dbHandleAuthenticated) 1347{ 1348 return query(dbHandleArray, dbHandleArrayCount, dbHandleAuthenticated); 1349} 1350 1351Reason 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 1398Reason 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 1424QueryInvokeMechanism::QueryInvokeMechanism(const AuthHostType type, Session &session) : 1425 SecurityAgentQuery(type, session) { } 1426 1427void 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 1437void 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 1455void QueryInvokeMechanism::terminateAgent() 1456{ 1457 terminate(); 1458} 1459 1460// @@@ no pluggable authentication possible! 1461Reason 1462QueryKeychainAuth::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 1563Reason 1564QueryKeychainAuth::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 1576