diff options
Diffstat (limited to 'security/integrity')
26 files changed, 1493 insertions, 328 deletions
diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 0f9cffb1f9a..0793f4811cb 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -10,6 +10,6 @@ obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o  integrity-y := iint.o  subdir-$(CONFIG_IMA)			+= ima -obj-$(CONFIG_IMA)			+= ima/built-in.o +obj-$(CONFIG_IMA)			+= ima/  subdir-$(CONFIG_EVM)			+= evm -obj-$(CONFIG_EVM)			+= evm/built-in.o +obj-$(CONFIG_EVM)			+= evm/ diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 0b759e17a13..b4af4ebc5be 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -28,7 +28,7 @@ static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {  };  int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, -					const char *digest, int digestlen) +			    const char *digest, int digestlen)  {  	if (id >= INTEGRITY_KEYRING_MAX)  		return -EINVAL; @@ -44,9 +44,10 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,  		}  	} -	switch (sig[0]) { +	switch (sig[1]) {  	case 1: -		return digsig_verify(keyring[id], sig, siglen, +		/* v1 API expect signature without xattr type */ +		return digsig_verify(keyring[id], sig + 1, siglen - 1,  				     digest, digestlen);  	case 2:  		return asymmetric_verify(keyring[id], sig, siglen, diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index b4754667659..9eae4809006 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -20,17 +20,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.   */  static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index fea9749c375..d606f3d12d6 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -1,10 +1,10 @@  config EVM  	boolean "EVM support" -	depends on SECURITY && KEYS && (TRUSTED_KEYS=y || TRUSTED_KEYS=n) +	depends on SECURITY +	select KEYS +	select ENCRYPTED_KEYS  	select CRYPTO_HMAC -	select CRYPTO_MD5  	select CRYPTO_SHA1 -	select ENCRYPTED_KEYS  	default n  	help  	  EVM protects a file's security extended attributes against @@ -12,15 +12,41 @@ config EVM  	  If you are unsure how to answer this question, answer N. -config EVM_HMAC_VERSION -	int "EVM HMAC version" +if EVM + +menu "EVM options" + +config EVM_ATTR_FSUUID +	bool "FSUUID (version 2)" +	default y  	depends on EVM -	default 2  	help -	  This options adds EVM HMAC version support. -	  1 - original version -	  2 - add per filesystem unique identifier (UUID) (default) +	  Include filesystem UUID for HMAC calculation. -	  WARNING: changing the HMAC calculation method or adding  +	  Default value is 'selected', which is former version 2. +	  if 'not selected', it is former version 1 + +	  WARNING: changing the HMAC calculation method or adding  	  additional info to the calculation, requires existing EVM -	  labeled file systems to be relabeled.   +	  labeled file systems to be relabeled. + +config EVM_EXTRA_SMACK_XATTRS +	bool "Additional SMACK xattrs" +	depends on EVM && SECURITY_SMACK +	default n +	help +	  Include additional SMACK xattrs for HMAC calculation. + +	  In addition to the original security xattrs (eg. security.selinux, +	  security.SMACK64, security.capability, and security.ima) included +	  in the HMAC calculation, enabling this option includes newly defined +	  Smack xattrs: security.SMACK64EXEC, security.SMACK64TRANSMUTE and +	  security.SMACK64MMAP. + +	  WARNING: changing the HMAC calculation method or adding +	  additional info to the calculation, requires existing EVM +	  labeled file systems to be relabeled. + +endmenu + +endif diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index 30bd1ec0232..88bfe77efa1 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -24,7 +24,10 @@  extern int evm_initialized;  extern char *evm_hmac;  extern char *evm_hash; -extern int evm_hmac_version; + +#define EVM_ATTR_FSUUID		0x0001 + +extern int evm_hmac_attrs;  extern struct crypto_shash *hmac_tfm;  extern struct crypto_shash *hash_tfm; @@ -32,19 +35,19 @@ extern struct crypto_shash *hash_tfm;  /* List of EVM protected security xattrs */  extern char *evm_config_xattrnames[]; -extern int evm_init_key(void); -extern int evm_update_evmxattr(struct dentry *dentry, -			       const char *req_xattr_name, -			       const char *req_xattr_value, -			       size_t req_xattr_value_len); -extern int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, -			 const char *req_xattr_value, -			 size_t req_xattr_value_len, char *digest); -extern int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, -			 const char *req_xattr_value, -			 size_t req_xattr_value_len, char *digest); -extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr, -			 char *hmac_val); -extern int evm_init_secfs(void); +int evm_init_key(void); +int evm_update_evmxattr(struct dentry *dentry, +			const char *req_xattr_name, +			const char *req_xattr_value, +			size_t req_xattr_value_len); +int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, +		  const char *req_xattr_value, +		  size_t req_xattr_value_len, char *digest); +int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, +		  const char *req_xattr_value, +		  size_t req_xattr_value_len, char *digest); +int evm_init_hmac(struct inode *inode, const struct xattr *xattr, +		  char *hmac_val); +int evm_init_secfs(void);  #endif diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 3bab89eb21d..5e9687f02e1 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -13,6 +13,8 @@   *	 Using root's kernel master key (kmk), calculate the HMAC   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/module.h>  #include <linux/crypto.h>  #include <linux/xattr.h> @@ -103,14 +105,14 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,  		umode_t mode;  	} hmac_misc; -	memset(&hmac_misc, 0, sizeof hmac_misc); +	memset(&hmac_misc, 0, sizeof(hmac_misc));  	hmac_misc.ino = inode->i_ino;  	hmac_misc.generation = inode->i_generation;  	hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid);  	hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);  	hmac_misc.mode = inode->i_mode; -	crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc); -	if (evm_hmac_version > 1) +	crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc)); +	if (evm_hmac_attrs & EVM_ATTR_FSUUID)  		crypto_shash_update(desc, inode->i_sb->s_uuid,  				    sizeof(inode->i_sb->s_uuid));  	crypto_shash_final(desc, digest); @@ -137,7 +139,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,  	int error;  	int size; -	if (!inode->i_op || !inode->i_op->getxattr) +	if (!inode->i_op->getxattr)  		return -EOPNOTSUPP;  	desc = init_desc(type);  	if (IS_ERR(desc)) @@ -221,7 +223,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,  	desc = init_desc(EVM_XATTR_HMAC);  	if (IS_ERR(desc)) { -		printk(KERN_INFO "init_desc failed\n"); +		pr_info("init_desc failed\n");  		return PTR_ERR(desc);  	} diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index af9b6852f4e..3bcb80df4d0 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -14,6 +14,8 @@   *	evm_inode_removexattr, and evm_verifyxattr   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/module.h>  #include <linux/crypto.h>  #include <linux/audit.h> @@ -30,7 +32,7 @@ static char *integrity_status_msg[] = {  };  char *evm_hmac = "hmac(sha1)";  char *evm_hash = "sha1"; -int evm_hmac_version = CONFIG_EVM_HMAC_VERSION; +int evm_hmac_attrs;  char *evm_config_xattrnames[] = {  #ifdef CONFIG_SECURITY_SELINUX @@ -38,6 +40,11 @@ char *evm_config_xattrnames[] = {  #endif  #ifdef CONFIG_SECURITY_SMACK  	XATTR_NAME_SMACK, +#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS +	XATTR_NAME_SMACKEXEC, +	XATTR_NAME_SMACKTRANSMUTE, +	XATTR_NAME_SMACKMMAP, +#endif  #endif  #ifdef CONFIG_IMA_APPRAISE  	XATTR_NAME_IMA, @@ -55,6 +62,14 @@ static int __init evm_set_fixmode(char *str)  }  __setup("evm=", evm_set_fixmode); +static void __init evm_init_config(void) +{ +#ifdef CONFIG_EVM_ATTR_FSUUID +	evm_hmac_attrs |= EVM_ATTR_FSUUID; +#endif +	pr_info("HMAC attrs: 0x%x\n", evm_hmac_attrs); +} +  static int evm_find_protected_xattrs(struct dentry *dentry)  {  	struct inode *inode = dentry->d_inode; @@ -62,7 +77,7 @@ static int evm_find_protected_xattrs(struct dentry *dentry)  	int error;  	int count = 0; -	if (!inode->i_op || !inode->i_op->getxattr) +	if (!inode->i_op->getxattr)  		return -EOPNOTSUPP;  	for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) { @@ -123,7 +138,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,  		goto out;  	} -	xattr_len = rc - 1; +	xattr_len = rc;  	/* check value type */  	switch (xattr_data->type) { @@ -143,7 +158,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,  		if (rc)  			break;  		rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, -					xattr_data->digest, xattr_len, +					(const char *)xattr_data, xattr_len,  					calc.digest, sizeof(calc.digest));  		if (!rc) {  			/* we probably want to replace rsa with hmac here */ @@ -285,12 +300,20 @@ out:   * @xattr_value: pointer to the new extended attribute value   * @xattr_value_len: pointer to the new extended attribute value length   * - * Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that - * the current value is valid. + * Before allowing the 'security.evm' protected xattr to be updated, + * verify the existing value is valid.  As only the kernel should have + * access to the EVM encrypted key needed to calculate the HMAC, prevent + * userspace from writing HMAC value.  Writing 'security.evm' requires + * requires CAP_SYS_ADMIN privileges.   */  int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,  		       const void *xattr_value, size_t xattr_value_len)  { +	const struct evm_ima_xattr_data *xattr_data = xattr_value; + +	if ((strcmp(xattr_name, XATTR_NAME_EVM) == 0) +	    && (xattr_data->type == EVM_XATTR_HMAC)) +		return -EPERM;  	return evm_protect_xattr(dentry, xattr_name, xattr_value,  				 xattr_value_len);  } @@ -430,9 +453,11 @@ static int __init init_evm(void)  {  	int error; +	evm_init_config(); +  	error = evm_init_secfs();  	if (error < 0) { -		printk(KERN_INFO "EVM: Error registering secfs\n"); +		pr_info("Error registering secfs\n");  		goto err;  	} @@ -449,7 +474,7 @@ static int __init evm_display_config(void)  	char **xattrname;  	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) -		printk(KERN_INFO "EVM: %s\n", *xattrname); +		pr_info("%s\n", *xattrname);  	return 0;  } diff --git a/security/integrity/evm/evm_posix_acl.c b/security/integrity/evm/evm_posix_acl.c index b1753e98bf9..46408b9e62e 100644 --- a/security/integrity/evm/evm_posix_acl.c +++ b/security/integrity/evm/evm_posix_acl.c @@ -11,8 +11,9 @@  #include <linux/module.h>  #include <linux/xattr.h> +#include <linux/evm.h> -int posix_xattr_acl(char *xattr) +int posix_xattr_acl(const char *xattr)  {  	int xattr_len = strlen(xattr); diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index 30f670ad6ac..cf12a04717d 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -13,6 +13,8 @@   *	- Get the key and enable EVM   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/uaccess.h>  #include <linux/module.h>  #include "evm.h" @@ -79,9 +81,9 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf,  	error = evm_init_key();  	if (!error) {  		evm_initialized = 1; -		pr_info("EVM: initialized\n"); +		pr_info("initialized\n");  	} else -		pr_err("EVM: initialization failed\n"); +		pr_err("initialization failed\n");  	return count;  } diff --git a/security/integrity/iint.c b/security/integrity/iint.c index 74522dbd10a..a521edf4cbd 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -70,6 +70,8 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode)  static void iint_free(struct integrity_iint_cache *iint)  { +	kfree(iint->ima_hash); +	iint->ima_hash = NULL;  	iint->version = 0;  	iint->flags = 0UL;  	iint->ima_file_status = INTEGRITY_UNKNOWN; @@ -149,7 +151,7 @@ static void init_once(void *foo)  {  	struct integrity_iint_cache *iint = foo; -	memset(iint, 0, sizeof *iint); +	memset(iint, 0, sizeof(*iint));  	iint->version = 0;  	iint->flags = 0UL;  	iint->ima_file_status = INTEGRITY_UNKNOWN; diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 39196abaff0..81a27971d88 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -9,6 +9,7 @@ config IMA  	select CRYPTO_HMAC  	select CRYPTO_MD5  	select CRYPTO_SHA1 +	select CRYPTO_HASH_INFO  	select TCG_TPM if HAS_IOMEM && !UML  	select TCG_TIS if TCG_TPM && X86  	select TCG_IBMVTPM if TCG_TPM && PPC64 @@ -45,6 +46,69 @@ config IMA_LSM_RULES  	help  	  Disabling this option will disregard LSM based policy rules. +choice +	prompt "Default template" +	default IMA_NG_TEMPLATE +	depends on IMA +	help +	  Select the default IMA measurement template. + +	  The original 'ima' measurement list template contains a +	  hash, defined as 20 bytes, and a null terminated pathname, +	  limited to 255 characters.  The 'ima-ng' measurement list +	  template permits both larger hash digests and longer +	  pathnames. + +	config IMA_TEMPLATE +		bool "ima" +	config IMA_NG_TEMPLATE +		bool "ima-ng (default)" +	config IMA_SIG_TEMPLATE +		bool "ima-sig" +endchoice + +config IMA_DEFAULT_TEMPLATE +	string +	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" +	default IMA_DEFAULT_HASH_SHA1 +	depends on IMA +	help +	   Select the default hash algorithm used for the measurement +	   list, integrity appraisal and audit log.  The compiled default +	   hash algorithm can be overwritten using the kernel command +	   line 'ima_hash=' option. + +	config IMA_DEFAULT_HASH_SHA1 +		bool "SHA1 (default)" +		depends on CRYPTO_SHA1 + +	config IMA_DEFAULT_HASH_SHA256 +		bool "SHA256" +		depends on CRYPTO_SHA256 && !IMA_TEMPLATE + +	config IMA_DEFAULT_HASH_SHA512 +		bool "SHA512" +		depends on CRYPTO_SHA512 && !IMA_TEMPLATE + +	config IMA_DEFAULT_HASH_WP512 +		bool "WP512" +		depends on CRYPTO_WP512 && !IMA_TEMPLATE +endchoice + +config IMA_DEFAULT_HASH +	string +	depends on IMA +	default "sha1" if IMA_DEFAULT_HASH_SHA1 +	default "sha256" if IMA_DEFAULT_HASH_SHA256 +	default "sha512" if IMA_DEFAULT_HASH_SHA512 +	default "wp512" if IMA_DEFAULT_HASH_WP512 +  config IMA_APPRAISE  	bool "Appraise integrity measurements"  	depends on IMA diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 56dfee7cbf6..d79263d2fdb 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_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 b3dd616560f..f79fa8be203 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -26,7 +26,8 @@  #include "../integrity.h" -enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII }; +enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_BINARY_NO_FIELD_LEN, +		     IMA_SHOW_BINARY_OLD_STRING_FMT, IMA_SHOW_ASCII };  enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };  /* digest size for IMA, fits SHA1 or MD5 */ @@ -36,23 +37,48 @@ 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 + +#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; -extern char *ima_hash; +extern int ima_hash_algo;  extern int ima_appraise; -/* 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 */ +/* 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 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); +}; + +/* IMA template descriptor definition */ +struct ima_template_desc { +	char *name; +	char *fmt; +	int num_fields; +	struct ima_template_field **fields;  };  struct ima_template_entry { -	u8 digest[IMA_DIGEST_SIZE];	/* sha1 or md5 measurement hash */ -	const char *template_name; -	int template_len; -	struct ima_template_data template; +	u8 digest[TPM_DIGEST_SIZE];	/* sha1 or md5 measurement hash */ +	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 { @@ -69,13 +95,22 @@ 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); -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_boot_aggregate(char *digest); -void ima_add_violation(struct inode *inode, const unsigned char *filename, +			   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_field_array_hash(struct ima_field_data *field_data, +			      struct ima_template_desc *desc, 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);  /*   * used to protect h_table and sha_table @@ -98,14 +133,22 @@ 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); +			   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 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); -void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show); +		       struct inode *inode, const unsigned char *filename); +void ima_free_template_entry(struct ima_template_entry *entry);  const char *ima_d_path(struct path *path, char **pathbuf);  /* rbtree tree calls to lookup, insert, delete @@ -131,17 +174,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;  } @@ -162,6 +213,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 1c03e8f1e0e..d9cd5ce14d2 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -18,9 +18,59 @@  #include <linux/fs.h>  #include <linux/xattr.h>  #include <linux/evm.h> +#include <crypto/hash_info.h>  #include "ima.h" -static const char *IMA_TEMPLATE_NAME = "ima"; +/* + * ima_free_template_entry - free an existing template entry + */ +void ima_free_template_entry(struct ima_template_entry *entry) +{ +	int i; + +	for (i = 0; i < entry->template_desc->num_fields; i++) +		kfree(entry->template_data[i].data); + +	kfree(entry); +} + +/* + * 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 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; + +	*entry = kzalloc(sizeof(**entry) + template_desc->num_fields * +			 sizeof(struct ima_field_data), GFP_NOFS); +	if (!*entry) +		return -ENOMEM; + +	(*entry)->template_desc = template_desc; +	for (i = 0; i < template_desc->num_fields; i++) { +		struct ima_template_field *field = template_desc->fields[i]; +		u32 len; + +		result = field->field_init(iint, file, filename, +					   xattr_value, xattr_len, +					   &((*entry)->template_data[i])); +		if (result != 0) +			goto out; + +		len = (*entry)->template_data[i].len; +		(*entry)->template_data_len += sizeof(len); +		(*entry)->template_data_len += len; +	} +	return 0; +out: +	ima_free_template_entry(*entry); +	*entry = NULL; +	return result; +}  /*   * ima_store_template - store ima template measurements @@ -39,28 +89,35 @@ 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"; +	static const char op[] = "add_template_measure"; +	static const char audit_cause[] = "hashing_error"; +	char *template_name = entry->template_desc->name;  	int result; - -	memset(entry->digest, 0, sizeof(entry->digest)); -	entry->template_name = IMA_TEMPLATE_NAME; -	entry->template_len = sizeof(entry->template); +	struct { +		struct ima_digest_data hdr; +		char digest[TPM_DIGEST_SIZE]; +	} hash;  	if (!violation) { -		result = ima_calc_buffer_hash(&entry->template, -						entry->template_len, -						entry->digest); +		int num_fields = entry->template_desc->num_fields; + +		/* this function uses default algo */ +		hash.hdr.algo = HASH_ALGO_SHA1; +		result = ima_calc_field_array_hash(&entry->template_data[0], +						   entry->template_desc, +						   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;  		} +		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;  } @@ -71,26 +128,26 @@ 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_inode(file);  	int violation = 1;  	int result;  	/* 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, +					 NULL, 0, &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); +	result = ima_store_template(entry, violation, inode, filename);  	if (result < 0) -		kfree(entry); +		ima_free_template_entry(entry);  err_out:  	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,  			    op, cause, result, 0); @@ -103,10 +160,10 @@ err_out:   * @function: calling function (FILE_CHECK, BPRM_CHECK, MMAP_CHECK, MODULE_CHECK)   *   * The policy is defined in terms of keypairs: - * 		subj=, obj=, type=, func=, mask=, fsmagic= + *		subj=, obj=, type=, func=, mask=, fsmagic=   *	subj,obj, and type: are LSM specific. - * 	func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK - * 	mask: contains the permission mask + *	func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK + *	mask: contains the permission mask   *	fsmagic: hex value   *   * Returns IMA_MEASURE, IMA_APPRAISE mask. @@ -138,25 +195,55 @@ 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)  { +	const char *audit_cause = "failed";  	struct inode *inode = file_inode(file);  	const char *filename = file->f_dentry->d_name.name;  	int result = 0; +	struct { +		struct ima_digest_data hdr; +		char digest[IMA_MAX_DIGEST_SIZE]; +	} hash; + +	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; -		iint->ima_xattr.type = IMA_XATTR_DIGEST; -		result = ima_calc_file_hash(file, iint->ima_xattr.digest); +		if (file->f_flags & O_DIRECT) { +			audit_cause = "failed(directio)"; +			result = -EACCES; +			goto out; +		} + +		/* use default hash algorithm */ +		hash.hdr.algo = ima_hash_algo; + +		if (xattr_value) +			ima_get_hash_algo(*xattr_value, *xattr_len, &hash.hdr); + +		result = ima_calc_file_hash(file, &hash.hdr);  		if (!result) { -			iint->version = i_version; -			iint->flags |= IMA_COLLECTED; +			int length = sizeof(hash.hdr) + hash.hdr.length; +			void *tmpbuf = krealloc(iint->ima_hash, length, +						GFP_NOFS); +			if (tmpbuf) { +				iint->ima_hash = tmpbuf; +				memcpy(iint->ima_hash, &hash, length); +				iint->version = i_version; +				iint->flags |= IMA_COLLECTED; +			} else +				result = -ENOMEM;  		}  	} +out:  	if (result)  		integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, -				    filename, "collect_data", "failed", +				    filename, "collect_data", audit_cause,  				    result, 0);  	return result;  } @@ -169,7 +256,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,   *   * We only get here if the inode has not already been measured,   * but the measurement could already exist: - * 	- multiple copies of the same file on either the same or + *	- multiple copies of the same file on either the same or   *	  different filesystems.   *	- the inode was previously flushed as well as the iint info,   *	  containing the hashing info. @@ -177,10 +264,12 @@ 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"; +	static const char op[] = "add_template_measure"; +	static const char audit_cause[] = "ENOMEM";  	int result = -ENOMEM;  	struct inode *inode = file_inode(file);  	struct ima_template_entry *entry; @@ -189,37 +278,35 @@ 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, +					 xattr_value, xattr_len, &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)); -	memcpy(entry->template.digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE); -	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); +	result = ima_store_template(entry, violation, inode, filename);  	if (!result || result == -EEXIST)  		iint->flags |= IMA_MEASURED;  	if (result < 0) -		kfree(entry); +		ima_free_template_entry(entry);  }  void ima_audit_measurement(struct integrity_iint_cache *iint,  			   const unsigned char *filename)  {  	struct audit_buffer *ab; -	char hash[(IMA_DIGEST_SIZE * 2) + 1]; +	char hash[(iint->ima_hash->length * 2) + 1]; +	const char *algo_name = hash_algo_name[iint->ima_hash->algo]; +	char algo_hash[sizeof(hash) + strlen(algo_name) + 2];  	int i;  	if (iint->flags & IMA_AUDITED)  		return; -	for (i = 0; i < IMA_DIGEST_SIZE; i++) -		hex_byte_pack(hash + (i * 2), iint->ima_xattr.digest[i]); +	for (i = 0; i < iint->ima_hash->length; i++) +		hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]);  	hash[i * 2] = '\0';  	ab = audit_log_start(current->audit_context, GFP_KERNEL, @@ -230,7 +317,8 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,  	audit_log_format(ab, "file=");  	audit_log_untrustedstring(ab, filename);  	audit_log_format(ab, " hash="); -	audit_log_untrustedstring(ab, hash); +	snprintf(algo_hash, sizeof(algo_hash), "%s:%s", algo_name, hash); +	audit_log_untrustedstring(ab, algo_hash);  	audit_log_task_info(ab, current);  	audit_log_end(ab); @@ -252,5 +340,5 @@ const char *ima_d_path(struct path *path, char **pathbuf)  			pathname = NULL;  		}  	} -	return pathname; +	return pathname ?: (const char *)path->dentry->d_name.name;  } diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 2d4becab891..d3113d4aaa3 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -15,6 +15,7 @@  #include <linux/magic.h>  #include <linux/ima.h>  #include <linux/evm.h> +#include <crypto/hash_info.h>  #include "ima.h" @@ -43,19 +44,31 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)  }  static int ima_fix_xattr(struct dentry *dentry, -			  struct integrity_iint_cache *iint) +			 struct integrity_iint_cache *iint)  { -	iint->ima_xattr.type = IMA_XATTR_DIGEST; -	return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, -				     (u8 *)&iint->ima_xattr, -				      sizeof(iint->ima_xattr), 0); +	int rc, offset; +	u8 algo = iint->ima_hash->algo; + +	if (algo <= HASH_ALGO_SHA1) { +		offset = 1; +		iint->ima_hash->xattr.sha1.type = IMA_XATTR_DIGEST; +	} else { +		offset = 0; +		iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG; +		iint->ima_hash->xattr.ng.algo = algo; +	} +	rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, +				   &iint->ima_hash->xattr.data[offset], +				   (sizeof(iint->ima_hash->xattr) - offset) + +				   iint->ima_hash->length, 0); +	return rc;  }  /* Return specific func appraised cached result */  enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,  					   int func)  { -	switch(func) { +	switch (func) {  	case MMAP_CHECK:  		return iint->ima_mmap_status;  	case BPRM_CHECK: @@ -71,7 +84,7 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,  static void ima_set_cache_status(struct integrity_iint_cache *iint,  				 int func, enum integrity_status status)  { -	switch(func) { +	switch (func) {  	case MMAP_CHECK:  		iint->ima_mmap_status = status;  		break; @@ -90,7 +103,7 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,  static void ima_cache_flags(struct integrity_iint_cache *iint, int func)  { -	switch(func) { +	switch (func) {  	case MMAP_CHECK:  		iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);  		break; @@ -107,6 +120,50 @@ 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 < 2) +		return; + +	switch (xattr_value->type) { +	case EVM_IMA_XATTR_DIGSIG: +		sig = (typeof(sig))xattr_value; +		if (sig->version != 2 || xattr_len <= sizeof(*sig)) +			return; +		hash->algo = sig->hash_algo; +		break; +	case IMA_XATTR_DIGEST_NG: +		hash->algo = xattr_value->digest[0]; +		break; +	case IMA_XATTR_DIGEST: +		/* this is for backward compatibility */ +		if (xattr_len == 21) { +			unsigned int zero = 0; +			if (!memcmp(&xattr_value->digest[16], &zero, 4)) +				hash->algo = HASH_ALGO_MD5; +			else +				hash->algo = HASH_ALGO_SHA1; +		} else if (xattr_len == 17) +			hash->algo = HASH_ALGO_MD5; +		break; +	} +} + +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 +173,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)  { +	static const char op[] = "appraise_data"; +	char *cause = "unknown";  	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, hash_start = 0;  	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; @@ -153,14 +209,25 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,  		goto out;  	}  	switch (xattr_value->type) { +	case IMA_XATTR_DIGEST_NG: +		/* first byte contains algorithm id */ +		hash_start = 1;  	case IMA_XATTR_DIGEST:  		if (iint->flags & IMA_DIGSIG_REQUIRED) {  			cause = "IMA signature required";  			status = INTEGRITY_FAIL;  			break;  		} -		rc = memcmp(xattr_value->digest, iint->ima_xattr.digest, -			    IMA_DIGEST_SIZE); +		if (xattr_len - sizeof(xattr_value->type) - hash_start >= +				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[hash_start], +				    iint->ima_hash->digest, +				    iint->ima_hash->length); +		else +			rc = -EINVAL;  		if (rc) {  			cause = "invalid-hash";  			status = INTEGRITY_FAIL; @@ -171,9 +238,9 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,  	case EVM_IMA_XATTR_DIGSIG:  		iint->flags |= IMA_DIGSIG;  		rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, -					     xattr_value->digest, rc - 1, -					     iint->ima_xattr.digest, -					     IMA_DIGEST_SIZE); +					     (const char *)xattr_value, rc, +					     iint->ima_hash->digest, +					     iint->ima_hash->length);  		if (rc == -EOPNOTSUPP) {  			status = INTEGRITY_UNKNOWN;  		} else if (rc) { @@ -203,7 +270,6 @@ out:  		ima_cache_flags(iint, func);  	}  	ima_set_cache_status(iint, func, status); -	kfree(xattr_value);  	return status;  } @@ -219,7 +285,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; @@ -275,7 +341,7 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,  	return 0;  } -static void ima_reset_appraise_flags(struct inode *inode) +static void ima_reset_appraise_flags(struct inode *inode, int digsig)  {  	struct integrity_iint_cache *iint; @@ -287,18 +353,22 @@ static void ima_reset_appraise_flags(struct inode *inode)  		return;  	iint->flags &= ~IMA_DONE_MASK; +	if (digsig) +		iint->flags |= IMA_DIGSIG;  	return;  }  int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,  		       const void *xattr_value, size_t xattr_value_len)  { +	const struct evm_ima_xattr_data *xvalue = xattr_value;  	int result;  	result = ima_protect_xattr(dentry, xattr_name, xattr_value,  				   xattr_value_len);  	if (result == 1) { -		ima_reset_appraise_flags(dentry->d_inode); +		ima_reset_appraise_flags(dentry->d_inode, +			 (xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0);  		result = 0;  	}  	return result; @@ -310,7 +380,7 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)  	result = ima_protect_xattr(dentry, xattr_name, NULL, 0);  	if (result == 1) { -		ima_reset_appraise_flags(dentry->d_inode); +		ima_reset_appraise_flags(dentry->d_inode, 0);  		result = 0;  	}  	return result; diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index a02e0791cf1..ccd0ac8fa9a 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -10,9 +10,11 @@   * 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> @@ -20,57 +22,119 @@  #include <linux/err.h>  #include <linux/slab.h>  #include <crypto/hash.h> +#include <crypto/hash_info.h>  #include "ima.h"  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) +{ +	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; +} +  int ima_init_crypto(void)  {  	long rc; -	ima_shash_tfm = crypto_alloc_shash(ima_hash, 0, 0); +	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", ima_hash, rc); +		pr_err("Can not allocate %s (reason: %ld)\n", +		       hash_algo_name[ima_hash_algo], rc);  		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_file_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)  {  	loff_t i_size, offset = 0;  	char *rbuf;  	int rc, read = 0;  	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; +	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_inode(file)); +  	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; @@ -83,29 +147,99 @@ int ima_calc_file_hash(struct file *file, char *digest)  		if (rc)  			break;  	} -	kfree(rbuf); -	if (!rc) -		rc = crypto_shash_final(&desc.shash, digest);  	if (read)  		file->f_mode &= ~FMODE_READ; +	kfree(rbuf);  out: +	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 buffer + * Calculate the hash of template data   */ -int ima_calc_buffer_hash(const void *data, int len, 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 {  		struct shash_desc shash; -		char ctx[crypto_shash_descsize(ima_shash_tfm)]; +		char ctx[crypto_shash_descsize(tfm)];  	} desc; +	int rc, i; -	desc.shash.tfm = ima_shash_tfm; +	desc.shash.tfm = tfm;  	desc.shash.flags = 0; -	return crypto_shash_digest(&desc.shash, data, len, digest); +	hash->length = crypto_shash_digestsize(tfm); + +	rc = crypto_shash_init(&desc.shash); +	if (rc != 0) +		return rc; + +	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_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;  }  static void __init ima_pcrread(int idx, u8 *pcr) @@ -114,22 +248,23 @@ 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)  { -	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(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); @@ -140,9 +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 */ -		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);  	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_fs.c b/security/integrity/ima/ima_fs.c index 38477c9c341..da92fcc08d1 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -88,8 +88,7 @@ static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)  	 * against concurrent list-extension  	 */  	rcu_read_lock(); -	qe = list_entry_rcu(qe->later.next, -			    struct ima_queue_entry, later); +	qe = list_entry_rcu(qe->later.next, struct ima_queue_entry, later);  	rcu_read_unlock();  	(*pos)++; @@ -100,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++); @@ -111,6 +110,7 @@ static 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) @@ -120,6 +120,8 @@ 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; +	bool is_ima_template = false; +	int i;  	/* get entry */  	e = qe->entry; @@ -131,21 +133,37 @@ static int ima_measurements_show(struct seq_file *m, void *v)  	 * PCR used is always the same (config option) in  	 * little-endian format  	 */ -	ima_putc(m, &pcr, sizeof pcr); +	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); -	ima_putc(m, &namelen, sizeof namelen); +	namelen = strlen(e->template_desc->name); +	ima_putc(m, &namelen, sizeof(namelen));  	/* 4th:  template name */ -	ima_putc(m, (void *)e->template_name, namelen); - -	/* 5th:  template specific data */ -	ima_template_show(m, (struct ima_template_data *)&e->template, -			  IMA_SHOW_BINARY); +	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) +		is_ima_template = true; + +	if (!is_ima_template) +		ima_putc(m, &e->template_data_len, +			 sizeof(e->template_data_len)); + +	/* 6th:  template specific data */ +	for (i = 0; i < e->template_desc->num_fields; i++) { +		enum ima_show_type show = IMA_SHOW_BINARY; +		struct ima_template_field *field = e->template_desc->fields[i]; + +		if (is_ima_template && strcmp(field->field_id, "d") == 0) +			show = IMA_SHOW_BINARY_NO_FIELD_LEN; +		if (is_ima_template && strcmp(field->field_id, "n") == 0) +			show = IMA_SHOW_BINARY_OLD_STRING_FMT; +		field->field_show(m, show, &e->template_data[i]); +	}  	return 0;  } @@ -168,41 +186,21 @@ static const struct file_operations ima_measurements_ops = {  	.release = seq_release,  }; -static void ima_print_digest(struct seq_file *m, u8 *digest) +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));  } -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); -		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; @@ -213,14 +211,21 @@ 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); +	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;  } @@ -287,7 +292,7 @@ static atomic_t policy_opencount = ATOMIC_INIT(1);  /*   * ima_open_policy: sequentialize access to the policy file   */ -static int ima_open_policy(struct inode * inode, struct file * filp) +static int ima_open_policy(struct inode *inode, struct file *filp)  {  	/* No point in being allowed to open it if you aren't going to write */  	if (!(filp->f_flags & O_WRONLY)) diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 162ea723db3..e8f9d70a465 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -14,10 +14,14 @@   * File: ima_init.c   *             initialization and cleanup functions   */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/module.h>  #include <linux/scatterlist.h>  #include <linux/slab.h>  #include <linux/err.h> +#include <crypto/hash_info.h>  #include "ima.h"  /* name for boot aggregate entry */ @@ -41,31 +45,40 @@ int ima_used_chip;   */  static void __init ima_add_boot_aggregate(void)  { -	struct ima_template_entry *entry; -	const char *op = "add_boot_aggregate"; +	static const char op[] = "add_boot_aggregate";  	const char *audit_cause = "ENOMEM"; +	struct ima_template_entry *entry; +	struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;  	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; -		result = ima_calc_boot_aggregate(entry->template.digest); +		result = ima_calc_boot_aggregate(&hash.hdr);  		if (result < 0) {  			audit_cause = "hashing_error"; -			kfree(entry);  			goto err_out;  		}  	} -	result = ima_store_template(entry, violation, NULL); + +	result = ima_alloc_init_template(iint, NULL, boot_aggregate_name, +					 NULL, 0, &entry);  	if (result < 0) -		kfree(entry); +		return; + +	result = ima_store_template(entry, violation, NULL, +				    boot_aggregate_name); +	if (result < 0) +		ima_free_template_entry(entry);  	return;  err_out:  	integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op, @@ -74,7 +87,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; @@ -83,11 +96,15 @@ int __init ima_init(void)  		ima_used_chip = 1;  	if (!ima_used_chip) -		pr_info("IMA: No TPM chip found, activating TPM-bypass!\n"); +		pr_info("No TPM chip found, activating TPM-bypass!\n");  	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_main.c b/security/integrity/ima/ima_main.c index e9508d5bbfc..09baa335ebc 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -24,6 +24,7 @@  #include <linux/slab.h>  #include <linux/xattr.h>  #include <linux/ima.h> +#include <crypto/hash_info.h>  #include "ima.h" @@ -35,11 +36,33 @@ int ima_appraise = IMA_APPRAISE_ENFORCE;  int ima_appraise;  #endif -char *ima_hash = "sha1"; +int ima_hash_algo = HASH_ALGO_SHA1; +static int hash_setup_done; +  static int __init hash_setup(char *str)  { -	if (strncmp(str, "md5", 3) == 0) -		ima_hash = "md5"; +	struct ima_template_desc *template_desc = ima_template_desc_current(); +	int i; + +	if (hash_setup_done) +		return 1; + +	if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { +		if (strncmp(str, "sha1", 4) == 0) +			ima_hash_algo = HASH_ALGO_SHA1; +		else if (strncmp(str, "md5", 3) == 0) +			ima_hash_algo = HASH_ALGO_MD5; +		goto out; +	} + +	for (i = 0; i < HASH_ALGO__LAST; i++) { +		if (strcmp(str, hash_algo_name[i]) == 0) { +			ima_hash_algo = i; +			break; +		} +	} +out: +	hash_setup_done = 1;  	return 1;  }  __setup("ima_hash=", hash_setup); @@ -48,18 +71,16 @@ __setup("ima_hash=", hash_setup);   * ima_rdwr_violation_check   *   * Only invalidate the PCR for measured files: - * 	- Opening a file for write when already open for read, + *	- Opening a file for write when already open for read,   *	  results in a time of measure, time of use (ToMToU) error.   *	- Opening a file for read when already open for write, - * 	  could result in a file measurement error. + *	  could result in a file measurement error.   *   */  static void ima_rdwr_violation_check(struct file *file)  { -	struct dentry *dentry = file->f_path.dentry;  	struct inode *inode = file_inode(file);  	fmode_t mode = file->f_mode; -	int must_measure;  	bool send_tomtou = false, send_writers = false;  	char *pathbuf = NULL;  	const char *pathname; @@ -70,32 +91,30 @@ static void ima_rdwr_violation_check(struct file *file)  	mutex_lock(&inode->i_mutex);	/* file metadata: permissions, xattr */  	if (mode & FMODE_WRITE) { -		if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) -			send_tomtou = true; -		goto out; +		if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { +			struct integrity_iint_cache *iint; +			iint = integrity_iint_find(inode); +			/* IMA_MEASURE is set from reader side */ +			if (iint && (iint->flags & IMA_MEASURE)) +				send_tomtou = true; +		} +	} else { +		if ((atomic_read(&inode->i_writecount) > 0) && +		    ima_must_measure(inode, MAY_READ, FILE_CHECK)) +			send_writers = true;  	} -	must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK); -	if (!must_measure) -		goto out; - -	if (atomic_read(&inode->i_writecount) > 0) -		send_writers = true; -out:  	mutex_unlock(&inode->i_mutex);  	if (!send_tomtou && !send_writers)  		return;  	pathname = ima_d_path(&file->f_path, &pathbuf); -	if (!pathname || strlen(pathname) > IMA_EVENT_NAME_LEN_MAX) -		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);  } @@ -144,9 +163,12 @@ static int process_measurement(struct file *file, const char *filename,  {  	struct inode *inode = file_inode(file);  	struct integrity_iint_cache *iint; +	struct ima_template_desc *template_desc = ima_template_desc_current();  	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; @@ -185,18 +207,27 @@ static int process_measurement(struct file *file, const char *filename,  		goto out_digsig;  	} -	rc = ima_collect_measurement(iint, file); -	if (rc != 0) +	if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { +		if (action & IMA_APPRAISE_SUBMASK) +			xattr_ptr = &xattr_value; +	} else +		xattr_ptr = &xattr_value; + +	rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len); +	if (rc != 0) { +		if (file->f_flags & O_DIRECT) +			rc = (iint->flags & IMA_PERMIT_DIRECTIO) ? 0 : -EACCES;  		goto out_digsig; +	} -	pathname = !filename ? ima_d_path(&file->f_path, &pathbuf) : filename; -	if (!pathname) -		pathname = (const char *)file->f_dentry->d_name.name; +	pathname = filename ?: ima_d_path(&file->f_path, &pathbuf);  	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); +		rc = ima_appraise_measurement(_func, iint, file, pathname, +					      xattr_value, xattr_len);  	if (action & IMA_AUDIT)  		ima_audit_measurement(iint, pathname);  	kfree(pathbuf); @@ -205,6 +236,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; @@ -244,9 +276,9 @@ int ima_file_mmap(struct file *file, unsigned long prot)  int ima_bprm_check(struct linux_binprm *bprm)  {  	return process_measurement(bprm->file, -				 (strcmp(bprm->filename, bprm->interp) == 0) ? -				 bprm->filename : bprm->interp, -				 MAY_EXEC, BPRM_CHECK); +				   (strcmp(bprm->filename, bprm->interp) == 0) ? +				   bprm->filename : bprm->interp, +				   MAY_EXEC, BPRM_CHECK);  }  /** @@ -263,8 +295,8 @@ int ima_file_check(struct file *file, int mask)  {  	ima_rdwr_violation_check(file);  	return process_measurement(file, NULL, -				 mask & (MAY_READ | MAY_WRITE | MAY_EXEC), -				 FILE_CHECK); +				   mask & (MAY_READ | MAY_WRITE | MAY_EXEC), +				   FILE_CHECK);  }  EXPORT_SYMBOL_GPL(ima_file_check); @@ -294,6 +326,7 @@ static int __init init_ima(void)  {  	int error; +	hash_setup(CONFIG_IMA_DEFAULT_HASH);  	error = ima_init();  	if (!error)  		ima_initialized = 1; diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 399433ad614..40a7488f672 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -7,7 +7,7 @@   * the Free Software Foundation, version 2 of the License.   *   * ima_policy.c - * 	- initialize default measure policy rules + *	- initialize default measure policy rules   *   */  #include <linux/module.h> @@ -21,8 +21,8 @@  #include "ima.h"  /* flags definitions */ -#define IMA_FUNC 	0x0001 -#define IMA_MASK 	0x0002 +#define IMA_FUNC	0x0001 +#define IMA_MASK	0x0002  #define IMA_FSMAGIC	0x0004  #define IMA_UID		0x0008  #define IMA_FOWNER	0x0010 @@ -69,36 +69,35 @@ struct ima_rule_entry {   * and running executables.   */  static struct ima_rule_entry default_rules[] = { -	{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = MEASURE,.func = MMAP_CHECK,.mask = MAY_EXEC, +	{.action = DONT_MEASURE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = TMPFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = DEVPTS_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = MEASURE, .func = MMAP_CHECK, .mask = MAY_EXEC,  	 .flags = IMA_FUNC | IMA_MASK}, -	{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC, +	{.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC,  	 .flags = IMA_FUNC | IMA_MASK}, -	{.action = MEASURE,.func = FILE_CHECK,.mask = MAY_READ,.uid = GLOBAL_ROOT_UID, +	{.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, .uid = GLOBAL_ROOT_UID,  	 .flags = IMA_FUNC | IMA_MASK | IMA_UID}, -	{.action = MEASURE,.func = MODULE_CHECK, .flags = IMA_FUNC}, +	{.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC},  };  static struct ima_rule_entry default_appraise_rules[] = { -	{.action = DONT_APPRAISE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = CGROUP_SUPER_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = APPRAISE,.fowner = GLOBAL_ROOT_UID,.flags = IMA_FOWNER}, +	{.action = DONT_APPRAISE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = TMPFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = RAMFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = DEVPTS_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER},  };  static LIST_HEAD(ima_default_rules); @@ -123,12 +122,12 @@ static int __init default_appraise_policy_setup(char *str)  }  __setup("ima_appraise_tcb", default_appraise_policy_setup); -/*  +/*   * Although the IMA policy does not change, the LSM policy can be   * reloaded, leaving the IMA LSM based rules referring to the old,   * stale LSM policy.   * - * Update the IMA LSM based rules to reflect the reloaded LSM policy.  + * Update the IMA LSM based rules to reflect the reloaded LSM policy.   * We assume the rules still exist; and BUG_ON() if they don't.   */  static void ima_lsm_update_rules(void) @@ -168,9 +167,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule,  	const struct cred *cred = current_cred();  	int i; -	if ((rule->flags & IMA_FUNC) && rule->func != func) +	if ((rule->flags & IMA_FUNC) && +	    (rule->func != func && func != POST_SETATTR))  		return false; -	if ((rule->flags & IMA_MASK) && rule->mask != mask) +	if ((rule->flags & IMA_MASK) && +	    (rule->mask != mask && func != POST_SETATTR))  		return false;  	if ((rule->flags & IMA_FSMAGIC)  	    && rule->fsmagic != inode->i_sb->s_magic) @@ -217,7 +218,7 @@ retry:  			retried = 1;  			ima_lsm_update_rules();  			goto retry; -		}  +		}  		if (!rc)  			return false;  	} @@ -233,7 +234,7 @@ static int get_subaction(struct ima_rule_entry *rule, int func)  	if (!(rule->flags & IMA_FUNC))  		return IMA_FILE_APPRAISE; -	switch(func) { +	switch (func) {  	case MMAP_CHECK:  		return IMA_MMAP_APPRAISE;  	case BPRM_CHECK: @@ -305,7 +306,7 @@ void __init ima_init_policy(void)  	measure_entries = ima_use_tcb ? ARRAY_SIZE(default_rules) : 0;  	appraise_entries = ima_use_appraise_tcb ?  			 ARRAY_SIZE(default_appraise_rules) : 0; -	 +  	for (i = 0; i < measure_entries + appraise_entries; i++) {  		if (i < measure_entries)  			list_add_tail(&default_rules[i].list, @@ -330,7 +331,7 @@ void __init ima_init_policy(void)   */  void ima_update_policy(void)  { -	const char *op = "policy_update"; +	static const char op[] = "policy_update";  	const char *cause = "already exists";  	int result = 1;  	int audit_info = 0; @@ -352,7 +353,7 @@ enum {  	Opt_obj_user, Opt_obj_role, Opt_obj_type,  	Opt_subj_user, Opt_subj_role, Opt_subj_type,  	Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner, -	Opt_appraise_type, Opt_fsuuid +	Opt_appraise_type, Opt_fsuuid, Opt_permit_directio  };  static match_table_t policy_tokens = { @@ -374,6 +375,7 @@ static match_table_t policy_tokens = {  	{Opt_uid, "uid=%s"},  	{Opt_fowner, "fowner=%s"},  	{Opt_appraise_type, "appraise_type=%s"}, +	{Opt_permit_directio, "permit_directio"},  	{Opt_err, NULL}  }; @@ -521,8 +523,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)  				break;  			} -			result = strict_strtoul(args[0].from, 16, -						&entry->fsmagic); +			result = kstrtoul(args[0].from, 16, &entry->fsmagic);  			if (!result)  				entry->flags |= IMA_FSMAGIC;  			break; @@ -548,7 +549,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)  				break;  			} -			result = strict_strtoul(args[0].from, 10, &lnum); +			result = kstrtoul(args[0].from, 10, &lnum);  			if (!result) {  				entry->uid = make_kuid(current_user_ns(), (uid_t)lnum);  				if (!uid_valid(entry->uid) || (((uid_t)lnum) != lnum)) @@ -565,7 +566,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)  				break;  			} -			result = strict_strtoul(args[0].from, 10, &lnum); +			result = kstrtoul(args[0].from, 10, &lnum);  			if (!result) {  				entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum);  				if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum)) @@ -622,6 +623,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)  			else  				result = -EINVAL;  			break; +		case Opt_permit_directio: +			entry->flags |= IMA_PERMIT_DIRECTIO; +			break;  		case Opt_err:  			ima_log_string(ab, "UNKNOWN", p);  			result = -EINVAL; @@ -646,7 +650,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)   */  ssize_t ima_parse_add_rule(char *rule)  { -	const char *op = "update_policy"; +	static const char op[] = "update_policy";  	char *p;  	struct ima_rule_entry *entry;  	ssize_t result, len; diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index ff63fe00c19..552705d5a78 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -18,6 +18,9 @@   *       The measurement list is append-only. No entry is   *       ever removed or changed during the boot-cycle.   */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/module.h>  #include <linux/rculist.h>  #include <linux/slab.h> @@ -50,7 +53,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; @@ -72,7 +75,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry)  	qe = kmalloc(sizeof(*qe), GFP_KERNEL);  	if (qe == NULL) { -		pr_err("IMA: OUT OF MEMORY ERROR creating queue entry.\n"); +		pr_err("OUT OF MEMORY ERROR creating queue entry\n");  		return -ENOMEM;  	}  	qe->entry = entry; @@ -95,8 +98,7 @@ static int ima_pcr_extend(const u8 *hash)  	result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);  	if (result != 0) -		pr_err("IMA: Error Communicating to TPM chip, result: %d\n", -		       result); +		pr_err("Error Communicating to TPM chip, result: %d\n", result);  	return result;  } @@ -104,9 +106,10 @@ 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[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; @@ -114,7 +117,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,  	mutex_lock(&ima_extend_list_mutex);  	if (!violation) { -		memcpy(digest, entry->digest, sizeof digest); +		memcpy(digest, entry->digest, sizeof(digest));  		if (ima_lookup_digest_entry(digest)) {  			audit_cause = "hash_exists";  			result = -EEXIST; @@ -130,7 +133,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,  	}  	if (violation)		/* invalidate pcr */ -		memset(digest, 0xff, sizeof digest); +		memset(digest, 0xff, sizeof(digest));  	tpmresult = ima_pcr_extend(digest);  	if (tpmresult != 0) { @@ -141,8 +144,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;  } diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c new file mode 100644 index 00000000000..a076a967ec4 --- /dev/null +++ b/security/integrity/ima/ima_template.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2013 Politecnico di Torino, Italy + *                    TORSEC group -- http://security.polito.it + * + * Author: Roberto Sassu <roberto.sassu@polito.it> + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <crypto/hash_info.h> + +#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}, +	{.name = "ima-ng", .fmt = "d-ng|n-ng"}, +	{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"}, +}; + +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}, +	{.field_id = "d-ng", .field_init = ima_eventdigest_ng_init, +	 .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; +static struct ima_template_desc *lookup_template_desc(const char *name); + +static int __init ima_template_setup(char *str) +{ +	struct ima_template_desc *template_desc; +	int template_len = strlen(str); + +	/* +	 * Verify that a template with the supplied name exists. +	 * If not, use CONFIG_IMA_DEFAULT_TEMPLATE. +	 */ +	template_desc = lookup_template_desc(str); +	if (!template_desc) +		return 1; + +	/* +	 * Verify whether the current hash algorithm is supported +	 * by the 'ima' template. +	 */ +	if (template_len == 3 && strcmp(str, IMA_TEMPLATE_IMA_NAME) == 0 && +	    ima_hash_algo != HASH_ALGO_SHA1 && ima_hash_algo != HASH_ALGO_MD5) { +		pr_err("template does not support hash alg\n"); +		return 1; +	} + +	ima_template = template_desc; +	return 1; +} +__setup("ima_template=", ima_template_setup); + +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; + +	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 template_fmt_size(const 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(const char *template_fmt, +				     struct ima_template_field ***fields, +				     int *num_fields) +{ +	char *c, *template_fmt_copy, *template_fmt_ptr; +	int template_num_fields = template_fmt_size(template_fmt); +	int i, result = 0; + +	if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) +		return -EINVAL; + +	/* copying is needed as strsep() modifies the original buffer */ +	template_fmt_copy = kstrdup(template_fmt, GFP_KERNEL); +	if (template_fmt_copy == NULL) +		return -ENOMEM; + +	*fields = kzalloc(template_num_fields * sizeof(*fields), GFP_KERNEL); +	if (*fields == NULL) { +		result = -ENOMEM; +		goto out; +	} + +	template_fmt_ptr = template_fmt_copy; +	for (i = 0; (c = strsep(&template_fmt_ptr, "|")) != NULL && +	     i < template_num_fields; i++) { +		struct ima_template_field *f = lookup_template_field(c); + +		if (!f) { +			result = -ENOENT; +			goto out; +		} +		(*fields)[i] = f; +	} +	*num_fields = i; +out: +	if (result < 0) { +		kfree(*fields); +		*fields = NULL; +	} +	kfree(template_fmt_copy); +	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; +} + +struct ima_template_desc *ima_template_desc_current(void) +{ +	if (!ima_template) +		ima_template = +		    lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE); +	return ima_template; +} + +int ima_init_template(void) +{ +	int result; + +	result = init_defined_templates(); +	if (result < 0) +		return result; + +	return 0; +} diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c new file mode 100644 index 00000000000..1506f024857 --- /dev/null +++ b/security/integrity/ima/ima_template_lib.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2013 Politecnico di Torino, Italy + *                    TORSEC group -- http://security.polito.it + * + * Author: Roberto Sassu <roberto.sassu@polito.it> + * + * 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 <crypto/hash_info.h> + +#include "ima_template_lib.h" + +static bool ima_template_hash_algo_allowed(u8 algo) +{ +	if (algo == HASH_ALGO_SHA1 || algo == HASH_ALGO_MD5) +		return true; + +	return false; +} + +enum data_formats { +	DATA_FMT_DIGEST = 0, +	DATA_FMT_DIGEST_WITH_ALGO, +	DATA_FMT_STRING, +	DATA_FMT_HEX +}; + +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 = datalen; + +	if (datafmt == DATA_FMT_STRING) +		buflen = datalen + 1; + +	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_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) +{ +	u8 *buf_ptr = field_data->data, buflen = field_data->len; + +	switch (datafmt) { +	case DATA_FMT_DIGEST_WITH_ALGO: +		buf_ptr = strnchr(field_data->data, buflen, ':'); +		if (buf_ptr != field_data->data) +			seq_printf(m, "%s", field_data->data); + +		/* skip ':' and '\0' */ +		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: +		seq_printf(m, "%s", buf_ptr); +		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) +{ +	u32 len = (show == IMA_SHOW_BINARY_OLD_STRING_FMT) ? +	    strlen(field_data->data) : field_data->len; + +	if (show != IMA_SHOW_BINARY_NO_FIELD_LEN) +		ima_putc(m, &len, sizeof(len)); + +	if (!len) +		return; + +	ima_putc(m, field_data->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: +	case IMA_SHOW_BINARY_NO_FIELD_LEN: +	case IMA_SHOW_BINARY_OLD_STRING_FMT: +		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_digest_ng(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_WITH_ALGO, +				     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); +} + +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) +{ +	/* +	 * digest formats: +	 *  - DATA_FMT_DIGEST: digest +	 *  - DATA_FMT_DIGEST_WITH_ALGO: [<hash algo>] + ':' + '\0' + digest, +	 *    where <hash algo> is provided if the hash algoritm is not +	 *    SHA1 or MD5 +	 */ +	u8 buffer[CRYPTO_MAX_ALG_NAME + 2 + IMA_MAX_DIGEST_SIZE] = { 0 }; +	enum data_formats fmt = DATA_FMT_DIGEST; +	u32 offset = 0; + +	if (hash_algo < HASH_ALGO__LAST) { +		fmt = DATA_FMT_DIGEST_WITH_ALGO; +		offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1, "%s", +				   hash_algo_name[hash_algo]); +		buffer[offset] = ':'; +		offset += 2; +	} + +	if (digest) +		memcpy(buffer + offset, digest, digestsize); +	else +		/* +		 * If digest is NULL, the event being recorded is a violation. +		 * Make room for the digest by increasing the offset of +		 * IMA_DIGEST_SIZE. +		 */ +		offset += IMA_DIGEST_SIZE; + +	return ima_write_template_field_data(buffer, offset + digestsize, +					     fmt, field_data); +} + +/* + * This function writes the digest of an event (with size limit). + */ +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 { +		struct ima_digest_data hdr; +		char digest[IMA_MAX_DIGEST_SIZE]; +	} hash; +	u8 *cur_digest = NULL; +	u32 cur_digestsize = 0; +	struct inode *inode; +	int result; + +	memset(&hash, 0, sizeof(hash)); + +	if (!iint)		/* recording a violation. */ +		goto out; + +	if (ima_template_hash_algo_allowed(iint->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_template_hash_algo_allowed(ima_hash_algo) ? +	    ima_hash_algo : HASH_ALGO_SHA1; +	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; +	} +	cur_digest = hash.hdr.digest; +	cur_digestsize = hash.hdr.length; +out: +	return ima_eventdigest_init_common(cur_digest, cur_digestsize, +					   HASH_ALGO__LAST, field_data); +} + +/* + * This function writes the digest of an event (without size limit). + */ +int ima_eventdigest_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) +{ +	u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1; +	u32 cur_digestsize = 0; + +	/* If iint is NULL, we are recording a violation. */ +	if (!iint) +		goto out; + +	cur_digest = iint->ima_hash->digest; +	cur_digestsize = iint->ima_hash->length; + +	hash_algo = iint->ima_hash->algo; +out: +	return ima_eventdigest_init_common(cur_digest, cur_digestsize, +					   hash_algo, field_data); +} + +static int ima_eventname_init_common(struct integrity_iint_cache *iint, +				     struct file *file, +				     const unsigned char *filename, +				     struct ima_field_data *field_data, +				     bool size_limit) +{ +	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 (!size_limit || 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_STRING, field_data); +} + +/* + * This function writes the name of an event (with size limit). + */ +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, +					 field_data, true); +} + +/* + * This function writes the name of an event (without size limit). + */ +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 new file mode 100644 index 00000000000..63f6b52cb1c --- /dev/null +++ b/security/integrity/ima/ima_template_lib.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 Politecnico di Torino, Italy + *                    TORSEC group -- http://security.polito.it + * + * Author: Roberto Sassu <roberto.sassu@polito.it> + * + * 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 <linux/seq_file.h> +#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_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 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 */ diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index c42fb7a70de..33c0a70f6b1 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -30,6 +30,7 @@  #define IMA_ACTION_FLAGS	0xff000000  #define IMA_DIGSIG		0x01000000  #define IMA_DIGSIG_REQUIRED	0x02000000 +#define IMA_PERMIT_DIRECTIO	0x04000000  #define IMA_DO_MASK		(IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \  				 IMA_APPRAISE_SUBMASK) @@ -54,25 +55,57 @@ enum evm_ima_xattr_type {  	IMA_XATTR_DIGEST = 0x01,  	EVM_XATTR_HMAC,  	EVM_IMA_XATTR_DIGSIG, +	IMA_XATTR_DIGEST_NG,  };  struct evm_ima_xattr_data {  	u8 type;  	u8 digest[SHA1_DIGEST_SIZE]; -}  __attribute__((packed)); +} __packed; + +#define IMA_MAX_DIGEST_SIZE	64 + +struct ima_digest_data { +	u8 algo; +	u8 length; +	union { +		struct { +			u8 unused; +			u8 type; +		} sha1; +		struct { +			u8 type; +			u8 algo; +		} ng; +		u8 data[2]; +	} xattr; +	u8 digest[0]; +} __packed; + +/* + * signature format v2 - for using with asymmetric keys + */ +struct signature_v2_hdr { +	uint8_t type;		/* xattr type */ +	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 */ +	struct rb_node rb_node;	/* rooted in integrity_iint_tree */  	struct inode *inode;	/* back pointer to inode in question */  	u64 version;		/* track inode changes */  	unsigned long flags; -	struct evm_ima_xattr_data ima_xattr;  	enum integrity_status ima_file_status:4;  	enum integrity_status ima_mmap_status:4;  	enum integrity_status ima_bprm_status:4;  	enum integrity_status ima_module_status:4;  	enum integrity_status evm_status:4; +	struct ima_digest_data *ima_hash;  };  /* rbtree tree calls to lookup, insert, delete @@ -89,7 +122,7 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode);  #ifdef CONFIG_INTEGRITY_SIGNATURE  int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, -					const char *digest, int digestlen); +			    const char *digest, int digestlen);  #else diff --git a/security/integrity/integrity_audit.c b/security/integrity/integrity_audit.c index d7efb30404a..90987d15b6f 100644 --- a/security/integrity/integrity_audit.c +++ b/security/integrity/integrity_audit.c @@ -7,7 +7,7 @@   * the Free Software Foundation, version 2 of the License.   *   * File: integrity_audit.c - * 	Audit calls for the integrity subsystem + *	Audit calls for the integrity subsystem   */  #include <linux/fs.h> @@ -22,7 +22,7 @@ static int __init integrity_audit_setup(char *str)  {  	unsigned long audit; -	if (!strict_strtoul(str, 0, &audit)) +	if (!kstrtoul(str, 0, &audit))  		integrity_audit_info = audit ? 1 : 0;  	return 1;  } @@ -33,13 +33,14 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,  			 const char *cause, int result, int audit_info)  {  	struct audit_buffer *ab; +	char name[TASK_COMM_LEN];  	if (!integrity_audit_info && audit_info == 1)	/* Skip info messages */  		return;  	ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);  	audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u", -			 current->pid, +			 task_pid_nr(current),  			 from_kuid(&init_user_ns, current_cred()->uid),  			 from_kuid(&init_user_ns, audit_get_loginuid(current)),  			 audit_get_sessionid(current)); @@ -49,7 +50,7 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,  	audit_log_format(ab, " cause=");  	audit_log_string(ab, cause);  	audit_log_format(ab, " comm="); -	audit_log_untrustedstring(ab, current->comm); +	audit_log_untrustedstring(ab, get_task_comm(name, current));  	if (fname) {  		audit_log_format(ab, " name=");  		audit_log_untrustedstring(ab, fname);  | 
