1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2026 Isovalent */ 3 4 #include <test_progs.h> 5 #include <sys/syscall.h> 6 #include <sys/mman.h> 7 #include <sys/wait.h> 8 #include <sys/stat.h> 9 #include <fcntl.h> 10 #include <limits.h> 11 #include <linux/keyctl.h> 12 #include <linux/bpf.h> 13 14 #include <bpf/btf.h> 15 16 #include "bpf/libbpf_internal.h" /* for libbpf_sha256() */ 17 #include "bpf/skel_internal.h" /* for loader ctx layout (bpf_loader_ctx etc) */ 18 19 #include "test_signed_loader.skel.h" 20 #include "test_signed_loader_map.skel.h" 21 #include "test_signed_loader_data.skel.h" 22 #include "test_signed_loader_lsm.skel.h" 23 24 enum { 25 BPF_SIG_UNSIGNED = 0, 26 BPF_SIG_VERIFIED, 27 }; 28 29 enum { 30 BPF_SIG_KEYRING_NONE = 0, 31 BPF_SIG_KEYRING_BUILTIN, 32 BPF_SIG_KEYRING_SECONDARY, 33 BPF_SIG_KEYRING_PLATFORM, 34 BPF_SIG_KEYRING_USER, 35 BPF_SIG_KEYRING_BPF, 36 }; 37 38 #ifndef KEY_SPEC_BPF_KEYRING 39 #define KEY_SPEC_BPF_KEYRING -9 40 #endif 41 42 /* verify_sig_setup.sh exits with this when openssl cannot do ML-DSA. */ 43 #define SETUP_SKIP (-77) 44 45 /* FIPS-204 ML-DSA-87 signature size, see include/crypto/mldsa.h. */ 46 #define MLDSA87_SIGNATURE_SIZE 4627 47 48 static int load_loader_log(const void *insns, __u32 insns_sz, int map_fd, 49 const void *sig, __u32 sig_sz, __s32 keyring_id, 50 __u32 fd_array_cnt, char *log_buf, __u32 log_sz) 51 { 52 union bpf_attr attr; 53 int fd; 54 55 memset(&attr, 0, sizeof(attr)); 56 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 57 attr.insns = ptr_to_u64(insns); 58 attr.insn_cnt = insns_sz / sizeof(struct bpf_insn); 59 attr.license = ptr_to_u64("Dual BSD/GPL"); 60 attr.prog_flags = BPF_F_SLEEPABLE; 61 attr.fd_array = ptr_to_u64(&map_fd); 62 attr.fd_array_cnt = fd_array_cnt; 63 if (sig) { 64 attr.signature = ptr_to_u64(sig); 65 attr.signature_size = sig_sz; 66 attr.keyring_id = keyring_id; 67 } 68 if (log_buf) { 69 attr.log_level = 1; 70 attr.log_buf = ptr_to_u64(log_buf); 71 attr.log_size = log_sz; 72 } 73 memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog")); 74 fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 75 offsetofend(union bpf_attr, keyring_id)); 76 return fd < 0 ? -errno : fd; 77 } 78 79 static int load_loader(const void *insns, __u32 insns_sz, int map_fd, 80 const void *sig, __u32 sig_sz, __s32 keyring_id, 81 __u32 fd_array_cnt) 82 { 83 return load_loader_log(insns, insns_sz, map_fd, sig, sig_sz, keyring_id, 84 fd_array_cnt, NULL, 0); 85 } 86 87 static int run_gen_loader(const void *insns, __u32 insns_sz, 88 const void *data, __u32 data_sz, 89 const void *excl, __u32 excl_sz, 90 const void *sig, __u32 sig_sz, 91 void *ctx, __u32 ctx_sz, bool *loader_ran) 92 { 93 LIBBPF_OPTS(bpf_map_create_opts, mopts, 94 .excl_prog_hash = excl, 95 .excl_prog_hash_size = excl_sz); 96 __u32 key = 0; 97 union bpf_attr attr; 98 int map_fd, prog_fd, ret; 99 100 *loader_ran = false; 101 102 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 103 4, data_sz, 1, &mopts); 104 if (map_fd < 0) 105 return -errno; 106 if (bpf_map_update_elem(map_fd, &key, data, 0)) { 107 ret = -errno; 108 goto out_map; 109 } 110 if (bpf_map_freeze(map_fd)) { 111 ret = -errno; 112 goto out_map; 113 } 114 115 memset(&attr, 0, sizeof(attr)); 116 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 117 attr.insns = ptr_to_u64(insns); 118 attr.insn_cnt = insns_sz / sizeof(struct bpf_insn); 119 attr.license = ptr_to_u64("Dual BSD/GPL"); 120 attr.prog_flags = BPF_F_SLEEPABLE; 121 attr.fd_array = ptr_to_u64(&map_fd); 122 if (sig) { 123 attr.signature = ptr_to_u64(sig); 124 attr.signature_size = sig_sz; 125 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 126 attr.fd_array_cnt = 1; 127 } 128 memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog")); 129 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 130 offsetofend(union bpf_attr, keyring_id)); 131 if (prog_fd < 0) { 132 ret = -errno; 133 goto out_map; 134 } 135 136 memset(&attr, 0, sizeof(attr)); 137 attr.test.prog_fd = prog_fd; 138 attr.test.ctx_in = ptr_to_u64(ctx); 139 attr.test.ctx_size_in = ctx_sz; 140 if (syscall(__NR_bpf, BPF_PROG_RUN, &attr, 141 offsetofend(union bpf_attr, test)) < 0) { 142 ret = -errno; 143 goto out_prog; 144 } 145 *loader_ran = true; 146 ret = (int)attr.test.retval; 147 out_prog: 148 close(prog_fd); 149 out_map: 150 close(map_fd); 151 return ret; 152 } 153 154 static void close_loader_ctx_fds(void *ctx, int nr_maps, int nr_progs) 155 { 156 struct bpf_map_desc *md = (struct bpf_map_desc *)((char *)ctx + 157 sizeof(struct bpf_loader_ctx)); 158 struct bpf_prog_desc *pd = (struct bpf_prog_desc *)(md + nr_maps); 159 int i; 160 161 for (i = 0; i < nr_maps; i++) 162 if (md[i].map_fd > 0) 163 close(md[i].map_fd); 164 for (i = 0; i < nr_progs; i++) 165 if (pd[i].prog_fd > 0) 166 close(pd[i].prog_fd); 167 } 168 169 static int run_setup(const char *cmd, const char *dir) 170 { 171 int pid, status; 172 173 pid = fork(); 174 if (pid < 0) 175 return -errno; 176 if (pid == 0) { 177 execlp("./verify_sig_setup.sh", "./verify_sig_setup.sh", 178 cmd, dir, NULL); 179 exit(1); 180 } 181 if (waitpid(pid, &status, 0) < 0) 182 return -errno; 183 if (!WIFEXITED(status)) 184 return -EINVAL; 185 return -WEXITSTATUS(status); 186 } 187 188 static void genkey_dir_fini(const char *dir) 189 { 190 static const char * const files[] = { 191 "signing_key.der", "signing_key.pem", "x509.genkey", 192 }; 193 char path[PATH_MAX]; 194 size_t i; 195 196 if (!dir) 197 return; 198 for (i = 0; i < ARRAY_SIZE(files); i++) { 199 snprintf(path, sizeof(path), "%s/%s", dir, files[i]); 200 unlink(path); 201 } 202 rmdir(dir); 203 } 204 205 static int sign_buf_digest(const char *dir, const void *buf, __u32 len, 206 void *sig, __u32 *sig_sz, const char *digest) 207 { 208 char data_tmpl[PATH_MAX], key[PATH_MAX]; 209 char sigpath[PATH_MAX + sizeof(".p7s")]; 210 int fd, pid, status, ret; 211 struct stat st; 212 213 ret = snprintf(data_tmpl, sizeof(data_tmpl), "%s/dataXXXXXX", dir); 214 if (ret < 0 || ret >= (int)sizeof(data_tmpl)) 215 return -ENAMETOOLONG; 216 ret = 0; 217 218 fd = mkstemp(data_tmpl); 219 if (fd < 0) 220 return -errno; 221 snprintf(sigpath, sizeof(sigpath), "%s.p7s", data_tmpl); 222 if (write(fd, buf, len) != (ssize_t)len) { 223 close(fd); 224 ret = -EIO; 225 goto out; 226 } 227 close(fd); 228 229 pid = fork(); 230 if (pid < 0) { 231 ret = -errno; 232 goto out; 233 } 234 if (pid == 0) { 235 snprintf(key, sizeof(key), "%s/signing_key.pem", dir); 236 execlp("./sign-file", "./sign-file", "-d", digest, 237 key, key, data_tmpl, NULL); 238 exit(1); 239 } 240 if (waitpid(pid, &status, 0) < 0 || 241 !WIFEXITED(status) || WEXITSTATUS(status)) { 242 ret = -EINVAL; 243 goto out; 244 } 245 246 if (stat(sigpath, &st) < 0) { 247 ret = -errno; 248 goto out; 249 } 250 if (st.st_size > (off_t)*sig_sz) { 251 ret = -E2BIG; 252 goto out; 253 } 254 fd = open(sigpath, O_RDONLY); 255 if (fd < 0) { 256 ret = -errno; 257 goto out; 258 } 259 if (read(fd, sig, st.st_size) != st.st_size) { 260 close(fd); 261 ret = -EIO; 262 goto out; 263 } 264 close(fd); 265 *sig_sz = st.st_size; 266 out: 267 unlink(sigpath); 268 unlink(data_tmpl); 269 return ret; 270 } 271 272 static int sign_buf(const char *dir, const void *buf, __u32 len, 273 void *sig, __u32 *sig_sz) 274 { 275 return sign_buf_digest(dir, buf, len, sig, sig_sz, "sha256"); 276 } 277 278 struct gen_loader_fixture { 279 struct test_signed_loader *skel; 280 struct gen_loader_opts gopts; 281 unsigned char *blob; 282 void *ctx; 283 __u32 data_sz; 284 __u32 ctx_sz; 285 int nr_maps; 286 int nr_progs; 287 __u8 excl[SHA256_DIGEST_LENGTH]; 288 }; 289 290 static int gen_loader_fixture_init(struct gen_loader_fixture *f) 291 { 292 LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true); 293 int nr_maps = 0, nr_progs = 0; 294 struct bpf_program *p; 295 struct bpf_map *m; 296 297 memset(f, 0, sizeof(*f)); 298 f->skel = test_signed_loader__open(); 299 if (!ASSERT_OK_PTR(f->skel, "skel_open")) 300 return -1; 301 if (!ASSERT_OK(bpf_object__gen_loader(f->skel->obj, &gopts), "gen_loader")) 302 return -1; 303 if (!ASSERT_OK(bpf_object__load(f->skel->obj), "gen_load")) 304 return -1; 305 f->gopts = gopts; 306 307 bpf_object__for_each_program(p, f->skel->obj) 308 nr_progs++; 309 bpf_object__for_each_map(m, f->skel->obj) 310 nr_maps++; 311 f->nr_maps = nr_maps; 312 f->nr_progs = nr_progs; 313 f->ctx_sz = sizeof(struct bpf_loader_ctx) + 314 nr_maps * sizeof(struct bpf_map_desc) + 315 nr_progs * sizeof(struct bpf_prog_desc); 316 f->ctx = calloc(1, f->ctx_sz); 317 if (!ASSERT_OK_PTR(f->ctx, "ctx_alloc")) 318 return -1; 319 ((struct bpf_loader_ctx *)f->ctx)->sz = f->ctx_sz; 320 321 f->data_sz = gopts.data_sz; 322 f->blob = malloc(f->data_sz); 323 if (!ASSERT_OK_PTR(f->blob, "blob_alloc")) 324 return -1; 325 memcpy(f->blob, gopts.data, f->data_sz); 326 327 /* excl_prog_hash = SHA256(loader insns) == the loader's prog->digest. */ 328 libbpf_sha256(gopts.insns, gopts.insns_sz, f->excl); 329 return 0; 330 } 331 332 static void gen_loader_fixture_fini(struct gen_loader_fixture *f) 333 { 334 if (f->ctx) 335 close_loader_ctx_fds(f->ctx, f->nr_maps, f->nr_progs); 336 free(f->blob); 337 free(f->ctx); 338 test_signed_loader__destroy(f->skel); 339 } 340 341 static void metadata_match(void) 342 { 343 struct gen_loader_fixture f; 344 bool ran; 345 int r; 346 347 if (gen_loader_fixture_init(&f) == 0) { 348 r = run_gen_loader(f.gopts.insns, f.gopts.insns_sz, f.blob, 349 f.data_sz, f.excl, sizeof(f.excl), NULL, 0, 350 f.ctx, f.ctx_sz, &ran); 351 ASSERT_TRUE(ran, "loader ran"); 352 ASSERT_EQ(r, 0, "honest loader retval"); 353 } 354 gen_loader_fixture_fini(&f); 355 } 356 357 static void signature_enforced(void) 358 { 359 static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, }; 360 struct gen_loader_fixture f; 361 int fd; 362 363 if (gen_loader_fixture_init(&f) == 0) { 364 /* 365 * A present-but-invalid signature (the cert bytes are not a 366 * PKCS#7 signature) must be rejected at load: the signature 367 * path is honored, not ignored. (The valid path is covered by 368 * the signed lskels.) Pin -EBADMSG, the PKCS#7 parse failure: 369 * a looser fd < 0 check could also be satisfied by the sparse 370 * fd_array rejection (-EACCES) that the loader's map reference 371 * would trip even if the signature were silently ignored. 372 */ 373 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk, 374 sizeof(junk), KEY_SPEC_SESSION_KEYRING, 0); 375 ASSERT_EQ(fd, -EBADMSG, "invalid signature rejected at load"); 376 if (fd >= 0) 377 close(fd); 378 } 379 gen_loader_fixture_fini(&f); 380 } 381 382 /* 383 * CO-RE relocations would have the kernel rewrite the insns after the 384 * signature was checked over them, with values the loader picks through the 385 * BTF it supplies. A signed load that carries any is rejected before the 386 * signature is examined, so junk serves as one. 387 */ 388 static void signed_core_relo_rejected(void) 389 { 390 static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, }; 391 struct bpf_insn insns[] = { 392 BPF_MOV64_IMM(BPF_REG_0, 0), 393 BPF_EXIT_INSN(), 394 }; 395 struct bpf_core_relo relo = { 396 .insn_off = 0, 397 .type_id = 1, 398 .kind = BPF_CORE_TYPE_ID_LOCAL, 399 }; 400 char log_buf[1024] = {}; 401 union bpf_attr attr; 402 int fd; 403 404 memset(&attr, 0, sizeof(attr)); 405 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 406 attr.insns = ptr_to_u64(insns); 407 attr.insn_cnt = ARRAY_SIZE(insns); 408 attr.license = ptr_to_u64("Dual BSD/GPL"); 409 attr.prog_flags = BPF_F_SLEEPABLE; 410 attr.signature = ptr_to_u64(junk); 411 attr.signature_size = sizeof(junk); 412 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 413 attr.core_relos = ptr_to_u64(&relo); 414 attr.core_relo_cnt = 1; 415 attr.core_relo_rec_size = sizeof(relo); 416 attr.log_level = 1; 417 attr.log_buf = ptr_to_u64(log_buf); 418 attr.log_size = sizeof(log_buf); 419 fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, offsetofend(union bpf_attr, keyring_id)); 420 fd = fd < 0 ? -errno : fd; 421 ASSERT_EQ(fd, -EACCES, "signed load with CO-RE relocations rejected"); 422 ASSERT_HAS_SUBSTR(log_buf, "signed program cannot carry CO-RE relocations", "log"); 423 if (fd >= 0) 424 close(fd); 425 } 426 427 static void signed_nonexcl_fd_array_rejected(void) 428 { 429 static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, }; 430 struct gen_loader_fixture f; 431 int map_fd, fd; 432 433 if (gen_loader_fixture_init(&f) == 0) { 434 /* 435 * A signed program may only bind exclusive maps through fd_array 436 * (their contents are folded into the signature). Binding a 437 * non-exclusive map is rejected, before the signature is even 438 * examined. 439 */ 440 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "nonexcl", 4, 441 f.data_sz, 1, NULL); 442 if (ASSERT_OK_FD(map_fd, "nonexcl_map")) { 443 if (ASSERT_OK(bpf_map_freeze(map_fd), "freeze")) { 444 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, 445 map_fd, junk, sizeof(junk), 446 KEY_SPEC_SESSION_KEYRING, 1); 447 ASSERT_EQ(fd, -EPERM, 448 "non-exclusive map in signed fd_array rejected"); 449 if (fd >= 0) 450 close(fd); 451 } 452 close(map_fd); 453 } 454 } 455 gen_loader_fixture_fini(&f); 456 } 457 458 static void signed_unfrozen_fd_array_rejected(void) 459 { 460 static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, }; 461 LIBBPF_OPTS(bpf_map_create_opts, mopts); 462 struct gen_loader_fixture f; 463 __u32 key = 0; 464 int map_fd, fd; 465 466 if (gen_loader_fixture_init(&f) == 0) { 467 /* 468 * The metadata map must be frozen before a signed load so the 469 * folded bytes cannot change afterwards. Bind an exclusive map 470 * with matching contents but skip the freeze: the load must be 471 * rejected by the frozen check with -EPERM. The exclusivity 472 * check right after it would pass, so the errno uniquely pins 473 * the freeze requirement. 474 */ 475 mopts.excl_prog_hash = f.excl; 476 mopts.excl_prog_hash_size = sizeof(f.excl); 477 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "unfrozen", 4, 478 f.data_sz, 1, &mopts); 479 if (ASSERT_OK_FD(map_fd, "unfrozen_map")) { 480 if (ASSERT_OK(bpf_map_update_elem(map_fd, &key, f.blob, 0), 481 "update")) { 482 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, 483 map_fd, junk, sizeof(junk), 484 KEY_SPEC_SESSION_KEYRING, 1); 485 ASSERT_EQ(fd, -EPERM, 486 "unfrozen map in signed fd_array rejected"); 487 if (fd >= 0) 488 close(fd); 489 } 490 close(map_fd); 491 } 492 } 493 gen_loader_fixture_fini(&f); 494 } 495 496 static void signed_nonarray_fd_array_rejected(void) 497 { 498 static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, }; 499 LIBBPF_OPTS(bpf_map_create_opts, mopts); 500 struct gen_loader_fixture f; 501 int map_fd, fd; 502 503 if (gen_loader_fixture_init(&f) == 0) { 504 /* 505 * Only a plain BPF_MAP_TYPE_ARRAY may be folded into the 506 * signature. An exclusive map of any other type is rejected 507 * (-EINVAL) rather than folded - this is the type gate that 508 * keeps arena maps (map_direct_value_addr() returns a user 509 * address) and insn-array maps (buffer smaller than value_size) 510 * out of the hashed region, where the old code would have 511 * memcpy()'d from them. A hash map stands in here: it is 512 * exclusive (bound to the loader digest) but not an array. 513 */ 514 mopts.excl_prog_hash = f.excl; 515 mopts.excl_prog_hash_size = sizeof(f.excl); 516 map_fd = bpf_map_create(BPF_MAP_TYPE_HASH, "excl_hash", 4, 4, 1, 517 &mopts); 518 if (ASSERT_OK_FD(map_fd, "excl_hash_map")) { 519 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, 520 junk, sizeof(junk), 521 KEY_SPEC_SESSION_KEYRING, 1); 522 ASSERT_EQ(fd, -EINVAL, 523 "non-array map in signed fd_array rejected"); 524 if (fd >= 0) 525 close(fd); 526 close(map_fd); 527 } 528 } 529 gen_loader_fixture_fini(&f); 530 } 531 532 static int setup_meta_map(const struct gen_loader_fixture *f); 533 534 static void signed_btf_fd_array_rejected(void) 535 { 536 char dir_tmpl[] = "/tmp/signed_loader_btfXXXXXX", *dir = NULL; 537 __u32 sig_sz = 8192; 538 int map_fd = -1, prog_fd = -1; 539 unsigned char *buf = NULL; 540 struct gen_loader_fixture f; 541 bool have_fixture = false; 542 struct btf *btf = NULL; 543 union bpf_attr attr; 544 int fds[2]; 545 __u8 sig[8192]; 546 547 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 548 KEY_SPEC_SESSION_KEYRING); 549 dir = mkdtemp(dir_tmpl); 550 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 551 return; 552 if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) { 553 rmdir(dir); 554 return; 555 } 556 have_fixture = true; 557 if (gen_loader_fixture_init(&f) != 0) 558 goto out; 559 560 /* 561 * fd_array binds maps and BTFs alike, but only exclusive array maps are 562 * folded into the signature. Build an otherwise genuinely signed load - 563 * insns || metadata, exclusive frozen map at fd_array[0] - then smuggle 564 * an extra BTF into fd_array[1]. A signed program may not bind any BTF, 565 * so resolving the fd_array entries rejects the BTF with -EACCES (in 566 * __add_used_btf(), before the signature is even verified). 567 */ 568 buf = malloc((size_t)f.gopts.insns_sz + f.data_sz); 569 if (!ASSERT_OK_PTR(buf, "signbuf")) 570 goto out; 571 memcpy(buf, f.gopts.insns, f.gopts.insns_sz); 572 memcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz); 573 if (!ASSERT_OK(sign_buf(dir, buf, f.gopts.insns_sz + f.data_sz, sig, 574 &sig_sz), "sign insns||metadata")) 575 goto out; 576 577 map_fd = setup_meta_map(&f); 578 if (!ASSERT_OK_FD(map_fd, "meta_map")) 579 goto out; 580 btf = btf__new_empty(); 581 if (!ASSERT_OK_PTR(btf, "btf_new_empty")) 582 goto out; 583 btf__add_int(btf, "int", 4, BTF_INT_SIGNED); 584 if (!ASSERT_OK(btf__load_into_kernel(btf), "btf_load")) 585 goto out; 586 587 fds[0] = map_fd; 588 fds[1] = btf__fd(btf); 589 memset(&attr, 0, sizeof(attr)); 590 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 591 attr.insns = ptr_to_u64(f.gopts.insns); 592 attr.insn_cnt = f.gopts.insns_sz / sizeof(struct bpf_insn); 593 attr.license = ptr_to_u64("Dual BSD/GPL"); 594 attr.prog_flags = BPF_F_SLEEPABLE; 595 attr.fd_array = ptr_to_u64(fds); 596 attr.fd_array_cnt = 2; 597 attr.signature = ptr_to_u64(sig); 598 attr.signature_size = sig_sz; 599 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 600 memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog")); 601 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 602 offsetofend(union bpf_attr, keyring_id)); 603 ASSERT_EQ(prog_fd < 0 ? -errno : prog_fd, -EACCES, 604 "BTF in signed fd_array rejected"); 605 if (prog_fd >= 0) 606 close(prog_fd); 607 out: 608 if (btf) 609 btf__free(btf); 610 if (map_fd >= 0) 611 close(map_fd); 612 if (have_fixture) 613 gen_loader_fixture_fini(&f); 614 if (dir) 615 run_setup("cleanup", dir); 616 free(buf); 617 } 618 619 static void signature_failure_logs(void) 620 { 621 static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, }; 622 char log_buf[1024] = {}; 623 struct gen_loader_fixture f; 624 int fd; 625 626 if (gen_loader_fixture_init(&f) == 0) { 627 /* 628 * Signature verification now runs inside bpf_check(), so a 629 * failure is reported through the verifier log. A present-but- 630 * invalid signature is rejected and the log says why. 631 */ 632 fd = load_loader_log(f.gopts.insns, f.gopts.insns_sz, -1, junk, 633 sizeof(junk), KEY_SPEC_SESSION_KEYRING, 0, 634 log_buf, sizeof(log_buf)); 635 ASSERT_LT(fd, 0, "invalid signature rejected at load"); 636 if (fd >= 0) 637 close(fd); 638 ASSERT_HAS_SUBSTR(log_buf, "signature verification failed", 639 "verifier logs signature failure"); 640 } 641 gen_loader_fixture_fini(&f); 642 } 643 644 static void signature_too_large(void) 645 { 646 static const __u8 junk[64] = {}; 647 struct gen_loader_fixture f; 648 int fd; 649 650 if (gen_loader_fixture_init(&f) == 0) { 651 /* 652 * signature_size beyond the kernel's bound 653 * (BPF_PROG_MAX_SIGNATURE_SIZE) is rejected before the buffer 654 * is read. 655 */ 656 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk, 657 64 << 20, KEY_SPEC_SESSION_KEYRING, 0); 658 ASSERT_EQ(fd, -EINVAL, "oversized signature rejected"); 659 if (fd >= 0) 660 close(fd); 661 } 662 gen_loader_fixture_fini(&f); 663 } 664 665 static void signature_zero_size(void) 666 { 667 static const __u8 junk[64] = {}; 668 struct gen_loader_fixture f; 669 int fd; 670 671 if (gen_loader_fixture_init(&f) == 0) { 672 /* 673 * A present signature with signature_size == 0 is rejected 674 * up front, before the keyring is resolved or the signature 675 * buffer is read. 676 */ 677 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk, 678 0, KEY_SPEC_SESSION_KEYRING, 0); 679 ASSERT_EQ(fd, -EINVAL, "zero-size signature rejected"); 680 if (fd >= 0) 681 close(fd); 682 } 683 gen_loader_fixture_fini(&f); 684 } 685 686 static void signature_bad_keyring(void) 687 { 688 static const __u8 junk[64] = {}; 689 struct gen_loader_fixture f; 690 int fd; 691 692 if (gen_loader_fixture_init(&f) == 0) { 693 /* 694 * A present signature with a keyring_id that resolves to no key is 695 * rejected up front: bpf_prog_verify_signature() fails the keyring 696 * lookup (-EINVAL) before it ever looks at the signature bytes. A 697 * large positive serial takes the user-keyring path and won't exist. 698 */ 699 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk, 700 sizeof(junk), INT_MAX, 0); 701 ASSERT_EQ(fd, -EINVAL, "signature with bad keyring_id rejected"); 702 if (fd >= 0) 703 close(fd); 704 } 705 gen_loader_fixture_fini(&f); 706 } 707 708 static bool keyring_unsealed_boot(void) 709 { 710 char val = 0; 711 int fd; 712 713 fd = open("/sys/module/bpf/parameters/keyring_unsealed", O_RDONLY); 714 if (fd < 0) 715 return false; 716 if (read(fd, &val, 1) != 1) 717 val = 0; 718 close(fd); 719 return val == 'Y' || val == '1'; 720 } 721 722 static int bpf_keyring_lookup(int *nr_keys) 723 { 724 char line[512], type[32], desc[64]; 725 int serial = -ENOENT; 726 FILE *f; 727 728 f = fopen("/proc/keys", "r"); 729 if (!f) 730 return -errno; 731 732 while (fgets(line, sizeof(line), f)) { 733 unsigned int hex; 734 char *sum; 735 736 if (sscanf(line, "%x %*s %*s %*s %*s %*s %*s %31s %63s", 737 &hex, type, desc) != 3) 738 continue; 739 if (strcmp(type, "keyring") || strcmp(desc, ".bpf:")) 740 continue; 741 742 serial = (int)hex; 743 if (nr_keys) { 744 sum = strstr(line, ".bpf: "); 745 *nr_keys = !sum || !strncmp(sum + 6, "empty", 5) ? 746 0 : atoi(sum + 6); 747 } 748 break; 749 } 750 fclose(f); 751 return serial; 752 } 753 754 static long keyctl_ret(int cmd, unsigned long arg2, unsigned long arg3) 755 { 756 long ret = syscall(__NR_keyctl, cmd, arg2, arg3); 757 758 return ret < 0 ? -errno : ret; 759 } 760 761 /* 762 * What the bpf keyring still needs once it got provisioned: KEY_POS_SEARCH 763 * for the in-kernel search during verification, and the user view/read bits 764 * so it stays visible in /proc/keys, rest is dropped so the enrolled is 765 * therefore final. 766 */ 767 #define BPF_KEYRING_PERM_LOCKED 0x08030000 768 /* What bpf_keyring_init() grants at boot. */ 769 #define BPF_KEYRING_PERM_INITIAL 0x082f0000 770 771 static void bpf_keyring_sealed(void) 772 { 773 static const __u8 junk[64] = {}; 774 struct gen_loader_fixture f; 775 int serial, key, fd; 776 777 if (keyring_unsealed_boot()) { 778 printf("%s:SKIP:the bpf keyring was unsealed at boot\n", __func__); 779 test__skip(); 780 return; 781 } 782 serial = bpf_keyring_lookup(NULL); 783 if (serial >= 0) { 784 ASSERT_EQ(keyctl_ret(KEYCTL_GET_KEYRING_ID, 785 KEY_SPEC_BPF_KEYRING, 0), serial, 786 "KEY_SPEC_BPF_KEYRING resolves to the bpf keyring"); 787 key = syscall(__NR_add_key, "user", "sealprobe", "x", 1, 788 KEY_SPEC_BPF_KEYRING); 789 if (key >= 0) 790 syscall(__NR_keyctl, KEYCTL_UNLINK, key, 791 KEY_SPEC_BPF_KEYRING); 792 ASSERT_EQ(key < 0 ? -errno : 0, -EPERM, 793 "nothing links into a sealed keyring"); 794 } 795 if (gen_loader_fixture_init(&f) == 0) { 796 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk, 797 sizeof(junk), KEY_SPEC_BPF_KEYRING, 0); 798 ASSERT_EQ(fd, -ENOKEY, "sealed bpf keyring rejected"); 799 if (fd >= 0) 800 close(fd); 801 } 802 gen_loader_fixture_fini(&f); 803 } 804 805 static int try_load(const struct gen_loader_fixture *f, const void *sig, 806 __u32 sig_sz, __s32 keyring_id, char *log_buf, __u32 log_sz) 807 { 808 int map_fd, prog_fd; 809 810 map_fd = setup_meta_map(f); 811 if (!ASSERT_OK_FD(map_fd, "meta_map")) 812 return map_fd; 813 prog_fd = load_loader_log(f->gopts.insns, f->gopts.insns_sz, map_fd, 814 sig, sig_sz, keyring_id, 1, log_buf, log_sz); 815 close(map_fd); 816 if (prog_fd >= 0) 817 close(prog_fd); 818 return prog_fd; 819 } 820 821 /* 822 * This needs bpf.keyring_unsealed=1 on the guest kernel command line, which 823 * vmtest.sh can pass via KERNEL_CMDLINE_EXTRA. There is no way to unseal the 824 * keyring from here, so without it the test skips. It also only works once 825 * per boot, as restricting a keyring cannot be undone. 826 */ 827 static void bpf_keyring_provisioned(void) 828 { 829 char dir_tmpl[] = "/tmp/bpfkeyringXXXXXX"; 830 char bad_tmpl[] = "/tmp/bpfkeyringbadXXXXXX"; 831 __u8 *sig = NULL, *bad = NULL, *buf = NULL; 832 int serial, err; 833 int nr_keys = 0, der_fd = -1; 834 struct gen_loader_fixture f; 835 __u32 sig_sz = 8192, bad_sz; 836 bool have_fixture = false; 837 char *dir, *bad_dir = NULL; 838 char log_buf[1024] = {}; 839 char path[PATH_MAX]; 840 __u8 der[4096]; 841 ssize_t der_sz; 842 843 serial = bpf_keyring_lookup(&nr_keys); 844 if (serial < 0) { 845 printf("%s:SKIP:no bpf keyring (needs CONFIG_KEYS)\n", __func__); 846 test__skip(); 847 return; 848 } 849 if (nr_keys != 0) { 850 printf("%s:SKIP:the bpf keyring has already been provisioned\n", 851 __func__); 852 test__skip(); 853 return; 854 } 855 856 dir = mkdtemp(dir_tmpl); 857 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 858 return; 859 if (!ASSERT_OK(run_setup("genkey", dir), "verify_sig_setup genkey")) 860 goto rmdir; 861 862 snprintf(path, sizeof(path), "%s/signing_key.der", dir); 863 der_fd = open(path, O_RDONLY); 864 if (!ASSERT_OK_FD(der_fd, "open signing_key.der")) 865 goto rmdir; 866 der_sz = read(der_fd, der, sizeof(der)); 867 close(der_fd); 868 if (!ASSERT_GT(der_sz, 0, "read signing_key.der")) 869 goto rmdir; 870 871 ASSERT_EQ(keyctl_ret(KEYCTL_GET_KEYRING_ID, KEY_SPEC_BPF_KEYRING, 0), 872 serial, "KEY_SPEC_BPF_KEYRING resolves to the bpf keyring"); 873 874 err = syscall(__NR_add_key, "asymmetric", "", der, (size_t)der_sz, 875 KEY_SPEC_BPF_KEYRING); 876 if (err < 0 && errno == EPERM) { 877 printf("%s:SKIP:the bpf keyring is sealed, need bpf.keyring_unsealed=1\n", 878 __func__); 879 test__skip(); 880 goto rmdir; 881 } 882 if (!ASSERT_GE(err, 0, "add the signing key to the bpf keyring")) 883 goto rmdir; 884 885 sig = malloc(sig_sz); 886 if (!ASSERT_OK_PTR(sig, "sig buf")) 887 goto out; 888 have_fixture = true; 889 if (gen_loader_fixture_init(&f) != 0) 890 goto out; 891 892 buf = malloc((size_t)f.gopts.insns_sz + f.data_sz); 893 if (!ASSERT_OK_PTR(buf, "signbuf")) 894 goto out; 895 memcpy(buf, f.gopts.insns, f.gopts.insns_sz); 896 memcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz); 897 if (!ASSERT_OK(sign_buf(dir, buf, f.gopts.insns_sz + f.data_sz, sig, 898 &sig_sz), "sign insns||metadata")) 899 goto out; 900 901 ASSERT_EQ(try_load(&f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, NULL, 0), 902 -ENOKEY, "unrestricted keyring still not consulted"); 903 904 ASSERT_EQ(try_load(&f, sig, sig_sz, KEY_SPEC_SESSION_KEYRING, NULL, 0), 905 -EPERM, "caller-supplied keyring refused before provisioning"); 906 907 if (!ASSERT_OK(syscall(__NR_keyctl, KEYCTL_RESTRICT_KEYRING, 908 KEY_SPEC_BPF_KEYRING, NULL, NULL), 909 "restrict bpf keyring")) 910 goto out; 911 912 if (!ASSERT_OK_FD(try_load(&f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, 913 NULL, 0), 914 "load signed by a key in the .bpf keyring")) 915 goto out; 916 917 bad_dir = mkdtemp(bad_tmpl); 918 if (!ASSERT_OK_PTR(bad_dir, "mkdtemp unenrolled")) 919 goto out; 920 if (!ASSERT_OK(run_setup("genkey", bad_dir), "verify_sig_setup genkey unenrolled")) 921 goto out; 922 bad_sz = 8192; 923 bad = malloc(bad_sz); 924 if (!ASSERT_OK_PTR(bad, "bad sig buf")) 925 goto out; 926 if (!ASSERT_OK(sign_buf(bad_dir, buf, f.gopts.insns_sz + f.data_sz, bad, 927 &bad_sz), "sign with an unenrolled key")) 928 goto out; 929 930 ASSERT_EQ(try_load(&f, bad, bad_sz, KEY_SPEC_BPF_KEYRING, log_buf, 931 sizeof(log_buf)), -ENOKEY, 932 "key outside the bpf keyring refused"); 933 ASSERT_HAS_SUBSTR(log_buf, "signature verification failed", 934 "the bpf keyring was consulted"); 935 936 f.blob[0] ^= 0xff; 937 err = try_load(&f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, NULL, 0); 938 f.blob[0] ^= 0xff; 939 ASSERT_EQ(err, -EKEYREJECTED, "tampered metadata refused"); 940 941 ASSERT_EQ(try_load(&f, sig, sig_sz, KEY_SPEC_SESSION_KEYRING, NULL, 0), 942 -EPERM, "caller-supplied keyring refused once .bpf is in use"); 943 944 err = keyctl_ret(KEYCTL_UNLINK, KEY_SPEC_SESSION_KEYRING, serial); 945 ASSERT_EQ(err, -ENOENT, "keyring writable while the user bits are there"); 946 947 err = keyctl_ret(KEYCTL_SETPERM, serial, BPF_KEYRING_PERM_LOCKED); 948 if (!ASSERT_OK(err, "drop the user bits on the bpf keyring")) 949 goto out; 950 951 ASSERT_OK_FD(try_load(&f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, NULL, 0), 952 "load still verified against the locked keyring"); 953 ASSERT_EQ(keyctl_ret(KEYCTL_GET_KEYRING_ID, KEY_SPEC_BPF_KEYRING, 0), 954 -EACCES, "the special id grants no rights of its own"); 955 956 err = keyctl_ret(KEYCTL_UNLINK, KEY_SPEC_SESSION_KEYRING, serial); 957 ASSERT_EQ(err, -EACCES, "unlink refused"); 958 err = keyctl_ret(KEYCTL_CLEAR, serial, 0); 959 ASSERT_EQ(err, -EACCES, "clear refused"); 960 err = keyctl_ret(KEYCTL_REVOKE, serial, 0); 961 ASSERT_EQ(err, -EACCES, "revoke refused"); 962 err = keyctl_ret(KEYCTL_INVALIDATE, serial, 0); 963 ASSERT_EQ(err, -EACCES, "invalidate refused"); 964 err = keyctl_ret(KEYCTL_SET_TIMEOUT, serial, 1); 965 ASSERT_EQ(err, -EACCES, "timeout refused"); 966 err = keyctl_ret(KEYCTL_SETPERM, serial, BPF_KEYRING_PERM_INITIAL); 967 ASSERT_EQ(err, -EACCES, "the bits cannot be granted back"); 968 969 ASSERT_EQ(bpf_keyring_lookup(&nr_keys), serial, "keyring still there"); 970 ASSERT_EQ(nr_keys, 1, "the enrolled key survived"); 971 out: 972 if (have_fixture) 973 gen_loader_fixture_fini(&f); 974 genkey_dir_fini(bad_dir); 975 free(buf); 976 free(bad); 977 free(sig); 978 rmdir: 979 genkey_dir_fini(dir); 980 } 981 982 /* 983 * A signed loader must ignore ctx-supplied map dimensions: the host cannot 984 * resize a signed program's maps via the loader ctx. Drive a one-map program 985 * through gen_loader, ask (via ctx) for every map to be resized to a bogus 986 * value, and confirm the created maps keep their attested size. 987 */ 988 #define GATING_BOGUS_MAX 0x4000 989 990 static void metadata_ctx_max_entries_ignored(void) 991 { 992 LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true); 993 struct test_signed_loader_map *skel; 994 __u8 excl[SHA256_DIGEST_LENGTH]; 995 int nr_maps = 0, nr_progs = 0, i, checked = 0, r; 996 struct bpf_program *p; 997 struct bpf_map *m; 998 struct bpf_map_desc *md; 999 unsigned char *blob; 1000 __u32 ctx_sz, data_sz; 1001 void *ctx; 1002 bool ran; 1003 1004 skel = test_signed_loader_map__open(); 1005 if (!ASSERT_OK_PTR(skel, "skel_open")) 1006 return; 1007 if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader")) 1008 goto destroy; 1009 if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load")) 1010 goto destroy; 1011 1012 bpf_object__for_each_program(p, skel->obj) 1013 nr_progs++; 1014 bpf_object__for_each_map(m, skel->obj) 1015 nr_maps++; 1016 ctx_sz = sizeof(struct bpf_loader_ctx) + 1017 nr_maps * sizeof(struct bpf_map_desc) + 1018 nr_progs * sizeof(struct bpf_prog_desc); 1019 ctx = calloc(1, ctx_sz); 1020 if (!ASSERT_OK_PTR(ctx, "ctx_alloc")) 1021 goto destroy; 1022 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 1023 1024 md = (struct bpf_map_desc *)((char *)ctx + sizeof(struct bpf_loader_ctx)); 1025 for (i = 0; i < nr_maps; i++) 1026 md[i].max_entries = GATING_BOGUS_MAX; 1027 1028 libbpf_sha256(gopts.insns, gopts.insns_sz, excl); 1029 data_sz = gopts.data_sz; 1030 blob = malloc(data_sz); 1031 if (!ASSERT_OK_PTR(blob, "blob_alloc")) 1032 goto free_ctx; 1033 memcpy(blob, gopts.data, data_sz); 1034 1035 r = run_gen_loader(gopts.insns, gopts.insns_sz, blob, data_sz, 1036 excl, sizeof(excl), NULL, 0, ctx, ctx_sz, &ran); 1037 if (!ASSERT_TRUE(ran, "loader ran") || 1038 !ASSERT_EQ(r, 0, "loader retval")) 1039 goto free_blob; 1040 1041 for (i = 0; i < nr_maps; i++) { 1042 struct bpf_map_info info; 1043 __u32 ilen = sizeof(info); 1044 int fd = md[i].map_fd; 1045 1046 if (fd <= 0) 1047 continue; 1048 memset(&info, 0, sizeof(info)); 1049 if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "map_info")) { 1050 ASSERT_NEQ(info.max_entries, GATING_BOGUS_MAX, 1051 "ctx max_entries ignored for signed loader"); 1052 checked++; 1053 } 1054 } 1055 ASSERT_GT(checked, 0, "inspected a created map"); 1056 1057 free_blob: 1058 free(blob); 1059 free_ctx: 1060 close_loader_ctx_fds(ctx, nr_maps, nr_progs); 1061 free(ctx); 1062 destroy: 1063 test_signed_loader_map__destroy(skel); 1064 } 1065 1066 /* 1067 * A signed loader must also ignore ctx-supplied initial_value: the host cannot 1068 * re-seed a signed program's map contents through the loader ctx. Drive a 1069 * program with one initialized global (a .data map) through gen_loader, point 1070 * every map's ctx initial_value at an adversarial buffer, and confirm the 1071 * created map still holds the attested value, never the ctx bytes. 1072 */ 1073 #define DATA_MAGIC 0x5eed1234abad1deaULL 1074 1075 static void metadata_ctx_initial_value_ignored(void) 1076 { 1077 LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true); 1078 struct test_signed_loader_data *skel; 1079 __u8 excl[SHA256_DIGEST_LENGTH], evil[64]; 1080 int nr_maps = 0, nr_progs = 0, i, found = 0, r; 1081 struct bpf_program *p; 1082 struct bpf_map *m; 1083 struct bpf_map_desc *md; 1084 unsigned char *blob; 1085 __u32 ctx_sz, data_sz; 1086 void *ctx; 1087 bool ran; 1088 1089 skel = test_signed_loader_data__open(); 1090 if (!ASSERT_OK_PTR(skel, "skel_open")) 1091 return; 1092 if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader")) 1093 goto destroy; 1094 if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load")) 1095 goto destroy; 1096 1097 bpf_object__for_each_program(p, skel->obj) 1098 nr_progs++; 1099 bpf_object__for_each_map(m, skel->obj) 1100 nr_maps++; 1101 ctx_sz = sizeof(struct bpf_loader_ctx) + 1102 nr_maps * sizeof(struct bpf_map_desc) + 1103 nr_progs * sizeof(struct bpf_prog_desc); 1104 ctx = calloc(1, ctx_sz); 1105 if (!ASSERT_OK_PTR(ctx, "ctx_alloc")) 1106 goto destroy; 1107 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 1108 1109 memset(evil, 0xAA, sizeof(evil)); 1110 md = (struct bpf_map_desc *)((char *)ctx + sizeof(struct bpf_loader_ctx)); 1111 for (i = 0; i < nr_maps; i++) 1112 md[i].initial_value = ptr_to_u64(evil); 1113 1114 libbpf_sha256(gopts.insns, gopts.insns_sz, excl); 1115 data_sz = gopts.data_sz; 1116 blob = malloc(data_sz); 1117 if (!ASSERT_OK_PTR(blob, "blob_alloc")) 1118 goto free_ctx; 1119 memcpy(blob, gopts.data, data_sz); 1120 1121 r = run_gen_loader(gopts.insns, gopts.insns_sz, blob, data_sz, 1122 excl, sizeof(excl), NULL, 0, ctx, ctx_sz, &ran); 1123 if (!ASSERT_TRUE(ran, "loader ran") || 1124 !ASSERT_EQ(r, 0, "loader retval")) 1125 goto free_blob; 1126 1127 for (i = 0; i < nr_maps; i++) { 1128 struct bpf_map_info info; 1129 __u32 ilen = sizeof(info), key = 0; 1130 __u8 value[64] = {}; 1131 __u64 got; 1132 int fd = md[i].map_fd; 1133 1134 if (fd <= 0) 1135 continue; 1136 memset(&info, 0, sizeof(info)); 1137 if (!ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "map_info")) 1138 continue; 1139 if (info.value_size <= sizeof(value) && 1140 bpf_map_lookup_elem(fd, &key, value) == 0) { 1141 memcpy(&got, value, sizeof(got)); 1142 /* attested .data survives; ctx bytes (0xAA..) ignored */ 1143 if (got == DATA_MAGIC) 1144 found = 1; 1145 ASSERT_NEQ(got, 0xAAAAAAAAAAAAAAAAULL, 1146 "ctx initial_value ignored for signed loader"); 1147 } 1148 } 1149 ASSERT_EQ(found, 1, "attested .data value preserved"); 1150 1151 free_blob: 1152 free(blob); 1153 free_ctx: 1154 close_loader_ctx_fds(ctx, nr_maps, nr_progs); 1155 free(ctx); 1156 destroy: 1157 test_signed_loader_data__destroy(skel); 1158 } 1159 1160 /* 1161 * The load-time signature must authenticate the loader instructions: a valid 1162 * signature loads, and the very same signature over one-byte-tampered insns is 1163 * rejected. Uses ./verify_sig_setup.sh + ./sign-file at runtime, like 1164 * verify_pkcs7_sig, and verifies against the session keyring the key was added 1165 * to. (signature_enforced/_too_large only cover a malformed signature.) 1166 */ 1167 static void signature_authenticates_insns(void) 1168 { 1169 LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true); 1170 char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir; 1171 struct test_signed_loader *skel = NULL; 1172 __u8 excl[SHA256_DIGEST_LENGTH], sig[8192]; 1173 __u32 sig_sz = sizeof(sig), insns_sz, data_sz, ctx_sz; 1174 unsigned char *insns = NULL, *tampered = NULL, *blob = NULL; 1175 unsigned char *signbuf = NULL; 1176 int nr_maps = 0, nr_progs = 0, r; 1177 struct bpf_program *p; 1178 struct bpf_map *m; 1179 void *ctx = NULL; 1180 bool ran; 1181 1182 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 1183 KEY_SPEC_SESSION_KEYRING); 1184 dir = mkdtemp(dir_tmpl); 1185 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 1186 return; 1187 if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) { 1188 rmdir(dir); 1189 return; 1190 } 1191 1192 skel = test_signed_loader__open(); 1193 if (!ASSERT_OK_PTR(skel, "skel_open")) 1194 goto cleanup; 1195 if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader")) 1196 goto cleanup; 1197 if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load")) 1198 goto cleanup; 1199 1200 bpf_object__for_each_program(p, skel->obj) 1201 nr_progs++; 1202 bpf_object__for_each_map(m, skel->obj) 1203 nr_maps++; 1204 ctx_sz = sizeof(struct bpf_loader_ctx) + 1205 nr_maps * sizeof(struct bpf_map_desc) + 1206 nr_progs * sizeof(struct bpf_prog_desc); 1207 insns_sz = gopts.insns_sz; 1208 data_sz = gopts.data_sz; 1209 ctx = calloc(1, ctx_sz); 1210 insns = malloc(insns_sz); 1211 tampered = malloc(insns_sz); 1212 blob = malloc(data_sz); 1213 if (!ASSERT_OK_PTR(ctx, "ctx") || 1214 !ASSERT_OK_PTR(insns, "insns") || 1215 !ASSERT_OK_PTR(tampered, "tampered") || 1216 !ASSERT_OK_PTR(blob, "blob")) 1217 goto cleanup; 1218 memcpy(insns, gopts.insns, insns_sz); 1219 memcpy(blob, gopts.data, data_sz); 1220 libbpf_sha256(insns, insns_sz, excl); 1221 1222 signbuf = malloc((size_t)insns_sz + data_sz); 1223 if (!ASSERT_OK_PTR(signbuf, "signbuf")) 1224 goto cleanup; 1225 memcpy(signbuf, insns, insns_sz); 1226 memcpy(signbuf + insns_sz, blob, data_sz); 1227 if (!ASSERT_OK(sign_buf(dir, signbuf, insns_sz + data_sz, sig, &sig_sz), 1228 "sign-file")) 1229 goto cleanup; 1230 1231 memset(ctx, 0, ctx_sz); 1232 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 1233 r = run_gen_loader(insns, insns_sz, blob, data_sz, excl, sizeof(excl), 1234 sig, sig_sz, ctx, ctx_sz, &ran); 1235 ASSERT_TRUE(ran, "valid signature: loader loaded and ran"); 1236 ASSERT_EQ(r, 0, "valid signature accepted"); 1237 close_loader_ctx_fds(ctx, nr_maps, nr_progs); 1238 1239 memcpy(tampered, insns, insns_sz); 1240 tampered[insns_sz / 2] ^= 0xff; 1241 /* 1242 * Bind the metadata map to the tampered loader's own digest, so the 1243 * verifier's exclusive-map check (excl_prog_sha == prog->digest) passes 1244 * and the signature - verified after the maps are resolved - is what 1245 * rejects the load. This is the attacker's best case: even after 1246 * re-binding the exclusive map to their tampered loader, the signature 1247 * over the original insns || metadata still fails. (Leaving the map 1248 * bound to the original digest would instead trip the excl check first.) 1249 */ 1250 libbpf_sha256(tampered, insns_sz, excl); 1251 memset(ctx, 0, ctx_sz); 1252 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 1253 r = run_gen_loader(tampered, insns_sz, blob, data_sz, excl, sizeof(excl), 1254 sig, sig_sz, ctx, ctx_sz, &ran); 1255 ASSERT_FALSE(ran, "tampered loader rejected before run"); 1256 ASSERT_EQ(r, -EKEYREJECTED, "signature is bound to the instructions"); 1257 cleanup: 1258 free(insns); 1259 free(tampered); 1260 free(blob); 1261 free(signbuf); 1262 free(ctx); 1263 test_signed_loader__destroy(skel); 1264 run_setup("cleanup", dir); 1265 } 1266 1267 static void signature_authenticates_metadata(void) 1268 { 1269 LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true); 1270 char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir; 1271 struct test_signed_loader *skel = NULL; 1272 __u8 excl[SHA256_DIGEST_LENGTH], sig[8192]; 1273 __u32 sig_sz = sizeof(sig), insns_sz, data_sz, ctx_sz; 1274 unsigned char *insns = NULL, *blob = NULL; 1275 unsigned char *signbuf = NULL; 1276 int nr_maps = 0, nr_progs = 0, r; 1277 struct bpf_program *p; 1278 struct bpf_map *m; 1279 void *ctx = NULL; 1280 bool ran; 1281 1282 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 1283 KEY_SPEC_SESSION_KEYRING); 1284 dir = mkdtemp(dir_tmpl); 1285 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 1286 return; 1287 if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) { 1288 rmdir(dir); 1289 return; 1290 } 1291 1292 skel = test_signed_loader__open(); 1293 if (!ASSERT_OK_PTR(skel, "skel_open")) 1294 goto cleanup; 1295 if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader")) 1296 goto cleanup; 1297 if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load")) 1298 goto cleanup; 1299 1300 bpf_object__for_each_program(p, skel->obj) 1301 nr_progs++; 1302 bpf_object__for_each_map(m, skel->obj) 1303 nr_maps++; 1304 ctx_sz = sizeof(struct bpf_loader_ctx) + 1305 nr_maps * sizeof(struct bpf_map_desc) + 1306 nr_progs * sizeof(struct bpf_prog_desc); 1307 insns_sz = gopts.insns_sz; 1308 data_sz = gopts.data_sz; 1309 ctx = calloc(1, ctx_sz); 1310 insns = malloc(insns_sz); 1311 blob = malloc(data_sz); 1312 if (!ASSERT_OK_PTR(ctx, "ctx") || 1313 !ASSERT_OK_PTR(insns, "insns") || 1314 !ASSERT_OK_PTR(blob, "blob")) 1315 goto cleanup; 1316 memcpy(insns, gopts.insns, insns_sz); 1317 memcpy(blob, gopts.data, data_sz); 1318 libbpf_sha256(insns, insns_sz, excl); 1319 1320 signbuf = malloc((size_t)insns_sz + data_sz); 1321 if (!ASSERT_OK_PTR(signbuf, "signbuf")) 1322 goto cleanup; 1323 memcpy(signbuf, insns, insns_sz); 1324 memcpy(signbuf + insns_sz, blob, data_sz); 1325 if (!ASSERT_OK(sign_buf(dir, signbuf, insns_sz + data_sz, sig, &sig_sz), 1326 "sign-file")) 1327 goto cleanup; 1328 1329 memset(ctx, 0, ctx_sz); 1330 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 1331 r = run_gen_loader(insns, insns_sz, blob, data_sz, excl, sizeof(excl), 1332 sig, sig_sz, ctx, ctx_sz, &ran); 1333 ASSERT_TRUE(ran, "valid signature: loader loaded and ran"); 1334 ASSERT_EQ(r, 0, "valid signature accepted"); 1335 close_loader_ctx_fds(ctx, nr_maps, nr_progs); 1336 1337 /* 1338 * Tamper the metadata after signing while leaving the instructions 1339 * and thus the exclusive hash binding untouched: the map freezes 1340 * fine and excl_prog_sha still matches the loader's digest, so the 1341 * load reaches signature verification, which folds the live frozen 1342 * map bytes into the checked payload and must reject the modified 1343 * blob. A kernel folding anything but the map contents themselves 1344 * would wrongly accept this load. 1345 */ 1346 blob[data_sz / 2] ^= 0xff; 1347 memset(ctx, 0, ctx_sz); 1348 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 1349 r = run_gen_loader(insns, insns_sz, blob, data_sz, excl, sizeof(excl), 1350 sig, sig_sz, ctx, ctx_sz, &ran); 1351 ASSERT_FALSE(ran, "tampered metadata rejected before run"); 1352 ASSERT_EQ(r, -EKEYREJECTED, "signature is bound to the metadata"); 1353 cleanup: 1354 free(insns); 1355 free(blob); 1356 free(signbuf); 1357 free(ctx); 1358 test_signed_loader__destroy(skel); 1359 run_setup("cleanup", dir); 1360 } 1361 1362 static int make_excl_map(__u32 flags, __u32 value_size) 1363 { 1364 LIBBPF_OPTS(bpf_map_create_opts, opts); 1365 __u8 hash[SHA256_DIGEST_LENGTH] = { 1 }; /* any 32-byte value */ 1366 1367 opts.excl_prog_hash = hash; 1368 opts.excl_prog_hash_size = sizeof(hash); 1369 opts.map_flags = flags; 1370 return bpf_map_create(BPF_MAP_TYPE_ARRAY, "md", 4, value_size, 1, &opts); 1371 } 1372 1373 static void hash_requires_frozen(void) 1374 { 1375 __u8 hbuf[SHA256_DIGEST_LENGTH], val[64] = {}; 1376 struct bpf_map_info info; 1377 __u32 ilen, key = 0; 1378 int fd; 1379 1380 fd = make_excl_map(0, sizeof(val)); 1381 if (!ASSERT_OK_FD(fd, "excl_map")) 1382 return; 1383 ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update"); 1384 1385 memset(&info, 0, sizeof(info)); 1386 info.hash = ptr_to_u64(hbuf); 1387 info.hash_size = sizeof(hbuf); 1388 ilen = sizeof(info); 1389 ASSERT_EQ(bpf_map_get_info_by_fd(fd, &info, &ilen), -EPERM, 1390 "hash of unfrozen map rejected"); 1391 close(fd); 1392 } 1393 1394 static void no_update_after_freeze(void) 1395 { 1396 __u8 val[64] = {}; 1397 __u32 key = 0; 1398 int fd; 1399 1400 fd = make_excl_map(0, sizeof(val)); 1401 if (!ASSERT_OK_FD(fd, "excl_map")) 1402 return; 1403 ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update"); 1404 ASSERT_OK(bpf_map_freeze(fd), "freeze"); 1405 ASSERT_EQ(bpf_map_update_elem(fd, &key, val, 0), -EPERM, 1406 "update after freeze rejected"); 1407 close(fd); 1408 } 1409 1410 static void freeze_writable_mmap(void) 1411 { 1412 void *w; 1413 int fd; 1414 1415 fd = make_excl_map(BPF_F_MMAPABLE, 4096); 1416 if (!ASSERT_OK_FD(fd, "excl_mmapable_map")) 1417 return; 1418 w = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 1419 if (ASSERT_OK_PTR(w, "writable_mmap")) { 1420 ASSERT_EQ(bpf_map_freeze(fd), -EBUSY, 1421 "freeze rejected while writable mmap held"); 1422 munmap(w, 4096); 1423 } 1424 close(fd); 1425 } 1426 1427 static void no_writable_mmap_frozen(void) 1428 { 1429 void *w; 1430 int fd; 1431 1432 fd = make_excl_map(BPF_F_MMAPABLE, 4096); 1433 if (!ASSERT_OK_FD(fd, "excl_mmapable_map")) 1434 return; 1435 ASSERT_OK(bpf_map_freeze(fd), "freeze"); 1436 w = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 1437 ASSERT_EQ(w, MAP_FAILED, "writable mmap of frozen map rejected"); 1438 if (w != MAP_FAILED) 1439 munmap(w, 4096); 1440 close(fd); 1441 } 1442 1443 static void map_hash_matches_libbpf(void) 1444 { 1445 __u8 kbuf[SHA256_DIGEST_LENGTH], lbuf[SHA256_DIGEST_LENGTH], val[64] = {}; 1446 struct bpf_map_info info; 1447 __u32 ilen, key = 0; 1448 int fd, i; 1449 1450 /* 1451 * The signing scheme assumes the kernel's map hash equals what libbpf 1452 * computes over the same bytes (gen_loader bakes libbpf_sha256(blob); 1453 * the kernel recomputes via array_map_get_hash). Pin that they agree. 1454 */ 1455 for (i = 0; i < (int)sizeof(val); i++) 1456 val[i] = i * 7 + 1; 1457 fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "h", 4, sizeof(val), 1, NULL); 1458 if (!ASSERT_OK_FD(fd, "array_map")) 1459 return; 1460 ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update"); 1461 ASSERT_OK(bpf_map_freeze(fd), "freeze"); 1462 memset(&info, 0, sizeof(info)); 1463 info.hash = ptr_to_u64(kbuf); 1464 info.hash_size = sizeof(kbuf); 1465 ilen = sizeof(info); 1466 if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "get_hash")) { 1467 libbpf_sha256(val, sizeof(val), lbuf); 1468 ASSERT_EQ(memcmp(kbuf, lbuf, sizeof(kbuf)), 0, 1469 "kernel map hash matches libbpf_sha256"); 1470 } 1471 close(fd); 1472 } 1473 1474 static void map_hash_multi_element(void) 1475 { 1476 const __u32 nr = 8, value_size = 64; 1477 __u8 kbuf[SHA256_DIGEST_LENGTH], lbuf[SHA256_DIGEST_LENGTH]; 1478 struct bpf_map_info info; 1479 __u32 ilen, i, j; 1480 __u8 *full; 1481 int fd; 1482 1483 /* 1484 * array_map_get_hash() hashes elem_size * max_entries (the whole value 1485 * area), not just element 0. With an 8-aligned value_size elem_size has 1486 * no padding, so pin that a >1-entry array's kernel hash equals 1487 * libbpf_sha256() over the full, concatenated element contents. 1488 */ 1489 fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "h", 4, value_size, nr, NULL); 1490 if (!ASSERT_OK_FD(fd, "array_map")) 1491 return; 1492 full = calloc(nr, value_size); 1493 if (!ASSERT_OK_PTR(full, "buf")) 1494 goto close_fd; 1495 for (i = 0; i < nr; i++) { 1496 __u8 *v = full + i * value_size; 1497 1498 for (j = 0; j < value_size; j++) 1499 v[j] = i * 31 + j * 7 + 1; 1500 ASSERT_OK(bpf_map_update_elem(fd, &i, v, 0), "update"); 1501 } 1502 ASSERT_OK(bpf_map_freeze(fd), "freeze"); 1503 memset(&info, 0, sizeof(info)); 1504 info.hash = ptr_to_u64(kbuf); 1505 info.hash_size = sizeof(kbuf); 1506 ilen = sizeof(info); 1507 if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "get_hash")) { 1508 libbpf_sha256(full, (size_t)nr * value_size, lbuf); 1509 ASSERT_EQ(memcmp(kbuf, lbuf, sizeof(kbuf)), 0, 1510 "kernel hash covers full multi-element value area"); 1511 } 1512 free(full); 1513 close_fd: 1514 close(fd); 1515 } 1516 1517 static void map_hash_bad_size(void) 1518 { 1519 __u8 kbuf[SHA256_DIGEST_LENGTH], val[64] = {}; 1520 struct bpf_map_info info; 1521 __u32 ilen, key = 0; 1522 int fd; 1523 1524 fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "h", 4, sizeof(val), 1, NULL); 1525 if (!ASSERT_OK_FD(fd, "array_map")) 1526 return; 1527 ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update"); 1528 ASSERT_OK(bpf_map_freeze(fd), "freeze"); 1529 memset(&info, 0, sizeof(info)); 1530 info.hash = ptr_to_u64(kbuf); 1531 info.hash_size = sizeof(kbuf) / 2; 1532 ilen = sizeof(info); 1533 ASSERT_EQ(bpf_map_get_info_by_fd(fd, &info, &ilen), -EINVAL, 1534 "wrong hash_size rejected"); 1535 close(fd); 1536 } 1537 1538 static void map_hash_unsupported_type(void) 1539 { 1540 __u8 kbuf[SHA256_DIGEST_LENGTH]; 1541 struct bpf_map_info info; 1542 __u32 ilen; 1543 int fd; 1544 1545 /* Only arrays implement map_get_hash; a hash map must be refused. */ 1546 fd = bpf_map_create(BPF_MAP_TYPE_HASH, "h", 4, 8, 4, NULL); 1547 if (!ASSERT_OK_FD(fd, "hash_map")) 1548 return; 1549 memset(&info, 0, sizeof(info)); 1550 info.hash = ptr_to_u64(kbuf); 1551 info.hash_size = sizeof(kbuf); 1552 ilen = sizeof(info); 1553 ASSERT_EQ(bpf_map_get_info_by_fd(fd, &info, &ilen), -EINVAL, 1554 "hash unsupported for non-array map"); 1555 close(fd); 1556 } 1557 1558 static int setup_meta_map(const struct gen_loader_fixture *f) 1559 { 1560 LIBBPF_OPTS(bpf_map_create_opts, mopts, 1561 .excl_prog_hash = f->excl, 1562 .excl_prog_hash_size = sizeof(f->excl)); 1563 __u32 key = 0; 1564 int fd; 1565 1566 fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4, 1567 f->data_sz, 1, &mopts); 1568 if (fd < 0) 1569 return -errno; 1570 if (bpf_map_update_elem(fd, &key, f->blob, 0) || bpf_map_freeze(fd)) { 1571 close(fd); 1572 return -errno; 1573 } 1574 return fd; 1575 } 1576 1577 static void lsm_signature_verdict(void) 1578 { 1579 char dir_tmpl[] = "/tmp/signed_loader_lsmXXXXXX", *dir = NULL; 1580 struct test_signed_loader_lsm *lsm = NULL; 1581 __u32 sig_sz = 8192, msig_sz = 8192; 1582 int map_fd = -1, prog_fd = -1; 1583 bool have_fixture = false; 1584 struct gen_loader_fixture f; 1585 unsigned char *buf; 1586 __s32 ses_serial; 1587 __u8 sig[8192]; 1588 1589 lsm = test_signed_loader_lsm__open_and_load(); 1590 if (!ASSERT_OK_PTR(lsm, "lsm_skel_load")) 1591 return; 1592 lsm->bss->monitored_tid = sys_gettid(); 1593 if (!ASSERT_OK(test_signed_loader_lsm__attach(lsm), "lsm_attach")) 1594 goto out; 1595 1596 have_fixture = true; 1597 if (gen_loader_fixture_init(&f) != 0) 1598 goto out; 1599 1600 map_fd = setup_meta_map(&f); 1601 if (!ASSERT_OK_FD(map_fd, "meta_map_unsigned")) 1602 goto out; 1603 lsm->bss->seen = 0; 1604 prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, NULL, 0, 0, 0); 1605 close(map_fd); 1606 map_fd = -1; 1607 if (!ASSERT_OK_FD(prog_fd, "unsigned loader load")) 1608 goto out; 1609 close(prog_fd); 1610 prog_fd = -1; 1611 if (!ASSERT_NEQ(lsm->bss->seen, 0, "bpf LSM in the active LSM set")) 1612 goto out; 1613 ASSERT_EQ(lsm->bss->seen, 1, "unsigned: one observed load"); 1614 ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_UNSIGNED, "unsigned verdict"); 1615 ASSERT_EQ(lsm->bss->sig_keyring_type, BPF_SIG_KEYRING_NONE, "unsigned keyring type"); 1616 ASSERT_EQ(lsm->bss->sig_keyring_serial, 0, "unsigned: no keyring serial"); 1617 1618 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 1619 KEY_SPEC_SESSION_KEYRING); 1620 dir = mkdtemp(dir_tmpl); 1621 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 1622 goto out; 1623 if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) { 1624 rmdir(dir); 1625 dir = NULL; 1626 goto out; 1627 } 1628 if (!ASSERT_OK(sign_buf(dir, f.gopts.insns, f.gopts.insns_sz, sig, 1629 &sig_sz), "sign-file")) 1630 goto out; 1631 1632 map_fd = setup_meta_map(&f); 1633 if (!ASSERT_OK_FD(map_fd, "meta_map_signed")) 1634 goto out; 1635 lsm->bss->seen = 0; 1636 prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig, 1637 sig_sz, KEY_SPEC_SESSION_KEYRING, 0); 1638 close(map_fd); 1639 map_fd = -1; 1640 ASSERT_EQ(prog_fd, -EACCES, "unfolded metadata rejected"); 1641 if (prog_fd >= 0) 1642 close(prog_fd); 1643 prog_fd = -1; 1644 1645 ses_serial = syscall(__NR_keyctl, KEYCTL_GET_KEYRING_ID, 1646 KEY_SPEC_SESSION_KEYRING, 0); 1647 ASSERT_EQ(lsm->bss->seen, 1, "signed: one observed load"); 1648 ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_VERIFIED, 1649 "admission saw a valid signature"); 1650 ASSERT_EQ(lsm->bss->sig_keyring_type, BPF_SIG_KEYRING_USER, "signed keyring type"); 1651 ASSERT_GT(ses_serial, 0, "session keyring serial resolved"); 1652 ASSERT_EQ(lsm->bss->sig_keyring_serial, ses_serial, 1653 "signed: validated against session keyring"); 1654 1655 buf = malloc((size_t)f.gopts.insns_sz + f.data_sz); 1656 if (!ASSERT_OK_PTR(buf, "meta_signbuf")) 1657 goto out; 1658 memcpy(buf, f.gopts.insns, f.gopts.insns_sz); 1659 memcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz); 1660 if (!ASSERT_OK(sign_buf(dir, buf, f.gopts.insns_sz + f.data_sz, 1661 sig, &msig_sz), "sign insns||metadata")) { 1662 free(buf); 1663 goto out; 1664 } 1665 free(buf); 1666 1667 map_fd = setup_meta_map(&f); 1668 if (!ASSERT_OK_FD(map_fd, "meta_map_bound")) 1669 goto out; 1670 lsm->bss->seen = 0; 1671 prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig, 1672 msig_sz, KEY_SPEC_SESSION_KEYRING, 1); 1673 close(map_fd); 1674 map_fd = -1; 1675 if (!ASSERT_OK_FD(prog_fd, "metadata-bound loader load")) 1676 goto out; 1677 close(prog_fd); 1678 prog_fd = -1; 1679 ASSERT_EQ(lsm->bss->seen, 1, "metadata: one observed load"); 1680 ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_VERIFIED, 1681 "metadata-bound verdict"); 1682 out: 1683 if (map_fd >= 0) 1684 close(map_fd); 1685 if (prog_fd >= 0) 1686 close(prog_fd); 1687 if (have_fixture) 1688 gen_loader_fixture_fini(&f); 1689 if (dir) 1690 run_setup("cleanup", dir); 1691 test_signed_loader_lsm__destroy(lsm); 1692 } 1693 1694 /* 1695 * Load-time metadata verification: the kernel folds the frozen metadata map 1696 * into the signature (insns || metadata) and checks it at BPF_PROG_LOAD via 1697 * fd_array_cnt, rather than the loader checking from within BPF. Sign that 1698 * concatenation, hand the kernel the map, and confirm the signed loader loads, 1699 * runs, and installs its target. 1700 */ 1701 static int loadtime_drive(const char *dir, const void *insns, __u32 insns_sz, 1702 const void *data, __u32 data_sz, const __u8 *excl, 1703 void *ctx, __u32 ctx_sz, int *load_ret, bool *ran) 1704 { 1705 LIBBPF_OPTS(bpf_map_create_opts, mopts, 1706 .excl_prog_hash = excl, 1707 .excl_prog_hash_size = SHA256_DIGEST_LENGTH); 1708 __u32 sig_sz = 8192, key = 0; 1709 unsigned char *buf = NULL; 1710 int map_fd, prog_fd, ret = 0; 1711 union bpf_attr attr; 1712 __u8 sig[8192]; 1713 1714 *ran = false; 1715 *load_ret = 0; 1716 1717 /* 1718 * Metadata map, bound to the loader digest and frozen, exactly as 1719 * skel_internal.h's bpf_load_and_run() sets it up. 1720 */ 1721 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4, 1722 data_sz, 1, &mopts); 1723 if (map_fd < 0) { 1724 ret = -errno; 1725 goto out_load; 1726 } 1727 if (bpf_map_update_elem(map_fd, &key, data, 0) || bpf_map_freeze(map_fd)) { 1728 ret = -errno; 1729 goto out_load; 1730 } 1731 1732 /* Sign insns || metadata, the same bytes the kernel reconstructs. */ 1733 buf = malloc((size_t)insns_sz + data_sz); 1734 if (!buf) { 1735 ret = -ENOMEM; 1736 goto out_load; 1737 } 1738 memcpy(buf, insns, insns_sz); 1739 memcpy(buf + insns_sz, data, data_sz); 1740 ret = sign_buf(dir, buf, insns_sz + data_sz, sig, &sig_sz); 1741 if (ret) 1742 goto out_load; 1743 1744 memset(&attr, 0, sizeof(attr)); 1745 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 1746 attr.insns = ptr_to_u64(insns); 1747 attr.insn_cnt = insns_sz / sizeof(struct bpf_insn); 1748 attr.license = ptr_to_u64("Dual BSD/GPL"); 1749 attr.prog_flags = BPF_F_SLEEPABLE; 1750 attr.fd_array = ptr_to_u64(&map_fd); 1751 attr.signature = ptr_to_u64(sig); 1752 attr.signature_size = sig_sz; 1753 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 1754 attr.fd_array_cnt = 1; 1755 memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog")); 1756 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 1757 offsetofend(union bpf_attr, keyring_id)); 1758 if (prog_fd < 0) { 1759 ret = -errno; 1760 goto out_load; 1761 } 1762 1763 memset(&attr, 0, sizeof(attr)); 1764 attr.test.prog_fd = prog_fd; 1765 attr.test.ctx_in = ptr_to_u64(ctx); 1766 attr.test.ctx_size_in = ctx_sz; 1767 if (syscall(__NR_bpf, BPF_PROG_RUN, &attr, 1768 offsetofend(union bpf_attr, test)) < 0) { 1769 ret = -errno; 1770 goto out_prog; 1771 } 1772 *ran = true; 1773 ret = (int)attr.test.retval; 1774 out_prog: 1775 close(prog_fd); 1776 goto out_map; 1777 out_load: 1778 *load_ret = ret; 1779 out_map: 1780 free(buf); 1781 if (map_fd >= 0) 1782 close(map_fd); 1783 return ret; 1784 } 1785 1786 static void loadtime_verify(struct bpf_object *obj, int expect_maps) 1787 { 1788 LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true); 1789 char dir_tmpl[] = "/tmp/signed_loader_ltXXXXXX", *dir = NULL; 1790 int nr_maps = 0, nr_progs = 0, load_ret = 0, r; 1791 __u8 excl[SHA256_DIGEST_LENGTH]; 1792 struct bpf_prog_desc *pd; 1793 struct bpf_map_desc *md; 1794 unsigned char *blob = NULL; 1795 struct bpf_program *p; 1796 struct bpf_map *m; 1797 __u32 ctx_sz, data_sz; 1798 void *ctx = NULL; 1799 bool ran = false; 1800 1801 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 1802 KEY_SPEC_SESSION_KEYRING); 1803 dir = mkdtemp(dir_tmpl); 1804 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 1805 return; 1806 if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) { 1807 rmdir(dir); 1808 return; 1809 } 1810 1811 if (!ASSERT_OK(bpf_object__gen_loader(obj, &gopts), "gen_loader")) 1812 goto out; 1813 if (!ASSERT_OK(bpf_object__load(obj), "gen_load")) 1814 goto out; 1815 1816 bpf_object__for_each_program(p, obj) 1817 nr_progs++; 1818 bpf_object__for_each_map(m, obj) 1819 nr_maps++; 1820 if (!ASSERT_EQ(nr_maps, expect_maps, "fixture map count")) 1821 goto out; 1822 1823 ctx_sz = sizeof(struct bpf_loader_ctx) + 1824 nr_maps * sizeof(struct bpf_map_desc) + 1825 nr_progs * sizeof(struct bpf_prog_desc); 1826 ctx = calloc(1, ctx_sz); 1827 if (!ASSERT_OK_PTR(ctx, "ctx_alloc")) 1828 goto out; 1829 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 1830 1831 data_sz = gopts.data_sz; 1832 blob = malloc(data_sz); 1833 if (!ASSERT_OK_PTR(blob, "blob_alloc")) 1834 goto out; 1835 memcpy(blob, gopts.data, data_sz); 1836 1837 /* excl_prog_hash = SHA256(loader insns) == the loader's prog->digest. */ 1838 libbpf_sha256(gopts.insns, gopts.insns_sz, excl); 1839 1840 r = loadtime_drive(dir, gopts.insns, gopts.insns_sz, blob, data_sz, 1841 excl, ctx, ctx_sz, &load_ret, &ran); 1842 ASSERT_OK(load_ret, "signed loader loaded (insns || metadata)"); 1843 ASSERT_TRUE(ran, "loader ran"); 1844 ASSERT_EQ(r, 0, "loader installed its target"); 1845 1846 md = (struct bpf_map_desc *)((char *)ctx + sizeof(struct bpf_loader_ctx)); 1847 pd = (struct bpf_prog_desc *)(md + nr_maps); 1848 ASSERT_GT(pd[0].prog_fd, 0, "target program installed"); 1849 if (nr_maps) 1850 ASSERT_GT(md[0].map_fd, 0, "target map installed"); 1851 1852 close_loader_ctx_fds(ctx, nr_maps, nr_progs); 1853 out: 1854 free(blob); 1855 free(ctx); 1856 if (dir) 1857 run_setup("cleanup", dir); 1858 } 1859 1860 static void loadtime_no_map(void) 1861 { 1862 struct test_signed_loader *skel = test_signed_loader__open(); 1863 1864 if (!ASSERT_OK_PTR(skel, "skel_open")) 1865 return; 1866 loadtime_verify(skel->obj, 0); 1867 test_signed_loader__destroy(skel); 1868 } 1869 1870 static void loadtime_with_map(void) 1871 { 1872 struct test_signed_loader_map *skel = test_signed_loader_map__open(); 1873 1874 if (!ASSERT_OK_PTR(skel, "skel_open")) 1875 return; 1876 loadtime_verify(skel->obj, 1); 1877 test_signed_loader_map__destroy(skel); 1878 } 1879 1880 /* 1881 * End-to-end signed load with a post-quantum key. ML-DSA (FIPS-204) is wired 1882 * through the X.509 and PKCS#7 parsers, and BPF reaches them via 1883 * verify_pkcs7_signature() without knowing the algorithm, so an ML-DSA key in 1884 * the keyring should verify an ML-DSA signed program with no BPF-side work. 1885 */ 1886 static void mldsa_signed_load(void) 1887 { 1888 char dir_tmpl[] = "/tmp/bpfmldsaXXXXXX"; 1889 int map_fd = -1, prog_fd = -1, err; 1890 __u8 *sig = NULL, *buf = NULL; 1891 struct gen_loader_fixture f; 1892 bool have_fixture = false; 1893 __u32 sig_sz = 16384; 1894 char *dir; 1895 1896 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 1897 KEY_SPEC_SESSION_KEYRING); 1898 dir = mkdtemp(dir_tmpl); 1899 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 1900 return; 1901 1902 err = run_setup("setup-mldsa", dir); 1903 if (err == SETUP_SKIP) { 1904 printf("%s:SKIP:no working ML-DSA signing, set SELFTESTS_VERBOSE=1\n", 1905 __func__); 1906 test__skip(); 1907 genkey_dir_fini(dir); 1908 return; 1909 } 1910 if (!ASSERT_OK(err, "verify_sig_setup setup-mldsa")) { 1911 genkey_dir_fini(dir); 1912 return; 1913 } 1914 1915 sig = malloc(sig_sz); 1916 if (!ASSERT_OK_PTR(sig, "sig buf")) 1917 goto out; 1918 have_fixture = true; 1919 if (gen_loader_fixture_init(&f) != 0) 1920 goto out; 1921 1922 buf = malloc((size_t)f.gopts.insns_sz + f.data_sz); 1923 if (!ASSERT_OK_PTR(buf, "signbuf")) 1924 goto out; 1925 memcpy(buf, f.gopts.insns, f.gopts.insns_sz); 1926 memcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz); 1927 1928 /* 1929 * ML-DSA hashes the message itself, but openssl before 4.0 cannot 1930 * produce a CMS message without signedAttrs for it, and with those in 1931 * play only SHA-512 is permitted for the messageDigest attribute. 1932 */ 1933 if (!ASSERT_OK(sign_buf_digest(dir, buf, f.gopts.insns_sz + f.data_sz, 1934 sig, &sig_sz, "sha512"), 1935 "sign insns||metadata with ML-DSA")) 1936 goto out; 1937 1938 /* 1939 * Guard against the setup silently handing back some other key type: 1940 * an RSA or ECDSA signature is a few hundred bytes, where an ML-DSA-87 1941 * one cannot be smaller than the raw signature it carries. 1942 */ 1943 ASSERT_GT(sig_sz, MLDSA87_SIGNATURE_SIZE, "ML-DSA-87 signature size"); 1944 1945 map_fd = setup_meta_map(&f); 1946 if (!ASSERT_OK_FD(map_fd, "meta_map")) 1947 goto out; 1948 prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig, 1949 sig_sz, KEY_SPEC_SESSION_KEYRING, 1); 1950 ASSERT_OK_FD(prog_fd, "ML-DSA signed loader load"); 1951 out: 1952 if (prog_fd >= 0) 1953 close(prog_fd); 1954 if (map_fd >= 0) 1955 close(map_fd); 1956 if (have_fixture) 1957 gen_loader_fixture_fini(&f); 1958 free(buf); 1959 free(sig); 1960 run_setup("cleanup", dir); 1961 } 1962 1963 /* 1964 * A signed program need not bind any map. A plain BPF_PROG_TYPE_SYSCALL 1965 * program with no fd_array is signed over its instructions alone: the kernel 1966 * verifies the signature, folds no metadata, and the program loads. Exercise 1967 * the fd_array == NULL / fd_array_cnt == 0 path, and confirm the signature 1968 * still authenticates the instructions (a tampered copy is rejected). 1969 */ 1970 static void signed_no_fd_array(void) 1971 { 1972 struct bpf_insn insns[] = { 1973 BPF_MOV64_IMM(BPF_REG_0, 0), 1974 BPF_EXIT_INSN(), 1975 }; 1976 char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir; 1977 __u32 sig_sz = 8192; 1978 union bpf_attr attr; 1979 __u8 sig[8192]; 1980 int prog_fd, err; 1981 1982 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 1983 KEY_SPEC_SESSION_KEYRING); 1984 dir = mkdtemp(dir_tmpl); 1985 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 1986 return; 1987 if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) { 1988 rmdir(dir); 1989 return; 1990 } 1991 1992 /* No metadata map: the signed payload is the instructions alone. */ 1993 if (!ASSERT_OK(sign_buf(dir, insns, sizeof(insns), sig, &sig_sz), 1994 "sign-file")) 1995 goto cleanup; 1996 1997 memset(&attr, 0, sizeof(attr)); 1998 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 1999 attr.insns = ptr_to_u64(insns); 2000 attr.insn_cnt = ARRAY_SIZE(insns); 2001 attr.license = ptr_to_u64("Dual BSD/GPL"); 2002 attr.prog_flags = BPF_F_SLEEPABLE; 2003 attr.signature = ptr_to_u64(sig); 2004 attr.signature_size = sig_sz; 2005 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 2006 /* fd_array and fd_array_cnt deliberately left NULL/0. */ 2007 memcpy(attr.prog_name, "signed_nomap", sizeof("signed_nomap")); 2008 2009 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 2010 offsetofend(union bpf_attr, keyring_id)); 2011 if (!ASSERT_GE(prog_fd, 0, "map-less signed program loaded")) { 2012 if (prog_fd >= 0) 2013 close(prog_fd); 2014 goto cleanup; 2015 } 2016 close(prog_fd); 2017 2018 /* The signature covers the instructions, so tampering must be rejected. */ 2019 insns[0].imm = 1; 2020 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 2021 offsetofend(union bpf_attr, keyring_id)); 2022 err = prog_fd < 0 ? -errno : prog_fd; 2023 ASSERT_EQ(err, -EKEYREJECTED, "tampered map-less program rejected"); 2024 if (prog_fd >= 0) 2025 close(prog_fd); 2026 cleanup: 2027 run_setup("cleanup", dir); 2028 } 2029 2030 /* 2031 * A signed program may reach maps only through fd_array indices, so the kernel 2032 * folds (and thus attests) them. A direct BPF_PSEUDO_MAP_FD reference - a raw, 2033 * unfolded fd baked into the signed instructions - is rejected by the verifier. 2034 */ 2035 static void signed_map_by_fd_rejected(void) 2036 { 2037 struct bpf_insn insns[] = { 2038 BPF_LD_MAP_FD(BPF_REG_1, 0), 2039 BPF_MOV64_IMM(BPF_REG_0, 0), 2040 BPF_EXIT_INSN(), 2041 }; 2042 char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir; 2043 __u32 sig_sz = 8192; 2044 union bpf_attr attr; 2045 __u8 sig[8192]; 2046 int map_fd, prog_fd, err; 2047 2048 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "sig_mapfd", 4, 4, 1, NULL); 2049 if (!ASSERT_GE(map_fd, 0, "map_create")) 2050 return; 2051 insns[0].imm = map_fd; /* bake the raw map fd into the ld_imm64 */ 2052 2053 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 2054 KEY_SPEC_SESSION_KEYRING); 2055 dir = mkdtemp(dir_tmpl); 2056 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 2057 goto out_map; 2058 if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) { 2059 rmdir(dir); 2060 goto out_map; 2061 } 2062 2063 /* Sign the instructions, raw map fd and all. */ 2064 if (!ASSERT_OK(sign_buf(dir, insns, sizeof(insns), sig, &sig_sz), 2065 "sign-file")) 2066 goto cleanup; 2067 2068 memset(&attr, 0, sizeof(attr)); 2069 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 2070 attr.insns = ptr_to_u64(insns); 2071 attr.insn_cnt = ARRAY_SIZE(insns); 2072 attr.license = ptr_to_u64("Dual BSD/GPL"); 2073 attr.prog_flags = BPF_F_SLEEPABLE; 2074 attr.signature = ptr_to_u64(sig); 2075 attr.signature_size = sig_sz; 2076 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 2077 /* No fd_array: the map is reached by a raw fd in the instructions. */ 2078 memcpy(attr.prog_name, "signed_mapfd", sizeof("signed_mapfd")); 2079 2080 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 2081 offsetofend(union bpf_attr, keyring_id)); 2082 err = prog_fd < 0 ? -errno : prog_fd; 2083 ASSERT_EQ(err, -EINVAL, "signed program referencing a map by fd rejected"); 2084 if (prog_fd >= 0) 2085 close(prog_fd); 2086 cleanup: 2087 run_setup("cleanup", dir); 2088 out_map: 2089 close(map_fd); 2090 } 2091 2092 /* 2093 * A signed program may reach maps only through the continuous fd_array, so the 2094 * kernel folds (and thus attests) them. Referencing a map by fd_array *index* 2095 * while leaving fd_array_cnt at 0 selects the sparse path, which resolves a map 2096 * the signature never covered; the verifier rejects it up front with -EACCES. 2097 */ 2098 static void signed_sparse_fd_array_rejected(void) 2099 { 2100 struct bpf_insn insns[] = { 2101 BPF_LD_IMM64_RAW(BPF_REG_1, BPF_PSEUDO_MAP_IDX, 0), 2102 BPF_MOV64_IMM(BPF_REG_0, 0), 2103 BPF_EXIT_INSN(), 2104 }; 2105 char dir_tmpl[] = "/tmp/signed_loader_spXXXXXX", *dir; 2106 __u32 sig_sz = 8192; 2107 union bpf_attr attr; 2108 __u8 sig[8192]; 2109 int map_fd, prog_fd, err; 2110 2111 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "sig_sparse", 4, 4, 1, NULL); 2112 if (!ASSERT_GE(map_fd, 0, "map_create")) 2113 return; 2114 2115 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 2116 KEY_SPEC_SESSION_KEYRING); 2117 dir = mkdtemp(dir_tmpl); 2118 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 2119 goto out_map; 2120 if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) { 2121 rmdir(dir); 2122 goto out_map; 2123 } 2124 2125 /* Sign the instructions alone; the sparse map is not folded. */ 2126 if (!ASSERT_OK(sign_buf(dir, insns, sizeof(insns), sig, &sig_sz), 2127 "sign-file")) 2128 goto cleanup; 2129 2130 memset(&attr, 0, sizeof(attr)); 2131 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 2132 attr.insns = ptr_to_u64(insns); 2133 attr.insn_cnt = ARRAY_SIZE(insns); 2134 attr.license = ptr_to_u64("Dual BSD/GPL"); 2135 attr.prog_flags = BPF_F_SLEEPABLE; 2136 attr.fd_array = ptr_to_u64(&map_fd); 2137 attr.fd_array_cnt = 0; /* sparse: force lazy map resolution */ 2138 attr.signature = ptr_to_u64(sig); 2139 attr.signature_size = sig_sz; 2140 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 2141 memcpy(attr.prog_name, "signed_sparse", sizeof("signed_sparse")); 2142 2143 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 2144 offsetofend(union bpf_attr, keyring_id)); 2145 err = prog_fd < 0 ? -errno : prog_fd; 2146 ASSERT_EQ(err, -EACCES, "signed program binding a sparse fd_array map rejected"); 2147 if (prog_fd >= 0) 2148 close(prog_fd); 2149 cleanup: 2150 run_setup("cleanup", dir); 2151 out_map: 2152 close(map_fd); 2153 } 2154 2155 static void signed_module_kfunc_rejected(void) 2156 { 2157 struct bpf_insn insns[] = { 2158 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 1, 1), 2159 BPF_MOV64_IMM(BPF_REG_0, 0), 2160 BPF_EXIT_INSN(), 2161 }; 2162 char dir_tmpl[] = "/tmp/signed_loader_kfnXXXXXX", *dir; 2163 int prog_fd, err, fds[2]; 2164 struct btf *btf = NULL; 2165 __u32 sig_sz = 8192; 2166 union bpf_attr attr; 2167 __u8 sig[8192]; 2168 2169 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 2170 KEY_SPEC_SESSION_KEYRING); 2171 dir = mkdtemp(dir_tmpl); 2172 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 2173 return; 2174 if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) { 2175 rmdir(dir); 2176 return; 2177 } 2178 if (!ASSERT_OK(sign_buf(dir, insns, sizeof(insns), sig, &sig_sz), 2179 "sign-file")) 2180 goto cleanup; 2181 btf = btf__new_empty(); 2182 if (!ASSERT_OK_PTR(btf, "btf_new_empty")) 2183 goto cleanup; 2184 btf__add_int(btf, "int", 4, BTF_INT_SIGNED); 2185 if (!ASSERT_OK(btf__load_into_kernel(btf), "btf_load")) 2186 goto cleanup; 2187 fds[0] = -1; 2188 fds[1] = btf__fd(btf); 2189 2190 memset(&attr, 0, sizeof(attr)); 2191 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 2192 attr.insns = ptr_to_u64(insns); 2193 attr.insn_cnt = ARRAY_SIZE(insns); 2194 attr.license = ptr_to_u64("Dual BSD/GPL"); 2195 attr.prog_flags = BPF_F_SLEEPABLE; 2196 attr.fd_array = ptr_to_u64(fds); 2197 attr.fd_array_cnt = 0; /* sparse: force lazy kfunc BTF resolution */ 2198 attr.signature = ptr_to_u64(sig); 2199 attr.signature_size = sig_sz; 2200 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 2201 memcpy(attr.prog_name, "signed_kfunc", sizeof("signed_kfunc")); 2202 2203 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 2204 offsetofend(union bpf_attr, keyring_id)); 2205 err = prog_fd < 0 ? -errno : prog_fd; 2206 if (prog_fd >= 0) 2207 close(prog_fd); 2208 2209 ASSERT_EQ(err, -EACCES, "module kfunc BTF in signed program rejected"); 2210 cleanup: 2211 if (btf) 2212 btf__free(btf); 2213 run_setup("cleanup", dir); 2214 } 2215 2216 enum subtest_boot { 2217 BOOT_ANY, 2218 BOOT_SEALED, 2219 BOOT_UNSEALED, 2220 }; 2221 2222 static const struct { 2223 const char *name; 2224 void (*fn)(void); 2225 enum subtest_boot boot; 2226 } subtests[] = { 2227 { "loadtime_no_map", loadtime_no_map, BOOT_SEALED }, 2228 { "loadtime_with_map", loadtime_with_map, BOOT_SEALED }, 2229 { "metadata_match", metadata_match, BOOT_ANY }, 2230 { "signature_enforced", signature_enforced, BOOT_SEALED }, 2231 { "signed_nonexcl_fd_array_rejected", signed_nonexcl_fd_array_rejected, BOOT_SEALED }, 2232 { "signed_unfrozen_fd_array_rejected", signed_unfrozen_fd_array_rejected, BOOT_SEALED }, 2233 { "signed_nonarray_fd_array_rejected", signed_nonarray_fd_array_rejected, BOOT_SEALED }, 2234 { "signed_btf_fd_array_rejected", signed_btf_fd_array_rejected, BOOT_ANY }, 2235 { "signed_core_relo_rejected", signed_core_relo_rejected, BOOT_ANY }, 2236 { "signed_module_kfunc_rejected", signed_module_kfunc_rejected, BOOT_SEALED }, 2237 { "signature_failure_logs", signature_failure_logs, BOOT_SEALED }, 2238 { "signature_too_large", signature_too_large, BOOT_ANY }, 2239 { "signature_zero_size", signature_zero_size, BOOT_ANY }, 2240 { "signature_bad_keyring", signature_bad_keyring, BOOT_SEALED }, 2241 { "bpf_keyring_sealed", bpf_keyring_sealed, BOOT_ANY }, 2242 { "mldsa_signed_load", mldsa_signed_load, BOOT_SEALED }, 2243 { "metadata_ctx_max_entries_ignored", metadata_ctx_max_entries_ignored, BOOT_ANY }, 2244 { "metadata_ctx_initial_value_ignored", metadata_ctx_initial_value_ignored, BOOT_ANY }, 2245 { "signature_authenticates_insns", signature_authenticates_insns, BOOT_SEALED }, 2246 { "signature_authenticates_metadata", signature_authenticates_metadata, BOOT_SEALED }, 2247 { "hash_requires_frozen", hash_requires_frozen, BOOT_ANY }, 2248 { "no_update_after_freeze", no_update_after_freeze, BOOT_ANY }, 2249 { "freeze_writable_mmap", freeze_writable_mmap, BOOT_ANY }, 2250 { "no_writable_mmap_frozen", no_writable_mmap_frozen, BOOT_ANY }, 2251 { "map_hash_matches_libbpf", map_hash_matches_libbpf, BOOT_ANY }, 2252 { "map_hash_multi_element", map_hash_multi_element, BOOT_ANY }, 2253 { "map_hash_bad_size", map_hash_bad_size, BOOT_ANY }, 2254 { "map_hash_unsupported_type", map_hash_unsupported_type, BOOT_ANY }, 2255 { "lsm_signature_verdict", lsm_signature_verdict, BOOT_SEALED }, 2256 { "signed_no_fd_array", signed_no_fd_array, BOOT_SEALED }, 2257 { "signed_map_by_fd_rejected", signed_map_by_fd_rejected, BOOT_SEALED }, 2258 { "signed_sparse_fd_array_rejected", signed_sparse_fd_array_rejected, BOOT_SEALED }, 2259 { "bpf_keyring_provisioned", bpf_keyring_provisioned, BOOT_UNSEALED }, 2260 }; 2261 2262 void test_signed_loader(void) 2263 { 2264 bool unsealed = keyring_unsealed_boot(); 2265 unsigned int i; 2266 2267 for (i = 0; i < ARRAY_SIZE(subtests); i++) { 2268 if (!test__start_subtest(subtests[i].name)) 2269 continue; 2270 if (subtests[i].boot == BOOT_SEALED && unsealed) { 2271 printf("%s:SKIP:needs a boot without bpf.keyring_unsealed=1\n", 2272 subtests[i].name); 2273 test__skip(); 2274 continue; 2275 } 2276 if (subtests[i].boot == BOOT_UNSEALED && !unsealed) { 2277 printf("%s:SKIP:needs bpf.keyring_unsealed=1\n", 2278 subtests[i].name); 2279 test__skip(); 2280 continue; 2281 } 2282 subtests[i].fn(); 2283 } 2284 } 2285