]> git.saurik.com Git - apple/hfs.git/blob - tests/cases/test-class-roll.c
hfs-522.0.9.tar.gz
[apple/hfs.git] / tests / cases / test-class-roll.c
1 #include <TargetConditionals.h>
2
3 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
4
5 #include <unistd.h>
6 #include <sys/stat.h>
7 #include <sys/mman.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>
13 #include <sys/mount.h>
14 #include <sys/param.h>
15
16 #include "hfs-tests.h"
17 #include "test-utils.h"
18
19 TEST(class_roll, .run_as_root = true)
20
21 #define CLASS_ROLL_TEST_FILE "/tmp/class-roll.data"
22
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;
28
29 int run_class_roll(__unused test_ctx_t *ctx)
30 {
31 // The root file system needs to be HFS
32 struct statfs sfs;
33
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");
37 return 0;
38 }
39
40 // Let's create a file to work with
41 unlink(CLASS_ROLL_TEST_FILE);
42 int fd;
43
44 assert_with_errno((fd = open(CLASS_ROLL_TEST_FILE,
45 O_CREAT | O_RDWR, 0666)) >= 0);
46
47 size_t size = 1000000 * 4;
48
49 assert_no_err(ftruncate(fd, size));
50
51 int *p;
52 assert_with_errno((p = mmap(NULL, size, PROT_READ | PROT_WRITE,
53 MAP_SHARED, fd, 0)) != MAP_FAILED);
54
55 for (int i = 0; i < 1000000; ++i)
56 p[i] = i;
57
58 assert_no_err(msync(p, size, MS_SYNC));
59
60 // Switch to class C
61 assert_no_err(fcntl(fd, F_SETPROTECTIONCLASS, 3));
62
63 // Figure out the ID
64 struct stat sb;
65 assert_no_err(fstat(fd, &sb));
66
67 // Start class rolling
68 int ret = MKBKeyBagChangeSystemGeneration(NULL, 1);
69
70 if (ret && ret != kMobileKeyBagNotReady)
71 assert_fail("MKBKeyBagChangeSystemGeneration returned %d\n", ret);
72
73 uint32_t keybag_state;
74 assert(!MKBKeyBagGetSystemGeneration(&keybag_state)
75 && (keybag_state & generation_change_in_progress));
76
77 static const uint32_t max_ids = 1000000;
78 static const uint32_t max_ids_per_iter = 262144;
79
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),
83 };
84
85 uint32_t *file_ids = malloc(4 * max_ids);
86 uint32_t count = 0;
87
88 bzero(file_ids, 4 * max_ids);
89
90 do {
91 list_file_ids.count = max_ids_per_iter;
92 list_file_ids.file_ids = file_ids + count;
93
94 if (fsctl("/private/var", HFSIOC_CP_LIST_IDS, &list_file_ids, 0) < 0) {
95 assert_with_errno(errno == EINTR);
96 count = 0;
97 bzero(list_file_ids.state, sizeof(list_file_ids.state));
98 continue;
99 }
100 count += list_file_ids.count;
101
102 assert(count < max_ids);
103
104 } while (list_file_ids.count == max_ids_per_iter);
105
106 assert_no_err(statfs("/private/var", &sfs));
107
108 bool did_file = false;
109
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,
113 };
114
115 assert_no_err(fsctl("/private/var", HFSIOC_SET_KEY_AUTO_ROLL,
116 &auto_roll_args, 0));
117
118 auto_roll_args.min_key_os_version = auto_roll_args.max_key_os_version = 0xffff;
119 auto_roll_args.flags = 0xffffffff;
120
121 assert_no_err(fsctl("/private/var", HFSIOC_GET_KEY_AUTO_ROLL,
122 &auto_roll_args, 0));
123
124 assert(auto_roll_args.flags == HFS_KEY_AUTO_ROLL_OLD_CLASS_GENERATION);
125
126 for (unsigned i = 0; i < count; ++i) {
127 char path[PATH_MAX];
128
129 if (fsgetpath(path, sizeof(path), &sfs.f_fsid,
130 (uint64_t)file_ids[i]) < 0) {
131 assert_with_errno(errno == ENOENT);
132 continue;
133 }
134
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,
139 };
140
141 assert_no_err(ffsctl(fd, HFSIOC_KEY_ROLL, &args, 0));
142
143 assert(args.done == -1
144 && (args.key_class_generation
145 == ((keybag_state & generation_current) ^ 1)));
146
147 int raw_fd;
148 assert_with_errno((raw_fd = open_dprotected_np(CLASS_ROLL_TEST_FILE,
149 O_RDONLY, 0, 1, 0)) >= 0);
150
151 uint16_t old_key_rev = args.key_revision;
152
153 assert_no_err(ffsctl(fd, HFSIOC_KEY_ROLL, &args, 0));
154
155 assert(args.done == 0
156 && (args.key_class_generation
157 == ((keybag_state & generation_current))));
158
159 assert((args.key_revision & 0xff00)
160 == ((old_key_rev + 0x0100) & 0xff00));
161
162 // Force flush of cache
163 assert_no_err(msync(p, sb.st_size, MS_INVALIDATE));
164
165 // Check the file
166 for (int i = 0; i < 1000000; ++i) {
167 assert(p[i] == i);
168 }
169
170 // Roll 1 chunk
171 args.operation = HFS_KR_OP_STEP;
172
173 assert_no_err(ffsctl(fd, HFSIOC_KEY_ROLL, &args, 0));
174
175 assert(args.done == 2 * 1024 * 1024);
176
177 // Force flush of cache
178 assert_no_err(msync(p, sb.st_size, MS_INVALIDATE));
179
180 // Check the file again
181 for (int i = 0; i < 1000000; ++i) {
182 assert(p[i] == i);
183 }
184
185 // We're done with the mmap
186 assert_no_err(munmap(p, sb.st_size));
187
188 assert_no_err(close(raw_fd));
189
190 did_file = true;
191 }
192
193 assert_no_err(fsctl("/private/var", HFSIOC_OP_CPFORCEREWRAP,
194 &file_ids[i], 0));
195 }
196
197 // Mark as done
198 uint32_t flags = HFS_SET_CPFLAG;
199
200 fsctl("/private/var", HFSIOC_OP_CRYPTOGEN, &flags, 0);
201
202 int attempts = 0;
203 while (!_SecKeychainRollKeys(true, NULL) && ++attempts < 1000)
204 ;
205 assert(attempts < 1000);
206
207 // Tell MobileKeyBag that we're done
208 assert(!MKBKeyBagChangeSystemGeneration(NULL, 2));
209 assert(!MKBKeyBagGetSystemGeneration(&keybag_state));
210
211 assert(did_file);
212
213 auto_roll_args.min_key_os_version = auto_roll_args.max_key_os_version = 0xffff;
214 auto_roll_args.flags = 0xffffffff;
215
216 assert_no_err(fsctl("/private/var", HFSIOC_GET_KEY_AUTO_ROLL,
217 &auto_roll_args, 0));
218
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);
222
223 free(file_ids);
224
225 return 0;
226 }
227
228 #endif // TARGET_OS_IPHONE & !SIM