From c7c8bb237fdbff932b5e431aebee5ce862ea07d1 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 25 Apr 2013 10:43:56 +0300 Subject: ima: provide support for arbitrary hash algorithms In preparation of supporting more hash algorithms with larger hash sizes needed for signature verification, this patch replaces the 20 byte sized digest, with a more flexible structure. The new structure includes the hash algorithm, digest size, and digest. Changelog: - recalculate filedata hash for the measurement list, if the signature hash digest size is greater than 20 bytes. - use generic HASH_ALGO_ - make ima_calc_file_hash static - scripts lindent and checkpatch fixes Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'security/integrity/ima/ima.h') diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index b3dd616560f7..eb86032f4f1e 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -39,7 +39,7 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; /* set during initialization */ extern int ima_initialized; extern int ima_used_chip; -extern char *ima_hash; +extern int ima_hash_algo; extern int ima_appraise; /* IMA inode template definition */ @@ -70,8 +70,9 @@ void ima_fs_cleanup(void); int ima_inode_alloc(struct inode *inode); int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode); -int ima_calc_file_hash(struct file *file, char *digest); -int ima_calc_buffer_hash(const void *data, int len, char *digest); +int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash); +int ima_calc_buffer_hash(const void *data, int len, + struct ima_digest_data *hash); int ima_calc_boot_aggregate(char *digest); void ima_add_violation(struct inode *inode, const unsigned char *filename, const char *op, const char *cause); -- cgit v1.2.3 From d3634d0f426bdeb433cb288bdbb0a5e16cf3dbbf Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 25 Apr 2013 10:44:04 +0300 Subject: ima: read and use signature hash algorithm All files on the filesystem, currently, are hashed using the same hash algorithm. In preparation for files from different packages being signed using different hash algorithms, this patch adds support for reading the signature hash algorithm from the 'security.ima' extended attribute and calculates the appropriate file data hash based on it. Changelog: - fix scripts Lindent and checkpatch msgs - Mimi - fix md5 support for older version, which occupied 20 bytes in the xattr, not the expected 16 bytes. Fix the comparison to compare only the first 16 bytes. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/digsig_asymmetric.c | 11 --------- security/integrity/ima/ima.h | 29 +++++++++++++++++++--- security/integrity/ima/ima_api.c | 12 ++++++++- security/integrity/ima/ima_appraise.c | 45 ++++++++++++++++++++++++++++------ security/integrity/ima/ima_main.c | 11 +++++++-- security/integrity/integrity.h | 11 +++++++++ 6 files changed, 94 insertions(+), 25 deletions(-) (limited to 'security/integrity/ima/ima.h') diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index b4754667659d..9eae4809006b 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -19,17 +19,6 @@ #include "integrity.h" -/* - * signature format v2 - for using with asymmetric keys - */ -struct signature_v2_hdr { - uint8_t version; /* signature format version */ - uint8_t hash_algo; /* Digest algorithm [enum pkey_hash_algo] */ - uint32_t keyid; /* IMA key identifier - not X509/PGP specific*/ - uint16_t sig_size; /* signature size */ - uint8_t sig[0]; /* signature payload */ -} __packed; - /* * Request an asymmetric key. */ diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index eb86032f4f1e..efcdef2bf1bc 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -99,7 +99,9 @@ static inline unsigned long ima_hash_key(u8 *digest) int ima_get_action(struct inode *inode, int mask, int function); int ima_must_measure(struct inode *inode, int mask, int function); int ima_collect_measurement(struct integrity_iint_cache *iint, - struct file *file); + struct file *file, + struct evm_ima_xattr_data **xattr_value, + int *xattr_len); void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename); void ima_audit_measurement(struct integrity_iint_cache *iint, @@ -132,17 +134,25 @@ void ima_delete_rules(void); #ifdef CONFIG_IMA_APPRAISE int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, - struct file *file, const unsigned char *filename); + struct file *file, const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, + int xattr_len); int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func); void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, int func); +void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len, + struct ima_digest_data *hash); +int ima_read_xattr(struct dentry *dentry, + struct evm_ima_xattr_data **xattr_value); #else static inline int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, struct file *file, - const unsigned char *filename) + const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, + int xattr_len) { return INTEGRITY_UNKNOWN; } @@ -163,6 +173,19 @@ static inline enum integrity_status ima_get_cache_status(struct integrity_iint_c { return INTEGRITY_UNKNOWN; } + +static inline void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, + int xattr_len, + struct ima_digest_data *hash) +{ +} + +static inline int ima_read_xattr(struct dentry *dentry, + struct evm_ima_xattr_data **xattr_value) +{ + return 0; +} + #endif /* LSM based policy rules require audit */ diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index e531fe22e582..1dba98e2d7e9 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -139,17 +139,27 @@ int ima_must_measure(struct inode *inode, int mask, int function) * Return 0 on success, error code otherwise */ int ima_collect_measurement(struct integrity_iint_cache *iint, - struct file *file) + struct file *file, + struct evm_ima_xattr_data **xattr_value, + int *xattr_len) { struct inode *inode = file_inode(file); const char *filename = file->f_dentry->d_name.name; int result = 0; + if (xattr_value) + *xattr_len = ima_read_xattr(file->f_dentry, xattr_value); + if (!(iint->flags & IMA_COLLECTED)) { u64 i_version = file_inode(file)->i_version; /* use default hash algorithm */ iint->ima_hash.algo = ima_hash_algo; + + if (xattr_value) + ima_get_hash_algo(*xattr_value, *xattr_len, + &iint->ima_hash); + result = ima_calc_file_hash(file, &iint->ima_hash); if (!result) { iint->version = i_version; diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 3833b0fa7108..00708a3052cc 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -107,6 +107,34 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func) } } +void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len, + struct ima_digest_data *hash) +{ + struct signature_v2_hdr *sig; + + if (!xattr_value || xattr_len < 0 || xattr_len <= 1 + sizeof(*sig)) + return; + + sig = (typeof(sig)) xattr_value->digest; + + if (xattr_value->type != EVM_IMA_XATTR_DIGSIG || sig->version != 2) + return; + + hash->algo = sig->hash_algo; +} + +int ima_read_xattr(struct dentry *dentry, + struct evm_ima_xattr_data **xattr_value) +{ + struct inode *inode = dentry->d_inode; + + if (!inode->i_op->getxattr) + return 0; + + return vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value, + 0, GFP_NOFS); +} + /* * ima_appraise_measurement - appraise file measurement * @@ -116,23 +144,22 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func) * Return 0 on success, error code otherwise */ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, - struct file *file, const unsigned char *filename) + struct file *file, const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, + int xattr_len) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - struct evm_ima_xattr_data *xattr_value = NULL; enum integrity_status status = INTEGRITY_UNKNOWN; const char *op = "appraise_data"; char *cause = "unknown"; - int rc; + int rc = xattr_len; if (!ima_appraise) return 0; if (!inode->i_op->getxattr) return INTEGRITY_UNKNOWN; - rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value, - 0, GFP_NOFS); if (rc <= 0) { if (rc && rc != -ENODATA) goto out; @@ -159,7 +186,10 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, status = INTEGRITY_FAIL; break; } - if (rc - 1 == iint->ima_hash.length) + if (xattr_len - 1 >= iint->ima_hash.length) + /* xattr length may be longer. md5 hash in previous + version occupied 20 bytes in xattr, instead of 16 + */ rc = memcmp(xattr_value->digest, iint->ima_hash.digest, iint->ima_hash.length); @@ -207,7 +237,6 @@ out: ima_cache_flags(iint, func); } ima_set_cache_status(iint, func, status); - kfree(xattr_value); return status; } @@ -223,7 +252,7 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) if (iint->flags & IMA_DIGSIG) return; - rc = ima_collect_measurement(iint, file); + rc = ima_collect_measurement(iint, file, NULL, NULL); if (rc < 0) return; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 7708c2120d9c..95b5df2c6501 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -149,6 +149,8 @@ static int process_measurement(struct file *file, const char *filename, char *pathbuf = NULL; const char *pathname = NULL; int rc = -ENOMEM, action, must_appraise, _func; + struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL; + int xattr_len = 0; if (!ima_initialized || !S_ISREG(inode->i_mode)) return 0; @@ -187,7 +189,10 @@ static int process_measurement(struct file *file, const char *filename, goto out_digsig; } - rc = ima_collect_measurement(iint, file); + if (action & IMA_APPRAISE_SUBMASK) + xattr_ptr = &xattr_value; + + rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len); if (rc != 0) goto out_digsig; @@ -198,7 +203,8 @@ static int process_measurement(struct file *file, const char *filename, if (action & IMA_MEASURE) ima_store_measurement(iint, file, pathname); if (action & IMA_APPRAISE_SUBMASK) - rc = ima_appraise_measurement(_func, iint, file, pathname); + rc = ima_appraise_measurement(_func, iint, file, pathname, + xattr_value, xattr_len); if (action & IMA_AUDIT) ima_audit_measurement(iint, pathname); kfree(pathbuf); @@ -207,6 +213,7 @@ out_digsig: rc = -EACCES; out: mutex_unlock(&inode->i_mutex); + kfree(xattr_value); if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) return -EACCES; return 0; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 0b02ea868e30..ea2318983d97 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -70,6 +70,17 @@ struct ima_digest_data { u8 digest[IMA_MAX_DIGEST_SIZE]; } __packed; +/* + * signature format v2 - for using with asymmetric keys + */ +struct signature_v2_hdr { + uint8_t version; /* signature format version */ + uint8_t hash_algo; /* Digest algorithm [enum pkey_hash_algo] */ + uint32_t keyid; /* IMA key identifier - not X509/PGP specific */ + uint16_t sig_size; /* signature size */ + uint8_t sig[0]; /* signature payload */ +} __packed; + /* integrity data associated with an inode */ struct integrity_iint_cache { struct rb_node rb_node; /* rooted in integrity_iint_tree */ -- cgit v1.2.3 From 140d802240a4ba3351494b4ab199964b96f87493 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 11 Mar 2013 20:29:47 -0400 Subject: ima: differentiate between template hash and file data hash sizes The TPM v1.2 limits the template hash size to 20 bytes. This patch differentiates between the template hash size, as defined in the ima_template_entry, and the file data hash size, as defined in the ima_template_data. Subsequent patches add support for different file data hash algorithms. Change log: - hash digest definition in ima_store_template() should be TPM_DIGEST_SIZE Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 2 +- security/integrity/ima/ima_api.c | 2 +- security/integrity/ima/ima_crypto.c | 4 ++-- security/integrity/ima/ima_fs.c | 10 +++++----- security/integrity/ima/ima_init.c | 2 +- security/integrity/ima/ima_queue.c | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) (limited to 'security/integrity/ima/ima.h') diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index efcdef2bf1bc..52393edfbfd9 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -49,7 +49,7 @@ struct ima_template_data { }; struct ima_template_entry { - u8 digest[IMA_DIGEST_SIZE]; /* sha1 or md5 measurement hash */ + u8 digest[TPM_DIGEST_SIZE]; /* sha1 or md5 measurement hash */ const char *template_name; int template_len; struct ima_template_data template; diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 5a7942e20814..2cc5dcc6bdeb 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -46,7 +46,7 @@ int ima_store_template(struct ima_template_entry *entry, int result; struct { struct ima_digest_data hdr; - char digest[IMA_MAX_DIGEST_SIZE]; + char digest[TPM_DIGEST_SIZE]; } hash; memset(entry->digest, 0, sizeof(entry->digest)); diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 2fd178651467..872c6698067c 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -155,7 +155,7 @@ static void __init ima_pcrread(int idx, u8 *pcr) */ int __init ima_calc_boot_aggregate(char *digest) { - u8 pcr_i[IMA_DIGEST_SIZE]; + u8 pcr_i[TPM_DIGEST_SIZE]; int rc, i; struct { struct shash_desc shash; @@ -173,7 +173,7 @@ int __init ima_calc_boot_aggregate(char *digest) for (i = TPM_PCR0; i < TPM_PCR8; i++) { ima_pcrread(i, pcr_i); /* now accumulate with current aggregate */ - rc = crypto_shash_update(&desc.shash, pcr_i, IMA_DIGEST_SIZE); + rc = crypto_shash_update(&desc.shash, pcr_i, TPM_DIGEST_SIZE); } if (!rc) crypto_shash_final(&desc.shash, digest); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 5f0fd113433a..c35cfb5b3c5e 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -133,7 +133,7 @@ static int ima_measurements_show(struct seq_file *m, void *v) ima_putc(m, &pcr, sizeof pcr); /* 2nd: template digest */ - ima_putc(m, e->digest, IMA_DIGEST_SIZE); + ima_putc(m, e->digest, TPM_DIGEST_SIZE); /* 3rd: template name size */ namelen = strlen(e->template_name); @@ -167,11 +167,11 @@ static const struct file_operations ima_measurements_ops = { .release = seq_release, }; -static void ima_print_digest(struct seq_file *m, u8 *digest) +static void ima_print_digest(struct seq_file *m, u8 *digest, int size) { int i; - for (i = 0; i < IMA_DIGEST_SIZE; i++) + for (i = 0; i < size; i++) seq_printf(m, "%02x", *(digest + i)); } @@ -182,7 +182,7 @@ void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show) switch (show) { case IMA_SHOW_ASCII: - ima_print_digest(m, entry->digest); + ima_print_digest(m, entry->digest, IMA_DIGEST_SIZE); seq_printf(m, " %s\n", entry->file_name); break; case IMA_SHOW_BINARY: @@ -212,7 +212,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX); /* 2nd: SHA1 template hash */ - ima_print_digest(m, e->digest); + ima_print_digest(m, e->digest, TPM_DIGEST_SIZE); /* 3th: template name */ seq_printf(m, " %s ", e->template_name); diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 162ea723db3d..9d0243c10558 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -74,7 +74,7 @@ err_out: int __init ima_init(void) { - u8 pcr_i[IMA_DIGEST_SIZE]; + u8 pcr_i[TPM_DIGEST_SIZE]; int rc; ima_used_chip = 0; diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index ff63fe00c195..e63ff3369f10 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -50,7 +50,7 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value) key = ima_hash_key(digest_value); rcu_read_lock(); hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) { - rc = memcmp(qe->entry->digest, digest_value, IMA_DIGEST_SIZE); + rc = memcmp(qe->entry->digest, digest_value, TPM_DIGEST_SIZE); if (rc == 0) { ret = qe; break; @@ -106,7 +106,7 @@ static int ima_pcr_extend(const u8 *hash) int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode) { - u8 digest[IMA_DIGEST_SIZE]; + u8 digest[TPM_DIGEST_SIZE]; const char *audit_cause = "hash_added"; char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX]; int audit_info = 1; -- cgit v1.2.3 From 09ef54359c4ad49c01a12503b2c510b424ecf059 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 7 Jun 2013 12:16:25 +0200 Subject: ima: ima_calc_boot_agregate must use SHA1 With multiple hash algorithms, ima_hash_tfm is no longer guaranteed to be sha1. Need to force to use sha1. Changelog: - pass ima_digest_data to ima_calc_boot_aggregate() instead of char * (Roberto Sassu); - create an ima_digest_data structure in ima_add_boot_aggregate() (Roberto Sassu); - pass hash->algo to ima_alloc_tfm() (Roberto Sassu, reported by Dmitry). - "move hash definition in ima_add_boot_aggregate()" commit hunk to here. - sparse warning fix - Fengguang Wu Signed-off-by: Dmitry Kasatkin Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 2 +- security/integrity/ima/ima_crypto.c | 24 +++++++++++++++++++++--- security/integrity/ima/ima_init.c | 10 +++++++++- 3 files changed, 31 insertions(+), 5 deletions(-) (limited to 'security/integrity/ima/ima.h') diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 52393edfbfd9..e0e1cde6e674 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -73,7 +73,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash); int ima_calc_buffer_hash(const void *data, int len, struct ima_digest_data *hash); -int ima_calc_boot_aggregate(char *digest); +int __init ima_calc_boot_aggregate(struct ima_digest_data *hash); void ima_add_violation(struct inode *inode, const unsigned char *filename, const char *op, const char *cause); int ima_init_crypto(void); diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index e2be2524a372..22be23f13b3d 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -184,16 +184,17 @@ static void __init ima_pcrread(int idx, u8 *pcr) /* * Calculate the boot aggregate hash */ -int __init ima_calc_boot_aggregate(char *digest) +static int __init ima_calc_boot_aggregate_tfm(char *digest, + struct crypto_shash *tfm) { u8 pcr_i[TPM_DIGEST_SIZE]; int rc, i; struct { struct shash_desc shash; - char ctx[crypto_shash_descsize(ima_shash_tfm)]; + char ctx[crypto_shash_descsize(tfm)]; } desc; - desc.shash.tfm = ima_shash_tfm; + desc.shash.tfm = tfm; desc.shash.flags = 0; rc = crypto_shash_init(&desc.shash); @@ -210,3 +211,20 @@ int __init ima_calc_boot_aggregate(char *digest) crypto_shash_final(&desc.shash, digest); return rc; } + +int __init ima_calc_boot_aggregate(struct ima_digest_data *hash) +{ + struct crypto_shash *tfm; + int rc; + + tfm = ima_alloc_tfm(hash->algo); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + hash->length = crypto_shash_digestsize(tfm); + rc = ima_calc_boot_aggregate_tfm(hash->digest, tfm); + + ima_free_tfm(tfm); + + return rc; +} diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 9d0243c10558..77cd5005f2db 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "ima.h" /* name for boot aggregate entry */ @@ -46,6 +47,10 @@ static void __init ima_add_boot_aggregate(void) const char *audit_cause = "ENOMEM"; int result = -ENOMEM; int violation = 1; + struct { + struct ima_digest_data hdr; + char digest[TPM_DIGEST_SIZE]; + } hash; entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (!entry) @@ -56,12 +61,15 @@ static void __init ima_add_boot_aggregate(void) IMA_EVENT_NAME_LEN_MAX); if (ima_used_chip) { violation = 0; - result = ima_calc_boot_aggregate(entry->template.digest); + hash.hdr.algo = HASH_ALGO_SHA1; + result = ima_calc_boot_aggregate(&hash.hdr); if (result < 0) { audit_cause = "hashing_error"; kfree(entry); goto err_out; } + memcpy(entry->template.digest, hash.hdr.digest, + hash.hdr.length); } result = ima_store_template(entry, violation, NULL); if (result < 0) -- cgit v1.2.3 From 7d802a227b7f26c89f13dab09767e6b0aebd9c9f Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 7 Jun 2013 12:16:26 +0200 Subject: ima: pass the file descriptor to ima_add_violation() Pass the file descriptor instead of the inode to ima_add_violation(), to make the latter consistent with ima_store_measurement() in preparation for the new template architecture. Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 2 +- security/integrity/ima/ima_api.c | 3 ++- security/integrity/ima/ima_main.c | 5 ++--- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'security/integrity/ima/ima.h') diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index e0e1cde6e674..d7bec6f37b61 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -74,7 +74,7 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash); int ima_calc_buffer_hash(const void *data, int len, struct ima_digest_data *hash); int __init ima_calc_boot_aggregate(struct ima_digest_data *hash); -void ima_add_violation(struct inode *inode, const unsigned char *filename, +void ima_add_violation(struct file *file, const unsigned char *filename, const char *op, const char *cause); int ima_init_crypto(void); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index bc1d1282a06f..98160a3259fe 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -78,10 +78,11 @@ int ima_store_template(struct ima_template_entry *entry, * By extending the PCR with 0xFF's instead of with zeroes, the PCR * value is invalidated. */ -void ima_add_violation(struct inode *inode, const unsigned char *filename, +void ima_add_violation(struct file *file, const unsigned char *filename, const char *op, const char *cause) { struct ima_template_entry *entry; + struct inode *inode = file->f_dentry->d_inode; int violation = 1; int result; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 95b5df2c6501..5e8b1f7becd2 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -94,10 +94,9 @@ out: pathname = dentry->d_name.name; if (send_tomtou) - ima_add_violation(inode, pathname, - "invalid_pcr", "ToMToU"); + ima_add_violation(file, pathname, "invalid_pcr", "ToMToU"); if (send_writers) - ima_add_violation(inode, pathname, + ima_add_violation(file, pathname, "invalid_pcr", "open_writers"); kfree(pathbuf); } -- cgit v1.2.3 From 9803d413f41db86fdf0097f1af781fe2e68f474c Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 7 Jun 2013 12:16:27 +0200 Subject: ima: pass the filename argument up to ima_add_template_entry() Pass the filename argument to ima_add_template_entry() in order to eliminate a dependency on template specific data (third argument of integrity_audit_msg). This change is required because, with the new template management mechanism, the generation of a new measurement entry will be performed by new specific functions (introduced in next patches) and the current IMA code will not be aware anymore of how data is stored in the entry payload. Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 5 +++-- security/integrity/ima/ima_api.c | 9 +++++---- security/integrity/ima/ima_init.c | 3 ++- security/integrity/ima/ima_queue.c | 6 +++--- 4 files changed, 13 insertions(+), 10 deletions(-) (limited to 'security/integrity/ima/ima.h') diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index d7bec6f37b61..27d2ffbd0763 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -69,7 +69,8 @@ int ima_fs_init(void); void ima_fs_cleanup(void); int ima_inode_alloc(struct inode *inode); int ima_add_template_entry(struct ima_template_entry *entry, int violation, - const char *op, struct inode *inode); + 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_buffer_hash(const void *data, int len, struct ima_digest_data *hash); @@ -107,7 +108,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, void ima_audit_measurement(struct integrity_iint_cache *iint, const unsigned char *filename); int ima_store_template(struct ima_template_entry *entry, int violation, - struct inode *inode); + struct inode *inode, const unsigned char *filename); void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show); const char *ima_d_path(struct path *path, char **pathbuf); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 98160a3259fe..a0fe5041310a 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -40,7 +40,8 @@ static const char *IMA_TEMPLATE_NAME = "ima"; * Returns 0 on success, error code otherwise */ int ima_store_template(struct ima_template_entry *entry, - int violation, struct inode *inode) + int violation, struct inode *inode, + const unsigned char *filename) { const char *op = "add_template_measure"; const char *audit_cause = "hashing_error"; @@ -67,7 +68,7 @@ int ima_store_template(struct ima_template_entry *entry, } memcpy(entry->digest, hash.hdr.digest, hash.hdr.length); } - result = ima_add_template_entry(entry, violation, op, inode); + result = ima_add_template_entry(entry, violation, op, inode, filename); return result; } @@ -96,7 +97,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename, } memset(&entry->template, 0, sizeof(entry->template)); strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX); - result = ima_store_template(entry, violation, inode); + result = ima_store_template(entry, violation, inode, filename); if (result < 0) kfree(entry); err_out: @@ -248,7 +249,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint, (strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ? file->f_dentry->d_name.name : filename); - result = ima_store_template(entry, violation, inode); + result = ima_store_template(entry, violation, inode, filename); if (!result || result == -EEXIST) iint->flags |= IMA_MEASURED; if (result < 0) diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 77cd5005f2db..d42fac308aaa 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -71,7 +71,8 @@ static void __init ima_add_boot_aggregate(void) memcpy(entry->template.digest, hash.hdr.digest, hash.hdr.length); } - result = ima_store_template(entry, violation, NULL); + result = ima_store_template(entry, violation, NULL, + boot_aggregate_name); if (result < 0) kfree(entry); return; diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index e63ff3369f10..d85e99761f4f 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -104,7 +104,8 @@ static int ima_pcr_extend(const u8 *hash) * and extend the pcr. */ int ima_add_template_entry(struct ima_template_entry *entry, int violation, - const char *op, struct inode *inode) + const char *op, struct inode *inode, + const unsigned char *filename) { u8 digest[TPM_DIGEST_SIZE]; const char *audit_cause = "hash_added"; @@ -141,8 +142,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, } out: mutex_unlock(&ima_extend_list_mutex); - integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, - entry->template.file_name, + integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, op, audit_cause, result, audit_info); return result; } -- cgit v1.2.3 From 7bc5f447ce9d01e19394b5399bf1a4fcebf0f8dd Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 7 Jun 2013 12:16:28 +0200 Subject: ima: define new function ima_alloc_init_template() to API Instead of allocating and initializing the template entry from multiple places (eg. boot aggregate, violation, and regular measurements), this patch defines a new function called ima_alloc_init_template(). The new function allocates and initializes the measurement entry with the inode digest and the filename. In respect to the current behavior, it truncates the file name passed in the 'filename' argument if the latter's size is greater than 255 bytes and the passed file descriptor is NULL. Changelog: - initialize 'hash' variable for non TPM case - Mimi - conform to expectation for 'iint' to be defined as a pointer. - Mimi - add missing 'file' dependency for recalculating file hash. - Mimi Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 3 ++ security/integrity/ima/ima_api.c | 88 ++++++++++++++++++++++++++------------- security/integrity/ima/ima_init.c | 24 ++++++----- 3 files changed, 76 insertions(+), 39 deletions(-) (limited to 'security/integrity/ima/ima.h') diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 27d2ffbd0763..da03d3389619 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -107,6 +107,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename); void ima_audit_measurement(struct integrity_iint_cache *iint, const unsigned char *filename); +int ima_alloc_init_template(struct integrity_iint_cache *iint, + struct file *file, const unsigned char *filename, + struct ima_template_entry **entry); int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode, const unsigned char *filename); void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index a0fe5041310a..29dd43de823a 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -23,6 +23,62 @@ static const char *IMA_TEMPLATE_NAME = "ima"; +/* + * ima_alloc_init_template - create and initialize a new template entry + */ +int ima_alloc_init_template(struct integrity_iint_cache *iint, + struct file *file, const unsigned char *filename, + struct ima_template_entry **entry) +{ + struct ima_template_entry *e; + int result = 0; + + e = kzalloc(sizeof(**entry), GFP_NOFS); + if (!e) + return -ENOMEM; + + memset(&(e)->template, 0, sizeof(e->template)); + if (!iint) /* IMA measurement violation entry */ + goto out; + + if (iint->ima_hash->algo != ima_hash_algo) { + struct inode *inode; + struct { + struct ima_digest_data hdr; + char digest[IMA_MAX_DIGEST_SIZE]; + } hash; + + if (!file) { + result = -EINVAL; + goto out_free; + } + + inode = file_inode(file); + hash.hdr.algo = ima_hash_algo; + hash.hdr.length = SHA1_DIGEST_SIZE; + result = ima_calc_file_hash(file, &hash.hdr); + if (result) { + integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, + filename, "collect_data", + "failed", result, 0); + goto out_free; + } else + memcpy(e->template.digest, hash.hdr.digest, + hash.hdr.length); + } else + memcpy(e->template.digest, iint->ima_hash->digest, + iint->ima_hash->length); +out: + strcpy(e->template.file_name, + (strlen(filename) > IMA_EVENT_NAME_LEN_MAX && file != NULL) ? + file->f_dentry->d_name.name : filename); + *entry = e; + return 0; +out_free: + kfree(e); + return result; +} + /* * ima_store_template - store ima template measurements * @@ -90,13 +146,11 @@ void ima_add_violation(struct file *file, const unsigned char *filename, /* can overflow, only indicator */ atomic_long_inc(&ima_htable.violations); - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { + result = ima_alloc_init_template(NULL, file, filename, &entry); + if (result < 0) { result = -ENOMEM; goto err_out; } - memset(&entry->template, 0, sizeof(entry->template)); - strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX); result = ima_store_template(entry, violation, inode, filename); if (result < 0) kfree(entry); @@ -220,34 +274,12 @@ void ima_store_measurement(struct integrity_iint_cache *iint, if (iint->flags & IMA_MEASURED) return; - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { + result = ima_alloc_init_template(iint, file, filename, &entry); + if (result < 0) { integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, op, audit_cause, result, 0); return; } - memset(&entry->template, 0, sizeof(entry->template)); - if (iint->ima_hash->algo != ima_hash_algo) { - struct { - struct ima_digest_data hdr; - char digest[IMA_MAX_DIGEST_SIZE]; - } hash; - - hash.hdr.algo = ima_hash_algo; - result = ima_calc_file_hash(file, &hash.hdr); - if (result) - integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, - filename, "collect_data", "failed", - result, 0); - else - memcpy(entry->template.digest, hash.hdr.digest, - hash.hdr.length); - } else - memcpy(entry->template.digest, iint->ima_hash->digest, - iint->ima_hash->length); - strcpy(entry->template.file_name, - (strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ? - file->f_dentry->d_name.name : filename); result = ima_store_template(entry, violation, inode, filename); if (!result || result == -EEXIST) diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index d42fac308aaa..50e15e6336c4 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -43,34 +43,36 @@ int ima_used_chip; static void __init ima_add_boot_aggregate(void) { struct ima_template_entry *entry; + struct integrity_iint_cache tmp_iint, *iint = &tmp_iint; const char *op = "add_boot_aggregate"; const char *audit_cause = "ENOMEM"; int result = -ENOMEM; - int violation = 1; + int violation = 0; struct { struct ima_digest_data hdr; char digest[TPM_DIGEST_SIZE]; } hash; - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - goto err_out; + memset(iint, 0, sizeof(*iint)); + memset(&hash, 0, sizeof(hash)); + iint->ima_hash = &hash.hdr; + iint->ima_hash->algo = HASH_ALGO_SHA1; + iint->ima_hash->length = SHA1_DIGEST_SIZE; - memset(&entry->template, 0, sizeof(entry->template)); - strncpy(entry->template.file_name, boot_aggregate_name, - IMA_EVENT_NAME_LEN_MAX); if (ima_used_chip) { - violation = 0; - hash.hdr.algo = HASH_ALGO_SHA1; result = ima_calc_boot_aggregate(&hash.hdr); if (result < 0) { audit_cause = "hashing_error"; kfree(entry); goto err_out; } - memcpy(entry->template.digest, hash.hdr.digest, - hash.hdr.length); } + + result = ima_alloc_init_template(iint, NULL, boot_aggregate_name, + &entry); + if (result < 0) + return; + result = ima_store_template(entry, violation, NULL, boot_aggregate_name); if (result < 0) -- cgit v1.2.3 From adf53a778a0a5a5dc9103509da4a9719046e5310 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 7 Jun 2013 12:16:29 +0200 Subject: ima: new templates management mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original 'ima' template is fixed length, containing the filedata hash and pathname. The filedata hash is limited to 20 bytes (md5/sha1). The pathname is a null terminated string, limited to 255 characters. To overcome these limitations and to add additional file metadata, it is necessary to extend the current version of IMA by defining additional templates. The main reason to introduce this feature is that, each time a new template is defined, the functions that generate and display the measurement list would include the code for handling a new format and, thus, would significantly grow over time. This patch set solves this problem by separating the template management from the remaining IMA code. The core of this solution is the definition of two new data structures: a template descriptor, to determine which information should be included in the measurement list, and a template field, to generate and display data of a given type. To define a new template field, developers define the field identifier and implement two functions, init() and show(), respectively to generate and display measurement entries. Initially, this patch set defines the following template fields (support for additional data types will be added later):  - 'd': the digest of the event (i.e. the digest of a measured file),         calculated with the SHA1 or MD5 hash algorithm;  - 'n': the name of the event (i.e. the file name), with size up to         255 bytes;  - 'd-ng': the digest of the event, calculated with an arbitrary hash            algorithm (field format: [:]digest, where the digest            prefix is shown only if the hash algorithm is not SHA1 or MD5);  - 'n-ng': the name of the event, without size limitations. Defining a new template descriptor requires specifying the template format, a string of field identifiers separated by the '|' character. This patch set defines the following template descriptors:  - "ima": its format is 'd|n';  - "ima-ng" (default): its format is 'd-ng|n-ng' Further details about the new template architecture can be found in Documentation/security/IMA-templates.txt. Changelog: - don't defer calling ima_init_template() - Mimi - don't define ima_lookup_template_desc() until used - Mimi - squashed with documentation patch - Mimi Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- Documentation/security/00-INDEX | 2 + Documentation/security/IMA-templates.txt | 87 ++++++++++++++++++++++++ security/integrity/ima/Makefile | 2 +- security/integrity/ima/ima.h | 29 ++++++++ security/integrity/ima/ima_init.c | 4 ++ security/integrity/ima/ima_template.c | 112 +++++++++++++++++++++++++++++++ 6 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 Documentation/security/IMA-templates.txt create mode 100644 security/integrity/ima/ima_template.c (limited to 'security/integrity/ima/ima.h') diff --git a/Documentation/security/00-INDEX b/Documentation/security/00-INDEX index 414235c1fcfc..45c82fd3e9d3 100644 --- a/Documentation/security/00-INDEX +++ b/Documentation/security/00-INDEX @@ -22,3 +22,5 @@ keys.txt - description of the kernel key retention service. tomoyo.txt - documentation on the TOMOYO Linux Security Module. +IMA-templates.txt + - documentation on the template management mechanism for IMA. diff --git a/Documentation/security/IMA-templates.txt b/Documentation/security/IMA-templates.txt new file mode 100644 index 000000000000..a777e5f1df5b --- /dev/null +++ b/Documentation/security/IMA-templates.txt @@ -0,0 +1,87 @@ + IMA Template Management Mechanism + + +==== INTRODUCTION ==== + +The original 'ima' template is fixed length, containing the filedata hash +and pathname. The filedata hash is limited to 20 bytes (md5/sha1). +The pathname is a null terminated string, limited to 255 characters. +To overcome these limitations and to add additional file metadata, it is +necessary to extend the current version of IMA by defining additional +templates. For example, information that could be possibly reported are +the inode UID/GID or the LSM labels either of the inode and of the process +that is accessing it. + +However, the main problem to introduce this feature is that, each time +a new template is defined, the functions that generate and display +the measurements list would include the code for handling a new format +and, thus, would significantly grow over the time. + +The proposed solution solves this problem by separating the template +management from the remaining IMA code. The core of this solution is the +definition of two new data structures: a template descriptor, to determine +which information should be included in the measurement list; a template +field, to generate and display data of a given type. + +Managing templates with these structures is very simple. To support +a new data type, developers define the field identifier and implement +two functions, init() and show(), respectively to generate and display +measurement entries. Defining a new template descriptor requires +specifying the template format, a string of field identifiers separated +by the '|' character. While in the current implementation it is possible +to define new template descriptors only by adding their definition in the +template specific code (ima_template.c), in a future version it will be +possible to register a new template on a running kernel by supplying to IMA +the desired format string. In this version, IMA initializes at boot time +all defined template descriptors by translating the format into an array +of template fields structures taken from the set of the supported ones. + +After the initialization step, IMA will call ima_alloc_init_template() +(new function defined within the patches for the new template management +mechanism) to generate a new measurement entry by using the template +descriptor chosen through the kernel configuration or through the newly +introduced 'ima_template=' kernel command line parameter. It is during this +phase that the advantages of the new architecture are clearly shown: +the latter function will not contain specific code to handle a given template +but, instead, it simply calls the init() method of the template fields +associated to the chosen template descriptor and store the result (pointer +to allocated data and data length) in the measurement entry structure. + +The same mechanism is employed to display measurements entries. +The functions ima[_ascii]_measurements_show() retrieve, for each entry, +the template descriptor used to produce that entry and call the show() +method for each item of the array of template fields structures. + + + +==== SUPPORTED TEMPLATE FIELDS AND DESCRIPTORS ==== + +In the following, there is the list of supported template fields +('': description), that can be used to define new template +descriptors by adding their identifier to the format string +(support for more data types will be added later): + + - 'd': the digest of the event (i.e. the digest of a measured file), + calculated with the SHA1 or MD5 hash algorithm; + - 'n': the name of the event (i.e. the file name), with size up to 255 bytes; + - 'd-ng': the digest of the event, calculated with an arbitrary hash + algorithm (field format: [:]digest, where the digest + prefix is shown only if the hash algorithm is not SHA1 or MD5); + - 'n-ng': the name of the event, without size limitations. + + +Below, there is the list of defined template descriptors: + - "ima": its format is 'd|n'; + - "ima-ng" (default): its format is 'd-ng|n-ng'. + + + +==== USE ==== + +To specify the template descriptor to be used to generate measurement entries, +currently the following methods are supported: + + - select a template descriptor among those supported in the kernel + configuration ('ima-ng' is the default choice); + - specify a template descriptor name from the kernel command line through + the 'ima_template=' parameter. diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 56dfee7cbf61..7fe4ae37dbea 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -6,5 +6,5 @@ obj-$(CONFIG_IMA) += ima.o ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ - ima_policy.o + ima_policy.o ima_template.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index da03d3389619..c85718f1f623 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -36,12 +36,39 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; #define IMA_HASH_BITS 9 #define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS) +#define IMA_TEMPLATE_FIELD_ID_MAX_LEN 16 +#define IMA_TEMPLATE_NUM_FIELDS_MAX 15 + /* set during initialization */ extern int ima_initialized; extern int ima_used_chip; extern int ima_hash_algo; extern int ima_appraise; +/* IMA template field data definition */ +struct ima_field_data { + u8 *data; + u32 len; +}; + +/* IMA template field definition */ +struct ima_template_field { + const char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN]; + int (*field_init) (struct integrity_iint_cache *iint, struct file *file, + const unsigned char *filename, + struct ima_field_data *field_data); + void (*field_show) (struct seq_file *m, enum ima_show_type show, + struct ima_field_data *field_data); +}; + +/* IMA template descriptor definition */ +struct ima_template_desc { + char *name; + char *fmt; + int num_fields; + struct ima_template_field **fields; +}; + /* IMA inode template definition */ struct ima_template_data { u8 digest[IMA_DIGEST_SIZE]; /* sha1/md5 measurement hash */ @@ -79,6 +106,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename, const char *op, const char *cause); int ima_init_crypto(void); +int ima_init_template(void); + /* * used to protect h_table and sha_table */ diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 50e15e6336c4..f84aec5412f3 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -99,6 +99,10 @@ int __init ima_init(void) rc = ima_init_crypto(); if (rc) return rc; + rc = ima_init_template(); + if (rc != 0) + return rc; + ima_add_boot_aggregate(); /* boot aggregate must be first entry */ ima_init_policy(); diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c new file mode 100644 index 000000000000..7e86783ccf51 --- /dev/null +++ b/security/integrity/ima/ima_template.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2013 Politecnico di Torino, Italy + * TORSEC group -- http://security.polito.it + * + * Author: Roberto Sassu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_template.c + * Helpers to manage template descriptors. + */ +#include "ima.h" + +static struct ima_template_desc defined_templates[] = { +}; + +static struct ima_template_field supported_fields[] = { +}; + +static struct ima_template_field *ima_lookup_template_field( + const char *field_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(supported_fields); i++) + if (strncmp(supported_fields[i].field_id, field_id, + IMA_TEMPLATE_FIELD_ID_MAX_LEN) == 0) + return &supported_fields[i]; + return NULL; +} + +static int ima_template_fmt_size(char *template_fmt) +{ + char c; + int template_fmt_len = strlen(template_fmt); + int i = 0, j = 0; + + while (i < template_fmt_len) { + c = template_fmt[i]; + if (c == '|') + j++; + i++; + } + + return j + 1; +} + +static int template_desc_init_fields(char *template_fmt, + struct ima_template_field ***fields, + int *num_fields) +{ + char *c, *template_fmt_ptr = template_fmt; + int template_num_fields = ima_template_fmt_size(template_fmt); + int i, result = 0; + + if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) + return -EINVAL; + + *fields = kzalloc(template_num_fields * sizeof(*fields), GFP_KERNEL); + if (*fields == NULL) { + result = -ENOMEM; + goto out; + } + for (i = 0; (c = strsep(&template_fmt_ptr, "|")) != NULL && + i < template_num_fields; i++) { + struct ima_template_field *f = ima_lookup_template_field(c); + + if (!f) { + result = -ENOENT; + goto out; + } + (*fields)[i] = f; + } + *num_fields = i; + return 0; +out: + kfree(*fields); + *fields = NULL; + return result; +} + +static int init_defined_templates(void) +{ + int i = 0; + int result = 0; + + /* Init defined templates. */ + for (i = 0; i < ARRAY_SIZE(defined_templates); i++) { + struct ima_template_desc *template = &defined_templates[i]; + + result = template_desc_init_fields(template->fmt, + &(template->fields), + &(template->num_fields)); + if (result < 0) + return result; + } + return result; +} + +int ima_init_template(void) +{ + int result; + + result = init_defined_templates(); + if (result < 0) + return result; + + return 0; +} -- cgit v1.2.3 From 3ce1217d6cd5dfa82a9db5c2a999cc1bb01490d9 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 7 Jun 2013 12:16:30 +0200 Subject: ima: define template fields library and new helpers This patch defines a library containing two initial template fields, inode digest (d) and file name (n), the 'ima' template descriptor, whose format is 'd|n', and two helper functions, ima_write_template_field_data() and ima_show_template_field_data(). Changelog: - replace ima_eventname_init() parameter NULL checking with BUG_ON. (suggested by Mimi) - include "new template fields for inode digest (d) and file name (n)" definitions to fix a compiler warning. - Mimi - unnecessary to prefix static function names with 'ima_'. remove prefix to resolve Lindent formatting changes. - Mimi - abbreviated/removed inline comments - Mimi - always send the template field length - Mimi Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/Makefile | 2 +- security/integrity/ima/ima.h | 5 + security/integrity/ima/ima_fs.c | 4 +- security/integrity/ima/ima_template.c | 15 ++- security/integrity/ima/ima_template_lib.c | 193 ++++++++++++++++++++++++++++++ security/integrity/ima/ima_template_lib.h | 31 +++++ 6 files changed, 242 insertions(+), 8 deletions(-) create mode 100644 security/integrity/ima/ima_template_lib.c create mode 100644 security/integrity/ima/ima_template_lib.h (limited to 'security/integrity/ima/ima.h') diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 7fe4ae37dbea..d79263d2fdbf 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -6,5 +6,5 @@ obj-$(CONFIG_IMA) += ima.o ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ - ima_policy.o ima_template.o + ima_policy.o ima_template.o ima_template_lib.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c85718f1f623..e1f081d65dbf 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -39,6 +39,9 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; #define IMA_TEMPLATE_FIELD_ID_MAX_LEN 16 #define IMA_TEMPLATE_NUM_FIELDS_MAX 15 +#define IMA_TEMPLATE_IMA_NAME "ima" +#define IMA_TEMPLATE_IMA_FMT "d|n" + /* set during initialization */ extern int ima_initialized; extern int ima_used_chip; @@ -105,6 +108,8 @@ int __init ima_calc_boot_aggregate(struct ima_digest_data *hash); void ima_add_violation(struct file *file, const unsigned char *filename, const char *op, const char *cause); int ima_init_crypto(void); +void ima_putc(struct seq_file *m, void *data, int datalen); +void ima_print_digest(struct seq_file *m, u8 *digest, int size); int ima_init_template(void); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index c35cfb5b3c5e..414862e1904b 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -99,7 +99,7 @@ static void ima_measurements_stop(struct seq_file *m, void *v) { } -static void ima_putc(struct seq_file *m, void *data, int datalen) +void ima_putc(struct seq_file *m, void *data, int datalen) { while (datalen--) seq_putc(m, *(char *)data++); @@ -167,7 +167,7 @@ static const struct file_operations ima_measurements_ops = { .release = seq_release, }; -static void ima_print_digest(struct seq_file *m, u8 *digest, int size) +void ima_print_digest(struct seq_file *m, u8 *digest, int size) { int i; diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index 7e86783ccf51..810042264396 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -13,15 +13,20 @@ * Helpers to manage template descriptors. */ #include "ima.h" +#include "ima_template_lib.h" static struct ima_template_desc defined_templates[] = { + {.name = IMA_TEMPLATE_IMA_NAME,.fmt = IMA_TEMPLATE_IMA_FMT}, }; static struct ima_template_field supported_fields[] = { + {.field_id = "d",.field_init = ima_eventdigest_init, + .field_show = ima_show_template_digest}, + {.field_id = "n",.field_init = ima_eventname_init, + .field_show = ima_show_template_string}, }; -static struct ima_template_field *ima_lookup_template_field( - const char *field_id) +static struct ima_template_field *lookup_template_field(const char *field_id) { int i; @@ -32,7 +37,7 @@ static struct ima_template_field *ima_lookup_template_field( return NULL; } -static int ima_template_fmt_size(char *template_fmt) +static int template_fmt_size(char *template_fmt) { char c; int template_fmt_len = strlen(template_fmt); @@ -53,7 +58,7 @@ static int template_desc_init_fields(char *template_fmt, int *num_fields) { char *c, *template_fmt_ptr = template_fmt; - int template_num_fields = ima_template_fmt_size(template_fmt); + int template_num_fields = template_fmt_size(template_fmt); int i, result = 0; if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) @@ -66,7 +71,7 @@ static int template_desc_init_fields(char *template_fmt, } for (i = 0; (c = strsep(&template_fmt_ptr, "|")) != NULL && i < template_num_fields; i++) { - struct ima_template_field *f = ima_lookup_template_field(c); + struct ima_template_field *f = lookup_template_field(c); if (!f) { result = -ENOENT; diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c new file mode 100644 index 000000000000..e13fc7c04ebc --- /dev/null +++ b/security/integrity/ima/ima_template_lib.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2013 Politecnico di Torino, Italy + * TORSEC group -- http://security.polito.it + * + * Author: Roberto Sassu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_template_lib.c + * Library of supported template fields. + */ +#include "ima_template_lib.h" + +enum data_formats { DATA_FMT_DIGEST = 0, DATA_FMT_EVENT_NAME, DATA_FMT_STRING }; +static int ima_write_template_field_data(const void *data, const u32 datalen, + enum data_formats datafmt, + struct ima_field_data *field_data) +{ + u8 *buf, *buf_ptr; + u32 buflen; + + switch (datafmt) { + case DATA_FMT_EVENT_NAME: + buflen = IMA_EVENT_NAME_LEN_MAX + 1; + break; + case DATA_FMT_STRING: + buflen = datalen + 1; + break; + default: + buflen = datalen; + } + + buf = kzalloc(buflen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, data, datalen); + + /* + * Replace all space characters with underscore for event names and + * strings. This avoid that, during the parsing of a measurements list, + * filenames with spaces or that end with the suffix ' (deleted)' are + * split into multiple template fields (the space is the delimitator + * character for measurements lists in ASCII format). + */ + if (datafmt == DATA_FMT_EVENT_NAME || datafmt == DATA_FMT_STRING) { + for (buf_ptr = buf; buf_ptr - buf < datalen; buf_ptr++) + if (*buf_ptr == ' ') + *buf_ptr = '_'; + } + + field_data->data = buf; + field_data->len = buflen; + return 0; +} + +static void ima_show_template_data_ascii(struct seq_file *m, + enum ima_show_type show, + enum data_formats datafmt, + struct ima_field_data *field_data) +{ + switch (datafmt) { + case DATA_FMT_DIGEST: + ima_print_digest(m, field_data->data, field_data->len); + break; + case DATA_FMT_STRING: + seq_printf(m, "%s", field_data->data); + break; + default: + break; + } +} + +static void ima_show_template_data_binary(struct seq_file *m, + enum ima_show_type show, + enum data_formats datafmt, + struct ima_field_data *field_data) +{ + ima_putc(m, &field_data->len, sizeof(u32)); + if (!field_data->len) + return; + ima_putc(m, field_data->data, field_data->len); +} + +static void ima_show_template_field_data(struct seq_file *m, + enum ima_show_type show, + enum data_formats datafmt, + struct ima_field_data *field_data) +{ + switch (show) { + case IMA_SHOW_ASCII: + ima_show_template_data_ascii(m, show, datafmt, field_data); + break; + case IMA_SHOW_BINARY: + ima_show_template_data_binary(m, show, datafmt, field_data); + break; + default: + break; + } +} + +void ima_show_template_digest(struct seq_file *m, enum ima_show_type show, + struct ima_field_data *field_data) +{ + ima_show_template_field_data(m, show, DATA_FMT_DIGEST, field_data); +} + +void ima_show_template_string(struct seq_file *m, enum ima_show_type show, + struct ima_field_data *field_data) +{ + ima_show_template_field_data(m, show, DATA_FMT_STRING, field_data); +} + +/* + * This function writes the digest of an event. + */ +int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file, + const unsigned char *filename, + struct ima_field_data *field_data) +{ + struct { + struct ima_digest_data hdr; + char digest[IMA_MAX_DIGEST_SIZE]; + } hash; + u8 *cur_digest = hash.hdr.digest; + u32 cur_digestsize = IMA_DIGEST_SIZE; + struct inode *inode; + int result; + + memset(&hash, 0, sizeof(hash)); + + if (!iint) /* recording a violation. */ + goto out; + + if (iint->ima_hash->algo == ima_hash_algo) { + cur_digest = iint->ima_hash->digest; + cur_digestsize = iint->ima_hash->length; + goto out; + } + + if (!file) /* missing info to re-calculate the digest */ + return -EINVAL; + + inode = file_inode(file); + hash.hdr.algo = ima_hash_algo; + result = ima_calc_file_hash(file, &hash.hdr); + if (result) { + integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, + filename, "collect_data", + "failed", result, 0); + return result; + } +out: + return ima_write_template_field_data(cur_digest, cur_digestsize, + DATA_FMT_DIGEST, field_data); +} + +/* + * This function writes the name of an event. + */ +int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file, + const unsigned char *filename, + struct ima_field_data *field_data) +{ + const char *cur_filename = NULL; + u32 cur_filename_len = 0; + + BUG_ON(filename == NULL && file == NULL); + + if (filename) { + cur_filename = filename; + cur_filename_len = strlen(filename); + + if (cur_filename_len <= IMA_EVENT_NAME_LEN_MAX) + goto out; + } + + if (file) { + cur_filename = file->f_dentry->d_name.name; + cur_filename_len = strlen(cur_filename); + } else + /* + * Truncate filename if the latter is too long and + * the file descriptor is not available. + */ + cur_filename_len = IMA_EVENT_NAME_LEN_MAX; +out: + return ima_write_template_field_data(cur_filename, cur_filename_len, + DATA_FMT_EVENT_NAME, field_data); +} diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h new file mode 100644 index 000000000000..2cecc83a928d --- /dev/null +++ b/security/integrity/ima/ima_template_lib.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2013 Politecnico di Torino, Italy + * TORSEC group -- http://security.polito.it + * + * Author: Roberto Sassu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_template_lib.h + * Header for the library of supported template fields. + */ +#ifndef __LINUX_IMA_TEMPLATE_LIB_H +#define __LINUX_IMA_TEMPLATE_LIB_H + +#include +#include "ima.h" + +void ima_show_template_digest(struct seq_file *m, enum ima_show_type show, + struct ima_field_data *field_data); +void ima_show_template_string(struct seq_file *m, enum ima_show_type show, + struct ima_field_data *field_data); +int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file, + const unsigned char *filename, + struct ima_field_data *field_data); +int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file, + const unsigned char *filename, + struct ima_field_data *field_data); +#endif /* __LINUX_IMA_TEMPLATE_LIB_H */ -- cgit v1.2.3 From a71dc65d30a472409f05d247f4eab91b14acf2f5 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 7 Jun 2013 12:16:33 +0200 Subject: ima: switch to new template management mechanism This patch performs the switch to the new template mechanism by modifying the functions ima_alloc_init_template(), ima_measurements_show() and ima_ascii_measurements_show(). The old function ima_template_show() was removed as it is no longer needed. Also, if the template descriptor used to generate a measurement entry is not 'ima', the whole length of field data stored for an entry is provided before the data itself through the binary_runtime_measurement interface. Changelog: - unnecessary to use strncmp() (Mimi Zohar) - create new variable 'field' in ima_alloc_init_template() (Roberto Sassu) - use GFP_NOFS flag in ima_alloc_init_template() (Roberto Sassu) - new variable 'num_fields' in ima_store_template() (Roberto Sassu, proposed by Mimi Zohar) - rename ima_calc_buffer_hash/template_hash() to ima_calc_field_array_hash(), something more generic (Mimi, requested by Dmitry) - sparse error fix - Fengguang Wu - fix lindent warnings - always include the field length in the template data length - include the template field length variable size in the template data length - include both the template field data and field length in the template digest calculation. Simplifies verifying the template digest. (Mimi) Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 19 ++++----- security/integrity/ima/ima_api.c | 75 ++++++++++++----------------------- security/integrity/ima/ima_crypto.c | 34 ++++++++++++---- security/integrity/ima/ima_fs.c | 54 ++++++++++++------------- security/integrity/ima/ima_template.c | 22 ++++++++++ 5 files changed, 107 insertions(+), 97 deletions(-) (limited to 'security/integrity/ima/ima.h') diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index e1f081d65dbf..72d013e190b1 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -72,17 +72,11 @@ struct ima_template_desc { struct ima_template_field **fields; }; -/* IMA inode template definition */ -struct ima_template_data { - u8 digest[IMA_DIGEST_SIZE]; /* sha1/md5 measurement hash */ - char file_name[IMA_EVENT_NAME_LEN_MAX + 1]; /* name + \0 */ -}; - struct ima_template_entry { u8 digest[TPM_DIGEST_SIZE]; /* sha1 or md5 measurement hash */ - const char *template_name; - int template_len; - struct ima_template_data template; + struct ima_template_desc *template_desc; /* template descriptor */ + u32 template_data_len; + struct ima_field_data template_data[0]; /* template related data */ }; struct ima_queue_entry { @@ -102,14 +96,16 @@ 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_buffer_hash(const void *data, int len, - struct ima_digest_data *hash); +int ima_calc_field_array_hash(struct ima_field_data *field_data, int num_fields, + struct ima_digest_data *hash); int __init ima_calc_boot_aggregate(struct ima_digest_data *hash); void ima_add_violation(struct file *file, const unsigned char *filename, const char *op, const char *cause); int ima_init_crypto(void); void ima_putc(struct seq_file *m, void *data, int datalen); void ima_print_digest(struct seq_file *m, u8 *digest, int size); +struct ima_template_desc *ima_template_desc_current(void); +int ima_init_template(void); int ima_init_template(void); @@ -146,7 +142,6 @@ int ima_alloc_init_template(struct integrity_iint_cache *iint, struct ima_template_entry **entry); int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode, const unsigned char *filename); -void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show); const char *ima_d_path(struct path *path, char **pathbuf); /* rbtree tree calls to lookup, insert, delete diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 29dd43de823a..baa348179527 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -21,8 +21,6 @@ #include #include "ima.h" -static const char *IMA_TEMPLATE_NAME = "ima"; - /* * ima_alloc_init_template - create and initialize a new template entry */ @@ -30,52 +28,32 @@ int ima_alloc_init_template(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct ima_template_entry **entry) { - struct ima_template_entry *e; - int result = 0; + struct ima_template_desc *template_desc = ima_template_desc_current(); + int i, result = 0; - e = kzalloc(sizeof(**entry), GFP_NOFS); - if (!e) + *entry = kzalloc(sizeof(**entry) + template_desc->num_fields * + sizeof(struct ima_field_data), GFP_NOFS); + if (!*entry) return -ENOMEM; - memset(&(e)->template, 0, sizeof(e->template)); - if (!iint) /* IMA measurement violation entry */ - goto out; - - if (iint->ima_hash->algo != ima_hash_algo) { - struct inode *inode; - struct { - struct ima_digest_data hdr; - char digest[IMA_MAX_DIGEST_SIZE]; - } hash; + for (i = 0; i < template_desc->num_fields; i++) { + struct ima_template_field *field = template_desc->fields[i]; + u32 len; - if (!file) { - result = -EINVAL; - goto out_free; - } + result = field->field_init(iint, file, filename, + &((*entry)->template_data[i])); + if (result != 0) + goto out; - inode = file_inode(file); - hash.hdr.algo = ima_hash_algo; - hash.hdr.length = SHA1_DIGEST_SIZE; - result = ima_calc_file_hash(file, &hash.hdr); - if (result) { - integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, - filename, "collect_data", - "failed", result, 0); - goto out_free; - } else - memcpy(e->template.digest, hash.hdr.digest, - hash.hdr.length); - } else - memcpy(e->template.digest, iint->ima_hash->digest, - iint->ima_hash->length); -out: - strcpy(e->template.file_name, - (strlen(filename) > IMA_EVENT_NAME_LEN_MAX && file != NULL) ? - file->f_dentry->d_name.name : filename); - *entry = e; + len = (*entry)->template_data[i].len; + (*entry)->template_data_len += sizeof(len); + (*entry)->template_data_len += len; + } + (*entry)->template_desc = template_desc; return 0; -out_free: - kfree(e); +out: + kfree(*entry); + *entry = NULL; return result; } @@ -101,24 +79,23 @@ int ima_store_template(struct ima_template_entry *entry, { const char *op = "add_template_measure"; const char *audit_cause = "hashing_error"; + char *template_name = entry->template_desc->name; int result; struct { struct ima_digest_data hdr; char digest[TPM_DIGEST_SIZE]; } hash; - memset(entry->digest, 0, sizeof(entry->digest)); - entry->template_name = IMA_TEMPLATE_NAME; - entry->template_len = sizeof(entry->template); - if (!violation) { + int num_fields = entry->template_desc->num_fields; + /* this function uses default algo */ hash.hdr.algo = HASH_ALGO_SHA1; - result = ima_calc_buffer_hash(&entry->template, - entry->template_len, &hash.hdr); + result = ima_calc_field_array_hash(&entry->template_data[0], + num_fields, &hash.hdr); if (result < 0) { integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, - entry->template_name, op, + template_name, op, audit_cause, result, 0); return result; } diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 22be23f13b3d..676e0292dfec 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -137,26 +137,46 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) } /* - * Calculate the hash of a given buffer + * Calculate the hash of template data */ -static int ima_calc_buffer_hash_tfm(const void *buf, int len, - struct ima_digest_data *hash, - struct crypto_shash *tfm) +static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data, + int num_fields, + struct ima_digest_data *hash, + struct crypto_shash *tfm) { struct { struct shash_desc shash; char ctx[crypto_shash_descsize(tfm)]; } desc; + int rc, i; desc.shash.tfm = tfm; desc.shash.flags = 0; hash->length = crypto_shash_digestsize(tfm); - return crypto_shash_digest(&desc.shash, buf, len, hash->digest); + rc = crypto_shash_init(&desc.shash); + if (rc != 0) + return rc; + + for (i = 0; i < num_fields; i++) { + rc = crypto_shash_update(&desc.shash, + (const u8 *) &field_data[i].len, + sizeof(field_data[i].len)); + rc = crypto_shash_update(&desc.shash, field_data[i].data, + field_data[i].len); + if (rc) + break; + } + + if (!rc) + rc = crypto_shash_final(&desc.shash, hash->digest); + + return rc; } -int ima_calc_buffer_hash(const void *buf, int len, struct ima_digest_data *hash) +int ima_calc_field_array_hash(struct ima_field_data *field_data, int num_fields, + struct ima_digest_data *hash) { struct crypto_shash *tfm; int rc; @@ -165,7 +185,7 @@ int ima_calc_buffer_hash(const void *buf, int len, struct ima_digest_data *hash) if (IS_ERR(tfm)) return PTR_ERR(tfm); - rc = ima_calc_buffer_hash_tfm(buf, len, hash, tfm); + rc = ima_calc_field_array_hash_tfm(field_data, num_fields, hash, tfm); ima_free_tfm(tfm); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 414862e1904b..d47a7c86a21d 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -110,6 +110,7 @@ void ima_putc(struct seq_file *m, void *data, int datalen) * char[20]=template digest * 32bit-le=template name size * char[n]=template name + * [eventdata length] * eventdata[n]=template specific data */ static int ima_measurements_show(struct seq_file *m, void *v) @@ -119,6 +120,7 @@ static int ima_measurements_show(struct seq_file *m, void *v) struct ima_template_entry *e; int namelen; u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX; + int i; /* get entry */ e = qe->entry; @@ -136,15 +138,22 @@ static int ima_measurements_show(struct seq_file *m, void *v) ima_putc(m, e->digest, TPM_DIGEST_SIZE); /* 3rd: template name size */ - namelen = strlen(e->template_name); + namelen = strlen(e->template_desc->name); ima_putc(m, &namelen, sizeof namelen); /* 4th: template name */ - ima_putc(m, (void *)e->template_name, namelen); + ima_putc(m, e->template_desc->name, namelen); + + /* 5th: template length (except for 'ima' template) */ + if (strcmp(e->template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) + ima_putc(m, &e->template_data_len, + sizeof(e->template_data_len)); - /* 5th: template specific data */ - ima_template_show(m, (struct ima_template_data *)&e->template, - IMA_SHOW_BINARY); + /* 6th: template specific data */ + for (i = 0; i < e->template_desc->num_fields; i++) { + e->template_desc->fields[i]->field_show(m, IMA_SHOW_BINARY, + &e->template_data[i]); + } return 0; } @@ -175,33 +184,13 @@ void ima_print_digest(struct seq_file *m, u8 *digest, int size) seq_printf(m, "%02x", *(digest + i)); } -void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show) -{ - struct ima_template_data *entry = e; - int namelen; - - switch (show) { - case IMA_SHOW_ASCII: - ima_print_digest(m, entry->digest, IMA_DIGEST_SIZE); - seq_printf(m, " %s\n", entry->file_name); - break; - case IMA_SHOW_BINARY: - ima_putc(m, entry->digest, IMA_DIGEST_SIZE); - - namelen = strlen(entry->file_name); - ima_putc(m, &namelen, sizeof namelen); - ima_putc(m, entry->file_name, namelen); - default: - break; - } -} - /* print in ascii */ static int ima_ascii_measurements_show(struct seq_file *m, void *v) { /* the list never shrinks, so we don't need a lock here */ struct ima_queue_entry *qe = v; struct ima_template_entry *e; + int i; /* get entry */ e = qe->entry; @@ -215,11 +204,18 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) ima_print_digest(m, e->digest, TPM_DIGEST_SIZE); /* 3th: template name */ - seq_printf(m, " %s ", e->template_name); + seq_printf(m, " %s", e->template_desc->name); /* 4th: template specific data */ - ima_template_show(m, (struct ima_template_data *)&e->template, - IMA_SHOW_ASCII); + for (i = 0; i < e->template_desc->num_fields; i++) { + seq_puts(m, " "); + if (e->template_data[i].len == 0) + continue; + + e->template_desc->fields[i]->field_show(m, IMA_SHOW_ASCII, + &e->template_data[i]); + } + seq_puts(m, "\n"); return 0; } diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index bf38d1af3cfd..1c4cf194592c 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -31,6 +31,20 @@ static struct ima_template_field supported_fields[] = { .field_show = ima_show_template_string}, }; +static struct ima_template_desc *ima_template; + +static struct ima_template_desc *lookup_template_desc(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(defined_templates); i++) { + if (strcmp(defined_templates[i].name, name) == 0) + return defined_templates + i; + } + + return NULL; +} + static struct ima_template_field *lookup_template_field(const char *field_id) { int i; @@ -110,6 +124,14 @@ static int init_defined_templates(void) return result; } +struct ima_template_desc *ima_template_desc_current(void) +{ + if (!ima_template) + ima_template = lookup_template_desc(IMA_TEMPLATE_IMA_NAME); + + return ima_template; +} + int ima_init_template(void) { int result; -- cgit v1.2.3 From bcbc9b0cf6d8f340a1d166e414f4612b353f7a9b Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 23 Jul 2013 11:15:00 -0400 Subject: ima: extend the measurement list to include the file signature This patch defines a new template called 'ima-sig', which includes the file signature in the template data, in addition to the file's digest and pathname. A template is composed of a set of fields. Associated with each field is an initialization and display function. This patch defines a new template field called 'sig', the initialization function ima_eventsig_init(), and the display function ima_show_template_sig(). This patch modifies the .field_init() function definition to include the 'security.ima' extended attribute and length. Changelog: - remove unused code (Dmitry Kasatkin) - avoid calling ima_write_template_field_data() unnecesarily (Roberto Sassu) - rename DATA_FMT_SIG to DATA_FMT_HEX - cleanup ima_eventsig_init() based on Roberto's comments Signed-off-by: Mimi Zohar Signed-off-by: Dmitry Kasatkin Signed-off-by: Roberto Sassu --- security/integrity/ima/Kconfig | 3 +++ security/integrity/ima/ima.h | 10 +++++--- security/integrity/ima/ima_api.c | 14 ++++++++---- security/integrity/ima/ima_init.c | 2 +- security/integrity/ima/ima_main.c | 3 ++- security/integrity/ima/ima_template.c | 3 +++ security/integrity/ima/ima_template_lib.c | 38 +++++++++++++++++++++++++++++-- security/integrity/ima/ima_template_lib.h | 12 +++++++++- 8 files changed, 73 insertions(+), 12 deletions(-) (limited to 'security/integrity/ima/ima.h') diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 351a58ed56ab..81a27971d884 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -63,6 +63,8 @@ choice bool "ima" config IMA_NG_TEMPLATE bool "ima-ng (default)" + config IMA_SIG_TEMPLATE + bool "ima-sig" endchoice config IMA_DEFAULT_TEMPLATE @@ -70,6 +72,7 @@ config IMA_DEFAULT_TEMPLATE depends on IMA default "ima" if IMA_TEMPLATE default "ima-ng" if IMA_NG_TEMPLATE + default "ima-sig" if IMA_SIG_TEMPLATE choice prompt "Default integrity hash algorithm" diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 72d013e190b1..bf03c6a16cc8 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -59,7 +59,8 @@ struct ima_template_field { const char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN]; int (*field_init) (struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, - struct ima_field_data *field_data); + struct evm_ima_xattr_data *xattr_value, + int xattr_len, struct ima_field_data *field_data); void (*field_show) (struct seq_file *m, enum ima_show_type show, struct ima_field_data *field_data); }; @@ -134,12 +135,15 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, struct evm_ima_xattr_data **xattr_value, int *xattr_len); void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, - const unsigned char *filename); + const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, + int xattr_len); void ima_audit_measurement(struct integrity_iint_cache *iint, const unsigned char *filename); int ima_alloc_init_template(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, - struct ima_template_entry **entry); + struct evm_ima_xattr_data *xattr_value, + int xattr_len, struct ima_template_entry **entry); int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode, const unsigned char *filename); const char *ima_d_path(struct path *path, char **pathbuf); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 5fcc80695d87..0e7540863fc2 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -26,7 +26,8 @@ */ int ima_alloc_init_template(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, - struct ima_template_entry **entry) + struct evm_ima_xattr_data *xattr_value, + int xattr_len, struct ima_template_entry **entry) { struct ima_template_desc *template_desc = ima_template_desc_current(); int i, result = 0; @@ -41,6 +42,7 @@ int ima_alloc_init_template(struct integrity_iint_cache *iint, u32 len; result = field->field_init(iint, file, filename, + xattr_value, xattr_len, &((*entry)->template_data[i])); if (result != 0) goto out; @@ -123,7 +125,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename, /* can overflow, only indicator */ atomic_long_inc(&ima_htable.violations); - result = ima_alloc_init_template(NULL, file, filename, &entry); + result = ima_alloc_init_template(NULL, file, filename, + NULL, 0, &entry); if (result < 0) { result = -ENOMEM; goto err_out; @@ -239,7 +242,9 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, * Must be called with iint->mutex held. */ void ima_store_measurement(struct integrity_iint_cache *iint, - struct file *file, const unsigned char *filename) + struct file *file, const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, + int xattr_len) { const char *op = "add_template_measure"; const char *audit_cause = "ENOMEM"; @@ -251,7 +256,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint, if (iint->flags & IMA_MEASURED) return; - result = ima_alloc_init_template(iint, file, filename, &entry); + result = ima_alloc_init_template(iint, file, filename, + xattr_value, xattr_len, &entry); if (result < 0) { integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, op, audit_cause, result, 0); diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index f84aec5412f3..15f34bd40abe 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -69,7 +69,7 @@ static void __init ima_add_boot_aggregate(void) } result = ima_alloc_init_template(iint, NULL, boot_aggregate_name, - &entry); + NULL, 0, &entry); if (result < 0) return; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 14d4cb557894..149ee1119f87 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -225,7 +225,8 @@ static int process_measurement(struct file *file, const char *filename, pathname = (const char *)file->f_dentry->d_name.name; if (action & IMA_MEASURE) - ima_store_measurement(iint, file, pathname); + ima_store_measurement(iint, file, pathname, + xattr_value, xattr_len); if (action & IMA_APPRAISE_SUBMASK) rc = ima_appraise_measurement(_func, iint, file, pathname, xattr_value, xattr_len); diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index 000221419f6c..4e5da990630b 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -20,6 +20,7 @@ static struct ima_template_desc defined_templates[] = { {.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT}, {.name = "ima-ng",.fmt = "d-ng|n-ng"}, + {.name = "ima-sig",.fmt = "d-ng|n-ng|sig"}, }; static struct ima_template_field supported_fields[] = { @@ -31,6 +32,8 @@ static struct ima_template_field supported_fields[] = { .field_show = ima_show_template_digest_ng}, {.field_id = "n-ng",.field_init = ima_eventname_ng_init, .field_show = ima_show_template_string}, + {.field_id = "sig",.field_init = ima_eventsig_init, + .field_show = ima_show_template_sig}, }; static struct ima_template_desc *ima_template; diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index 7d841448f246..6d66ad6ed265 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -28,7 +28,8 @@ enum data_formats { DATA_FMT_DIGEST = 0, DATA_FMT_DIGEST_WITH_ALGO, DATA_FMT_EVENT_NAME, - DATA_FMT_STRING + DATA_FMT_STRING, + DATA_FMT_HEX }; static int ima_write_template_field_data(const void *data, const u32 datalen, @@ -90,6 +91,9 @@ static void ima_show_template_data_ascii(struct seq_file *m, buf_ptr += 2; buflen -= buf_ptr - field_data->data; case DATA_FMT_DIGEST: + case DATA_FMT_HEX: + if (!buflen) + break; ima_print_digest(m, buf_ptr, buflen); break; case DATA_FMT_STRING: @@ -147,6 +151,12 @@ void ima_show_template_string(struct seq_file *m, enum ima_show_type show, ima_show_template_field_data(m, show, DATA_FMT_STRING, field_data); } +void ima_show_template_sig(struct seq_file *m, enum ima_show_type show, + struct ima_field_data *field_data) +{ + ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data); +} + static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo, struct ima_field_data *field_data, bool size_limit) @@ -190,6 +200,7 @@ static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo, */ int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, int xattr_len, struct ima_field_data *field_data) { struct { @@ -237,7 +248,8 @@ out: */ int ima_eventdigest_ng_init(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, - struct ima_field_data *field_data) + struct evm_ima_xattr_data *xattr_value, + int xattr_len, struct ima_field_data *field_data) { u8 *cur_digest = NULL, hash_algo = HASH_ALGO__LAST; u32 cur_digestsize = 0; @@ -295,6 +307,7 @@ out: */ int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, int xattr_len, struct ima_field_data *field_data) { return ima_eventname_init_common(iint, file, filename, @@ -306,8 +319,29 @@ int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file, */ int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, int xattr_len, struct ima_field_data *field_data) { return ima_eventname_init_common(iint, file, filename, field_data, false); } + +/* + * ima_eventsig_init - include the file signature as part of the template data + */ +int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file, + const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, int xattr_len, + struct ima_field_data *field_data) +{ + enum data_formats fmt = DATA_FMT_HEX; + int rc = 0; + + if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG)) + goto out; + + rc = ima_write_template_field_data(xattr_value, xattr_len, fmt, + field_data); +out: + return rc; +} diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h index 16c5e7810234..63f6b52cb1c2 100644 --- a/security/integrity/ima/ima_template_lib.h +++ b/security/integrity/ima/ima_template_lib.h @@ -24,16 +24,26 @@ void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show, struct ima_field_data *field_data); void ima_show_template_string(struct seq_file *m, enum ima_show_type show, struct ima_field_data *field_data); +void ima_show_template_sig(struct seq_file *m, enum ima_show_type show, + struct ima_field_data *field_data); int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, int xattr_len, struct ima_field_data *field_data); int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, int xattr_len, struct ima_field_data *field_data); int ima_eventdigest_ng_init(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, - struct ima_field_data *field_data); + struct evm_ima_xattr_data *xattr_value, + int xattr_len, struct ima_field_data *field_data); int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, int xattr_len, struct ima_field_data *field_data); +int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file, + const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, int xattr_len, + struct ima_field_data *field_data); #endif /* __LINUX_IMA_TEMPLATE_LIB_H */ -- cgit v1.2.3