1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
4 */
5
6 #include <linux/fs.h>
7 #include <linux/types.h>
8 #include <linux/slab.h>
9 #include <linux/file.h>
10 #include <linux/sched.h>
11 #include <linux/rcupdate.h>
12 #include <linux/moduleparam.h>
13 #include <linux/fsverity.h>
14
15 #include "ipe.h"
16 #include "eval.h"
17 #include "policy.h"
18 #include "audit.h"
19 #include "digest.h"
20
21 struct ipe_policy __rcu *ipe_active_policy;
22 bool success_audit;
23 bool enforce = true;
24 #define INO_BLOCK_DEV(ino) ((ino)->i_sb->s_bdev)
25
26 #define FILE_SUPERBLOCK(f) ((f)->f_path.mnt->mnt_sb)
27
28 /**
29 * build_ipe_sb_ctx() - Build initramfs field of an ipe evaluation context.
30 * @ctx: Supplies a pointer to the context to be populated.
31 * @file: Supplies the file struct of the file triggered IPE event.
32 */
build_ipe_sb_ctx(struct ipe_eval_ctx * ctx,const struct file * const file)33 static void build_ipe_sb_ctx(struct ipe_eval_ctx *ctx, const struct file *const file)
34 {
35 ctx->initramfs = ipe_sb(FILE_SUPERBLOCK(file))->initramfs;
36 }
37
38 #ifdef CONFIG_IPE_PROP_DM_VERITY
39 /**
40 * build_ipe_bdev_ctx() - Build ipe_bdev field of an evaluation context.
41 * @ctx: Supplies a pointer to the context to be populated.
42 * @ino: Supplies the inode struct of the file triggered IPE event.
43 */
build_ipe_bdev_ctx(struct ipe_eval_ctx * ctx,const struct inode * const ino)44 static void build_ipe_bdev_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino)
45 {
46 if (INO_BLOCK_DEV(ino))
47 ctx->ipe_bdev = ipe_bdev(INO_BLOCK_DEV(ino));
48 }
49 #else
build_ipe_bdev_ctx(struct ipe_eval_ctx * ctx,const struct inode * const ino)50 static void build_ipe_bdev_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino)
51 {
52 }
53 #endif /* CONFIG_IPE_PROP_DM_VERITY */
54
55 #ifdef CONFIG_IPE_PROP_FS_VERITY
56 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
build_ipe_inode_blob_ctx(struct ipe_eval_ctx * ctx,const struct inode * const ino)57 static void build_ipe_inode_blob_ctx(struct ipe_eval_ctx *ctx,
58 const struct inode *const ino)
59 {
60 ctx->ipe_inode = ipe_inode(ctx->ino);
61 }
62 #else
build_ipe_inode_blob_ctx(struct ipe_eval_ctx * ctx,const struct inode * const ino)63 static inline void build_ipe_inode_blob_ctx(struct ipe_eval_ctx *ctx,
64 const struct inode *const ino)
65 {
66 }
67 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
68
69 /**
70 * build_ipe_inode_ctx() - Build inode fields of an evaluation context.
71 * @ctx: Supplies a pointer to the context to be populated.
72 * @ino: Supplies the inode struct of the file triggered IPE event.
73 */
build_ipe_inode_ctx(struct ipe_eval_ctx * ctx,const struct inode * const ino)74 static void build_ipe_inode_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino)
75 {
76 ctx->ino = ino;
77 build_ipe_inode_blob_ctx(ctx, ino);
78 }
79 #else
build_ipe_inode_ctx(struct ipe_eval_ctx * ctx,const struct inode * const ino)80 static void build_ipe_inode_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino)
81 {
82 }
83 #endif /* CONFIG_IPE_PROP_FS_VERITY */
84
85 /**
86 * ipe_build_eval_ctx() - Build an ipe evaluation context.
87 * @ctx: Supplies a pointer to the context to be populated.
88 * @file: Supplies a pointer to the file to associated with the evaluation.
89 * @op: Supplies the IPE policy operation associated with the evaluation.
90 * @hook: Supplies the LSM hook associated with the evaluation.
91 */
ipe_build_eval_ctx(struct ipe_eval_ctx * ctx,const struct file * file,enum ipe_op_type op,enum ipe_hook_type hook)92 void ipe_build_eval_ctx(struct ipe_eval_ctx *ctx,
93 const struct file *file,
94 enum ipe_op_type op,
95 enum ipe_hook_type hook)
96 {
97 struct inode *ino;
98
99 ctx->file = file;
100 ctx->op = op;
101 ctx->hook = hook;
102
103 if (file) {
104 build_ipe_sb_ctx(ctx, file);
105 ino = d_real_inode(file->f_path.dentry);
106 build_ipe_bdev_ctx(ctx, ino);
107 build_ipe_inode_ctx(ctx, ino);
108 }
109 }
110
111 /**
112 * evaluate_boot_verified() - Evaluate @ctx for the boot verified property.
113 * @ctx: Supplies a pointer to the context being evaluated.
114 *
115 * Return:
116 * * %true - The current @ctx match the @p
117 * * %false - The current @ctx doesn't match the @p
118 */
evaluate_boot_verified(const struct ipe_eval_ctx * const ctx)119 static bool evaluate_boot_verified(const struct ipe_eval_ctx *const ctx)
120 {
121 return ctx->initramfs;
122 }
123
124 #ifdef CONFIG_IPE_PROP_DM_VERITY
125 /**
126 * evaluate_dmv_roothash() - Evaluate @ctx against a dmv roothash property.
127 * @ctx: Supplies a pointer to the context being evaluated.
128 * @p: Supplies a pointer to the property being evaluated.
129 *
130 * Return:
131 * * %true - The current @ctx match the @p
132 * * %false - The current @ctx doesn't match the @p
133 */
evaluate_dmv_roothash(const struct ipe_eval_ctx * const ctx,struct ipe_prop * p)134 static bool evaluate_dmv_roothash(const struct ipe_eval_ctx *const ctx,
135 struct ipe_prop *p)
136 {
137 const struct digest_info *root_hash;
138
139 if (!ctx->ipe_bdev)
140 return false;
141
142 root_hash = rcu_dereference(ctx->ipe_bdev->root_hash);
143
144 return root_hash && ipe_digest_eval(p->value, root_hash);
145 }
146 #else
evaluate_dmv_roothash(const struct ipe_eval_ctx * const ctx,struct ipe_prop * p)147 static bool evaluate_dmv_roothash(const struct ipe_eval_ctx *const ctx,
148 struct ipe_prop *p)
149 {
150 return false;
151 }
152 #endif /* CONFIG_IPE_PROP_DM_VERITY */
153
154 #ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE
155 /**
156 * evaluate_dmv_sig_false() - Evaluate @ctx against a dmv sig false property.
157 * @ctx: Supplies a pointer to the context being evaluated.
158 *
159 * Return:
160 * * %true - The current @ctx match the property
161 * * %false - The current @ctx doesn't match the property
162 */
evaluate_dmv_sig_false(const struct ipe_eval_ctx * const ctx)163 static bool evaluate_dmv_sig_false(const struct ipe_eval_ctx *const ctx)
164 {
165 return !ctx->ipe_bdev || (!ctx->ipe_bdev->dm_verity_signed);
166 }
167
168 /**
169 * evaluate_dmv_sig_true() - Evaluate @ctx against a dmv sig true property.
170 * @ctx: Supplies a pointer to the context being evaluated.
171 *
172 * Return:
173 * * %true - The current @ctx match the property
174 * * %false - The current @ctx doesn't match the property
175 */
evaluate_dmv_sig_true(const struct ipe_eval_ctx * const ctx)176 static bool evaluate_dmv_sig_true(const struct ipe_eval_ctx *const ctx)
177 {
178 return !evaluate_dmv_sig_false(ctx);
179 }
180 #else
evaluate_dmv_sig_false(const struct ipe_eval_ctx * const ctx)181 static bool evaluate_dmv_sig_false(const struct ipe_eval_ctx *const ctx)
182 {
183 return false;
184 }
185
evaluate_dmv_sig_true(const struct ipe_eval_ctx * const ctx)186 static bool evaluate_dmv_sig_true(const struct ipe_eval_ctx *const ctx)
187 {
188 return false;
189 }
190 #endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */
191
192 #ifdef CONFIG_IPE_PROP_FS_VERITY
193 /**
194 * evaluate_fsv_digest() - Evaluate @ctx against a fsv digest property.
195 * @ctx: Supplies a pointer to the context being evaluated.
196 * @p: Supplies a pointer to the property being evaluated.
197 *
198 * Return:
199 * * %true - The current @ctx match the @p
200 * * %false - The current @ctx doesn't match the @p
201 */
evaluate_fsv_digest(const struct ipe_eval_ctx * const ctx,struct ipe_prop * p)202 static bool evaluate_fsv_digest(const struct ipe_eval_ctx *const ctx,
203 struct ipe_prop *p)
204 {
205 enum hash_algo alg;
206 u8 digest[FS_VERITY_MAX_DIGEST_SIZE];
207 struct digest_info info;
208
209 if (!ctx->ino)
210 return false;
211 if (!fsverity_get_digest((struct inode *)ctx->ino,
212 digest,
213 NULL,
214 &alg))
215 return false;
216
217 info.alg = hash_algo_name[alg];
218 info.digest = digest;
219 info.digest_len = hash_digest_size[alg];
220
221 return ipe_digest_eval(p->value, &info);
222 }
223 #else
evaluate_fsv_digest(const struct ipe_eval_ctx * const ctx,struct ipe_prop * p)224 static bool evaluate_fsv_digest(const struct ipe_eval_ctx *const ctx,
225 struct ipe_prop *p)
226 {
227 return false;
228 }
229 #endif /* CONFIG_IPE_PROP_FS_VERITY */
230
231 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
232 /**
233 * evaluate_fsv_sig_false() - Evaluate @ctx against a fsv sig false property.
234 * @ctx: Supplies a pointer to the context being evaluated.
235 *
236 * Return:
237 * * %true - The current @ctx match the property
238 * * %false - The current @ctx doesn't match the property
239 */
evaluate_fsv_sig_false(const struct ipe_eval_ctx * const ctx)240 static bool evaluate_fsv_sig_false(const struct ipe_eval_ctx *const ctx)
241 {
242 return !ctx->ino ||
243 !IS_VERITY(ctx->ino) ||
244 !ctx->ipe_inode ||
245 !ctx->ipe_inode->fs_verity_signed;
246 }
247
248 /**
249 * evaluate_fsv_sig_true() - Evaluate @ctx against a fsv sig true property.
250 * @ctx: Supplies a pointer to the context being evaluated.
251 *
252 * Return:
253 * * %true - The current @ctx match the property
254 * * %false - The current @ctx doesn't match the property
255 */
evaluate_fsv_sig_true(const struct ipe_eval_ctx * const ctx)256 static bool evaluate_fsv_sig_true(const struct ipe_eval_ctx *const ctx)
257 {
258 return !evaluate_fsv_sig_false(ctx);
259 }
260 #else
evaluate_fsv_sig_false(const struct ipe_eval_ctx * const ctx)261 static bool evaluate_fsv_sig_false(const struct ipe_eval_ctx *const ctx)
262 {
263 return false;
264 }
265
evaluate_fsv_sig_true(const struct ipe_eval_ctx * const ctx)266 static bool evaluate_fsv_sig_true(const struct ipe_eval_ctx *const ctx)
267 {
268 return false;
269 }
270 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
271
272 /**
273 * evaluate_property() - Analyze @ctx against a rule property.
274 * @ctx: Supplies a pointer to the context to be evaluated.
275 * @p: Supplies a pointer to the property to be evaluated.
276 *
277 * This function Determines whether the specified @ctx
278 * matches the conditions defined by a rule property @p.
279 *
280 * Return:
281 * * %true - The current @ctx match the @p
282 * * %false - The current @ctx doesn't match the @p
283 */
evaluate_property(const struct ipe_eval_ctx * const ctx,struct ipe_prop * p)284 static bool evaluate_property(const struct ipe_eval_ctx *const ctx,
285 struct ipe_prop *p)
286 {
287 switch (p->type) {
288 case IPE_PROP_BOOT_VERIFIED_FALSE:
289 return !evaluate_boot_verified(ctx);
290 case IPE_PROP_BOOT_VERIFIED_TRUE:
291 return evaluate_boot_verified(ctx);
292 case IPE_PROP_DMV_ROOTHASH:
293 return evaluate_dmv_roothash(ctx, p);
294 case IPE_PROP_DMV_SIG_FALSE:
295 return evaluate_dmv_sig_false(ctx);
296 case IPE_PROP_DMV_SIG_TRUE:
297 return evaluate_dmv_sig_true(ctx);
298 case IPE_PROP_FSV_DIGEST:
299 return evaluate_fsv_digest(ctx, p);
300 case IPE_PROP_FSV_SIG_FALSE:
301 return evaluate_fsv_sig_false(ctx);
302 case IPE_PROP_FSV_SIG_TRUE:
303 return evaluate_fsv_sig_true(ctx);
304 default:
305 return false;
306 }
307 }
308
309 /**
310 * ipe_evaluate_event() - Analyze @ctx against the current active policy.
311 * @ctx: Supplies a pointer to the context to be evaluated.
312 *
313 * This is the loop where all policy evaluations happen against the IPE policy.
314 *
315 * Return:
316 * * %0 - Success
317 * * %-EACCES - @ctx did not pass evaluation
318 */
ipe_evaluate_event(const struct ipe_eval_ctx * const ctx)319 int ipe_evaluate_event(const struct ipe_eval_ctx *const ctx)
320 {
321 const struct ipe_op_table *rules = NULL;
322 const struct ipe_rule *rule = NULL;
323 struct ipe_policy *pol = NULL;
324 struct ipe_prop *prop = NULL;
325 enum ipe_action_type action;
326 enum ipe_match match_type;
327 bool match = false;
328 int rc = 0;
329
330 rcu_read_lock();
331
332 pol = rcu_dereference(ipe_active_policy);
333 if (!pol) {
334 rcu_read_unlock();
335 return 0;
336 }
337
338 if (ctx->op == IPE_OP_INVALID) {
339 if (pol->parsed->global_default_action == IPE_ACTION_INVALID) {
340 WARN(1, "no default rule set for unknown op, ALLOW it");
341 action = IPE_ACTION_ALLOW;
342 } else {
343 action = pol->parsed->global_default_action;
344 }
345 match_type = IPE_MATCH_GLOBAL;
346 goto eval;
347 }
348
349 rules = &pol->parsed->rules[ctx->op];
350
351 list_for_each_entry(rule, &rules->rules, next) {
352 match = true;
353
354 list_for_each_entry(prop, &rule->props, next) {
355 match = evaluate_property(ctx, prop);
356 if (!match)
357 break;
358 }
359
360 if (match)
361 break;
362 }
363
364 if (match) {
365 action = rule->action;
366 match_type = IPE_MATCH_RULE;
367 } else if (rules->default_action != IPE_ACTION_INVALID) {
368 action = rules->default_action;
369 match_type = IPE_MATCH_TABLE;
370 } else {
371 action = pol->parsed->global_default_action;
372 match_type = IPE_MATCH_GLOBAL;
373 }
374
375 eval:
376 ipe_audit_match(ctx, match_type, action, rule);
377 rcu_read_unlock();
378
379 if (action == IPE_ACTION_DENY)
380 rc = -EACCES;
381
382 if (!READ_ONCE(enforce))
383 rc = 0;
384
385 return rc;
386 }
387
388 /* Set the right module name */
389 #ifdef KBUILD_MODNAME
390 #undef KBUILD_MODNAME
391 #define KBUILD_MODNAME "ipe"
392 #endif
393
394 module_param(success_audit, bool, 0400);
395 MODULE_PARM_DESC(success_audit, "Start IPE with success auditing enabled");
396 module_param(enforce, bool, 0400);
397 MODULE_PARM_DESC(enforce, "Start IPE in enforce or permissive mode");
398