ima: provide signature based 'init' appraisal
Subsystems (4)
v4 cover ima: provide signature based 'init' appraisal
Currently secure IMA/EVM initialization has to be done from the initramfs, embedded in the signed kernel image. Many systems do not want to use initramfs or usage of embedded initramfs makes it difficult to have multi-target kernels.
This is a very simple patchset which makes it possible to perform secure initialization by requiring initial user-space to be signed.
It does it by:
- introducing a hook to load keys
- loading IMA signed public key certificate into the '.ima' trusted keyring
- making default IMA appraisal policy to require everything to be signed
When builtin initramfs is not in use, keys cannot be read from initcalls, because root filesystem is not yet mounted. In order to read keys before executing init process, ima_prepare_keys() hook is introduced. Reading public keys from the kernel is justified because signature verification key is needed in order to verify anything else which is read from the file system. Public keys are X509 certificates and itself signed by the trusted key from the .system keyring. Kernel BIG KEYS support is an example of reading keys directly by the kernel.
CONFIG_IMA_APPRAISE_SIGNED_INIT kernel option is provided to make the IMA default appraisal policy to required signature validation. Signed init process need to initialize EVM key and load appropriate IMA policy which would not require everything to be signed.
Unless real '/sbin/init' is signed, a simple and practical way is to place all signed programs, libraries, scripts and configuration files under dedicated directory, for example '/ima', and run signed init process by providing a kernel command line parameter 'init=/ima/init'.
In the first post of these patches Andrew Morton noted that integrity_read_file() is a very simple open-file-and-slurp-it-into-memory and if there are other similar functions that can be made in ./lib. I found out that only sound:sound_firmware.c:do_mod_firmware_load(), which is enabled by CONFIG_SOUND_PRIME which is related to deprecated OSS interface and is not enabled anymore in latest Ubuntu kernels, at least. So I am keeping integrity_read_file() in integrity subsystem.
cpio based initramfs currently does not support extended attributes. There is an initial agreement to introduce light-weight tar parser to the kernel to support extended attributes which will make it possible to use IMA appraisal with external initramfs. It will benefit from this patchset and allow to update initramfs with signed files also on the running system as distros do.
Changes in v4:
* use ima_policy_flag to disable appraisal
* key directory path changed to /etc/keys (Mimi)
* slightly updated patch descriptions
Changes in v3:
* ima_prepare_keys() renamed to integrity_load_keys() to be the hook for both modules of integrity subsystem IMA/EVM.
* removed unnecessary configuration options and declared init functions with '__init'.
* updated to lately introduced 'ima_policy_flag' variable to disabled and enable IMA appraisal.
* separated key loading patch from policy change patch
* added patch which refactor vfs_read(). Agreed with Mimi to offer to move calling file operations hooks to a separate helper function which is then used by vfs_read() and integrity_kernel_read(). Applying this patch does not affect functionality and can be applied if agreed so.
Changes in v2:
* ima_kernel_read() moved as integrity_kernel_read() from ima_crypto.c to iint.c for use by integrity_read_file. The reason for keeping internal version is because 'integrity' version does not call fsnotify_access(), add_rchar() and inc_syscr().
* integrity_read_file() moved from digsig.c to iint.c because it is used by IMA crypto subsystem and should not depend on digsig support being enabled.
-Dmitry
*** BLURB HERE ***
Dmitry Kasatkin (6):
integrity: define a new function integrity_read_file()
integrity: provide a function to load x509 certificate from the kernel
ima: load x509 certificate from the kernel
integrity: provide a hook to load keys when rootfs is ready
ima: require signature based appraisal
VFS: refactor vfs_read()
fs/read_write.c | 24 ++++++++--- include/linux/fs.h | 1 + include/linux/integrity.h | 6 +++ init/main.c | 6 ++- security/integrity/digsig.c | 37 +++++++++++++++- security/integrity/iint.c | 85 +++++++++++++++++++++++++++++++++++++ security/integrity/ima/Kconfig | 22 ++++++++++ security/integrity/ima/ima_api.c | 3 +- security/integrity/ima/ima_crypto.c | 35 ++------------- security/integrity/ima/ima_init.c | 17 ++++++++ security/integrity/ima/ima_policy.c | 5 +++ security/integrity/integrity.h | 14 ++++++ 12 files changed, 213 insertions(+), 42 deletions(-)
Signature · 1 line
1.9.1
PATCH v4 1/6 integrity: define a new function integrity_read_file()
This patch defines a new function called integrity_read_file() to read file from the kernel into a buffer. Subsequent patches will read a file containing the public keys and load them onto the IMA keyring.
This patch moves and renames ima_kernel_read(), the non-security checking version of kernel_read(), to integrity_kernel_read().
Changes in v3:
* Patch descriptions improved (Mimi)
Changes in v2:
* configuration option removed
* function declared as '__init'
--- security/integrity/iint.c | 78 +++++++++++++++++++++++++++++++++++++ security/integrity/ima/ima_crypto.c | 35 ++--------------- security/integrity/integrity.h | 4 ++ 3 files changed, 85 insertions(+), 32 deletions(-)
security/integrity/iint.c +78 -0
@@ -19,6 +19,8 @@#include <linux/module.h> #include <linux/spinlock.h> #include <linux/rbtree.h> +#include <linux/file.h> +#include <linux/uaccess.h> #include "integrity.h" static struct rb_root integrity_iint_tree =RB_ROOT ;
@@ -167,3 +169,79 @@ static int __init integrity_iintcache_init(void)return 0 ; }security_initcall (integrity_iintcache_init); + + +/* +* integrity_kernel_read - read data from the file +* +* This is a function for reading file content instead of kernel_read(). +* It does not perform locking checks to ensure it cannot be blocked. +* It does not perform security checks because it is irrelevant for IMA. +* +*/ +int integrity_kernel_read (struct file *file,loff_t offset, +char *addr,unsigned long count) +{ +mm_segment_t old_fs; +char __user *buf = addr; +ssize_t ret = -EINVAL ; + +if (!(file->f_mode &FMODE_READ )) +return -EBADF ; + + old_fs =get_fs (); +set_fs (get_ds ()); +if (file->f_op->read) + ret = file->f_op->read (file, buf, count, &offset); +else if (file->f_op->aio_read) + ret =do_sync_read (file, buf, count, &offset); +else if (file->f_op->read_iter) + ret =new_sync_read (file, buf, count, &offset); +set_fs (old_fs); +return ret; +} + +/* +* integrity_read_file - read entire file content into the buffer +* +* This is function opens a file, allocates the buffer of required +* size, read entire file content to the buffer and closes the file +* +* It is used only by init code. +* +*/ +int __init integrity_read_file (const char *path,char **data) +{ +struct file *file; +loff_t size; +char *buf; +int rc = -EINVAL ; + + file =filp_open (path,O_RDONLY ,0 ); +if (IS_ERR (file)) { + rc =PTR_ERR (file); +pr_err ("Unable to open file: %s (%d)" , path, rc); +return rc; + } + + size =i_size_read (file_inode (file)); +if (size <=0 ) + goto out; + + buf =kmalloc (size,GFP_KERNEL ); +if (!buf) { + rc = -ENOMEM ; + goto out; + } + + rc =integrity_kernel_read (file,0 , buf, size); +if (rc <0 ) +kfree (buf); +else if (rc != size) + rc = -EIO ; +else + *data = buf; +out: +fput (file); +return rc; +}
security/integrity/ima/ima_crypto.c +3 -32
@@ -67,36 +67,6 @@ MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size");static struct crypto_shash *ima_shash_tfm;static struct crypto_ahash *ima_ahash_tfm; -/** -* ima_kernel_read - read file content -* -* This is a function for reading file content instead of kernel_read(). -* It does not perform locking checks to ensure it cannot be blocked. -* It does not perform security checks because it is irrelevant for IMA. -* -*/ -static int ima_kernel_read (struct file *file,loff_t offset, -char *addr,unsigned long count) -{ -mm_segment_t old_fs; -char __user *buf = addr; -ssize_t ret = -EINVAL ; - -if (!(file->f_mode &FMODE_READ )) -return -EBADF ; - - old_fs =get_fs (); -set_fs (get_ds ()); -if (file->f_op->read) - ret = file->f_op->read (file, buf, count, &offset); -else if (file->f_op->aio_read) - ret =do_sync_read (file, buf, count, &offset); -else if (file->f_op->read_iter) - ret =new_sync_read (file, buf, count, &offset); -set_fs (old_fs); -return ret; -} -int __init ima_init_crypto (void ) {long rc;
@@ -324,7 +294,8 @@ static int ima_calc_file_hash_atfm(struct file *file, }/* read buffer */ rbuf_len =min_t (loff_t, i_size - offset, rbuf_size[active]); - rc =ima_kernel_read (file, offset, rbuf[active], rbuf_len); + rc =integrity_kernel_read (file, offset, rbuf[active], + rbuf_len);if (rc != rbuf_len) goto out3;
@@ -417,7 +388,7 @@ static int ima_calc_file_hash_tfm(struct file *file,while (offset < i_size) {int rbuf_len; - rbuf_len =ima_kernel_read (file, offset, rbuf,PAGE_SIZE ); + rbuf_len =integrity_kernel_read (file, offset, rbuf,PAGE_SIZE );if (rbuf_len <0 ) { rc = rbuf_len;break ;
security/integrity/integrity.h +4 -0
@@ -119,6 +119,10 @@ struct integrity_iint_cache {*/ struct integrity_iint_cache *integrity_iint_find (struct inode *inode); +int integrity_kernel_read (struct file *file,loff_t offset, +char *addr,unsigned long count); +int __init integrity_read_file (const char *path,char **data); +#define INTEGRITY_KEYRING_EVM 0#define INTEGRITY_KEYRING_MODULE 1#define INTEGRITY_KEYRING_IMA 2
Signature · 1 line
1.9.1
PATCH v4 2/6 integrity: provide a function to load x509 certificate from the kernel
In reply to Dmitry KasatkinProvide the function to load x509 certificates from the kernel into the integrity kernel keyring.
Changes in v2:
* configuration option removed
* function declared as '__init'
--- security/integrity/digsig.c | 37 ++++++++++++++++++++++++++++++++++++- security/integrity/integrity.h | 2 ++ 2 files changed, 38 insertions(+), 1 deletion(-)
security/integrity/digsig.c +36 -1
@@ -14,7 +14,7 @@#include <linux/err.h> #include <linux/sched.h> -#include <linux/rbtree.h> +#include <linux/slab.h> #include <linux/cred.h> #include <linux/key-type.h> #include <linux/digsig.h>
@@ -84,3 +84,38 @@ int __init integrity_init_keyring(const unsigned int id) }return err; } + +int __init integrity_load_x509 (const unsigned int id,char *path) +{ +key_ref_t key; +char *data; +int rc; + +if (!keyring[id]) +return -EINVAL ; + + rc =integrity_read_file (path, &data); +if (rc <0 ) +return rc; + + key =key_create_or_update (make_key_ref (keyring[id],1 ), +"asymmetric" , +NULL , + data, + rc, + ((KEY_POS_ALL & ~KEY_POS_SETATTR ) | +KEY_USR_VIEW |KEY_USR_READ ), +KEY_ALLOC_NOT_IN_QUOTA | +KEY_ALLOC_TRUSTED ); +if (IS_ERR (key)) { + rc =PTR_ERR (key); +pr_err ("Problem loading X.509 certificate (%d): %s\n" , + rc, path); + }else { +pr_notice ("Loaded X.509 cert '%s': %s\n" , +key_ref_to_ptr (key)->description, path); +key_ref_put (key); + } +kfree (data); +return 0 ; +}
security/integrity/integrity.h +2 -0
@@ -134,6 +134,7 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, const char *digest, int digestlen);int __init integrity_init_keyring (const unsigned int id); +int __init integrity_load_x509 (const unsigned int id,char *path);#else static inline int integrity_digsig_verify(const unsigned int id,
@@ -147,6 +148,7 @@ static inline int integrity_init_keyring(const unsigned int id) {return 0 ; } +#endif /* CONFIG_INTEGRITY_SIGNATURE */ #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
Signature · 1 line
1.9.1
PATCH v4 3/6 ima: load x509 certificate from the kernel
In reply to Dmitry KasatkinDefine configuration option to load X509 certificate into the IMA trusted kernel keyring. It implements ima_load_x509() hook to load X509 certificate into the .ima trusted kernel keyring from the root filesystem.
Changes in v3:
* use ima_policy_flag in ima_get_action() ima_load_x509 temporarily clears ima_policy_flag to disable appraisal to load key. Use it to skip appraisal rules.
* Key directory path changed to /etc/keys (Mimi)
Changes in v2:
* added '__init'
* use ima_policy_flag to disable appraisal to load keys
--- security/integrity/ima/Kconfig | 15 +++++++++++++++ security/integrity/ima/ima_api.c | 3 +-- security/integrity/ima/ima_init.c | 17 +++++++++++++++++ security/integrity/integrity.h | 8 ++++++++ 4 files changed, 41 insertions(+), 2 deletions(-)
security/integrity/ima/Kconfig +15 -0
@@ -131,3 +131,18 @@ config IMA_TRUSTED_KEYRING help This option requires that all keys added to the .ima keyring be signed by a key on the system trusted keyring. + +config IMA_LOAD_X509 + bool "Load X509 certificate to the '.ima' trusted keyring" + depends on IMA_TRUSTED_KEYRING + default n + help + This option enables X509 certificate loading from the kernel + to the '.ima' trusted keyring. + +config IMA_X509_PATH + string "IMA X509 certificate path" + depends on IMA_LOAD_X509 + default "/etc/keys/x509_ima.der" + help + This option defines IMA X509 certificate path.
security/integrity/ima/ima_api.c +1 -2
@@ -173,8 +173,7 @@ int ima_get_action(struct inode *inode, int mask, int function) {int flags =IMA_MEASURE |IMA_AUDIT |IMA_APPRAISE ; -if (!ima_appraise) - flags &= ~IMA_APPRAISE ; + flags &= ima_policy_flag;return ima_match_policy (inode, function, mask, flags); }
security/integrity/ima/ima_init.c +17 -0
@@ -24,6 +24,12 @@#include <crypto/hash_info.h> #include "ima.h" +#ifdef CONFIG_IMA_X509_PATH +#define IMA_X509_PATH CONFIG_IMA_X509_PATH +#else +#define IMA_X509_PATH "/etc/keys/x509_ima.der" +#endif +/* name for boot aggregate entry */ static const char *boot_aggregate_name ="boot_aggregate" ;int ima_used_chip;
@@ -91,6 +97,17 @@ err_out:return result; } +#ifdef CONFIG_IMA_LOAD_X509 +void __init ima_load_x509 (void ) +{ +int unset_flags = ima_policy_flag &IMA_APPRAISE ; + + ima_policy_flag &= ~unset_flags; +integrity_load_x509 (INTEGRITY_KEYRING_IMA ,IMA_X509_PATH ); + ima_policy_flag |= unset_flags; +} +#endif +int __init ima_init (void ) {u8 pcr_i[TPM_DIGEST_SIZE ];
security/integrity/integrity.h +8 -0
@@ -162,6 +162,14 @@ static inline int asymmetric_verify(struct key *keyring, const char *sig, }#endif +#ifdef CONFIG_IMA_LOAD_X509 +void __init ima_load_x509 (void ); +#else +static inline void ima_load_x509 (void ) +{ +} +#endif +#ifdef CONFIG_INTEGRITY_AUDIT /* declarations */ void integrity_audit_msg(int audit_msgno, struct inode *inode,
Signature · 1 line
1.9.1
PATCH v4 4/6 integrity: provide a hook to load keys when rootfs is ready
In reply to Dmitry KasatkinKeys can only be loaded once the rootfs is mounted. Initcalls are not suitable for that. This patch defines a special hook to load the x509 public keys onto the IMA keyring, before attempting to access any file. The keys are required for verifying the file's signature. The hook is called after the root filesystem is mounted and before the kernel calls 'init'.
Changes in v3:
* added more explanation to the patch description (Mimi)
Changes in v2:
* Hook renamed as 'integrity_load_keys()' to handle both IMA and EVM keys by integrity subsystem.
* Hook patch moved after defining loading functions
--- include/linux/integrity.h | 6 ++++++ init/main.c | 6 +++++- security/integrity/iint.c | 11 +++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-)
include/linux/integrity.h +6 -0
@@ -24,6 +24,7 @@ enum integrity_status {#ifdef CONFIG_INTEGRITY extern struct integrity_iint_cache *integrity_inode_get (struct inode *inode);extern void integrity_inode_free (struct inode *inode); +extern void __init integrity_load_keys (void );#else static inline struct integrity_iint_cache *
@@ -36,5 +37,10 @@ static inline void integrity_inode_free(struct inode *inode) {return ; } + +static inline void integrity_load_keys (void ) +{ +}#endif /* CONFIG_INTEGRITY */ +#endif /* _LINUX_INTEGRITY_H */
init/main.c +5 -1
@@ -78,6 +78,7 @@#include <linux/context_tracking.h> #include <linux/random.h> #include <linux/list.h> +#include <linux/integrity.h> #include <asm/io.h> #include <asm/bugs.h>
@@ -1026,8 +1027,11 @@ static noinline void __init kernel_init_freeable(void)* Ok, we have completed the initial bootup, and * we're essentially up and running. Get rid of the * initmem segments and start the user-mode stuff.. +* +* rootfs is available now, try loading the public keys +* and default modules */ -/* rootfs is available now, try loading default modules */ +integrity_load_keys ();load_default_modules (); }
security/integrity/iint.c +11 -0
@@ -245,3 +245,14 @@ out:fput (file);return rc; } + +/* +* integrity_load_keys - load integrity keys hook +* +* Hooks is called from init/main.c:kernel_init_freeable() +* when rootfs is ready +*/ +void __init integrity_load_keys (void ) +{ +ima_load_x509 (); +}
Signature · 1 line
1.9.1
PATCH v4 5/6 ima: require signature based appraisal
In reply to Dmitry KasatkinThis patch provides CONFIG_IMA_APPRAISE_SIGNED_INIT kernel configuration option to force IMA appraisal using signatures. This is useful, when EVM key is not initialized yet and we want securely initialize integrity or any other functionality.
It forces embedded policy to require signature. Signed initialization script can initialize EVM key, update the IMA policy and change further requirement of everything to be signed.
Changes in v3:
* kernel parameter fixed to configuration option in the patch description
Changes in v2:
* policy change of this patch separated from the key loading patch
--- security/integrity/ima/Kconfig | 7 +++++++ security/integrity/ima/ima_policy.c | 5 +++++ 2 files changed, 12 insertions(+)
security/integrity/ima/Kconfig +7 -0
@@ -146,3 +146,10 @@ config IMA_X509_PATH default "/etc/keys/x509_ima.der" help This option defines IMA X509 certificate path. + +config IMA_APPRAISE_SIGNED_INIT + bool "Require signed user-space initialization" + depends on IMA_LOAD_X509 + default n + help + This option requires user-space init to be signed.
security/integrity/ima/ima_policy.c +5 -0
@@ -100,7 +100,12 @@ static struct ima_rule_entry default_appraise_rules[] = { {.action = DONT_APPRAISE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +#ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER}, +#else + /* force signature */ + {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER | IMA_DIGSIG_REQUIRED}, +#endif }; static LIST_HEAD(ima_default_rules);
Signature · 1 line
1.9.1
PATCH v4 6/6 VFS: refactor vfs_read()
In reply to Dmitry Kasatkinintegrity_kernel_read() duplicates the file read operations code in vfs_read(). This patch refactors vfs_read() code creating a helper function __vfs_read(). It is used by both vfs_read() and integrity_kernel_read().
--- fs/read_write.c | 24 ++++++++++++++++++------ include/linux/fs.h | 1 + security/integrity/iint.c | 10 +++------- 3 files changed, 22 insertions(+), 13 deletions(-)
fs/read_write.c +18 -6
@@ -412,6 +412,23 @@ ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *pEXPORT_SYMBOL (new_sync_read); +ssize_t __vfs_read (struct file *file,char __user *buf,size_t count, +loff_t *pos) +{ +ssize_t ret; + +if (file->f_op->read) + ret = file->f_op->read (file, buf, count, pos); +else if (file->f_op->aio_read) + ret =do_sync_read (file, buf, count, pos); +else if (file->f_op->read_iter) + ret =new_sync_read (file, buf, count, pos); +else + ret = -EINVAL ; + +return ret; +} +ssize_t vfs_read (struct file *file,char __user *buf,size_t count,loff_t *pos) {ssize_t ret;
@@ -426,12 +443,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) ret =rw_verify_area (READ , file, pos, count);if (ret >=0 ) { count = ret; -if (file->f_op->read) - ret = file->f_op->read (file, buf, count, pos); -else if (file->f_op->aio_read) - ret =do_sync_read (file, buf, count, pos); -else - ret =new_sync_read (file, buf, count, pos); + ret =__vfs_read (file, buf, count, pos);if (ret >0 ) {fsnotify_access (file);add_rchar (current, ret);
include/linux/fs.h +1 -0
@@ -1527,6 +1527,7 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,struct iovec *fast_pointer, struct iovec **ret_pointer); +extern ssize_t __vfs_read (struct file *,char __user *,size_t ,loff_t *);extern ssize_t vfs_read (struct file *,char __user *,size_t ,loff_t *);extern ssize_t vfs_write (struct file *,const char __user *,size_t ,loff_t *); extern ssize_t vfs_readv(struct file *, const struct iovec __user *,
security/integrity/iint.c +3 -7
@@ -184,20 +184,16 @@ int integrity_kernel_read(struct file *file, loff_t offset, {mm_segment_t old_fs;char __user *buf = addr; -ssize_t ret = -EINVAL ; +ssize_t ret;if (!(file->f_mode &FMODE_READ ))return -EBADF ; old_fs =get_fs ();set_fs (get_ds ()); -if (file->f_op->read) - ret = file->f_op->read (file, buf, count, &offset); -else if (file->f_op->aio_read) - ret =do_sync_read (file, buf, count, &offset); -else if (file->f_op->read_iter) - ret =new_sync_read (file, buf, count, &offset); + ret =__vfs_read (file, buf, count, &offset);set_fs (old_fs); +return ret; }
Signature · 1 line
1.9.1