1 #include <TargetConditionals.h>
3 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
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>
13 #include <sys/mount.h>
14 #include <sys/param.h>
16 #include "hfs-tests.h"
17 #include "test-utils.h"
19 TEST(class_roll
, .run_as_root
= true)
21 #define CLASS_ROLL_TEST_FILE "/tmp/class-roll.data"
23 typedef enum generation_status
{
24 generation_current
= 1 << 0,
25 generation_change_in_progress
= 1 << 1,
26 generation_change_pending
= 1 << 2,
27 } generation_status_t
;
29 int run_class_roll(__unused test_ctx_t
*ctx
)
31 // The root file system needs to be HFS
34 assert(statfs("/tmp", &sfs
) == 0);
35 if (strcmp(sfs
.f_fstypename
, "hfs")) {
36 printf("class_roll needs hfs as root file system - skipping.\n");
40 // Let's create a file to work with
41 unlink(CLASS_ROLL_TEST_FILE
);
44 assert_with_errno((fd
= open(CLASS_ROLL_TEST_FILE
,
45 O_CREAT
| O_RDWR
, 0666)) >= 0);
47 size_t size
= 1000000 * 4;
49 assert_no_err(ftruncate(fd
, size
));
52 assert_with_errno((p
= mmap(NULL
, size
, PROT_READ
| PROT_WRITE
,
53 MAP_SHARED
, fd
, 0)) != MAP_FAILED
);
55 for (int i
= 0; i
< 1000000; ++i
)
58 assert_no_err(msync(p
, size
, MS_SYNC
));
61 assert_no_err(fcntl(fd
, F_SETPROTECTIONCLASS
, 3));
65 assert_no_err(fstat(fd
, &sb
));
67 // Start class rolling
68 int ret
= MKBKeyBagChangeSystemGeneration(NULL
, 1);
70 if (ret
&& ret
!= kMobileKeyBagNotReady
)
71 assert_fail("MKBKeyBagChangeSystemGeneration returned %d\n", ret
);
73 uint32_t keybag_state
;
74 assert(!MKBKeyBagGetSystemGeneration(&keybag_state
)
75 && (keybag_state
& generation_change_in_progress
));
77 static const uint32_t max_ids
= 1000000;
78 static const uint32_t max_ids_per_iter
= 262144;
80 struct cp_listids list_file_ids
= {
81 .api_version
= CPLIDS_API_VERSION_1
,
82 .flags
= (CPLID_USING_OLD_CLASS_KEY
| CPLID_PROT_CLASS_A
| CPLID_PROT_CLASS_B
| CPLID_PROT_CLASS_C
),
85 uint32_t *file_ids
= malloc(4 * max_ids
);
88 bzero(file_ids
, 4 * max_ids
);
91 list_file_ids
.count
= max_ids_per_iter
;
92 list_file_ids
.file_ids
= file_ids
+ count
;
94 if (fsctl("/private/var", HFSIOC_CP_LIST_IDS
, &list_file_ids
, 0) < 0) {
95 assert_with_errno(errno
== EINTR
);
97 bzero(list_file_ids
.state
, sizeof(list_file_ids
.state
));
100 count
+= list_file_ids
.count
;
102 assert(count
< max_ids
);
104 } while (list_file_ids
.count
== max_ids_per_iter
);
106 assert_no_err(statfs("/private/var", &sfs
));
108 bool did_file
= false;
110 hfs_key_auto_roll_args_t auto_roll_args
= {
111 .api_version
= HFS_KEY_AUTO_ROLL_API_LATEST_VERSION
,
112 .flags
= HFS_KEY_AUTO_ROLL_OLD_CLASS_GENERATION
,
115 assert_no_err(fsctl("/private/var", HFSIOC_SET_KEY_AUTO_ROLL
,
116 &auto_roll_args
, 0));
118 auto_roll_args
.min_key_os_version
= auto_roll_args
.max_key_os_version
= 0xffff;
119 auto_roll_args
.flags
= 0xffffffff;
121 assert_no_err(fsctl("/private/var", HFSIOC_GET_KEY_AUTO_ROLL
,
122 &auto_roll_args
, 0));
124 assert(auto_roll_args
.flags
== HFS_KEY_AUTO_ROLL_OLD_CLASS_GENERATION
);
126 for (unsigned i
= 0; i
< count
; ++i
) {
129 if (fsgetpath(path
, sizeof(path
), &sfs
.f_fsid
,
130 (uint64_t)file_ids
[i
]) < 0) {
131 assert_with_errno(errno
== ENOENT
);
135 if (file_ids
[i
] == sb
.st_ino
) {
136 hfs_key_roll_args_t args
= {
137 .api_version
= HFS_KR_API_LATEST_VERSION
,
138 .operation
= HFS_KR_OP_STATUS
,
141 assert_no_err(ffsctl(fd
, HFSIOC_KEY_ROLL
, &args
, 0));
143 assert(args
.done
== -1
144 && (args
.key_class_generation
145 == ((keybag_state
& generation_current
) ^ 1)));
148 assert_with_errno((raw_fd
= open_dprotected_np(CLASS_ROLL_TEST_FILE
,
149 O_RDONLY
, 0, 1, 0)) >= 0);
151 uint16_t old_key_rev
= args
.key_revision
;
153 assert_no_err(ffsctl(fd
, HFSIOC_KEY_ROLL
, &args
, 0));
155 assert(args
.done
== 0
156 && (args
.key_class_generation
157 == ((keybag_state
& generation_current
))));
159 assert((args
.key_revision
& 0xff00)
160 == ((old_key_rev
+ 0x0100) & 0xff00));
162 // Force flush of cache
163 assert_no_err(msync(p
, sb
.st_size
, MS_INVALIDATE
));
166 for (int i
= 0; i
< 1000000; ++i
) {
171 args
.operation
= HFS_KR_OP_STEP
;
173 assert_no_err(ffsctl(fd
, HFSIOC_KEY_ROLL
, &args
, 0));
175 assert(args
.done
== 2 * 1024 * 1024);
177 // Force flush of cache
178 assert_no_err(msync(p
, sb
.st_size
, MS_INVALIDATE
));
180 // Check the file again
181 for (int i
= 0; i
< 1000000; ++i
) {
185 // We're done with the mmap
186 assert_no_err(munmap(p
, sb
.st_size
));
188 assert_no_err(close(raw_fd
));
193 assert_no_err(fsctl("/private/var", HFSIOC_OP_CPFORCEREWRAP
,
198 uint32_t flags
= HFS_SET_CPFLAG
;
200 fsctl("/private/var", HFSIOC_OP_CRYPTOGEN
, &flags
, 0);
203 while (!_SecKeychainRollKeys(true, NULL
) && ++attempts
< 1000)
205 assert(attempts
< 1000);
207 // Tell MobileKeyBag that we're done
208 assert(!MKBKeyBagChangeSystemGeneration(NULL
, 2));
209 assert(!MKBKeyBagGetSystemGeneration(&keybag_state
));
213 auto_roll_args
.min_key_os_version
= auto_roll_args
.max_key_os_version
= 0xffff;
214 auto_roll_args
.flags
= 0xffffffff;
216 assert_no_err(fsctl("/private/var", HFSIOC_GET_KEY_AUTO_ROLL
,
217 &auto_roll_args
, 0));
219 assert(!auto_roll_args
.flags
);
220 assert(auto_roll_args
.min_key_os_version
== 0);
221 assert(auto_roll_args
.max_key_os_version
== 0);
228 #endif // TARGET_OS_IPHONE & !SIM