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.
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.
13 #include <TargetConditionals.h>
15 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
19 #include <sys/param.h>
20 #include <sys/mount.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>
28 #define CONFIG_PROTECT 1
30 #include "hfs-tests.h"
31 #include "test-utils.h"
33 #include "../../core/hfs_fsctl.h"
35 TEST(getattrlist_dprotect, .run_as_root = true)
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;
43 #define TEST_FILE "/tmp/getattrlist_dprotect.data"
45 int run_getattrlist_dprotect(__unused test_ctx_t *ctx)
47 // The root file system needs to be HFS
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");
58 int fd = open_dprotected_np(TEST_FILE,
59 O_RDWR | O_CREAT, 3, 0, 0666);
61 assert_with_errno(fd >= 0);
63 struct attrlist attrlist = {
64 .bitmapcount = ATTR_BIT_MAP_COUNT,
65 .commonattr = ATTR_CMN_DATA_PROTECT_FLAGS,
73 assert_no_err(fgetattrlist(fd, &attrlist, &attrs, sizeof(attrs), 0));
75 // The generation should not be returned here
76 assert(attrs.dp_flags == 3);
78 // Check Foundation's API
79 NSFileManager *fm = [NSFileManager defaultManager];
81 assert([[fm attributesOfItemAtPath:@TEST_FILE error:NULL][NSFileProtectionKey]
82 isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication]);
85 assert([fm setAttributes:@{ NSFileProtectionKey: NSFileProtectionComplete }
86 ofItemAtPath:@TEST_FILE
89 assert([[fm attributesOfItemAtPath:@TEST_FILE error:NULL][NSFileProtectionKey]
90 isEqualToString:NSFileProtectionComplete]);
92 uint32_t keybag_state;
93 assert(!MKBKeyBagGetSystemGeneration(&keybag_state));
96 int ret = MKBKeyBagChangeSystemGeneration(NULL, 1);
98 if (ret && ret != kMobileKeyBagNotReady)
99 assert_fail("MKBKeyBagChangeSystemGeneration returned %d\n", ret);
101 assert(!MKBKeyBagGetSystemGeneration(&keybag_state)
102 && (keybag_state & generation_change_in_progress));
104 static const uint32_t max_ids = 1000000;
105 static const uint32_t max_ids_per_iter = 262144;
107 struct listxattrid_cp list_file_ids = {
108 .flags = (LSXCP_PROT_CLASS_A | LSXCP_PROT_CLASS_B | LSXCP_PROT_CLASS_C),
111 uint32_t *file_ids = malloc(4 * max_ids);
114 bzero(file_ids, 4 * max_ids);
117 list_file_ids.count = max_ids_per_iter;
118 list_file_ids.fileid = file_ids + count;
120 if (fsctl("/private/var", HFSIOC_LISTXATTRID_CP, &list_file_ids, 0) < 0) {
121 assert_with_errno(errno == EINTR);
123 bzero(list_file_ids.state, sizeof(list_file_ids.state));
126 count += list_file_ids.count;
128 assert(count < max_ids);
129 } while (list_file_ids.count == max_ids_per_iter);
131 assert_no_err(statfs("/private/var", &sfs));
133 for (unsigned i = 0; i < count; ++i) {
136 if (fsgetpath(path, sizeof(path), &sfs.f_fsid,
137 (uint64_t)file_ids[i]) < 0) {
138 assert_with_errno(errno == ENOENT);
142 if (fsctl("/private/var", HFSIOC_OP_CPFORCEREWRAP,
143 &file_ids[i], 0) != 0) {
144 assert_with_errno(errno == ENOENT);
149 uint32_t flags = HFS_SET_CPFLAG;
151 fsctl("/private/var", HFSIOC_OP_CRYPTOGEN, &flags, 0);
154 while (!_SecKeychainRollKeys(true, NULL) && ++attempts < 1000)
156 assert(attempts < 1000);
158 // Tell MobileKeyBag that we're done
159 assert(!MKBKeyBagChangeSystemGeneration(NULL, 2));
162 assert_no_err(fgetattrlist(fd, &attrlist, &attrs, sizeof(attrs), 0));
164 // The class should still be A
165 assert(attrs.dp_flags == 1);
167 // Check Foundation's API
168 assert([[fm attributesOfItemAtPath:@TEST_FILE error:NULL][NSFileProtectionKey]
169 isEqualToString:NSFileProtectionComplete]);
172 assert([fm setAttributes:@{ NSFileProtectionKey: NSFileProtectionCompleteUntilFirstUserAuthentication }
173 ofItemAtPath:@TEST_FILE
176 assert([[fm attributesOfItemAtPath:@TEST_FILE error:NULL][NSFileProtectionKey]
177 isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication]);
179 assert_no_err(close(fd));
180 assert_no_err(unlink(TEST_FILE));
183 fd = open_dprotected_np(TEST_FILE,
184 O_RDWR | O_CREAT, 3, 0, 0666);
186 assert_with_errno(fd >= 0);
188 assert_no_err(fgetattrlist(fd, &attrlist, &attrs, sizeof(attrs), 0));
191 assert(attrs.dp_flags == 3);
193 // Check Foundation's API
194 assert([[fm attributesOfItemAtPath:@TEST_FILE error:NULL][NSFileProtectionKey]
195 isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication]);
198 assert([fm setAttributes:@{ NSFileProtectionKey: NSFileProtectionComplete }
199 ofItemAtPath:@TEST_FILE
202 assert([[fm attributesOfItemAtPath:@TEST_FILE error:NULL][NSFileProtectionKey]
203 isEqualToString:NSFileProtectionComplete]);
205 assert_no_err(unlink(TEST_FILE));
207 printf("[PASSED] getattrlist-dprotect\n");
212 #endif // (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)