Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipe: introduce 'boot_verified' as a trust provider

IPE is designed to provide system level trust guarantees, this usually
implies that trust starts from bootup with a hardware root of trust,
which validates the bootloader. After this, the bootloader verifies
the kernel and the initramfs.

As there's no currently supported integrity method for initramfs, and
it's typically already verified by the bootloader. This patch introduces
a new IPE property `boot_verified` which allows author of IPE policy to
indicate trust for files from initramfs.

The implementation of this feature utilizes the newly added
`initramfs_populated` hook. This hook marks the superblock of the rootfs
after the initramfs has been unpacked into it.

Before mounting the real rootfs on top of the initramfs, initramfs
script will recursively remove all files and directories on the
initramfs. This is typically implemented by using switch_root(8)
(https://man7.org/linux/man-pages/man8/switch_root.8.html).
Therefore the initramfs will be empty and not accessible after the real
rootfs takes over. It is advised to switch to a different policy
that doesn't rely on the `boot_verified` property after this point.
This ensures that the trust policies remain relevant and effective
throughout the system's operation.

Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Fan Wu and committed by
Paul Moore
a8a74df1 2fea0c26

+101 -6
+38 -3
security/ipe/eval.c
··· 16 16 17 17 struct ipe_policy __rcu *ipe_active_policy; 18 18 19 + #define FILE_SUPERBLOCK(f) ((f)->f_path.mnt->mnt_sb) 20 + 21 + /** 22 + * build_ipe_sb_ctx() - Build initramfs field of an ipe evaluation context. 23 + * @ctx: Supplies a pointer to the context to be populated. 24 + * @file: Supplies the file struct of the file triggered IPE event. 25 + */ 26 + static void build_ipe_sb_ctx(struct ipe_eval_ctx *ctx, const struct file *const file) 27 + { 28 + ctx->initramfs = ipe_sb(FILE_SUPERBLOCK(file))->initramfs; 29 + } 30 + 19 31 /** 20 32 * ipe_build_eval_ctx() - Build an ipe evaluation context. 21 33 * @ctx: Supplies a pointer to the context to be populated. ··· 40 28 { 41 29 ctx->file = file; 42 30 ctx->op = op; 31 + 32 + if (file) 33 + build_ipe_sb_ctx(ctx, file); 34 + } 35 + 36 + /** 37 + * evaluate_boot_verified() - Evaluate @ctx for the boot verified property. 38 + * @ctx: Supplies a pointer to the context being evaluated. 39 + * 40 + * Return: 41 + * * %true - The current @ctx match the @p 42 + * * %false - The current @ctx doesn't match the @p 43 + */ 44 + static bool evaluate_boot_verified(const struct ipe_eval_ctx *const ctx) 45 + { 46 + return ctx->initramfs; 43 47 } 44 48 45 49 /** ··· 63 35 * @ctx: Supplies a pointer to the context to be evaluated. 64 36 * @p: Supplies a pointer to the property to be evaluated. 65 37 * 66 - * This is a placeholder. The actual function will be introduced in the 67 - * latter commits. 38 + * This function Determines whether the specified @ctx 39 + * matches the conditions defined by a rule property @p. 68 40 * 69 41 * Return: 70 42 * * %true - The current @ctx match the @p ··· 73 45 static bool evaluate_property(const struct ipe_eval_ctx *const ctx, 74 46 struct ipe_prop *p) 75 47 { 76 - return false; 48 + switch (p->type) { 49 + case IPE_PROP_BOOT_VERIFIED_FALSE: 50 + return !evaluate_boot_verified(ctx); 51 + case IPE_PROP_BOOT_VERIFIED_TRUE: 52 + return evaluate_boot_verified(ctx); 53 + default: 54 + return false; 55 + } 77 56 } 78 57 79 58 /**
+5
security/ipe/eval.h
··· 15 15 16 16 extern struct ipe_policy __rcu *ipe_active_policy; 17 17 18 + struct ipe_superblock { 19 + bool initramfs; 20 + }; 21 + 18 22 struct ipe_eval_ctx { 19 23 enum ipe_op_type op; 20 24 21 25 const struct file *file; 26 + bool initramfs; 22 27 }; 23 28 24 29 void ipe_build_eval_ctx(struct ipe_eval_ctx *ctx,
+9
security/ipe/hooks.c
··· 4 4 */ 5 5 6 6 #include <linux/fs.h> 7 + #include <linux/fs_struct.h> 7 8 #include <linux/types.h> 8 9 #include <linux/binfmts.h> 9 10 #include <linux/mman.h> ··· 182 181 183 182 ipe_build_eval_ctx(&ctx, NULL, op); 184 183 return ipe_evaluate_event(&ctx); 184 + } 185 + 186 + /** 187 + * ipe_unpack_initramfs() - Mark the current rootfs as initramfs. 188 + */ 189 + void ipe_unpack_initramfs(void) 190 + { 191 + ipe_sb(current->fs->root.mnt->mnt_sb)->initramfs = true; 185 192 }
+2
security/ipe/hooks.h
··· 22 22 23 23 int ipe_kernel_load_data(enum kernel_load_data_id id, bool contents); 24 24 25 + void ipe_unpack_initramfs(void); 26 + 25 27 #endif /* _IPE_HOOKS_H */
+8
security/ipe/ipe.c
··· 5 5 #include <uapi/linux/lsm.h> 6 6 7 7 #include "ipe.h" 8 + #include "eval.h" 8 9 #include "hooks.h" 9 10 10 11 static struct lsm_blob_sizes ipe_blobs __ro_after_init = { 12 + .lbs_superblock = sizeof(struct ipe_superblock), 11 13 }; 12 14 13 15 static const struct lsm_id ipe_lsmid = { ··· 17 15 .id = LSM_ID_IPE, 18 16 }; 19 17 18 + struct ipe_superblock *ipe_sb(const struct super_block *sb) 19 + { 20 + return sb->s_security + ipe_blobs.lbs_superblock; 21 + } 22 + 20 23 static struct security_hook_list ipe_hooks[] __ro_after_init = { 21 24 LSM_HOOK_INIT(bprm_check_security, ipe_bprm_check_security), 22 25 LSM_HOOK_INIT(mmap_file, ipe_mmap_file), 23 26 LSM_HOOK_INIT(file_mprotect, ipe_file_mprotect), 24 27 LSM_HOOK_INIT(kernel_read_file, ipe_kernel_read_file), 25 28 LSM_HOOK_INIT(kernel_load_data, ipe_kernel_load_data), 29 + LSM_HOOK_INIT(initramfs_populated, ipe_unpack_initramfs), 26 30 }; 27 31 28 32 /**
+1
security/ipe/ipe.h
··· 12 12 #define pr_fmt(fmt) "ipe: " fmt 13 13 14 14 #include <linux/lsm_hooks.h> 15 + struct ipe_superblock *ipe_sb(const struct super_block *sb); 15 16 16 17 #endif /* _IPE_H */
+2
security/ipe/policy.h
··· 30 30 #define IPE_ACTION_INVALID __IPE_ACTION_MAX 31 31 32 32 enum ipe_prop_type { 33 + IPE_PROP_BOOT_VERIFIED_FALSE, 34 + IPE_PROP_BOOT_VERIFIED_TRUE, 33 35 __IPE_PROP_MAX 34 36 }; 35 37
+36 -3
security/ipe/policy_parser.c
··· 270 270 return match_token(t, action_tokens, args); 271 271 } 272 272 273 + static const match_table_t property_tokens = { 274 + {IPE_PROP_BOOT_VERIFIED_FALSE, "boot_verified=FALSE"}, 275 + {IPE_PROP_BOOT_VERIFIED_TRUE, "boot_verified=TRUE"}, 276 + {IPE_PROP_INVALID, NULL} 277 + }; 278 + 273 279 /** 274 280 * parse_property() - Parse a rule property given a token string. 275 281 * @t: Supplies the token string to be parsed. 276 282 * @r: Supplies the ipe_rule the parsed property will be associated with. 277 283 * 278 - * This is a placeholder. The actual function will be introduced in the 279 - * latter commits. 284 + * This function parses and associates a property with an IPE rule based 285 + * on a token string. 280 286 * 281 287 * Return: 282 288 * * %0 - Success ··· 291 285 */ 292 286 static int parse_property(char *t, struct ipe_rule *r) 293 287 { 294 - return -EBADMSG; 288 + substring_t args[MAX_OPT_ARGS]; 289 + struct ipe_prop *p = NULL; 290 + int rc = 0; 291 + int token; 292 + 293 + p = kzalloc(sizeof(*p), GFP_KERNEL); 294 + if (!p) 295 + return -ENOMEM; 296 + 297 + token = match_token(t, property_tokens, args); 298 + 299 + switch (token) { 300 + case IPE_PROP_BOOT_VERIFIED_FALSE: 301 + case IPE_PROP_BOOT_VERIFIED_TRUE: 302 + p->type = token; 303 + break; 304 + default: 305 + rc = -EBADMSG; 306 + break; 307 + } 308 + if (rc) 309 + goto err; 310 + list_add_tail(&p->next, &r->props); 311 + 312 + return rc; 313 + err: 314 + kfree(p); 315 + return rc; 295 316 } 296 317 297 318 /**