1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/slab.h> |
7 | #include <linux/audit.h> |
8 | #include <linux/types.h> |
9 | #include <crypto/hash.h> |
10 | |
11 | #include "ipe.h" |
12 | #include "eval.h" |
13 | #include "hooks.h" |
14 | #include "policy.h" |
15 | #include "audit.h" |
16 | #include "digest.h" |
17 | |
18 | #define ACTSTR(x) ((x) == IPE_ACTION_ALLOW ? "ALLOW" : "DENY") |
19 | |
20 | #define IPE_AUDIT_HASH_ALG "sha256" |
21 | |
22 | #define AUDIT_POLICY_LOAD_FMT "policy_name=\"%s\" policy_version=%hu.%hu.%hu "\ |
23 | "policy_digest=" IPE_AUDIT_HASH_ALG ":" |
24 | #define AUDIT_POLICY_LOAD_NULL_FMT "policy_name=? policy_version=? "\ |
25 | "policy_digest=?" |
26 | #define AUDIT_OLD_ACTIVE_POLICY_FMT "old_active_pol_name=\"%s\" "\ |
27 | "old_active_pol_version=%hu.%hu.%hu "\ |
28 | "old_policy_digest=" IPE_AUDIT_HASH_ALG ":" |
29 | #define AUDIT_OLD_ACTIVE_POLICY_NULL_FMT "old_active_pol_name=? "\ |
30 | "old_active_pol_version=? "\ |
31 | "old_policy_digest=?" |
32 | #define AUDIT_NEW_ACTIVE_POLICY_FMT "new_active_pol_name=\"%s\" "\ |
33 | "new_active_pol_version=%hu.%hu.%hu "\ |
34 | "new_policy_digest=" IPE_AUDIT_HASH_ALG ":" |
35 | |
36 | static const char *const audit_op_names[__IPE_OP_MAX + 1] = { |
37 | "EXECUTE" , |
38 | "FIRMWARE" , |
39 | "KMODULE" , |
40 | "KEXEC_IMAGE" , |
41 | "KEXEC_INITRAMFS" , |
42 | "POLICY" , |
43 | "X509_CERT" , |
44 | "UNKNOWN" , |
45 | }; |
46 | |
47 | static const char *const audit_hook_names[__IPE_HOOK_MAX] = { |
48 | "BPRM_CHECK" , |
49 | "MMAP" , |
50 | "MPROTECT" , |
51 | "KERNEL_READ" , |
52 | "KERNEL_LOAD" , |
53 | }; |
54 | |
55 | static const char *const audit_prop_names[__IPE_PROP_MAX] = { |
56 | "boot_verified=FALSE" , |
57 | "boot_verified=TRUE" , |
58 | "dmverity_roothash=" , |
59 | "dmverity_signature=FALSE" , |
60 | "dmverity_signature=TRUE" , |
61 | "fsverity_digest=" , |
62 | "fsverity_signature=FALSE" , |
63 | "fsverity_signature=TRUE" , |
64 | }; |
65 | |
66 | /** |
67 | * audit_dmv_roothash() - audit the roothash of a dmverity_roothash property. |
68 | * @ab: Supplies a pointer to the audit_buffer to append to. |
69 | * @rh: Supplies a pointer to the digest structure. |
70 | */ |
71 | static void audit_dmv_roothash(struct audit_buffer *ab, const void *rh) |
72 | { |
73 | audit_log_format(ab, fmt: "%s" , audit_prop_names[IPE_PROP_DMV_ROOTHASH]); |
74 | ipe_digest_audit(ab, val: rh); |
75 | } |
76 | |
77 | /** |
78 | * audit_fsv_digest() - audit the digest of a fsverity_digest property. |
79 | * @ab: Supplies a pointer to the audit_buffer to append to. |
80 | * @d: Supplies a pointer to the digest structure. |
81 | */ |
82 | static void audit_fsv_digest(struct audit_buffer *ab, const void *d) |
83 | { |
84 | audit_log_format(ab, fmt: "%s" , audit_prop_names[IPE_PROP_FSV_DIGEST]); |
85 | ipe_digest_audit(ab, val: d); |
86 | } |
87 | |
88 | /** |
89 | * audit_rule() - audit an IPE policy rule. |
90 | * @ab: Supplies a pointer to the audit_buffer to append to. |
91 | * @r: Supplies a pointer to the ipe_rule to approximate a string form for. |
92 | */ |
93 | static void audit_rule(struct audit_buffer *ab, const struct ipe_rule *r) |
94 | { |
95 | const struct ipe_prop *ptr; |
96 | |
97 | audit_log_format(ab, fmt: " rule=\"op=%s " , audit_op_names[r->op]); |
98 | |
99 | list_for_each_entry(ptr, &r->props, next) { |
100 | switch (ptr->type) { |
101 | case IPE_PROP_DMV_ROOTHASH: |
102 | audit_dmv_roothash(ab, rh: ptr->value); |
103 | break; |
104 | case IPE_PROP_FSV_DIGEST: |
105 | audit_fsv_digest(ab, d: ptr->value); |
106 | break; |
107 | default: |
108 | audit_log_format(ab, fmt: "%s" , audit_prop_names[ptr->type]); |
109 | break; |
110 | } |
111 | |
112 | audit_log_format(ab, fmt: " " ); |
113 | } |
114 | |
115 | audit_log_format(ab, fmt: "action=%s\"" , ACTSTR(r->action)); |
116 | } |
117 | |
118 | /** |
119 | * ipe_audit_match() - Audit a rule match in a policy evaluation. |
120 | * @ctx: Supplies a pointer to the evaluation context that was used in the |
121 | * evaluation. |
122 | * @match_type: Supplies the scope of the match: rule, operation default, |
123 | * global default. |
124 | * @act: Supplies the IPE's evaluation decision, deny or allow. |
125 | * @r: Supplies a pointer to the rule that was matched, if possible. |
126 | */ |
127 | void ipe_audit_match(const struct ipe_eval_ctx *const ctx, |
128 | enum ipe_match match_type, |
129 | enum ipe_action_type act, const struct ipe_rule *const r) |
130 | { |
131 | const char *op = audit_op_names[ctx->op]; |
132 | char comm[sizeof(current->comm)]; |
133 | struct audit_buffer *ab; |
134 | struct inode *inode; |
135 | |
136 | if (act != IPE_ACTION_DENY && !READ_ONCE(success_audit)) |
137 | return; |
138 | |
139 | ab = audit_log_start(ctx: audit_context(), GFP_ATOMIC | __GFP_NOWARN, |
140 | AUDIT_IPE_ACCESS); |
141 | if (!ab) |
142 | return; |
143 | |
144 | audit_log_format(ab, fmt: "ipe_op=%s ipe_hook=%s enforcing=%d pid=%d comm=" , |
145 | op, audit_hook_names[ctx->hook], READ_ONCE(enforce), |
146 | task_tgid_nr(current)); |
147 | audit_log_untrustedstring(ab, get_task_comm(comm, current)); |
148 | |
149 | if (ctx->file) { |
150 | audit_log_d_path(ab, prefix: " path=" , path: &ctx->file->f_path); |
151 | inode = file_inode(f: ctx->file); |
152 | if (inode) { |
153 | audit_log_format(ab, fmt: " dev=" ); |
154 | audit_log_untrustedstring(ab, string: inode->i_sb->s_id); |
155 | audit_log_format(ab, fmt: " ino=%lu" , inode->i_ino); |
156 | } else { |
157 | audit_log_format(ab, fmt: " dev=? ino=?" ); |
158 | } |
159 | } else { |
160 | audit_log_format(ab, fmt: " path=? dev=? ino=?" ); |
161 | } |
162 | |
163 | if (match_type == IPE_MATCH_RULE) |
164 | audit_rule(ab, r); |
165 | else if (match_type == IPE_MATCH_TABLE) |
166 | audit_log_format(ab, fmt: " rule=\"DEFAULT op=%s action=%s\"" , op, |
167 | ACTSTR(act)); |
168 | else |
169 | audit_log_format(ab, fmt: " rule=\"DEFAULT action=%s\"" , |
170 | ACTSTR(act)); |
171 | |
172 | audit_log_end(ab); |
173 | } |
174 | |
175 | /** |
176 | * audit_policy() - Audit a policy's name, version and thumbprint to @ab. |
177 | * @ab: Supplies a pointer to the audit buffer to append to. |
178 | * @audit_format: Supplies a pointer to the audit format string |
179 | * @p: Supplies a pointer to the policy to audit. |
180 | */ |
181 | static void audit_policy(struct audit_buffer *ab, |
182 | const char *audit_format, |
183 | const struct ipe_policy *const p) |
184 | { |
185 | SHASH_DESC_ON_STACK(desc, tfm); |
186 | struct crypto_shash *tfm; |
187 | u8 *digest = NULL; |
188 | |
189 | tfm = crypto_alloc_shash(IPE_AUDIT_HASH_ALG, type: 0, mask: 0); |
190 | if (IS_ERR(ptr: tfm)) |
191 | return; |
192 | |
193 | desc->tfm = tfm; |
194 | |
195 | digest = kzalloc(crypto_shash_digestsize(tfm), GFP_KERNEL); |
196 | if (!digest) |
197 | goto out; |
198 | |
199 | if (crypto_shash_init(desc)) |
200 | goto out; |
201 | |
202 | if (crypto_shash_update(desc, data: p->pkcs7, len: p->pkcs7len)) |
203 | goto out; |
204 | |
205 | if (crypto_shash_final(desc, out: digest)) |
206 | goto out; |
207 | |
208 | audit_log_format(ab, fmt: audit_format, p->parsed->name, |
209 | p->parsed->version.major, p->parsed->version.minor, |
210 | p->parsed->version.rev); |
211 | audit_log_n_hex(ab, buf: digest, len: crypto_shash_digestsize(tfm)); |
212 | |
213 | out: |
214 | kfree(objp: digest); |
215 | crypto_free_shash(tfm); |
216 | } |
217 | |
218 | /** |
219 | * ipe_audit_policy_activation() - Audit a policy being activated. |
220 | * @op: Supplies a pointer to the previously activated policy to audit. |
221 | * @np: Supplies a pointer to the newly activated policy to audit. |
222 | */ |
223 | void ipe_audit_policy_activation(const struct ipe_policy *const op, |
224 | const struct ipe_policy *const np) |
225 | { |
226 | struct audit_buffer *ab; |
227 | |
228 | ab = audit_log_start(ctx: audit_context(), GFP_KERNEL, |
229 | AUDIT_IPE_CONFIG_CHANGE); |
230 | if (!ab) |
231 | return; |
232 | |
233 | if (op) { |
234 | audit_policy(ab, AUDIT_OLD_ACTIVE_POLICY_FMT, p: op); |
235 | audit_log_format(ab, fmt: " " ); |
236 | } else { |
237 | /* |
238 | * old active policy can be NULL if there is no kernel |
239 | * built-in policy |
240 | */ |
241 | audit_log_format(ab, AUDIT_OLD_ACTIVE_POLICY_NULL_FMT); |
242 | audit_log_format(ab, fmt: " " ); |
243 | } |
244 | audit_policy(ab, AUDIT_NEW_ACTIVE_POLICY_FMT, p: np); |
245 | audit_log_format(ab, fmt: " auid=%u ses=%u lsm=ipe res=1" , |
246 | from_kuid(to: &init_user_ns, uid: audit_get_loginuid(current)), |
247 | audit_get_sessionid(current)); |
248 | |
249 | audit_log_end(ab); |
250 | } |
251 | |
252 | /** |
253 | * ipe_audit_policy_load() - Audit a policy loading event. |
254 | * @p: Supplies a pointer to the policy to audit or an error pointer. |
255 | */ |
256 | void ipe_audit_policy_load(const struct ipe_policy *const p) |
257 | { |
258 | struct audit_buffer *ab; |
259 | int err = 0; |
260 | |
261 | ab = audit_log_start(ctx: audit_context(), GFP_KERNEL, |
262 | AUDIT_IPE_POLICY_LOAD); |
263 | if (!ab) |
264 | return; |
265 | |
266 | if (!IS_ERR(ptr: p)) { |
267 | audit_policy(ab, AUDIT_POLICY_LOAD_FMT, p); |
268 | } else { |
269 | audit_log_format(ab, AUDIT_POLICY_LOAD_NULL_FMT); |
270 | err = PTR_ERR(ptr: p); |
271 | } |
272 | |
273 | audit_log_format(ab, fmt: " auid=%u ses=%u lsm=ipe res=%d errno=%d" , |
274 | from_kuid(to: &init_user_ns, uid: audit_get_loginuid(current)), |
275 | audit_get_sessionid(current), !err, err); |
276 | |
277 | audit_log_end(ab); |
278 | } |
279 | |
280 | /** |
281 | * ipe_audit_enforce() - Audit a change in IPE's enforcement state. |
282 | * @new_enforce: The new value enforce to be set. |
283 | * @old_enforce: The old value currently in enforce. |
284 | */ |
285 | void ipe_audit_enforce(bool new_enforce, bool old_enforce) |
286 | { |
287 | struct audit_buffer *ab; |
288 | |
289 | ab = audit_log_start(ctx: audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS); |
290 | if (!ab) |
291 | return; |
292 | |
293 | audit_log(ctx: audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS, |
294 | fmt: "enforcing=%d old_enforcing=%d auid=%u ses=%u" |
295 | " enabled=1 old-enabled=1 lsm=ipe res=1" , |
296 | new_enforce, old_enforce, |
297 | from_kuid(to: &init_user_ns, uid: audit_get_loginuid(current)), |
298 | audit_get_sessionid(current)); |
299 | |
300 | audit_log_end(ab); |
301 | } |
302 | |