566 lines · c
1// SPDX-License-Identifier: GPL-2.02 3/*4 * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH5 *6 * Author: Roberto Sassu <roberto.sassu@huawei.com>7 */8 9#include <stdio.h>10#include <errno.h>11#include <stdlib.h>12#include <unistd.h>13#include <endian.h>14#include <limits.h>15#include <sys/stat.h>16#include <sys/wait.h>17#include <sys/mman.h>18#include <linux/keyctl.h>19#include <sys/xattr.h>20#include <linux/fsverity.h>21#include <test_progs.h>22 23#include "test_verify_pkcs7_sig.skel.h"24#include "test_sig_in_xattr.skel.h"25 26#define MAX_DATA_SIZE (1024 * 1024)27#define MAX_SIG_SIZE 102428 29#define VERIFY_USE_SECONDARY_KEYRING (1UL)30#define VERIFY_USE_PLATFORM_KEYRING (2UL)31 32#ifndef SHA256_DIGEST_SIZE33#define SHA256_DIGEST_SIZE 3234#endif35 36/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */37#define MODULE_SIG_STRING "~Module signature appended~\n"38 39/*40 * Module signature information block.41 *42 * The constituents of the signature section are, in order:43 *44 * - Signer's name45 * - Key identifier46 * - Signature data47 * - Information block48 */49struct module_signature {50 __u8 algo; /* Public-key crypto algorithm [0] */51 __u8 hash; /* Digest algorithm [0] */52 __u8 id_type; /* Key identifier type [PKEY_ID_PKCS7] */53 __u8 signer_len; /* Length of signer's name [0] */54 __u8 key_id_len; /* Length of key identifier [0] */55 __u8 __pad[3];56 __be32 sig_len; /* Length of signature data */57};58 59struct data {60 __u8 data[MAX_DATA_SIZE];61 __u32 data_len;62 __u8 sig[MAX_SIG_SIZE];63 __u32 sig_len;64};65 66static bool kfunc_not_supported;67 68static int libbpf_print_cb(enum libbpf_print_level level, const char *fmt,69 va_list args)70{71 if (level == LIBBPF_WARN)72 vprintf(fmt, args);73 74 if (strcmp(fmt, "libbpf: extern (func ksym) '%s': not found in kernel or module BTFs\n"))75 return 0;76 77 if (strcmp(va_arg(args, char *), "bpf_verify_pkcs7_signature"))78 return 0;79 80 kfunc_not_supported = true;81 return 0;82}83 84static int _run_setup_process(const char *setup_dir, const char *cmd)85{86 int child_pid, child_status;87 88 child_pid = fork();89 if (child_pid == 0) {90 execlp("./verify_sig_setup.sh", "./verify_sig_setup.sh", cmd,91 setup_dir, NULL);92 exit(errno);93 94 } else if (child_pid > 0) {95 waitpid(child_pid, &child_status, 0);96 return WEXITSTATUS(child_status);97 }98 99 return -EINVAL;100}101 102static int populate_data_item_str(const char *tmp_dir, struct data *data_item)103{104 struct stat st;105 char data_template[] = "/tmp/dataXXXXXX";106 char path[PATH_MAX];107 int ret, fd, child_status, child_pid;108 109 data_item->data_len = 4;110 memcpy(data_item->data, "test", data_item->data_len);111 112 fd = mkstemp(data_template);113 if (fd == -1)114 return -errno;115 116 ret = write(fd, data_item->data, data_item->data_len);117 118 close(fd);119 120 if (ret != data_item->data_len) {121 ret = -EIO;122 goto out;123 }124 125 child_pid = fork();126 127 if (child_pid == -1) {128 ret = -errno;129 goto out;130 }131 132 if (child_pid == 0) {133 snprintf(path, sizeof(path), "%s/signing_key.pem", tmp_dir);134 135 return execlp("./sign-file", "./sign-file", "-d", "sha256",136 path, path, data_template, NULL);137 }138 139 waitpid(child_pid, &child_status, 0);140 141 ret = WEXITSTATUS(child_status);142 if (ret)143 goto out;144 145 snprintf(path, sizeof(path), "%s.p7s", data_template);146 147 ret = stat(path, &st);148 if (ret == -1) {149 ret = -errno;150 goto out;151 }152 153 if (st.st_size > sizeof(data_item->sig)) {154 ret = -EINVAL;155 goto out_sig;156 }157 158 data_item->sig_len = st.st_size;159 160 fd = open(path, O_RDONLY);161 if (fd == -1) {162 ret = -errno;163 goto out_sig;164 }165 166 ret = read(fd, data_item->sig, data_item->sig_len);167 168 close(fd);169 170 if (ret != data_item->sig_len) {171 ret = -EIO;172 goto out_sig;173 }174 175 ret = 0;176out_sig:177 unlink(path);178out:179 unlink(data_template);180 return ret;181}182 183static int populate_data_item_mod(struct data *data_item)184{185 char mod_path[PATH_MAX], *mod_path_ptr;186 struct stat st;187 void *mod;188 FILE *fp;189 struct module_signature ms;190 int ret, fd, modlen, marker_len, sig_len;191 192 data_item->data_len = 0;193 194 if (stat("/lib/modules", &st) == -1)195 return 0;196 197 /* Requires CONFIG_TCP_CONG_BIC=m. */198 fp = popen("find /lib/modules/$(uname -r) -name tcp_bic.ko", "r");199 if (!fp)200 return 0;201 202 mod_path_ptr = fgets(mod_path, sizeof(mod_path), fp);203 pclose(fp);204 205 if (!mod_path_ptr)206 return 0;207 208 mod_path_ptr = strchr(mod_path, '\n');209 if (!mod_path_ptr)210 return 0;211 212 *mod_path_ptr = '\0';213 214 if (stat(mod_path, &st) == -1)215 return 0;216 217 modlen = st.st_size;218 marker_len = sizeof(MODULE_SIG_STRING) - 1;219 220 fd = open(mod_path, O_RDONLY);221 if (fd == -1)222 return -errno;223 224 mod = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);225 226 close(fd);227 228 if (mod == MAP_FAILED)229 return -errno;230 231 if (strncmp(mod + modlen - marker_len, MODULE_SIG_STRING, marker_len)) {232 ret = -EINVAL;233 goto out;234 }235 236 modlen -= marker_len;237 238 memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms));239 240 sig_len = __be32_to_cpu(ms.sig_len);241 modlen -= sig_len + sizeof(ms);242 243 if (modlen > sizeof(data_item->data)) {244 ret = -E2BIG;245 goto out;246 }247 248 memcpy(data_item->data, mod, modlen);249 data_item->data_len = modlen;250 251 if (sig_len > sizeof(data_item->sig)) {252 ret = -E2BIG;253 goto out;254 }255 256 memcpy(data_item->sig, mod + modlen, sig_len);257 data_item->sig_len = sig_len;258 ret = 0;259out:260 munmap(mod, st.st_size);261 return ret;262}263 264static void test_verify_pkcs7_sig_from_map(void)265{266 libbpf_print_fn_t old_print_cb;267 char tmp_dir_template[] = "/tmp/verify_sigXXXXXX";268 char *tmp_dir;269 struct test_verify_pkcs7_sig *skel = NULL;270 struct bpf_map *map;271 struct data data;272 int ret, zero = 0;273 274 /* Trigger creation of session keyring. */275 syscall(__NR_request_key, "keyring", "_uid.0", NULL,276 KEY_SPEC_SESSION_KEYRING);277 278 tmp_dir = mkdtemp(tmp_dir_template);279 if (!ASSERT_OK_PTR(tmp_dir, "mkdtemp"))280 return;281 282 ret = _run_setup_process(tmp_dir, "setup");283 if (!ASSERT_OK(ret, "_run_setup_process"))284 goto close_prog;285 286 skel = test_verify_pkcs7_sig__open();287 if (!ASSERT_OK_PTR(skel, "test_verify_pkcs7_sig__open"))288 goto close_prog;289 290 old_print_cb = libbpf_set_print(libbpf_print_cb);291 ret = test_verify_pkcs7_sig__load(skel);292 libbpf_set_print(old_print_cb);293 294 if (ret < 0 && kfunc_not_supported) {295 printf(296 "%s:SKIP:bpf_verify_pkcs7_signature() kfunc not supported\n",297 __func__);298 test__skip();299 goto close_prog;300 }301 302 if (!ASSERT_OK(ret, "test_verify_pkcs7_sig__load"))303 goto close_prog;304 305 ret = test_verify_pkcs7_sig__attach(skel);306 if (!ASSERT_OK(ret, "test_verify_pkcs7_sig__attach"))307 goto close_prog;308 309 map = bpf_object__find_map_by_name(skel->obj, "data_input");310 if (!ASSERT_OK_PTR(map, "data_input not found"))311 goto close_prog;312 313 skel->bss->monitored_pid = getpid();314 315 /* Test without data and signature. */316 skel->bss->user_keyring_serial = KEY_SPEC_SESSION_KEYRING;317 318 ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);319 if (!ASSERT_LT(ret, 0, "bpf_map_update_elem data_input"))320 goto close_prog;321 322 /* Test successful signature verification with session keyring. */323 ret = populate_data_item_str(tmp_dir, &data);324 if (!ASSERT_OK(ret, "populate_data_item_str"))325 goto close_prog;326 327 ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);328 if (!ASSERT_OK(ret, "bpf_map_update_elem data_input"))329 goto close_prog;330 331 /* Test successful signature verification with testing keyring. */332 skel->bss->user_keyring_serial = syscall(__NR_request_key, "keyring",333 "ebpf_testing_keyring", NULL,334 KEY_SPEC_SESSION_KEYRING);335 336 ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);337 if (!ASSERT_OK(ret, "bpf_map_update_elem data_input"))338 goto close_prog;339 340 /*341 * Ensure key_task_permission() is called and rejects the keyring342 * (no Search permission).343 */344 syscall(__NR_keyctl, KEYCTL_SETPERM, skel->bss->user_keyring_serial,345 0x37373737);346 347 ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);348 if (!ASSERT_LT(ret, 0, "bpf_map_update_elem data_input"))349 goto close_prog;350 351 syscall(__NR_keyctl, KEYCTL_SETPERM, skel->bss->user_keyring_serial,352 0x3f3f3f3f);353 354 /*355 * Ensure key_validate() is called and rejects the keyring (key expired)356 */357 syscall(__NR_keyctl, KEYCTL_SET_TIMEOUT,358 skel->bss->user_keyring_serial, 1);359 sleep(1);360 361 ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);362 if (!ASSERT_LT(ret, 0, "bpf_map_update_elem data_input"))363 goto close_prog;364 365 skel->bss->user_keyring_serial = KEY_SPEC_SESSION_KEYRING;366 367 /* Test with corrupted data (signature verification should fail). */368 data.data[0] = 'a';369 ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);370 if (!ASSERT_LT(ret, 0, "bpf_map_update_elem data_input"))371 goto close_prog;372 373 ret = populate_data_item_mod(&data);374 if (!ASSERT_OK(ret, "populate_data_item_mod"))375 goto close_prog;376 377 /* Test signature verification with system keyrings. */378 if (data.data_len) {379 skel->bss->user_keyring_serial = 0;380 skel->bss->system_keyring_id = 0;381 382 ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data,383 BPF_ANY);384 if (!ASSERT_OK(ret, "bpf_map_update_elem data_input"))385 goto close_prog;386 387 skel->bss->system_keyring_id = VERIFY_USE_SECONDARY_KEYRING;388 389 ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data,390 BPF_ANY);391 if (!ASSERT_OK(ret, "bpf_map_update_elem data_input"))392 goto close_prog;393 394 skel->bss->system_keyring_id = VERIFY_USE_PLATFORM_KEYRING;395 396 ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data,397 BPF_ANY);398 ASSERT_LT(ret, 0, "bpf_map_update_elem data_input");399 }400 401close_prog:402 _run_setup_process(tmp_dir, "cleanup");403 404 if (!skel)405 return;406 407 skel->bss->monitored_pid = 0;408 test_verify_pkcs7_sig__destroy(skel);409}410 411static int get_signature_size(const char *sig_path)412{413 struct stat st;414 415 if (stat(sig_path, &st) == -1)416 return -1;417 418 return st.st_size;419}420 421static int add_signature_to_xattr(const char *data_path, const char *sig_path)422{423 char sig[MAX_SIG_SIZE] = {0};424 int fd, size, ret;425 426 if (sig_path) {427 fd = open(sig_path, O_RDONLY);428 if (fd < 0)429 return -1;430 431 size = read(fd, sig, MAX_SIG_SIZE);432 close(fd);433 if (size <= 0)434 return -1;435 } else {436 /* no sig_path, just write 32 bytes of zeros */437 size = 32;438 }439 ret = setxattr(data_path, "user.sig", sig, size, 0);440 if (!ASSERT_OK(ret, "setxattr"))441 return -1;442 443 return 0;444}445 446static int test_open_file(struct test_sig_in_xattr *skel, char *data_path,447 pid_t pid, bool should_success, char *name)448{449 int ret;450 451 skel->bss->monitored_pid = pid;452 ret = open(data_path, O_RDONLY);453 close(ret);454 skel->bss->monitored_pid = 0;455 456 if (should_success) {457 if (!ASSERT_GE(ret, 0, name))458 return -1;459 } else {460 if (!ASSERT_LT(ret, 0, name))461 return -1;462 }463 return 0;464}465 466static void test_pkcs7_sig_fsverity(void)467{468 char data_path[PATH_MAX];469 char sig_path[PATH_MAX];470 char tmp_dir_template[] = "/tmp/verify_sigXXXXXX";471 char *tmp_dir;472 struct test_sig_in_xattr *skel = NULL;473 pid_t pid;474 int ret;475 476 tmp_dir = mkdtemp(tmp_dir_template);477 if (!ASSERT_OK_PTR(tmp_dir, "mkdtemp"))478 return;479 480 snprintf(data_path, PATH_MAX, "%s/data-file", tmp_dir);481 snprintf(sig_path, PATH_MAX, "%s/sig-file", tmp_dir);482 483 ret = _run_setup_process(tmp_dir, "setup");484 if (!ASSERT_OK(ret, "_run_setup_process"))485 goto out;486 487 ret = _run_setup_process(tmp_dir, "fsverity-create-sign");488 489 if (ret) {490 printf("%s: SKIP: fsverity [sign|enable] doesn't work.\n"491 "To run this test, try enable CONFIG_FS_VERITY and enable FSVerity for the filesystem.\n",492 __func__);493 test__skip();494 goto out;495 }496 497 skel = test_sig_in_xattr__open();498 if (!ASSERT_OK_PTR(skel, "test_sig_in_xattr__open"))499 goto out;500 ret = get_signature_size(sig_path);501 if (!ASSERT_GT(ret, 0, "get_signature_size"))502 goto out;503 skel->bss->sig_size = ret;504 skel->bss->user_keyring_serial = syscall(__NR_request_key, "keyring",505 "ebpf_testing_keyring", NULL,506 KEY_SPEC_SESSION_KEYRING);507 memcpy(skel->bss->digest, "FSVerity", 8);508 509 ret = test_sig_in_xattr__load(skel);510 if (!ASSERT_OK(ret, "test_sig_in_xattr__load"))511 goto out;512 513 ret = test_sig_in_xattr__attach(skel);514 if (!ASSERT_OK(ret, "test_sig_in_xattr__attach"))515 goto out;516 517 pid = getpid();518 519 /* Case 1: fsverity is not enabled, open should succeed */520 if (test_open_file(skel, data_path, pid, true, "open_1"))521 goto out;522 523 /* Case 2: fsverity is enabled, xattr is missing, open should524 * fail525 */526 ret = _run_setup_process(tmp_dir, "fsverity-enable");527 if (!ASSERT_OK(ret, "fsverity-enable"))528 goto out;529 if (test_open_file(skel, data_path, pid, false, "open_2"))530 goto out;531 532 /* Case 3: fsverity is enabled, xattr has valid signature, open533 * should succeed534 */535 ret = add_signature_to_xattr(data_path, sig_path);536 if (!ASSERT_OK(ret, "add_signature_to_xattr_1"))537 goto out;538 539 if (test_open_file(skel, data_path, pid, true, "open_3"))540 goto out;541 542 /* Case 4: fsverity is enabled, xattr has invalid signature, open543 * should fail544 */545 ret = add_signature_to_xattr(data_path, NULL);546 if (!ASSERT_OK(ret, "add_signature_to_xattr_2"))547 goto out;548 test_open_file(skel, data_path, pid, false, "open_4");549 550out:551 _run_setup_process(tmp_dir, "cleanup");552 if (!skel)553 return;554 555 skel->bss->monitored_pid = 0;556 test_sig_in_xattr__destroy(skel);557}558 559void test_verify_pkcs7_sig(void)560{561 if (test__start_subtest("pkcs7_sig_from_map"))562 test_verify_pkcs7_sig_from_map();563 if (test__start_subtest("pkcs7_sig_fsverity"))564 test_pkcs7_sig_fsverity();565}566