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