1/* Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. */ 2 3#include "securityd_service.h" 4#include "securityd_service_client.h" 5#include <libaks.h> 6 7#include <sandbox.h> 8#include <vproc.h> 9#include <xpc/xpc.h> 10#include <xpc/private.h> 11#include <dispatch/dispatch.h> 12#include <sys/types.h> 13#include <sys/stat.h> 14#include <stdio.h> 15#include <errno.h> 16#include <assert.h> 17#include <syslog.h> 18#include <unistd.h> 19#include <pwd.h> 20#include <uuid/uuid.h> 21#include <bsm/libbsm.h> 22#include <copyfile.h> 23#include <AssertMacros.h> 24#include <Security/Security.h> 25#include <Security/SecKeychainPriv.h> 26 27#include <IOKit/IOKitLib.h> 28#include <Kernel/IOKit/crypto/AppleFDEKeyStoreDefs.h> 29 30#if DEBUG 31#define LOG(...) syslog(LOG_ERR, ##__VA_ARGS__); 32#else 33#define LOG(...) 34#endif 35 36// exported from libaks.a 37kern_return_t aks_register_for_notifications(mach_port_t server_port, uintptr_t message_id); 38kern_return_t _aks_stash_create_internal(keybag_handle_t handle, bool stage_key, const void * passcode, int length); 39kern_return_t _aks_stash_load_internal(keybag_handle_t handle, bool verify, uint8_t * data, size_t length, keybag_handle_t * handle_out); 40kern_return_t _aks_stash_destroy_internal(void); 41kern_return_t _aks_stash_commit_internal(void ** data, int * length); 42 43const char * kb_home_path = "Library/Keychains"; 44const char * kb_user_bag = "user.kb"; 45const char * kb_stash_bag = "stash.kb"; 46 47#define HEXBUF_LEN 2048 48 49typedef struct { 50 uid_t uid; 51 gid_t gid; 52 char * name; 53 char * home; 54} service_user_record_t; 55 56typedef enum { 57 kb_bag_type_user, 58 kb_bag_type_stash 59} kb_bag_type_t; 60 61static io_connect_t 62openiodev(void) 63{ 64 io_registry_entry_t service; 65 io_connect_t conn; 66 kern_return_t kr; 67 68 service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kAppleFDEKeyStoreServiceName)); 69 if (service == IO_OBJECT_NULL) 70 return IO_OBJECT_NULL; 71 72 kr = IOServiceOpen(service, mach_task_self(), 0, &conn); 73 if (kr != KERN_SUCCESS) 74 return IO_OBJECT_NULL; 75 76 kr = IOConnectCallMethod(conn, kAppleFDEKeyStoreUserClientOpen, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL); 77 if (kr != KERN_SUCCESS) { 78 IOServiceClose(conn); 79 return IO_OBJECT_NULL; 80 } 81 82 return conn; 83} 84 85static void 86closeiodev(io_connect_t conn) 87{ 88 kern_return_t kr; 89 kr = IOConnectCallMethod(conn, kAppleFDEKeyStoreUserClientClose, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL); 90 if (kr != KERN_SUCCESS) 91 return; 92 IOServiceClose(conn); 93} 94 95static dispatch_queue_t 96_kb_service_get_dispatch_queue() 97{ 98 static dispatch_once_t onceToken = 0; 99 static dispatch_queue_t connection_queue = NULL; 100 101 dispatch_once(&onceToken, ^{ 102 connection_queue = dispatch_queue_create("kb-service-queue", DISPATCH_QUEUE_SERIAL); 103 }); 104 105 return connection_queue; 106} 107 108static service_user_record_t * get_user_record(uid_t uid) 109{ 110 service_user_record_t * ur = NULL; 111 long bufsize = 0; 112 if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) { 113 bufsize = 4096; 114 } 115 char buf[bufsize]; 116 struct passwd pwbuf, *pw = NULL; 117 if ((getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0) && pw != NULL) { 118 ur = calloc(1u, sizeof(service_user_record_t)); 119 require(ur, done); 120 ur->uid = pw->pw_uid; 121 ur->gid = pw->pw_gid; 122 ur->home = strdup(pw->pw_dir); 123 ur->name = strdup(pw->pw_name); 124 } else { 125 syslog(LOG_ERR, "failed to lookup user record for uid: %d", uid); 126 } 127 128done: 129 return ur; 130} 131 132static void free_user_record(service_user_record_t * ur) 133{ 134 if (ur != NULL) { 135 if (ur->home) { 136 free(ur->home); 137 } 138 if (ur->name) { 139 free(ur->name); 140 } 141 free(ur); 142 } 143} 144 145static const char * get_host_uuid() 146{ 147 static uuid_string_t hostuuid = {}; 148 static dispatch_once_t onceToken; 149 dispatch_once(&onceToken, ^{ 150 struct timespec timeout = {30, 0}; 151 uuid_t uuid = {}; 152 if (gethostuuid(uuid, &timeout) == 0) { 153 uuid_unparse(uuid, hostuuid); 154 } else { 155 syslog(LOG_ERR, "failed to get host uuid"); 156 } 157 }); 158 159 return hostuuid; 160} 161 162static char * 163_kb_copy_bag_filename(service_user_record_t * ur, kb_bag_type_t type) 164{ 165 char * bag_file = NULL; 166 const char * name = NULL; 167 168 require(ur, done); 169 switch(type) { 170 case kb_bag_type_user: 171 name = kb_user_bag; 172 break; 173 case kb_bag_type_stash: 174 name = kb_stash_bag; 175 break; 176 default: 177 goto done; 178 } 179 180 bag_file = calloc(1u, PATH_MAX); 181 require(bag_file, done); 182 183 snprintf(bag_file, PATH_MAX, "%s/%s/%s/%s", ur->home, kb_home_path, get_host_uuid(), name); 184 185done: 186 return bag_file; 187} 188 189static bool 190_kb_verify_create_path(service_user_record_t * ur) 191{ 192 bool created = false; 193 struct stat st_info = {}; 194 char new_path[PATH_MAX] = {}; 195 char kb_path[PATH_MAX] = {}; 196 snprintf(kb_path, sizeof(kb_path), "%s/%s/%s", ur->home, kb_home_path, get_host_uuid()); 197 if (lstat(kb_path, &st_info) == 0) { 198 if (S_ISDIR(st_info.st_mode)) { 199 created = true; 200 } else { 201 syslog(LOG_ERR, "invalid directory at '%s' moving aside", kb_path); 202 snprintf(new_path, sizeof(new_path), "%s-invalid", kb_path); 203 unlink(new_path); 204 if (rename(kb_path, new_path) != 0) { 205 syslog(LOG_ERR, "failed to rename file: %s (%s)", kb_path, strerror(errno)); 206 goto done; 207 } 208 } 209 } 210 if (!created) { 211 require_action(mkpath_np(kb_path, 0700) == 0, done, syslog(LOG_ERR, "could not create path: %s (%s)", kb_path, strerror(errno))); 212 created = true; 213 } 214 215done: 216 return created; 217} 218 219static void 220_set_thread_credentials(service_user_record_t * ur) 221{ 222 int rc = pthread_setugid_np(ur->uid, ur->gid); 223 if (rc) { syslog(LOG_ERR, "failed to set thread credential: %i (%s)", errno, strerror(errno)); } 224 225 rc = initgroups(ur->name, ur->gid); 226 if (rc) { syslog(LOG_ERR, "failed to initgroups: %i", rc); } 227} 228 229static void 230_clear_thread_credentials() 231{ 232 int rc = pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE); 233 if (rc) { syslog(LOG_ERR, "failed to reset thread credential: %i (%s)", errno, strerror(errno)); } 234} 235 236static bool 237_kb_bag_exists(service_user_record_t * ur, const char * bag_file) 238{ 239 bool exists = false; 240 struct stat st_info = {}; 241 char new_file[PATH_MAX] = {}; 242 243 require(ur, done); 244 245 _set_thread_credentials(ur); 246 if (lstat(bag_file, &st_info) == 0) { 247 if (S_ISREG(st_info.st_mode)) { 248 exists = true; 249 } else { 250 syslog(LOG_ERR, "invalid file at '%s' moving aside", bag_file); 251 snprintf(new_file, sizeof(new_file), "%s-invalid", bag_file); 252 unlink(new_file); 253 if (rename(bag_file, new_file) != 0) { 254 syslog(LOG_ERR, "failed to rename file: %s (%s)", bag_file, strerror(errno)); 255 } 256 } 257 } 258 259done: 260 _clear_thread_credentials(); 261 return exists; 262} 263 264static bool 265_kb_save_bag_to_disk(service_user_record_t * ur, const char * bag_file, void * data, size_t length) 266{ 267 bool result = false; 268 int fd = -1; 269 270 require(bag_file, done); 271 272 _set_thread_credentials(ur); 273 require(_kb_verify_create_path(ur), done); 274 275 fd = open(bag_file, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0600); 276 require_action(fd != -1, done, syslog(LOG_ERR, "could not create file: %s (%s)", bag_file, strerror(errno))); 277 require_action(write(fd, data, length) != -1, done, syslog(LOG_ERR, "failed to write keybag to disk %s", strerror(errno))); 278 279 result = true; 280 281done: 282 if (fd != -1) { close(fd); } 283 _clear_thread_credentials(); 284 return result; 285} 286 287static bool 288_kb_load_bag_from_disk(service_user_record_t * ur, const char * bag_file, uint8_t ** data, size_t * length) 289{ 290 bool result = false; 291 int fd = -1; 292 uint8_t * buf = NULL; 293 size_t buf_size = 0; 294 struct stat st_info = {}; 295 296 require(bag_file, done); 297 298 _set_thread_credentials(ur); 299 require(_kb_verify_create_path(ur), done); 300 require_quiet(lstat(bag_file, &st_info) == 0, done); 301 require_action(S_ISREG(st_info.st_mode), done, syslog(LOG_ERR, "failed to load, not a file: %s", bag_file)); 302 buf_size = (size_t)st_info.st_size; 303 304 fd = open(bag_file, O_RDONLY | O_NOFOLLOW); 305 require_action(fd != -1, done, syslog(LOG_ERR, "could not open file: %s (%s)", bag_file, strerror(errno))); 306 307 buf = (uint8_t *)calloc(1u, buf_size); 308 require(buf != NULL, done); 309 require(read(fd, buf, buf_size) == buf_size, done); 310 311 *data = buf; 312 *length = buf_size; 313 buf = NULL; 314 result = true; 315 316done: 317 if (fd != -1) { close(fd); } 318 if (buf) { free(buf); } 319 _clear_thread_credentials(); 320 return result; 321} 322 323static void 324_kb_rename_bag_on_disk(service_user_record_t * ur, const char * bag_file) 325{ 326 char new_file[PATH_MAX] = {}; 327 if (bag_file) { 328 _set_thread_credentials(ur); 329 snprintf(new_file, sizeof(new_file), "%s-invalid", bag_file); 330 unlink(new_file); 331 rename(bag_file, new_file); 332 _clear_thread_credentials(); 333 } 334} 335 336static void 337_kb_delete_bag_on_disk(service_user_record_t * ur, const char * bag_file) 338{ 339 if (bag_file) { 340 _set_thread_credentials(ur); 341 unlink(bag_file); 342 _clear_thread_credentials(); 343 } 344} 345 346static int 347_kb_get_session_handle(service_context_t * context, keybag_handle_t * handle_out) 348{ 349 int rc = KB_BagNotLoaded; 350 keybag_handle_t session_handle = bad_keybag_handle; 351 require_noerr_quiet(aks_get_system(context->s_uid, &session_handle), done); 352 353 *handle_out = session_handle; 354 rc = KB_Success; 355 356done: 357 return rc; 358} 359 360static void update_keybag_handle(keybag_handle_t handle) 361{ 362 dispatch_sync(_kb_service_get_dispatch_queue(), ^{ 363 uid_t uid = abs(handle); 364 uint8_t * buf = NULL; 365 size_t buf_size = 0; 366 service_user_record_t * ur = NULL; 367 char * bag_file = NULL; 368 369 require_noerr(aks_save_bag(handle, (void**)&buf, (int*)&buf_size), done); 370 require(ur = get_user_record(uid), done); 371 require(bag_file = _kb_copy_bag_filename(ur, kb_bag_type_user), done); 372 require(_kb_save_bag_to_disk(ur, bag_file, buf, buf_size), done); 373 374 syslog(LOG_NOTICE, "successfully updated handle %d", handle); 375 376 done: 377 if (buf) free(buf); 378 if (ur) free_user_record(ur); 379 if (bag_file) free(bag_file); 380 }); 381} 382 383static int 384service_kb_create(service_context_t * context, const void * secret, int secret_len) 385{ 386 __block int rc = KB_GeneralError; 387 388 dispatch_sync(_kb_service_get_dispatch_queue(), ^{ 389 uint8_t * buf = NULL; 390 size_t buf_size = 0; 391 keybag_handle_t session_handle = bad_keybag_handle; 392 service_user_record_t * ur = get_user_record(context->s_uid); 393 char * bag_file = _kb_copy_bag_filename(ur, kb_bag_type_user); 394 395 require(bag_file, done); 396 397 // check for the existance of the bagfile 398 require_action(!_kb_bag_exists(ur, bag_file), done, rc = KB_BagExists); 399 400 require_noerr(rc = aks_create_bag(secret, secret_len, kAppleKeyStoreDeviceBag, &session_handle), done); 401 require_noerr(rc = aks_save_bag(session_handle, (void**)&buf, (int*)&buf_size), done); 402 require_action(_kb_save_bag_to_disk(ur, bag_file, buf, buf_size), done, rc = KB_BagError); 403 require_noerr(rc = aks_set_system(session_handle, context->s_uid), done); 404 aks_unload_bag(session_handle); 405 require_noerr(rc = _kb_get_session_handle(context, &session_handle), done); 406 407 if (secret && rc == KB_Success) { 408 aks_unlock_bag(session_handle, secret, secret_len); 409 } 410 411 done: 412 if (buf) free(buf); 413 if (bag_file) { free(bag_file); } 414 if (ur) free_user_record(ur); 415 }); 416 417 return rc; 418} 419 420static int 421service_kb_load(service_context_t * context) 422{ 423 __block int rc = KB_GeneralError; 424 425 dispatch_sync(_kb_service_get_dispatch_queue(), ^{ 426 uint8_t * buf = NULL; 427 size_t buf_size = 0; 428 keybag_handle_t session_handle = bad_keybag_handle; 429 service_user_record_t * ur = NULL; 430 char * bag_file = NULL; 431 432 rc = aks_get_system(context->s_uid, &session_handle); 433 if (rc == kIOReturnNotFound) { 434 require_action(ur = get_user_record(context->s_uid), done, rc = KB_GeneralError); 435 require_action(bag_file = _kb_copy_bag_filename(ur, kb_bag_type_user), done, rc = KB_GeneralError); 436 require_action_quiet(_kb_load_bag_from_disk(ur, bag_file, &buf, &buf_size), done, rc = KB_BagNotFound); 437 rc = aks_load_bag(buf, (int)buf_size, &session_handle); 438 if (rc == kIOReturnNotPermitted) { 439 syslog(LOG_ERR, "error loading keybag for uid (%i) in session (%i)", context->s_uid, context->s_id); 440 _kb_rename_bag_on_disk(ur, bag_file); 441 rc = KB_BagNotFound; 442 } 443 require_noerr(rc, done); 444 require_noerr(rc = aks_set_system(session_handle, context->s_uid), done); 445 aks_unload_bag(session_handle); 446 } 447 require(rc == KB_Success, done); 448 449 done: 450 if (buf) free(buf); 451 if (ur) free_user_record(ur); 452 if (bag_file) free(bag_file); 453 }); 454 455 return rc; 456} 457 458 459static int 460service_kb_unlock(service_context_t * context, const void * secret, int secret_len) 461{ 462 int rc = KB_GeneralError; 463 keybag_handle_t session_handle; 464 require_noerr(rc = _kb_get_session_handle(context, &session_handle), done); 465 466 rc = aks_unlock_bag(session_handle, secret, secret_len); 467 468done: 469 return rc; 470} 471 472static int 473service_kb_lock(service_context_t * context) 474{ 475 int rc = KB_GeneralError; 476 keybag_handle_t session_handle; 477 require_noerr(rc = _kb_get_session_handle(context, &session_handle), done); 478 479 rc = aks_lock_bag(session_handle); 480 481done: 482 return rc; 483} 484 485static int 486service_kb_change_secret(service_context_t * context, const void * secret, int secret_len, const void * new_secret, int new_secret_len) 487{ 488 __block int rc = KB_GeneralError; 489 keybag_handle_t session_handle; 490 require_noerr(rc = _kb_get_session_handle(context, &session_handle), done); 491 492 dispatch_sync(_kb_service_get_dispatch_queue(), ^{ 493 uint8_t * buf = NULL; 494 size_t buf_size = 0; 495 service_user_record_t * ur = NULL; 496 char * bag_file = NULL; 497 498 require_noerr(rc = aks_change_secret(session_handle, secret, secret_len, new_secret, new_secret_len, NULL, NULL), done); 499 require_noerr(rc = aks_save_bag(session_handle, (void**)&buf, (int*)&buf_size), done); 500 require_action(ur = get_user_record(context->s_uid), done, rc = KB_GeneralError); 501 require_action(bag_file = _kb_copy_bag_filename(ur, kb_bag_type_user), done, rc = KB_GeneralError); 502 require_action(_kb_save_bag_to_disk(ur, bag_file, buf, buf_size), done, rc = KB_BagError); 503 504 rc = KB_Success; 505 506 done: 507 if (buf) free(buf); 508 if (ur) free_user_record(ur); 509 if (bag_file) free(bag_file); 510 return; 511 }); 512 513done: 514 return rc; 515} 516 517static int 518service_kb_reset(service_context_t * context, const void * secret, int secret_len) 519{ 520 __block int rc = KB_GeneralError; 521 service_user_record_t * ur = NULL; 522 char * bag_file = NULL; 523 524 require_action(ur = get_user_record(context->s_uid), done, rc = KB_GeneralError); 525 require_action(bag_file = _kb_copy_bag_filename(ur, kb_bag_type_user), done, rc = KB_GeneralError); 526 527 dispatch_sync(_kb_service_get_dispatch_queue(), ^{ 528 uint8_t * buf = NULL; 529 size_t buf_size = 0; 530 keybag_handle_t session_handle = bad_keybag_handle; 531 532 syslog(LOG_ERR, "resetting keybag for uid (%i) in session (%i)", context->s_uid, context->s_id); 533 _kb_rename_bag_on_disk(ur, bag_file); 534 535 require_noerr(rc = aks_create_bag(secret, secret_len, kAppleKeyStoreDeviceBag, &session_handle), done); 536 require_noerr(rc = aks_save_bag(session_handle, (void**)&buf, (int*)&buf_size), done); 537 require_action(_kb_save_bag_to_disk(ur, bag_file, buf, buf_size), done, rc = KB_BagError); 538 require_noerr(rc = aks_set_system(session_handle, context->s_uid), done); 539 aks_unload_bag(session_handle); 540 require_noerr(rc = _kb_get_session_handle(context, &session_handle), done); 541 542 if (secret && rc == KB_Success) { 543 aks_unlock_bag(session_handle, secret, secret_len); 544 } 545 546 done: 547 if (buf) free(buf); 548 return; 549 }); 550 551done: 552 if (ur) free_user_record(ur); 553 if (bag_file) free(bag_file); 554 return rc; 555} 556 557static int 558service_kb_is_locked(service_context_t * context, xpc_object_t reply) 559{ 560 int rc = KB_GeneralError; 561 keybag_state_t state; 562 keybag_handle_t session_handle; 563 require_noerr(rc = _kb_get_session_handle(context, &session_handle), done); 564 565 require_noerr(rc = aks_get_lock_state(session_handle, &state), done); 566 567 xpc_dictionary_set_bool(reply, SERVICE_XPC_LOCKED, state & keybag_state_locked); 568 xpc_dictionary_set_bool(reply, SERVICE_XPC_NO_PIN, state & keybag_state_no_pin); 569 570done: 571 return rc; 572} 573 574static int 575service_kb_stash_create(service_context_t * context, const void * key, unsigned key_size) 576{ 577 int rc = KB_GeneralError; 578 char * bag_file = NULL; 579 keybag_handle_t session_handle; 580 service_user_record_t * ur = NULL; 581 void * stashbag = NULL; 582 unsigned stashbag_size = 0; 583 __block bool saved = false; 584 585 require(key, done); 586 require_noerr(rc = _kb_get_session_handle(context, &session_handle), done); 587 require_action(ur = get_user_record(context->s_uid), done, rc = KB_GeneralError); 588 require_noerr(rc = _aks_stash_create_internal(session_handle, false, key, key_size), done); 589 require_noerr(rc = _aks_stash_commit_internal((void**)&stashbag, (int*)&stashbag_size), done); 590 591 require_action(bag_file = _kb_copy_bag_filename(ur, kb_bag_type_stash), done, rc = KB_GeneralError); 592 593 // sync writing the bag to disk 594 dispatch_sync(_kb_service_get_dispatch_queue(), ^{ 595 saved = _kb_save_bag_to_disk(ur, bag_file, stashbag, stashbag_size); 596 }); 597 require_action(saved, done, rc = KB_BagError); 598 rc = KB_Success; 599 600done: 601 if (stashbag) { free(stashbag); } 602 if (bag_file) { free(bag_file); } 603 if (ur) free_user_record(ur); 604 return rc; 605} 606 607static int 608service_kb_stash_load(service_context_t * context, const void * key, unsigned key_size, bool nondestructive) 609{ 610 __block int rc = KB_GeneralError; 611 char * bag_file = NULL; 612 keybag_handle_t session_handle; 613 service_user_record_t * ur = NULL; 614 __block uint8_t * stashbag = NULL; 615 __block size_t stashbag_size = 0; 616 617 require(key, done); 618 require_noerr(rc = _kb_get_session_handle(context, &session_handle), done); 619 require_action(ur = get_user_record(context->s_uid), done, rc = KB_GeneralError); 620 require_action(bag_file = _kb_copy_bag_filename(ur, kb_bag_type_stash), done, rc = KB_GeneralError); 621 622 // sync loading the bag from disk 623 dispatch_sync(_kb_service_get_dispatch_queue(), ^{ 624 if (!_kb_load_bag_from_disk(ur, bag_file, &stashbag, &stashbag_size)) { 625 rc = KB_BagError; 626 } 627 }); 628 require_noerr(rc, done); 629 630 require_noerr(rc = _aks_stash_create_internal(session_handle, true, key, key_size), done); 631 require_noerr(rc = _aks_stash_load_internal(session_handle, false, stashbag, stashbag_size, NULL), done); 632 rc = KB_Success; 633 634done: 635 if (stashbag) { free(stashbag); } 636 if ((bag_file) && (!nondestructive)) { 637 _kb_delete_bag_on_disk(ur, bag_file); 638 free(bag_file); 639 } 640 if (ur) free_user_record(ur); 641 return rc; 642} 643 644// 645// Get the keychain master key from the AppleFDEKeyStore. 646// Note that this is a one-time call - the master key is 647// removed from the keystore after it is returned. 648// Requires the entitlement: com.apple.private.securityd.keychain 649// 650OSStatus service_stash_get_key(service_context_t * context, xpc_object_t event, xpc_object_t reply) 651{ 652 getStashKey_InStruct_t inStruct; 653 getStashKey_OutStruct_t outStruct; 654 size_t outSize = sizeof(outStruct); 655 kern_return_t kr = KERN_INVALID_ARGUMENT; 656 657 io_connect_t conn = openiodev(); 658 require(conn, done); 659 inStruct.type = kAppleFDEKeyStoreStash_master; 660 661 kr = IOConnectCallMethod(conn, kAppleFDEKeyStore_getStashKey, 662 NULL, 0, 663 &inStruct, sizeof(inStruct), 664 NULL, NULL, 665 &outStruct, &outSize); 666 667 if (kr == KERN_SUCCESS) { 668 xpc_dictionary_set_data(reply, SERVICE_XPC_KEY, outStruct.outBuf.key.key, outStruct.outBuf.key.keysize); 669 service_kb_stash_load(context, outStruct.outBuf.key.key, outStruct.outBuf.key.keysize, false); 670 } 671 672done: 673 if (conn) 674 closeiodev(conn); 675 676 return kr; 677} 678 679// 680// Stash the keychain master key in the AppleFDEKeyStore and 681// flag it as the keychain master key to be added to the 682// reboot NVRAM blob. 683// This requires two calls to the AKS: the first to store the 684// key and get its uuid. The second uses the uuid to flag the 685// key for blob inclusion. 686// 687OSStatus service_stash_set_key(service_context_t * context, xpc_object_t event, xpc_object_t reply) 688{ 689 kern_return_t kr = KERN_INVALID_ARGUMENT; 690 size_t keydata_len = 0; 691 size_t len; 692 693 io_connect_t conn = openiodev(); 694 require(conn, done); 695 696 // Store the key in the keystore and get its uuid 697 setKeyGetUUID_InStruct_t inStruct1; 698 uuid_OutStruct_t outStruct1; 699 700 701 const uint8_t *keydata = xpc_dictionary_get_data(event, SERVICE_XPC_KEY, &keydata_len); 702 require(keydata, done); 703 704 memcpy(&inStruct1.inKey.key.key, keydata, keydata_len); 705 inStruct1.inKey.key.keysize = (cryptosize_t) keydata_len; 706 len = sizeof(outStruct1); 707 kr = IOConnectCallMethod(conn, kAppleFDEKeyStore_setKeyGetUUID, 708 NULL, 0, 709 &inStruct1, sizeof(inStruct1), 710 NULL, NULL, 711 &outStruct1, &len); 712 require(kr == KERN_SUCCESS, done); 713 714 // Now using the uuid stash it as the master key 715 setStashKey_InStruct_t inStruct2; 716 memcpy(&inStruct2.uuid, &outStruct1.uuid, sizeof(outStruct1.uuid)); 717 inStruct2.type = kAppleFDEKeyStoreStash_master; 718 719 kr = IOConnectCallMethod(conn, kAppleFDEKeyStore_setStashKey, 720 NULL, 0, 721 &inStruct2, sizeof(inStruct2), 722 NULL, NULL, 723 NULL, NULL); 724 725 if (kr == KERN_SUCCESS) { 726 service_kb_stash_create(context, keydata, (unsigned)keydata_len); 727 } 728done: 729 if (conn) 730 closeiodev(conn); 731 732 return kr; 733} 734 735// 736// Load the master stash key 737// 738OSStatus service_stash_load_key(service_context_t * context, xpc_object_t event, xpc_object_t reply) 739{ 740 kern_return_t kr = KERN_SUCCESS; 741 size_t keydata_len = 0; 742 743 const uint8_t *keydata = xpc_dictionary_get_data(event, SERVICE_XPC_KEY, &keydata_len); 744 require(keydata, done); 745 746 kr = service_kb_stash_load(context, keydata, (cryptosize_t) keydata_len, true); 747done: 748 749 return kr; 750} 751 752// 753// Signal the AppleFDEKeyStore to take the tagged FDE key 754// and keychain master key, stash them in an encrypted 755// blob structure and write the blob to NVRAM. The random 756// encryption key is written to the SMC. 757// 758#if DEBUG 759OSStatus service_stash_blob(xpc_object_t event, xpc_object_t reply) 760{ 761 kern_return_t kr = KERN_INVALID_ARGUMENT; 762 763 io_connect_t conn = openiodev(); 764 require(conn, done); 765 766 kr = IOConnectCallMethod(conn, kAppleFDEKeyStore_commitStash, 767 NULL, 0, 768 NULL, 0, 769 NULL, NULL, 770 NULL, NULL); 771done: 772 if (conn) 773 closeiodev(conn); 774 775 return kr; 776} 777#endif 778 779bool peer_has_entitlement(xpc_connection_t peer, const char * entitlement) 780{ 781 bool entitled = false; 782 783 xpc_object_t value = xpc_connection_copy_entitlement_value(peer, entitlement); 784 if (value && (xpc_get_type(value) == XPC_TYPE_BOOL)) { 785 entitled = xpc_bool_get_value(value); 786 } 787 788 if (value) xpc_release(value); 789 return entitled; 790} 791 792#if DEBUG 793static char * 794to_hex(char * dst, const void * src, size_t size) 795{ 796 int notleading = 0; 797 798 if ((size * 2) > HEXBUF_LEN) return NULL; 799 800 uint8_t * buf = (uint8_t *)src; 801 register char *chp = dst; 802 *dst = '\0'; 803 if (size != 0) do { 804 if(notleading || *buf != '\0') { 805 if(!notleading && (*buf & 0xf0) == 0) { 806 sprintf(chp, "%.1x", * (unsigned char *) src); 807 chp += 1; 808 } 809 else { 810 sprintf(chp, "%.2x", * (unsigned char *) src); 811 chp += 2; 812 } 813 notleading = 1; 814 } 815 ++src; 816 } while (--size != 0); 817 return dst; 818} 819#endif // DEBUG 820 821static char * sel_to_char(uint64_t sel) 822{ 823 switch (sel) { 824 case SERVICE_STASH_SET_KEY: 825 return "set_key"; 826 case SERVICE_STASH_GET_KEY: 827 return "get_key"; 828 case SERVICE_STASH_BLOB: 829 return "stash_blob"; 830 case SERVICE_KB_LOAD: 831 return "kb_load"; 832 case SERVICE_KB_UNLOCK: 833 return "kb_unlock"; 834 case SERVICE_KB_LOCK: 835 return "kb_lock"; 836 case SERVICE_KB_CHANGE_SECRET: 837 return "kb_change_secret"; 838 case SERVICE_KB_CREATE: 839 return "kb_create"; 840 case SERVICE_KB_IS_LOCKED: 841 return "kb_is_locked"; 842 case SERVICE_KB_RESET: 843 return "kb_reset"; 844 default: 845 return "unknown"; 846 } 847} 848 849static char * err_to_char(int err) 850{ 851 switch (err) { 852 case KB_Success: 853 return "success"; 854 case KB_GeneralError: 855 return "general error"; 856 case KB_BagNotFound: 857 return "bag not found"; 858 case KB_BagError: 859 return "bag error"; 860 case KB_BagNotLoaded: 861 return "bag not loaded"; 862 case KB_BagExists: 863 return "bag exists"; 864 case KB_InvalidSession: 865 return "invalid session"; 866 default: 867 return ""; 868 } 869} 870 871void service_peer_event_handler(xpc_connection_t connection, xpc_object_t event) 872{ 873#if DEBUG 874 char hexbuf1[HEXBUF_LEN]; 875 char hexbuf2[HEXBUF_LEN]; 876#endif // DEBUG 877 xpc_type_t type = xpc_get_type(event); 878 879 if (type == XPC_TYPE_ERROR) { 880 if (event == XPC_ERROR_CONNECTION_INVALID) { 881 } 882 } else { 883 assert(type == XPC_TYPE_DICTIONARY); 884 885 int rc = KB_GeneralError; 886 uint64_t request = 0; 887 const uint8_t * secret = NULL, * new_secret = NULL; 888 size_t secret_len = 0, new_secret_len = 0, data_len = 0; 889 service_context_t * context = NULL; 890 const void * data; 891 892 xpc_object_t reply = xpc_dictionary_create_reply(event); 893 894 data = xpc_dictionary_get_data(event, SERVICE_XPC_CONTEXT, &data_len); 895 require(data, done); 896 require(data_len == sizeof(service_context_t), done); 897 context = (service_context_t*)data; 898 899 request = xpc_dictionary_get_uint64(event, SERVICE_XPC_REQUEST); 900 901 require_action(context->s_id != AU_DEFAUDITSID, done, rc = KB_InvalidSession); 902 require_action(context->s_uid != AU_DEFAUDITID, done, rc = KB_InvalidSession); // we only want to work in actual user sessions. 903 904 switch (request) { 905 case SERVICE_KB_CREATE: 906 // if (kb_service_has_entitlement(peer, "com.apple.keystore.device")) { 907 secret = xpc_dictionary_get_data(event, SERVICE_XPC_SECRET, &secret_len); 908 rc = service_kb_create(context, secret, (int)secret_len); 909 // } 910 break; 911 case SERVICE_KB_LOAD: 912 rc = service_kb_load(context); 913 break; 914 case SERVICE_KB_UNLOCK: 915 secret = xpc_dictionary_get_data(event, SERVICE_XPC_SECRET, &secret_len); 916 rc = service_kb_unlock(context, secret, (int)secret_len); 917 break; 918 case SERVICE_KB_LOCK: 919 rc = service_kb_lock(context); 920 break; 921 case SERVICE_KB_CHANGE_SECRET: 922 secret = xpc_dictionary_get_data(event, SERVICE_XPC_SECRET, &secret_len); 923 new_secret = xpc_dictionary_get_data(event, SERVICE_XPC_SECRET_NEW, &new_secret_len); 924 rc = service_kb_change_secret(context, secret, (int)secret_len, new_secret, (int)new_secret_len); 925 break; 926 case SERVICE_KB_RESET: 927 secret = xpc_dictionary_get_data(event, SERVICE_XPC_SECRET, &secret_len); 928 rc = service_kb_reset(context, secret, (int)secret_len); 929 break; 930 case SERVICE_KB_IS_LOCKED: 931 rc = service_kb_is_locked(context, reply); 932 break; 933 case SERVICE_STASH_GET_KEY: 934 rc = service_stash_get_key(context, event, reply); 935 break; 936 case SERVICE_STASH_SET_KEY: 937 rc = service_stash_set_key(context, event, reply); 938 break; 939 case SERVICE_STASH_LOAD_KEY: 940 rc = service_stash_load_key(context, event, reply); 941 break; 942#if DEBUG 943 case SERVICE_STASH_BLOB: 944 rc = service_stash_blob(event, reply); 945 break; 946#endif 947 default: 948 LOG("unknown service type"); 949 break; 950 } 951 952 done: 953#if DEBUG 954 LOG("selector: %s (%llu), error: %s (%x), '%s' secret_len: %zu, '%s' new_secret_len: %zu, sid: %d, suid: %d", sel_to_char(request), request, err_to_char(rc), rc, to_hex(hexbuf1, secret, secret_len), secret_len, to_hex(hexbuf2, new_secret, new_secret_len), new_secret_len, context ? context->s_id : 0, context ? context->s_uid : 0); 955#else 956 if (rc != 0) { 957 syslog(LOG_NOTICE, "selector: %s (%llu), error: %s (%x), sid: %d, suid: %d", sel_to_char(request), request, err_to_char(rc), rc, context ? context->s_id : 0, context ? context->s_uid : 0); 958 } 959#endif 960 xpc_dictionary_set_int64(reply, SERVICE_XPC_RC, rc); 961 xpc_connection_send_message(connection, reply); 962 xpc_release(reply); 963 } 964} 965 966bool check_signature(xpc_connection_t connection) 967{ 968 CFStringRef reqStr = CFSTR("identifier com.apple.securityd and anchor apple"); 969 SecRequirementRef requirement = NULL; 970 SecCodeRef codeRef = NULL; 971 CFMutableDictionaryRef codeDict = NULL; 972 CFNumberRef codePid = NULL; 973 pid_t pid = xpc_connection_get_pid(connection); 974 975 OSStatus status = SecRequirementCreateWithString(reqStr, kSecCSDefaultFlags, &requirement); 976 require_action(status == errSecSuccess, done, LOG("failed to create requirement")); 977 978 codeDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 979 codePid = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid); 980 CFDictionarySetValue(codeDict, kSecGuestAttributePid, codePid); 981 status = SecCodeCopyGuestWithAttributes(NULL, codeDict, kSecCSDefaultFlags, &codeRef); 982 require_action(status == errSecSuccess, done, LOG("failed to get code ref")); 983 984 status = SecCodeCheckValidity(codeRef, kSecCSDefaultFlags, 985#if DEBUG || RC_BUILDIT_YES 986 NULL); 987#else 988 requirement); 989#endif 990 require_action(status == errSecSuccess, done, syslog(LOG_ERR, "pid %d, does not satisfy code requirment (%d)", pid, status)); 991 992done: 993 if (codeRef) CFRelease(codeRef); 994 if (requirement) CFRelease(requirement); 995 if (codeDict) CFRelease(codeDict); 996 if (codePid) CFRelease(codePid); 997 998 return (status == errSecSuccess); 999} 1000 1001static void register_for_notifications() 1002{ 1003 __block kern_return_t kr; 1004 static mach_port_t mp = MACH_PORT_NULL; 1005 1006 static dispatch_once_t onceToken = 0; 1007 dispatch_once(&onceToken, ^{ 1008 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp); 1009 if (kr == KERN_SUCCESS) { 1010 dispatch_source_t mach_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); 1011 dispatch_source_set_event_handler(mach_src, ^{ 1012 mach_msg_return_t mr; 1013 uint8_t buf[sizeof(aks_notification_msg_t) + MAX_TRAILER_SIZE] = {}; 1014 aks_notification_msg_t * msg = (aks_notification_msg_t*)buf; 1015 mr = mach_msg((mach_msg_header_t*)&buf, MACH_RCV_MSG, 0, sizeof(buf), mp, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 1016 if (mr == MACH_MSG_SUCCESS && msg->hdr.msgh_id == AKS_NOTIFICATION_MSGID) { 1017 // ignored for now 1018 } else if (mr == MACH_MSG_SUCCESS && msg->hdr.msgh_id == AKS_NOTIFICATION_WRITE_SYSTEM_KEYBAG) { 1019 syslog(LOG_NOTICE, "request to update handle %d", msg->handle); 1020 update_keybag_handle(msg->handle); 1021 } else { 1022 syslog(LOG_ERR, "mach_msg error: %x", mr); 1023 } 1024 }); 1025 dispatch_resume(mach_src); 1026 } else { 1027 syslog(LOG_NOTICE, "failed to create notification port"); 1028 } 1029 1030 }); 1031 1032 kr = aks_register_for_notifications(mp, AKS_NOTIFICATION_WRITE_SYSTEM_KEYBAG); 1033 if (kr == KERN_SUCCESS) { 1034 syslog(LOG_NOTICE, "registered for notifications"); 1035 } else { 1036 syslog(LOG_NOTICE, "failed to register for notifications %d", kr); 1037 } 1038} 1039 1040int main(int argc, const char * argv[]) 1041{ 1042 char * errorbuf; 1043 if (sandbox_init(SECURITYD_SERVICE_NAME, SANDBOX_NAMED, &errorbuf) != 0) { 1044 syslog(LOG_ERR, "sandbox_init failed %s", errorbuf); 1045 sandbox_free_error(errorbuf); 1046#ifndef DEBUG 1047 abort(); 1048#endif 1049 } 1050 1051 register_for_notifications(); 1052 1053 xpc_connection_t listener = xpc_connection_create_mach_service(SECURITYD_SERVICE_NAME, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER); 1054 xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) { 1055 // It is safe to cast 'peer' to xpc_connection_t assuming 1056 // we have a correct configuration in our launchd.plist. 1057 1058 if (xpc_connection_get_euid(peer) != 0) { 1059 xpc_connection_cancel(peer); 1060 return; 1061 } 1062 1063 if (!check_signature(peer)) { 1064 xpc_connection_cancel(peer); 1065 return; 1066 } 1067 1068 xpc_connection_set_event_handler(peer, ^(xpc_object_t event) { 1069 vproc_transaction_t transaction = vproc_transaction_begin(NULL); 1070 service_peer_event_handler(peer, event); 1071 vproc_transaction_end(NULL, transaction); 1072 }); 1073 xpc_connection_resume(peer); 1074 }); 1075 xpc_connection_resume(listener); 1076 1077 dispatch_main(); 1078 exit(EXIT_FAILURE); 1079} 1080 1081