9 #include <IOKit/IOKitLib.h>
10 #include <Kernel/IOKit/crypto/AppleKeyStoreDefs.h>
11 #include <Kernel/sys/content_protection.h>
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. */
17 /* TODO: Change the test to use a single cleanup label. */
19 #define CPT_IO_SIZE 4096
20 #define CPT_AKS_BUF_SIZE 256
21 #define CPT_MAX_PASS_LEN 64
23 #define GET_PROT_CLASS(fd) fcntl((fd), F_GETPROTECTIONCLASS)
24 #define SET_PROT_CLASS(fd, prot_class) fcntl((fd), F_SETPROTECTIONCLASS, (prot_class))
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__);
29 extern char g_target_path
[PATH_MAX
];
31 char * cpt_fail_header
= "Content protection test failed";
32 char * keystorectl_path
= "/usr/local/bin/keystorectl";
34 /* Shamelessly ripped from keystorectl routines; a wrapper for invoking the AKS user client. */
35 int apple_key_store(uint32_t command
,
39 size_t input_struct_count
,
41 uint32_t * output_count
)
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
;
49 apple_key_bag_service
= IOServiceGetMatchingService(kIOMasterPortDefault
, IOServiceMatching(kAppleKeyStoreServiceName
));
51 if (apple_key_bag_service
== IO_OBJECT_NULL
)
53 printf("FAILURE: failed to match kAppleKeyStoreServiceName.\n");
57 k_result
= IOServiceOpen(apple_key_bag_service
, mach_task_self(), 0, &connection
);
59 if (k_result
!= KERN_SUCCESS
)
61 printf("FAILURE: failed to open AppleKeyStore.\n");
65 k_result
= IOConnectCallMethod(connection
, kAppleKeyStoreUserClientOpen
, NULL
, 0, NULL
, 0, NULL
, NULL
, NULL
, NULL
);
67 if (k_result
!= KERN_SUCCESS
)
69 printf("FAILURE: call to AppleKeyStore method kAppleKeyStoreUserClientOpen failed.\n");
73 io_result
= IOConnectCallMethod(connection
, command
, inputs
, input_count
, input_structs
, input_struct_count
, outputs
, output_count
, NULL
, NULL
);
75 if (io_result
!= kIOReturnSuccess
)
77 printf("FAILURE: call to AppleKeyStore method %d failed.\n", command
);
84 IOServiceClose(apple_key_bag_service
);
90 #ifndef KEYBAG_ENTITLEMENTS
91 /* Just a wrapper around forking to exec keystorectl for commands requiring entitlements. */
92 int keystorectl(char * const command
[])
94 int child_result
= -1;
102 printf("FAILURE: failed to fork.\n");
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. */
112 execv(keystorectl_path
, command
);
113 printf("FAILURE: child failed to execv keystorectl, errno = %s.\n",
118 if ((waitpid(child
, &child_result
, 0) != child
) || WEXITSTATUS(child_result
))
120 printf("FAILURE: keystorectl failed.\n");
131 #endif /* KEYBAG_ENTITLEMENTS */
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. */
137 int supports_content_prot()
139 int local_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
;
147 defaults
= IORegistryEntryFromPath(kIOMasterPortDefault
, kIODeviceTreePlane
":/defaults");
149 if (defaults
== IO_OBJECT_NULL
)
151 printf("FAILURE: failed to find defaults registry entry.\n");
155 k_result
= IORegistryEntryGetProperty(defaults
, "content-protect", buffer
, &buffer_size
);
157 if (k_result
!= KERN_SUCCESS
)
158 { /* This isn't a failure; it means the entry doesn't exist, so we assume CP
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
167 local_result
= statfs(g_target_path
, &statfs_results
);
169 if (local_result
== -1)
171 printf("FAILURE: failed to statfs the test directory, errno = %s.\n",
174 else if (statfs_results
.f_flags
& MNT_CPROTECT
)
179 { /* This isn't a failure, it means the filesystem isn't formatted for CP. */
188 int device_lock_state()
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? */
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
);
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 */
219 int unlock_device(char * passcode
)
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;
229 if ((passcode
== NULL
) || ((input_struct_count
= strnlen(passcode
, CPT_MAX_PASS_LEN
)) == CPT_MAX_PASS_LEN
))
232 input_struct_count
= 0;
235 result
= apple_key_store(kAppleKeyStoreKeyBagUnlock
, inputs
, input_count
, passcode
, input_struct_count
, NULL
, NULL
);
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
))
243 char * const keystorectl_args
[] = {keystorectl_path
, "unlock", passcode
, NULL
};
244 result
= keystorectl(keystorectl_args
);
245 #endif /* KEYBAG_ENTITLEMENTS */
250 int set_passcode(char * new_passcode
, char * old_passcode
)
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;
265 if ((old_passcode
== NULL
) || ((old_passcode_len
= strnlen(old_passcode
, CPT_MAX_PASS_LEN
)) == CPT_MAX_PASS_LEN
))
268 old_passcode_len
= 0;
271 if ((new_passcode
== NULL
) || ((new_passcode_len
= strnlen(new_passcode
, CPT_MAX_PASS_LEN
)) == CPT_MAX_PASS_LEN
))
274 new_passcode_len
= 0;
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
);
290 result
= apple_key_store(kAppleKeyStoreKeyBagSetPasscode
, inputs
, input_count
, input_structs
, input_struct_count
, NULL
, NULL
);
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
))
298 if ((new_passcode
== NULL
) || (strnlen(new_passcode
, CPT_MAX_PASS_LEN
) == CPT_MAX_PASS_LEN
))
303 char * const keystorectl_args
[] = {keystorectl_path
, "change-password", old_passcode
, new_passcode
, NULL
};
304 result
= keystorectl(keystorectl_args
);
305 #endif /* KEYBAG_ENTITLEMENTS */
310 int clear_passcode(char * passcode
)
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). */
316 result
= set_passcode(NULL
, passcode
);
322 /* Determines if we will try to test class C semanatics. */
323 int unlocked_since_boot()
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. */
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.
338 Or we could just try "" to ""; it works. */
343 result
= set_passcode(NULL
, NULL
);
348 int content_protection_test(void * argp
)
350 #pragma unused (argp)
352 int local_result
= -1;
353 int test_result
= -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";
367 /* Do some initial setup (names). */
368 bzero(filepath
, PATH_MAX
);
369 bzero(dirpath
, PATH_MAX
);
370 bzero(subdirpath
, PATH_MAX
);
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
);
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__
);
388 local_result
= supports_content_prot();
390 if (local_result
== -1)
392 printf("%s, line %d: failed to determine if content protection is supported.\n",
393 cpt_fail_header
, __LINE__
);
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");
403 /* If we support content protection, we'll need to be able to set the passcode. */
404 local_result
= has_passcode();
406 if (local_result
== -1)
408 printf("%s, line %d: the device appears to have a passcode.\n",
409 cpt_fail_header
, __LINE__
);
413 if (set_passcode(passcode
, NULL
))
415 printf("%s, line %d: failed to set a new passcode.\n",
416 cpt_fail_header
, __LINE__
);
420 fd
= open(filepath
, O_CREAT
| O_EXCL
| O_RDWR
| O_CLOEXEC
, 0777);
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
;
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
++)
432 old_prot_class
= GET_PROT_CLASS(fd
);
434 if (old_prot_class
== -1)
436 printf("%s, line %d: failed to get protection class when unlocked, errno = %s.\n",
437 cpt_fail_header
, __LINE__
, strerror(errno
));
441 if (SET_PROT_CLASS(fd
, new_prot_class
))
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
));
449 /* Query the filesystem for the default CP level (Is it C?) */
450 #ifndef F_GETDEFAULTPROTLEVEL
451 #define F_GETDEFAULTPROTLEVEL 79
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
));
461 /* XXX: Do we want to do anything with the level? What should it be? */
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.
472 /* re-create the file */
473 fd
= open (filepath
, O_CREAT
| O_EXCL
| O_RDWR
| O_CLOEXEC
);
475 printf("%s, line %d: failed to create the test file, errno = %s.\n",
476 cpt_fail_header
, __LINE__
, strerror(errno
));
480 /* Try making a class A file while locked. */
487 if (!SET_PROT_CLASS(fd
, PROTECTION_CLASS_A
))
489 printf("%s, line %d: was able to change protection class from D to A when locked.\n",
490 cpt_fail_header
, __LINE__
);
494 if (unlock_device(passcode
))
500 /* Attempt opening/IO to a class A file while unlocked. */
501 if (SET_PROT_CLASS(fd
, PROTECTION_CLASS_A
))
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
));
509 fd
= open(filepath
, O_RDWR
| O_CLOEXEC
);
513 printf("%s, line %d: failed to open a class A file when unlocked, errno = %s.\n",
514 cpt_fail_header
, __LINE__
, strerror(errno
));
518 /* TODO: Write specific data we can check for.
519 If we're going to do that, the write scheme should be deliberately ugly. */
522 while (current_byte
< CPT_IO_SIZE
)
524 local_result
= pwrite(fd
, &wr_buffer
[current_byte
], CPT_IO_SIZE
- current_byte
, current_byte
);
526 if (local_result
== -1)
528 printf("%s, line %d: failed to write to class A file when unlocked, errno = %s.\n",
529 cpt_fail_header
, __LINE__
, strerror(errno
));
533 current_byte
+= local_result
;
538 while (current_byte
< CPT_IO_SIZE
)
540 local_result
= pread(fd
, &rd_buffer
[current_byte
], CPT_IO_SIZE
- current_byte
, current_byte
);
542 if (local_result
== -1)
544 printf("%s, line %d: failed to read from class A file when unlocked, errno = %s.\n",
545 cpt_fail_header
, __LINE__
, strerror(errno
));
549 current_byte
+= local_result
;
552 /* Again, but now while locked; and try to change the file class as well. */
559 if (pread(fd
, rd_buffer
, CPT_IO_SIZE
, 0) > 0)
561 printf("%s, line %d: was able to read from a class A file when locked.\n",
562 cpt_fail_header
, __LINE__
);
566 if (pwrite(fd
, wr_buffer
, CPT_IO_SIZE
, 0) > 0)
568 printf("%s, line %d: was able to write to a class A file when locked.\n",
569 cpt_fail_header
, __LINE__
);
573 if (!SET_PROT_CLASS(fd
, PROTECTION_CLASS_D
))
575 printf("%s, line %d: was able to change protection class from A to D when locked.\n",
576 cpt_fail_header
, __LINE__
);
580 /* Try to open and truncate the file. */
582 fd
= open(filepath
, O_RDWR
| O_TRUNC
| O_CLOEXEC
);
586 printf("%s, line %d: was able to open and truncate a class A file when locked.\n",
587 cpt_fail_header
, __LINE__
);
591 /* Try to open the file */
592 fd
= open(filepath
, O_RDWR
| O_CLOEXEC
);
596 printf("%s, line %d: was able to open a class A file when locked.\n",
597 cpt_fail_header
, __LINE__
);
601 /* What about class B files? */
602 if (unlock_device(passcode
))
608 fd
= open(filepath
, O_RDWR
| O_CLOEXEC
);
612 printf("%s, line %d: was unable to open a class A file when unlocked.\n",
613 cpt_fail_header
, __LINE__
);
617 if (SET_PROT_CLASS(fd
, PROTECTION_CLASS_D
))
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
));
630 /* Can we create a class B file while locked? */
631 if (SET_PROT_CLASS(fd
, PROTECTION_CLASS_B
))
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
));
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__
);
645 /* We should also be able to read/write to the file descriptor while it is open. */
648 while (current_byte
< CPT_IO_SIZE
)
650 local_result
= pwrite(fd
, &wr_buffer
[current_byte
], CPT_IO_SIZE
- current_byte
, current_byte
);
652 if (local_result
== -1)
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
));
659 current_byte
+= local_result
;
664 while (current_byte
< CPT_IO_SIZE
)
666 local_result
= pread(fd
, &rd_buffer
[current_byte
], CPT_IO_SIZE
- current_byte
, current_byte
);
668 if (local_result
== -1)
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
));
675 current_byte
+= local_result
;
678 /* We should not be able to open a class B file under lock. */
680 fd
= open(filepath
, O_RDWR
| O_CLOEXEC
);
684 printf("%s, line %d: was able to open a class B file when locked.\n",
685 cpt_fail_header
, __LINE__
);
691 /* We still need to test directory semantics. */
692 if (mkdir(dirpath
, 0x0777) == -1)
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
;
699 /* The newly created directory should not have a protection class. */
700 dir_fd
= open(dirpath
, O_RDONLY
| O_CLOEXEC
);
704 printf("%s, line %d: failed to open an unclassed directory when locked, errno = %s.\n",
705 cpt_fail_header
, __LINE__
, strerror(errno
));
709 if ((GET_PROT_CLASS(dir_fd
) != PROTECTION_CLASS_D
) && (GET_PROT_CLASS(dir_fd
) != PROTECTION_CLASS_DIR_NONE
))
711 printf("%s, line %d: newly created directory had a non-D and non-NONE protection class.\n",
712 cpt_fail_header
, __LINE__
);
716 if (SET_PROT_CLASS(dir_fd
, PROTECTION_CLASS_A
))
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__
);
723 if (SET_PROT_CLASS(dir_fd
, PROTECTION_CLASS_D
))
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
));
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
))
733 printf("%s, line %d: failed to construct the path for a file in the directory.\n",
734 cpt_fail_header
, __LINE__
);
738 if (unlock_device(passcode
))
744 for (new_prot_class
= PROTECTION_CLASS_A
; new_prot_class
<= PROTECTION_CLASS_D
; new_prot_class
++)
747 old_prot_class
= GET_PROT_CLASS(dir_fd
);
749 if (old_prot_class
== -1)
751 printf("%s, line %d: failed to get the protection class for the directory, errno = %s.\n",
752 cpt_fail_header
, __LINE__
, strerror(errno
));
756 if (SET_PROT_CLASS(dir_fd
, new_prot_class
))
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
));
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
);
771 fd
= open(filepath
, O_CREAT
| O_EXCL
| O_CLOEXEC
, 0777);
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
));
780 local_result
= GET_PROT_CLASS(fd
);
782 if (local_result
== -1)
784 printf("%s, line %d: failed to get the new file's protection class, errno = %s.\n",
785 cpt_fail_header
, __LINE__
, strerror(errno
));
788 else if (local_result
!= new_prot_class
)
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
);
800 /* Do we disallow creation of a class F directory? */
801 if (!SET_PROT_CLASS(dir_fd
, PROTECTION_CLASS_F
))
803 printf("%s, line %d: creation of a class F directory did not fail as expected.\n",
804 cpt_fail_header
, __LINE__
);
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
))
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
));
822 fd
= open(filepath
, O_CREAT
| O_EXCL
| O_CLOEXEC
, 0777);
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
));
831 if (unlock_device(passcode
))
837 if (SET_PROT_CLASS(dir_fd
, PROTECTION_CLASS_B
))
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
));
850 fd
= open(filepath
, O_CREAT
| O_EXCL
| O_RDWR
| O_CLOEXEC
, 0777);
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
));
859 local_result
= GET_PROT_CLASS(fd
);
861 if (local_result
== -1)
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
));
867 else if (local_result
!= PROTECTION_CLASS_B
)
869 printf("%s, line %d: new file in class B directory did not inherit protection class.\n",
870 cpt_fail_header
, __LINE__
, strerror(errno
));
874 /* What happens when we try to create new subdirectories? */
875 if (unlock_device(passcode
))
881 for (new_prot_class
= PROTECTION_CLASS_A
; new_prot_class
<= PROTECTION_CLASS_D
; new_prot_class
++)
883 if (SET_PROT_CLASS(dir_fd
, new_prot_class
))
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
));
890 local_result
= mkdir(subdirpath
, 0x0777);
892 if (local_result
== -1)
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
));
899 subdir_fd
= open(subdirpath
, O_RDONLY
| O_CLOEXEC
);
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
));
908 local_result
= GET_PROT_CLASS(subdir_fd
);
910 if (local_result
== -1)
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
));
916 else if (local_result
!= new_prot_class
)
918 printf("%s, line %d: new subdirectory had different class than class %d parent.\n",
919 cpt_fail_header
, __LINE__
, new_prot_class
);
927 /* If we've made it this far, the test was successful. */
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
))
952 printf("WARNING: failed to unlock the device.\n");
955 if (clear_passcode(passcode
))
957 printf("WARNING: failed to clear the passcode.\n");