1/* Copyright (c) 2012 Apple Inc. All rights reserved. */ 2 3#include "server.h" 4#include "session.h" 5#include "process.h" 6#include "authtoken.h" 7#include "authdb.h" 8#include "rule.h" 9#include "authutilities.h" 10#include "crc.h" 11#include "mechanism.h" 12#include "agent.h" 13#include "authitems.h" 14#include "debugging.h" 15#include "engine.h" 16#include "connection.h" 17 18#include <bsm/libbsm.h> 19#include <Security/Authorization.h> 20#include <Security/AuthorizationPriv.h> 21#include <Security/AuthorizationTagsPriv.h> 22#include <xpc/private.h> 23#include <dispatch/dispatch.h> 24#include <CoreFoundation/CoreFoundation.h> 25#include <CoreFoundation/CFXPCBridge.h> 26#include <IOKit/IOMessage.h> 27#include <IOKit/pwr_mgt/IOPMLib.h> 28#include <IOKit/pwr_mgt/IOPMLibPrivate.h> 29 30#define MAX_PROCESS_RIGHTS 30 31 32static CFMutableDictionaryRef gProcessMap = NULL; 33static CFMutableDictionaryRef gSessionMap = NULL; 34static CFMutableDictionaryRef gAuthTokenMap = NULL; 35static authdb_t gDatabase = NULL; 36 37static dispatch_queue_t power_queue; 38static bool gInDarkWake = false; 39static IOPMConnection gIOPMconn = NULL; 40static bool gXPCTransaction = false; 41 42static dispatch_queue_t 43get_server_dispatch_queue() 44{ 45 static dispatch_once_t onceToken; 46 static dispatch_queue_t server_queue = NULL; 47 48 dispatch_once(&onceToken, ^{ 49 server_queue = dispatch_queue_create("com.apple.security.auth.server", DISPATCH_QUEUE_SERIAL); 50 check(server_queue != NULL); 51 }); 52 53 return server_queue; 54} 55 56static Boolean _processEqualCallBack(const void *value1, const void *value2) 57{ 58 audit_info_s * info1 = (audit_info_s*)value1; 59 audit_info_s * info2 = (audit_info_s*)value2; 60 if (info1->pid == info2->pid) { 61 if (info1->tid == info2->tid) { 62 return true; 63 } 64 } 65 return false; 66} 67 68static CFHashCode _processHashCallBack(const void *value) 69{ 70 audit_info_s * info = (audit_info_s*)value; 71 uint64_t crc = crc64_init(); 72 crc = crc64_update(crc, &info->pid, sizeof(info->pid)); 73 crc = crc64_update(crc, &info->tid, sizeof(info->tid)); 74 crc = crc64_final(crc); 75 return crc; 76} 77 78static const CFDictionaryKeyCallBacks kProcessMapKeyCallBacks = { 79 .version = 0, 80 .retain = NULL, 81 .release = NULL, 82 .copyDescription = NULL, 83 .equal = &_processEqualCallBack, 84 .hash = &_processHashCallBack 85}; 86 87static Boolean _sessionEqualCallBack(const void *value1, const void *value2) 88{ 89 return (*(session_id_t*)value1) == (*(session_id_t*)value2); 90} 91 92static CFHashCode _sessionHashCallBack(const void *value) 93{ 94 return (CFHashCode)(*(session_id_t*)(value)); 95} 96 97static const CFDictionaryKeyCallBacks kSessionMapKeyCallBacks = { 98 .version = 0, 99 .retain = NULL, 100 .release = NULL, 101 .copyDescription = NULL, 102 .equal = &_sessionEqualCallBack, 103 .hash = &_sessionHashCallBack 104}; 105 106void server_cleanup() 107{ 108 CFRelease(gProcessMap); 109 CFRelease(gSessionMap); 110 CFRelease(gAuthTokenMap); 111 112 IOPMConnectionSetDispatchQueue(gIOPMconn, NULL); 113 IOPMConnectionRelease(gIOPMconn); 114 115 dispatch_queue_t queue = get_server_dispatch_queue(); 116 if (queue) { 117 dispatch_release(queue); 118 } 119 dispatch_release(power_queue); 120} 121 122static void _IOMPCallBack(void * param AUTH_UNUSED, IOPMConnection connection, IOPMConnectionMessageToken token, IOPMSystemPowerStateCapabilities capabilities) 123{ 124 LOGV("server: IOMP powerstates %i", capabilities); 125 if (capabilities & kIOPMSystemPowerStateCapabilityDisk) 126 LOGV("server: disk"); 127 if (capabilities & kIOPMSystemPowerStateCapabilityNetwork) 128 LOGV("server: net"); 129 if (capabilities & kIOPMSystemPowerStateCapabilityAudio) 130 LOGV("server: audio"); 131 if (capabilities & kIOPMSystemPowerStateCapabilityVideo) 132 LOGV("server: video"); 133 134 /* if cpu and no display -> in DarkWake */ 135 LOGD("server: DarkWake check current=%i==%i", (capabilities & (kIOPMSystemPowerStateCapabilityCPU|kIOPMSystemPowerStateCapabilityVideo)), kIOPMSystemPowerStateCapabilityCPU); 136 if ((capabilities & (kIOPMSystemPowerStateCapabilityCPU|kIOPMSystemPowerStateCapabilityVideo)) == kIOPMSystemPowerStateCapabilityCPU) { 137 LOGV("server: enter DarkWake"); 138 gInDarkWake = true; 139 } else if (gInDarkWake) { 140 LOGV("server: exit DarkWake"); 141 gInDarkWake = false; 142 } 143 144 (void)IOPMConnectionAcknowledgeEvent(connection, token); 145 146 return; 147} 148 149static void 150_setupDarkWake(void *ctx) 151{ 152 IOReturn ret; 153 154 IOPMConnectionCreate(CFSTR("IOPowerWatcher"), 155 kIOPMSystemPowerStateCapabilityDisk 156 | kIOPMSystemPowerStateCapabilityNetwork 157 | kIOPMSystemPowerStateCapabilityAudio 158 | kIOPMSystemPowerStateCapabilityVideo, 159 &gIOPMconn); 160 161 ret = IOPMConnectionSetNotification(gIOPMconn, NULL, _IOMPCallBack); 162 if (ret != kIOReturnSuccess) 163 return; 164 165 IOPMConnectionSetDispatchQueue(gIOPMconn, power_queue); 166 167 IOPMScheduleUserActiveChangedNotification(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(bool active) { 168 if (active) { 169 gInDarkWake = false; 170 } 171 }); 172} 173 174bool server_in_dark_wake() 175{ 176 return gInDarkWake; 177} 178 179authdb_t server_get_database() 180{ 181 return gDatabase; 182} 183 184static void _setupAuditSessionMonitor() 185{ 186 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 187 au_sdev_handle_t *dev = au_sdev_open(AU_SDEVF_ALLSESSIONS); 188 int event; 189 auditinfo_addr_t aia; 190 191 if (NULL == dev) { 192 LOGE("server: could not open %s %d", AUDIT_SDEV_PATH, errno); 193 return; 194 } 195 196 for (;;) { 197 if (0 != au_sdev_read_aia(dev, &event, &aia)) { 198 LOGE("server: au_sdev_read_aia failed: %d", errno); 199 continue; 200 } 201 LOGD("server: au_sdev_handle_t event=%i, session=%i", event, aia.ai_asid); 202 if (event == AUE_SESSION_CLOSE) { 203 dispatch_async(get_server_dispatch_queue(), ^{ 204 LOGV("server: session %i destroyed", aia.ai_asid); 205 CFDictionaryRemoveValue(gSessionMap, &aia.ai_asid); 206 }); 207 } 208 } 209 210 }); 211} 212 213static void _setupSignalHandlers() 214{ 215 signal(SIGTERM, SIG_IGN); 216 static dispatch_source_t sigtermHandler; 217 sigtermHandler = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGTERM, 0, get_server_dispatch_queue()); 218 if (sigtermHandler) { 219 dispatch_source_set_event_handler(sigtermHandler, ^{ 220 221 // should we clean up any state? 222 exit(EXIT_SUCCESS); 223 }); 224 dispatch_resume(sigtermHandler); 225 } 226} 227 228OSStatus server_init(void) 229{ 230 OSStatus status = errAuthorizationSuccess; 231 232 auditinfo_addr_t info; 233 memset(&info, 0, sizeof(info)); 234 getaudit_addr(&info, sizeof(info)); 235 LOGV("server: uid=%i, sid=%i", info.ai_auid, info.ai_asid); 236 237 require_action(get_server_dispatch_queue() != NULL, done, status = errAuthorizationInternal); 238 239 gProcessMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kProcessMapKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 240 require_action(gProcessMap != NULL, done, status = errAuthorizationInternal); 241 242 gSessionMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kSessionMapKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 243 require_action(gSessionMap != NULL, done, status = errAuthorizationInternal); 244 245 gAuthTokenMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kAuthTokenKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 246 require_action(gAuthTokenMap != NULL, done, status = errAuthorizationInternal); 247 248 gDatabase = authdb_create(); 249 require_action(gDatabase != NULL, done, status = errAuthorizationInternal); 250 251 // check to see if we have an updates 252 authdb_connection_t dbconn = authdb_connection_acquire(gDatabase); 253 authdb_maintenance(dbconn); 254 authdb_connection_release(&dbconn); 255 256 power_queue = dispatch_queue_create("com.apple.security.auth.power", DISPATCH_QUEUE_SERIAL); 257 check(power_queue != NULL); 258 dispatch_async_f(power_queue, NULL, _setupDarkWake); 259 260 _setupAuditSessionMonitor(); 261 _setupSignalHandlers(); 262 263done: 264 return status; 265} 266 267static void _server_parse_audit_token(audit_token_t * token, audit_info_s * info) 268{ 269 if (token && info) { 270 memset(info, 0, sizeof(*info)); 271 au_tid_t tid; 272 memset(&tid, 0, sizeof(tid)); 273 audit_token_to_au32(*token, &info->auid, &info->euid, 274 &info->egid, &info->ruid, &info->rgid, 275 &info->pid, &info->asid, &tid); 276 info->tid = tid.port; 277 info->opaqueToken = *token; 278 } 279} 280 281connection_t 282server_register_connection(xpc_connection_t connection) 283{ 284 __block connection_t conn = NULL; 285 __block session_t session = NULL; 286 __block process_t proc = NULL; 287 __block CFIndex conn_count = 0; 288 289 require(connection != NULL, done); 290 291 audit_token_t auditToken; 292 audit_info_s info; 293 xpc_connection_get_audit_token(connection, &auditToken); 294 _server_parse_audit_token(&auditToken, &info); 295 296 297 dispatch_sync(get_server_dispatch_queue(), ^{ 298 session = (session_t)CFDictionaryGetValue(gSessionMap, &info.asid); 299 if (session) { 300 CFRetain(session); 301 } else { 302 session = session_create(info.asid); 303 CFDictionarySetValue(gSessionMap, session_get_key(session), session); 304 } 305 306 proc = (process_t)CFDictionaryGetValue(gProcessMap, &info); 307 if (proc) { 308 CFRetain(proc); 309 } 310 311 if (proc) { 312 conn = connection_create(proc); 313 conn_count = process_add_connection(proc, conn); 314 } else { 315 proc = process_create(&info, session); 316 if (proc) { 317 conn = connection_create(proc); 318 conn_count = process_add_connection(proc, conn); 319 session_add_process(session, proc); 320 CFDictionarySetValue(gProcessMap, process_get_key(proc), proc); 321 } 322 } 323 324 if (!gXPCTransaction) { 325 xpc_transaction_begin(); 326 gXPCTransaction = true; 327 } 328 }); 329 330 LOGV("server[%i]: registered connection (total=%li)", info.pid, conn_count); 331 332done: 333 CFReleaseSafe(session); 334 CFReleaseSafe(proc); 335 return conn; 336} 337 338void 339server_unregister_connection(connection_t conn) 340{ 341 if (conn != NULL) { 342 process_t proc = connection_get_process(conn); 343 344 dispatch_sync(get_server_dispatch_queue(), ^{ 345 CFIndex connectionCount = process_get_connection_count(proc); 346 LOGV("server[%i]: unregistered connection (total=%li)", process_get_pid(proc), connectionCount); 347 348 if (connectionCount == 1) { 349 CFDictionaryRemoveValue(gProcessMap, process_get_key(proc)); 350 } 351 352 if (CFDictionaryGetCount(gProcessMap) == 0) { 353 xpc_transaction_end(); 354 gXPCTransaction = false; 355 } 356 }); 357 // move the destruction of the connection/process off the server queue 358 CFRelease(conn); 359 } 360} 361 362void 363server_register_auth_token(auth_token_t auth) 364{ 365 if (auth != NULL) { 366 dispatch_sync(get_server_dispatch_queue(), ^{ 367 LOGV("server: registering auth %p", auth); 368 CFDictionarySetValue(gAuthTokenMap, auth_token_get_key(auth), auth); 369 auth_token_set_state(auth, auth_token_state_registered); 370 }); 371 } 372} 373 374void 375server_unregister_auth_token(auth_token_t auth) 376{ 377 if (auth != NULL) { 378 AuthorizationBlob blob = *(AuthorizationBlob*)auth_token_get_key(auth); 379 dispatch_async(get_server_dispatch_queue(), ^{ 380 LOGV("server: unregistering auth %p", auth); 381 CFDictionaryRemoveValue(gAuthTokenMap, &blob); 382 }); 383 } 384} 385 386auth_token_t 387server_find_copy_auth_token(AuthorizationBlob * blob) 388{ 389 __block auth_token_t auth = NULL; 390 if (blob != NULL) { 391 dispatch_sync(get_server_dispatch_queue(), ^{ 392 auth = (auth_token_t)CFDictionaryGetValue(gAuthTokenMap, blob); 393 if (auth) { 394 CFRetain(auth); 395 } 396 }); 397 } 398 return auth; 399} 400 401session_t 402server_find_copy_session(session_id_t sid, bool create) 403{ 404 __block session_t session = NULL; 405 406 dispatch_sync(get_server_dispatch_queue(), ^{ 407 session = (session_t)CFDictionaryGetValue(gSessionMap, &sid); 408 if (session) { 409 CFRetain(session); 410 } else if (create) { 411 session = session_create(sid); 412 if (session) { 413 CFDictionarySetValue(gSessionMap, session_get_key(session), session); 414 } 415 } 416 }); 417 418 return session; 419} 420 421#pragma mark - 422#pragma mark API 423 424static OSStatus 425_process_find_copy_auth_token_from_xpc(process_t proc, xpc_object_t message, auth_token_t * auth_out) 426{ 427 OSStatus status = errAuthorizationSuccess; 428 require_action(auth_out != NULL, done, status = errAuthorizationInternal); 429 430 size_t len; 431 AuthorizationBlob * blob = (AuthorizationBlob *)xpc_dictionary_get_data(message, AUTH_XPC_BLOB, &len); 432 require_action(blob != NULL, done, status = errAuthorizationInvalidRef); 433 require_action(len == sizeof(AuthorizationBlob), done, status = errAuthorizationInvalidRef); 434 435 auth_token_t auth = process_find_copy_auth_token(proc, blob); 436 require_action(auth != NULL, done, status = errAuthorizationInvalidRef); 437 438#if DEBUG 439 LOGV("server[%i]: authtoken lookup %#x%x %p", process_get_pid(proc), blob->data[1],blob->data[0], auth); 440#else 441 LOGV("server[%i]: authtoken lookup %p", process_get_pid(proc), auth); 442#endif 443 444 *auth_out = auth; 445 446done: 447 return status; 448} 449 450static OSStatus _server_authorize(connection_t conn, auth_token_t auth, AuthorizationFlags flags, auth_rights_t rights, auth_items_t enviroment, engine_t * engine_out) 451{ 452 __block OSStatus status = errAuthorizationDenied; 453 engine_t engine = NULL; 454 455 require_action(conn, done, status = errAuthorizationInternal); 456 457 engine = engine_create(conn, auth); 458 require_action(engine, done, status = errAuthorizationInternal); 459 460 if (flags & kAuthorizationFlagInteractionAllowed) { 461 dispatch_sync(connection_get_dispatch_queue(conn), ^{ 462 connection_set_engine(conn, engine); 463 status = engine_authorize(engine, rights, enviroment, flags); 464 connection_set_engine(conn, NULL); 465 }); 466 } else { 467 status = engine_authorize(engine, rights, enviroment, flags); 468 } 469 470done: 471 if (engine) { 472 if (engine_out) { 473 *engine_out = engine; 474 } else { 475 CFRelease(engine); 476 } 477 } 478 return status; 479} 480 481// IN: AUTH_XPC_RIGHTS, AUTH_XPC_ENVIROMENT, AUTH_XPC_FLAGS 482// OUT: AUTH_XPC_BLOB 483OSStatus 484authorization_create(connection_t conn, xpc_object_t message, xpc_object_t reply) 485{ 486 OSStatus status = errAuthorizationDenied; 487 488 process_t proc = connection_get_process(conn); 489 490 // Passed in args 491 auth_rights_t rights = auth_rights_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_RIGHTS)); 492 auth_items_t enviroment = auth_items_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_ENVIROMENT)); 493 AuthorizationFlags flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS); 494 495 // Create Authorization Token 496 auth_token_t auth = auth_token_create(proc, flags & kAuthorizationFlagLeastPrivileged); 497 require_action(auth != NULL, done, status = errAuthorizationInternal); 498 499 if (!(flags & kAuthorizationFlagNoData)) { 500 process_add_auth_token(proc,auth); 501 } 502 503 status = _server_authorize(conn, auth, flags, rights, enviroment, NULL); 504 require_noerr(status, done); 505 506 //reply 507 xpc_dictionary_set_data(reply, AUTH_XPC_BLOB, auth_token_get_blob(auth), sizeof(AuthorizationBlob)); 508 509done: 510 CFReleaseSafe(rights); 511 CFReleaseSafe(enviroment); 512 CFReleaseSafe(auth); 513 return status; 514} 515 516// IN: AUTH_XPC_DATA, AUTH_XPC_ENVIROMENT, AUTH_XPC_FLAGS 517// OUT: AUTH_XPC_BLOB 518OSStatus authorization_create_with_audit_token(connection_t conn, xpc_object_t message, xpc_object_t reply) 519{ 520 OSStatus status = errAuthorizationDenied; 521 auth_token_t auth = NULL; 522 523 process_t proc = connection_get_process(conn); 524 require(process_get_uid(proc) == 0, done); //only root can use this call 525 526 // Passed in args 527 size_t len = 0; 528 const char * data = xpc_dictionary_get_data(message, AUTH_XPC_DATA, &len); 529 require(data != NULL, done); 530 require(len == sizeof(audit_token_t), done); 531 532// auth_items_t enviroment = auth_items_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_ENVIROMENT)); 533 AuthorizationFlags flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS); 534 535 audit_info_s auditInfo; 536 _server_parse_audit_token((audit_token_t*)data, &auditInfo); 537 538 // Create Authorization Token 539 auth = auth_token_create(proc, flags & kAuthorizationFlagLeastPrivileged); 540 require_action(auth != NULL, done, status = errAuthorizationInternal); 541 542 process_add_auth_token(proc,auth); 543 544 //reply 545 xpc_dictionary_set_data(reply, AUTH_XPC_BLOB, auth_token_get_blob(auth), sizeof(AuthorizationBlob)); 546 547done: 548// CFReleaseSafe(enviroment); 549 CFReleaseSafe(auth); 550 return status; 551} 552 553// IN: AUTH_XPC_BLOB, AUTH_XPC_FLAGS 554// OUT: 555OSStatus 556authorization_free(connection_t conn, xpc_object_t message, xpc_object_t reply AUTH_UNUSED) 557{ 558 OSStatus status = errAuthorizationSuccess; 559 AuthorizationFlags flags = 0; 560 process_t proc = connection_get_process(conn); 561 562 auth_token_t auth = NULL; 563 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth); 564 require_noerr(status, done); 565 566 flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS); 567 568 if (flags & kAuthorizationFlagDestroyRights) { 569 auth_token_credentials_iterate(auth, ^bool(credential_t cred) { 570 credential_invalidate(cred); 571 LOGV("engine[%i]: invalidating %scredential %s (%i) from authorization (%p)", connection_get_pid(conn), credential_get_shared(cred) ? "shared " : "", credential_get_name(cred), credential_get_uid(cred), auth); 572 return true; 573 }); 574 575 session_credentials_purge(auth_token_get_session(auth)); 576 } 577 578 process_remove_auth_token(proc, auth, flags); 579 580done: 581 CFReleaseSafe(auth); 582 LOGV("server[%i]: AuthorizationFree %i (flags:%x)", connection_get_pid(conn), status, flags); 583 return status; 584} 585 586// IN: AUTH_XPC_BLOB, AUTH_XPC_RIGHTS, AUTH_XPC_ENVIROMENT, AUTH_XPC_FLAGS 587// OUT: AUTH_XPC_OUT_ITEMS 588OSStatus 589authorization_copy_rights(connection_t conn, xpc_object_t message, xpc_object_t reply) 590{ 591 OSStatus status = errAuthorizationDenied; 592 engine_t engine = NULL; 593 594 process_t proc = connection_get_process(conn); 595 596 // Passed in args 597 auth_rights_t rights = auth_rights_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_RIGHTS)); 598 auth_items_t enviroment = auth_items_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_ENVIROMENT)); 599 AuthorizationFlags flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS); 600 601 auth_token_t auth = NULL; 602 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth); 603 require_noerr(status, done); 604 605 status = _server_authorize(conn, auth, flags, rights, enviroment, &engine); 606 require_noerr(status, done); 607 608 //reply 609 xpc_object_t outItems = auth_rights_export_xpc(engine_get_granted_rights(engine)); 610 xpc_dictionary_set_value(reply, AUTH_XPC_OUT_ITEMS, outItems); 611 xpc_release_safe(outItems); 612 613done: 614 CFReleaseSafe(rights); 615 CFReleaseSafe(enviroment); 616 CFReleaseSafe(auth); 617 CFReleaseSafe(engine); 618 619 return status; 620} 621 622// IN: AUTH_XPC_BLOB, AUTH_XPC_TAG 623// OUT: AUTH_XPC_OUT_ITEMS 624OSStatus 625authorization_copy_info(connection_t conn, xpc_object_t message, xpc_object_t reply) 626{ 627 628 OSStatus status = errAuthorizationSuccess; 629 auth_items_t items = NULL; 630 const char * tag = NULL; 631 632 process_t proc = connection_get_process(conn); 633 634 auth_token_t auth = NULL; 635 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth); 636 require_noerr(status, done); 637 638 items = auth_items_create(); 639 640 tag = xpc_dictionary_get_string(message, AUTH_XPC_TAG); 641 LOGV("server[%i]: requested tag: %s", connection_get_pid(conn), tag ? tag : "(all)"); 642 if (tag) { 643 size_t len; 644 const void * data = auth_items_get_data(auth_token_get_context(auth), tag, &len); 645 if (data) { 646 auth_items_set_data(items, tag, data, len); 647 } 648 } else { 649 auth_items_copy(items, auth_token_get_context(auth)); 650 } 651 652#if DEBUG 653 LOGV("server[%i]: Dumping requested AuthRef items", connection_get_pid(conn)); 654 _show_cf(items); 655#endif 656 657 //reply 658 xpc_object_t outItems = auth_items_export_xpc(items); 659 xpc_dictionary_set_value(reply, AUTH_XPC_OUT_ITEMS, outItems); 660 xpc_release_safe(outItems); 661 662done: 663 CFReleaseSafe(items); 664 CFReleaseSafe(auth); 665 LOGV("server[%i]: AuthorizationCopyInfo %i", connection_get_pid(conn), status); 666 return status; 667} 668 669// IN: AUTH_XPC_BLOB 670// OUT: AUTH_XPC_EXTERNAL 671OSStatus 672authorization_make_external_form(connection_t conn, xpc_object_t message, xpc_object_t reply) 673{ 674 OSStatus status = errAuthorizationSuccess; 675 676 process_t proc = connection_get_process(conn); 677 678 auth_token_t auth = NULL; 679 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth); 680 require_noerr(status, done); 681 682 AuthorizationExternalForm exForm; 683 AuthorizationExternalBlob * exBlob = (AuthorizationExternalBlob *)&exForm; 684 memset(&exForm, 0, sizeof(exForm)); 685 686 exBlob->blob = *auth_token_get_blob(auth); 687 exBlob->session = process_get_session_id(proc); 688 689 xpc_dictionary_set_data(reply, AUTH_XPC_EXTERNAL, &exForm, sizeof(exForm)); 690 server_register_auth_token(auth); 691 692done: 693 CFReleaseSafe(auth); 694 LOGV("server[%i]: AuthorizationMakeExternalForm %i", connection_get_pid(conn), status); 695 return status; 696} 697 698// IN: AUTH_XPC_EXTERNAL 699// OUT: AUTH_XPC_BLOB 700OSStatus 701authorization_create_from_external_form(connection_t conn, xpc_object_t message, xpc_object_t reply) 702{ 703 OSStatus status = errAuthorizationSuccess; 704 auth_token_t auth = NULL; 705 706 process_t proc = connection_get_process(conn); 707 708 size_t len; 709 AuthorizationExternalForm * exForm = (AuthorizationExternalForm *)xpc_dictionary_get_data(message, AUTH_XPC_EXTERNAL, &len); 710 require_action(exForm != NULL, done, status = errAuthorizationInternal); 711 require_action(len == sizeof(AuthorizationExternalForm), done, status = errAuthorizationInvalidRef); 712 713 AuthorizationExternalBlob * exBlob = (AuthorizationExternalBlob *)exForm; 714 auth = server_find_copy_auth_token(&exBlob->blob); 715 require_action(auth != NULL, done, status = errAuthorizationDenied); 716 717 process_add_auth_token(proc, auth); 718 xpc_dictionary_set_data(reply, AUTH_XPC_BLOB, auth_token_get_blob(auth), sizeof(AuthorizationBlob)); 719 720done: 721 CFReleaseSafe(auth); 722 LOGV("server[%i]: AuthorizationCreateFromExternalForm %i", connection_get_pid(conn), status); 723 return status; 724} 725 726// IN: AUTH_XPC_RIGHT_NAME 727// OUT: AUTH_XPC_DATA 728OSStatus 729authorization_right_get(connection_t conn AUTH_UNUSED, xpc_object_t message, xpc_object_t reply) 730{ 731 OSStatus status = errAuthorizationDenied; 732 rule_t rule = NULL; 733 CFTypeRef cfdict = NULL; 734 xpc_object_t xpcdict = NULL; 735 736 authdb_connection_t dbconn = authdb_connection_acquire(server_get_database()); 737 rule = rule_create_with_string(xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME), dbconn); 738 require(rule != NULL, done); 739 require(rule_get_id(rule) != 0, done); 740 741 cfdict = rule_copy_to_cfobject(rule, dbconn); 742 require(cfdict != NULL, done); 743 744 xpcdict = _CFXPCCreateXPCObjectFromCFObject(cfdict); 745 require(xpcdict != NULL, done); 746 747 // reply 748 xpc_dictionary_set_value(reply, AUTH_XPC_DATA, xpcdict); 749 750 status = errAuthorizationSuccess; 751 752done: 753 authdb_connection_release(&dbconn); 754 CFReleaseSafe(cfdict); 755 xpc_release_safe(xpcdict); 756 CFReleaseSafe(rule); 757 LOGV("server[%i]: AuthorizationRightGet %i", connection_get_pid(conn), status); 758 return status; 759} 760 761static bool _prompt_for_modifications(process_t proc, rule_t rule) 762{ 763// <rdar://problem/13853228> will put back it back at some later date 764// SecRequirementRef ruleReq = rule_get_requirment(rule); 765// 766// if (ruleReq && process_verify_requirment(proc, ruleReq)) { 767// return false; 768// } 769 770 return true; 771} 772 773static CFIndex _get_mechanism_index(CFArrayRef mechanisms, CFStringRef m_name) 774{ 775 CFIndex index = -1; 776 require(mechanisms, done); 777 778 CFIndex c = CFArrayGetCount(mechanisms); 779 CFStringRef i_name = NULL; 780 for (CFIndex i = 0; i < c; ++i) 781 { 782 i_name = CFArrayGetValueAtIndex(mechanisms, i); 783 if (i_name && (CFGetTypeID(m_name) == CFStringGetTypeID())) { 784 if (CFStringCompare(i_name, m_name, kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 785 index = i; 786 break; 787 } 788 } 789 } 790 791done: 792 return index; 793} 794 795static bool _update_rule_mechanism(authdb_connection_t dbconn, const char * rule_name, CFStringRef mechanism_name, CFStringRef insert_after_name, bool remove) 796{ 797 bool updated = false; 798 rule_t rule = NULL; 799 rule_t update_rule = NULL; 800 CFMutableDictionaryRef cfdict = NULL; 801 CFStringRef update_name = NULL; 802 803 require(mechanism_name, done); 804 805 rule = rule_create_with_string(rule_name, dbconn); 806 require(rule_get_id(rule) != 0, done); // rule doesn't exist in the database 807 808 cfdict = rule_copy_to_cfobject(rule, dbconn); 809 require(cfdict != NULL, done); 810 811 CFMutableArrayRef mechanisms = NULL; 812 bool res = CFDictionaryGetValueIfPresent(cfdict, CFSTR(kAuthorizationRuleParameterMechanisms), (void*)&mechanisms); 813 require(res == true, done); 814 815 CFIndex index = -1; 816 817 if (remove) { 818 index = _get_mechanism_index(mechanisms, mechanism_name); 819 } else { 820 if (insert_after_name) { 821 if ((index = _get_mechanism_index(mechanisms, insert_after_name)) != -1) { 822 index++; 823 } else { 824 index = 0; // if we couldn't find the index add it to the begining 825 } 826 } else { 827 index = 0; 828 } 829 } 830 831 if (index != -1) { 832 if(remove) { 833 CFArrayRemoveValueAtIndex(mechanisms, index); 834 } else { 835 if (index < CFArrayGetCount(mechanisms)) { 836 require_action(CFStringCompare(CFArrayGetValueAtIndex(mechanisms, index), mechanism_name, kCFCompareCaseInsensitive) != kCFCompareEqualTo, done, updated = true); 837 } 838 CFArrayInsertValueAtIndex(mechanisms, index, mechanism_name); 839 } 840 841 CFDictionarySetValue(cfdict, CFSTR(kAuthorizationRuleParameterMechanisms), mechanisms); 842 843 // and write it back 844 update_name = CFStringCreateWithCString(kCFAllocatorDefault, rule_name, kCFStringEncodingUTF8); 845 require(update_name, done); 846 update_rule = rule_create_with_plist(rule_get_type(rule), update_name, cfdict, dbconn); 847 require(update_rule, done); 848 849 require(rule_sql_commit(update_rule, dbconn, CFAbsoluteTimeGetCurrent(), NULL), done); 850 } 851 852 updated = true; 853 854done: 855 CFReleaseSafe(rule); 856 CFReleaseSafe(update_rule); 857 CFReleaseSafe(cfdict); 858 CFReleaseSafe(update_name); 859 return updated; 860} 861 862/// IN: AUTH_XPC_BLOB, AUTH_XPC_INT64 863// OUT: 864OSStatus 865authorization_enable_smartcard(connection_t conn, xpc_object_t message, xpc_object_t reply AUTH_UNUSED) 866{ 867 const CFStringRef SMARTCARD_LINE = CFSTR("builtin:smartcard-sniffer,privileged"); 868 const CFStringRef BUILTIN_LINE = CFSTR("builtin:policy-banner"); 869 const char* SYSTEM_LOGIN_CONSOLE = "system.login.console"; 870 const char* AUTHENTICATE = "authenticate"; 871 872 __block OSStatus status = errAuthorizationSuccess; 873 bool enable_smartcard = false; 874 authdb_connection_t dbconn = NULL; 875 auth_token_t auth = NULL; 876 auth_rights_t checkRight = NULL; 877 878 process_t proc = connection_get_process(conn); 879 880 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth); 881 require_noerr(status, done); 882 883 checkRight = auth_rights_create(); 884 auth_rights_add(checkRight, "config.modify.smartcard"); 885 status = _server_authorize(conn, auth, kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights, checkRight, NULL, NULL); 886 require_noerr(status, done); 887 888 enable_smartcard = xpc_dictionary_get_bool(message, AUTH_XPC_DATA); 889 890 dbconn = authdb_connection_acquire(server_get_database()); 891 892 if (!_update_rule_mechanism(dbconn, SYSTEM_LOGIN_CONSOLE, SMARTCARD_LINE, BUILTIN_LINE, enable_smartcard ? false : true)) { 893 status = errAuthorizationInternal; 894 LOGE("server[%i]: smartcard: enable(%i) failed to update %s", connection_get_pid(conn), enable_smartcard, SYSTEM_LOGIN_CONSOLE); 895 } 896 if (!_update_rule_mechanism(dbconn, AUTHENTICATE, SMARTCARD_LINE, NULL, enable_smartcard ? false : true)) { 897 status = errAuthorizationInternal; 898 LOGE("server[%i]: smartcard: enable(%i) failed to update %s", connection_get_pid(conn), enable_smartcard, AUTHENTICATE); 899 } 900 901 authdb_checkpoint(dbconn); 902 903done: 904 authdb_connection_release(&dbconn); 905 CFReleaseSafe(checkRight); 906 CFReleaseSafe(auth); 907 return status; 908} 909 910static int64_t _process_get_identifier_count(process_t proc, authdb_connection_t conn) 911{ 912 __block int64_t result = 0; 913 914 authdb_step(conn, "SELECT COUNT(*) AS cnt FROM rules WHERE identifier = ? ", ^(sqlite3_stmt *stmt) { 915 sqlite3_bind_text(stmt, 1, process_get_identifier(proc), -1, NULL); 916 }, ^bool(auth_items_t data) { 917 result = auth_items_get_int64(data, "cnt"); 918 return true; 919 }); 920 921 return result; 922} 923 924static int64_t _get_max_process_rights() 925{ 926 static dispatch_once_t onceToken; 927 static int64_t max_rights = MAX_PROCESS_RIGHTS; 928 929 //sudo defaults write /Library/Preferences/com.apple.authd max_process_rights -bool true 930 dispatch_once(&onceToken, ^{ 931 CFTypeRef max = (CFNumberRef)CFPreferencesCopyValue(CFSTR("max_process_rights"), CFSTR(SECURITY_AUTH_NAME), kCFPreferencesAnyUser, kCFPreferencesCurrentHost); 932 933 if (max && CFGetTypeID(max) == CFNumberGetTypeID()) { 934 CFNumberGetValue(max, kCFNumberSInt64Type, &max_rights); 935 } 936 CFReleaseSafe(max); 937 }); 938 939 return max_rights; 940} 941 942// IN: AUTH_XPC_BLOB, AUTH_XPC_RIGHT_NAME, AUTH_XPC_DATA 943// OUT: 944OSStatus 945authorization_right_set(connection_t conn, xpc_object_t message, xpc_object_t reply AUTH_UNUSED) 946{ 947 __block OSStatus status = errAuthorizationDenied; 948 __block engine_t engine = NULL; 949 CFStringRef cf_rule_name = NULL; 950 CFDictionaryRef cf_rule_dict = NULL; 951 rule_t rule = NULL; 952 rule_t existingRule = NULL; 953 authdb_connection_t dbconn = NULL; 954 auth_token_t auth = NULL; 955 bool force_modify = false; 956 RuleType rule_type = RT_RIGHT; 957 const char * rule_name = NULL; 958 bool auth_rule = false; 959 960 process_t proc = connection_get_process(conn); 961 962 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth); 963 require_noerr(status, done); 964 965 require_action(xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME) != NULL, done, status = errAuthorizationInternal); 966 require_action(xpc_dictionary_get_value(message, AUTH_XPC_DATA) != NULL, done, status = errAuthorizationInternal); 967 968 rule_name = xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME); 969 require(rule_name != NULL, done); 970 971 if (_compare_string(rule_name, "authenticate")) { 972 rule_type = RT_RULE; 973 auth_rule = true; 974 } 975 976 cf_rule_name = CFStringCreateWithCString(kCFAllocatorDefault, rule_name, kCFStringEncodingUTF8); 977 require(cf_rule_name != NULL, done); 978 979 cf_rule_dict = _CFXPCCreateCFObjectFromXPCObject(xpc_dictionary_get_value(message, AUTH_XPC_DATA)); 980 require(cf_rule_dict != NULL, done); 981 982 dbconn = authdb_connection_acquire(server_get_database()); 983 984 rule = rule_create_with_plist(rule_type, cf_rule_name, cf_rule_dict, dbconn); 985 if (process_get_uid(proc) != 0) { 986 require_action(rule_get_extract_password(rule) == false, done, status = errAuthorizationDenied; LOGE("server[%i]: AuthorizationRightSet not allowed to set extract-password. (denied)", connection_get_pid(conn))); 987 } 988 989 // if rule doesn't currently exist then we have to check to see if they are over the Max. 990 if (rule_get_id(rule) == 0) { 991 if (process_get_identifier(proc) == NULL) { 992 LOGE("server[%i]: AuthorizationRightSet required for process %s (missing code signature). To add rights to the Authorization database, your process must have a code signature.", connection_get_pid(conn), process_get_code_url(proc)); 993 force_modify = true; 994 } else { 995 int64_t process_rule_count = _process_get_identifier_count(proc, dbconn); 996 if ((process_rule_count >= _get_max_process_rights())) { 997 if (!connection_get_syslog_warn(conn)) { 998 LOGE("server[%i]: AuthorizationRightSet Denied API abuse process %s already contains %lli rights.", connection_get_pid(conn), process_get_code_url(proc), _get_max_process_rights()); 999 connection_set_syslog_warn(conn); 1000 } 1001 status = errAuthorizationDenied; 1002 goto done; 1003 } 1004 } 1005 } else { 1006 if (auth_rule) { 1007 if (process_get_uid(proc) != 0) { 1008 LOGE("server[%i]: AuthorizationRightSet denied, root required to update the 'authenticate' rule", connection_get_pid(conn)); 1009 status = errAuthorizationDenied; 1010 goto done; 1011 } 1012 } else { 1013 // verify they are updating a right and not a rule 1014 existingRule = rule_create_with_string(rule_get_name(rule), dbconn); 1015 if (rule_get_type(existingRule) == RT_RULE) { 1016 LOGE("server[%i]: AuthorizationRightSet Denied updating '%s' rule is prohibited", connection_get_pid(conn), rule_get_name(existingRule)); 1017 status = errAuthorizationDenied; 1018 goto done; 1019 } 1020 } 1021 } 1022 1023 if (_prompt_for_modifications(proc,rule)) { 1024 authdb_connection_release(&dbconn); 1025 1026 dispatch_sync(connection_get_dispatch_queue(conn), ^{ 1027 engine = engine_create(conn, auth); 1028 connection_set_engine(conn, engine); 1029 status = engine_verify_modification(engine, rule, false, force_modify); 1030 connection_set_engine(conn, NULL); 1031 }); 1032 require_noerr(status, done); 1033 1034 dbconn = authdb_connection_acquire(server_get_database()); 1035 } 1036 1037 if (rule_sql_commit(rule, dbconn, engine ? engine_get_time(engine) : CFAbsoluteTimeGetCurrent(), proc)) { 1038 LOGV("server[%i]: Successfully updated rule %s", connection_get_pid(conn), rule_get_name(rule)); 1039 authdb_checkpoint(dbconn); 1040 status = errAuthorizationSuccess; 1041 } else { 1042 LOGE("server[%i]: Failed to update rule %s", connection_get_pid(conn), rule_get_name(rule)); 1043 status = errAuthorizationDenied; 1044 } 1045 1046done: 1047 authdb_connection_release(&dbconn); 1048 CFReleaseSafe(existingRule); 1049 CFReleaseSafe(cf_rule_name); 1050 CFReleaseSafe(cf_rule_dict); 1051 CFReleaseSafe(auth); 1052 CFReleaseSafe(rule); 1053 CFReleaseSafe(engine); 1054 return status; 1055} 1056 1057// IN: AUTH_XPC_BLOB, AUTH_XPC_RIGHT_NAME 1058// OUT: 1059OSStatus 1060authorization_right_remove(connection_t conn, xpc_object_t message, xpc_object_t reply AUTH_UNUSED) 1061{ 1062 __block OSStatus status = errAuthorizationDenied; 1063 __block engine_t engine = NULL; 1064 rule_t rule = NULL; 1065 authdb_connection_t dbconn = NULL; 1066 1067 process_t proc = connection_get_process(conn); 1068 1069 auth_token_t auth = NULL; 1070 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth); 1071 require_noerr(status, done); 1072 1073 dbconn = authdb_connection_acquire(server_get_database()); 1074 1075 rule = rule_create_with_string(xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME), dbconn); 1076 require(rule != NULL, done); 1077 1078 if (_prompt_for_modifications(proc,rule)) { 1079 authdb_connection_release(&dbconn); 1080 1081 dispatch_sync(connection_get_dispatch_queue(conn), ^{ 1082 engine = engine_create(conn, auth); 1083 connection_set_engine(conn, engine); 1084 status = engine_verify_modification(engine, rule, true, false); 1085 connection_set_engine(conn, NULL); 1086 }); 1087 require_noerr(status, done); 1088 1089 dbconn = authdb_connection_acquire(server_get_database()); 1090 } 1091 1092 if (rule_get_id(rule) != 0) { 1093 rule_sql_remove(rule, dbconn); 1094 } 1095 1096done: 1097 authdb_connection_release(&dbconn); 1098 CFReleaseSafe(auth); 1099 CFReleaseSafe(rule); 1100 CFReleaseSafe(engine); 1101 LOGV("server[%i]: AuthorizationRightRemove %i", connection_get_pid(conn), status); 1102 return status; 1103} 1104 1105#pragma mark - 1106#pragma mark test code 1107 1108OSStatus 1109session_set_user_preferences(connection_t conn, xpc_object_t message, xpc_object_t reply) 1110{ 1111 (void)conn; 1112 (void)message; 1113 (void)reply; 1114 return errAuthorizationSuccess; 1115} 1116 1117void 1118server_dev() { 1119// rule_t rule = rule_create_with_string("system.preferences.accounts"); 1120// CFDictionaryRef dict = rule_copy_to_cfobject(rule); 1121// _show_cf(dict); 1122// CFReleaseSafe(rule); 1123// CFReleaseSafe(dict); 1124 1125// auth_items_t config = NULL; 1126// double d2 = 0, d1 = 5; 1127// authdb_get_key_value(server_get_authdb_reader(), "config", &config); 1128// auth_items_set_double(config, "test", d1); 1129// d2 = auth_items_get_double(config, "test"); 1130// LOGV("d1=%f d2=%f", d1, d2); 1131// CFReleaseSafe(config); 1132 1133 1134// auth_items_t items = auth_items_create(); 1135// auth_items_set_string(items, "test", "testing 1"); 1136// auth_items_set_string(items, "test2", "testing 2"); 1137// auth_items_set_string(items, "test3", "testing 3"); 1138// auth_items_set_flags(items, "test3", 4); 1139// auth_items_set_string(items, "apple", "apple"); 1140// auth_items_set_flags(items, "apple", 1); 1141// auth_items_set_int(items, "int", 45); 1142// auth_items_set_flags(items, "int", 2); 1143// auth_items_set_bool(items, "true", true); 1144// auth_items_set_bool(items, "false", false); 1145// auth_items_set(items, "com.apple."); 1146// auth_show(items); 1147// LOGD("Yeah it works: %s", auth_items_get_string(items, "test3")); 1148// LOGD("Yeah it works: %i", auth_items_get_bool(items, "true")); 1149// LOGD("Yeah it works: %i", auth_items_get_bool(items, "false")); 1150// LOGD("Yeah it works: %i", auth_items_get_int(items, "int")); 1151// (void)auth_items_get_bool(items, "test3"); 1152// AuthorizationItemSet * itemSet = auth_items_get_item_set(items); 1153// for (uint32_t i = 0; i < itemSet->count; i++) { 1154// LOGD("item: %s", itemSet->items[i].name); 1155// } 1156// 1157// xpc_object_t xpcdata = SerializeItemSet(auth_items_get_item_set(items)); 1158// auth_items_t items2 = auth_items_create_with_xpc(xpcdata); 1159// xpc_release(xpcdata); 1160// auth_items_remove_with_flags(items2, 7); 1161//// auth_items_set_string(items2, "test3", "testing 3 very good"); 1162// auth_items_copy_with_flags(items2, items, 7); 1163// LOGD("Yeah it works: %s", auth_items_get_string(items2, "test3")); 1164// auth_show(items2); 1165// CFReleaseSafe(items2); 1166// 1167// CFReleaseSafe(items); 1168} 1169 1170