292 lines
7.9 KiB
C
292 lines
7.9 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_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 being loaded into the kernel.
|
|
* @p: Supplies a pointer to the policy to audit.
|
|
*/
|
|
void ipe_audit_policy_load(const struct ipe_policy *const p)
|
|
{
|
|
struct audit_buffer *ab;
|
|
|
|
ab = audit_log_start(audit_context(), GFP_KERNEL,
|
|
AUDIT_IPE_POLICY_LOAD);
|
|
if (!ab)
|
|
return;
|
|
|
|
audit_policy(ab, AUDIT_POLICY_LOAD_FMT, p);
|
|
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_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);
|
|
}
|