// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved. */ #include #include #include #include #include #include #include #include #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");