mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00

Users of IPE require a way to identify when and why an operation fails, allowing them to both respond to violations of policy and be notified of potentially malicious actions on their systems with respect to IPE. This patch introduces a new error field to the AUDIT_IPE_POLICY_LOAD event to log policy loading failures. Currently, IPE only logs successful policy loads, but not failures. Tracking failures is crucial to detect malicious attempts and ensure a complete audit trail for security events. The new error field will capture the following error codes: * -ENOKEY: Key used to sign the IPE policy not found in the keyring * -ESTALE: Attempting to update an IPE policy with an older version * -EKEYREJECTED: IPE signature verification failed * -ENOENT: Policy was deleted while updating * -EEXIST: Same name policy already deployed * -ERANGE: Policy version number overflow * -EINVAL: Policy version parsing error * -EPERM: Insufficient permission * -ENOMEM: Out of memory (OOM) * -EBADMSG: Policy is invalid Here are some examples of the updated audit record types: AUDIT_IPE_POLICY_LOAD(1422): audit: AUDIT1422 policy_name="Test_Policy" policy_version=0.0.1 policy_digest=sha256:84EFBA8FA71E62AE0A537FAB962F8A2BD1053964C4299DCA 92BFFF4DB82E86D3 auid=1000 ses=3 lsm=ipe res=1 errno=0 The above record shows a new policy has been successfully loaded into the kernel with the policy name, version, and hash with the errno=0. AUDIT_IPE_POLICY_LOAD(1422) with error: audit: AUDIT1422 policy_name=? policy_version=? policy_digest=? auid=1000 ses=3 lsm=ipe res=0 errno=-74 The above record shows a policy load failure due to an invalid policy (-EBADMSG). By adding this error field, we ensure that all policy load attempts, whether successful or failed, are logged, providing a comprehensive audit trail for IPE policy management. Signed-off-by: Jasjiv Singh <jasjivsingh@linux.microsoft.com> Signed-off-by: Fan Wu <wufan@kernel.org>
302 lines
8.1 KiB
C
302 lines
8.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/audit.h>
|
|
#include <linux/types.h>
|
|
#include <crypto/hash.h>
|
|
|
|
#include "ipe.h"
|
|
#include "eval.h"
|
|
#include "hooks.h"
|
|
#include "policy.h"
|
|
#include "audit.h"
|
|
#include "digest.h"
|
|
|
|
#define ACTSTR(x) ((x) == IPE_ACTION_ALLOW ? "ALLOW" : "DENY")
|
|
|
|
#define IPE_AUDIT_HASH_ALG "sha256"
|
|
|
|
#define AUDIT_POLICY_LOAD_FMT "policy_name=\"%s\" policy_version=%hu.%hu.%hu "\
|
|
"policy_digest=" IPE_AUDIT_HASH_ALG ":"
|
|
#define AUDIT_POLICY_LOAD_NULL_FMT "policy_name=? policy_version=? "\
|
|
"policy_digest=?"
|
|
#define AUDIT_OLD_ACTIVE_POLICY_FMT "old_active_pol_name=\"%s\" "\
|
|
"old_active_pol_version=%hu.%hu.%hu "\
|
|
"old_policy_digest=" IPE_AUDIT_HASH_ALG ":"
|
|
#define AUDIT_OLD_ACTIVE_POLICY_NULL_FMT "old_active_pol_name=? "\
|
|
"old_active_pol_version=? "\
|
|
"old_policy_digest=?"
|
|
#define AUDIT_NEW_ACTIVE_POLICY_FMT "new_active_pol_name=\"%s\" "\
|
|
"new_active_pol_version=%hu.%hu.%hu "\
|
|
"new_policy_digest=" IPE_AUDIT_HASH_ALG ":"
|
|
|
|
static const char *const audit_op_names[__IPE_OP_MAX + 1] = {
|
|
"EXECUTE",
|
|
"FIRMWARE",
|
|
"KMODULE",
|
|
"KEXEC_IMAGE",
|
|
"KEXEC_INITRAMFS",
|
|
"POLICY",
|
|
"X509_CERT",
|
|
"UNKNOWN",
|
|
};
|
|
|
|
static const char *const audit_hook_names[__IPE_HOOK_MAX] = {
|
|
"BPRM_CHECK",
|
|
"MMAP",
|
|
"MPROTECT",
|
|
"KERNEL_READ",
|
|
"KERNEL_LOAD",
|
|
};
|
|
|
|
static const char *const audit_prop_names[__IPE_PROP_MAX] = {
|
|
"boot_verified=FALSE",
|
|
"boot_verified=TRUE",
|
|
"dmverity_roothash=",
|
|
"dmverity_signature=FALSE",
|
|
"dmverity_signature=TRUE",
|
|
"fsverity_digest=",
|
|
"fsverity_signature=FALSE",
|
|
"fsverity_signature=TRUE",
|
|
};
|
|
|
|
/**
|
|
* audit_dmv_roothash() - audit the roothash of a dmverity_roothash property.
|
|
* @ab: Supplies a pointer to the audit_buffer to append to.
|
|
* @rh: Supplies a pointer to the digest structure.
|
|
*/
|
|
static void audit_dmv_roothash(struct audit_buffer *ab, const void *rh)
|
|
{
|
|
audit_log_format(ab, "%s", audit_prop_names[IPE_PROP_DMV_ROOTHASH]);
|
|
ipe_digest_audit(ab, rh);
|
|
}
|
|
|
|
/**
|
|
* audit_fsv_digest() - audit the digest of a fsverity_digest property.
|
|
* @ab: Supplies a pointer to the audit_buffer to append to.
|
|
* @d: Supplies a pointer to the digest structure.
|
|
*/
|
|
static void audit_fsv_digest(struct audit_buffer *ab, const void *d)
|
|
{
|
|
audit_log_format(ab, "%s", audit_prop_names[IPE_PROP_FSV_DIGEST]);
|
|
ipe_digest_audit(ab, d);
|
|
}
|
|
|
|
/**
|
|
* audit_rule() - audit an IPE policy rule.
|
|
* @ab: Supplies a pointer to the audit_buffer to append to.
|
|
* @r: Supplies a pointer to the ipe_rule to approximate a string form for.
|
|
*/
|
|
static void audit_rule(struct audit_buffer *ab, const struct ipe_rule *r)
|
|
{
|
|
const struct ipe_prop *ptr;
|
|
|
|
audit_log_format(ab, " rule=\"op=%s ", audit_op_names[r->op]);
|
|
|
|
list_for_each_entry(ptr, &r->props, next) {
|
|
switch (ptr->type) {
|
|
case IPE_PROP_DMV_ROOTHASH:
|
|
audit_dmv_roothash(ab, ptr->value);
|
|
break;
|
|
case IPE_PROP_FSV_DIGEST:
|
|
audit_fsv_digest(ab, ptr->value);
|
|
break;
|
|
default:
|
|
audit_log_format(ab, "%s", audit_prop_names[ptr->type]);
|
|
break;
|
|
}
|
|
|
|
audit_log_format(ab, " ");
|
|
}
|
|
|
|
audit_log_format(ab, "action=%s\"", ACTSTR(r->action));
|
|
}
|
|
|
|
/**
|
|
* ipe_audit_match() - Audit a rule match in a policy evaluation.
|
|
* @ctx: Supplies a pointer to the evaluation context that was used in the
|
|
* evaluation.
|
|
* @match_type: Supplies the scope of the match: rule, operation default,
|
|
* global default.
|
|
* @act: Supplies the IPE's evaluation decision, deny or allow.
|
|
* @r: Supplies a pointer to the rule that was matched, if possible.
|
|
*/
|
|
void ipe_audit_match(const struct ipe_eval_ctx *const ctx,
|
|
enum ipe_match match_type,
|
|
enum ipe_action_type act, const struct ipe_rule *const r)
|
|
{
|
|
const char *op = audit_op_names[ctx->op];
|
|
char comm[sizeof(current->comm)];
|
|
struct audit_buffer *ab;
|
|
struct inode *inode;
|
|
|
|
if (act != IPE_ACTION_DENY && !READ_ONCE(success_audit))
|
|
return;
|
|
|
|
ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
|
|
AUDIT_IPE_ACCESS);
|
|
if (!ab)
|
|
return;
|
|
|
|
audit_log_format(ab, "ipe_op=%s ipe_hook=%s enforcing=%d pid=%d comm=",
|
|
op, audit_hook_names[ctx->hook], READ_ONCE(enforce),
|
|
task_tgid_nr(current));
|
|
audit_log_untrustedstring(ab, get_task_comm(comm, current));
|
|
|
|
if (ctx->file) {
|
|
audit_log_d_path(ab, " path=", &ctx->file->f_path);
|
|
inode = file_inode(ctx->file);
|
|
if (inode) {
|
|
audit_log_format(ab, " dev=");
|
|
audit_log_untrustedstring(ab, inode->i_sb->s_id);
|
|
audit_log_format(ab, " ino=%lu", inode->i_ino);
|
|
} else {
|
|
audit_log_format(ab, " dev=? ino=?");
|
|
}
|
|
} else {
|
|
audit_log_format(ab, " path=? dev=? ino=?");
|
|
}
|
|
|
|
if (match_type == IPE_MATCH_RULE)
|
|
audit_rule(ab, r);
|
|
else if (match_type == IPE_MATCH_TABLE)
|
|
audit_log_format(ab, " rule=\"DEFAULT op=%s action=%s\"", op,
|
|
ACTSTR(act));
|
|
else
|
|
audit_log_format(ab, " rule=\"DEFAULT action=%s\"",
|
|
ACTSTR(act));
|
|
|
|
audit_log_end(ab);
|
|
}
|
|
|
|
/**
|
|
* audit_policy() - Audit a policy's name, version and thumbprint to @ab.
|
|
* @ab: Supplies a pointer to the audit buffer to append to.
|
|
* @audit_format: Supplies a pointer to the audit format string
|
|
* @p: Supplies a pointer to the policy to audit.
|
|
*/
|
|
static void audit_policy(struct audit_buffer *ab,
|
|
const char *audit_format,
|
|
const struct ipe_policy *const p)
|
|
{
|
|
SHASH_DESC_ON_STACK(desc, tfm);
|
|
struct crypto_shash *tfm;
|
|
u8 *digest = NULL;
|
|
|
|
tfm = crypto_alloc_shash(IPE_AUDIT_HASH_ALG, 0, 0);
|
|
if (IS_ERR(tfm))
|
|
return;
|
|
|
|
desc->tfm = tfm;
|
|
|
|
digest = kzalloc(crypto_shash_digestsize(tfm), GFP_KERNEL);
|
|
if (!digest)
|
|
goto out;
|
|
|
|
if (crypto_shash_init(desc))
|
|
goto out;
|
|
|
|
if (crypto_shash_update(desc, p->pkcs7, p->pkcs7len))
|
|
goto out;
|
|
|
|
if (crypto_shash_final(desc, digest))
|
|
goto out;
|
|
|
|
audit_log_format(ab, audit_format, p->parsed->name,
|
|
p->parsed->version.major, p->parsed->version.minor,
|
|
p->parsed->version.rev);
|
|
audit_log_n_hex(ab, digest, crypto_shash_digestsize(tfm));
|
|
|
|
out:
|
|
kfree(digest);
|
|
crypto_free_shash(tfm);
|
|
}
|
|
|
|
/**
|
|
* ipe_audit_policy_activation() - Audit a policy being activated.
|
|
* @op: Supplies a pointer to the previously activated policy to audit.
|
|
* @np: Supplies a pointer to the newly activated policy to audit.
|
|
*/
|
|
void ipe_audit_policy_activation(const struct ipe_policy *const op,
|
|
const struct ipe_policy *const np)
|
|
{
|
|
struct audit_buffer *ab;
|
|
|
|
ab = audit_log_start(audit_context(), GFP_KERNEL,
|
|
AUDIT_IPE_CONFIG_CHANGE);
|
|
if (!ab)
|
|
return;
|
|
|
|
if (op) {
|
|
audit_policy(ab, AUDIT_OLD_ACTIVE_POLICY_FMT, op);
|
|
audit_log_format(ab, " ");
|
|
} else {
|
|
/*
|
|
* old active policy can be NULL if there is no kernel
|
|
* built-in policy
|
|
*/
|
|
audit_log_format(ab, AUDIT_OLD_ACTIVE_POLICY_NULL_FMT);
|
|
audit_log_format(ab, " ");
|
|
}
|
|
audit_policy(ab, AUDIT_NEW_ACTIVE_POLICY_FMT, np);
|
|
audit_log_format(ab, " auid=%u ses=%u lsm=ipe res=1",
|
|
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
|
audit_get_sessionid(current));
|
|
|
|
audit_log_end(ab);
|
|
}
|
|
|
|
/**
|
|
* ipe_audit_policy_load() - Audit a policy loading event.
|
|
* @p: Supplies a pointer to the policy to audit or an error pointer.
|
|
*/
|
|
void ipe_audit_policy_load(const struct ipe_policy *const p)
|
|
{
|
|
struct audit_buffer *ab;
|
|
int err = 0;
|
|
|
|
ab = audit_log_start(audit_context(), GFP_KERNEL,
|
|
AUDIT_IPE_POLICY_LOAD);
|
|
if (!ab)
|
|
return;
|
|
|
|
if (!IS_ERR(p)) {
|
|
audit_policy(ab, AUDIT_POLICY_LOAD_FMT, p);
|
|
} else {
|
|
audit_log_format(ab, AUDIT_POLICY_LOAD_NULL_FMT);
|
|
err = PTR_ERR(p);
|
|
}
|
|
|
|
audit_log_format(ab, " auid=%u ses=%u lsm=ipe res=%d errno=%d",
|
|
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
|
audit_get_sessionid(current), !err, err);
|
|
|
|
audit_log_end(ab);
|
|
}
|
|
|
|
/**
|
|
* ipe_audit_enforce() - Audit a change in IPE's enforcement state.
|
|
* @new_enforce: The new value enforce to be set.
|
|
* @old_enforce: The old value currently in enforce.
|
|
*/
|
|
void ipe_audit_enforce(bool new_enforce, bool old_enforce)
|
|
{
|
|
struct audit_buffer *ab;
|
|
|
|
ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS);
|
|
if (!ab)
|
|
return;
|
|
|
|
audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS,
|
|
"enforcing=%d old_enforcing=%d auid=%u ses=%u"
|
|
" enabled=1 old-enabled=1 lsm=ipe res=1",
|
|
new_enforce, old_enforce,
|
|
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
|
audit_get_sessionid(current));
|
|
|
|
audit_log_end(ab);
|
|
}
|