]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/darwintests/data_protection.c
c9a69fee74cf2ecb16083164d1880029ed7c6dec
[apple/xnu.git] / tools / tests / darwintests / data_protection.c
1 #include <darwintest.h>
2 #include <darwintest_utils.h>
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <stdlib.h>
6 #include <sys/mount.h>
7 #include <sys/wait.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <string.h>
12
13 #include <IOKit/IOKitLib.h>
14 #include <Kernel/IOKit/crypto/AppleKeyStoreDefs.h>
15 #include <Kernel/sys/content_protection.h>
16
17 #define CPT_IO_SIZE 4096
18 #define CPT_AKS_BUF_SIZE 256
19 #define CPT_MAX_PASS_LEN 64
20
21 #define GET_PROT_CLASS(fd) \
22 fcntl((fd), F_GETPROTECTIONCLASS)
23
24 #define SET_PROT_CLASS(fd, prot_class) \
25 fcntl((fd), F_SETPROTECTIONCLASS, (prot_class))
26
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"
31
32 int g_fd = -1;
33 int g_dir_fd = -1;
34 int g_subdir_fd = -1;
35 int g_passcode_set = 0;
36
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] = "";
41
42 int apple_key_store(
43 uint32_t command,
44 uint64_t * inputs,
45 uint32_t input_count,
46 void * input_structs,
47 size_t input_struct_count,
48 uint64_t * outputs,
49 uint32_t * output_count
50 );
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);
59 void setup(void);
60 void cleanup(void);
61
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;
67 int current_byte = 0;
68 char rd_buffer[CPT_IO_SIZE];
69 char wr_buffer[CPT_IO_SIZE];
70
71 setup();
72
73 /*
74 * Ensure we can freely read and change
75 * protection classes when unlocked.
76 */
77 for(
78 new_prot_class = PROTECTION_CLASS_A;
79 new_prot_class <= PROTECTION_CLASS_F;
80 new_prot_class++
81 ) {
82 T_ASSERT_NE(
83 old_prot_class = GET_PROT_CLASS(g_fd),
84 -1,
85 "Get protection class when locked"
86 );
87 T_WITH_ERRNO;
88 T_ASSERT_NE(
89 SET_PROT_CLASS(g_fd, new_prot_class),
90 -1,
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)
95 );
96 }
97
98 /* Query the filesystem for the default CP level (Is it C?) */
99 #ifndef F_GETDEFAULTPROTLEVEL
100 #define F_GETDEFAULTPROTLEVEL 79
101 #endif
102
103 T_WITH_ERRNO;
104 T_ASSERT_NE(
105 old_prot_class = fcntl(g_fd, F_GETDEFAULTPROTLEVEL),
106 -1,
107 "Get default protection level for filesystem"
108 );
109
110 /* XXX: Do we want to do anything with the level? What should it be? */
111
112 /*
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.
115 */
116 close(g_fd);
117 unlink(g_filepath);
118
119 /* re-create the file */
120 T_WITH_ERRNO;
121 T_ASSERT_GE(
122 g_fd = open(g_filepath, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC),
123 0,
124 "Recreate test file"
125 );
126
127 /* Try making a class A file while locked. */
128 T_ASSERT_EQ(lock_device(), 0, "*** Lock device ***");
129
130 T_WITH_ERRNO;
131 T_ASSERT_EQ(
132 SET_PROT_CLASS(g_fd, PROTECTION_CLASS_A),
133 -1,
134 "Should not be able to change protection "
135 "from class D to class A when locked"
136 );
137 T_ASSERT_EQ(unlock_device(TEST_PASSCODE), 0, "*** Unlock device ***");
138
139 /* Attempt opening/IO to a class A file while unlocked. */
140 T_WITH_ERRNO;
141 T_ASSERT_EQ(
142 SET_PROT_CLASS(g_fd, PROTECTION_CLASS_A),
143 0,
144 "Should be able to change protection "
145 "from class D to class A when unlocked"
146 );
147
148 close(g_fd);
149
150 T_WITH_ERRNO;
151 T_ASSERT_GE(
152 g_fd = open(g_filepath, O_RDWR|O_CLOEXEC),
153 0,
154 "Should be able to open a class A file when unlocked");
155
156 /*
157 * TODO: Write specific data we can check for. If we're going to do
158 * that, the write scheme should be deliberately ugly.
159 */
160 current_byte = 0;
161
162 while(current_byte < CPT_IO_SIZE) {
163 local_result = pwrite(
164 g_fd,
165 &wr_buffer[current_byte],
166 CPT_IO_SIZE - current_byte,
167 current_byte
168 );
169
170 T_WITH_ERRNO;
171 T_ASSERT_NE(
172 local_result,
173 -1,
174 "Should be able to write to "
175 "a class A file when unlocked"
176 );
177
178 current_byte += local_result;
179 }
180
181 current_byte = 0;
182
183 while(current_byte < CPT_IO_SIZE) {
184 local_result = pread(
185 g_fd,
186 &rd_buffer[current_byte],
187 CPT_IO_SIZE - current_byte,
188 current_byte
189 );
190
191 T_WITH_ERRNO;
192 T_ASSERT_NE(
193 local_result,
194 -1,
195 "Should be able to read from "
196 "a class A file when unlocked"
197 );
198
199 current_byte += local_result;
200 }
201
202 /*
203 * Again, but now while locked; and try to change the file class
204 * as well.
205 */
206 T_ASSERT_EQ(lock_device(), 0, "*** Lock device ***");
207
208 T_ASSERT_LE(
209 pread(g_fd, rd_buffer, CPT_IO_SIZE, 0),
210 0,
211 "Should not be able to read from a class A file when locked"
212 );
213
214 T_ASSERT_LE(
215 pwrite(g_fd, wr_buffer, CPT_IO_SIZE, 0),
216 0,
217 "Should not be able to write to a class A file when locked"
218 );
219
220 T_ASSERT_EQ(
221 SET_PROT_CLASS(g_fd, PROTECTION_CLASS_D),
222 -1,
223 "Should not be able to change protection "
224 "from class A to class D when locked"
225 );
226
227 /* Try to open and truncate the file. */
228 close(g_fd);
229
230 T_ASSERT_EQ(
231 g_fd = open(g_filepath, O_RDWR|O_TRUNC|O_CLOEXEC),
232 -1,
233 "Should not be able to open and truncate "
234 "a class A file when locked"
235 );
236
237 /* Try to open the file */
238 T_ASSERT_EQ(
239 g_fd = open(g_filepath, O_RDWR|O_CLOEXEC),
240 -1,
241 "Should not be able to open a class A file when locked"
242 );
243
244 /* What about class B files? */
245 T_ASSERT_EQ(unlock_device(TEST_PASSCODE), 0, "*** Unlock device ***");
246
247 T_ASSERT_GE(
248 g_fd = open(g_filepath, O_RDWR|O_CLOEXEC),
249 0,
250 "Should be able to open a class A file when unlocked"
251 );
252
253 T_WITH_ERRNO;
254 T_ASSERT_EQ(
255 SET_PROT_CLASS(g_fd, PROTECTION_CLASS_D),
256 0,
257 "Should be able to change protection "
258 "class from A to D when unlocked"
259 );
260
261 T_ASSERT_EQ(lock_device(), 0, "*** Lock device ***");
262
263 /* Can we create a class B file while locked? */
264 T_ASSERT_EQ(
265 SET_PROT_CLASS(g_fd, PROTECTION_CLASS_B),
266 0,
267 "Should be able to change protection "
268 "class from D to B when locked"
269 );
270
271 T_ASSERT_EQ(
272 GET_PROT_CLASS(g_fd),
273 PROTECTION_CLASS_B,
274 "File should now have class B protection"
275 );
276
277 /*
278 * We should also be able to read/write to the
279 * file descriptor while it is open.
280 */
281 current_byte = 0;
282
283 while(current_byte < CPT_IO_SIZE) {
284 local_result = pwrite(
285 g_fd,
286 &wr_buffer[current_byte],
287 CPT_IO_SIZE - current_byte,
288 current_byte
289 );
290
291 T_WITH_ERRNO;
292 T_ASSERT_NE(
293 local_result,
294 -1,
295 "Should be able to write to a "
296 "new class B file when locked"
297 );
298
299 current_byte += local_result;
300 }
301
302 current_byte = 0;
303
304 while(current_byte < CPT_IO_SIZE) {
305 local_result = pread(
306 g_fd,
307 &rd_buffer[current_byte],
308 CPT_IO_SIZE - current_byte,
309 current_byte
310 );
311
312 T_ASSERT_NE(
313 local_result,
314 -1,
315 "Should be able to read from a "
316 "new class B file when locked"
317 );
318
319 current_byte += local_result;
320 }
321
322 /* We should not be able to open a class B file under lock. */
323 close(g_fd);
324 T_WITH_ERRNO;
325 T_ASSERT_EQ(
326 g_fd = open(g_filepath, O_RDWR|O_CLOEXEC),
327 -1,
328 "Should not be able to open a class B file when locked"
329 );
330
331 unlink(g_filepath);
332
333 /* We still need to test directory semantics. */
334 T_WITH_ERRNO;
335 T_ASSERT_NE(
336 mkdir(g_dirpath, 0x0777),
337 -1,
338 "Should be able to create a new directory when locked"
339 );
340
341 /* The newly created directory should not have a protection class. */
342 T_ASSERT_NE(
343 g_dir_fd = open(g_dirpath, O_RDONLY|O_CLOEXEC),
344 -1,
345 "Should be able to open an unclassed directory when locked"
346 );
347
348 T_ASSERT_TRUE(
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"
352 );
353
354 T_ASSERT_EQ(
355 SET_PROT_CLASS(g_dir_fd, PROTECTION_CLASS_A),
356 0,
357 "Should be able to change a directory from "
358 "class D to class A while locked"
359 );
360
361 T_ASSERT_EQ(
362 SET_PROT_CLASS(g_dir_fd, PROTECTION_CLASS_D),
363 0,
364 "Should be able to change a directory from "
365 "class A to class D while locked"
366 );
367
368 /*
369 * Do all files created in the directory properly inherit the
370 * directory's protection class?
371 */
372 T_SETUPBEGIN;
373 T_ASSERT_LT(
374 strlcpy(g_filepath, g_dirpath, PATH_MAX),
375 PATH_MAX,
376 "Construct path for file in the directory"
377 );
378 T_ASSERT_LT(
379 strlcat(g_filepath, "test_file", PATH_MAX),
380 PATH_MAX,
381 "Construct path for file in the directory"
382 );
383 T_SETUPEND;
384
385 T_ASSERT_EQ(unlock_device(TEST_PASSCODE), 0, "*** Unlock device ***");
386
387 for(
388 new_prot_class = PROTECTION_CLASS_A;
389 new_prot_class <= PROTECTION_CLASS_D;
390 new_prot_class++
391 ) {
392 int getclass_dir;
393
394 T_WITH_ERRNO;
395 T_ASSERT_NE(
396 old_prot_class = GET_PROT_CLASS(g_dir_fd),
397 -1,
398 "Get protection class for the directory"
399 );
400
401 T_WITH_ERRNO;
402 T_ASSERT_EQ(
403 SET_PROT_CLASS(g_dir_fd, new_prot_class),
404 0,
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)
409 );
410
411 T_EXPECT_EQ(
412 getclass_dir = GET_PROT_CLASS(g_dir_fd),
413 new_prot_class,
414 "Get protection class for the directory"
415 );
416
417 T_WITH_ERRNO;
418 T_ASSERT_GE(
419 g_fd = open(g_filepath, O_CREAT|O_EXCL|O_CLOEXEC, 0777),
420 0,
421 "Should be able to create file in "
422 "%s directory when unlocked",
423 dp_class_num_to_string(new_prot_class)
424 );
425
426 T_WITH_ERRNO;
427 T_ASSERT_NE(
428 local_result = GET_PROT_CLASS(g_fd),
429 -1,
430 "Get the new file's protection class"
431 );
432
433 T_ASSERT_EQ(
434 local_result,
435 new_prot_class,
436 "File should have %s protection",
437 dp_class_num_to_string(new_prot_class)
438 );
439
440 close(g_fd);
441 unlink(g_filepath);
442 }
443
444 /* Do we disallow creation of a class F directory? */
445 T_ASSERT_NE(
446 SET_PROT_CLASS(g_dir_fd, PROTECTION_CLASS_F),
447 0,
448 "Should not be able to create class F directory"
449 );
450
451 /*
452 * Are class A and class B semantics followed for when
453 * we create these files during lock?
454 */
455 T_WITH_ERRNO;
456 T_ASSERT_EQ(
457 SET_PROT_CLASS(g_dir_fd, PROTECTION_CLASS_A),
458 0,
459 "Should be able to change protection "
460 "from class F to class A when unlocked"
461 );
462
463 T_ASSERT_EQ(lock_device(), 0, "*** Lock device ***");
464
465 T_ASSERT_EQ(
466 g_fd = open(g_filepath, O_CREAT|O_EXCL|O_CLOEXEC, 0777),
467 -1,
468 "Should not be able to create a new file "
469 "in a class A directory when locked"
470 );
471
472 T_ASSERT_EQ(unlock_device(TEST_PASSCODE), 0, "*** Unlock device ***");
473
474 T_WITH_ERRNO;
475 T_ASSERT_EQ(
476 SET_PROT_CLASS(g_dir_fd, PROTECTION_CLASS_B),
477 0,
478 "Should be able to change directory "
479 "from class A to class B when unlocked"
480 );
481
482 T_ASSERT_EQ(lock_device(), 0, "*** Lock device ***");
483
484 T_ASSERT_GE(
485 g_fd = open(g_filepath, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC, 0777),
486 0,
487 "Should be able to create a new file "
488 "in class B directory when locked"
489 );
490
491 T_ASSERT_NE(
492 local_result = GET_PROT_CLASS(g_fd),
493 -1,
494 "Get the new file's protection class"
495 );
496
497 T_ASSERT_EQ(
498 local_result,
499 PROTECTION_CLASS_B,
500 "File should inherit protection class of class B directory"
501 );
502
503 /* What happens when we try to create new subdirectories? */
504 T_ASSERT_EQ(unlock_device(TEST_PASSCODE), 0, "*** Unlock device ***");
505
506 for(
507 new_prot_class = PROTECTION_CLASS_A;
508 new_prot_class <= PROTECTION_CLASS_D;
509 new_prot_class++
510 ) {
511 T_WITH_ERRNO;
512 T_ASSERT_EQ(
513 SET_PROT_CLASS(g_dir_fd, new_prot_class),
514 0,
515 "Change directory to %s",
516 dp_class_num_to_string(new_prot_class)
517 );
518
519 T_WITH_ERRNO;
520 T_ASSERT_NE(
521 mkdir(g_subdirpath, 0x0777),
522 -1,
523 "Create subdirectory in %s directory",
524 dp_class_num_to_string(new_prot_class)
525 );
526
527 T_WITH_ERRNO;
528 T_ASSERT_NE(
529 g_subdir_fd = open(g_subdirpath, O_RDONLY|O_CLOEXEC),
530 -1,
531 "Should be able to open subdirectory in %s directory",
532 dp_class_num_to_string(new_prot_class)
533 );
534
535 T_ASSERT_NE(
536 local_result = GET_PROT_CLASS(g_subdir_fd),
537 -1,
538 "Get protection class of new subdirectory "
539 "of %s directory",
540 dp_class_num_to_string(new_prot_class)
541 );
542
543 T_ASSERT_EQ(
544 local_result,
545 new_prot_class,
546 "New subdirectory should have same class as %s parent",
547 dp_class_num_to_string(new_prot_class)
548 );
549
550 close(g_subdir_fd);
551 rmdir(g_subdirpath);
552 }
553 }
554
555 void
556 setup(void) {
557 int ret = 0;
558 int local_result = -1;
559
560 T_SETUPBEGIN;
561
562 T_ATEND(cleanup);
563
564 T_WITH_ERRNO;
565 T_ASSERT_NOTNULL(
566 mkdtemp(g_test_tempdir),
567 "Create temporary directory for test"
568 );
569 T_LOG("Test temp dir: %s", g_test_tempdir);
570
571 T_ASSERT_NE(
572 local_result = supports_content_prot(),
573 -1,
574 "Get content protection support status"
575 );
576
577 if(local_result == 0) {
578 T_SKIP("Data protection not supported on this system");
579 }
580
581 T_ASSERT_EQ(
582 has_passcode(),
583 0,
584 "Device should not have existing passcode"
585 );
586
587 T_ASSERT_EQ(
588 set_passcode(TEST_PASSCODE, NULL),
589 0,
590 "Set test passcode"
591 );
592
593 bzero(g_filepath, PATH_MAX);
594 bzero(g_dirpath, PATH_MAX);
595 bzero(g_subdirpath, PATH_MAX);
596
597 ret |= (strlcat(g_filepath, g_test_tempdir, PATH_MAX) == PATH_MAX);
598 ret |= (strlcat(g_filepath, "/", PATH_MAX) == PATH_MAX);
599 ret |= (strlcpy(g_dirpath, g_filepath, PATH_MAX) == PATH_MAX);
600 ret |= (strlcat(g_filepath, "test_file", PATH_MAX) == PATH_MAX);
601 ret |= (strlcat(g_dirpath, "test_dir/", PATH_MAX) == PATH_MAX);
602 ret |= (strlcpy(g_subdirpath, g_dirpath, PATH_MAX) == PATH_MAX);
603 ret |= (strlcat(g_subdirpath, "test_subdir/", PATH_MAX) == PATH_MAX);
604
605 T_QUIET;
606 T_ASSERT_EQ(ret, 0, "Initialize test path strings");
607
608 T_WITH_ERRNO;
609 T_ASSERT_GE(
610 g_fd = open(g_filepath, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC, 0777),
611 0,
612 "Create test file"
613 );
614
615 T_SETUPEND;
616 }
617
618 void
619 cleanup(void) {
620 T_LOG("Cleaning up…");
621
622 if(g_subdir_fd >= 0) {
623 T_LOG("Cleanup: closing fd %d", g_subdir_fd);
624 close(g_subdir_fd);
625 }
626
627 if(g_subdirpath[0]) {
628 T_LOG("Cleanup: removing %s", g_subdirpath);
629 rmdir(g_subdirpath);
630 }
631
632 if(g_fd >= 0) {
633 T_LOG("Cleanup: closing fd %d", g_fd);
634 close(g_fd);
635 }
636
637 if(g_filepath[0]) {
638 T_LOG("Cleanup: removing %s", g_filepath);
639 unlink(g_filepath);
640 }
641
642 if(g_dir_fd >= 0) {
643 T_LOG("Cleanup: closing fd %d", g_dir_fd);
644 close(g_dir_fd);
645 }
646
647 if(g_dirpath[0]) {
648 T_LOG("Cleanup: removing %s", g_dirpath);
649 rmdir(g_dirpath);
650 }
651
652 if(strcmp(g_test_tempdir, TEMP_DIR_TEMPLATE)) {
653 T_LOG("Cleanup: removing %s", g_test_tempdir);
654 rmdir(g_test_tempdir);
655 }
656
657 if(g_passcode_set) {
658 T_LOG("Cleanup: unlocking device");
659 if(unlock_device(TEST_PASSCODE)) {
660 T_LOG("Warning: failed to unlock device in cleanup");
661 }
662
663 T_LOG("Cleanup: clearing passcode");
664 if(clear_passcode(TEST_PASSCODE)) {
665 T_LOG("Warning: failed to clear passcode in cleanup");
666 }
667 }
668 }
669
670 int
671 set_passcode(char * new_passcode, char * old_passcode) {
672 int result = -1;
673
674 #ifdef KEYBAG_ENTITLEMENTS
675 /* If we're entitled, we can set the passcode ourselves. */
676 uint64_t inputs[] = {device_keybag_handle};
677 uint32_t input_count = (sizeof(inputs) / sizeof(*inputs));
678 void * input_structs = NULL;
679 size_t input_struct_count = 0;
680 char buffer[CPT_AKS_BUF_SIZE];
681 char * buffer_ptr = buffer;
682 uint32_t old_passcode_len = 0;
683 uint32_t new_passcode_len = 0;
684
685 T_LOG("%s(): using keybag entitlements", __func__);
686
687 old_passcode_len = strnlen(old_passcode, CPT_MAX_PASS_LEN);
688 new_passcode_len = strnlen(new_passcode, CPT_MAX_PASS_LEN);
689
690 if((old_passcode == NULL) || (old_passcode_len == CPT_MAX_PASS_LEN)) {
691 old_passcode = "";
692 old_passcode_len = 0;
693 }
694 if((new_passcode == NULL) || (new_passcode_len == CPT_MAX_PASS_LEN)) {
695 new_passcode = "";
696 new_passcode_len = 0;
697 }
698
699 *((uint32_t *) buffer_ptr) = ((uint32_t) 2);
700 buffer_ptr += sizeof(uint32_t);
701
702 *((uint32_t *) buffer_ptr) = old_passcode_len;
703 buffer_ptr += sizeof(uint32_t);
704
705 memcpy(buffer_ptr, old_passcode, old_passcode_len);
706 buffer_ptr += ((old_passcode_len + sizeof(uint32_t) - 1) &
707 ~(sizeof(uint32_t) - 1));
708
709 *((uint32_t *) buffer_ptr) = new_passcode_len;
710 buffer_ptr += sizeof(uint32_t);
711
712 memcpy(buffer_ptr, new_passcode, new_passcode_len);
713 buffer_ptr += ((new_passcode_len + sizeof(uint32_t) - 1) &
714 ~(sizeof(uint32_t) - 1));
715
716 input_structs = buffer;
717 input_struct_count = (buffer_ptr - buffer);
718
719 result = apple_key_store(
720 kAppleKeyStoreKeyBagSetPasscode,
721 inputs,
722 input_count,
723 input_structs,
724 input_struct_count,
725 NULL,
726 NULL
727 );
728 #else
729 /*
730 * If we aren't entitled, we'll need to use
731 * keystorectl to set the passcode.
732 */
733 T_LOG("%s(): using keystorectl", __func__);
734
735 if(
736 (old_passcode == NULL) ||
737 (strnlen(old_passcode, CPT_MAX_PASS_LEN) == CPT_MAX_PASS_LEN)
738 ) {
739 old_passcode = "";
740 }
741
742 if(
743 (new_passcode == NULL) ||
744 (strnlen(new_passcode, CPT_MAX_PASS_LEN) == CPT_MAX_PASS_LEN)
745 ) {
746 new_passcode = "";
747 }
748
749 char * const keystorectl_args[] = {
750 KEYSTORECTL_PATH,
751 "change-password",
752 old_passcode,
753 new_passcode,
754 NULL
755 };
756 result = spawn_proc(keystorectl_args);
757 #endif /* KEYBAG_ENTITLEMENTS */
758 if(result == 0 && new_passcode != NULL) {
759 g_passcode_set = 1;
760 } else if(result == 0 && new_passcode == NULL) {
761 g_passcode_set = 0;
762 }
763
764 return(result);
765 }
766
767 int
768 clear_passcode(char * passcode) {
769 /*
770 * For the moment, this will set the passcode to the empty string
771 * (a known value); this will most likely need to change, or running
772 * this test may ruin everything™
773 */
774 return set_passcode(NULL, passcode);
775 }
776
777 int
778 has_passcode(void) {
779 return set_passcode(NULL, NULL);
780 }
781
782 int
783 lock_device(void) {
784 int result = -1;
785
786 /*
787 * Pass in the path to keybagdTest instead. By doing this, we bypass
788 * the shortcut to get in to the keybag via IOKit and instead use the
789 * pre-existing command line tool.
790 *
791 * This also goes through the normal "lock → locking (10s) → locked"
792 * flow that would normally occuring during system runtime when the
793 * lock button is depressed. To ensure that our single threaded test
794 * works properly in this case, poll until we can't create a class A
795 * file to be safe.
796 */
797 char * const kbd_args[] = {KEYBAGDTEST_PATH, "lock", NULL};
798 result = spawn_proc(kbd_args);
799 if(result) {
800 return result;
801 }
802
803 /*
804 * Delete the file if it is present. Note that this may fail if the
805 * file is actually not there. So don't bomb out if we can't delete
806 * this file right now.
807 */
808 (void) unlink("/private/var/foo_test_file");
809
810 while(1) {
811 int dp_fd;
812
813 dp_fd = open_dprotected_np(
814 "/private/var/foo_test_file",
815 O_RDWR|O_CREAT,
816 PROTECTION_CLASS_A,
817 0
818 );
819
820 if(dp_fd >= 0) {
821 /* delete it and sleep */
822 close(dp_fd);
823 result = unlink("/private/var/foo_test_file");
824
825 if(result) {
826 return result;
827 }
828
829 sync();
830 sleep(1);
831 } else {
832 /* drop out of our polling loop. */
833 break;
834 }
835 }
836
837 /*
838 * Note that our loop breakout condition is whether or not we can
839 * create a class A file, so that loop may execute up to 10 times
840 * (due to the 10s grace period). By the time we get here, we assume
841 * that we didn't hit any of the error cases above.
842 */
843
844 return 0;
845 }
846
847 int
848 unlock_device(char * passcode) {
849 int result = -1;
850
851 #ifdef KEYBAG_ENTITLEMENTS
852 /* If we're entitled, we can unlock the device ourselves. */
853 uint64_t inputs[] = {device_keybag_handle};
854 uint32_t input_count = (sizeof(inputs) / sizeof(*inputs));
855 size_t input_struct_count = 0;
856
857 T_LOG("%s(): using keybag entitlements", __func__);
858
859 input_struct_count = strnlen(passcode, CPT_MAX_PASS_LEN);
860 if((passcode == NULL) || (input_struct_count == CPT_MAX_PASS_LEN)) {
861 passcode = "";
862 input_struct_count = 0;
863 }
864
865 result = apple_key_store(
866 kAppleKeyStoreKeyBagUnlock,
867 inputs,
868 input_count,
869 passcode,
870 input_struct_count,
871 NULL,
872 NULL
873 );
874 #else
875 /*
876 * If we aren't entitled, we'll need to use
877 * keystorectl to unlock the device.
878 */
879 T_LOG("%s(): using keystorectl", __func__);
880
881 if(
882 (passcode == NULL) ||
883 (strnlen(passcode, CPT_MAX_PASS_LEN) == CPT_MAX_PASS_LEN)
884 ) {
885 passcode = "";
886 }
887
888 char * const keystorectl_args[] = {
889 KEYSTORECTL_PATH, "unlock", passcode, NULL
890 };
891
892 result = spawn_proc(keystorectl_args);
893 #endif /* KEYBAG_ENTITLEMENTS */
894
895 return(result);
896 }
897
898 /*
899 * Code based on Mobile Key Bag; specifically
900 * MKBDeviceSupportsContentProtection and
901 * MKBDeviceFormattedForContentProtection.
902 *
903 * We want to verify that we support content protection, and that
904 * we are formatted for it.
905 */
906 int
907 supports_content_prot(void) {
908 int local_result = -1;
909 int result = -1;
910 uint32_t buffer_size = 1;
911 char buffer[buffer_size];
912 io_registry_entry_t defaults = IO_OBJECT_NULL;
913 kern_return_t k_result = KERN_FAILURE;
914 struct statfs statfs_results;
915
916 defaults = IORegistryEntryFromPath(
917 kIOMasterPortDefault,
918 kIODeviceTreePlane ":/defaults"
919 );
920
921 if(defaults == IO_OBJECT_NULL) {
922 /* Assume data protection is unsupported */
923 T_LOG(
924 "%s(): no defaults entry in IORegistry",
925 __func__
926 );
927 return 0;
928 }
929
930 k_result = IORegistryEntryGetProperty(
931 defaults,
932 "content-protect",
933 buffer,
934 &buffer_size
935 );
936
937 if(k_result != KERN_SUCCESS) {
938 /* Assume data protection is unsupported */
939 T_LOG(
940 "%s(): no content-protect property in IORegistry",
941 __func__
942 );
943 return 0;
944 }
945
946 /*
947 * At this point, we SUPPORT content protection… but are we
948 * formatted for it? This is ugly; we should be testing the file
949 * system we'll be testing in, not just /tmp/.
950 */
951 local_result = statfs(g_test_tempdir, &statfs_results);
952
953 if(local_result == -1) {
954 T_LOG(
955 "%s(): failed to statfs the test directory, errno = %s",
956 __func__, strerror(errno)
957 );
958 return -1;
959 } else if(statfs_results.f_flags & MNT_CPROTECT) {
960 return 1;
961 } else {
962 T_LOG(
963 "%s(): filesystem not formatted for data protection",
964 __func__
965 );
966 return 0;
967 }
968 }
969
970 /*
971 * Shamelessly ripped from keystorectl routines;
972 * a wrapper for invoking the AKS user client.
973 */
974 int
975 apple_key_store(uint32_t command,
976 uint64_t * inputs,
977 uint32_t input_count,
978 void * input_structs,
979 size_t input_struct_count,
980 uint64_t * outputs,
981 uint32_t * output_count) {
982 int result = -1;
983 io_connect_t connection = IO_OBJECT_NULL;
984 io_registry_entry_t apple_key_bag_service = IO_OBJECT_NULL;
985 kern_return_t k_result = KERN_FAILURE;
986 IOReturn io_result = IO_OBJECT_NULL;
987
988 apple_key_bag_service = IOServiceGetMatchingService(
989 kIOMasterPortDefault,
990 IOServiceMatching(kAppleKeyStoreServiceName)
991 );
992 if(apple_key_bag_service == IO_OBJECT_NULL) {
993 T_LOG(
994 "%s: failed to match kAppleKeyStoreServiceName",
995 __func__
996 );
997 goto end;
998 }
999
1000 k_result = IOServiceOpen(
1001 apple_key_bag_service,
1002 mach_task_self(),
1003 0,
1004 &connection
1005 );
1006 if(k_result != KERN_SUCCESS) {
1007 T_LOG(
1008 "%s: failed to open AppleKeyStore: "
1009 "IOServiceOpen() returned %d",
1010 __func__, k_result
1011 );
1012 goto end;
1013 }
1014
1015 k_result = IOConnectCallMethod(
1016 connection,
1017 kAppleKeyStoreUserClientOpen,
1018 NULL, 0, NULL, 0, NULL, NULL, NULL, NULL
1019 );
1020 if(k_result != KERN_SUCCESS) {
1021 T_LOG(
1022 "%s: call to AppleKeyStore method "
1023 "kAppleKeyStoreUserClientOpen failed",
1024 __func__
1025 );
1026 goto close;
1027 }
1028
1029 io_result = IOConnectCallMethod(
1030 connection, command, inputs, input_count, input_structs,
1031 input_struct_count, outputs, output_count, NULL, NULL
1032 );
1033 if(io_result != kIOReturnSuccess) {
1034 T_LOG("%s: call to AppleKeyStore method %d failed", __func__);
1035 goto close;
1036 }
1037
1038 result = 0;
1039
1040 close:
1041 IOServiceClose(apple_key_bag_service);
1042 end:
1043 return(result);
1044 }
1045
1046 /*
1047 * Helper function for launching tools
1048 */
1049 int
1050 spawn_proc(char * const command[]) {
1051 pid_t pid = 0;
1052 int launch_tool_ret = 0;
1053 bool waitpid_ret = true;
1054 int status = 0;
1055 int signal = 0;
1056 int timeout = 30;
1057
1058 launch_tool_ret = dt_launch_tool(&pid, command, false, NULL, NULL);
1059 T_EXPECT_EQ(launch_tool_ret, 0, "launch tool: %s", command[0]);
1060 if(launch_tool_ret != 0) {
1061 return 1;
1062 }
1063
1064 waitpid_ret = dt_waitpid(pid, &status, &signal, timeout);
1065 T_EXPECT_TRUE(waitpid_ret, "%s should succeed", command[0]);
1066 if(waitpid_ret == false) {
1067 if(status != 0) {
1068 T_LOG("%s exited %d", command[0], status);
1069 }
1070 if(signal != 0) {
1071 T_LOG("%s received signal %d", command[0], signal);
1072 }
1073 return 1;
1074 }
1075
1076 return 0;
1077 }
1078
1079 char*
1080 dp_class_num_to_string(int num) {
1081 switch(num) {
1082 case 0:
1083 return "unclassed";
1084 case PROTECTION_CLASS_A:
1085 return "class A";
1086 case PROTECTION_CLASS_B:
1087 return "class B";
1088 case PROTECTION_CLASS_C:
1089 return "class C";
1090 case PROTECTION_CLASS_D:
1091 return "class D";
1092 case PROTECTION_CLASS_E:
1093 return "class E";
1094 case PROTECTION_CLASS_F:
1095 return "class F";
1096 default:
1097 return "<unknown class>";
1098 }
1099 }
1100
1101 #if 0
1102 int device_lock_state(void) {
1103 /*
1104 * TODO: Actually implement this.
1105 *
1106 * We fail if a passcode already exists, and the methods being used
1107 * to lock/unlock the device in this test appear to be synchronous…
1108 * do we need this function?
1109 */
1110 int result = -1;
1111
1112 return(result);
1113 }
1114
1115 /* Determines if we will try to test class C semanatics. */
1116 int unlocked_since_boot() {
1117 /*
1118 * TODO: Actually implement this.
1119 *
1120 * The actual semantics for CP mean that even with this primative,
1121 * we would need to set a passcode and then reboot the device in
1122 * order to test this; this function will probably be rather
1123 * worthless as a result.
1124 */
1125 int result = 1;
1126
1127 return(result);
1128 }
1129 #endif
1130