From: Matthew Garrett <mjg59@xxxxxxxxxx> Some filesystems may be able to provide hashes in an out of band manner, and allowing them to do so is a performance win. This is especially true of FUSE-based filesystems where otherwise we recalculate the hash on every measurement. Make use of this if policy says we should, but provide a parameter to force recalculation rather than trusting the filesystem. Signed-off-by: Matthew Garrett <mjg59@xxxxxxxxxx> --- Documentation/ABI/testing/ima_policy | 5 +++- .../admin-guide/kernel-parameters.txt | 5 ++++ security/integrity/ima/ima.h | 3 ++- security/integrity/ima/ima_api.c | 5 +++- security/integrity/ima/ima_crypto.c | 23 ++++++++++++++++++- security/integrity/ima/ima_policy.c | 8 ++++++- security/integrity/ima/ima_template_lib.c | 2 +- security/integrity/integrity.h | 1 + 8 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 09a5def7e28a..6a517282068d 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -24,7 +24,8 @@ Description: [euid=] [fowner=] [fsname=] [subtype=]] lsm: [[subj_user=] [subj_role=] [subj_type=] [obj_user=] [obj_role=] [obj_type=]] - option: [[appraise_type=]] [permit_directio] + option: [[appraise_type=] [permit_directio] + [trust_vfs]] base: func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK] [FIRMWARE_CHECK] @@ -41,6 +42,8 @@ Description: lsm: are LSM specific option: appraise_type:= [imasig] pcr:= decimal value + permit_directio:= allow directio accesses + trust_vfs:= trust VFS-provided hash values default policy: # PROC_SUPER_MAGIC diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 52e6fbb042cc..cc9302b79409 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1658,6 +1658,11 @@ different crypto accelerators. This option can be used to achieve best performance for particular HW. + ima.force_hash= [IMA] Force hash calculation in IMA + Format: <bool> + Always calculate hashes rather than trusting the + filesystem to provide them to us. + init= [KNL] Format: <full_path> Run specified binary instead of /sbin/init as init diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index d213e835c498..cdaffe6c8a8d 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -133,7 +133,8 @@ int ima_fs_init(void); int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode, const unsigned char *filename); -int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash); +int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash, + bool trust_vfs); int ima_calc_buffer_hash(const void *buf, loff_t len, struct ima_digest_data *hash); int ima_calc_field_array_hash(struct ima_field_data *field_data, diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index c7505fb122d4..0def9cf43549 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -206,6 +206,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, int length; void *tmpbuf; u64 i_version; + bool trust_vfs; struct { struct ima_digest_data hdr; char digest[IMA_MAX_DIGEST_SIZE]; @@ -225,10 +226,12 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, /* Initialize hash digest to 0's in case of failure */ memset(&hash.digest, 0, sizeof(hash.digest)); + trust_vfs = iint->flags & IMA_TRUST_VFS; + if (buf) result = ima_calc_buffer_hash(buf, size, &hash.hdr); else - result = ima_calc_file_hash(file, &hash.hdr); + result = ima_calc_file_hash(file, &hash.hdr, trust_vfs); if (result && result != -EBADF && result != -EINVAL) goto out; diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index a32878e10ebc..1c83d23d21a6 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -32,6 +32,10 @@ static unsigned long ima_ahash_minsize; module_param_named(ahash_minsize, ima_ahash_minsize, ulong, 0644); MODULE_PARM_DESC(ahash_minsize, "Minimum file size for ahash use"); +static bool ima_force_hash; +module_param_named(force_hash, ima_force_hash, bool_enable_only, 0644); +MODULE_PARM_DESC(force_hash, "Always calculate hashes"); + /* default is 0 - 1 page. */ static int ima_maxorder; static unsigned int ima_bufsize = PAGE_SIZE; @@ -401,11 +405,14 @@ static int ima_calc_file_shash(struct file *file, struct ima_digest_data *hash) * shash for the hash calculation. If ahash fails, it falls back to using * shash. */ -int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) +int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash, + bool trust_vfs) { loff_t i_size; int rc; struct file *f = file; + struct dentry *dentry = file_dentry(file); + struct inode *inode = d_backing_inode(dentry); bool new_file_instance = false, modified_flags = false; /* @@ -418,6 +425,20 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) return -EINVAL; } + /* + * If policy says we should trust VFS-provided hashes, and + * we're not configured to force hashing, and if the + * filesystem is trusted, ask the VFS if it can obtain the + * hash without us having to calculate it ourself. + */ + if (trust_vfs && !ima_force_hash && + !(inode->i_sb->s_iflags & SB_I_UNTRUSTED_MOUNTER)) { + hash->length = hash_digest_size[hash->algo]; + rc = vfs_get_hash(file, hash->algo, hash->digest, hash->length); + if (!rc) + return 0; + } + /* Open a new file instance in O_RDONLY if we cannot read */ if (!(file->f_mode & FMODE_READ)) { int flags = file->f_flags & ~(O_WRONLY | O_APPEND | diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index bb4e265823a7..c293cbc6c578 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -681,7 +681,7 @@ enum { Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt, Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt, Opt_appraise_type, Opt_permit_directio, - Opt_pcr, Opt_err + Opt_pcr, Opt_trust_vfs, Opt_err }; static const match_table_t policy_tokens = { @@ -716,6 +716,7 @@ static const match_table_t policy_tokens = { {Opt_appraise_type, "appraise_type=%s"}, {Opt_permit_directio, "permit_directio"}, {Opt_pcr, "pcr=%s"}, + {Opt_trust_vfs, "trust_vfs"}, {Opt_err, NULL} }; @@ -1062,6 +1063,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) case Opt_permit_directio: entry->flags |= IMA_PERMIT_DIRECTIO; break; + case Opt_trust_vfs: + entry->flags |= IMA_TRUST_VFS; + break; case Opt_pcr: if (entry->action != MEASURE) { result = -EINVAL; @@ -1358,6 +1362,8 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, "appraise_type=imasig "); if (entry->flags & IMA_PERMIT_DIRECTIO) seq_puts(m, "permit_directio "); + if (entry->flags & IMA_TRUST_VFS) + seq_puts(m, "trust_vfs "); rcu_read_unlock(); seq_puts(m, "\n"); return 0; diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index 513b457ae900..26f71c5805f0 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -291,7 +291,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data, inode = file_inode(event_data->file); hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ? ima_hash_algo : HASH_ALGO_SHA1; - result = ima_calc_file_hash(event_data->file, &hash.hdr); + result = ima_calc_file_hash(event_data->file, &hash.hdr, false); if (result) { integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, event_data->filename, "collect_data", diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 7de59f44cba3..a03f859c1602 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -36,6 +36,7 @@ #define IMA_NEW_FILE 0x04000000 #define EVM_IMMUTABLE_DIGSIG 0x08000000 #define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000 +#define IMA_TRUST_VFS 0x20000000 #define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ IMA_HASH | IMA_APPRAISE_SUBMASK) -- 2.21.0.1020.gf2820cf01a-goog