]> git.saurik.com Git - apple/hfs.git/blob - tests/cases/test-class-roll.c
hfs-366.1.1.tar.gz
[apple/hfs.git] / tests / cases / test-class-roll.c
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