diff options
Diffstat (limited to 'security/ipe/eval.c')
-rw-r--r-- | security/ipe/eval.c | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/security/ipe/eval.c b/security/ipe/eval.c new file mode 100644 index 000000000000..21439c5be336 --- /dev/null +++ b/security/ipe/eval.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved. + */ + +#include <linux/fs.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/file.h> +#include <linux/sched.h> +#include <linux/rcupdate.h> +#include <linux/moduleparam.h> +#include <linux/fsverity.h> + +#include "ipe.h" +#include "eval.h" +#include "policy.h" +#include "audit.h" +#include "digest.h" + +struct ipe_policy __rcu *ipe_active_policy; +bool success_audit; +bool enforce = true; +#define INO_BLOCK_DEV(ino) ((ino)->i_sb->s_bdev) + +#define FILE_SUPERBLOCK(f) ((f)->f_path.mnt->mnt_sb) + +/** + * build_ipe_sb_ctx() - Build initramfs field of an ipe evaluation context. + * @ctx: Supplies a pointer to the context to be populated. + * @file: Supplies the file struct of the file triggered IPE event. + */ +static void build_ipe_sb_ctx(struct ipe_eval_ctx *ctx, const struct file *const file) +{ + ctx->initramfs = ipe_sb(FILE_SUPERBLOCK(file))->initramfs; +} + +#ifdef CONFIG_IPE_PROP_DM_VERITY +/** + * build_ipe_bdev_ctx() - Build ipe_bdev field of an evaluation context. + * @ctx: Supplies a pointer to the context to be populated. + * @ino: Supplies the inode struct of the file triggered IPE event. + */ +static void build_ipe_bdev_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino) +{ + if (INO_BLOCK_DEV(ino)) + ctx->ipe_bdev = ipe_bdev(INO_BLOCK_DEV(ino)); +} +#else +static void build_ipe_bdev_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino) +{ +} +#endif /* CONFIG_IPE_PROP_DM_VERITY */ + +#ifdef CONFIG_IPE_PROP_FS_VERITY +#ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG +static void build_ipe_inode_blob_ctx(struct ipe_eval_ctx *ctx, + const struct inode *const ino) +{ + ctx->ipe_inode = ipe_inode(ctx->ino); +} +#else +static inline void build_ipe_inode_blob_ctx(struct ipe_eval_ctx *ctx, + const struct inode *const ino) +{ +} +#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */ + +/** + * build_ipe_inode_ctx() - Build inode fields of an evaluation context. + * @ctx: Supplies a pointer to the context to be populated. + * @ino: Supplies the inode struct of the file triggered IPE event. + */ +static void build_ipe_inode_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino) +{ + ctx->ino = ino; + build_ipe_inode_blob_ctx(ctx, ino); +} +#else +static void build_ipe_inode_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino) +{ +} +#endif /* CONFIG_IPE_PROP_FS_VERITY */ + +/** + * ipe_build_eval_ctx() - Build an ipe evaluation context. + * @ctx: Supplies a pointer to the context to be populated. + * @file: Supplies a pointer to the file to associated with the evaluation. + * @op: Supplies the IPE policy operation associated with the evaluation. + * @hook: Supplies the LSM hook associated with the evaluation. + */ +void ipe_build_eval_ctx(struct ipe_eval_ctx *ctx, + const struct file *file, + enum ipe_op_type op, + enum ipe_hook_type hook) +{ + struct inode *ino; + + ctx->file = file; + ctx->op = op; + ctx->hook = hook; + + if (file) { + build_ipe_sb_ctx(ctx, file); + ino = d_real_inode(file->f_path.dentry); + build_ipe_bdev_ctx(ctx, ino); + build_ipe_inode_ctx(ctx, ino); + } +} + +/** + * evaluate_boot_verified() - Evaluate @ctx for the boot verified property. + * @ctx: Supplies a pointer to the context being evaluated. + * + * Return: + * * %true - The current @ctx match the @p + * * %false - The current @ctx doesn't match the @p + */ +static bool evaluate_boot_verified(const struct ipe_eval_ctx *const ctx) +{ + return ctx->initramfs; +} + +#ifdef CONFIG_IPE_PROP_DM_VERITY +/** + * evaluate_dmv_roothash() - Evaluate @ctx against a dmv roothash property. + * @ctx: Supplies a pointer to the context being evaluated. + * @p: Supplies a pointer to the property being evaluated. + * + * Return: + * * %true - The current @ctx match the @p + * * %false - The current @ctx doesn't match the @p + */ +static bool evaluate_dmv_roothash(const struct ipe_eval_ctx *const ctx, + struct ipe_prop *p) +{ + return !!ctx->ipe_bdev && + !!ctx->ipe_bdev->root_hash && + ipe_digest_eval(p->value, + ctx->ipe_bdev->root_hash); +} +#else +static bool evaluate_dmv_roothash(const struct ipe_eval_ctx *const ctx, + struct ipe_prop *p) +{ + return false; +} +#endif /* CONFIG_IPE_PROP_DM_VERITY */ + +#ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE +/** + * evaluate_dmv_sig_false() - Evaluate @ctx against a dmv sig false property. + * @ctx: Supplies a pointer to the context being evaluated. + * + * Return: + * * %true - The current @ctx match the property + * * %false - The current @ctx doesn't match the property + */ +static bool evaluate_dmv_sig_false(const struct ipe_eval_ctx *const ctx) +{ + return !ctx->ipe_bdev || (!ctx->ipe_bdev->dm_verity_signed); +} + +/** + * evaluate_dmv_sig_true() - Evaluate @ctx against a dmv sig true property. + * @ctx: Supplies a pointer to the context being evaluated. + * + * Return: + * * %true - The current @ctx match the property + * * %false - The current @ctx doesn't match the property + */ +static bool evaluate_dmv_sig_true(const struct ipe_eval_ctx *const ctx) +{ + return !evaluate_dmv_sig_false(ctx); +} +#else +static bool evaluate_dmv_sig_false(const struct ipe_eval_ctx *const ctx) +{ + return false; +} + +static bool evaluate_dmv_sig_true(const struct ipe_eval_ctx *const ctx) +{ + return false; +} +#endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */ + +#ifdef CONFIG_IPE_PROP_FS_VERITY +/** + * evaluate_fsv_digest() - Evaluate @ctx against a fsv digest property. + * @ctx: Supplies a pointer to the context being evaluated. + * @p: Supplies a pointer to the property being evaluated. + * + * Return: + * * %true - The current @ctx match the @p + * * %false - The current @ctx doesn't match the @p + */ +static bool evaluate_fsv_digest(const struct ipe_eval_ctx *const ctx, + struct ipe_prop *p) +{ + enum hash_algo alg; + u8 digest[FS_VERITY_MAX_DIGEST_SIZE]; + struct digest_info info; + + if (!ctx->ino) + return false; + if (!fsverity_get_digest((struct inode *)ctx->ino, + digest, + NULL, + &alg)) + return false; + + info.alg = hash_algo_name[alg]; + info.digest = digest; + info.digest_len = hash_digest_size[alg]; + + return ipe_digest_eval(p->value, &info); +} +#else +static bool evaluate_fsv_digest(const struct ipe_eval_ctx *const ctx, + struct ipe_prop *p) +{ + return false; +} +#endif /* CONFIG_IPE_PROP_FS_VERITY */ + +#ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG +/** + * evaluate_fsv_sig_false() - Evaluate @ctx against a fsv sig false property. + * @ctx: Supplies a pointer to the context being evaluated. + * + * Return: + * * %true - The current @ctx match the property + * * %false - The current @ctx doesn't match the property + */ +static bool evaluate_fsv_sig_false(const struct ipe_eval_ctx *const ctx) +{ + return !ctx->ino || + !IS_VERITY(ctx->ino) || + !ctx->ipe_inode || + !ctx->ipe_inode->fs_verity_signed; +} + +/** + * evaluate_fsv_sig_true() - Evaluate @ctx against a fsv sig true property. + * @ctx: Supplies a pointer to the context being evaluated. + * + * Return: + * * %true - The current @ctx match the property + * * %false - The current @ctx doesn't match the property + */ +static bool evaluate_fsv_sig_true(const struct ipe_eval_ctx *const ctx) +{ + return !evaluate_fsv_sig_false(ctx); +} +#else +static bool evaluate_fsv_sig_false(const struct ipe_eval_ctx *const ctx) +{ + return false; +} + +static bool evaluate_fsv_sig_true(const struct ipe_eval_ctx *const ctx) +{ + return false; +} +#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */ + +/** + * evaluate_property() - Analyze @ctx against a rule property. + * @ctx: Supplies a pointer to the context to be evaluated. + * @p: Supplies a pointer to the property to be evaluated. + * + * This function Determines whether the specified @ctx + * matches the conditions defined by a rule property @p. + * + * Return: + * * %true - The current @ctx match the @p + * * %false - The current @ctx doesn't match the @p + */ +static bool evaluate_property(const struct ipe_eval_ctx *const ctx, + struct ipe_prop *p) +{ + switch (p->type) { + case IPE_PROP_BOOT_VERIFIED_FALSE: + return !evaluate_boot_verified(ctx); + case IPE_PROP_BOOT_VERIFIED_TRUE: + return evaluate_boot_verified(ctx); + case IPE_PROP_DMV_ROOTHASH: + return evaluate_dmv_roothash(ctx, p); + case IPE_PROP_DMV_SIG_FALSE: + return evaluate_dmv_sig_false(ctx); + case IPE_PROP_DMV_SIG_TRUE: + return evaluate_dmv_sig_true(ctx); + case IPE_PROP_FSV_DIGEST: + return evaluate_fsv_digest(ctx, p); + case IPE_PROP_FSV_SIG_FALSE: + return evaluate_fsv_sig_false(ctx); + case IPE_PROP_FSV_SIG_TRUE: + return evaluate_fsv_sig_true(ctx); + default: + return false; + } +} + +/** + * ipe_evaluate_event() - Analyze @ctx against the current active policy. + * @ctx: Supplies a pointer to the context to be evaluated. + * + * This is the loop where all policy evaluations happen against the IPE policy. + * + * Return: + * * %0 - Success + * * %-EACCES - @ctx did not pass evaluation + */ +int ipe_evaluate_event(const struct ipe_eval_ctx *const ctx) +{ + const struct ipe_op_table *rules = NULL; + const struct ipe_rule *rule = NULL; + struct ipe_policy *pol = NULL; + struct ipe_prop *prop = NULL; + enum ipe_action_type action; + enum ipe_match match_type; + bool match = false; + int rc = 0; + + rcu_read_lock(); + + pol = rcu_dereference(ipe_active_policy); + if (!pol) { + rcu_read_unlock(); + return 0; + } + + if (ctx->op == IPE_OP_INVALID) { + if (pol->parsed->global_default_action == IPE_ACTION_INVALID) { + WARN(1, "no default rule set for unknown op, ALLOW it"); + action = IPE_ACTION_ALLOW; + } else { + action = pol->parsed->global_default_action; + } + match_type = IPE_MATCH_GLOBAL; + goto eval; + } + + rules = &pol->parsed->rules[ctx->op]; + + list_for_each_entry(rule, &rules->rules, next) { + match = true; + + list_for_each_entry(prop, &rule->props, next) { + match = evaluate_property(ctx, prop); + if (!match) + break; + } + + if (match) + break; + } + + if (match) { + action = rule->action; + match_type = IPE_MATCH_RULE; + } else if (rules->default_action != IPE_ACTION_INVALID) { + action = rules->default_action; + match_type = IPE_MATCH_TABLE; + } else { + action = pol->parsed->global_default_action; + match_type = IPE_MATCH_GLOBAL; + } + +eval: + ipe_audit_match(ctx, match_type, action, rule); + rcu_read_unlock(); + + if (action == IPE_ACTION_DENY) + rc = -EACCES; + + if (!READ_ONCE(enforce)) + rc = 0; + + return rc; +} + +/* Set the right module name */ +#ifdef KBUILD_MODNAME +#undef KBUILD_MODNAME +#define KBUILD_MODNAME "ipe" +#endif + +module_param(success_audit, bool, 0400); +MODULE_PARM_DESC(success_audit, "Start IPE with success auditing enabled"); +module_param(enforce, bool, 0400); +MODULE_PARM_DESC(enforce, "Start IPE in enforce or permissive mode"); |