Commit a77c2cfd authored by Alexei Starovoitov's avatar Alexei Starovoitov

Merge branch 'bpf-lsm: Extend interoperability with IMA'

Roberto Sassu says:

====================
Extend the interoperability with IMA, to give wider flexibility for the
implementation of integrity-focused LSMs based on eBPF.

Patch 1 fixes some style issues.

Patches 2-6 give the ability to eBPF-based LSMs to take advantage of the
measurement capability of IMA without needing to setup a policy in IMA
(those LSMs might implement the policy capability themselves).

Patches 7-9 allow eBPF-based LSMs to evaluate files read by the kernel.

Changelog

v2:
- Add better description to patch 1 (suggested by Shuah)
- Recalculate digest if it is not fresh (when IMA_COLLECTED flag not set)
- Move declaration of bpf_ima_file_hash() at the end (suggested by
  Yonghong)
- Add tests to check if the digest has been recalculated
- Add deny test for bpf_kernel_read_file()
- Add description to tests

v1:
- Modify ima_file_hash() only and allow the usage of the function with the
  modified behavior by eBPF-based LSMs through the new function
  bpf_ima_file_hash() (suggested by Mimi)
- Make bpf_lsm_kernel_read_file() sleepable so that bpf_ima_inode_hash()
  and bpf_ima_file_hash() can be called inside the implementation of
  eBPF-based LSMs for this hook
