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