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
)
559 int local_result
= -1;
567 mkdtemp(g_test_tempdir
),
568 "Create temporary directory for test"
570 T_LOG("Test temp dir: %s", g_test_tempdir
);
573 local_result
= supports_content_prot(),
575 "Get content protection support status"
578 if (local_result
== 0) {
579 T_SKIP("Data protection not supported on this system");
585 "Device should not have existing passcode"
589 set_passcode(TEST_PASSCODE
, NULL
),
594 bzero(g_filepath
, PATH_MAX
);
595 bzero(g_dirpath
, PATH_MAX
);
596 bzero(g_subdirpath
, PATH_MAX
);
598 ret
|= (strlcat(g_filepath
, g_test_tempdir
, PATH_MAX
) == PATH_MAX
);
599 ret
|= (strlcat(g_filepath
, "/", PATH_MAX
) == PATH_MAX
);
600 ret
|= (strlcpy(g_dirpath
, g_filepath
, PATH_MAX
) == PATH_MAX
);
601 ret
|= (strlcat(g_filepath
, "test_file", PATH_MAX
) == PATH_MAX
);
602 ret
|= (strlcat(g_dirpath
, "test_dir/", PATH_MAX
) == PATH_MAX
);
603 ret
|= (strlcpy(g_subdirpath
, g_dirpath
, PATH_MAX
) == PATH_MAX
);
604 ret
|= (strlcat(g_subdirpath
, "test_subdir/", PATH_MAX
) == PATH_MAX
);
607 T_ASSERT_EQ(ret
, 0, "Initialize test path strings");
611 g_fd
= open(g_filepath
, O_CREAT
| O_EXCL
| O_RDWR
| O_CLOEXEC
, 0777),
622 T_LOG("Cleaning up…");
624 if (g_subdir_fd
>= 0) {
625 T_LOG("Cleanup: closing fd %d", g_subdir_fd
);
629 if (g_subdirpath
[0]) {
630 T_LOG("Cleanup: removing %s", g_subdirpath
);
635 T_LOG("Cleanup: closing fd %d", g_fd
);
640 T_LOG("Cleanup: removing %s", g_filepath
);
645 T_LOG("Cleanup: closing fd %d", g_dir_fd
);
650 T_LOG("Cleanup: removing %s", g_dirpath
);
654 if (strcmp(g_test_tempdir
, TEMP_DIR_TEMPLATE
)) {
655 T_LOG("Cleanup: removing %s", g_test_tempdir
);
656 rmdir(g_test_tempdir
);
659 if (g_passcode_set
) {
660 T_LOG("Cleanup: unlocking device");
661 if (unlock_device(TEST_PASSCODE
)) {
662 T_LOG("Warning: failed to unlock device in cleanup");
665 T_LOG("Cleanup: clearing passcode");
666 if (clear_passcode(TEST_PASSCODE
)) {
667 T_LOG("Warning: failed to clear passcode in cleanup");
673 set_passcode(char * new_passcode
, char * old_passcode
)
677 #ifdef KEYBAG_ENTITLEMENTS
678 /* If we're entitled, we can set the passcode ourselves. */
679 uint64_t inputs
[] = {device_keybag_handle
};
680 uint32_t input_count
= (sizeof(inputs
) / sizeof(*inputs
));
681 void * input_structs
= NULL
;
682 size_t input_struct_count
= 0;
683 char buffer
[CPT_AKS_BUF_SIZE
];
684 char * buffer_ptr
= buffer
;
685 uint32_t old_passcode_len
= 0;
686 uint32_t new_passcode_len
= 0;
688 T_LOG("%s(): using keybag entitlements", __func__
);
690 old_passcode_len
= strnlen(old_passcode
, CPT_MAX_PASS_LEN
);
691 new_passcode_len
= strnlen(new_passcode
, CPT_MAX_PASS_LEN
);
693 if ((old_passcode
== NULL
) || (old_passcode_len
== CPT_MAX_PASS_LEN
)) {
695 old_passcode_len
= 0;
697 if ((new_passcode
== NULL
) || (new_passcode_len
== CPT_MAX_PASS_LEN
)) {
699 new_passcode_len
= 0;
702 *((uint32_t *) buffer_ptr
) = ((uint32_t) 2);
703 buffer_ptr
+= sizeof(uint32_t);
705 *((uint32_t *) buffer_ptr
) = old_passcode_len
;
706 buffer_ptr
+= sizeof(uint32_t);
708 memcpy(buffer_ptr
, old_passcode
, old_passcode_len
);
709 buffer_ptr
+= ((old_passcode_len
+ sizeof(uint32_t) - 1) &
710 ~(sizeof(uint32_t) - 1));
712 *((uint32_t *) buffer_ptr
) = new_passcode_len
;
713 buffer_ptr
+= sizeof(uint32_t);
715 memcpy(buffer_ptr
, new_passcode
, new_passcode_len
);
716 buffer_ptr
+= ((new_passcode_len
+ sizeof(uint32_t) - 1) &
717 ~(sizeof(uint32_t) - 1));
719 input_structs
= buffer
;
720 input_struct_count
= (buffer_ptr
- buffer
);
722 result
= apple_key_store(
723 kAppleKeyStoreKeyBagSetPasscode
,
733 * If we aren't entitled, we'll need to use
734 * keystorectl to set the passcode.
736 T_LOG("%s(): using keystorectl", __func__
);
739 (old_passcode
== NULL
) ||
740 (strnlen(old_passcode
, CPT_MAX_PASS_LEN
) == CPT_MAX_PASS_LEN
)
746 (new_passcode
== NULL
) ||
747 (strnlen(new_passcode
, CPT_MAX_PASS_LEN
) == CPT_MAX_PASS_LEN
)
752 char * const keystorectl_args
[] = {
759 result
= spawn_proc(keystorectl_args
);
760 #endif /* KEYBAG_ENTITLEMENTS */
761 if (result
== 0 && new_passcode
!= NULL
) {
763 } else if (result
== 0 && new_passcode
== NULL
) {
771 clear_passcode(char * passcode
)
774 * For the moment, this will set the passcode to the empty string
775 * (a known value); this will most likely need to change, or running
776 * this test may ruin everything™
778 return set_passcode(NULL
, passcode
);
784 return set_passcode(NULL
, NULL
);
793 * Pass in the path to keybagdTest instead. By doing this, we bypass
794 * the shortcut to get in to the keybag via IOKit and instead use the
795 * pre-existing command line tool.
797 * This also goes through the normal "lock → locking (10s) → locked"
798 * flow that would normally occuring during system runtime when the
799 * lock button is depressed. To ensure that our single threaded test
800 * works properly in this case, poll until we can't create a class A
803 char * const kbd_args
[] = {KEYBAGDTEST_PATH
, "lock", NULL
};
804 result
= spawn_proc(kbd_args
);
810 * Delete the file if it is present. Note that this may fail if the
811 * file is actually not there. So don't bomb out if we can't delete
812 * this file right now.
814 (void) unlink("/private/var/foo_test_file");
819 dp_fd
= open_dprotected_np(
820 "/private/var/foo_test_file",
827 /* delete it and sleep */
829 result
= unlink("/private/var/foo_test_file");
838 /* drop out of our polling loop. */
844 * Note that our loop breakout condition is whether or not we can
845 * create a class A file, so that loop may execute up to 10 times
846 * (due to the 10s grace period). By the time we get here, we assume
847 * that we didn't hit any of the error cases above.
854 unlock_device(char * passcode
)
858 #ifdef KEYBAG_ENTITLEMENTS
859 /* If we're entitled, we can unlock the device ourselves. */
860 uint64_t inputs
[] = {device_keybag_handle
};
861 uint32_t input_count
= (sizeof(inputs
) / sizeof(*inputs
));
862 size_t input_struct_count
= 0;
864 T_LOG("%s(): using keybag entitlements", __func__
);
866 input_struct_count
= strnlen(passcode
, CPT_MAX_PASS_LEN
);
867 if ((passcode
== NULL
) || (input_struct_count
== CPT_MAX_PASS_LEN
)) {
869 input_struct_count
= 0;
872 result
= apple_key_store(
873 kAppleKeyStoreKeyBagUnlock
,
883 * If we aren't entitled, we'll need to use
884 * keystorectl to unlock the device.
886 T_LOG("%s(): using keystorectl", __func__
);
889 (passcode
== NULL
) ||
890 (strnlen(passcode
, CPT_MAX_PASS_LEN
) == CPT_MAX_PASS_LEN
)
895 char * const keystorectl_args
[] = {
896 KEYSTORECTL_PATH
, "unlock", passcode
, NULL
899 result
= spawn_proc(keystorectl_args
);
900 #endif /* KEYBAG_ENTITLEMENTS */
906 * Code based on Mobile Key Bag; specifically
907 * MKBDeviceSupportsContentProtection and
908 * MKBDeviceFormattedForContentProtection.
910 * We want to verify that we support content protection, and that
911 * we are formatted for it.
914 supports_content_prot(void)
916 int local_result
= -1;
918 uint32_t buffer_size
= 1;
919 char buffer
[buffer_size
];
920 io_registry_entry_t defaults
= IO_OBJECT_NULL
;
921 kern_return_t k_result
= KERN_FAILURE
;
922 struct statfs statfs_results
;
924 defaults
= IORegistryEntryFromPath(
925 kIOMasterPortDefault
,
926 kIODeviceTreePlane
":/defaults"
929 if (defaults
== IO_OBJECT_NULL
) {
930 /* Assume data protection is unsupported */
932 "%s(): no defaults entry in IORegistry",
938 k_result
= IORegistryEntryGetProperty(
945 if (k_result
!= KERN_SUCCESS
) {
946 /* Assume data protection is unsupported */
948 "%s(): no content-protect property in IORegistry",
955 * At this point, we SUPPORT content protection… but are we
956 * formatted for it? This is ugly; we should be testing the file
957 * system we'll be testing in, not just /tmp/.
959 local_result
= statfs(g_test_tempdir
, &statfs_results
);
961 if (local_result
== -1) {
963 "%s(): failed to statfs the test directory, errno = %s",
964 __func__
, strerror(errno
)
967 } else if (statfs_results
.f_flags
& MNT_CPROTECT
) {
971 "%s(): filesystem not formatted for data protection",
979 * Shamelessly ripped from keystorectl routines;
980 * a wrapper for invoking the AKS user client.
983 apple_key_store(uint32_t command
,
985 uint32_t input_count
,
986 void * input_structs
,
987 size_t input_struct_count
,
989 uint32_t * output_count
)
992 io_connect_t connection
= IO_OBJECT_NULL
;
993 io_registry_entry_t apple_key_bag_service
= IO_OBJECT_NULL
;
994 kern_return_t k_result
= KERN_FAILURE
;
995 IOReturn io_result
= IO_OBJECT_NULL
;
997 apple_key_bag_service
= IOServiceGetMatchingService(
998 kIOMasterPortDefault
,
999 IOServiceMatching(kAppleKeyStoreServiceName
)
1001 if (apple_key_bag_service
== IO_OBJECT_NULL
) {
1003 "%s: failed to match kAppleKeyStoreServiceName",
1009 k_result
= IOServiceOpen(
1010 apple_key_bag_service
,
1015 if (k_result
!= KERN_SUCCESS
) {
1017 "%s: failed to open AppleKeyStore: "
1018 "IOServiceOpen() returned %d",
1024 k_result
= IOConnectCallMethod(
1026 kAppleKeyStoreUserClientOpen
,
1027 NULL
, 0, NULL
, 0, NULL
, NULL
, NULL
, NULL
1029 if (k_result
!= KERN_SUCCESS
) {
1031 "%s: call to AppleKeyStore method "
1032 "kAppleKeyStoreUserClientOpen failed",
1038 io_result
= IOConnectCallMethod(
1039 connection
, command
, inputs
, input_count
, input_structs
,
1040 input_struct_count
, outputs
, output_count
, NULL
, NULL
1042 if (io_result
!= kIOReturnSuccess
) {
1043 T_LOG("%s: call to AppleKeyStore method %d failed", __func__
);
1050 IOServiceClose(apple_key_bag_service
);
1056 * Helper function for launching tools
1059 spawn_proc(char * const command
[])
1062 int launch_tool_ret
= 0;
1063 bool waitpid_ret
= true;
1068 launch_tool_ret
= dt_launch_tool(&pid
, command
, false, NULL
, NULL
);
1069 T_EXPECT_EQ(launch_tool_ret
, 0, "launch tool: %s", command
[0]);
1070 if (launch_tool_ret
!= 0) {
1074 waitpid_ret
= dt_waitpid(pid
, &status
, &signal
, timeout
);
1075 T_EXPECT_TRUE(waitpid_ret
, "%s should succeed", command
[0]);
1076 if (waitpid_ret
== false) {
1078 T_LOG("%s exited %d", command
[0], status
);
1081 T_LOG("%s received signal %d", command
[0], signal
);
1090 dp_class_num_to_string(int num
)
1095 case PROTECTION_CLASS_A
:
1097 case PROTECTION_CLASS_B
:
1099 case PROTECTION_CLASS_C
:
1101 case PROTECTION_CLASS_D
:
1103 case PROTECTION_CLASS_E
:
1105 case PROTECTION_CLASS_F
:
1108 return "<unknown class>";
1114 device_lock_state(void)
1117 * TODO: Actually implement this.
1119 * We fail if a passcode already exists, and the methods being used
1120 * to lock/unlock the device in this test appear to be synchronous…
1121 * do we need this function?
1128 /* Determines if we will try to test class C semanatics. */
1130 unlocked_since_boot()
1133 * TODO: Actually implement this.
1135 * The actual semantics for CP mean that even with this primative,
1136 * we would need to set a passcode and then reboot the device in
1137 * order to test this; this function will probably be rather
1138 * worthless as a result.