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