1 #include <TargetConditionals.h>
8 #include <sys/sysctl.h>
9 #include <System/sys/fsgetpath.h>
10 #include <MobileKeyBag/MobileKeyBag.h>
11 #import <Security/SecItemPriv.h>
12 #include <hfs/hfs_fsctl.h>
14 #include "hfs-tests.h"
15 #include "test-utils.h"
17 TEST(class_roll
, .run_as_root
= true)
19 #define CLASS_ROLL_TEST_FILE "/tmp/class-roll.data"
21 typedef enum generation_status
{
22 generation_current
= 1 << 0,
23 generation_change_in_progress
= 1 << 1,
24 generation_change_pending
= 1 << 2,
25 } generation_status_t
;
27 int run_class_roll(__unused test_ctx_t
*ctx
)
29 // The root file system needs to be HFS
32 assert(statfs("/tmp", &sfs
) == 0);
33 if (strcmp(sfs
.f_fstypename
, "hfs")) {
34 printf("class_roll needs hfs as root file system - skipping.\n");
38 // Let's create a file to work with
39 unlink(CLASS_ROLL_TEST_FILE
);
42 assert_with_errno((fd
= open(CLASS_ROLL_TEST_FILE
,
43 O_CREAT
| O_RDWR
, 0666)) >= 0);
45 size_t size
= 1000000 * 4;
47 assert_no_err(ftruncate(fd
, size
));
50 assert_with_errno((p
= mmap(NULL
, size
, PROT_READ
| PROT_WRITE
,
51 MAP_SHARED
, fd
, 0)) != MAP_FAILED
);
53 for (int i
= 0; i
< 1000000; ++i
)
56 assert_no_err(msync(p
, size
, MS_SYNC
));
59 assert_no_err(fcntl(fd
, F_SETPROTECTIONCLASS
, 3));
63 assert_no_err(fstat(fd
, &sb
));
65 // Start class rolling
66 int ret
= MKBKeyBagChangeSystemGeneration(NULL
, 1);
68 if (ret
&& ret
!= kMobileKeyBagNotReady
)
69 assert_fail("MKBKeyBagChangeSystemGeneration returned %d\n", ret
);
71 uint32_t keybag_state
;
72 assert(!MKBKeyBagGetSystemGeneration(&keybag_state
)
73 && (keybag_state
& generation_change_in_progress
));
75 static const uint32_t max_ids
= 1000000;
76 static const uint32_t max_ids_per_iter
= 262144;
78 struct cp_listids list_file_ids
= {
79 .api_version
= CPLIDS_API_VERSION_1
,
80 .flags
= (CPLID_USING_OLD_CLASS_KEY
| CPLID_PROT_CLASS_A
| CPLID_PROT_CLASS_B
| CPLID_PROT_CLASS_C
),
83 uint32_t *file_ids
= malloc(4 * max_ids
);
86 bzero(file_ids
, 4 * max_ids
);
89 list_file_ids
.count
= max_ids_per_iter
;
90 list_file_ids
.file_ids
= file_ids
+ count
;
92 if (fsctl("/private/var", HFSIOC_CP_LIST_IDS
, &list_file_ids
, 0) < 0) {
93 assert_with_errno(errno
== EINTR
);
95 bzero(list_file_ids
.state
, sizeof(list_file_ids
.state
));
98 count
+= list_file_ids
.count
;
100 assert(count
< max_ids
);
102 } while (list_file_ids
.count
== max_ids_per_iter
);
104 assert_no_err(statfs("/private/var", &sfs
));
106 bool did_file
= false;
108 hfs_key_auto_roll_args_t auto_roll_args
= {
109 .api_version
= HFS_KEY_AUTO_ROLL_API_LATEST_VERSION
,
110 .flags
= HFS_KEY_AUTO_ROLL_OLD_CLASS_GENERATION
,
113 assert_no_err(fsctl("/private/var", HFSIOC_SET_KEY_AUTO_ROLL
,
114 &auto_roll_args
, 0));
116 auto_roll_args
.min_key_os_version
= auto_roll_args
.max_key_os_version
= 0xffff;
117 auto_roll_args
.flags
= 0xffffffff;
119 assert_no_err(fsctl("/private/var", HFSIOC_GET_KEY_AUTO_ROLL
,
120 &auto_roll_args
, 0));
122 assert(auto_roll_args
.flags
== HFS_KEY_AUTO_ROLL_OLD_CLASS_GENERATION
);
124 for (unsigned i
= 0; i
< count
; ++i
) {
127 if (fsgetpath(path
, sizeof(path
), &sfs
.f_fsid
,
128 (uint64_t)file_ids
[i
]) < 0) {
129 assert_with_errno(errno
== ENOENT
);
133 if (file_ids
[i
] == sb
.st_ino
) {
134 hfs_key_roll_args_t args
= {
135 .api_version
= HFS_KR_API_LATEST_VERSION
,
136 .operation
= HFS_KR_OP_STATUS
,
139 assert_no_err(ffsctl(fd
, HFSIOC_KEY_ROLL
, &args
, 0));
141 assert(args
.done
== -1
142 && (args
.key_class_generation
143 == ((keybag_state
& generation_current
) ^ 1)));
146 assert_with_errno((raw_fd
= open_dprotected_np(CLASS_ROLL_TEST_FILE
,
147 O_RDONLY
, 0, 1, 0)) >= 0);
149 uint16_t old_key_rev
= args
.key_revision
;
151 assert_no_err(ffsctl(fd
, HFSIOC_KEY_ROLL
, &args
, 0));
153 assert(args
.done
== 0
154 && (args
.key_class_generation
155 == ((keybag_state
& generation_current
))));
157 assert((args
.key_revision
& 0xff00)
158 == ((old_key_rev
+ 0x0100) & 0xff00));
160 // Force flush of cache
161 assert_no_err(msync(p
, sb
.st_size
, MS_INVALIDATE
));
164 for (int i
= 0; i
< 1000000; ++i
) {
169 args
.operation
= HFS_KR_OP_STEP
;
171 assert_no_err(ffsctl(fd
, HFSIOC_KEY_ROLL
, &args
, 0));
173 assert(args
.done
== 2 * 1024 * 1024);
175 // Force flush of cache
176 assert_no_err(msync(p
, sb
.st_size
, MS_INVALIDATE
));
178 // Check the file again
179 for (int i
= 0; i
< 1000000; ++i
) {
183 // We're done with the mmap
184 assert_no_err(munmap(p
, sb
.st_size
));
186 assert_no_err(close(raw_fd
));
191 assert_no_err(fsctl("/private/var", HFSIOC_OP_CPFORCEREWRAP
,
196 uint32_t flags
= HFS_SET_CPFLAG
;
198 fsctl("/private/var", HFSIOC_OP_CRYPTOGEN
, &flags
, 0);
201 while (!_SecKeychainRollKeys(true, NULL
) && ++attempts
< 1000)
203 assert(attempts
< 1000);
205 // Tell MobileKeyBag that we're done
206 assert(!MKBKeyBagChangeSystemGeneration(NULL
, 2));
207 assert(!MKBKeyBagGetSystemGeneration(&keybag_state
));
211 auto_roll_args
.min_key_os_version
= auto_roll_args
.max_key_os_version
= 0xffff;
212 auto_roll_args
.flags
= 0xffffffff;
214 assert_no_err(fsctl("/private/var", HFSIOC_GET_KEY_AUTO_ROLL
,
215 &auto_roll_args
, 0));
217 assert(!auto_roll_args
.flags
);
218 assert(auto_roll_args
.min_key_os_version
== 0);
219 assert(auto_roll_args
.max_key_os_version
== 0);
226 #endif // TARGET_OS_EMBEDDED