]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/xnu_quick_test/content_protection_test.c
xnu-2050.7.9.tar.gz
[apple/xnu.git] / tools / tests / xnu_quick_test / content_protection_test.c
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
29 extern char g_target_path[PATH_MAX];
30
31 char * cpt_fail_header = "Content protection test failed";
32 char * keystorectl_path = "/usr/local/bin/keystorectl";
33
34 /* Shamelessly ripped from keystorectl routines; a wrapper for invoking the AKS user client. */
35 int 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
83 close:
84 IOServiceClose(apple_key_bag_service);
85
86 end:
87 return(result);
88 }
89
90 #ifndef KEYBAG_ENTITLEMENTS
91 /* Just a wrapper around forking to exec keystorectl for commands requiring entitlements. */
92 int 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
128 end:
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. */
137 int 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
183 end:
184 return(result);
185 }
186
187 #if 0
188 int 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
199 int 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
219 int 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
250 int 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
310 int 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. */
323 int 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. */
339 int has_passcode()
340 {
341 int result = -1;
342
343 result = set_passcode(NULL, NULL);
344
345 return(result);
346 }
347
348 int content_protection_test(void * argp)
349 {
350 #pragma unused (argp)
351 int init_result = 0;
352 int local_result = -1;
353 int test_result = -1;
354 int fd = -1;
355 int dir_fd = -1;
356 int subdir_fd = -1;
357 int new_prot_class = -1;
358 int old_prot_class = -1;
359 int current_byte = 0;
360 char filepath[PATH_MAX];
361 char dirpath[PATH_MAX];
362 char subdirpath[PATH_MAX];
363 char rd_buffer[CPT_IO_SIZE];
364 char wr_buffer[CPT_IO_SIZE];
365 char * passcode = "IAmASecurePassword";
366
367 /* Do some initial setup (names). */
368 bzero(filepath, PATH_MAX);
369 bzero(dirpath, PATH_MAX);
370 bzero(subdirpath, PATH_MAX);
371
372 /* This is just easier than checking each result individually. */
373 init_result |= (strlcat(filepath, g_target_path, PATH_MAX) == PATH_MAX);
374 init_result |= (strlcat(filepath, "/", PATH_MAX) == PATH_MAX);
375 init_result |= (strlcpy(dirpath, filepath, PATH_MAX) == PATH_MAX);
376 init_result |= (strlcat(filepath, "cpt_test_file", PATH_MAX) == PATH_MAX);
377 init_result |= (strlcat(dirpath, "cpt_test_dir/", PATH_MAX) == PATH_MAX);
378 init_result |= (strlcpy(subdirpath, dirpath, PATH_MAX) == PATH_MAX);
379 init_result |= (strlcat(subdirpath, "cpt_test_subdir/", PATH_MAX) == PATH_MAX);
380
381 if (init_result)
382 { /* If any of the initialization failed, we're just going to fail now. */
383 printf("%s, line %d: failed to initialize test strings.\n",
384 cpt_fail_header, __LINE__);
385 goto end;
386 }
387
388 local_result = supports_content_prot();
389
390 if (local_result == -1)
391 {
392 printf("%s, line %d: failed to determine if content protection is supported.\n",
393 cpt_fail_header, __LINE__);
394 goto end;
395 }
396 else if (local_result == 0)
397 { /* If we don't support content protection at the moment, pass the test. */
398 printf("This device does not support or is not formatted for content protection.\n");
399 test_result = 0;
400 goto end;
401 }
402
403 /* If we support content protection, we'll need to be able to set the passcode. */
404 local_result = has_passcode();
405
406 if (local_result == -1)
407 {
408 printf("%s, line %d: the device appears to have a passcode.\n",
409 cpt_fail_header, __LINE__);
410 goto end;
411 }
412
413 if (set_passcode(passcode, NULL))
414 {
415 printf("%s, line %d: failed to set a new passcode.\n",
416 cpt_fail_header, __LINE__);
417 goto end;
418 }
419
420 fd = open(filepath, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC);
421
422 if (fd == -1)
423 {
424 printf("%s, line %d: failed to create the test file, errno = %s.\n",
425 cpt_fail_header, __LINE__, strerror(errno));
426 goto remove_passcode;
427 }
428
429 /* Ensure we can freely read and change protection classes when unlocked. */
430 for (new_prot_class = PROTECTION_CLASS_A; new_prot_class <= PROTECTION_CLASS_F; new_prot_class++)
431 {
432 old_prot_class = GET_PROT_CLASS(fd);
433
434 if (old_prot_class == -1)
435 {
436 printf("%s, line %d: failed to get protection class when unlocked, errno = %s.\n",
437 cpt_fail_header, __LINE__, strerror(errno));
438 goto cleanup_file;
439 }
440
441 if (SET_PROT_CLASS(fd, new_prot_class))
442 {
443 printf("%s, line %d: failed to change protection class from %d to %d during unlock, errno = %s.\n",
444 cpt_fail_header, __LINE__, old_prot_class, new_prot_class, strerror(errno));
445 goto cleanup_file;
446 }
447 }
448
449 if (SET_PROT_CLASS(fd, PROTECTION_CLASS_D))
450 {
451 printf("%s, line %d: failed to change protection class from F to D when unlocked, errno = %s.\n",
452 cpt_fail_header, __LINE__, strerror(errno));
453 goto cleanup_file;
454 }
455
456 /* Try making a class A file while locked. */
457 if (lock_device())
458 {
459 PRINT_LOCK_FAIL;
460 goto cleanup_file;
461 }
462
463 if (!SET_PROT_CLASS(fd, PROTECTION_CLASS_A))
464 {
465 printf("%s, line %d: was able to change protection class from D to A when locked.\n",
466 cpt_fail_header, __LINE__);
467 goto cleanup_file;
468 }
469
470 if (unlock_device(passcode))
471 {
472 PRINT_UNLOCK_FAIL;
473 goto cleanup_file;
474 }
475
476 /* Attempt opening/IO to a class A file while unlocked. */
477 if (SET_PROT_CLASS(fd, PROTECTION_CLASS_A))
478 {
479 printf("%s, line %d: failed to change protection class from D to A when unlocked, errno = %s.\n",
480 cpt_fail_header, __LINE__, strerror(errno));
481 goto cleanup_file;
482 }
483
484 close(fd);
485 fd = open(filepath, O_RDWR | O_CLOEXEC);
486
487 if (fd == -1)
488 {
489 printf("%s, line %d: failed to open a class A file when unlocked, errno = %s.\n",
490 cpt_fail_header, __LINE__, strerror(errno));
491 goto remove_file;
492 }
493
494 /* TODO: Write specific data we can check for.
495 If we're going to do that, the write scheme should be deliberately ugly. */
496 current_byte = 0;
497
498 while (current_byte < CPT_IO_SIZE)
499 {
500 local_result = pwrite(fd, &wr_buffer[current_byte], CPT_IO_SIZE - current_byte, current_byte);
501
502 if (local_result == -1)
503 {
504 printf("%s, line %d: failed to write to class A file when unlocked, errno = %s.\n",
505 cpt_fail_header, __LINE__, strerror(errno));
506 goto cleanup_file;
507 }
508
509 current_byte += local_result;
510 }
511
512 current_byte = 0;
513
514 while (current_byte < CPT_IO_SIZE)
515 {
516 local_result = pread(fd, &rd_buffer[current_byte], CPT_IO_SIZE - current_byte, current_byte);
517
518 if (local_result == -1)
519 {
520 printf("%s, line %d: failed to read from class A file when unlocked, errno = %s.\n",
521 cpt_fail_header, __LINE__, strerror(errno));
522 goto cleanup_file;
523 }
524
525 current_byte += local_result;
526 }
527
528 /* Again, but now while locked; and try to change the file class as well. */
529 if (lock_device())
530 {
531 PRINT_LOCK_FAIL;
532 goto cleanup_file;
533 }
534
535 if (pread(fd, rd_buffer, CPT_IO_SIZE, 0) > 0)
536 {
537 printf("%s, line %d: was able to read from a class A file when locked.\n",
538 cpt_fail_header, __LINE__);
539 goto cleanup_file;
540 }
541
542 if (pwrite(fd, wr_buffer, CPT_IO_SIZE, 0) > 0)
543 {
544 printf("%s, line %d: was able to write to a class A file when locked.\n",
545 cpt_fail_header, __LINE__);
546 goto cleanup_file;
547 }
548
549 if (!SET_PROT_CLASS(fd, PROTECTION_CLASS_D))
550 {
551 printf("%s, line %d: was able to change protection class from A to D when locked.\n",
552 cpt_fail_header, __LINE__);
553 goto cleanup_file;
554 }
555
556 /* Try to open and truncate the file. */
557 close(fd);
558 fd = open(filepath, O_RDWR | O_TRUNC | O_CLOEXEC);
559
560 if (fd != -1)
561 {
562 printf("%s, line %d: was able to open and truncate a class A file when locked.\n",
563 cpt_fail_header, __LINE__);
564 goto cleanup_file;
565 }
566
567 /* Try to open the file */
568 fd = open(filepath, O_RDWR | O_CLOEXEC);
569
570 if (fd != -1)
571 {
572 printf("%s, line %d: was able to open a class A file when locked.\n",
573 cpt_fail_header, __LINE__);
574 goto cleanup_file;
575 }
576
577 /* What about class B files? */
578 if (unlock_device(passcode))
579 {
580 PRINT_UNLOCK_FAIL;
581 goto cleanup_file;
582 }
583
584 fd = open(filepath, O_RDWR | O_CLOEXEC);
585
586 if (fd == -1)
587 {
588 printf("%s, line %d: was unable to open a class A file when unlocked.\n",
589 cpt_fail_header, __LINE__);
590 goto cleanup_file;
591 }
592
593 if (SET_PROT_CLASS(fd, PROTECTION_CLASS_D))
594 {
595 printf("%s, line %d: failed to change protection class from A to D when unlocked, errno = %s.\n",
596 cpt_fail_header, __LINE__, strerror(errno));
597 goto cleanup_file;
598 }
599
600 if (lock_device())
601 {
602 PRINT_LOCK_FAIL;
603 goto cleanup_file;
604 }
605
606 /* Can we create a class B file while locked? */
607 if (SET_PROT_CLASS(fd, PROTECTION_CLASS_B))
608 {
609 printf("%s, line %d: failed to change protection class from D to B when locked, errno = %s.\n",
610 cpt_fail_header, __LINE__, strerror(errno));
611 goto cleanup_file;
612 }
613
614 /* We should also be able to read/write to the file descriptor while it is open. */
615 current_byte = 0;
616
617 while (current_byte < CPT_IO_SIZE)
618 {
619 local_result = pwrite(fd, &wr_buffer[current_byte], CPT_IO_SIZE - current_byte, current_byte);
620
621 if (local_result == -1)
622 {
623 printf("%s, line %d: failed to write to new class B file when locked, errno = %s.\n",
624 cpt_fail_header, __LINE__, strerror(errno));
625 goto cleanup_file;
626 }
627
628 current_byte += local_result;
629 }
630
631 current_byte = 0;
632
633 while (current_byte < CPT_IO_SIZE)
634 {
635 local_result = pread(fd, &rd_buffer[current_byte], CPT_IO_SIZE - current_byte, current_byte);
636
637 if (local_result == -1)
638 {
639 printf("%s, line %d: failed to read from new class B file when locked, errno = %s.\n",
640 cpt_fail_header, __LINE__, strerror(errno));
641 goto cleanup_file;
642 }
643
644 current_byte += local_result;
645 }
646
647 /* We should not be able to open a class B file under lock. */
648 close(fd);
649 fd = open(filepath, O_RDWR | O_CLOEXEC);
650
651 if (fd != -1)
652 {
653 printf("%s, line %d: was able to open a class B file when locked.\n",
654 cpt_fail_header, __LINE__);
655 goto cleanup_file;
656 }
657
658 unlink(filepath);
659
660 /* We still need to test directory semantics. */
661 if (mkdir(dirpath, 0x0777) == -1)
662 {
663 printf("%s, line %d: failed to create a new directory when locked, errno = %s.\n",
664 cpt_fail_header, __LINE__, strerror(errno));
665 goto remove_passcode;
666 }
667
668 /* The newly created directory should not have a protection class. */
669 dir_fd = open(dirpath, O_RDONLY | O_CLOEXEC);
670
671 if (dir_fd == -1)
672 {
673 printf("%s, line %d: failed to open an unclassed directory when locked, errno = %s.\n",
674 cpt_fail_header, __LINE__, strerror(errno));
675 goto remove_dir;
676 }
677
678 if (GET_PROT_CLASS(dir_fd) != PROTECTION_CLASS_D)
679 {
680 printf("%s, line %d: newly created directory had a non-D protection class.\n",
681 cpt_fail_header, __LINE__);
682 goto cleanup_dir;
683 }
684
685 if (SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_A))
686 {
687 printf("%s, line %d: was unable to change a directory from class D to class A during lock.\n",
688 cpt_fail_header, __LINE__);
689 goto cleanup_dir;
690 }
691
692 if (SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_D))
693 {
694 printf("%s, line %d: failed to change a directory from class A to class D during lock, errno = %s.\n",
695 cpt_fail_header, __LINE__, strerror(errno));
696 goto cleanup_dir;
697 }
698
699 /* Do all files created in the directory properly inherit the directory's protection class? */
700 if ((strlcpy(filepath, dirpath, PATH_MAX) == PATH_MAX) || (strlcat(filepath, "cpt_test_file", PATH_MAX) == PATH_MAX))
701 {
702 printf("%s, line %d: failed to construct the path for a file in the directory.\n",
703 cpt_fail_header, __LINE__);
704 goto cleanup_dir;
705 }
706
707 if (unlock_device(passcode))
708 {
709 PRINT_UNLOCK_FAIL;
710 goto cleanup_dir;
711 }
712
713 for (new_prot_class = PROTECTION_CLASS_A; new_prot_class <= PROTECTION_CLASS_E; new_prot_class++)
714 {
715 old_prot_class = GET_PROT_CLASS(dir_fd);
716
717 if (old_prot_class == -1)
718 {
719 printf("%s, line %d: failed to get the protection class for the directory, errno = %s.\n",
720 cpt_fail_header, __LINE__, strerror(errno));
721 goto cleanup_dir;
722 }
723
724 if (SET_PROT_CLASS(dir_fd, new_prot_class))
725 {
726 printf("%s, line %d: failed to change the protection class for the directory from %d to %d, errno = %s.\n",
727 cpt_fail_header, __LINE__, old_prot_class, new_prot_class, strerror(errno));
728 goto cleanup_dir;
729 }
730
731 fd = open(filepath, O_CREAT | O_EXCL | O_CLOEXEC);
732
733 if (fd == -1)
734 {
735 printf("%s, line %d: failed to create a file in a class %d directory when unlocked, errno = %s.\n",
736 cpt_fail_header, __LINE__, new_prot_class, strerror(errno));
737 goto cleanup_dir;
738 }
739
740 local_result = GET_PROT_CLASS(fd);
741
742 if (local_result == -1)
743 {
744 printf("%s, line %d: failed to get the new file's protection class, errno = %s.\n",
745 cpt_fail_header, __LINE__, strerror(errno));
746 goto cleanup_file;
747 }
748 else if (local_result != new_prot_class)
749 {
750 printf("%s, line %d: new file did not inherit the directory's protection class.\n",
751 cpt_fail_header, __LINE__, strerror(errno));
752 goto cleanup_file;
753 }
754
755 close(fd);
756 unlink(filepath);
757 }
758
759 /* Do we disallow creation of a class F directory? */
760 if (!SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_F))
761 {
762 printf("%s, line %d: creation of a class F directory did not fail as expected.\n",
763 cpt_fail_header, __LINE__);
764 goto cleanup_dir;
765 }
766
767 /* And are class A and class B semantics followed for when we create these files during lock? */
768 if (SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_A))
769 {
770 printf("%s, line %d: failed to change directory class from F to A when unlocked, errno = %s.\n",
771 cpt_fail_header, __LINE__, strerror(errno));
772 goto cleanup_dir;
773 }
774
775 if (lock_device())
776 {
777 PRINT_LOCK_FAIL;
778 goto cleanup_dir;
779 }
780
781 fd = open(filepath, O_CREAT | O_EXCL | O_CLOEXEC);
782
783 if (fd != -1)
784 {
785 printf("%s, line %d: was able to create a new file in a class A directory when locked.\n",
786 cpt_fail_header, __LINE__, strerror(errno));
787 goto cleanup_file;
788 }
789
790 if (unlock_device(passcode))
791 {
792 PRINT_UNLOCK_FAIL;
793 goto cleanup_dir;
794 }
795
796 if (SET_PROT_CLASS(dir_fd, PROTECTION_CLASS_B))
797 {
798 printf("%s, line %d: failed to change directory class from A to B when unlocked, errno = %s.\n",
799 cpt_fail_header, __LINE__, strerror(errno));
800 goto cleanup_dir;
801 }
802
803 if (lock_device())
804 {
805 PRINT_LOCK_FAIL;
806 goto cleanup_dir;
807 }
808
809 fd = open(filepath, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC);
810
811 if (fd == -1)
812 {
813 printf("%s, line %d: failed to create new file in class B directory when locked, errno = %s.\n",
814 cpt_fail_header, __LINE__, strerror(errno));
815 goto cleanup_dir;
816 }
817
818 local_result = GET_PROT_CLASS(fd);
819
820 if (local_result == -1)
821 {
822 printf("%s, line %d: failed to get protection class for a new file when locked, errno = %s.\n",
823 cpt_fail_header, __LINE__, strerror(errno));
824 goto cleanup_file;
825 }
826 else if (local_result != PROTECTION_CLASS_B)
827 {
828 printf("%s, line %d: new file in class B directory did not inherit protection class.\n",
829 cpt_fail_header, __LINE__, strerror(errno));
830 goto cleanup_file;
831 }
832
833 /* What happens when we try to create new subdirectories? */
834 if (unlock_device(passcode))
835 {
836 PRINT_UNLOCK_FAIL;
837 goto cleanup_file;
838 }
839
840 for (new_prot_class = PROTECTION_CLASS_A; new_prot_class <= PROTECTION_CLASS_E; new_prot_class++)
841 {
842 if (SET_PROT_CLASS(dir_fd, new_prot_class))
843 {
844 printf("%s, line %d: failed to change directory to class %d, errno = %s.\n",
845 cpt_fail_header, __LINE__, new_prot_class, strerror(errno));
846 goto cleanup_file;
847 }
848
849 local_result = mkdir(subdirpath, 0x0777);
850
851 if (local_result == -1)
852 {
853 printf("%s, line %d: failed to create subdirectory in class %d directory, errno = %s.\n",
854 cpt_fail_header, __LINE__, new_prot_class, strerror(errno));
855 goto cleanup_file;
856 }
857
858 subdir_fd = open(subdirpath, O_RDONLY | O_CLOEXEC);
859
860 if (subdir_fd == -1)
861 {
862 printf("%s, line %d: failed to open subdirectory in class %d directory, errno = %s.\n",
863 cpt_fail_header, __LINE__, new_prot_class, strerror(errno));
864 goto remove_subdir;
865 }
866
867 local_result = GET_PROT_CLASS(subdir_fd);
868
869 if (local_result == -1)
870 {
871 printf("%s, line %d: failed to get class of new subdirectory of class %d directory, errno = %s.\n",
872 cpt_fail_header, __LINE__, new_prot_class, strerror(errno));
873 goto cleanup_subdir;
874 }
875 else if (local_result != new_prot_class)
876 {
877 printf("%s, line %d: new subdirectory had different class than class %d parent.\n",
878 cpt_fail_header, __LINE__, new_prot_class);
879 goto cleanup_subdir;
880 }
881
882 close(subdir_fd);
883 rmdir(subdirpath);
884 }
885
886 /* If we've made it this far, the test was successful. */
887 test_result = 0;
888
889 cleanup_subdir:
890 close(subdir_fd);
891
892 remove_subdir:
893 rmdir(subdirpath);
894
895 cleanup_file:
896 close(fd);
897
898 remove_file:
899 unlink(filepath);
900
901 cleanup_dir:
902 close(dir_fd);
903
904 remove_dir:
905 rmdir(dirpath);
906
907 remove_passcode:
908 /* Try to unlock the device (no ramifications if it isn't locked when we try) and remove the passcode. */
909 if (unlock_device(passcode))
910 {
911 printf("WARNING: failed to unlock the device.\n");
912 }
913
914 if (clear_passcode(passcode))
915 {
916 printf("WARNING: failed to clear the passcode.\n");
917 }
918
919 end:
920 return(test_result);
921 }
922