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, 0777); 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 /* Query the filesystem for the default CP level (Is it C?) */ 450#ifndef F_GETDEFAULTPROTLEVEL 451#define F_GETDEFAULTPROTLEVEL 79 452#endif 453 454 old_prot_class = fcntl(fd, F_GETDEFAULTPROTLEVEL); 455 if (old_prot_class == -1) { 456 printf("%s , line %d: failed to acquire default protection level for filesystem , errno = %s \n", 457 cpt_fail_header, __LINE__, strerror(errno)); 458 goto cleanup_file; 459 } 460 461 /* XXX: Do we want to do anything with the level? What should it be? */ 462 463 464 /* 465 * files are allowed to move into F, but not out of it. They can also only do so 466 * when they do not have content. 467 */ 468 close (fd); 469 unlink (filepath); 470 471 472 /* re-create the file */ 473 fd = open (filepath, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC); 474 if (fd == -1) { 475 printf("%s, line %d: failed to create the test file, errno = %s.\n", 476 cpt_fail_header, __LINE__, strerror(errno)); 477 goto cleanup_file; 478 } 479 480 /* Try making a class A file while locked. */ 481 if (lock_device()) 482 { 483 PRINT_LOCK_FAIL; 484 goto cleanup_file; 485 } 486 487 if (!SET_PROT_CLASS(fd, PROTECTION_CLASS_A)) 488 { 489 printf("%s, line %d: was able to change protection class from D to A when locked.\n", 490 cpt_fail_header, __LINE__); 491 goto cleanup_file; 492 } 493 494 if (unlock_device(passcode)) 495 { 496 PRINT_UNLOCK_FAIL; 497 goto cleanup_file; 498 } 499 500 /* Attempt opening/IO to a class A file while unlocked. */ 501 if (SET_PROT_CLASS(fd, PROTECTION_CLASS_A)) 502 { 503 printf("%s, line %d: failed to change protection class from D to A when unlocked, errno = %s.\n", 504 cpt_fail_header, __LINE__, strerror(errno)); 505 goto cleanup_file; 506 } 507 508 close(fd); 509 fd = open(filepath, O_RDWR | O_CLOEXEC); 510 511 if (fd == -1) 512 { 513 printf("%s, line %d: failed to open a class A file when unlocked, errno = %s.\n", 514 cpt_fail_header, __LINE__, strerror(errno)); 515 goto remove_file; 516 } 517 518 /* TODO: Write specific data we can check for. 519 If we're going to do that, the write scheme should be deliberately ugly. */ 520 current_byte = 0; 521 522 while (current_byte < CPT_IO_SIZE) 523 { 524 local_result = pwrite(fd, &wr_buffer[current_byte], CPT_IO_SIZE - current_byte, current_byte); 525 526 if (local_result == -1) 527 { 528 printf("%s, line %d: failed to write to class A file when unlocked, errno = %s.\n", 529 cpt_fail_header, __LINE__, strerror(errno)); 530 goto cleanup_file; 531 } 532 533 current_byte += local_result; 534 } 535 536 current_byte = 0; 537 538 while (current_byte < CPT_IO_SIZE) 539 { 540 local_result = pread(fd, &rd_buffer[current_byte], CPT_IO_SIZE - current_byte, current_byte); 541 542 if (local_result == -1) 543 { 544 printf("%s, line %d: failed to read from class A file when unlocked, errno = %s.\n", 545 cpt_fail_header, __LINE__, strerror(errno)); 546 goto cleanup_file; 547 } 548 549 current_byte += local_result; 550 } 551 552 /* Again, but now while locked; and try to change the file class as well. */ 553 if (lock_device()) 554 { 555 PRINT_LOCK_FAIL; 556 goto cleanup_file; 557 } 558 559 if (pread(fd, rd_buffer, CPT_IO_SIZE, 0) > 0) 560 { 561 printf("%s, line %d: was able to read from a class A file when locked.\n", 562 cpt_fail_header, __LINE__); 563 goto cleanup_file; 564 } 565 566 if (pwrite(fd, wr_buffer, CPT_IO_SIZE, 0) > 0) 567 { 568 printf("%s, line %d: was able to write to a class A file when locked.\n", 569 cpt_fail_header, __LINE__); 570 goto cleanup_file; 571 } 572 573 if (!SET_PROT_CLASS(fd, PROTECTION_CLASS_D)) 574 { 575 printf("%s, line %d: was able to change protection class from A to D when locked.\n", 576 cpt_fail_header, __LINE__); 577 goto cleanup_file; 578 } 579 580 /* Try to open and truncate the file. */ 581 close(fd); 582 fd = open(filepath, O_RDWR | O_TRUNC | O_CLOEXEC); 583 584 if (fd != -1) 585 { 586 printf("%s, line %d: was able to open and truncate a class A file when locked.\n", 587 cpt_fail_header, __LINE__); 588 goto cleanup_file; 589 } 590 591 /* Try to open the file */ 592 fd = open(filepath, O_RDWR | O_CLOEXEC); 593 594 if (fd != -1) 595 { 596 printf("%s, line %d: was able to open a class A file when locked.\n", 597 cpt_fail_header, __LINE__); 598 goto cleanup_file; 599 } 600 601 /* What about class B files? */ 602 if (unlock_device(passcode)) 603 { 604 PRINT_UNLOCK_FAIL; 605 goto cleanup_file; 606 } 607 608 fd = open(filepath, O_RDWR | O_CLOEXEC); 609 610 if (fd == -1) 611 { 612 printf("%s, line %d: was unable to open a class A file when unlocked.\n", 613 cpt_fail_header, __LINE__); 614 goto cleanup_file; 615 } 616 617 if (SET_PROT_CLASS(fd, PROTECTION_CLASS_D)) 618 { 619 printf("%s, line %d: failed to change protection class from A to D when unlocked, errno = %s.\n", 620 cpt_fail_header, __LINE__, strerror(errno)); 621 goto cleanup_file; 622 } 623 624 if (lock_device()) 625 { 626 PRINT_LOCK_FAIL; 627 goto cleanup_file; 628 } 629 630 /* Can we create a class B file while locked? */ 631 if (SET_PROT_CLASS(fd, PROTECTION_CLASS_B)) 632 { 633 printf("%s, line %d: failed to change protection class from D to B when locked, errno = %s.\n", 634 cpt_fail_header, __LINE__, strerror(errno)); 635 goto cleanup_file; 636 } 637 638 if (GET_PROT_CLASS (fd) != PROTECTION_CLASS_B) { 639 printf("%s, line %d: Failed to switch to class B file \n", 640 cpt_fail_header, __LINE__ ); 641 goto cleanup_file; 642 } 643 644 645 /* We should also be able to read/write to the file descriptor while it is open. */ 646 current_byte = 0; 647 648 while (current_byte < CPT_IO_SIZE) 649 { 650 local_result = pwrite(fd, &wr_buffer[current_byte], CPT_IO_SIZE - current_byte, current_byte); 651 652 if (local_result == -1) 653 { 654 printf("%s, line %d: failed to write to new class B file when locked, errno = %s.\n", 655 cpt_fail_header, __LINE__, strerror(errno)); 656 goto cleanup_file; 657 } 658 659 current_byte += local_result; 660 } 661 662 current_byte = 0; 663 664 while (current_byte < CPT_IO_SIZE) 665 { 666 local_result = pread(fd, &rd_buffer[current_byte], CPT_IO_SIZE - current_byte, current_byte); 667 668 if (local_result == -1) 669 { 670 printf("%s, line %d: failed to read from new class B file when locked, errno = %s.\n", 671 cpt_fail_header, __LINE__, strerror(errno)); 672 goto cleanup_file; 673 } 674 675 current_byte += local_result; 676 } 677 678 /* We should not be able to open a class B file under lock. */ 679 close(fd); 680 fd = open(filepath, O_RDWR | O_CLOEXEC); 681 682 if (fd != -1) 683 { 684 printf("%s, line %d: was able to open a class B file when locked.\n", 685 cpt_fail_header, __LINE__); 686 goto cleanup_file; 687 } 688 689 unlink(filepath); 690 691 /* We still need to test directory semantics. */ 692 if (mkdir(dirpath, 0x0777) == -1) 693 { 694 printf("%s, line %d: failed to create a new directory when locked, errno = %s.\n", 695 cpt_fail_header, __LINE__, strerror(errno)); 696 goto remove_passcode; 697 } 698 699 /* The newly created directory should not have a protection class. */ 700 dir_fd = open(dirpath, O_RDONLY | O_CLOEXEC); 701 702 if (dir_fd == -1) 703 { 704 printf("%s, line %d: failed to open an unclassed directory when locked, errno = %s.\n", 705 cpt_fail_header, __LINE__, strerror(errno)); 706 goto remove_dir; 707 } 708 709 if ((GET_PROT_CLASS(dir_fd) != PROTECTION_CLASS_D) && (GET_PROT_CLASS(dir_fd) != PROTECTION_CLASS_DIR_NONE)) 710 { 711 printf("%s, line %d: newly created directory had a non-D and non-NONE protection class.\n", 712 cpt_fail_header, __LINE__); 713 goto cleanup_dir; 714 } 715 716 if (SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_A)) 717 { 718 printf("%s, line %d: was unable to change a directory from class D to class A during lock.\n", 719 cpt_fail_header, __LINE__); 720 goto cleanup_dir; 721 } 722 723 if (SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_D)) 724 { 725 printf("%s, line %d: failed to change a directory from class A to class D during lock, errno = %s.\n", 726 cpt_fail_header, __LINE__, strerror(errno)); 727 goto cleanup_dir; 728 } 729 730 /* Do all files created in the directory properly inherit the directory's protection class? */ 731 if ((strlcpy(filepath, dirpath, PATH_MAX) == PATH_MAX) || (strlcat(filepath, "cpt_test_file", PATH_MAX) == PATH_MAX)) 732 { 733 printf("%s, line %d: failed to construct the path for a file in the directory.\n", 734 cpt_fail_header, __LINE__); 735 goto cleanup_dir; 736 } 737 738 if (unlock_device(passcode)) 739 { 740 PRINT_UNLOCK_FAIL; 741 goto cleanup_dir; 742 } 743 744 for (new_prot_class = PROTECTION_CLASS_A; new_prot_class <= PROTECTION_CLASS_D; new_prot_class++) 745 { 746 int getclass_dir; 747 old_prot_class = GET_PROT_CLASS(dir_fd); 748 749 if (old_prot_class == -1) 750 { 751 printf("%s, line %d: failed to get the protection class for the directory, errno = %s.\n", 752 cpt_fail_header, __LINE__, strerror(errno)); 753 goto cleanup_dir; 754 } 755 756 if (SET_PROT_CLASS(dir_fd, new_prot_class)) 757 { 758 printf("%s, line %d: failed to change the protection class for the directory from %d to %d, errno = %s.\n", 759 cpt_fail_header, __LINE__, old_prot_class, new_prot_class, strerror(errno)); 760 goto cleanup_dir; 761 } 762 763 getclass_dir = GET_PROT_CLASS(dir_fd); 764 if (getclass_dir != new_prot_class) { 765 printf("%s, line %d: failed to get the new protection class for the directory %d (got %d) \n", 766 cpt_fail_header, __LINE__, new_prot_class, getclass_dir); 767 goto cleanup_dir; 768 769 } 770 771 fd = open(filepath, O_CREAT | O_EXCL | O_CLOEXEC, 0777); 772 773 if (fd == -1) 774 { 775 printf("%s, line %d: failed to create a file in a class %d directory when unlocked, errno = %s.\n", 776 cpt_fail_header, __LINE__, new_prot_class, strerror(errno)); 777 goto cleanup_dir; 778 } 779 780 local_result = GET_PROT_CLASS(fd); 781 782 if (local_result == -1) 783 { 784 printf("%s, line %d: failed to get the new file's protection class, errno = %s.\n", 785 cpt_fail_header, __LINE__, strerror(errno)); 786 goto cleanup_file; 787 } 788 else if (local_result != new_prot_class) 789 { 790 791 printf("%s, line %d: new file (%d) did not inherit the directory's protection class (%d) .\n", 792 cpt_fail_header, __LINE__, local_result, new_prot_class); 793 goto cleanup_file; 794 } 795 796 close(fd); 797 unlink(filepath); 798 } 799 800 /* Do we disallow creation of a class F directory? */ 801 if (!SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_F)) 802 { 803 printf("%s, line %d: creation of a class F directory did not fail as expected.\n", 804 cpt_fail_header, __LINE__); 805 goto cleanup_dir; 806 } 807 808 /* And are class A and class B semantics followed for when we create these files during lock? */ 809 if (SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_A)) 810 { 811 printf("%s, line %d: failed to change directory class from F to A when unlocked, errno = %s.\n", 812 cpt_fail_header, __LINE__, strerror(errno)); 813 goto cleanup_dir; 814 } 815 816 if (lock_device()) 817 { 818 PRINT_LOCK_FAIL; 819 goto cleanup_dir; 820 } 821 822 fd = open(filepath, O_CREAT | O_EXCL | O_CLOEXEC, 0777); 823 824 if (fd != -1) 825 { 826 printf("%s, line %d: was able to create a new file in a class A directory when locked.\n", 827 cpt_fail_header, __LINE__, strerror(errno)); 828 goto cleanup_file; 829 } 830 831 if (unlock_device(passcode)) 832 { 833 PRINT_UNLOCK_FAIL; 834 goto cleanup_dir; 835 } 836 837 if (SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_B)) 838 { 839 printf("%s, line %d: failed to change directory class from A to B when unlocked, errno = %s.\n", 840 cpt_fail_header, __LINE__, strerror(errno)); 841 goto cleanup_dir; 842 } 843 844 if (lock_device()) 845 { 846 PRINT_LOCK_FAIL; 847 goto cleanup_dir; 848 } 849 850 fd = open(filepath, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0777); 851 852 if (fd == -1) 853 { 854 printf("%s, line %d: failed to create new file in class B directory when locked, errno = %s.\n", 855 cpt_fail_header, __LINE__, strerror(errno)); 856 goto cleanup_dir; 857 } 858 859 local_result = GET_PROT_CLASS(fd); 860 861 if (local_result == -1) 862 { 863 printf("%s, line %d: failed to get protection class for a new file when locked, errno = %s.\n", 864 cpt_fail_header, __LINE__, strerror(errno)); 865 goto cleanup_file; 866 } 867 else if (local_result != PROTECTION_CLASS_B) 868 { 869 printf("%s, line %d: new file in class B directory did not inherit protection class.\n", 870 cpt_fail_header, __LINE__, strerror(errno)); 871 goto cleanup_file; 872 } 873 874 /* What happens when we try to create new subdirectories? */ 875 if (unlock_device(passcode)) 876 { 877 PRINT_UNLOCK_FAIL; 878 goto cleanup_file; 879 } 880 881 for (new_prot_class = PROTECTION_CLASS_A; new_prot_class <= PROTECTION_CLASS_D; new_prot_class++) 882 { 883 if (SET_PROT_CLASS(dir_fd, new_prot_class)) 884 { 885 printf("%s, line %d: failed to change directory to class %d, errno = %s.\n", 886 cpt_fail_header, __LINE__, new_prot_class, strerror(errno)); 887 goto cleanup_file; 888 } 889 890 local_result = mkdir(subdirpath, 0x0777); 891 892 if (local_result == -1) 893 { 894 printf("%s, line %d: failed to create subdirectory in class %d directory, errno = %s.\n", 895 cpt_fail_header, __LINE__, new_prot_class, strerror(errno)); 896 goto cleanup_file; 897 } 898 899 subdir_fd = open(subdirpath, O_RDONLY | O_CLOEXEC); 900 901 if (subdir_fd == -1) 902 { 903 printf("%s, line %d: failed to open subdirectory in class %d directory, errno = %s.\n", 904 cpt_fail_header, __LINE__, new_prot_class, strerror(errno)); 905 goto remove_subdir; 906 } 907 908 local_result = GET_PROT_CLASS(subdir_fd); 909 910 if (local_result == -1) 911 { 912 printf("%s, line %d: failed to get class of new subdirectory of class %d directory, errno = %s.\n", 913 cpt_fail_header, __LINE__, new_prot_class, strerror(errno)); 914 goto cleanup_subdir; 915 } 916 else if (local_result != new_prot_class) 917 { 918 printf("%s, line %d: new subdirectory had different class than class %d parent.\n", 919 cpt_fail_header, __LINE__, new_prot_class); 920 goto cleanup_subdir; 921 } 922 923 close(subdir_fd); 924 rmdir(subdirpath); 925 } 926 927 /* If we've made it this far, the test was successful. */ 928 test_result = 0; 929 930cleanup_subdir: 931 close(subdir_fd); 932 933remove_subdir: 934 rmdir(subdirpath); 935 936cleanup_file: 937 close(fd); 938 939remove_file: 940 unlink(filepath); 941 942cleanup_dir: 943 close(dir_fd); 944 945remove_dir: 946 rmdir(dirpath); 947 948remove_passcode: 949 /* Try to unlock the device (no ramifications if it isn't locked when we try) and remove the passcode. */ 950 if (unlock_device(passcode)) 951 { 952 printf("WARNING: failed to unlock the device.\n"); 953 } 954 955 if (clear_passcode(passcode)) 956 { 957 printf("WARNING: failed to clear the passcode.\n"); 958 } 959 960end: 961 return(test_result); 962} 963 964