diff options
Diffstat (limited to 'security/integrity/ima/ima_crypto.c')
| -rw-r--r-- | security/integrity/ima/ima_crypto.c | 253 | 
1 files changed, 205 insertions, 48 deletions
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 9b3ade7468b..ccd0ac8fa9a 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -10,60 +10,131 @@   * the Free Software Foundation, version 2 of the License.   *   * File: ima_crypto.c - * 	Calculates md5/sha1 file hash, template hash, boot-aggreate hash + *	Calculates md5/sha1 file hash, template hash, boot-aggreate hash   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/kernel.h>  #include <linux/file.h>  #include <linux/crypto.h>  #include <linux/scatterlist.h>  #include <linux/err.h>  #include <linux/slab.h> +#include <crypto/hash.h> +#include <crypto/hash_info.h>  #include "ima.h" -static int init_desc(struct hash_desc *desc) +static struct crypto_shash *ima_shash_tfm; + +/** + * ima_kernel_read - read file content + * + * This is a function for reading file content instead of kernel_read(). + * It does not perform locking checks to ensure it cannot be blocked. + * It does not perform security checks because it is irrelevant for IMA. + * + */ +static int ima_kernel_read(struct file *file, loff_t offset, +			   char *addr, unsigned long count)  { -	int rc; +	mm_segment_t old_fs; +	char __user *buf = addr; +	ssize_t ret; + +	if (!(file->f_mode & FMODE_READ)) +		return -EBADF; +	if (!file->f_op->read && !file->f_op->aio_read) +		return -EINVAL; + +	old_fs = get_fs(); +	set_fs(get_ds()); +	if (file->f_op->read) +		ret = file->f_op->read(file, buf, count, &offset); +	else +		ret = do_sync_read(file, buf, count, &offset); +	set_fs(old_fs); +	return ret; +} -	desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC); -	if (IS_ERR(desc->tfm)) { -		pr_info("IMA: failed to load %s transform: %ld\n", -			ima_hash, PTR_ERR(desc->tfm)); -		rc = PTR_ERR(desc->tfm); +int ima_init_crypto(void) +{ +	long rc; + +	ima_shash_tfm = crypto_alloc_shash(hash_algo_name[ima_hash_algo], 0, 0); +	if (IS_ERR(ima_shash_tfm)) { +		rc = PTR_ERR(ima_shash_tfm); +		pr_err("Can not allocate %s (reason: %ld)\n", +		       hash_algo_name[ima_hash_algo], rc);  		return rc;  	} -	desc->flags = 0; -	rc = crypto_hash_init(desc); -	if (rc) -		crypto_free_hash(desc->tfm); -	return rc; +	return 0; +} + +static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo) +{ +	struct crypto_shash *tfm = ima_shash_tfm; +	int rc; + +	if (algo != ima_hash_algo && algo < HASH_ALGO__LAST) { +		tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0); +		if (IS_ERR(tfm)) { +			rc = PTR_ERR(tfm); +			pr_err("Can not allocate %s (reason: %d)\n", +			       hash_algo_name[algo], rc); +		} +	} +	return tfm; +} + +static void ima_free_tfm(struct crypto_shash *tfm) +{ +	if (tfm != ima_shash_tfm) +		crypto_free_shash(tfm);  }  /*   * Calculate the MD5/SHA1 file digest   */ -int ima_calc_hash(struct file *file, char *digest) +static int ima_calc_file_hash_tfm(struct file *file, +				  struct ima_digest_data *hash, +				  struct crypto_shash *tfm)  { -	struct hash_desc desc; -	struct scatterlist sg[1];  	loff_t i_size, offset = 0;  	char *rbuf; -	int rc; +	int rc, read = 0; +	struct { +		struct shash_desc shash; +		char ctx[crypto_shash_descsize(tfm)]; +	} desc; -	rc = init_desc(&desc); +	desc.shash.tfm = tfm; +	desc.shash.flags = 0; + +	hash->length = crypto_shash_digestsize(tfm); + +	rc = crypto_shash_init(&desc.shash);  	if (rc != 0)  		return rc; -	rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL); -	if (!rbuf) { -		rc = -ENOMEM; +	i_size = i_size_read(file_inode(file)); + +	if (i_size == 0)  		goto out; + +	rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL); +	if (!rbuf) +		return -ENOMEM; + +	if (!(file->f_mode & FMODE_READ)) { +		file->f_mode |= FMODE_READ; +		read = 1;  	} -	i_size = i_size_read(file->f_dentry->d_inode); +  	while (offset < i_size) {  		int rbuf_len; -		rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE); +		rbuf_len = ima_kernel_read(file, offset, rbuf, PAGE_SIZE);  		if (rbuf_len < 0) {  			rc = rbuf_len;  			break; @@ -71,38 +142,103 @@ int ima_calc_hash(struct file *file, char *digest)  		if (rbuf_len == 0)  			break;  		offset += rbuf_len; -		sg_init_one(sg, rbuf, rbuf_len); -		rc = crypto_hash_update(&desc, sg, rbuf_len); +		rc = crypto_shash_update(&desc.shash, rbuf, rbuf_len);  		if (rc)  			break;  	} +	if (read) +		file->f_mode &= ~FMODE_READ;  	kfree(rbuf); -	if (!rc) -		rc = crypto_hash_final(&desc, digest);  out: -	crypto_free_hash(desc.tfm); +	if (!rc) +		rc = crypto_shash_final(&desc.shash, hash->digest); +	return rc; +} + +int ima_calc_file_hash(struct file *file, 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); + +	rc = ima_calc_file_hash_tfm(file, hash, tfm); + +	ima_free_tfm(tfm); +  	return rc;  }  /* - * Calculate the hash of a given template + * Calculate the hash of template data   */ -int ima_calc_template_hash(int template_len, void *template, char *digest) +static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data, +					 struct ima_template_desc *td, +					 int num_fields, +					 struct ima_digest_data *hash, +					 struct crypto_shash *tfm)  { -	struct hash_desc desc; -	struct scatterlist sg[1]; -	int rc; +	struct { +		struct shash_desc shash; +		char ctx[crypto_shash_descsize(tfm)]; +	} desc; +	int rc, i; + +	desc.shash.tfm = tfm; +	desc.shash.flags = 0; -	rc = init_desc(&desc); +	hash->length = crypto_shash_digestsize(tfm); + +	rc = crypto_shash_init(&desc.shash);  	if (rc != 0)  		return rc; -	sg_init_one(sg, template, template_len); -	rc = crypto_hash_update(&desc, sg, template_len); +	for (i = 0; i < num_fields; i++) { +		u8 buffer[IMA_EVENT_NAME_LEN_MAX + 1] = { 0 }; +		u8 *data_to_hash = field_data[i].data; +		u32 datalen = field_data[i].len; + +		if (strcmp(td->name, IMA_TEMPLATE_IMA_NAME) != 0) { +			rc = crypto_shash_update(&desc.shash, +						(const u8 *) &field_data[i].len, +						sizeof(field_data[i].len)); +			if (rc) +				break; +		} else if (strcmp(td->fields[i]->field_id, "n") == 0) { +			memcpy(buffer, data_to_hash, datalen); +			data_to_hash = buffer; +			datalen = IMA_EVENT_NAME_LEN_MAX + 1; +		} +		rc = crypto_shash_update(&desc.shash, data_to_hash, datalen); +		if (rc) +			break; +	} +  	if (!rc) -		rc = crypto_hash_final(&desc, digest); -	crypto_free_hash(desc.tfm); +		rc = crypto_shash_final(&desc.shash, hash->digest); + +	return rc; +} + +int ima_calc_field_array_hash(struct ima_field_data *field_data, +			      struct ima_template_desc *desc, int num_fields, +			      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); + +	rc = ima_calc_field_array_hash_tfm(field_data, desc, num_fields, +					   hash, tfm); + +	ima_free_tfm(tfm); +  	return rc;  } @@ -112,20 +248,26 @@ static void __init ima_pcrread(int idx, u8 *pcr)  		return;  	if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0) -		pr_err("IMA: Error Communicating to TPM chip\n"); +		pr_err("Error Communicating to TPM chip\n");  }  /*   * 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)  { -	struct hash_desc desc; -	struct scatterlist sg; -	u8 pcr_i[IMA_DIGEST_SIZE]; +	u8 pcr_i[TPM_DIGEST_SIZE];  	int rc, i; +	struct { +		struct shash_desc shash; +		char ctx[crypto_shash_descsize(tfm)]; +	} desc; + +	desc.shash.tfm = tfm; +	desc.shash.flags = 0; -	rc = init_desc(&desc); +	rc = crypto_shash_init(&desc.shash);  	if (rc != 0)  		return rc; @@ -133,11 +275,26 @@ 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 */ -		sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE); -		rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE); +		rc = crypto_shash_update(&desc.shash, pcr_i, TPM_DIGEST_SIZE);  	}  	if (!rc) -		crypto_hash_final(&desc, digest); -	crypto_free_hash(desc.tfm); +		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;  }  | 