====================
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 357b3cc3 7bae42b6
...@@ -5119,6 +5119,16 @@ union bpf_attr { ...@@ -5119,6 +5119,16 @@ union bpf_attr {
* 0 on success. * 0 on success.
* **-EINVAL** for invalid input * **-EINVAL** for invalid input
* **-EOPNOTSUPP** for unsupported protocol * **-EOPNOTSUPP** for unsupported protocol
*
* long bpf_ima_file_hash(struct file *file, void *dst, u32 size)
* Description
* Returns a calculated IMA hash of the *file*.
* If the hash is larger than *size*, then only *size*
* bytes will be copied to *dst*
* Return
* The **hash_algo** is returned on success,
* **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if
* invalid arguments are passed.
*/ */
#define __BPF_FUNC_MAPPER(FN) \ #define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \ FN(unspec), \
...@@ -5314,6 +5324,7 @@ union bpf_attr { ...@@ -5314,6 +5324,7 @@ union bpf_attr {
FN(xdp_store_bytes), \ FN(xdp_store_bytes), \
FN(copy_from_user_task), \ FN(copy_from_user_task), \
FN(skb_set_tstamp), \ FN(skb_set_tstamp), \
FN(ima_file_hash), \
/* */ /* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper /* integer value in 'imm' field of BPF_CALL instruction selects which helper
......
...@@ -99,6 +99,24 @@ static const struct bpf_func_proto bpf_ima_inode_hash_proto = { ...@@ -99,6 +99,24 @@ static const struct bpf_func_proto bpf_ima_inode_hash_proto = {
.allowed = bpf_ima_inode_hash_allowed, .allowed = bpf_ima_inode_hash_allowed,
}; };
BPF_CALL_3(bpf_ima_file_hash, struct file *, file, void *, dst, u32, size)
{
return ima_file_hash(file, dst, size);
}
BTF_ID_LIST_SINGLE(bpf_ima_file_hash_btf_ids, struct, file)
static const struct bpf_func_proto bpf_ima_file_hash_proto = {
.func = bpf_ima_file_hash,
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &bpf_ima_file_hash_btf_ids[0],
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
.arg3_type = ARG_CONST_SIZE,
.allowed = bpf_ima_inode_hash_allowed,
};
static const struct bpf_func_proto * static const struct bpf_func_proto *
bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
{ {
...@@ -121,6 +139,8 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) ...@@ -121,6 +139,8 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return &bpf_bprm_opts_set_proto; return &bpf_bprm_opts_set_proto;
case BPF_FUNC_ima_inode_hash: case BPF_FUNC_ima_inode_hash:
return prog->aux->sleepable ? &bpf_ima_inode_hash_proto : NULL; return prog->aux->sleepable ? &bpf_ima_inode_hash_proto : NULL;
case BPF_FUNC_ima_file_hash:
return prog->aux->sleepable ? &bpf_ima_file_hash_proto : NULL;
default: default:
return tracing_prog_func_proto(func_id, prog); return tracing_prog_func_proto(func_id, prog);
} }
...@@ -167,6 +187,7 @@ BTF_ID(func, bpf_lsm_inode_setxattr) ...@@ -167,6 +187,7 @@ BTF_ID(func, bpf_lsm_inode_setxattr)
BTF_ID(func, bpf_lsm_inode_symlink) BTF_ID(func, bpf_lsm_inode_symlink)
BTF_ID(func, bpf_lsm_inode_unlink) BTF_ID(func, bpf_lsm_inode_unlink)
BTF_ID(func, bpf_lsm_kernel_module_request) BTF_ID(func, bpf_lsm_kernel_module_request)
BTF_ID(func, bpf_lsm_kernel_read_file)
BTF_ID(func, bpf_lsm_kernfs_init_security) BTF_ID(func, bpf_lsm_kernfs_init_security)
#ifdef CONFIG_KEYS #ifdef CONFIG_KEYS
......
...@@ -418,6 +418,7 @@ int ima_file_mmap(struct file *file, unsigned long prot) ...@@ -418,6 +418,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
/** /**
* ima_file_mprotect - based on policy, limit mprotect change * ima_file_mprotect - based on policy, limit mprotect change
* @vma: vm_area_struct protection is set to
* @prot: contains the protection that will be applied by the kernel. * @prot: contains the protection that will be applied by the kernel.
* *
* Files can be mmap'ed read/write and later changed to execute to circumvent * Files can be mmap'ed read/write and later changed to execute to circumvent
...@@ -519,20 +520,38 @@ int ima_file_check(struct file *file, int mask) ...@@ -519,20 +520,38 @@ int ima_file_check(struct file *file, int mask)
} }
EXPORT_SYMBOL_GPL(ima_file_check); EXPORT_SYMBOL_GPL(ima_file_check);
static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size) static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
size_t buf_size)
{ {
struct integrity_iint_cache *iint; struct integrity_iint_cache *iint = NULL, tmp_iint;
int hash_algo; int rc, hash_algo;
if (!ima_policy_flag) if (ima_policy_flag) {
return -EOPNOTSUPP; iint = integrity_iint_find(inode);
if (iint)
mutex_lock(&iint->mutex);
}
if ((!iint || !(iint->flags & IMA_COLLECTED)) && file) {
if (iint)
mutex_unlock(&iint->mutex);
memset(&tmp_iint, 0, sizeof(tmp_iint));
tmp_iint.inode = inode;
mutex_init(&tmp_iint.mutex);
rc = ima_collect_measurement(&tmp_iint, file, NULL, 0,
ima_hash_algo, NULL);
if (rc < 0)
return -EOPNOTSUPP;
iint = &tmp_iint;
mutex_lock(&iint->mutex);
}
iint = integrity_iint_find(inode);
if (!iint) if (!iint)
return -EOPNOTSUPP; return -EOPNOTSUPP;
mutex_lock(&iint->mutex);
/* /*
* ima_file_hash can be called when ima_collect_measurement has still * ima_file_hash can be called when ima_collect_measurement has still
* not been called, we might not always have a hash. * not been called, we might not always have a hash.
...@@ -551,12 +570,14 @@ static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size) ...@@ -551,12 +570,14 @@ static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
hash_algo = iint->ima_hash->algo; hash_algo = iint->ima_hash->algo;
mutex_unlock(&iint->mutex); mutex_unlock(&iint->mutex);
if (iint == &tmp_iint)
kfree(iint->ima_hash);
return hash_algo; return hash_algo;
} }
/** /**
* ima_file_hash - return the stored measurement if a file has been hashed and * ima_file_hash - return a measurement of the file
* is in the iint cache.
* @file: pointer to the file * @file: pointer to the file
* @buf: buffer in which to store the hash * @buf: buffer in which to store the hash
* @buf_size: length of the buffer * @buf_size: length of the buffer
...@@ -569,7 +590,7 @@ static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size) ...@@ -569,7 +590,7 @@ static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
* The file hash returned is based on the entire file, including the appended * The file hash returned is based on the entire file, including the appended
* signature. * signature.
* *
* If IMA is disabled or if no measurement is available, return -EOPNOTSUPP. * If the measurement cannot be performed, return -EOPNOTSUPP.
* If the parameters are incorrect, return -EINVAL. * If the parameters are incorrect, return -EINVAL.
*/ */
int ima_file_hash(struct file *file, char *buf, size_t buf_size) int ima_file_hash(struct file *file, char *buf, size_t buf_size)
...@@ -577,7 +598,7 @@ int ima_file_hash(struct file *file, char *buf, size_t buf_size) ...@@ -577,7 +598,7 @@ int ima_file_hash(struct file *file, char *buf, size_t buf_size)
if (!file) if (!file)
return -EINVAL; return -EINVAL;
return __ima_inode_hash(file_inode(file), buf, buf_size); return __ima_inode_hash(file_inode(file), file, buf, buf_size);
} }
EXPORT_SYMBOL_GPL(ima_file_hash); EXPORT_SYMBOL_GPL(ima_file_hash);
...@@ -604,14 +625,14 @@ int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size) ...@@ -604,14 +625,14 @@ int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
if (!inode) if (!inode)
return -EINVAL; return -EINVAL;
return __ima_inode_hash(inode, buf, buf_size); return __ima_inode_hash(inode, NULL, buf, buf_size);
} }
EXPORT_SYMBOL_GPL(ima_inode_hash); EXPORT_SYMBOL_GPL(ima_inode_hash);
/** /**
* ima_post_create_tmpfile - mark newly created tmpfile as new * ima_post_create_tmpfile - mark newly created tmpfile as new
* @mnt_userns: user namespace of the mount the inode was found from * @mnt_userns: user namespace of the mount the inode was found from
* @file : newly created tmpfile * @inode: inode of the newly created tmpfile
* *
* No measuring, appraising or auditing of newly created tmpfiles is needed. * No measuring, appraising or auditing of newly created tmpfiles is needed.
* Skip calling process_measurement(), but indicate which newly, created * Skip calling process_measurement(), but indicate which newly, created
...@@ -643,7 +664,7 @@ void ima_post_create_tmpfile(struct user_namespace *mnt_userns, ...@@ -643,7 +664,7 @@ void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
/** /**
* ima_post_path_mknod - mark as a new inode * ima_post_path_mknod - mark as a new inode
* @mnt_userns: user namespace of the mount the inode was found from * @mnt_userns: user namespace of the mount the inode was found from
* @dentry: newly created dentry * @dentry: newly created dentry
* *
* Mark files created via the mknodat syscall as new, so that the * Mark files created via the mknodat syscall as new, so that the
...@@ -814,8 +835,8 @@ int ima_load_data(enum kernel_load_data_id id, bool contents) ...@@ -814,8 +835,8 @@ int ima_load_data(enum kernel_load_data_id id, bool contents)
* ima_post_load_data - appraise decision based on policy * ima_post_load_data - appraise decision based on policy
* @buf: pointer to in memory file contents * @buf: pointer to in memory file contents
* @size: size of in memory file contents * @size: size of in memory file contents
* @id: kernel load data caller identifier * @load_id: kernel load data caller identifier
* @description: @id-specific description of contents * @description: @load_id-specific description of contents
* *
* Measure/appraise/audit in memory buffer based on policy. Policy rules * Measure/appraise/audit in memory buffer based on policy. Policy rules
* are written in terms of a policy identifier. * are written in terms of a policy identifier.
......
...@@ -5119,6 +5119,16 @@ union bpf_attr { ...@@ -5119,6 +5119,16 @@ union bpf_attr {
* 0 on success. * 0 on success.
* **-EINVAL** for invalid input * **-EINVAL** for invalid input
* **-EOPNOTSUPP** for unsupported protocol * **-EOPNOTSUPP** for unsupported protocol
*
* long bpf_ima_file_hash(struct file *file, void *dst, u32 size)
* Description
* Returns a calculated IMA hash of the *file*.
* If the hash is larger than *size*, then only *size*
* bytes will be copied to *dst*
* Return
* The **hash_algo** is returned on success,
* **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if
* invalid arguments are passed.
*/ */
#define __BPF_FUNC_MAPPER(FN) \ #define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \ FN(unspec), \
...@@ -5314,6 +5324,7 @@ union bpf_attr { ...@@ -5314,6 +5324,7 @@ union bpf_attr {
FN(xdp_store_bytes), \ FN(xdp_store_bytes), \
FN(copy_from_user_task), \ FN(copy_from_user_task), \
FN(skb_set_tstamp), \ FN(skb_set_tstamp), \
FN(ima_file_hash), \
/* */ /* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper /* integer value in 'imm' field of BPF_CALL instruction selects which helper
......
...@@ -12,7 +12,7 @@ LOG_FILE="$(mktemp /tmp/ima_setup.XXXX.log)" ...@@ -12,7 +12,7 @@ LOG_FILE="$(mktemp /tmp/ima_setup.XXXX.log)"
usage() usage()
{ {
echo "Usage: $0 <setup|cleanup|run> <existing_tmp_dir>" echo "Usage: $0 <setup|cleanup|run|modify-bin|restore-bin|load-policy> <existing_tmp_dir>"
exit 1 exit 1
} }
...@@ -51,6 +51,7 @@ setup() ...@@ -51,6 +51,7 @@ setup()
ensure_mount_securityfs ensure_mount_securityfs
echo "measure func=BPRM_CHECK fsuuid=${mount_uuid}" > ${IMA_POLICY_FILE} echo "measure func=BPRM_CHECK fsuuid=${mount_uuid}" > ${IMA_POLICY_FILE}
echo "measure func=BPRM_CHECK fsuuid=${mount_uuid}" > ${mount_dir}/policy_test
} }
cleanup() { cleanup() {
...@@ -77,6 +78,32 @@ run() ...@@ -77,6 +78,32 @@ run()
exec "${copied_bin_path}" exec "${copied_bin_path}"
} }
modify_bin()
{
local tmp_dir="$1"
local mount_dir="${tmp_dir}/mnt"
local copied_bin_path="${mount_dir}/$(basename ${TEST_BINARY})"
echo "mod" >> "${copied_bin_path}"
}
restore_bin()
{
local tmp_dir="$1"
local mount_dir="${tmp_dir}/mnt"
local copied_bin_path="${mount_dir}/$(basename ${TEST_BINARY})"
truncate -s -4 "${copied_bin_path}"
}
load_policy()
{
local tmp_dir="$1"
local mount_dir="${tmp_dir}/mnt"
echo ${mount_dir}/policy_test > ${IMA_POLICY_FILE} 2> /dev/null
}
catch() catch()
{ {
local exit_code="$1" local exit_code="$1"
...@@ -105,6 +132,12 @@ main() ...@@ -105,6 +132,12 @@ main()
cleanup "${tmp_dir}" cleanup "${tmp_dir}"
elif [[ "${action}" == "run" ]]; then elif [[ "${action}" == "run" ]]; then
run "${tmp_dir}" run "${tmp_dir}"
elif [[ "${action}" == "modify-bin" ]]; then
modify_bin "${tmp_dir}"
elif [[ "${action}" == "restore-bin" ]]; then
restore_bin "${tmp_dir}"
elif [[ "${action}" == "load-policy" ]]; then
load_policy "${tmp_dir}"
else else
echo "Unknown action: ${action}" echo "Unknown action: ${action}"
exit 1 exit 1
......
...@@ -13,14 +13,17 @@ ...@@ -13,14 +13,17 @@
#include "ima.skel.h" #include "ima.skel.h"
static int run_measured_process(const char *measured_dir, u32 *monitored_pid) #define MAX_SAMPLES 4
static int _run_measured_process(const char *measured_dir, u32 *monitored_pid,
const char *cmd)
{ {
int child_pid, child_status; int child_pid, child_status;
child_pid = fork(); child_pid = fork();
if (child_pid == 0) { if (child_pid == 0) {
*monitored_pid = getpid(); *monitored_pid = getpid();
execlp("./ima_setup.sh", "./ima_setup.sh", "run", measured_dir, execlp("./ima_setup.sh", "./ima_setup.sh", cmd, measured_dir,
NULL); NULL);
exit(errno); exit(errno);
...@@ -32,19 +35,39 @@ static int run_measured_process(const char *measured_dir, u32 *monitored_pid) ...@@ -32,19 +35,39 @@ static int run_measured_process(const char *measured_dir, u32 *monitored_pid)
return -EINVAL; return -EINVAL;
} }
static u64 ima_hash_from_bpf; static int run_measured_process(const char *measured_dir, u32 *monitored_pid)
{
return _run_measured_process(measured_dir, monitored_pid, "run");
}
static u64 ima_hash_from_bpf[MAX_SAMPLES];
static int ima_hash_from_bpf_idx;
static int process_sample(void *ctx, void *data, size_t len) static int process_sample(void *ctx, void *data, size_t len)
{ {
ima_hash_from_bpf = *((u64 *)data); if (ima_hash_from_bpf_idx >= MAX_SAMPLES)
return -ENOSPC;
ima_hash_from_bpf[ima_hash_from_bpf_idx++] = *((u64 *)data);
return 0; return 0;
} }
static void test_init(struct ima__bss *bss)
{
ima_hash_from_bpf_idx = 0;
bss->use_ima_file_hash = false;
bss->enable_bprm_creds_for_exec = false;
bss->enable_kernel_read_file = false;
bss->test_deny = false;
}
void test_test_ima(void) void test_test_ima(void)
{ {
char measured_dir_template[] = "/tmp/ima_measuredXXXXXX"; char measured_dir_template[] = "/tmp/ima_measuredXXXXXX";
struct ring_buffer *ringbuf = NULL; struct ring_buffer *ringbuf = NULL;
const char *measured_dir; const char *measured_dir;
u64 bin_true_sample;
char cmd[256]; char cmd[256];
int err, duration = 0; int err, duration = 0;
...@@ -72,13 +95,127 @@ void test_test_ima(void) ...@@ -72,13 +95,127 @@ void test_test_ima(void)
if (CHECK(err, "failed to run command", "%s, errno = %d\n", cmd, errno)) if (CHECK(err, "failed to run command", "%s, errno = %d\n", cmd, errno))
goto close_clean; goto close_clean;
/*
* Test #1
* - Goal: obtain a sample with the bpf_ima_inode_hash() helper
* - Expected result: 1 sample (/bin/true)
*/
test_init(skel->bss);
err = run_measured_process(measured_dir, &skel->bss->monitored_pid); err = run_measured_process(measured_dir, &skel->bss->monitored_pid);
if (CHECK(err, "run_measured_process", "err = %d\n", err)) if (CHECK(err, "run_measured_process #1", "err = %d\n", err))
goto close_clean; goto close_clean;
err = ring_buffer__consume(ringbuf); err = ring_buffer__consume(ringbuf);
ASSERT_EQ(err, 1, "num_samples_or_err"); ASSERT_EQ(err, 1, "num_samples_or_err");
ASSERT_NEQ(ima_hash_from_bpf, 0, "ima_hash"); ASSERT_NEQ(ima_hash_from_bpf[0], 0, "ima_hash");
/*
* Test #2
* - Goal: obtain samples with the bpf_ima_file_hash() helper
* - Expected result: 2 samples (./ima_setup.sh, /bin/true)
*/
test_init(skel->bss);
skel->bss->use_ima_file_hash = true;
err = run_measured_process(measured_dir, &skel->bss->monitored_pid);
if (CHECK(err, "run_measured_process #2", "err = %d\n", err))
goto close_clean;
err = ring_buffer__consume(ringbuf);
ASSERT_EQ(err, 2, "num_samples_or_err");
ASSERT_NEQ(ima_hash_from_bpf[0], 0, "ima_hash");
ASSERT_NEQ(ima_hash_from_bpf[1], 0, "ima_hash");
bin_true_sample = ima_hash_from_bpf[1];
/*
* Test #3
* - Goal: confirm that bpf_ima_inode_hash() returns a non-fresh digest
* - Expected result: 2 samples (/bin/true: non-fresh, fresh)
*/
test_init(skel->bss);
err = _run_measured_process(measured_dir, &skel->bss->monitored_pid,
"modify-bin");
if (CHECK(err, "modify-bin #3", "err = %d\n", err))
goto close_clean;
skel->bss->enable_bprm_creds_for_exec = true;
err = run_measured_process(measured_dir, &skel->bss->monitored_pid);
if (CHECK(err, "run_measured_process #3", "err = %d\n", err))
goto close_clean;
err = ring_buffer__consume(ringbuf);
ASSERT_EQ(err, 2, "num_samples_or_err");
ASSERT_NEQ(ima_hash_from_bpf[0], 0, "ima_hash");
ASSERT_NEQ(ima_hash_from_bpf[1], 0, "ima_hash");
ASSERT_EQ(ima_hash_from_bpf[0], bin_true_sample, "sample_equal_or_err");
/* IMA refreshed the digest. */
ASSERT_NEQ(ima_hash_from_bpf[1], bin_true_sample,
"sample_different_or_err");
/*
* Test #4
* - Goal: verify that bpf_ima_file_hash() returns a fresh digest
* - Expected result: 4 samples (./ima_setup.sh: fresh, fresh;
* /bin/true: fresh, fresh)
*/
test_init(skel->bss);
skel->bss->use_ima_file_hash = true;
skel->bss->enable_bprm_creds_for_exec = true;
err = run_measured_process(measured_dir, &skel->bss->monitored_pid);
if (CHECK(err, "run_measured_process #4", "err = %d\n", err))
goto close_clean;
err = ring_buffer__consume(ringbuf);
ASSERT_EQ(err, 4, "num_samples_or_err");
ASSERT_NEQ(ima_hash_from_bpf[0], 0, "ima_hash");
ASSERT_NEQ(ima_hash_from_bpf[1], 0, "ima_hash");
ASSERT_NEQ(ima_hash_from_bpf[2], 0, "ima_hash");
ASSERT_NEQ(ima_hash_from_bpf[3], 0, "ima_hash");
ASSERT_NEQ(ima_hash_from_bpf[2], bin_true_sample,
"sample_different_or_err");
ASSERT_EQ(ima_hash_from_bpf[3], ima_hash_from_bpf[2],
"sample_equal_or_err");
skel->bss->use_ima_file_hash = false;
skel->bss->enable_bprm_creds_for_exec = false;
err = _run_measured_process(measured_dir, &skel->bss->monitored_pid,
"restore-bin");
if (CHECK(err, "restore-bin #3", "err = %d\n", err))
goto close_clean;
/*
* Test #5
* - Goal: obtain a sample from the kernel_read_file hook
* - Expected result: 2 samples (./ima_setup.sh, policy_test)
*/
test_init(skel->bss);
skel->bss->use_ima_file_hash = true;
skel->bss->enable_kernel_read_file = true;
err = _run_measured_process(measured_dir, &skel->bss->monitored_pid,
"load-policy");
if (CHECK(err, "run_measured_process #5", "err = %d\n", err))
goto close_clean;
err = ring_buffer__consume(ringbuf);
ASSERT_EQ(err, 2, "num_samples_or_err");
ASSERT_NEQ(ima_hash_from_bpf[0], 0, "ima_hash");
ASSERT_NEQ(ima_hash_from_bpf[1], 0, "ima_hash");
/*
* Test #6
* - Goal: ensure that the kernel_read_file hook denies an operation
* - Expected result: 0 samples
*/
test_init(skel->bss);
skel->bss->enable_kernel_read_file = true;
skel->bss->test_deny = true;
err = _run_measured_process(measured_dir, &skel->bss->monitored_pid,
"load-policy");
if (CHECK(!err, "run_measured_process #6", "err = %d\n", err))
goto close_clean;
err = ring_buffer__consume(ringbuf);
ASSERT_EQ(err, 0, "num_samples_or_err");
close_clean: close_clean:
snprintf(cmd, sizeof(cmd), "./ima_setup.sh cleanup %s", measured_dir); snprintf(cmd, sizeof(cmd), "./ima_setup.sh cleanup %s", measured_dir);
......
...@@ -18,8 +18,12 @@ struct { ...@@ -18,8 +18,12 @@ struct {
char _license[] SEC("license") = "GPL"; char _license[] SEC("license") = "GPL";
SEC("lsm.s/bprm_committed_creds") bool use_ima_file_hash;
void BPF_PROG(ima, struct linux_binprm *bprm) bool enable_bprm_creds_for_exec;
bool enable_kernel_read_file;
bool test_deny;
static void ima_test_common(struct file *file)
{ {
u64 ima_hash = 0; u64 ima_hash = 0;
u64 *sample; u64 *sample;
...@@ -28,8 +32,12 @@ void BPF_PROG(ima, struct linux_binprm *bprm) ...@@ -28,8 +32,12 @@ void BPF_PROG(ima, struct linux_binprm *bprm)
pid = bpf_get_current_pid_tgid() >> 32; pid = bpf_get_current_pid_tgid() >> 32;
if (pid == monitored_pid) { if (pid == monitored_pid) {
ret = bpf_ima_inode_hash(bprm->file->f_inode, &ima_hash, if (!use_ima_file_hash)
sizeof(ima_hash)); ret = bpf_ima_inode_hash(file->f_inode, &ima_hash,
sizeof(ima_hash));
else
ret = bpf_ima_file_hash(file, &ima_hash,
sizeof(ima_hash));
if (ret < 0 || ima_hash == 0) if (ret < 0 || ima_hash == 0)
return; return;
...@@ -43,3 +51,53 @@ void BPF_PROG(ima, struct linux_binprm *bprm) ...@@ -43,3 +51,53 @@ void BPF_PROG(ima, struct linux_binprm *bprm)
return; return;
} }
static int ima_test_deny(void)
{
u32 pid;
pid = bpf_get_current_pid_tgid() >> 32;
if (pid == monitored_pid && test_deny)
return -EPERM;
return 0;
}
SEC("lsm.s/bprm_committed_creds")
void BPF_PROG(bprm_committed_creds, struct linux_binprm *bprm)
{
ima_test_common(bprm->file);
}
SEC("lsm.s/bprm_creds_for_exec")
int BPF_PROG(bprm_creds_for_exec, struct linux_binprm *bprm)
{
if (!enable_bprm_creds_for_exec)
return 0;
ima_test_common(bprm->file);
return 0;
}
SEC("lsm.s/kernel_read_file")
int BPF_PROG(kernel_read_file, struct file *file, enum kernel_read_file_id id,
bool contents)
{
int ret;
if (!enable_kernel_read_file)
return 0;
if (!contents)
return 0;
if (id != READING_POLICY)
return 0;
ret = ima_test_deny();
if (ret < 0)
return ret;
ima_test_common(file);
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment