1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ 3 4 #include <stdlib.h> 5 #include <sys/types.h> 6 #include <sys/xattr.h> 7 #include <linux/fsverity.h> 8 #include <unistd.h> 9 #include <test_progs.h> 10 #include "test_get_xattr.skel.h" 11 #include "test_set_remove_xattr.skel.h" 12 #include "test_fsverity.skel.h" 13 14 static const char testfile[] = "/tmp/test_progs_fs_kfuncs"; 15 16 static void test_get_xattr(const char *name, const char *value, bool allow_access) 17 { 18 struct test_get_xattr *skel = NULL; 19 int fd = -1, err; 20 int v[32]; 21 22 fd = open(testfile, O_CREAT | O_RDONLY, 0644); 23 if (!ASSERT_GE(fd, 0, "create_file")) 24 return; 25 26 close(fd); 27 fd = -1; 28 29 err = setxattr(testfile, name, value, strlen(value) + 1, 0); 30 if (err && errno == EOPNOTSUPP) { 31 printf("%s:SKIP:local fs doesn't support xattr (%d)\n" 32 "To run this test, make sure /tmp filesystem supports xattr.\n", 33 __func__, errno); 34 test__skip(); 35 goto out; 36 } 37 38 if (!ASSERT_OK(err, "setxattr")) 39 goto out; 40 41 skel = test_get_xattr__open_and_load(); 42 if (!ASSERT_OK_PTR(skel, "test_get_xattr__open_and_load")) 43 goto out; 44 45 skel->bss->monitored_pid = getpid(); 46 err = test_get_xattr__attach(skel); 47 48 if (!ASSERT_OK(err, "test_get_xattr__attach")) 49 goto out; 50 51 fd = open(testfile, O_RDONLY, 0644); 52 53 if (!ASSERT_GE(fd, 0, "open_file")) 54 goto out; 55 56 /* Trigger security_inode_getxattr */ 57 err = getxattr(testfile, name, v, sizeof(v)); 58 59 if (allow_access) { 60 ASSERT_EQ(err, -1, "getxattr_return"); 61 ASSERT_EQ(errno, EINVAL, "getxattr_errno"); 62 ASSERT_EQ(skel->bss->found_xattr_from_file, 1, "found_xattr_from_file"); 63 ASSERT_EQ(skel->bss->found_xattr_from_dentry, 1, "found_xattr_from_dentry"); 64 } else { 65 ASSERT_EQ(err, strlen(value) + 1, "getxattr_return"); 66 ASSERT_EQ(skel->bss->found_xattr_from_file, 0, "found_xattr_from_file"); 67 ASSERT_EQ(skel->bss->found_xattr_from_dentry, 0, "found_xattr_from_dentry"); 68 } 69 70 out: 71 close(fd); 72 test_get_xattr__destroy(skel); 73 remove(testfile); 74 } 75 76 /* xattr value we will set to security.bpf.foo */ 77 static const char value_foo[] = "hello"; 78 79 static void read_and_validate_foo(struct test_set_remove_xattr *skel) 80 { 81 char value_out[32]; 82 int err; 83 84 err = getxattr(testfile, skel->rodata->xattr_foo, value_out, sizeof(value_out)); 85 ASSERT_EQ(err, sizeof(value_foo), "getxattr size foo"); 86 ASSERT_EQ(strncmp(value_out, value_foo, sizeof(value_foo)), 0, "strncmp value_foo"); 87 } 88 89 static void set_foo(struct test_set_remove_xattr *skel) 90 { 91 ASSERT_OK(setxattr(testfile, skel->rodata->xattr_foo, value_foo, strlen(value_foo) + 1, 0), 92 "setxattr foo"); 93 } 94 95 static void validate_bar_match(struct test_set_remove_xattr *skel) 96 { 97 char value_out[32]; 98 int err; 99 100 err = getxattr(testfile, skel->rodata->xattr_bar, value_out, sizeof(value_out)); 101 ASSERT_EQ(err, sizeof(skel->data->value_bar), "getxattr size bar"); 102 ASSERT_EQ(strncmp(value_out, skel->data->value_bar, sizeof(skel->data->value_bar)), 0, 103 "strncmp value_bar"); 104 } 105 106 static void validate_bar_removed(struct test_set_remove_xattr *skel) 107 { 108 char value_out[32]; 109 int err; 110 111 err = getxattr(testfile, skel->rodata->xattr_bar, value_out, sizeof(value_out)); 112 ASSERT_LT(err, 0, "getxattr size bar should fail"); 113 } 114 115 static void test_set_remove_xattr(void) 116 { 117 struct test_set_remove_xattr *skel = NULL; 118 int fd = -1, err; 119 120 fd = open(testfile, O_CREAT | O_RDONLY, 0644); 121 if (!ASSERT_GE(fd, 0, "create_file")) 122 return; 123 124 close(fd); 125 fd = -1; 126 127 skel = test_set_remove_xattr__open_and_load(); 128 if (!ASSERT_OK_PTR(skel, "test_set_remove_xattr__open_and_load")) 129 return; 130 131 /* Set security.bpf.foo to "hello" */ 132 err = setxattr(testfile, skel->rodata->xattr_foo, value_foo, strlen(value_foo) + 1, 0); 133 if (err && errno == EOPNOTSUPP) { 134 printf("%s:SKIP:local fs doesn't support xattr (%d)\n" 135 "To run this test, make sure /tmp filesystem supports xattr.\n", 136 __func__, errno); 137 test__skip(); 138 goto out; 139 } 140 141 if (!ASSERT_OK(err, "setxattr")) 142 goto out; 143 144 skel->bss->monitored_pid = getpid(); 145 err = test_set_remove_xattr__attach(skel); 146 if (!ASSERT_OK(err, "test_set_remove_xattr__attach")) 147 goto out; 148 149 /* First, test not _locked version of the kfuncs with getxattr. */ 150 151 /* Read security.bpf.foo and trigger test_inode_getxattr. This 152 * bpf program will set security.bpf.bar to "world". 153 */ 154 read_and_validate_foo(skel); 155 validate_bar_match(skel); 156 157 /* Read security.bpf.foo and trigger test_inode_getxattr again. 158 * This will remove xattr security.bpf.bar. 159 */ 160 read_and_validate_foo(skel); 161 validate_bar_removed(skel); 162 163 ASSERT_TRUE(skel->bss->set_security_bpf_bar_success, "set_security_bpf_bar_success"); 164 ASSERT_TRUE(skel->bss->remove_security_bpf_bar_success, "remove_security_bpf_bar_success"); 165 ASSERT_TRUE(skel->bss->set_security_selinux_fail, "set_security_selinux_fail"); 166 ASSERT_TRUE(skel->bss->remove_security_selinux_fail, "remove_security_selinux_fail"); 167 168 /* Second, test _locked version of the kfuncs, with setxattr */ 169 170 /* Set security.bpf.foo and trigger test_inode_setxattr. This 171 * bpf program will set security.bpf.bar to "world". 172 */ 173 set_foo(skel); 174 validate_bar_match(skel); 175 176 /* Set security.bpf.foo and trigger test_inode_setxattr again. 177 * This will remove xattr security.bpf.bar. 178 */ 179 set_foo(skel); 180 validate_bar_removed(skel); 181 182 ASSERT_TRUE(skel->bss->locked_set_security_bpf_bar_success, 183 "locked_set_security_bpf_bar_success"); 184 ASSERT_TRUE(skel->bss->locked_remove_security_bpf_bar_success, 185 "locked_remove_security_bpf_bar_success"); 186 ASSERT_TRUE(skel->bss->locked_set_security_selinux_fail, 187 "locked_set_security_selinux_fail"); 188 ASSERT_TRUE(skel->bss->locked_remove_security_selinux_fail, 189 "locked_remove_security_selinux_fail"); 190 191 out: 192 close(fd); 193 test_set_remove_xattr__destroy(skel); 194 remove(testfile); 195 } 196 197 #ifndef SHA256_DIGEST_SIZE 198 #define SHA256_DIGEST_SIZE 32 199 #endif 200 201 static void test_fsverity(void) 202 { 203 struct fsverity_enable_arg arg = {0}; 204 struct test_fsverity *skel = NULL; 205 struct fsverity_digest *d; 206 int fd, err; 207 char buffer[4096]; 208 209 fd = open(testfile, O_CREAT | O_RDWR, 0644); 210 if (!ASSERT_GE(fd, 0, "create_file")) 211 return; 212 213 /* Write random buffer, so the file is not empty */ 214 err = write(fd, buffer, 4096); 215 if (!ASSERT_EQ(err, 4096, "write_file")) 216 goto out; 217 close(fd); 218 219 /* Reopen read-only, otherwise FS_IOC_ENABLE_VERITY will fail */ 220 fd = open(testfile, O_RDONLY, 0644); 221 if (!ASSERT_GE(fd, 0, "open_file1")) 222 return; 223 224 /* Enable fsverity for the file. 225 * If the file system doesn't support verity, this will fail. Skip 226 * the test in such case. 227 */ 228 arg.version = 1; 229 arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; 230 arg.block_size = 4096; 231 err = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg); 232 if (err) { 233 printf("%s:SKIP:local fs doesn't support fsverity (%d)\n" 234 "To run this test, try enable CONFIG_FS_VERITY and enable FSVerity for the filesystem.\n", 235 __func__, errno); 236 test__skip(); 237 goto out; 238 } 239 240 skel = test_fsverity__open_and_load(); 241 if (!ASSERT_OK_PTR(skel, "test_fsverity__open_and_load")) 242 goto out; 243 244 /* Get fsverity_digest from ioctl */ 245 d = (struct fsverity_digest *)skel->bss->expected_digest; 246 d->digest_algorithm = FS_VERITY_HASH_ALG_SHA256; 247 d->digest_size = SHA256_DIGEST_SIZE; 248 err = ioctl(fd, FS_IOC_MEASURE_VERITY, skel->bss->expected_digest); 249 if (!ASSERT_OK(err, "ioctl_FS_IOC_MEASURE_VERITY")) 250 goto out; 251 252 skel->bss->monitored_pid = getpid(); 253 err = test_fsverity__attach(skel); 254 if (!ASSERT_OK(err, "test_fsverity__attach")) 255 goto out; 256 257 /* Reopen the file to trigger the program */ 258 close(fd); 259 fd = open(testfile, O_RDONLY); 260 if (!ASSERT_GE(fd, 0, "open_file2")) 261 goto out; 262 263 ASSERT_EQ(skel->bss->got_fsverity, 1, "got_fsverity"); 264 ASSERT_EQ(skel->bss->digest_matches, 1, "digest_matches"); 265 out: 266 close(fd); 267 test_fsverity__destroy(skel); 268 remove(testfile); 269 } 270 271 void test_fs_kfuncs(void) 272 { 273 /* Matches xattr_names in progs/test_get_xattr.c */ 274 if (test__start_subtest("user_xattr")) 275 test_get_xattr("user.kfuncs", "hello", true); 276 277 if (test__start_subtest("security_bpf_xattr")) 278 test_get_xattr("security.bpf.xxx", "hello", true); 279 280 if (test__start_subtest("security_bpf_xattr_error")) 281 test_get_xattr("security.bpf", "hello", false); 282 283 if (test__start_subtest("security_selinux_xattr_error")) 284 test_get_xattr("security.selinux", "hello", false); 285 286 if (test__start_subtest("set_remove_xattr")) 287 test_set_remove_xattr(); 288 289 if (test__start_subtest("fsverity")) 290 test_fsverity(); 291 } 292