linux/security/ipe/eval.c
Fan Wu a8a74df150 ipe: introduce 'boot_verified' as a trust provider
IPE is designed to provide system level trust guarantees, this usually
implies that trust starts from bootup with a hardware root of trust,
which validates the bootloader. After this, the bootloader verifies
the kernel and the initramfs.

As there's no currently supported integrity method for initramfs, and
it's typically already verified by the bootloader. This patch introduces
a new IPE property `boot_verified` which allows author of IPE policy to
indicate trust for files from initramfs.

The implementation of this feature utilizes the newly added
`initramfs_populated` hook. This hook marks the superblock of the rootfs
after the initramfs has been unpacked into it.

Before mounting the real rootfs on top of the initramfs, initramfs
script will recursively remove all files and directories on the
initramfs. This is typically implemented by using switch_root(8)
(https://man7.org/linux/man-pages/man8/switch_root.8.html).
Therefore the initramfs will be empty and not accessible after the real
rootfs takes over. It is advised to switch to a different policy
that doesn't rely on the `boot_verified` property after this point.
This ensures that the trust policies remain relevant and effective
throughout the system's operation.

Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
2024-08-20 14:01:52 -04:00

151 lines
3.7 KiB
C

// 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 "ipe.h"
#include "eval.h"
#include "policy.h"
struct ipe_policy __rcu *ipe_active_policy;
#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;
}
/**
* 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.
*/
void ipe_build_eval_ctx(struct ipe_eval_ctx *ctx,
const struct file *file,
enum ipe_op_type op)
{
ctx->file = file;
ctx->op = op;
if (file)
build_ipe_sb_ctx(ctx, file);
}
/**
* 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;
}
/**
* 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);
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;
bool match = false;
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_DENY) {
rcu_read_unlock();
return -EACCES;
}
if (pol->parsed->global_default_action == IPE_ACTION_INVALID)
WARN(1, "no default rule set for unknown op, ALLOW it");
rcu_read_unlock();
return 0;
}
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;
else if (rules->default_action != IPE_ACTION_INVALID)
action = rules->default_action;
else
action = pol->parsed->global_default_action;
rcu_read_unlock();
if (action == IPE_ACTION_DENY)
return -EACCES;
return 0;
}