1#include "tests.h" 2 3#include <errno.h> 4#include <fcntl.h> 5#include <stdlib.h> 6#include <sys/mount.h> 7#include <sys/wait.h> 8 9#include <IOKit/IOKitLib.h> 10#include <Kernel/IOKit/crypto/AppleKeyStoreDefs.h> 11#include <Kernel/sys/content_protection.h> 12 13/* Note that this test (due to the need to lock/unlock the device on demand, and the 14 need to manipulate the passcode) has the unfortunate effect of link xnu_quick_test 15 to the IOKit Framework. */ 16 17/* TODO: Change the test to use a single cleanup label. */ 18 19#define CPT_IO_SIZE 4096 20#define CPT_AKS_BUF_SIZE 256 21#define CPT_MAX_PASS_LEN 64 22 23#define GET_PROT_CLASS(fd) fcntl((fd), F_GETPROTECTIONCLASS) 24#define SET_PROT_CLASS(fd, prot_class) fcntl((fd), F_SETPROTECTIONCLASS, (prot_class)) 25 26#define PRINT_LOCK_FAIL printf("%s, line %d: failed to lock the device.\n", cpt_fail_header, __LINE__); 27#define PRINT_UNLOCK_FAIL printf("%s, line %d: failed to unlock the device.\n", cpt_fail_header, __LINE__); 28 29extern char g_target_path[PATH_MAX]; 30 31char * cpt_fail_header = "Content protection test failed"; 32char * keystorectl_path = "/usr/local/bin/keystorectl"; 33 34/* Shamelessly ripped from keystorectl routines; a wrapper for invoking the AKS user client. */ 35int apple_key_store(uint32_t command, 36 uint64_t * inputs, 37 uint32_t input_count, 38 void * input_structs, 39 size_t input_struct_count, 40 uint64_t * outputs, 41 uint32_t * output_count) 42{ 43 int result = -1; 44 io_connect_t connection = IO_OBJECT_NULL; 45 io_registry_entry_t apple_key_bag_service = IO_OBJECT_NULL; 46 kern_return_t k_result = KERN_FAILURE; 47 IOReturn io_result = IO_OBJECT_NULL; 48 49 apple_key_bag_service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kAppleKeyStoreServiceName)); 50 51 if (apple_key_bag_service == IO_OBJECT_NULL) 52 { 53 printf("FAILURE: failed to match kAppleKeyStoreServiceName.\n"); 54 goto end; 55 } 56 57 k_result = IOServiceOpen(apple_key_bag_service, mach_task_self(), 0, &connection); 58 59 if (k_result != KERN_SUCCESS) 60 { 61 printf("FAILURE: failed to open AppleKeyStore.\n"); 62 goto end; 63 } 64 65 k_result = IOConnectCallMethod(connection, kAppleKeyStoreUserClientOpen, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL); 66 67 if (k_result != KERN_SUCCESS) 68 { 69 printf("FAILURE: call to AppleKeyStore method kAppleKeyStoreUserClientOpen failed.\n"); 70 goto close; 71 } 72 73 io_result = IOConnectCallMethod(connection, command, inputs, input_count, input_structs, input_struct_count, outputs, output_count, NULL, NULL); 74 75 if (io_result != kIOReturnSuccess) 76 { 77 printf("FAILURE: call to AppleKeyStore method %d failed.\n", command); 78 goto close; 79 } 80 81 result = 0; 82 83close: 84 IOServiceClose(apple_key_bag_service); 85 86end: 87 return(result); 88} 89 90#ifndef KEYBAG_ENTITLEMENTS 91/* Just a wrapper around forking to exec keystorectl for commands requiring entitlements. */ 92int keystorectl(char * const command[]) 93{ 94 int child_result = -1; 95 int result = -1; 96 pid_t child = -1; 97 98 child = fork(); 99 100 if (child == -1) 101 { 102 printf("FAILURE: failed to fork.\n"); 103 goto end; 104 } 105 else if (child == 0) 106 { 107 /* TODO: This keeps keystorectl from bombarding us with key state changes, but 108 there must be a better way of doing this; killing stderr is a bit nasty, 109 and if keystorectl fails, we want all the information we can get. */ 110 fclose(stderr); 111 fclose(stdin); 112 execv(keystorectl_path, command); 113 printf("FAILURE: child failed to execv keystorectl, errno = %s.\n", 114 strerror(errno)); 115 exit(EXIT_FAILURE); 116 } 117 118 if ((waitpid(child, &child_result, 0) != child) || WEXITSTATUS(child_result)) 119 { 120 printf("FAILURE: keystorectl failed.\n"); 121 result = -1; 122 } 123 else 124 { 125 result = 0; 126 } 127 128end: 129 return(result); 130} 131#endif /* KEYBAG_ENTITLEMENTS */ 132 133/* Code based on Mobile Key Bag; specifically MKBDeviceSupportsContentProtection 134 and MKBDeviceFormattedForContentProtection. */ 135/* We want to verify that we support content protection, and that 136 we are formatted for it. */ 137int supports_content_prot() 138{ 139 int local_result = -1; 140 int result = -1; 141 uint32_t buffer_size = 1; 142 char buffer[buffer_size]; 143 io_registry_entry_t defaults = IO_OBJECT_NULL; 144 kern_return_t k_result = KERN_FAILURE; 145 struct statfs statfs_results; 146 147 defaults = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/defaults"); 148 149 if (defaults == IO_OBJECT_NULL) 150 { 151 printf("FAILURE: failed to find defaults registry entry.\n"); 152 goto end; 153 } 154 155 k_result = IORegistryEntryGetProperty(defaults, "content-protect", buffer, &buffer_size); 156 157 if (k_result != KERN_SUCCESS) 158 { /* This isn't a failure; it means the entry doesn't exist, so we assume CP 159 is unsupported. */ 160 result = 0; 161 goto end; 162 } 163 164 /* At this point, we SUPPORT content protection... but are we formatted for it? */ 165 /* This is ugly; we should be testing the file system we'll be testing in, not 166 just /tmp/. */ 167 local_result = statfs(g_target_path, &statfs_results); 168 169 if (local_result == -1) 170 { 171 printf("FAILURE: failed to statfs the test directory, errno = %s.\n", 172 strerror(errno)); 173 } 174 else if (statfs_results.f_flags & MNT_CPROTECT) 175 { 176 result = 1; 177 } 178 else 179 { /* This isn't a failure, it means the filesystem isn't formatted for CP. */ 180 result = 0; 181 } 182 183end: 184 return(result); 185} 186 187#if 0 188int device_lock_state() 189{ 190 /* TODO: Actually implement this. */ 191 /* We fail if a passcode already exists, and the methods being used to lock/unlock 192 the device in this test appear to be synchronous... do we need this function? */ 193 int result = -1; 194 195 return(result); 196} 197#endif 198 199int lock_device() 200{ 201 int result = -1; 202 203#ifdef KEYBAG_ENTITLEMENTS 204 /* If we're entitled, we can lock the device ourselves. */ 205 uint64_t inputs[] = {device_keybag_handle}; 206 uint32_t input_count = (sizeof(inputs) / sizeof(*inputs)); 207 result = apple_key_store(kAppleKeyStoreKeyBagLock, inputs, input_count, NULL, 0, NULL, NULL); 208#else 209 /* If we aren't entitled, we'll need to use keystorectl to lock the device. */ 210 /* keystorectl seems to have a bus error (though it locks successfully) unless 211 lock is passed an argument, so we'll also pass it the empty string. */ 212 char * const keystorectl_args[] = {keystorectl_path, "lock", "", NULL}; 213 result = keystorectl(keystorectl_args); 214#endif /* KEYBAG_ENTITLEMENTS */ 215 216 return(result); 217} 218 219int unlock_device(char * passcode) 220{ 221 int result = -1; 222 223#ifdef KEYBAG_ENTITLEMENTS 224 /* If we're entitled, we can unlock the device ourselves. */ 225 uint64_t inputs[] = {device_keybag_handle}; 226 uint32_t input_count = (sizeof(inputs) / sizeof(*inputs)); 227 size_t input_struct_count = 0; 228 229 if ((passcode == NULL) || ((input_struct_count = strnlen(passcode, CPT_MAX_PASS_LEN)) == CPT_MAX_PASS_LEN)) 230 { 231 passcode = ""; 232 input_struct_count = 0; 233 } 234 235 result = apple_key_store(kAppleKeyStoreKeyBagUnlock, inputs, input_count, passcode, input_struct_count, NULL, NULL); 236#else 237 /* If we aren't entitled, we'll need to use keystorectl to unlock the device. */ 238 if ((passcode == NULL) || (strnlen(passcode, CPT_MAX_PASS_LEN) == CPT_MAX_PASS_LEN)) 239 { 240 passcode = ""; 241 } 242 243 char * const keystorectl_args[] = {keystorectl_path, "unlock", passcode, NULL}; 244 result = keystorectl(keystorectl_args); 245#endif /* KEYBAG_ENTITLEMENTS */ 246 247 return(result); 248} 249 250int set_passcode(char * new_passcode, char * old_passcode) 251{ 252 int result = -1; 253 254#ifdef KEYBAG_ENTITLEMENTS 255 /* If we're entitled, we can set the passcode ourselves. */ 256 uint64_t inputs[] = {device_keybag_handle}; 257 uint32_t input_count = (sizeof(inputs) / sizeof(*inputs)); 258 void * input_structs = NULL; 259 size_t input_struct_count = 0; 260 char buffer[CPT_AKS_BUF_SIZE]; 261 char * buffer_ptr = buffer; 262 uint32_t old_passcode_len = 0; 263 uint32_t new_passcode_len = 0; 264 265 if ((old_passcode == NULL) || ((old_passcode_len = strnlen(old_passcode, CPT_MAX_PASS_LEN)) == CPT_MAX_PASS_LEN)) 266 { 267 old_passcode = ""; 268 old_passcode_len = 0; 269 } 270 271 if ((new_passcode == NULL) || ((new_passcode_len = strnlen(new_passcode, CPT_MAX_PASS_LEN)) == CPT_MAX_PASS_LEN)) 272 { 273 new_passcode = ""; 274 new_passcode_len = 0; 275 } 276 277 *((uint32_t *) buffer_ptr) = ((uint32_t) 2); 278 buffer_ptr += sizeof(uint32_t); 279 *((uint32_t *) buffer_ptr) = old_passcode_len; 280 buffer_ptr += sizeof(uint32_t); 281 memcpy(buffer_ptr, old_passcode, old_passcode_len); 282 buffer_ptr += ((old_passcode_len + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1)); 283 *((uint32_t *) buffer_ptr) = new_passcode_len; 284 buffer_ptr += sizeof(uint32_t); 285 memcpy(buffer_ptr, new_passcode, new_passcode_len); 286 buffer_ptr += ((new_passcode_len + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1)); 287 input_structs = buffer; 288 input_struct_count = (buffer_ptr - buffer); 289 290 result = apple_key_store(kAppleKeyStoreKeyBagSetPasscode, inputs, input_count, input_structs, input_struct_count, NULL, NULL); 291#else 292 /* If we aren't entitled, we'll need to use keystorectl to set the passcode. */ 293 if ((old_passcode == NULL) || (strnlen(old_passcode, CPT_MAX_PASS_LEN) == CPT_MAX_PASS_LEN)) 294 { 295 old_passcode = ""; 296 } 297 298 if ((new_passcode == NULL) || (strnlen(new_passcode, CPT_MAX_PASS_LEN) == CPT_MAX_PASS_LEN)) 299 { 300 new_passcode = ""; 301 } 302 303 char * const keystorectl_args[] = {keystorectl_path, "change-password", old_passcode, new_passcode, NULL}; 304 result = keystorectl(keystorectl_args); 305#endif /* KEYBAG_ENTITLEMENTS */ 306 307 return(result); 308} 309 310int clear_passcode(char * passcode) 311{ 312 /* For the moment, this will set the passcode to the empty string (a known value); 313 this will most likely need to change, or running this test may ruin everything(tm). */ 314 int result = -1; 315 316 result = set_passcode(NULL, passcode); 317 318 return(result); 319} 320 321#if 0 322/* Determines if we will try to test class C semanatics. */ 323int unlocked_since_boot() 324{ 325 /* TODO: Actually implement this. */ 326 /* The actual semantics for CP mean that even with this primative, we would need 327 set a passcode and then reboot the device in order to test this; this function 328 will probably be rather worthless as a result. */ 329 int result = 1; 330 331 return(result); 332} 333#endif 334 335/* If the device has a passcode when we want to test it, things are going to go wrong. 336 As such, we'll assume the device never has a passcode. 337 No, not even then. 338 Or we could just try "" to ""; it works. */ 339int has_passcode() 340{ 341 int result = -1; 342 343 result = set_passcode(NULL, NULL); 344 345 return(result); 346} 347 348int content_protection_test(void * argp) 349{ 350 #pragma unused (argp) 351 int init_result = 0; 352 int local_result = -1; 353 int test_result = -1; 354 int fd = -1; 355 int dir_fd = -1; 356 int subdir_fd = -1; 357 int new_prot_class = -1; 358 int old_prot_class = -1; 359 int current_byte = 0; 360 char filepath[PATH_MAX]; 361 char dirpath[PATH_MAX]; 362 char subdirpath[PATH_MAX]; 363 char rd_buffer[CPT_IO_SIZE]; 364 char wr_buffer[CPT_IO_SIZE]; 365 char * passcode = "IAmASecurePassword"; 366 367 /* Do some initial setup (names). */ 368 bzero(filepath, PATH_MAX); 369 bzero(dirpath, PATH_MAX); 370 bzero(subdirpath, PATH_MAX); 371 372 /* This is just easier than checking each result individually. */ 373 init_result |= (strlcat(filepath, g_target_path, PATH_MAX) == PATH_MAX); 374 init_result |= (strlcat(filepath, "/", PATH_MAX) == PATH_MAX); 375 init_result |= (strlcpy(dirpath, filepath, PATH_MAX) == PATH_MAX); 376 init_result |= (strlcat(filepath, "cpt_test_file", PATH_MAX) == PATH_MAX); 377 init_result |= (strlcat(dirpath, "cpt_test_dir/", PATH_MAX) == PATH_MAX); 378 init_result |= (strlcpy(subdirpath, dirpath, PATH_MAX) == PATH_MAX); 379 init_result |= (strlcat(subdirpath, "cpt_test_subdir/", PATH_MAX) == PATH_MAX); 380 381 if (init_result) 382 { /* If any of the initialization failed, we're just going to fail now. */ 383 printf("%s, line %d: failed to initialize test strings.\n", 384 cpt_fail_header, __LINE__); 385 goto end; 386 } 387 388 local_result = supports_content_prot(); 389 390 if (local_result == -1) 391 { 392 printf("%s, line %d: failed to determine if content protection is supported.\n", 393 cpt_fail_header, __LINE__); 394 goto end; 395 } 396 else if (local_result == 0) 397 { /* If we don't support content protection at the moment, pass the test. */ 398 printf("This device does not support or is not formatted for content protection.\n"); 399 test_result = 0; 400 goto end; 401 } 402 403 /* If we support content protection, we'll need to be able to set the passcode. */ 404 local_result = has_passcode(); 405 406 if (local_result == -1) 407 { 408 printf("%s, line %d: the device appears to have a passcode.\n", 409 cpt_fail_header, __LINE__); 410 goto end; 411 } 412 413 if (set_passcode(passcode, NULL)) 414 { 415 printf("%s, line %d: failed to set a new passcode.\n", 416 cpt_fail_header, __LINE__); 417 goto end; 418 } 419 420 fd = open(filepath, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC); 421 422 if (fd == -1) 423 { 424 printf("%s, line %d: failed to create the test file, errno = %s.\n", 425 cpt_fail_header, __LINE__, strerror(errno)); 426 goto remove_passcode; 427 } 428 429 /* Ensure we can freely read and change protection classes when unlocked. */ 430 for (new_prot_class = PROTECTION_CLASS_A; new_prot_class <= PROTECTION_CLASS_F; new_prot_class++) 431 { 432 old_prot_class = GET_PROT_CLASS(fd); 433 434 if (old_prot_class == -1) 435 { 436 printf("%s, line %d: failed to get protection class when unlocked, errno = %s.\n", 437 cpt_fail_header, __LINE__, strerror(errno)); 438 goto cleanup_file; 439 } 440 441 if (SET_PROT_CLASS(fd, new_prot_class)) 442 { 443 printf("%s, line %d: failed to change protection class from %d to %d during unlock, errno = %s.\n", 444 cpt_fail_header, __LINE__, old_prot_class, new_prot_class, strerror(errno)); 445 goto cleanup_file; 446 } 447 } 448 449 if (SET_PROT_CLASS(fd, PROTECTION_CLASS_D)) 450 { 451 printf("%s, line %d: failed to change protection class from F to D when unlocked, errno = %s.\n", 452 cpt_fail_header, __LINE__, strerror(errno)); 453 goto cleanup_file; 454 } 455 456 /* Try making a class A file while locked. */ 457 if (lock_device()) 458 { 459 PRINT_LOCK_FAIL; 460 goto cleanup_file; 461 } 462 463 if (!SET_PROT_CLASS(fd, PROTECTION_CLASS_A)) 464 { 465 printf("%s, line %d: was able to change protection class from D to A when locked.\n", 466 cpt_fail_header, __LINE__); 467 goto cleanup_file; 468 } 469 470 if (unlock_device(passcode)) 471 { 472 PRINT_UNLOCK_FAIL; 473 goto cleanup_file; 474 } 475 476 /* Attempt opening/IO to a class A file while unlocked. */ 477 if (SET_PROT_CLASS(fd, PROTECTION_CLASS_A)) 478 { 479 printf("%s, line %d: failed to change protection class from D to A when unlocked, errno = %s.\n", 480 cpt_fail_header, __LINE__, strerror(errno)); 481 goto cleanup_file; 482 } 483 484 close(fd); 485 fd = open(filepath, O_RDWR | O_CLOEXEC); 486 487 if (fd == -1) 488 { 489 printf("%s, line %d: failed to open a class A file when unlocked, errno = %s.\n", 490 cpt_fail_header, __LINE__, strerror(errno)); 491 goto remove_file; 492 } 493 494 /* TODO: Write specific data we can check for. 495 If we're going to do that, the write scheme should be deliberately ugly. */ 496 current_byte = 0; 497 498 while (current_byte < CPT_IO_SIZE) 499 { 500 local_result = pwrite(fd, &wr_buffer[current_byte], CPT_IO_SIZE - current_byte, current_byte); 501 502 if (local_result == -1) 503 { 504 printf("%s, line %d: failed to write to class A file when unlocked, errno = %s.\n", 505 cpt_fail_header, __LINE__, strerror(errno)); 506 goto cleanup_file; 507 } 508 509 current_byte += local_result; 510 } 511 512 current_byte = 0; 513 514 while (current_byte < CPT_IO_SIZE) 515 { 516 local_result = pread(fd, &rd_buffer[current_byte], CPT_IO_SIZE - current_byte, current_byte); 517 518 if (local_result == -1) 519 { 520 printf("%s, line %d: failed to read from class A file when unlocked, errno = %s.\n", 521 cpt_fail_header, __LINE__, strerror(errno)); 522 goto cleanup_file; 523 } 524 525 current_byte += local_result; 526 } 527 528 /* Again, but now while locked; and try to change the file class as well. */ 529 if (lock_device()) 530 { 531 PRINT_LOCK_FAIL; 532 goto cleanup_file; 533 } 534 535 if (pread(fd, rd_buffer, CPT_IO_SIZE, 0) > 0) 536 { 537 printf("%s, line %d: was able to read from a class A file when locked.\n", 538 cpt_fail_header, __LINE__); 539 goto cleanup_file; 540 } 541 542 if (pwrite(fd, wr_buffer, CPT_IO_SIZE, 0) > 0) 543 { 544 printf("%s, line %d: was able to write to a class A file when locked.\n", 545 cpt_fail_header, __LINE__); 546 goto cleanup_file; 547 } 548 549 if (!SET_PROT_CLASS(fd, PROTECTION_CLASS_D)) 550 { 551 printf("%s, line %d: was able to change protection class from A to D when locked.\n", 552 cpt_fail_header, __LINE__); 553 goto cleanup_file; 554 } 555 556 /* Try to open and truncate the file. */ 557 close(fd); 558 fd = open(filepath, O_RDWR | O_TRUNC | O_CLOEXEC); 559 560 if (fd != -1) 561 { 562 printf("%s, line %d: was able to open and truncate a class A file when locked.\n", 563 cpt_fail_header, __LINE__); 564 goto cleanup_file; 565 } 566 567 /* Try to open the file */ 568 fd = open(filepath, O_RDWR | O_CLOEXEC); 569 570 if (fd != -1) 571 { 572 printf("%s, line %d: was able to open a class A file when locked.\n", 573 cpt_fail_header, __LINE__); 574 goto cleanup_file; 575 } 576 577 /* What about class B files? */ 578 if (unlock_device(passcode)) 579 { 580 PRINT_UNLOCK_FAIL; 581 goto cleanup_file; 582 } 583 584 fd = open(filepath, O_RDWR | O_CLOEXEC); 585 586 if (fd == -1) 587 { 588 printf("%s, line %d: was unable to open a class A file when unlocked.\n", 589 cpt_fail_header, __LINE__); 590 goto cleanup_file; 591 } 592 593 if (SET_PROT_CLASS(fd, PROTECTION_CLASS_D)) 594 { 595 printf("%s, line %d: failed to change protection class from A to D when unlocked, errno = %s.\n", 596 cpt_fail_header, __LINE__, strerror(errno)); 597 goto cleanup_file; 598 } 599 600 if (lock_device()) 601 { 602 PRINT_LOCK_FAIL; 603 goto cleanup_file; 604 } 605 606 /* Can we create a class B file while locked? */ 607 if (SET_PROT_CLASS(fd, PROTECTION_CLASS_B)) 608 { 609 printf("%s, line %d: failed to change protection class from D to B when locked, errno = %s.\n", 610 cpt_fail_header, __LINE__, strerror(errno)); 611 goto cleanup_file; 612 } 613 614 /* We should also be able to read/write to the file descriptor while it is open. */ 615 current_byte = 0; 616 617 while (current_byte < CPT_IO_SIZE) 618 { 619 local_result = pwrite(fd, &wr_buffer[current_byte], CPT_IO_SIZE - current_byte, current_byte); 620 621 if (local_result == -1) 622 { 623 printf("%s, line %d: failed to write to new class B file when locked, errno = %s.\n", 624 cpt_fail_header, __LINE__, strerror(errno)); 625 goto cleanup_file; 626 } 627 628 current_byte += local_result; 629 } 630 631 current_byte = 0; 632 633 while (current_byte < CPT_IO_SIZE) 634 { 635 local_result = pread(fd, &rd_buffer[current_byte], CPT_IO_SIZE - current_byte, current_byte); 636 637 if (local_result == -1) 638 { 639 printf("%s, line %d: failed to read from new class B file when locked, errno = %s.\n", 640 cpt_fail_header, __LINE__, strerror(errno)); 641 goto cleanup_file; 642 } 643 644 current_byte += local_result; 645 } 646 647 /* We should not be able to open a class B file under lock. */ 648 close(fd); 649 fd = open(filepath, O_RDWR | O_CLOEXEC); 650 651 if (fd != -1) 652 { 653 printf("%s, line %d: was able to open a class B file when locked.\n", 654 cpt_fail_header, __LINE__); 655 goto cleanup_file; 656 } 657 658 unlink(filepath); 659 660 /* We still need to test directory semantics. */ 661 if (mkdir(dirpath, 0x0777) == -1) 662 { 663 printf("%s, line %d: failed to create a new directory when locked, errno = %s.\n", 664 cpt_fail_header, __LINE__, strerror(errno)); 665 goto remove_passcode; 666 } 667 668 /* The newly created directory should not have a protection class. */ 669 dir_fd = open(dirpath, O_RDONLY | O_CLOEXEC); 670 671 if (dir_fd == -1) 672 { 673 printf("%s, line %d: failed to open an unclassed directory when locked, errno = %s.\n", 674 cpt_fail_header, __LINE__, strerror(errno)); 675 goto remove_dir; 676 } 677 678 if (GET_PROT_CLASS(dir_fd) != PROTECTION_CLASS_D) 679 { 680 printf("%s, line %d: newly created directory had a non-D protection class.\n", 681 cpt_fail_header, __LINE__); 682 goto cleanup_dir; 683 } 684 685 if (SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_A)) 686 { 687 printf("%s, line %d: was unable to change a directory from class D to class A during lock.\n", 688 cpt_fail_header, __LINE__); 689 goto cleanup_dir; 690 } 691 692 if (SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_D)) 693 { 694 printf("%s, line %d: failed to change a directory from class A to class D during lock, errno = %s.\n", 695 cpt_fail_header, __LINE__, strerror(errno)); 696 goto cleanup_dir; 697 } 698 699 /* Do all files created in the directory properly inherit the directory's protection class? */ 700 if ((strlcpy(filepath, dirpath, PATH_MAX) == PATH_MAX) || (strlcat(filepath, "cpt_test_file", PATH_MAX) == PATH_MAX)) 701 { 702 printf("%s, line %d: failed to construct the path for a file in the directory.\n", 703 cpt_fail_header, __LINE__); 704 goto cleanup_dir; 705 } 706 707 if (unlock_device(passcode)) 708 { 709 PRINT_UNLOCK_FAIL; 710 goto cleanup_dir; 711 } 712 713 for (new_prot_class = PROTECTION_CLASS_A; new_prot_class <= PROTECTION_CLASS_E; new_prot_class++) 714 { 715 old_prot_class = GET_PROT_CLASS(dir_fd); 716 717 if (old_prot_class == -1) 718 { 719 printf("%s, line %d: failed to get the protection class for the directory, errno = %s.\n", 720 cpt_fail_header, __LINE__, strerror(errno)); 721 goto cleanup_dir; 722 } 723 724 if (SET_PROT_CLASS(dir_fd, new_prot_class)) 725 { 726 printf("%s, line %d: failed to change the protection class for the directory from %d to %d, errno = %s.\n", 727 cpt_fail_header, __LINE__, old_prot_class, new_prot_class, strerror(errno)); 728 goto cleanup_dir; 729 } 730 731 fd = open(filepath, O_CREAT | O_EXCL | O_CLOEXEC); 732 733 if (fd == -1) 734 { 735 printf("%s, line %d: failed to create a file in a class %d directory when unlocked, errno = %s.\n", 736 cpt_fail_header, __LINE__, new_prot_class, strerror(errno)); 737 goto cleanup_dir; 738 } 739 740 local_result = GET_PROT_CLASS(fd); 741 742 if (local_result == -1) 743 { 744 printf("%s, line %d: failed to get the new file's protection class, errno = %s.\n", 745 cpt_fail_header, __LINE__, strerror(errno)); 746 goto cleanup_file; 747 } 748 else if (local_result != new_prot_class) 749 { 750 printf("%s, line %d: new file did not inherit the directory's protection class.\n", 751 cpt_fail_header, __LINE__, strerror(errno)); 752 goto cleanup_file; 753 } 754 755 close(fd); 756 unlink(filepath); 757 } 758 759 /* Do we disallow creation of a class F directory? */ 760 if (!SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_F)) 761 { 762 printf("%s, line %d: creation of a class F directory did not fail as expected.\n", 763 cpt_fail_header, __LINE__); 764 goto cleanup_dir; 765 } 766 767 /* And are class A and class B semantics followed for when we create these files during lock? */ 768 if (SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_A)) 769 { 770 printf("%s, line %d: failed to change directory class from F to A when unlocked, errno = %s.\n", 771 cpt_fail_header, __LINE__, strerror(errno)); 772 goto cleanup_dir; 773 } 774 775 if (lock_device()) 776 { 777 PRINT_LOCK_FAIL; 778 goto cleanup_dir; 779 } 780 781 fd = open(filepath, O_CREAT | O_EXCL | O_CLOEXEC); 782 783 if (fd != -1) 784 { 785 printf("%s, line %d: was able to create a new file in a class A directory when locked.\n", 786 cpt_fail_header, __LINE__, strerror(errno)); 787 goto cleanup_file; 788 } 789 790 if (unlock_device(passcode)) 791 { 792 PRINT_UNLOCK_FAIL; 793 goto cleanup_dir; 794 } 795 796 if (SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_B)) 797 { 798 printf("%s, line %d: failed to change directory class from A to B when unlocked, errno = %s.\n", 799 cpt_fail_header, __LINE__, strerror(errno)); 800 goto cleanup_dir; 801 } 802 803 if (lock_device()) 804 { 805 PRINT_LOCK_FAIL; 806 goto cleanup_dir; 807 } 808 809 fd = open(filepath, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC); 810 811 if (fd == -1) 812 { 813 printf("%s, line %d: failed to create new file in class B directory when locked, errno = %s.\n", 814 cpt_fail_header, __LINE__, strerror(errno)); 815 goto cleanup_dir; 816 } 817 818 local_result = GET_PROT_CLASS(fd); 819 820 if (local_result == -1) 821 { 822 printf("%s, line %d: failed to get protection class for a new file when locked, errno = %s.\n", 823 cpt_fail_header, __LINE__, strerror(errno)); 824 goto cleanup_file; 825 } 826 else if (local_result != PROTECTION_CLASS_B) 827 { 828 printf("%s, line %d: new file in class B directory did not inherit protection class.\n", 829 cpt_fail_header, __LINE__, strerror(errno)); 830 goto cleanup_file; 831 } 832 833 /* What happens when we try to create new subdirectories? */ 834 if (unlock_device(passcode)) 835 { 836 PRINT_UNLOCK_FAIL; 837 goto cleanup_file; 838 } 839 840 for (new_prot_class = PROTECTION_CLASS_A; new_prot_class <= PROTECTION_CLASS_E; new_prot_class++) 841 { 842 if (SET_PROT_CLASS(dir_fd, new_prot_class)) 843 { 844 printf("%s, line %d: failed to change directory to class %d, errno = %s.\n", 845 cpt_fail_header, __LINE__, new_prot_class, strerror(errno)); 846 goto cleanup_file; 847 } 848 849 local_result = mkdir(subdirpath, 0x0777); 850 851 if (local_result == -1) 852 { 853 printf("%s, line %d: failed to create subdirectory in class %d directory, errno = %s.\n", 854 cpt_fail_header, __LINE__, new_prot_class, strerror(errno)); 855 goto cleanup_file; 856 } 857 858 subdir_fd = open(subdirpath, O_RDONLY | O_CLOEXEC); 859 860 if (subdir_fd == -1) 861 { 862 printf("%s, line %d: failed to open subdirectory in class %d directory, errno = %s.\n", 863 cpt_fail_header, __LINE__, new_prot_class, strerror(errno)); 864 goto remove_subdir; 865 } 866 867 local_result = GET_PROT_CLASS(subdir_fd); 868 869 if (local_result == -1) 870 { 871 printf("%s, line %d: failed to get class of new subdirectory of class %d directory, errno = %s.\n", 872 cpt_fail_header, __LINE__, new_prot_class, strerror(errno)); 873 goto cleanup_subdir; 874 } 875 else if (local_result != new_prot_class) 876 { 877 printf("%s, line %d: new subdirectory had different class than class %d parent.\n", 878 cpt_fail_header, __LINE__, new_prot_class); 879 goto cleanup_subdir; 880 } 881 882 close(subdir_fd); 883 rmdir(subdirpath); 884 } 885 886 /* If we've made it this far, the test was successful. */ 887 test_result = 0; 888 889cleanup_subdir: 890 close(subdir_fd); 891 892remove_subdir: 893 rmdir(subdirpath); 894 895cleanup_file: 896 close(fd); 897 898remove_file: 899 unlink(filepath); 900 901cleanup_dir: 902 close(dir_fd); 903 904remove_dir: 905 rmdir(dirpath); 906 907remove_passcode: 908 /* Try to unlock the device (no ramifications if it isn't locked when we try) and remove the passcode. */ 909 if (unlock_device(passcode)) 910 { 911 printf("WARNING: failed to unlock the device.\n"); 912 } 913 914 if (clear_passcode(passcode)) 915 { 916 printf("WARNING: failed to clear the passcode.\n"); 917 } 918 919end: 920 return(test_result); 921} 922 923