]> git.saurik.com Git - apple/hfs.git/blob - tests/cases/test-getattrlist-dprotect.m
hfs-522.0.9.tar.gz
[apple/hfs.git] / tests / cases / test-getattrlist-dprotect.m
1 /*
2 * This test verifies the system calls that extract the data
3 * protection class (A -> D) work properly in the face of a key bag
4 * class roll. Normally, the data protection class only occupies the
5 * lower 5 bits of 32 bit space. However, once we undergo a keyroll
6 * one of the bits becomes hijacked to indicate whether or not the
7 * file belongs to new (vs. old) key bag for the class keys.
8 *
9 * If our APIs are not working properly, this bit (or others) could be
10 * exposed to userland — userland should only see the values 0 -> 32.
11 */
12
13 #include <TargetConditionals.h>
14
15 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
16
17 #include <unistd.h>
18 #include <sys/stat.h>
19 #include <sys/param.h>
20 #include <sys/mount.h>
21 #include <sys/mman.h>
22 #include <sys/sysctl.h>
23 #include <System/sys/fsgetpath.h>
24 #include <MobileKeyBag/MobileKeyBag.h>
25 #import <Foundation/Foundation.h>
26 #import <Security/SecItemPriv.h>
27
28 #define CONFIG_PROTECT 1
29
30 #include "hfs-tests.h"
31 #include "test-utils.h"
32
33 #include "../../core/hfs_fsctl.h"
34
35 TEST(getattrlist_dprotect, .run_as_root = true)
36
37 typedef enum generation_status {
38 generation_current = 1 << 0,
39 generation_change_in_progress = 1 << 1,
40 generation_change_pending = 1 << 2,
41 } generation_status_t;
42
43 #define TEST_FILE "/tmp/getattrlist_dprotect.data"
44
45 int run_getattrlist_dprotect(__unused test_ctx_t *ctx)
46 {
47 // The root file system needs to be HFS
48 struct statfs sfs;
49
50 assert(statfs("/tmp", &sfs) == 0);
51 if (strcmp(sfs.f_fstypename, "hfs")) {
52 printf("getatttrlist_dprotect needs hfs as root file system - skipping.\n");
53 return 0;
54 }
55
56 // Create a file
57 unlink(TEST_FILE);
58 int fd = open_dprotected_np(TEST_FILE,
59 O_RDWR | O_CREAT, 3, 0, 0666);
60
61 assert_with_errno(fd >= 0);
62
63 struct attrlist attrlist = {
64 .bitmapcount = ATTR_BIT_MAP_COUNT,
65 .commonattr = ATTR_CMN_DATA_PROTECT_FLAGS,
66 };
67
68 struct attrs {
69 uint32_t len;
70 uint32_t dp_flags;
71 } attrs;
72
73 assert_no_err(fgetattrlist(fd, &attrlist, &attrs, sizeof(attrs), 0));
74
75 // The generation should not be returned here
76 assert(attrs.dp_flags == 3);
77
78 // Check Foundation's API
79 NSFileManager *fm = [NSFileManager defaultManager];
80
81 assert([[fm attributesOfItemAtPath:@TEST_FILE error:NULL][NSFileProtectionKey]
82 isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication]);
83
84 // Change to class A
85 assert([fm setAttributes:@{ NSFileProtectionKey: NSFileProtectionComplete }
86 ofItemAtPath:@TEST_FILE
87 error:NULL]);
88
89 assert([[fm attributesOfItemAtPath:@TEST_FILE error:NULL][NSFileProtectionKey]
90 isEqualToString:NSFileProtectionComplete]);
91
92 uint32_t keybag_state;
93 assert(!MKBKeyBagGetSystemGeneration(&keybag_state));
94
95 // Class roll
96 int ret = MKBKeyBagChangeSystemGeneration(NULL, 1);
97
98 if (ret && ret != kMobileKeyBagNotReady)
99 assert_fail("MKBKeyBagChangeSystemGeneration returned %d\n", ret);
100
101 assert(!MKBKeyBagGetSystemGeneration(&keybag_state)
102 && (keybag_state & generation_change_in_progress));
103
104 static const uint32_t max_ids = 1000000;
105 static const uint32_t max_ids_per_iter = 262144;
106
107 struct listxattrid_cp list_file_ids = {
108 .flags = (LSXCP_PROT_CLASS_A | LSXCP_PROT_CLASS_B | LSXCP_PROT_CLASS_C),
109 };
110
111 uint32_t *file_ids = malloc(4 * max_ids);
112 uint32_t count = 0;
113
114 bzero(file_ids, 4 * max_ids);
115
116 do {
117 list_file_ids.count = max_ids_per_iter;
118 list_file_ids.fileid = file_ids + count;
119
120 if (fsctl("/private/var", HFSIOC_LISTXATTRID_CP, &list_file_ids, 0) < 0) {
121 assert_with_errno(errno == EINTR);
122 count = 0;
123 bzero(list_file_ids.state, sizeof(list_file_ids.state));
124 continue;
125 }
126 count += list_file_ids.count;
127
128 assert(count < max_ids);
129 } while (list_file_ids.count == max_ids_per_iter);
130
131 assert_no_err(statfs("/private/var", &sfs));
132
133 for (unsigned i = 0; i < count; ++i) {
134 char path[PATH_MAX];
135
136 if (fsgetpath(path, sizeof(path), &sfs.f_fsid,
137 (uint64_t)file_ids[i]) < 0) {
138 assert_with_errno(errno == ENOENT);
139 continue;
140 }
141
142 if (fsctl("/private/var", HFSIOC_OP_CPFORCEREWRAP,
143 &file_ids[i], 0) != 0) {
144 assert_with_errno(errno == ENOENT);
145 }
146 }
147
148 // Mark as done
149 uint32_t flags = HFS_SET_CPFLAG;
150
151 fsctl("/private/var", HFSIOC_OP_CRYPTOGEN, &flags, 0);
152
153 int attempts = 0;
154 while (!_SecKeychainRollKeys(true, NULL) && ++attempts < 1000)
155 ;
156 assert(attempts < 1000);
157
158 // Tell MobileKeyBag that we're done
159 assert(!MKBKeyBagChangeSystemGeneration(NULL, 2));
160
161 // Check class again
162 assert_no_err(fgetattrlist(fd, &attrlist, &attrs, sizeof(attrs), 0));
163
164 // The class should still be A
165 assert(attrs.dp_flags == 1);
166
167 // Check Foundation's API
168 assert([[fm attributesOfItemAtPath:@TEST_FILE error:NULL][NSFileProtectionKey]
169 isEqualToString:NSFileProtectionComplete]);
170
171 // Change to class C
172 assert([fm setAttributes:@{ NSFileProtectionKey: NSFileProtectionCompleteUntilFirstUserAuthentication }
173 ofItemAtPath:@TEST_FILE
174 error:NULL]);
175
176 assert([[fm attributesOfItemAtPath:@TEST_FILE error:NULL][NSFileProtectionKey]
177 isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication]);
178
179 assert_no_err(close(fd));
180 assert_no_err(unlink(TEST_FILE));
181
182 // Create a new file
183 fd = open_dprotected_np(TEST_FILE,
184 O_RDWR | O_CREAT, 3, 0, 0666);
185
186 assert_with_errno(fd >= 0);
187
188 assert_no_err(fgetattrlist(fd, &attrlist, &attrs, sizeof(attrs), 0));
189
190 // Check the class
191 assert(attrs.dp_flags == 3);
192
193 // Check Foundation's API
194 assert([[fm attributesOfItemAtPath:@TEST_FILE error:NULL][NSFileProtectionKey]
195 isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication]);
196
197 // Change to class A
198 assert([fm setAttributes:@{ NSFileProtectionKey: NSFileProtectionComplete }
199 ofItemAtPath:@TEST_FILE
200 error:NULL]);
201
202 assert([[fm attributesOfItemAtPath:@TEST_FILE error:NULL][NSFileProtectionKey]
203 isEqualToString:NSFileProtectionComplete]);
204
205 assert_no_err(unlink(TEST_FILE));
206
207 printf("[PASSED] getattrlist-dprotect\n");
208
209 return 0;
210 }
211
212 #endif // (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)