1 #include <darwintest.h>
2 #include <darwintest_utils.h>
13 #include <IOKit/IOKitLib.h>
14 #include <Kernel/IOKit/crypto/AppleKeyStoreDefs.h>
15 #include <Kernel/sys/content_protection.h>
17 #define CPT_IO_SIZE 4096
18 #define CPT_AKS_BUF_SIZE 256
19 #define CPT_MAX_PASS_LEN 64
21 #define GET_PROT_CLASS(fd) \
22 fcntl((fd), F_GETPROTECTIONCLASS)
24 #define SET_PROT_CLASS(fd, prot_class) \
25 fcntl((fd), F_SETPROTECTIONCLASS, (prot_class))
27 #define KEYSTORECTL_PATH "/usr/local/bin/keystorectl"
28 #define KEYBAGDTEST_PATH "/usr/local/bin/keybagdTest"
29 #define TEMP_DIR_TEMPLATE "/tmp/data_protection_test.XXXXXXXX"
30 #define TEST_PASSCODE "IAmASecurePassword"
35 int g_passcode_set
= 0;
37 char g_test_tempdir
[PATH_MAX
] = TEMP_DIR_TEMPLATE
;
38 char g_filepath
[PATH_MAX
] = "";
39 char g_dirpath
[PATH_MAX
] = "";
40 char g_subdirpath
[PATH_MAX
] = "";
47 size_t input_struct_count
,
49 uint32_t * output_count
51 int spawn_proc(char * const command
[]);
52 int supports_content_prot(void);
53 char* dp_class_num_to_string(int num
);
54 int lock_device(void);
55 int unlock_device(char * passcode
);
56 int set_passcode(char * new_passcode
, char * old_passcode
);
57 int clear_passcode(char * passcode
);
58 int has_passcode(void);
62 T_DECL(data_protection
,
63 "Verify behavior of the various data protection classes") {
64 int local_result
= -1;
65 int new_prot_class
= -1;
66 int old_prot_class
= -1;
68 char rd_buffer
[CPT_IO_SIZE
];
69 char wr_buffer
[CPT_IO_SIZE
];
74 * Ensure we can freely read and change
75 * protection classes when unlocked.
78 new_prot_class
= PROTECTION_CLASS_A
;
79 new_prot_class
<= PROTECTION_CLASS_F
;
83 old_prot_class
= GET_PROT_CLASS(g_fd
),
85 "Get protection class when locked"
89 SET_PROT_CLASS(g_fd
, new_prot_class
),
91 "Should be able to change protection "
92 "from %s to %s while unlocked",
93 dp_class_num_to_string(old_prot_class
),
94 dp_class_num_to_string(new_prot_class
)
98 /* Query the filesystem for the default CP level (Is it C?) */
99 #ifndef F_GETDEFAULTPROTLEVEL
100 #define F_GETDEFAULTPROTLEVEL 79
105 old_prot_class
= fcntl(g_fd
, F_GETDEFAULTPROTLEVEL
),
107 "Get default protection level for filesystem"
110 /* XXX: Do we want to do anything with the level? What should it be? */
113 * files are allowed to move into F, but not out of it. They can also
114 * only do so when they do not have content.
119 /* re-create the file */
122 g_fd
= open(g_filepath
, O_CREAT
|O_EXCL
|O_RDWR
|O_CLOEXEC
),
127 /* Try making a class A file while locked. */
128 T_ASSERT_EQ(lock_device(), 0, "*** Lock device ***");
132 SET_PROT_CLASS(g_fd
, PROTECTION_CLASS_A
),
134 "Should not be able to change protection "
135 "from class D to class A when locked"
137 T_ASSERT_EQ(unlock_device(TEST_PASSCODE
), 0, "*** Unlock device ***");
139 /* Attempt opening/IO to a class A file while unlocked. */
142 SET_PROT_CLASS(g_fd
, PROTECTION_CLASS_A
),
144 "Should be able to change protection "
145 "from class D to class A when unlocked"
152 g_fd
= open(g_filepath
, O_RDWR
|O_CLOEXEC
),
154 "Should be able to open a class A file when unlocked");
157 * TODO: Write specific data we can check for. If we're going to do
158 * that, the write scheme should be deliberately ugly.
162 while(current_byte
< CPT_IO_SIZE
) {
163 local_result
= pwrite(
165 &wr_buffer
[current_byte
],
166 CPT_IO_SIZE
- current_byte
,
174 "Should be able to write to "
175 "a class A file when unlocked"
178 current_byte
+= local_result
;
183 while(current_byte
< CPT_IO_SIZE
) {
184 local_result
= pread(
186 &rd_buffer
[current_byte
],
187 CPT_IO_SIZE
- current_byte
,
195 "Should be able to read from "
196 "a class A file when unlocked"
199 current_byte
+= local_result
;
203 * Again, but now while locked; and try to change the file class
206 T_ASSERT_EQ(lock_device(), 0, "*** Lock device ***");
209 pread(g_fd
, rd_buffer
, CPT_IO_SIZE
, 0),
211 "Should not be able to read from a class A file when locked"
215 pwrite(g_fd
, wr_buffer
, CPT_IO_SIZE
, 0),
217 "Should not be able to write to a class A file when locked"
221 SET_PROT_CLASS(g_fd
, PROTECTION_CLASS_D
),
223 "Should not be able to change protection "
224 "from class A to class D when locked"
227 /* Try to open and truncate the file. */
231 g_fd
= open(g_filepath
, O_RDWR
|O_TRUNC
|O_CLOEXEC
),
233 "Should not be able to open and truncate "
234 "a class A file when locked"
237 /* Try to open the file */
239 g_fd
= open(g_filepath
, O_RDWR
|O_CLOEXEC
),
241 "Should not be able to open a class A file when locked"
244 /* What about class B files? */
245 T_ASSERT_EQ(unlock_device(TEST_PASSCODE
), 0, "*** Unlock device ***");
248 g_fd
= open(g_filepath
, O_RDWR
|O_CLOEXEC
),
250 "Should be able to open a class A file when unlocked"
255 SET_PROT_CLASS(g_fd
, PROTECTION_CLASS_D
),
257 "Should be able to change protection "
258 "class from A to D when unlocked"
261 T_ASSERT_EQ(lock_device(), 0, "*** Lock device ***");
263 /* Can we create a class B file while locked? */
265 SET_PROT_CLASS(g_fd
, PROTECTION_CLASS_B
),
267 "Should be able to change protection "
268 "class from D to B when locked"
272 GET_PROT_CLASS(g_fd
),
274 "File should now have class B protection"
278 * We should also be able to read/write to the
279 * file descriptor while it is open.
283 while(current_byte
< CPT_IO_SIZE
) {
284 local_result
= pwrite(
286 &wr_buffer
[current_byte
],
287 CPT_IO_SIZE
- current_byte
,
295 "Should be able to write to a "
296 "new class B file when locked"
299 current_byte
+= local_result
;
304 while(current_byte
< CPT_IO_SIZE
) {
305 local_result
= pread(
307 &rd_buffer
[current_byte
],
308 CPT_IO_SIZE
- current_byte
,
315 "Should be able to read from a "
316 "new class B file when locked"
319 current_byte
+= local_result
;
322 /* We should not be able to open a class B file under lock. */
326 g_fd
= open(g_filepath
, O_RDWR
|O_CLOEXEC
),
328 "Should not be able to open a class B file when locked"
333 /* We still need to test directory semantics. */
336 mkdir(g_dirpath
, 0x0777),
338 "Should be able to create a new directory when locked"
341 /* The newly created directory should not have a protection class. */
343 g_dir_fd
= open(g_dirpath
, O_RDONLY
|O_CLOEXEC
),
345 "Should be able to open an unclassed directory when locked"
349 GET_PROT_CLASS(g_dir_fd
) == PROTECTION_CLASS_D
||
350 GET_PROT_CLASS(g_dir_fd
) == PROTECTION_CLASS_DIR_NONE
,
351 "Directory protection class sholud be D or NONE"
355 SET_PROT_CLASS(g_dir_fd
, PROTECTION_CLASS_A
),
357 "Should be able to change a directory from "
358 "class D to class A while locked"
362 SET_PROT_CLASS(g_dir_fd
, PROTECTION_CLASS_D
),
364 "Should be able to change a directory from "
365 "class A to class D while locked"
369 * Do all files created in the directory properly inherit the
370 * directory's protection class?
374 strlcpy(g_filepath
, g_dirpath
, PATH_MAX
),
376 "Construct path for file in the directory"
379 strlcat(g_filepath
, "test_file", PATH_MAX
),
381 "Construct path for file in the directory"
385 T_ASSERT_EQ(unlock_device(TEST_PASSCODE
), 0, "*** Unlock device ***");
388 new_prot_class
= PROTECTION_CLASS_A
;
389 new_prot_class
<= PROTECTION_CLASS_D
;
396 old_prot_class
= GET_PROT_CLASS(g_dir_fd
),
398 "Get protection class for the directory"
403 SET_PROT_CLASS(g_dir_fd
, new_prot_class
),
405 "Should be able to change directory "
406 "protection from %s to %s",
407 dp_class_num_to_string(old_prot_class
),
408 dp_class_num_to_string(new_prot_class
)
412 getclass_dir
= GET_PROT_CLASS(g_dir_fd
),
414 "Get protection class for the directory"
419 g_fd
= open(g_filepath
, O_CREAT
|O_EXCL
|O_CLOEXEC
, 0777),
421 "Should be able to create file in "
422 "%s directory when unlocked",
423 dp_class_num_to_string(new_prot_class
)
428 local_result
= GET_PROT_CLASS(g_fd
),
430 "Get the new file's protection class"
436 "File should have %s protection",
437 dp_class_num_to_string(new_prot_class
)
444 /* Do we disallow creation of a class F directory? */
446 SET_PROT_CLASS(g_dir_fd
, PROTECTION_CLASS_F
),
448 "Should not be able to create class F directory"
452 * Are class A and class B semantics followed for when
453 * we create these files during lock?
457 SET_PROT_CLASS(g_dir_fd
, PROTECTION_CLASS_A
),
459 "Should be able to change protection "
460 "from class F to class A when unlocked"
463 T_ASSERT_EQ(lock_device(), 0, "*** Lock device ***");
466 g_fd
= open(g_filepath
, O_CREAT
|O_EXCL
|O_CLOEXEC
, 0777),
468 "Should not be able to create a new file "
469 "in a class A directory when locked"
472 T_ASSERT_EQ(unlock_device(TEST_PASSCODE
), 0, "*** Unlock device ***");
476 SET_PROT_CLASS(g_dir_fd
, PROTECTION_CLASS_B
),
478 "Should be able to change directory "
479 "from class A to class B when unlocked"
482 T_ASSERT_EQ(lock_device(), 0, "*** Lock device ***");
485 g_fd
= open(g_filepath
, O_CREAT
|O_EXCL
|O_RDWR
|O_CLOEXEC
, 0777),
487 "Should be able to create a new file "
488 "in class B directory when locked"
492 local_result
= GET_PROT_CLASS(g_fd
),
494 "Get the new file's protection class"
500 "File should inherit protection class of class B directory"
503 /* What happens when we try to create new subdirectories? */
504 T_ASSERT_EQ(unlock_device(TEST_PASSCODE
), 0, "*** Unlock device ***");
507 new_prot_class
= PROTECTION_CLASS_A
;
508 new_prot_class
<= PROTECTION_CLASS_D
;
513 SET_PROT_CLASS(g_dir_fd
, new_prot_class
),
515 "Change directory to %s",
516 dp_class_num_to_string(new_prot_class
)
521 mkdir(g_subdirpath
, 0x0777),
523 "Create subdirectory in %s directory",
524 dp_class_num_to_string(new_prot_class
)
529 g_subdir_fd
= open(g_subdirpath
, O_RDONLY
|O_CLOEXEC
),
531 "Should be able to open subdirectory in %s directory",
532 dp_class_num_to_string(new_prot_class
)
536 local_result
= GET_PROT_CLASS(g_subdir_fd
),
538 "Get protection class of new subdirectory "
540 dp_class_num_to_string(new_prot_class
)
546 "New subdirectory should have same class as %s parent",
547 dp_class_num_to_string(new_prot_class
)
558 int local_result
= -1;
566 mkdtemp(g_test_tempdir
),
567 "Create temporary directory for test"
569 T_LOG("Test temp dir: %s", g_test_tempdir
);
572 local_result
= supports_content_prot(),
574 "Get content protection support status"
577 if(local_result
== 0) {
578 T_SKIP("Data protection not supported on this system");
584 "Device should not have existing passcode"
588 set_passcode(TEST_PASSCODE
, NULL
),
593 bzero(g_filepath
, PATH_MAX
);
594 bzero(g_dirpath
, PATH_MAX
);
595 bzero(g_subdirpath
, PATH_MAX
);
597 ret
|= (strlcat(g_filepath
, g_test_tempdir
, PATH_MAX
) == PATH_MAX
);
598 ret
|= (strlcat(g_filepath
, "/", PATH_MAX
) == PATH_MAX
);
599 ret
|= (strlcpy(g_dirpath
, g_filepath
, PATH_MAX
) == PATH_MAX
);
600 ret
|= (strlcat(g_filepath
, "test_file", PATH_MAX
) == PATH_MAX
);
601 ret
|= (strlcat(g_dirpath
, "test_dir/", PATH_MAX
) == PATH_MAX
);
602 ret
|= (strlcpy(g_subdirpath
, g_dirpath
, PATH_MAX
) == PATH_MAX
);
603 ret
|= (strlcat(g_subdirpath
, "test_subdir/", PATH_MAX
) == PATH_MAX
);
606 T_ASSERT_EQ(ret
, 0, "Initialize test path strings");
610 g_fd
= open(g_filepath
, O_CREAT
|O_EXCL
|O_RDWR
|O_CLOEXEC
, 0777),
620 T_LOG("Cleaning up…");
622 if(g_subdir_fd
>= 0) {
623 T_LOG("Cleanup: closing fd %d", g_subdir_fd
);
627 if(g_subdirpath
[0]) {
628 T_LOG("Cleanup: removing %s", g_subdirpath
);
633 T_LOG("Cleanup: closing fd %d", g_fd
);
638 T_LOG("Cleanup: removing %s", g_filepath
);
643 T_LOG("Cleanup: closing fd %d", g_dir_fd
);
648 T_LOG("Cleanup: removing %s", g_dirpath
);
652 if(strcmp(g_test_tempdir
, TEMP_DIR_TEMPLATE
)) {
653 T_LOG("Cleanup: removing %s", g_test_tempdir
);
654 rmdir(g_test_tempdir
);
658 T_LOG("Cleanup: unlocking device");
659 if(unlock_device(TEST_PASSCODE
)) {
660 T_LOG("Warning: failed to unlock device in cleanup");
663 T_LOG("Cleanup: clearing passcode");
664 if(clear_passcode(TEST_PASSCODE
)) {
665 T_LOG("Warning: failed to clear passcode in cleanup");
671 set_passcode(char * new_passcode
, char * old_passcode
) {
674 #ifdef KEYBAG_ENTITLEMENTS
675 /* If we're entitled, we can set the passcode ourselves. */
676 uint64_t inputs
[] = {device_keybag_handle
};
677 uint32_t input_count
= (sizeof(inputs
) / sizeof(*inputs
));
678 void * input_structs
= NULL
;
679 size_t input_struct_count
= 0;
680 char buffer
[CPT_AKS_BUF_SIZE
];
681 char * buffer_ptr
= buffer
;
682 uint32_t old_passcode_len
= 0;
683 uint32_t new_passcode_len
= 0;
685 T_LOG("%s(): using keybag entitlements", __func__
);
687 old_passcode_len
= strnlen(old_passcode
, CPT_MAX_PASS_LEN
);
688 new_passcode_len
= strnlen(new_passcode
, CPT_MAX_PASS_LEN
);
690 if((old_passcode
== NULL
) || (old_passcode_len
== CPT_MAX_PASS_LEN
)) {
692 old_passcode_len
= 0;
694 if((new_passcode
== NULL
) || (new_passcode_len
== CPT_MAX_PASS_LEN
)) {
696 new_passcode_len
= 0;
699 *((uint32_t *) buffer_ptr
) = ((uint32_t) 2);
700 buffer_ptr
+= sizeof(uint32_t);
702 *((uint32_t *) buffer_ptr
) = old_passcode_len
;
703 buffer_ptr
+= sizeof(uint32_t);
705 memcpy(buffer_ptr
, old_passcode
, old_passcode_len
);
706 buffer_ptr
+= ((old_passcode_len
+ sizeof(uint32_t) - 1) &
707 ~(sizeof(uint32_t) - 1));
709 *((uint32_t *) buffer_ptr
) = new_passcode_len
;
710 buffer_ptr
+= sizeof(uint32_t);
712 memcpy(buffer_ptr
, new_passcode
, new_passcode_len
);
713 buffer_ptr
+= ((new_passcode_len
+ sizeof(uint32_t) - 1) &
714 ~(sizeof(uint32_t) - 1));
716 input_structs
= buffer
;
717 input_struct_count
= (buffer_ptr
- buffer
);
719 result
= apple_key_store(
720 kAppleKeyStoreKeyBagSetPasscode
,
730 * If we aren't entitled, we'll need to use
731 * keystorectl to set the passcode.
733 T_LOG("%s(): using keystorectl", __func__
);
736 (old_passcode
== NULL
) ||
737 (strnlen(old_passcode
, CPT_MAX_PASS_LEN
) == CPT_MAX_PASS_LEN
)
743 (new_passcode
== NULL
) ||
744 (strnlen(new_passcode
, CPT_MAX_PASS_LEN
) == CPT_MAX_PASS_LEN
)
749 char * const keystorectl_args
[] = {
756 result
= spawn_proc(keystorectl_args
);
757 #endif /* KEYBAG_ENTITLEMENTS */
758 if(result
== 0 && new_passcode
!= NULL
) {
760 } else if(result
== 0 && new_passcode
== NULL
) {
768 clear_passcode(char * passcode
) {
770 * For the moment, this will set the passcode to the empty string
771 * (a known value); this will most likely need to change, or running
772 * this test may ruin everything™
774 return set_passcode(NULL
, passcode
);
779 return set_passcode(NULL
, NULL
);
787 * Pass in the path to keybagdTest instead. By doing this, we bypass
788 * the shortcut to get in to the keybag via IOKit and instead use the
789 * pre-existing command line tool.
791 * This also goes through the normal "lock → locking (10s) → locked"
792 * flow that would normally occuring during system runtime when the
793 * lock button is depressed. To ensure that our single threaded test
794 * works properly in this case, poll until we can't create a class A
797 char * const kbd_args
[] = {KEYBAGDTEST_PATH
, "lock", NULL
};
798 result
= spawn_proc(kbd_args
);
804 * Delete the file if it is present. Note that this may fail if the
805 * file is actually not there. So don't bomb out if we can't delete
806 * this file right now.
808 (void) unlink("/private/var/foo_test_file");
813 dp_fd
= open_dprotected_np(
814 "/private/var/foo_test_file",
821 /* delete it and sleep */
823 result
= unlink("/private/var/foo_test_file");
832 /* drop out of our polling loop. */
838 * Note that our loop breakout condition is whether or not we can
839 * create a class A file, so that loop may execute up to 10 times
840 * (due to the 10s grace period). By the time we get here, we assume
841 * that we didn't hit any of the error cases above.
848 unlock_device(char * passcode
) {
851 #ifdef KEYBAG_ENTITLEMENTS
852 /* If we're entitled, we can unlock the device ourselves. */
853 uint64_t inputs
[] = {device_keybag_handle
};
854 uint32_t input_count
= (sizeof(inputs
) / sizeof(*inputs
));
855 size_t input_struct_count
= 0;
857 T_LOG("%s(): using keybag entitlements", __func__
);
859 input_struct_count
= strnlen(passcode
, CPT_MAX_PASS_LEN
);
860 if((passcode
== NULL
) || (input_struct_count
== CPT_MAX_PASS_LEN
)) {
862 input_struct_count
= 0;
865 result
= apple_key_store(
866 kAppleKeyStoreKeyBagUnlock
,
876 * If we aren't entitled, we'll need to use
877 * keystorectl to unlock the device.
879 T_LOG("%s(): using keystorectl", __func__
);
882 (passcode
== NULL
) ||
883 (strnlen(passcode
, CPT_MAX_PASS_LEN
) == CPT_MAX_PASS_LEN
)
888 char * const keystorectl_args
[] = {
889 KEYSTORECTL_PATH
, "unlock", passcode
, NULL
892 result
= spawn_proc(keystorectl_args
);
893 #endif /* KEYBAG_ENTITLEMENTS */
899 * Code based on Mobile Key Bag; specifically
900 * MKBDeviceSupportsContentProtection and
901 * MKBDeviceFormattedForContentProtection.
903 * We want to verify that we support content protection, and that
904 * we are formatted for it.
907 supports_content_prot(void) {
908 int local_result
= -1;
910 uint32_t buffer_size
= 1;
911 char buffer
[buffer_size
];
912 io_registry_entry_t defaults
= IO_OBJECT_NULL
;
913 kern_return_t k_result
= KERN_FAILURE
;
914 struct statfs statfs_results
;
916 defaults
= IORegistryEntryFromPath(
917 kIOMasterPortDefault
,
918 kIODeviceTreePlane
":/defaults"
921 if(defaults
== IO_OBJECT_NULL
) {
922 /* Assume data protection is unsupported */
924 "%s(): no defaults entry in IORegistry",
930 k_result
= IORegistryEntryGetProperty(
937 if(k_result
!= KERN_SUCCESS
) {
938 /* Assume data protection is unsupported */
940 "%s(): no content-protect property in IORegistry",
947 * At this point, we SUPPORT content protection… but are we
948 * formatted for it? This is ugly; we should be testing the file
949 * system we'll be testing in, not just /tmp/.
951 local_result
= statfs(g_test_tempdir
, &statfs_results
);
953 if(local_result
== -1) {
955 "%s(): failed to statfs the test directory, errno = %s",
956 __func__
, strerror(errno
)
959 } else if(statfs_results
.f_flags
& MNT_CPROTECT
) {
963 "%s(): filesystem not formatted for data protection",
971 * Shamelessly ripped from keystorectl routines;
972 * a wrapper for invoking the AKS user client.
975 apple_key_store(uint32_t command
,
977 uint32_t input_count
,
978 void * input_structs
,
979 size_t input_struct_count
,
981 uint32_t * output_count
) {
983 io_connect_t connection
= IO_OBJECT_NULL
;
984 io_registry_entry_t apple_key_bag_service
= IO_OBJECT_NULL
;
985 kern_return_t k_result
= KERN_FAILURE
;
986 IOReturn io_result
= IO_OBJECT_NULL
;
988 apple_key_bag_service
= IOServiceGetMatchingService(
989 kIOMasterPortDefault
,
990 IOServiceMatching(kAppleKeyStoreServiceName
)
992 if(apple_key_bag_service
== IO_OBJECT_NULL
) {
994 "%s: failed to match kAppleKeyStoreServiceName",
1000 k_result
= IOServiceOpen(
1001 apple_key_bag_service
,
1006 if(k_result
!= KERN_SUCCESS
) {
1008 "%s: failed to open AppleKeyStore: "
1009 "IOServiceOpen() returned %d",
1015 k_result
= IOConnectCallMethod(
1017 kAppleKeyStoreUserClientOpen
,
1018 NULL
, 0, NULL
, 0, NULL
, NULL
, NULL
, NULL
1020 if(k_result
!= KERN_SUCCESS
) {
1022 "%s: call to AppleKeyStore method "
1023 "kAppleKeyStoreUserClientOpen failed",
1029 io_result
= IOConnectCallMethod(
1030 connection
, command
, inputs
, input_count
, input_structs
,
1031 input_struct_count
, outputs
, output_count
, NULL
, NULL
1033 if(io_result
!= kIOReturnSuccess
) {
1034 T_LOG("%s: call to AppleKeyStore method %d failed", __func__
);
1041 IOServiceClose(apple_key_bag_service
);
1047 * Helper function for launching tools
1050 spawn_proc(char * const command
[]) {
1052 int launch_tool_ret
= 0;
1053 bool waitpid_ret
= true;
1058 launch_tool_ret
= dt_launch_tool(&pid
, command
, false, NULL
, NULL
);
1059 T_EXPECT_EQ(launch_tool_ret
, 0, "launch tool: %s", command
[0]);
1060 if(launch_tool_ret
!= 0) {
1064 waitpid_ret
= dt_waitpid(pid
, &status
, &signal
, timeout
);
1065 T_EXPECT_TRUE(waitpid_ret
, "%s should succeed", command
[0]);
1066 if(waitpid_ret
== false) {
1068 T_LOG("%s exited %d", command
[0], status
);
1071 T_LOG("%s received signal %d", command
[0], signal
);
1080 dp_class_num_to_string(int num
) {
1084 case PROTECTION_CLASS_A
:
1086 case PROTECTION_CLASS_B
:
1088 case PROTECTION_CLASS_C
:
1090 case PROTECTION_CLASS_D
:
1092 case PROTECTION_CLASS_E
:
1094 case PROTECTION_CLASS_F
:
1097 return "<unknown class>";
1102 int device_lock_state(void) {
1104 * TODO: Actually implement this.
1106 * We fail if a passcode already exists, and the methods being used
1107 * to lock/unlock the device in this test appear to be synchronous…
1108 * do we need this function?
1115 /* Determines if we will try to test class C semanatics. */
1116 int unlocked_since_boot() {
1118 * TODO: Actually implement this.
1120 * The actual semantics for CP mean that even with this primative,
1121 * we would need to set a passcode and then reboot the device in
1122 * order to test this; this function will probably be rather
1123 * worthless as a result.