Message ID | 1716583609-21790-14-git-send-email-wufan@linux.microsoft.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Integrity Policy Enforcement LSM (IPE) | expand |
On May 24, 2024 Fan Wu <wufan@linux.microsoft.com> wrote: > > Allows author of IPE policy to indicate trust for a singular dm-verity > volume, identified by roothash, through "dmverity_roothash" and all > signed and validated dm-verity volumes, through "dmverity_signature". > > Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com> > Signed-off-by: Fan Wu <wufan@linux.microsoft.com> > --- > v2: > + No Changes > > v3: > + No changes > > v4: > + No changes > > v5: > + No changes > > v6: > + Fix an improper cleanup that can result in > a leak > > v7: > + Squash patch 08/12, 10/12 to [11/16] > > v8: > + Undo squash of 08/12, 10/12 - separating drivers/md/ from security/ > & block/ > + Use common-audit function for dmverity_signature. > + Change implementation for storing the dm-verity digest to use the > newly introduced dm_verity_digest structure introduced in patch > 14/20. > > v9: > + Adapt to the new parser > > v10: > + Select the Kconfig when all dependencies are enabled > > v11: > + No changes > > v12: > + Refactor to use struct digest_info* instead of void* > + Correct audit format > > v13: > + Remove the CONFIG_IPE_PROP_DM_VERITY dependency inside the parser > to make the policy grammar independent of the kernel config. > > v14: > + No changes > > v15: > + Fix one grammar issue in KCONFIG > + Switch to use security_bdev_setintegrity() hook > > v16: > + Refactor for enum integrity type > > v17: > + Add years to license header > + Fix code and documentation style issues > + Return -EINVAL in ipe_bdev_setintegrity when passed type is not > supported > + Use new enum name LSM_INT_DMVERITY_SIG_VALID > > v18: > + Add Kconfig IPE_PROP_DM_VERITY_SIGNATURE and make both DM_VERITY > config auto-selected > > v19: > + No changes > --- > security/ipe/Kconfig | 27 ++++++++ > security/ipe/Makefile | 1 + > security/ipe/audit.c | 29 ++++++++- > security/ipe/digest.c | 118 +++++++++++++++++++++++++++++++++++ > security/ipe/digest.h | 26 ++++++++ > security/ipe/eval.c | 93 ++++++++++++++++++++++++++- > security/ipe/eval.h | 12 ++++ > security/ipe/hooks.c | 93 +++++++++++++++++++++++++++ > security/ipe/hooks.h | 8 +++ > security/ipe/ipe.c | 15 +++++ > security/ipe/ipe.h | 4 ++ > security/ipe/policy.h | 3 + > security/ipe/policy_parser.c | 24 ++++++- > 13 files changed, 449 insertions(+), 4 deletions(-) > create mode 100644 security/ipe/digest.c > create mode 100644 security/ipe/digest.h ... > diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c > index b68719bf44fb..51f1e63c295c 100644 > --- a/security/ipe/hooks.c > +++ b/security/ipe/hooks.c > @@ -191,3 +193,94 @@ void ipe_unpack_initramfs(void) > { > ipe_sb(current->fs->root.mnt->mnt_sb)->initramfs = true; > } > + > +#ifdef CONFIG_IPE_PROP_DM_VERITY > +/** > + * ipe_bdev_free_security() - Free IPE's LSM blob of block_devices. > + * @bdev: Supplies a pointer to a block_device that contains the structure > + * to free. > + */ > +void ipe_bdev_free_security(struct block_device *bdev) > +{ > + struct ipe_bdev *blob = ipe_bdev(bdev); > + > + ipe_digest_free(blob->root_hash); > +} > + > +#ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE > +static void ipe_set_dmverity_signature(struct ipe_bdev *blob, > + const void *value, > + size_t size) > +{ > + blob->dm_verity_signed = size > 0 && value; > +} > +#else > +static inline void ipe_set_dmverity_signature(struct ipe_bdev *blob, > + const void *value, > + size_t size) > +{ > +} > +#endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */ > + > +/** > + * ipe_bdev_setintegrity() - Save integrity data from a bdev to IPE's LSM blob. > + * @bdev: Supplies a pointer to a block_device that contains the LSM blob. > + * @type: Supplies the integrity type. > + * @value: Supplies the value to store. > + * @size: The size of @value. > + * > + * This hook is currently used to save dm-verity's root hash or the existence > + * of a validated signed dm-verity root hash into LSM blob. > + * > + * Return: %0 on success. If an error occurs, the function will return the > + * -errno. > + */ > +int ipe_bdev_setintegrity(struct block_device *bdev, enum lsm_integrity_type type, > + const void *value, size_t size) > +{ > + const struct dm_verity_digest *digest = NULL; > + struct ipe_bdev *blob = ipe_bdev(bdev); > + struct digest_info *info = NULL; > + > + if (type == LSM_INT_DMVERITY_ROOTHASH) { > + if (!value) { > + ipe_digest_free(blob->root_hash); > + blob->root_hash = NULL; > + > + return 0; > + } > + digest = value; > + > + info = kzalloc(sizeof(*info), GFP_KERNEL); > + if (!info) > + return -ENOMEM; > + > + info->digest = kmemdup(digest->digest, digest->digest_len, > + GFP_KERNEL); > + if (!info->digest) > + goto dmv_roothash_err; > + > + info->alg = kstrdup(digest->alg, GFP_KERNEL); > + if (!info->alg) > + goto dmv_roothash_err; > + > + info->digest_len = digest->digest_len; > + > + if (blob->root_hash) > + ipe_digest_free(blob->root_hash); The above if/free looks like a new addition from v18 and I'm not quite sure why the `blob->root_hash` NULL check is necessary as ipe_digest_free() does a IS_ERR_OR_NULL() check right at the top. Likely harmless and doubtful to have any noticable performance impact, but I wanted to mention it just in case ... > + blob->root_hash = info; > + > + return 0; > +dmv_roothash_err: > + ipe_digest_free(info); > + > + return -ENOMEM; > + } else if (type == LSM_INT_DMVERITY_SIG_VALID) { > + ipe_set_dmverity_signature(blob, value, size); > + > + return 0; > + } > + > + return -EINVAL; > +} > +#endif /* CONFIG_IPE_PROP_DM_VERITY */ -- paul-moore.com
On 5/29/2024 6:44 PM, Paul Moore wrote: > On May 24, 2024 Fan Wu <wufan@linux.microsoft.com> wrote: >> >> Allows author of IPE policy to indicate trust for a singular dm-verity >> volume, identified by roothash, through "dmverity_roothash" and all >> signed and validated dm-verity volumes, through "dmverity_signature". >> >> Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com> >> Signed-off-by: Fan Wu <wufan@linux.microsoft.com> >> --- >> v2: >> + No Changes >> >> v3: >> + No changes >> >> v4: >> + No changes >> >> v5: >> + No changes >> >> v6: >> + Fix an improper cleanup that can result in >> a leak >> >> v7: >> + Squash patch 08/12, 10/12 to [11/16] >> >> v8: >> + Undo squash of 08/12, 10/12 - separating drivers/md/ from security/ >> & block/ >> + Use common-audit function for dmverity_signature. >> + Change implementation for storing the dm-verity digest to use the >> newly introduced dm_verity_digest structure introduced in patch >> 14/20. >> >> v9: >> + Adapt to the new parser >> >> v10: >> + Select the Kconfig when all dependencies are enabled >> >> v11: >> + No changes >> >> v12: >> + Refactor to use struct digest_info* instead of void* >> + Correct audit format >> >> v13: >> + Remove the CONFIG_IPE_PROP_DM_VERITY dependency inside the parser >> to make the policy grammar independent of the kernel config. >> >> v14: >> + No changes >> >> v15: >> + Fix one grammar issue in KCONFIG >> + Switch to use security_bdev_setintegrity() hook >> >> v16: >> + Refactor for enum integrity type >> >> v17: >> + Add years to license header >> + Fix code and documentation style issues >> + Return -EINVAL in ipe_bdev_setintegrity when passed type is not >> supported >> + Use new enum name LSM_INT_DMVERITY_SIG_VALID >> >> v18: >> + Add Kconfig IPE_PROP_DM_VERITY_SIGNATURE and make both DM_VERITY >> config auto-selected >> >> v19: >> + No changes >> --- >> security/ipe/Kconfig | 27 ++++++++ >> security/ipe/Makefile | 1 + >> security/ipe/audit.c | 29 ++++++++- >> security/ipe/digest.c | 118 +++++++++++++++++++++++++++++++++++ >> security/ipe/digest.h | 26 ++++++++ >> security/ipe/eval.c | 93 ++++++++++++++++++++++++++- >> security/ipe/eval.h | 12 ++++ >> security/ipe/hooks.c | 93 +++++++++++++++++++++++++++ >> security/ipe/hooks.h | 8 +++ >> security/ipe/ipe.c | 15 +++++ >> security/ipe/ipe.h | 4 ++ >> security/ipe/policy.h | 3 + >> security/ipe/policy_parser.c | 24 ++++++- >> 13 files changed, 449 insertions(+), 4 deletions(-) >> create mode 100644 security/ipe/digest.c >> create mode 100644 security/ipe/digest.h > > ... > >> diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c >> index b68719bf44fb..51f1e63c295c 100644 >> --- a/security/ipe/hooks.c >> +++ b/security/ipe/hooks.c >> @@ -191,3 +193,94 @@ void ipe_unpack_initramfs(void) >> { >> ipe_sb(current->fs->root.mnt->mnt_sb)->initramfs = true; >> } >> + >> +#ifdef CONFIG_IPE_PROP_DM_VERITY >> +/** >> + * ipe_bdev_free_security() - Free IPE's LSM blob of block_devices. >> + * @bdev: Supplies a pointer to a block_device that contains the structure >> + * to free. >> + */ >> +void ipe_bdev_free_security(struct block_device *bdev) >> +{ >> + struct ipe_bdev *blob = ipe_bdev(bdev); >> + >> + ipe_digest_free(blob->root_hash); >> +} >> + >> +#ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE >> +static void ipe_set_dmverity_signature(struct ipe_bdev *blob, >> + const void *value, >> + size_t size) >> +{ >> + blob->dm_verity_signed = size > 0 && value; >> +} >> +#else >> +static inline void ipe_set_dmverity_signature(struct ipe_bdev *blob, >> + const void *value, >> + size_t size) >> +{ >> +} >> +#endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */ >> + >> +/** >> + * ipe_bdev_setintegrity() - Save integrity data from a bdev to IPE's LSM blob. >> + * @bdev: Supplies a pointer to a block_device that contains the LSM blob. >> + * @type: Supplies the integrity type. >> + * @value: Supplies the value to store. >> + * @size: The size of @value. >> + * >> + * This hook is currently used to save dm-verity's root hash or the existence >> + * of a validated signed dm-verity root hash into LSM blob. >> + * >> + * Return: %0 on success. If an error occurs, the function will return the >> + * -errno. >> + */ >> +int ipe_bdev_setintegrity(struct block_device *bdev, enum lsm_integrity_type type, >> + const void *value, size_t size) >> +{ >> + const struct dm_verity_digest *digest = NULL; >> + struct ipe_bdev *blob = ipe_bdev(bdev); >> + struct digest_info *info = NULL; >> + >> + if (type == LSM_INT_DMVERITY_ROOTHASH) { >> + if (!value) { >> + ipe_digest_free(blob->root_hash); >> + blob->root_hash = NULL; >> + >> + return 0; >> + } >> + digest = value; >> + >> + info = kzalloc(sizeof(*info), GFP_KERNEL); >> + if (!info) >> + return -ENOMEM; >> + >> + info->digest = kmemdup(digest->digest, digest->digest_len, >> + GFP_KERNEL); >> + if (!info->digest) >> + goto dmv_roothash_err; >> + >> + info->alg = kstrdup(digest->alg, GFP_KERNEL); >> + if (!info->alg) >> + goto dmv_roothash_err; >> + >> + info->digest_len = digest->digest_len; >> + >> + if (blob->root_hash) >> + ipe_digest_free(blob->root_hash); > > The above if/free looks like a new addition from v18 and I'm not quite > sure why the `blob->root_hash` NULL check is necessary as > ipe_digest_free() does a IS_ERR_OR_NULL() check right at the top. > > Likely harmless and doubtful to have any noticable performance impact, > but I wanted to mention it just in case ... > Yes directly call ipe_digest_free() should be enough. Also this new free is introduced because the mapped device with an existing dm-verity target can be suspended and associated with a new dm-verity target. In this case, the root hash associated with the security blob will be stale and needs to be freed before setting the new data. -Fan >> + blob->root_hash = info; >> + >> + return 0; >> +dmv_roothash_err: >> + ipe_digest_free(info); >> + >> + return -ENOMEM; >> + } else if (type == LSM_INT_DMVERITY_SIG_VALID) { >> + ipe_set_dmverity_signature(blob, value, size); >> + >> + return 0; >> + } >> + >> + return -EINVAL; >> +} >> +#endif /* CONFIG_IPE_PROP_DM_VERITY */ > > -- > paul-moore.com
On Thu May 30, 2024 at 4:44 AM EEST, Paul Moore wrote: > On May 24, 2024 Fan Wu <wufan@linux.microsoft.com> wrote: > > + if (type == LSM_INT_DMVERITY_ROOTHASH) { > > + if (!value) { > > + ipe_digest_free(blob->root_hash); > > + blob->root_hash = NULL; > > + > > + return 0; > > + } > > + digest = value; > > + > > + info = kzalloc(sizeof(*info), GFP_KERNEL); > > + if (!info) > > + return -ENOMEM; > > + > > + info->digest = kmemdup(digest->digest, digest->digest_len, > > + GFP_KERNEL); > > + if (!info->digest) > > + goto dmv_roothash_err; > > + > > + info->alg = kstrdup(digest->alg, GFP_KERNEL); > > + if (!info->alg) > > + goto dmv_roothash_err; > > + > > + info->digest_len = digest->digest_len; > > + > > + if (blob->root_hash) > > + ipe_digest_free(blob->root_hash); I'd restructure this as: if (type == LSM_INT_DMVERITY_SIG_VALID { /* ... */ return 0; } if (type != LSM_INT_DMVERITY_ROOTHASH) return -EINVAL; * ... */ Right? Less packed to the right ;-) BR, Jarkko
On Thu May 30, 2024 at 6:58 AM EEST, Fan Wu wrote: > > > On 5/29/2024 6:44 PM, Paul Moore wrote: > > On May 24, 2024 Fan Wu <wufan@linux.microsoft.com> wrote: > >> > >> Allows author of IPE policy to indicate trust for a singular dm-verity > >> volume, identified by roothash, through "dmverity_roothash" and all > >> signed and validated dm-verity volumes, through "dmverity_signature". > >> > >> Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com> > >> Signed-off-by: Fan Wu <wufan@linux.microsoft.com> > >> --- > >> v2: > >> + No Changes > >> > >> v3: > >> + No changes > >> > >> v4: > >> + No changes > >> > >> v5: > >> + No changes > >> > >> v6: > >> + Fix an improper cleanup that can result in > >> a leak > >> > >> v7: > >> + Squash patch 08/12, 10/12 to [11/16] > >> > >> v8: > >> + Undo squash of 08/12, 10/12 - separating drivers/md/ from security/ > >> & block/ > >> + Use common-audit function for dmverity_signature. > >> + Change implementation for storing the dm-verity digest to use the > >> newly introduced dm_verity_digest structure introduced in patch > >> 14/20. > >> > >> v9: > >> + Adapt to the new parser > >> > >> v10: > >> + Select the Kconfig when all dependencies are enabled > >> > >> v11: > >> + No changes > >> > >> v12: > >> + Refactor to use struct digest_info* instead of void* > >> + Correct audit format > >> > >> v13: > >> + Remove the CONFIG_IPE_PROP_DM_VERITY dependency inside the parser > >> to make the policy grammar independent of the kernel config. > >> > >> v14: > >> + No changes > >> > >> v15: > >> + Fix one grammar issue in KCONFIG > >> + Switch to use security_bdev_setintegrity() hook > >> > >> v16: > >> + Refactor for enum integrity type > >> > >> v17: > >> + Add years to license header > >> + Fix code and documentation style issues > >> + Return -EINVAL in ipe_bdev_setintegrity when passed type is not > >> supported > >> + Use new enum name LSM_INT_DMVERITY_SIG_VALID > >> > >> v18: > >> + Add Kconfig IPE_PROP_DM_VERITY_SIGNATURE and make both DM_VERITY > >> config auto-selected > >> > >> v19: > >> + No changes > >> --- > >> security/ipe/Kconfig | 27 ++++++++ > >> security/ipe/Makefile | 1 + > >> security/ipe/audit.c | 29 ++++++++- > >> security/ipe/digest.c | 118 +++++++++++++++++++++++++++++++++++ > >> security/ipe/digest.h | 26 ++++++++ > >> security/ipe/eval.c | 93 ++++++++++++++++++++++++++- > >> security/ipe/eval.h | 12 ++++ > >> security/ipe/hooks.c | 93 +++++++++++++++++++++++++++ > >> security/ipe/hooks.h | 8 +++ > >> security/ipe/ipe.c | 15 +++++ > >> security/ipe/ipe.h | 4 ++ > >> security/ipe/policy.h | 3 + > >> security/ipe/policy_parser.c | 24 ++++++- > >> 13 files changed, 449 insertions(+), 4 deletions(-) > >> create mode 100644 security/ipe/digest.c > >> create mode 100644 security/ipe/digest.h > > > > ... > > > >> diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c > >> index b68719bf44fb..51f1e63c295c 100644 > >> --- a/security/ipe/hooks.c > >> +++ b/security/ipe/hooks.c > >> @@ -191,3 +193,94 @@ void ipe_unpack_initramfs(void) > >> { > >> ipe_sb(current->fs->root.mnt->mnt_sb)->initramfs = true; > >> } > >> + > >> +#ifdef CONFIG_IPE_PROP_DM_VERITY > >> +/** > >> + * ipe_bdev_free_security() - Free IPE's LSM blob of block_devices. > >> + * @bdev: Supplies a pointer to a block_device that contains the structure > >> + * to free. > >> + */ > >> +void ipe_bdev_free_security(struct block_device *bdev) > >> +{ > >> + struct ipe_bdev *blob = ipe_bdev(bdev); > >> + > >> + ipe_digest_free(blob->root_hash); > >> +} > >> + > >> +#ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE > >> +static void ipe_set_dmverity_signature(struct ipe_bdev *blob, > >> + const void *value, > >> + size_t size) > >> +{ > >> + blob->dm_verity_signed = size > 0 && value; > >> +} > >> +#else > >> +static inline void ipe_set_dmverity_signature(struct ipe_bdev *blob, > >> + const void *value, > >> + size_t size) > >> +{ > >> +} > >> +#endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */ > >> + > >> +/** > >> + * ipe_bdev_setintegrity() - Save integrity data from a bdev to IPE's LSM blob. > >> + * @bdev: Supplies a pointer to a block_device that contains the LSM blob. > >> + * @type: Supplies the integrity type. > >> + * @value: Supplies the value to store. > >> + * @size: The size of @value. > >> + * > >> + * This hook is currently used to save dm-verity's root hash or the existence > >> + * of a validated signed dm-verity root hash into LSM blob. > >> + * > >> + * Return: %0 on success. If an error occurs, the function will return the > >> + * -errno. > >> + */ > >> +int ipe_bdev_setintegrity(struct block_device *bdev, enum lsm_integrity_type type, > >> + const void *value, size_t size) > >> +{ > >> + const struct dm_verity_digest *digest = NULL; > >> + struct ipe_bdev *blob = ipe_bdev(bdev); > >> + struct digest_info *info = NULL; > >> + > >> + if (type == LSM_INT_DMVERITY_ROOTHASH) { > >> + if (!value) { > >> + ipe_digest_free(blob->root_hash); > >> + blob->root_hash = NULL; > >> + > >> + return 0; > >> + } > >> + digest = value; > >> + > >> + info = kzalloc(sizeof(*info), GFP_KERNEL); > >> + if (!info) > >> + return -ENOMEM; > >> + > >> + info->digest = kmemdup(digest->digest, digest->digest_len, > >> + GFP_KERNEL); > >> + if (!info->digest) > >> + goto dmv_roothash_err; > >> + > >> + info->alg = kstrdup(digest->alg, GFP_KERNEL); > >> + if (!info->alg) > >> + goto dmv_roothash_err; > >> + > >> + info->digest_len = digest->digest_len; > >> + > >> + if (blob->root_hash) > >> + ipe_digest_free(blob->root_hash); > > > > The above if/free looks like a new addition from v18 and I'm not quite > > sure why the `blob->root_hash` NULL check is necessary as > > ipe_digest_free() does a IS_ERR_OR_NULL() check right at the top. > > > > Likely harmless and doubtful to have any noticable performance impact, > > but I wanted to mention it just in case ... > > > > Yes directly call ipe_digest_free() should be enough. > > Also this new free is introduced because the mapped device with an > existing dm-verity target can be suspended and associated with a new > dm-verity target. In this case, the root hash associated with the > security blob will be stale and needs to be freed before setting the new > data. > > -Fan > > >> + blob->root_hash = info; > >> + > >> + return 0; > >> +dmv_roothash_err: Just a nitpick but 9/10 'err' is a prefix... Also now this patch set uses 'err'' ambiguously given the use as name of the variable to store a return code. Similar naming pattern would do miracles. > >> + ipe_digest_free(info); > >> + > >> + return -ENOMEM; > >> + } else if (type == LSM_INT_DMVERITY_SIG_VALID) { > >> + ipe_set_dmverity_signature(blob, value, size); > >> + > >> + return 0; > >> + } > >> + > >> + return -EINVAL; > >> +} > >> +#endif /* CONFIG_IPE_PROP_DM_VERITY */ > > > > -- > > paul-moore.com BR, Jarkko
diff --git a/security/ipe/Kconfig b/security/ipe/Kconfig index ac4d558e69d5..8279dddf92ad 100644 --- a/security/ipe/Kconfig +++ b/security/ipe/Kconfig @@ -8,6 +8,8 @@ menuconfig SECURITY_IPE depends on SECURITY && SECURITYFS && AUDIT && AUDITSYSCALL select PKCS7_MESSAGE_PARSER select SYSTEM_DATA_VERIFICATION + select IPE_PROP_DM_VERITY if DM_VERITY + select IPE_PROP_DM_VERITY_SIGNATURE if DM_VERITY && DM_VERITY_VERIFY_ROOTHASH_SIG help This option enables the Integrity Policy Enforcement LSM allowing users to define a policy to enforce a trust-based access @@ -15,3 +17,28 @@ menuconfig SECURITY_IPE admins to reconfigure trust requirements on the fly. If unsure, answer N. + +if SECURITY_IPE +menu "IPE Trust Providers" + +config IPE_PROP_DM_VERITY + bool "Enable support for dm-verity based on root hash" + depends on DM_VERITY + help + This option enables the 'dmverity_roothash' property within IPE + policies. The property evaluates to TRUE when a file from a dm-verity + volume is evaluated, and the volume's root hash matches the value + supplied in the policy. + +config IPE_PROP_DM_VERITY_SIGNATURE + bool "Enable support for dm-verity based on root hash signature" + depends on DM_VERITY && DM_VERITY_VERIFY_ROOTHASH_SIG + help + This option enables the 'dmverity_signature' property within IPE + policies. The property evaluates to TRUE when a file from a dm-verity + volume, which has been mounted with a valid signed root hash, + is evaluated. + +endmenu + +endif diff --git a/security/ipe/Makefile b/security/ipe/Makefile index 62caccba14b4..e1019bb9f0f3 100644 --- a/security/ipe/Makefile +++ b/security/ipe/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_SECURITY_IPE) += \ + digest.o \ eval.o \ hooks.o \ fs.o \ diff --git a/security/ipe/audit.c b/security/ipe/audit.c index a416291ba477..2c98520267c1 100644 --- a/security/ipe/audit.c +++ b/security/ipe/audit.c @@ -13,6 +13,7 @@ #include "hooks.h" #include "policy.h" #include "audit.h" +#include "digest.h" #define ACTSTR(x) ((x) == IPE_ACTION_ALLOW ? "ALLOW" : "DENY") @@ -49,8 +50,22 @@ static const char *const audit_hook_names[__IPE_HOOK_MAX] = { static const char *const audit_prop_names[__IPE_PROP_MAX] = { "boot_verified=FALSE", "boot_verified=TRUE", + "dmverity_roothash=", + "dmverity_signature=FALSE", + "dmverity_signature=TRUE", }; +/** + * audit_dmv_roothash() - audit the roothash of a dmverity_roothash property. + * @ab: Supplies a pointer to the audit_buffer to append to. + * @rh: Supplies a pointer to the digest structure. + */ +static void audit_dmv_roothash(struct audit_buffer *ab, const void *rh) +{ + audit_log_format(ab, "%s", audit_prop_names[IPE_PROP_DMV_ROOTHASH]); + ipe_digest_audit(ab, rh); +} + /** * audit_rule() - audit an IPE policy rule. * @ab: Supplies a pointer to the audit_buffer to append to. @@ -62,8 +77,18 @@ static void audit_rule(struct audit_buffer *ab, const struct ipe_rule *r) audit_log_format(ab, " rule=\"%s ", audit_op_names[r->op]); - list_for_each_entry(ptr, &r->props, next) - audit_log_format(ab, "%s ", audit_prop_names[ptr->type]); + list_for_each_entry(ptr, &r->props, next) { + switch (ptr->type) { + case IPE_PROP_DMV_ROOTHASH: + audit_dmv_roothash(ab, ptr->value); + break; + default: + audit_log_format(ab, "%s", audit_prop_names[ptr->type]); + break; + } + + audit_log_format(ab, " "); + } audit_log_format(ab, "action=%s\"", ACTSTR(r->action)); } diff --git a/security/ipe/digest.c b/security/ipe/digest.c new file mode 100644 index 000000000000..493716370570 --- /dev/null +++ b/security/ipe/digest.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved. + */ + +#include "digest.h" + +/** + * ipe_digest_parse() - parse a digest in IPE's policy. + * @valstr: Supplies the string parsed from the policy. + * + * Digests in IPE are defined in a standard way: + * <alg_name>:<hex> + * + * Use this function to create a property to parse the digest + * consistently. The parsed digest will be saved in @value in IPE's + * policy. + * + * Return: The parsed digest_info structure on success. If an error occurs, + * the function will return the error value (via ERR_PTR). + */ +struct digest_info *ipe_digest_parse(const char *valstr) +{ + struct digest_info *info = NULL; + char *sep, *raw_digest; + size_t raw_digest_len; + u8 *digest = NULL; + char *alg = NULL; + int rc = 0; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + sep = strchr(valstr, ':'); + if (!sep) { + rc = -EBADMSG; + goto err; + } + + alg = kstrndup(valstr, sep - valstr, GFP_KERNEL); + if (!alg) { + rc = -ENOMEM; + goto err; + } + + raw_digest = sep + 1; + raw_digest_len = strlen(raw_digest); + + info->digest_len = (raw_digest_len + 1) / 2; + digest = kzalloc(info->digest_len, GFP_KERNEL); + if (!digest) { + rc = -ENOMEM; + goto err; + } + + rc = hex2bin(digest, raw_digest, info->digest_len); + if (rc < 0) { + rc = -EINVAL; + goto err; + } + + info->alg = alg; + info->digest = digest; + return info; + +err: + kfree(alg); + kfree(digest); + kfree(info); + return ERR_PTR(rc); +} + +/** + * ipe_digest_eval() - evaluate an IPE digest against another digest. + * @expected: Supplies the policy-provided digest value. + * @digest: Supplies the digest to compare against the policy digest value. + * + * Return: + * * %true - digests match + * * %false - digests do not match + */ +bool ipe_digest_eval(const struct digest_info *expected, + const struct digest_info *digest) +{ + return (expected->digest_len == digest->digest_len) && + (!strcmp(expected->alg, digest->alg)) && + (!memcmp(expected->digest, digest->digest, expected->digest_len)); +} + +/** + * ipe_digest_free() - free an IPE digest. + * @info: Supplies a pointer the policy-provided digest to free. + */ +void ipe_digest_free(struct digest_info *info) +{ + if (IS_ERR_OR_NULL(info)) + return; + + kfree(info->alg); + kfree(info->digest); + kfree(info); +} + +/** + * ipe_digest_audit() - audit a digest that was sourced from IPE's policy. + * @ab: Supplies the audit_buffer to append the formatted result. + * @info: Supplies a pointer to source the audit record from. + * + * Digests in IPE are audited in this format: + * <alg_name>:<hex> + */ +void ipe_digest_audit(struct audit_buffer *ab, const struct digest_info *info) +{ + audit_log_untrustedstring(ab, info->alg); + audit_log_format(ab, ":"); + audit_log_n_hex(ab, info->digest, info->digest_len); +} diff --git a/security/ipe/digest.h b/security/ipe/digest.h new file mode 100644 index 000000000000..52c9b3844a38 --- /dev/null +++ b/security/ipe/digest.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved. + */ + +#ifndef _IPE_DIGEST_H +#define _IPE_DIGEST_H + +#include <linux/types.h> +#include <linux/audit.h> + +#include "policy.h" + +struct digest_info { + const char *alg; + const u8 *digest; + size_t digest_len; +}; + +struct digest_info *ipe_digest_parse(const char *valstr); +void ipe_digest_free(struct digest_info *digest_info); +void ipe_digest_audit(struct audit_buffer *ab, const struct digest_info *val); +bool ipe_digest_eval(const struct digest_info *expected, + const struct digest_info *digest); + +#endif /* _IPE_DIGEST_H */ diff --git a/security/ipe/eval.c b/security/ipe/eval.c index dd9064974be6..8f4f63088206 100644 --- a/security/ipe/eval.c +++ b/security/ipe/eval.c @@ -15,10 +15,12 @@ #include "eval.h" #include "policy.h" #include "audit.h" +#include "digest.h" struct ipe_policy __rcu *ipe_active_policy; bool success_audit; bool enforce = true; +#define INO_BLOCK_DEV(ino) ((ino)->i_sb->s_bdev) #define FILE_SUPERBLOCK(f) ((f)->f_path.mnt->mnt_sb) @@ -32,6 +34,23 @@ static void build_ipe_sb_ctx(struct ipe_eval_ctx *ctx, const struct file *const ctx->initramfs = ipe_sb(FILE_SUPERBLOCK(file))->initramfs; } +#ifdef CONFIG_IPE_PROP_DM_VERITY +/** + * build_ipe_bdev_ctx() - Build ipe_bdev field of an evaluation context. + * @ctx: Supplies a pointer to the context to be populated. + * @ino: Supplies the inode struct of the file triggered IPE event. + */ +static void build_ipe_bdev_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino) +{ + if (INO_BLOCK_DEV(ino)) + ctx->ipe_bdev = ipe_bdev(INO_BLOCK_DEV(ino)); +} +#else +static void build_ipe_bdev_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino) +{ +} +#endif /* CONFIG_IPE_PROP_DM_VERITY */ + /** * ipe_build_eval_ctx() - Build an ipe evaluation context. * @ctx: Supplies a pointer to the context to be populated. @@ -48,8 +67,10 @@ void ipe_build_eval_ctx(struct ipe_eval_ctx *ctx, ctx->op = op; ctx->hook = hook; - if (file) + if (file) { build_ipe_sb_ctx(ctx, file); + build_ipe_bdev_ctx(ctx, d_real_inode(file->f_path.dentry)); + } } /** @@ -65,6 +86,70 @@ static bool evaluate_boot_verified(const struct ipe_eval_ctx *const ctx) return ctx->initramfs; } +#ifdef CONFIG_IPE_PROP_DM_VERITY +/** + * evaluate_dmv_roothash() - Evaluate @ctx against a dmv roothash property. + * @ctx: Supplies a pointer to the context being evaluated. + * @p: Supplies a pointer to the property being evaluated. + * + * Return: + * * %true - The current @ctx match the @p + * * %false - The current @ctx doesn't match the @p + */ +static bool evaluate_dmv_roothash(const struct ipe_eval_ctx *const ctx, + struct ipe_prop *p) +{ + return !!ctx->ipe_bdev && + !!ctx->ipe_bdev->root_hash && + ipe_digest_eval(p->value, + ctx->ipe_bdev->root_hash); +} +#else +static bool evaluate_dmv_roothash(const struct ipe_eval_ctx *const ctx, + struct ipe_prop *p) +{ + return false; +} +#endif /* CONFIG_IPE_PROP_DM_VERITY */ + +#ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE +/** + * evaluate_dmv_sig_false() - Evaluate @ctx against a dmv sig false property. + * @ctx: Supplies a pointer to the context being evaluated. + * + * Return: + * * %true - The current @ctx match the property + * * %false - The current @ctx doesn't match the property + */ +static bool evaluate_dmv_sig_false(const struct ipe_eval_ctx *const ctx) +{ + return !ctx->ipe_bdev || (!ctx->ipe_bdev->dm_verity_signed); +} + +/** + * evaluate_dmv_sig_true() - Evaluate @ctx against a dmv sig true property. + * @ctx: Supplies a pointer to the context being evaluated. + * + * Return: + * * %true - The current @ctx match the property + * * %false - The current @ctx doesn't match the property + */ +static bool evaluate_dmv_sig_true(const struct ipe_eval_ctx *const ctx) +{ + return !evaluate_dmv_sig_false(ctx); +} +#else +static bool evaluate_dmv_sig_false(const struct ipe_eval_ctx *const ctx) +{ + return false; +} + +static bool evaluate_dmv_sig_true(const struct ipe_eval_ctx *const ctx) +{ + return false; +} +#endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */ + /** * evaluate_property() - Analyze @ctx against a rule property. * @ctx: Supplies a pointer to the context to be evaluated. @@ -85,6 +170,12 @@ static bool evaluate_property(const struct ipe_eval_ctx *const ctx, return !evaluate_boot_verified(ctx); case IPE_PROP_BOOT_VERIFIED_TRUE: return evaluate_boot_verified(ctx); + case IPE_PROP_DMV_ROOTHASH: + return evaluate_dmv_roothash(ctx, p); + case IPE_PROP_DMV_SIG_FALSE: + return evaluate_dmv_sig_false(ctx); + case IPE_PROP_DMV_SIG_TRUE: + return evaluate_dmv_sig_true(ctx); default: return false; } diff --git a/security/ipe/eval.h b/security/ipe/eval.h index 80b74f55fa69..4901df0e1369 100644 --- a/security/ipe/eval.h +++ b/security/ipe/eval.h @@ -22,12 +22,24 @@ struct ipe_superblock { bool initramfs; }; +#ifdef CONFIG_IPE_PROP_DM_VERITY +struct ipe_bdev { +#ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE + bool dm_verity_signed; +#endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */ + struct digest_info *root_hash; +}; +#endif /* CONFIG_IPE_PROP_DM_VERITY */ + struct ipe_eval_ctx { enum ipe_op_type op; enum ipe_hook_type hook; const struct file *file; bool initramfs; +#ifdef CONFIG_IPE_PROP_DM_VERITY + const struct ipe_bdev *ipe_bdev; +#endif /* CONFIG_IPE_PROP_DM_VERITY */ }; enum ipe_match { diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c index b68719bf44fb..51f1e63c295c 100644 --- a/security/ipe/hooks.c +++ b/security/ipe/hooks.c @@ -8,10 +8,12 @@ #include <linux/types.h> #include <linux/binfmts.h> #include <linux/mman.h> +#include <linux/blk_types.h> #include "ipe.h" #include "hooks.h" #include "eval.h" +#include "digest.h" /** * ipe_bprm_check_security() - ipe security hook function for bprm check. @@ -191,3 +193,94 @@ void ipe_unpack_initramfs(void) { ipe_sb(current->fs->root.mnt->mnt_sb)->initramfs = true; } + +#ifdef CONFIG_IPE_PROP_DM_VERITY +/** + * ipe_bdev_free_security() - Free IPE's LSM blob of block_devices. + * @bdev: Supplies a pointer to a block_device that contains the structure + * to free. + */ +void ipe_bdev_free_security(struct block_device *bdev) +{ + struct ipe_bdev *blob = ipe_bdev(bdev); + + ipe_digest_free(blob->root_hash); +} + +#ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE +static void ipe_set_dmverity_signature(struct ipe_bdev *blob, + const void *value, + size_t size) +{ + blob->dm_verity_signed = size > 0 && value; +} +#else +static inline void ipe_set_dmverity_signature(struct ipe_bdev *blob, + const void *value, + size_t size) +{ +} +#endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */ + +/** + * ipe_bdev_setintegrity() - Save integrity data from a bdev to IPE's LSM blob. + * @bdev: Supplies a pointer to a block_device that contains the LSM blob. + * @type: Supplies the integrity type. + * @value: Supplies the value to store. + * @size: The size of @value. + * + * This hook is currently used to save dm-verity's root hash or the existence + * of a validated signed dm-verity root hash into LSM blob. + * + * Return: %0 on success. If an error occurs, the function will return the + * -errno. + */ +int ipe_bdev_setintegrity(struct block_device *bdev, enum lsm_integrity_type type, + const void *value, size_t size) +{ + const struct dm_verity_digest *digest = NULL; + struct ipe_bdev *blob = ipe_bdev(bdev); + struct digest_info *info = NULL; + + if (type == LSM_INT_DMVERITY_ROOTHASH) { + if (!value) { + ipe_digest_free(blob->root_hash); + blob->root_hash = NULL; + + return 0; + } + digest = value; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->digest = kmemdup(digest->digest, digest->digest_len, + GFP_KERNEL); + if (!info->digest) + goto dmv_roothash_err; + + info->alg = kstrdup(digest->alg, GFP_KERNEL); + if (!info->alg) + goto dmv_roothash_err; + + info->digest_len = digest->digest_len; + + if (blob->root_hash) + ipe_digest_free(blob->root_hash); + blob->root_hash = info; + + return 0; +dmv_roothash_err: + ipe_digest_free(info); + + return -ENOMEM; + } else if (type == LSM_INT_DMVERITY_SIG_VALID) { + ipe_set_dmverity_signature(blob, value, size); + + return 0; + } + + return -EINVAL; +} +#endif /* CONFIG_IPE_PROP_DM_VERITY */ diff --git a/security/ipe/hooks.h b/security/ipe/hooks.h index f4f0b544ddcc..4d585fb6ada3 100644 --- a/security/ipe/hooks.h +++ b/security/ipe/hooks.h @@ -8,6 +8,7 @@ #include <linux/fs.h> #include <linux/binfmts.h> #include <linux/security.h> +#include <linux/blk_types.h> enum ipe_hook_type { IPE_HOOK_BPRM_CHECK = 0, @@ -35,4 +36,11 @@ int ipe_kernel_load_data(enum kernel_load_data_id id, bool contents); void ipe_unpack_initramfs(void); +#ifdef CONFIG_IPE_PROP_DM_VERITY +void ipe_bdev_free_security(struct block_device *bdev); + +int ipe_bdev_setintegrity(struct block_device *bdev, enum lsm_integrity_type type, + const void *value, size_t len); +#endif /* CONFIG_IPE_PROP_DM_VERITY */ + #endif /* _IPE_HOOKS_H */ diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c index 53f2196b9bcc..99cb42caa63a 100644 --- a/security/ipe/ipe.c +++ b/security/ipe/ipe.c @@ -7,11 +7,15 @@ #include "ipe.h" #include "eval.h" #include "hooks.h" +#include "eval.h" bool ipe_enabled; static struct lsm_blob_sizes ipe_blobs __ro_after_init = { .lbs_superblock = sizeof(struct ipe_superblock), +#ifdef CONFIG_IPE_PROP_DM_VERITY + .lbs_bdev = sizeof(struct ipe_bdev), +#endif /* CONFIG_IPE_PROP_DM_VERITY */ }; static const struct lsm_id ipe_lsmid = { @@ -24,6 +28,13 @@ struct ipe_superblock *ipe_sb(const struct super_block *sb) return sb->s_security + ipe_blobs.lbs_superblock; } +#ifdef CONFIG_IPE_PROP_DM_VERITY +struct ipe_bdev *ipe_bdev(struct block_device *b) +{ + return b->security + ipe_blobs.lbs_bdev; +} +#endif /* CONFIG_IPE_PROP_DM_VERITY */ + static struct security_hook_list ipe_hooks[] __ro_after_init = { LSM_HOOK_INIT(bprm_check_security, ipe_bprm_check_security), LSM_HOOK_INIT(mmap_file, ipe_mmap_file), @@ -31,6 +42,10 @@ static struct security_hook_list ipe_hooks[] __ro_after_init = { LSM_HOOK_INIT(kernel_read_file, ipe_kernel_read_file), LSM_HOOK_INIT(kernel_load_data, ipe_kernel_load_data), LSM_HOOK_INIT(initramfs_populated, ipe_unpack_initramfs), +#ifdef CONFIG_IPE_PROP_DM_VERITY + LSM_HOOK_INIT(bdev_free_security, ipe_bdev_free_security), + LSM_HOOK_INIT(bdev_setintegrity, ipe_bdev_setintegrity), +#endif /* CONFIG_IPE_PROP_DM_VERITY */ }; /** diff --git a/security/ipe/ipe.h b/security/ipe/ipe.h index 4aa18d1d0525..01f46286e383 100644 --- a/security/ipe/ipe.h +++ b/security/ipe/ipe.h @@ -16,4 +16,8 @@ struct ipe_superblock *ipe_sb(const struct super_block *sb); extern bool ipe_enabled; +#ifdef CONFIG_IPE_PROP_DM_VERITY +struct ipe_bdev *ipe_bdev(struct block_device *b); +#endif /* CONFIG_IPE_PROP_DM_VERITY */ + #endif /* _IPE_H */ diff --git a/security/ipe/policy.h b/security/ipe/policy.h index ffd60cc7fda6..26776092c710 100644 --- a/security/ipe/policy.h +++ b/security/ipe/policy.h @@ -33,6 +33,9 @@ enum ipe_action_type { enum ipe_prop_type { IPE_PROP_BOOT_VERIFIED_FALSE, IPE_PROP_BOOT_VERIFIED_TRUE, + IPE_PROP_DMV_ROOTHASH, + IPE_PROP_DMV_SIG_FALSE, + IPE_PROP_DMV_SIG_TRUE, __IPE_PROP_MAX }; diff --git a/security/ipe/policy_parser.c b/security/ipe/policy_parser.c index 84cc688be3a2..71c84b293029 100644 --- a/security/ipe/policy_parser.c +++ b/security/ipe/policy_parser.c @@ -11,6 +11,7 @@ #include "policy.h" #include "policy_parser.h" +#include "digest.h" #define START_COMMENT '#' #define IPE_POLICY_DELIM " \t" @@ -221,6 +222,7 @@ static void free_rule(struct ipe_rule *r) list_for_each_entry_safe(p, t, &r->props, next) { list_del(&p->next); + ipe_digest_free(p->value); kfree(p); } @@ -273,6 +275,9 @@ static enum ipe_action_type parse_action(char *t) static const match_table_t property_tokens = { {IPE_PROP_BOOT_VERIFIED_FALSE, "boot_verified=FALSE"}, {IPE_PROP_BOOT_VERIFIED_TRUE, "boot_verified=TRUE"}, + {IPE_PROP_DMV_ROOTHASH, "dmverity_roothash=%s"}, + {IPE_PROP_DMV_SIG_FALSE, "dmverity_signature=FALSE"}, + {IPE_PROP_DMV_SIG_TRUE, "dmverity_signature=TRUE"}, {IPE_PROP_INVALID, NULL} }; @@ -295,6 +300,7 @@ static int parse_property(char *t, struct ipe_rule *r) struct ipe_prop *p = NULL; int rc = 0; int token; + char *dup = NULL; p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) @@ -303,8 +309,22 @@ static int parse_property(char *t, struct ipe_rule *r) token = match_token(t, property_tokens, args); switch (token) { + case IPE_PROP_DMV_ROOTHASH: + dup = match_strdup(&args[0]); + if (!dup) { + rc = -ENOMEM; + goto err; + } + p->value = ipe_digest_parse(dup); + if (IS_ERR(p->value)) { + rc = PTR_ERR(p->value); + goto err; + } + fallthrough; case IPE_PROP_BOOT_VERIFIED_FALSE: case IPE_PROP_BOOT_VERIFIED_TRUE: + case IPE_PROP_DMV_SIG_FALSE: + case IPE_PROP_DMV_SIG_TRUE: p->type = token; break; default: @@ -315,10 +335,12 @@ static int parse_property(char *t, struct ipe_rule *r) goto err; list_add_tail(&p->next, &r->props); +out: + kfree(dup); return rc; err: kfree(p); - return rc; + goto out; } /**