summaryrefslogtreecommitdiffstats
path: root/security/ipe/audit.c
blob: 5a859f88cfb4648a9ed6d126e6c3aad8f3bcdda3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
// 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"

#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",
};

/**
 * 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)
		audit_log_format(ab, "%s ", audit_prop_names[ptr->type]);

	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 pid=%d comm=",
			 op, audit_hook_names[ctx->hook],
			 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);
}