]>
Commit | Line | Data |
---|---|---|
558d2836 A |
1 | #include <TargetConditionals.h> |
2 | ||
de8ee011 | 3 | #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) |
558d2836 A |
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> | |
de8ee011 A |
13 | #include <sys/mount.h> |
14 | #include <sys/param.h> | |
558d2836 A |
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 | ||
de8ee011 | 228 | #endif // TARGET_OS_IPHONE & !SIM |