@@ -143,6 +143,9 @@
#define AUDIT_MAC_UNLBL_STCDEL 1417 /* NetLabel: del a static label */
#define AUDIT_MAC_CALIPSO_ADD 1418 /* NetLabel: add CALIPSO DOI entry */
#define AUDIT_MAC_CALIPSO_DEL 1419 /* NetLabel: del CALIPSO DOI entry */
+#define AUDIT_IPE_ACCESS 1420 /* IPE denial or grant */
+#define AUDIT_IPE_CONFIG_CHANGE 1421 /* IPE config change */
+#define AUDIT_IPE_POLICY_LOAD 1422 /* IPE policy load */
#define AUDIT_FIRST_KERN_ANOM_MSG 1700
#define AUDIT_LAST_KERN_ANOM_MSG 1799
@@ -5,7 +5,7 @@
menuconfig SECURITY_IPE
bool "Integrity Policy Enforcement (IPE)"
- depends on SECURITY && SECURITYFS
+ depends on SECURITY && SECURITYFS && AUDIT && AUDITSYSCALL
select PKCS7_MESSAGE_PARSER
select SYSTEM_DATA_VERIFICATION
help
@@ -13,3 +13,4 @@ obj-$(CONFIG_SECURITY_IPE) += \
policy.o \
policy_fs.o \
policy_parser.o \
+ audit.o \
new file mode 100644
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 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"
+
+#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_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] = {
+ "EXECUTE",
+ "FIRMWARE",
+ "KMODULE",
+ "KEXEC_IMAGE",
+ "KEXEC_INITRAMFS",
+ "IMA_POLICY",
+ "IMA_X509_CERT",
+};
+
+static const char *const audit_prop_names[__IPE_PROP_MAX] = {
+ "boot_verified=FALSE",
+ "boot_verified=TRUE",
+};
+
+/**
+ * audit_rule - audit an IPE policy rule approximation.
+ * @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)
+ audit_log_format(ab, "%s ", audit_prop_names[ptr->type]);
+
+ audit_log_format(ab, "action=%s\"", ACTSTR(r->action));
+}
+
+/**
+ * ipe_audit_match - audit a match for IPE policy.
+ * @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.
+ * @enforce: Supplies the enforcement/permissive state at the point
+ * the enforcement decision was made.
+ */
+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)
+{
+ struct inode *inode;
+ struct audit_buffer *ab;
+ const char *op = audit_op_names[ctx->op];
+
+ if (act != IPE_ACTION_DENY && !READ_ONCE(success_audit))
+ return;
+
+ ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_IPE_ACCESS);
+ if (!ab)
+ return;
+
+ 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);
+ }
+ }
+
+ 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.
+ * @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)
+{
+ u8 *digest = NULL;
+ struct crypto_shash *tfm;
+ SHASH_DESC_ON_STACK(desc, tfm);
+
+ 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 made the active policy.
+ * @p: Supplies a pointer to the 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;
+
+ audit_policy(ab, AUDIT_OLD_ACTIVE_POLICY_FMT, op);
+ 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);
+}
new file mode 100644
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#ifndef _IPE_AUDIT_H
+#define _IPE_AUDIT_H
+
+#include "policy.h"
+
+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);
+void ipe_audit_policy_load(const struct ipe_policy *const p);
+void ipe_audit_policy_activation(const struct ipe_policy *const op,
+ const struct ipe_policy *const np);
+
+#endif /* _IPE_AUDIT_H */
@@ -10,12 +10,15 @@
#include <linux/sched.h>
#include <linux/rcupdate.h>
#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
#include "ipe.h"
#include "eval.h"
#include "policy.h"
+#include "audit.h"
struct ipe_policy __rcu *ipe_active_policy;
+bool success_audit;
static const struct super_block *pinned_sb;
static DEFINE_SPINLOCK(pin_lock);
@@ -112,6 +115,7 @@ int ipe_evaluate_event(const struct ipe_eval_ctx *const ctx)
{
bool match = false;
enum ipe_action_type action;
+ enum ipe_match match_type;
struct ipe_policy *pol = NULL;
const struct ipe_rule *rule = NULL;
const struct ipe_op_table *rules = NULL;
@@ -127,9 +131,9 @@ int ipe_evaluate_event(const struct ipe_eval_ctx *const ctx)
if (ctx->op == IPE_OP_INVALID) {
rcu_read_unlock();
- if (pol->parsed->global_default_action == IPE_ACTION_DENY)
- return -EACCES;
- return 0;
+ action = pol->parsed->global_default_action;
+ match_type = IPE_MATCH_GLOBAL;
+ goto eval;
}
rules = &pol->parsed->rules[ctx->op];
@@ -147,14 +151,21 @@ int ipe_evaluate_event(const struct ipe_eval_ctx *const ctx)
break;
}
- if (match)
+ if (match) {
action = rule->action;
- else if (rules->default_action != IPE_ACTION_INVALID)
+ match_type = IPE_MATCH_RULE;
+ } else if (rules->default_action != IPE_ACTION_INVALID) {
action = rules->default_action;
- else
+ match_type = IPE_MATCH_TABLE;
+ } else {
action = pol->parsed->global_default_action;
+ match_type = IPE_MATCH_GLOBAL;
+ }
rcu_read_unlock();
+eval:
+ ipe_audit_match(ctx, match_type, action, rule);
+
if (action == IPE_ACTION_DENY)
return -EACCES;
@@ -178,3 +189,12 @@ void ipe_invalidate_pinned_sb(const struct super_block *mnt_sb)
spin_unlock(&pin_lock);
}
+
+/* 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");
@@ -14,6 +14,7 @@
#define IPE_EVAL_CTX_INIT ((struct ipe_eval_ctx){ 0 })
extern struct ipe_policy __rcu *ipe_active_policy;
+extern bool success_audit;
struct ipe_eval_ctx {
enum ipe_op_type op;
@@ -22,6 +23,13 @@ struct ipe_eval_ctx {
bool from_init_sb;
};
+enum ipe_match {
+ IPE_MATCH_RULE = 0,
+ IPE_MATCH_TABLE,
+ IPE_MATCH_GLOBAL,
+ __IPE_MATCH_MAX
+};
+
void build_eval_ctx(struct ipe_eval_ctx *ctx, const struct file *file, enum ipe_op_type op);
int ipe_evaluate_event(const struct ipe_eval_ctx *const ctx);
void ipe_invalidate_pinned_sb(const struct super_block *mnt_sb);
@@ -8,11 +8,64 @@
#include "ipe.h"
#include "fs.h"
+#include "eval.h"
#include "policy.h"
+#include "audit.h"
static struct dentry *np __ro_after_init;
static struct dentry *root __ro_after_init;
struct dentry *policy_root __ro_after_init;
+static struct dentry *audit_node __ro_after_init;
+
+/**
+ * setaudit - Write handler for the securityfs node, "ipe/success_audit"
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the write syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * Return:
+ * * >0 - Success, Length of buffer written
+ * * <0 - Error
+ */
+static ssize_t setaudit(struct file *f, const char __user *data,
+ size_t len, loff_t *offset)
+{
+ int rc = 0;
+ bool value;
+
+ if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+ return -EPERM;
+
+ rc = kstrtobool_from_user(data, len, &value);
+ if (rc)
+ return rc;
+
+ WRITE_ONCE(success_audit, value);
+
+ return len;
+}
+
+/**
+ * getaudit - Read handler for the securityfs node, "ipe/success_audit"
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the read syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * Return:
+ * * >0 - Success, Length of buffer written
+ * * <0 - Error
+ */
+static ssize_t getaudit(struct file *f, char __user *data,
+ size_t len, loff_t *offset)
+{
+ const char *result;
+
+ result = ((READ_ONCE(success_audit)) ? "1" : "0");
+
+ return simple_read_from_buffer(data, len, offset, result, 1);
+}
/**
* new_policy - Write handler for the securityfs node, "ipe/new_policy".
@@ -46,6 +99,10 @@ static ssize_t new_policy(struct file *f, const char __user *data,
}
rc = ipe_new_policyfs_node(p);
+ if (rc)
+ goto out;
+
+ ipe_audit_policy_load(p);
out:
if (rc < 0 && !IS_ERR(p))
@@ -58,6 +115,11 @@ static const struct file_operations np_fops = {
.write = new_policy,
};
+static const struct file_operations audit_fops = {
+ .write = setaudit,
+ .read = getaudit,
+};
+
/**
* ipe_init_securityfs - Initialize IPE's securityfs tree at fsinit.
*
@@ -78,6 +140,13 @@ static int __init ipe_init_securityfs(void)
goto err;
}
+ audit_node = securityfs_create_file("success_audit", 0600, root,
+ NULL, &audit_fops);
+ if (IS_ERR(audit_node)) {
+ rc = PTR_ERR(audit_node);
+ goto err;
+ }
+
policy_root = securityfs_create_dir("policies", root);
if (IS_ERR(policy_root)) {
rc = PTR_ERR(policy_root);
@@ -94,6 +163,7 @@ static int __init ipe_init_securityfs(void)
err:
securityfs_remove(np);
securityfs_remove(policy_root);
+ securityfs_remove(audit_node);
securityfs_remove(root);
return rc;
}
@@ -11,6 +11,7 @@
#include "fs.h"
#include "policy.h"
#include "policy_parser.h"
+#include "audit.h"
/* lock for synchronizing writers across ipe policy */
DEFINE_MUTEX(ipe_policy_lock);
@@ -124,6 +125,8 @@ struct ipe_policy *ipe_update_policy(struct inode *root,
swap(new->policyfs, old->policyfs);
+ ipe_audit_policy_load(new);
+
return old;
err:
ipe_free_policy(new);
@@ -209,5 +212,7 @@ int ipe_set_active_pol(const struct ipe_policy *p)
rcu_assign_pointer(ipe_active_policy, p);
+ ipe_audit_policy_activation(ap, p);
+
return 0;
}