Re: [PATCH v3 04/12] ima: integrity appraisal extension

From: Kasatkin, Dmitry
Date: Thu Mar 22 2012 - 10:28:45 EST


On Wed, Mar 21, 2012 at 8:54 PM, Mimi Zohar <zohar@xxxxxxxxxxxxxxxxxx> wrote:
> IMA currently maintains an integrity measurement list used to assert the
> integrity of the running system to a third party. ÂThe IMA-appraisal
> extension adds local integrity validation and enforcement of the
> measurement against a "good" value stored as an extended attribute
> 'security.ima'. ÂThe initial methods for validating 'security.ima' are
> hashed based, which provides file data integrity, and digital signature
> based, which in addition to providing file data integrity, provides
> authenticity.
>
> This patch creates and maintains the 'security.ima' xattr, containing
> the file data hash measurement. ÂProtection of the xattr is provided by
> EVM, if enabled and configured.
>
> Based on policy, IMA calls evm_verifyxattr() to verify a file's metadata
> integrity and, assuming success, compares the file's current hash value
> with the one stored as an extended attribute in 'security.ima'.
>
> Changelog v3:
> - change appraisal default for filesystems without xattr support to fail
>
> Changelog v2:
> - fix audit msg 'res' value
> - removed unused 'ima_appraise=' values
>
> Changelog v1:
> - removed unused iint mutex (Dmitry Kasatkin)
> - setattr hook must not reset appraised (Dmitry Kasatkin)
> - evm_verifyxattr() now differentiates between no 'security.evm' xattr
> Â(INTEGRITY_NOLABEL) and no EVM 'protected' xattrs included in the
> Â'security.evm' (INTEGRITY_NOXATTRS).
> - replace hash_status with ima_status (Dmitry Kasatkin)
> - re-initialize slab element ima_status on free (Dmitry Kasatkin)
> - include 'security.ima' in EVM if CONFIG_IMA_APPRAISE, not CONFIG_IMA
> - merged half "ima: ima_must_appraise_or_measure API change" (Dmitry Kasatkin)
> - removed unnecessary error variable in process_measurement() (Dmitry Kasatkin)
> - use ima_inode_post_setattr() stub function, if IMA_APPRAISE not configured
> Â(moved ima_inode_post_setattr() to ima_appraise.c)
> - make sure ima_collect_measurement() can read file
>
> Changelog:
> - add 'iint' to evm_verifyxattr() call (Dimitry Kasatkin)
> - fix the race condition between chmod, which takes the i_mutex and then
> Âiint->mutex, and ima_file_free() and process_measurement(), which take
> Âthe locks in the reverse order, by eliminating iint->mutex. (Dmitry Kasatkin)
> - cleanup of ima_appraise_measurement() (Dmitry Kasatkin)
> - changes as a result of the iint not allocated for all regular files, but
> Âonly for those measured/appraised.
> - don't try to appraise new/empty files
> - expanded ima_appraisal description in ima/Kconfig
> - IMA appraise definitions required even if IMA_APPRAISE not enabled
> - add return value to ima_must_appraise() stub
> - unconditionally set status = INTEGRITY_PASS *after* testing status,
> Ânot before. Â(Found by Joe Perches)
>
> Signed-off-by: Mimi Zohar <zohar@xxxxxxxxxx>

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@xxxxxxxxx>

> ---
> ÂDocumentation/kernel-parameters.txt  |  Â4 +
> Âinclude/linux/xattr.h         |  Â3 +
> Âsecurity/integrity/evm/evm_main.c   |  Â3 +
> Âsecurity/integrity/iint.c       |  Â3 +-
> Âsecurity/integrity/ima/Kconfig    Â|  15 +++
> Âsecurity/integrity/ima/Makefile    |  Â2 +
> Âsecurity/integrity/ima/ima.h     Â|  37 +++++++-
> Âsecurity/integrity/ima/ima_api.c   Â|  50 +++++++---
> Âsecurity/integrity/ima/ima_appraise.c | Â168 +++++++++++++++++++++++++++++++++
> Âsecurity/integrity/ima/ima_crypto.c  |  Â8 ++-
> Âsecurity/integrity/ima/ima_main.c   |  78 ++++++++++-----
> Âsecurity/integrity/ima/ima_policy.c  |  32 +++++--
> Âsecurity/integrity/integrity.h    Â|  Â8 +-
> Â13 files changed, 358 insertions(+), 53 deletions(-)
> Âcreate mode 100644 security/integrity/ima/ima_appraise.c
>
> diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
> index 033d4e6..a86765d 100644
> --- a/Documentation/kernel-parameters.txt
> +++ b/Documentation/kernel-parameters.txt
> @@ -1004,6 +1004,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
> Â Â Â Âihash_entries= Â[KNL]
> Â Â Â Â Â Â Â Â Â Â Â ÂSet number of hash buckets for inode cache.
>
> + Â Â Â ima_appraise= Â [IMA] appraise integrity measurements
> + Â Â Â Â Â Â Â Â Â Â Â Format: { "off" | "enforce" | "fix" }
> + Â Â Â Â Â Â Â Â Â Â Â default: "enforce"
> +
> Â Â Â Âima_audit= Â Â Â[IMA]
> Â Â Â Â Â Â Â Â Â Â Â ÂFormat: { "0" | "1" }
> Â Â Â Â Â Â Â Â Â Â Â Â0 -- integrity auditing messages. (Default)
> diff --git a/include/linux/xattr.h b/include/linux/xattr.h
> index e5d1220..77a3e68 100644
> --- a/include/linux/xattr.h
> +++ b/include/linux/xattr.h
> @@ -33,6 +33,9 @@
> Â#define XATTR_EVM_SUFFIX "evm"
> Â#define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX
>
> +#define XATTR_IMA_SUFFIX "ima"
> +#define XATTR_NAME_IMA XATTR_SECURITY_PREFIX XATTR_IMA_SUFFIX
> +
> Â#define XATTR_SELINUX_SUFFIX "selinux"
> Â#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
>
> diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
> index 8901501..eb54845 100644
> --- a/security/integrity/evm/evm_main.c
> +++ b/security/integrity/evm/evm_main.c
> @@ -34,6 +34,9 @@ char *evm_config_xattrnames[] = {
> Â#ifdef CONFIG_SECURITY_SMACK
> Â Â Â ÂXATTR_NAME_SMACK,
> Â#endif
> +#ifdef CONFIG_IMA_APPRAISE
> + Â Â Â XATTR_NAME_IMA,
> +#endif
> Â Â Â ÂXATTR_NAME_CAPS,
> Â Â Â ÂNULL
> Â};
> diff --git a/security/integrity/iint.c b/security/integrity/iint.c
> index 399641c..e600986 100644
> --- a/security/integrity/iint.c
> +++ b/security/integrity/iint.c
> @@ -74,6 +74,7 @@ static void iint_free(struct integrity_iint_cache *iint)
> Â{
> Â Â Â Âiint->version = 0;
> Â Â Â Âiint->flags = 0UL;
> + Â Â Â iint->ima_status = INTEGRITY_UNKNOWN;
> Â Â Â Âiint->evm_status = INTEGRITY_UNKNOWN;
> Â Â Â Âkmem_cache_free(iint_cache, iint);
> Â}
> @@ -157,7 +158,7 @@ static void init_once(void *foo)
> Â Â Â Âmemset(iint, 0, sizeof *iint);
> Â Â Â Âiint->version = 0;
> Â Â Â Âiint->flags = 0UL;
> - Â Â Â mutex_init(&iint->mutex);
> + Â Â Â iint->ima_status = INTEGRITY_UNKNOWN;
> Â Â Â Âiint->evm_status = INTEGRITY_UNKNOWN;
> Â}
>
> diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> index 35664fe..b7465a1 100644
> --- a/security/integrity/ima/Kconfig
> +++ b/security/integrity/ima/Kconfig
> @@ -54,3 +54,18 @@ config IMA_LSM_RULES
> Â Â Â Âdefault y
> Â Â Â Âhelp
> Â Â Â Â ÂDisabling this option will disregard LSM based policy rules.
> +
> +config IMA_APPRAISE
> + Â Â Â bool "Appraise integrity measurements"
> + Â Â Â depends on IMA
> + Â Â Â default n
> + Â Â Â help
> + Â Â Â Â This option enables local measurement integrity appraisal.
> + Â Â Â Â It requires the system to be labeled with a security extended
> + Â Â Â Â attribute containing the file hash measurement. ÂTo protect
> + Â Â Â Â the security extended attributes from offline attack, enable
> + Â Â Â Â and configure EVM.
> +
> + Â Â Â Â For more information on integrity appraisal refer to:
> + Â Â Â Â <http://linux-ima.sourceforge.net>
> + Â Â Â Â If unsure, say N.
> diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
> index 5690c02..bd31516 100644
> --- a/security/integrity/ima/Makefile
> +++ b/security/integrity/ima/Makefile
> @@ -7,3 +7,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_audit.o
> +
> +ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index 3ccf7ac..d5bf463 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -40,6 +40,7 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
> Âextern int ima_initialized;
> Âextern int ima_used_chip;
> Âextern char *ima_hash;
> +extern int ima_appraise;
>
> Â/* IMA inode template definition */
> Âstruct ima_template_data {
> @@ -98,6 +99,7 @@ static inline unsigned long ima_hash_key(u8 *digest)
> Â}
>
> Â/* LIM API function definitions */
> +int ima_must_appraise_or_measure(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);
> @@ -114,14 +116,45 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
> Âstruct integrity_iint_cache *integrity_iint_find(struct inode *inode);
>
> Â/* IMA policy related functions */
> -enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };
> +enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, POST_SETATTR };
>
> -int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
> +int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
> + Â Â Â Â Â Â Â Â Â Âint flags);
> Âvoid ima_init_policy(void);
> Âvoid ima_update_policy(void);
> Âssize_t ima_parse_add_rule(char *);
> Âvoid ima_delete_rules(void);
>
> +/* Appraise integrity measurements */
> +#define IMA_APPRAISE_ENFORCE Â 0x01
> +#define IMA_APPRAISE_FIX Â Â Â 0x02
> +
> +#ifdef CONFIG_IMA_APPRAISE
> +int ima_appraise_measurement(struct integrity_iint_cache *iint,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct file *file, const unsigned char *filename);
> +int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask);
> +void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
> +
> +#else
> +static inline int ima_appraise_measurement(struct integrity_iint_cache *iint,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct file *file,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âconst unsigned char *filename)
> +{
> + Â Â Â return INTEGRITY_UNKNOWN;
> +}
> +
> +static inline int ima_must_appraise(struct inode *inode,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â enum ima_hooks func, int mask)
> +{
> + Â Â Â return 0;
> +}
> +
> +static inline void ima_update_xattr(struct integrity_iint_cache *iint,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct file *file)
> +{
> +}
> +#endif
> +
> Â/* LSM based policy rules require audit */
> Â#ifdef CONFIG_IMA_LSM_RULES
>
> diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
> index 88a2788..55deeb1 100644
> --- a/security/integrity/ima/ima_api.c
> +++ b/security/integrity/ima/ima_api.c
> @@ -9,13 +9,17 @@
> Â* License.
> Â*
> Â* File: ima_api.c
> - * Â Â Implements must_measure, collect_measurement, store_measurement,
> - * Â Â and store_template.
> + * Â Â Implements must_appraise_or_measure, collect_measurement,
> + * Â Â appraise_measurement, store_measurement and store_template.
> Â*/
> Â#include <linux/module.h>
> Â#include <linux/slab.h>
> -
> +#include <linux/file.h>
> +#include <linux/fs.h>
> +#include <linux/xattr.h>
> +#include <linux/evm.h>
> Â#include "ima.h"
> +
> Âstatic const char *IMA_TEMPLATE_NAME = "ima";
>
> Â/*
> @@ -93,7 +97,7 @@ err_out:
> Â}
>
> Â/**
> - * ima_must_measure - measure decision based on policy.
> + * ima_must_appraise_or_measure - appraise & measure decision based on policy.
> Â* @inode: pointer to inode to measure
> Â* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
> Â* @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP)
> @@ -105,15 +109,22 @@ err_out:
> Â* Â Â mask: contains the permission mask
> Â* Â Â fsmagic: hex value
> Â*
> - * Return 0 to measure. For matching a DONT_MEASURE policy, no policy,
> - * or other error, return an error code.
> -*/
> -int ima_must_measure(struct inode *inode, int mask, int function)
> + * Returns IMA_MEASURE, IMA_APPRAISE mask.
> + *
> + */
> +int ima_must_appraise_or_measure(struct inode *inode, int mask, int function)
> Â{
> - Â Â Â int must_measure;
> + Â Â Â int flags = IMA_MEASURE | IMA_APPRAISE;
> +
> + Â Â Â if (!ima_appraise)
> + Â Â Â Â Â Â Â flags &= ~IMA_APPRAISE;
> +
> + Â Â Â return ima_match_policy(inode, function, mask, flags);
> +}
>
> - Â Â Â must_measure = ima_match_policy(inode, function, mask);
> - Â Â Â return must_measure ? 0 : -EACCES;
> +int ima_must_measure(struct inode *inode, int mask, int function)
> +{
> + Â Â Â return ima_match_policy(inode, function, mask, IMA_MEASURE);
> Â}
>
> Â/*
> @@ -129,16 +140,24 @@ int ima_must_measure(struct inode *inode, int mask, int function)
> Âint ima_collect_measurement(struct integrity_iint_cache *iint,
> Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct file *file)
> Â{
> - Â Â Â int result = -EEXIST;
> + Â Â Â struct inode *inode = file->f_dentry->d_inode;
> + Â Â Â const char *filename = file->f_dentry->d_name.name;
> + Â Â Â int result = 0;
>
> - Â Â Â if (!(iint->flags & IMA_MEASURED)) {
> + Â Â Â if (!(iint->flags & IMA_COLLECTED)) {
> Â Â Â Â Â Â Â Âu64 i_version = file->f_dentry->d_inode->i_version;
>
> Â Â Â Â Â Â Â Âmemset(iint->digest, 0, IMA_DIGEST_SIZE);
> Â Â Â Â Â Â Â Âresult = ima_calc_hash(file, iint->digest);
> - Â Â Â Â Â Â Â if (!result)
> + Â Â Â Â Â Â Â if (!result) {
> Â Â Â Â Â Â Â Â Â Â Â Âiint->version = i_version;
> + Â Â Â Â Â Â Â Â Â Â Â iint->flags |= IMA_COLLECTED;
> + Â Â Â Â Â Â Â }
> Â Â Â Â}
> + Â Â Â if (result)
> + Â Â Â Â Â Â Â integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â filename, "collect_data", "failed",
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â result, 0);
> Â Â Â Âreturn result;
> Â}
>
> @@ -167,6 +186,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
> Â Â Â Âstruct ima_template_entry *entry;
> Â Â Â Âint violation = 0;
>
> + Â Â Â if (iint->flags & IMA_MEASURED)
> + Â Â Â Â Â Â Â return;
> +
> Â Â Â Âentry = kmalloc(sizeof(*entry), GFP_KERNEL);
> Â Â Â Âif (!entry) {
> Â Â Â Â Â Â Â Âintegrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
> diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
> new file mode 100644
> index 0000000..4865f61
> --- /dev/null
> +++ b/security/integrity/ima/ima_appraise.c
> @@ -0,0 +1,168 @@
> +/*
> + * Copyright (C) 2011 IBM Corporation
> + *
> + * Author:
> + * Mimi Zohar <zohar@xxxxxxxxxx>
> + *
> + * 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.
> + */
> +#include <linux/module.h>
> +#include <linux/file.h>
> +#include <linux/fs.h>
> +#include <linux/xattr.h>
> +#include <linux/magic.h>
> +#include <linux/ima.h>
> +#include <linux/evm.h>
> +
> +#include "ima.h"
> +
> +static int __init default_appraise_setup(char *str)
> +{
> + Â Â Â if (strncmp(str, "off", 3) == 0)
> + Â Â Â Â Â Â Â ima_appraise = 0;
> + Â Â Â else if (strncmp(str, "fix", 3) == 0)
> + Â Â Â Â Â Â Â ima_appraise = IMA_APPRAISE_FIX;
> + Â Â Â return 1;
> +}
> +
> +__setup("ima_appraise=", default_appraise_setup);
> +
> +/*
> + * ima_must_appraise - set appraise flag
> + *
> + * Return 1 to appraise
> + */
> +int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask)
> +{
> + Â Â Â return 0;
> +}
> +
> +static void ima_fix_xattr(struct dentry *dentry,
> + Â Â Â Â Â Â Â Â Â Â Â Â struct integrity_iint_cache *iint)
> +{
> + Â Â Â iint->digest[0] = IMA_XATTR_DIGEST;
> + Â Â Â __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â iint->digest, IMA_DIGEST_SIZE + 1, 0);
> +}
> +
> +/*
> + * ima_appraise_measurement - appraise file measurement
> + *
> + * Call evm_verifyxattr() to verify the integrity of 'security.ima'.
> + * Assuming success, compare the xattr hash with the collected measurement.
> + *
> + * Return 0 on success, error code otherwise
> + */
> +int ima_appraise_measurement(struct integrity_iint_cache *iint,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct file *file, const unsigned char *filename)
> +{
> + Â Â Â struct dentry *dentry = file->f_dentry;
> + Â Â Â struct inode *inode = dentry->d_inode;
> + Â Â Â u8 xattr_value[IMA_DIGEST_SIZE];
> + Â Â Â enum integrity_status status = INTEGRITY_UNKNOWN;
> + Â Â Â const char *op = "appraise_data";
> + Â Â Â char *cause = "unknown";
> + Â Â Â int rc;
> +
> + Â Â Â if (!ima_appraise)
> + Â Â Â Â Â Â Â return 0;
> + Â Â Â if (!inode->i_op->getxattr)
> + Â Â Â Â Â Â Â return INTEGRITY_UNKNOWN;
> +
> + Â Â Â if (iint->flags & IMA_APPRAISED)
> + Â Â Â Â Â Â Â return iint->ima_status;
> +
> + Â Â Â rc = inode->i_op->getxattr(dentry, XATTR_NAME_IMA, xattr_value,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂIMA_DIGEST_SIZE);
> + Â Â Â if (rc <= 0) {
> + Â Â Â Â Â Â Â if (rc && rc != -ENODATA)
> + Â Â Â Â Â Â Â Â Â Â Â goto out;
> +
> + Â Â Â Â Â Â Â cause = "missing-hash";
> + Â Â Â Â Â Â Â status =
> + Â Â Â Â Â Â Â Â Â (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL;
> + Â Â Â Â Â Â Â goto out;
> + Â Â Â }
> +
> + Â Â Â status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
> + Â Â Â if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) {
> + Â Â Â Â Â Â Â if ((status == INTEGRITY_NOLABEL)
> + Â Â Â Â Â Â Â Â Â || (status == INTEGRITY_NOXATTRS))
> + Â Â Â Â Â Â Â Â Â Â Â cause = "missing-HMAC";
> + Â Â Â Â Â Â Â else if (status == INTEGRITY_FAIL)
> + Â Â Â Â Â Â Â Â Â Â Â cause = "invalid-HMAC";
> + Â Â Â Â Â Â Â goto out;
> + Â Â Â }
> +
> + Â Â Â rc = memcmp(xattr_value, iint->digest, IMA_DIGEST_SIZE);
> + Â Â Â if (rc) {
> + Â Â Â Â Â Â Â status = INTEGRITY_FAIL;
> + Â Â Â Â Â Â Â cause = "invalid-hash";
> + Â Â Â Â Â Â Â print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âxattr_value, IMA_DIGEST_SIZE);
> + Â Â Â Â Â Â Â print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âiint->digest, IMA_DIGEST_SIZE);
> + Â Â Â Â Â Â Â goto out;
> + Â Â Â }
> + Â Â Â status = INTEGRITY_PASS;
> + Â Â Â iint->flags |= IMA_APPRAISED;
> +out:
> + Â Â Â if (status != INTEGRITY_PASS) {
> + Â Â Â Â Â Â Â if (ima_appraise & IMA_APPRAISE_FIX) {
> + Â Â Â Â Â Â Â Â Â Â Â ima_fix_xattr(dentry, iint);
> + Â Â Â Â Â Â Â Â Â Â Â status = INTEGRITY_PASS;
> + Â Â Â Â Â Â Â }
> + Â Â Â Â Â Â Â integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â op, cause, rc, 0);
> + Â Â Â }
> + Â Â Â iint->ima_status = status;
> + Â Â Â return status;
> +}
> +
> +/*
> + * ima_update_xattr - update 'security.ima' hash value
> + */
> +void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
> +{
> + Â Â Â struct dentry *dentry = file->f_dentry;
> + Â Â Â int rc = 0;
> +
> + Â Â Â rc = ima_collect_measurement(iint, file);
> + Â Â Â if (rc < 0)
> + Â Â Â Â Â Â Â return;
> + Â Â Â ima_fix_xattr(dentry, iint);
> +}
> +
> +/**
> + * ima_inode_post_setattr - reflect file metadata changes
> + * @dentry: pointer to the affected dentry
> + *
> + * Changes to a dentry's metadata might result in needing to appraise.
> + *
> + * This function is called from notify_change(), which expects the caller
> + * to lock the inode's i_mutex.
> + */
> +void ima_inode_post_setattr(struct dentry *dentry)
> +{
> + Â Â Â struct inode *inode = dentry->d_inode;
> + Â Â Â struct integrity_iint_cache *iint;
> + Â Â Â int must_appraise, rc;
> +
> + Â Â Â if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)
> + Â Â Â Â Â || !inode->i_op->removexattr)
> + Â Â Â Â Â Â Â return;
> +
> + Â Â Â must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
> + Â Â Â iint = integrity_iint_find(inode);
> + Â Â Â if (iint) {
> + Â Â Â Â Â Â Â if (must_appraise)
> + Â Â Â Â Â Â Â Â Â Â Â iint->flags |= IMA_APPRAISE;
> + Â Â Â Â Â Â Â else
> + Â Â Â Â Â Â Â Â Â Â Â iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED);
> + Â Â Â }
> + Â Â Â if (!must_appraise)
> + Â Â Â Â Â Â Â rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
> + Â Â Â return;
> +}
> diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
> index 9b3ade7..b21ee5b 100644
> --- a/security/integrity/ima/ima_crypto.c
> +++ b/security/integrity/ima/ima_crypto.c
> @@ -48,7 +48,7 @@ int ima_calc_hash(struct file *file, char *digest)
> Â Â Â Âstruct scatterlist sg[1];
> Â Â Â Âloff_t i_size, offset = 0;
> Â Â Â Âchar *rbuf;
> - Â Â Â int rc;
> + Â Â Â int rc, read = 0;
>
> Â Â Â Ârc = init_desc(&desc);
> Â Â Â Âif (rc != 0)
> @@ -59,6 +59,10 @@ int ima_calc_hash(struct file *file, char *digest)
> Â Â Â Â Â Â Â Ârc = -ENOMEM;
> Â Â Â Â Â Â Â Âgoto out;
> Â Â Â Â}
> + Â Â Â if (!(file->f_mode & FMODE_READ)) {
> + Â Â Â Â Â Â Â file->f_mode |= FMODE_READ;
> + Â Â Â Â Â Â Â read = 1;
> + Â Â Â }
> Â Â Â Âi_size = i_size_read(file->f_dentry->d_inode);
> Â Â Â Âwhile (offset < i_size) {
> Â Â Â Â Â Â Â Âint rbuf_len;
> @@ -80,6 +84,8 @@ int ima_calc_hash(struct file *file, char *digest)
> Â Â Â Âkfree(rbuf);
> Â Â Â Âif (!rc)
> Â Â Â Â Â Â Â Ârc = crypto_hash_final(&desc, digest);
> + Â Â Â if (read)
> + Â Â Â Â Â Â Â file->f_mode &= ~FMODE_READ;
> Âout:
> Â Â Â Âcrypto_free_hash(desc.tfm);
> Â Â Â Âreturn rc;
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 5b222eb..fba2f7b 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -22,12 +22,19 @@
> Â#include <linux/mount.h>
> Â#include <linux/mman.h>
> Â#include <linux/slab.h>
> +#include <linux/xattr.h>
> Â#include <linux/ima.h>
>
> Â#include "ima.h"
>
> Âint ima_initialized;
>
> +#ifdef CONFIG_IMA_APPRAISE
> +int ima_appraise = IMA_APPRAISE_ENFORCE;
> +#else
> +int ima_appraise;
> +#endif
> +
> Âchar *ima_hash = "sha1";
> Âstatic int __init hash_setup(char *str)
> Â{
> @@ -52,7 +59,7 @@ static void ima_rdwr_violation_check(struct file *file)
> Â Â Â Âstruct dentry *dentry = file->f_path.dentry;
> Â Â Â Âstruct inode *inode = dentry->d_inode;
> Â Â Â Âfmode_t mode = file->f_mode;
> - Â Â Â int rc;
> + Â Â Â int must_measure;
> Â Â Â Âbool send_tomtou = false, send_writers = false;
>
> Â Â Â Âif (!S_ISREG(inode->i_mode) || !ima_initialized)
> @@ -66,8 +73,8 @@ static void ima_rdwr_violation_check(struct file *file)
> Â Â Â Â Â Â Â Âgoto out;
> Â Â Â Â}
>
> - Â Â Â rc = ima_must_measure(inode, MAY_READ, FILE_CHECK);
> - Â Â Â if (rc < 0)
> + Â Â Â must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK);
> + Â Â Â if (!must_measure)
> Â Â Â Â Â Â Â Âgoto out;
>
> Â Â Â Âif (atomic_read(&inode->i_writecount) > 0)
> @@ -84,17 +91,21 @@ out:
> Â}
>
> Âstatic void ima_check_last_writer(struct integrity_iint_cache *iint,
> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct inode *inode,
> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct file *file)
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct inode *inode, struct file *file)
> Â{
> Â Â Â Âfmode_t mode = file->f_mode;
>
> - Â Â Â mutex_lock(&iint->mutex);
> - Â Â Â if (mode & FMODE_WRITE &&
> - Â Â Â Â Â atomic_read(&inode->i_writecount) == 1 &&
> - Â Â Â Â Â iint->version != inode->i_version)
> - Â Â Â Â Â Â Â iint->flags &= ~IMA_MEASURED;
> - Â Â Â mutex_unlock(&iint->mutex);
> + Â Â Â if (!(mode & FMODE_WRITE))
> + Â Â Â Â Â Â Â return;
> +
> + Â Â Â mutex_lock(&inode->i_mutex);
> + Â Â Â if (atomic_read(&inode->i_writecount) == 1 &&
> + Â Â Â Â Â iint->version != inode->i_version) {
> + Â Â Â Â Â Â Â iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED);
> + Â Â Â Â Â Â Â if (iint->flags & IMA_APPRAISE)
> + Â Â Â Â Â Â Â Â Â Â Â ima_update_xattr(iint, file);
> + Â Â Â }
> + Â Â Â mutex_unlock(&inode->i_mutex);
> Â}
>
> Â/**
> @@ -123,14 +134,17 @@ static int process_measurement(struct file *file, const unsigned char *filename,
> Â{
> Â Â Â Âstruct inode *inode = file->f_dentry->d_inode;
> Â Â Â Âstruct integrity_iint_cache *iint;
> - Â Â Â int rc = 0;
> + Â Â Â int rc = -ENOMEM, action, must_appraise;
>
> Â Â Â Âif (!ima_initialized || !S_ISREG(inode->i_mode))
> Â Â Â Â Â Â Â Âreturn 0;
>
> - Â Â Â rc = ima_must_measure(inode, mask, function);
> - Â Â Â if (rc != 0)
> - Â Â Â Â Â Â Â return rc;
> + Â Â Â /* Determine if in appraise/measurement policy,
> + Â Â Â Â* returns IMA_MEASURE, IMA_APPRAISE bitmask. Â*/
> + Â Â Â action = ima_must_appraise_or_measure(inode, mask, function);
> + Â Â Â if (!action)
> + Â Â Â Â Â Â Â return 0;
> +
> Âretry:
> Â Â Â Âiint = integrity_iint_find(inode);
> Â Â Â Âif (!iint) {
> @@ -140,18 +154,32 @@ retry:
> Â Â Â Â Â Â Â Âreturn rc;
> Â Â Â Â}
>
> - Â Â Â mutex_lock(&iint->mutex);
> + Â Â Â must_appraise = action & IMA_APPRAISE;
>
> - Â Â Â rc = iint->flags & IMA_MEASURED ? 1 : 0;
> - Â Â Â if (rc != 0)
> + Â Â Â mutex_lock(&inode->i_mutex);
> +
> + Â Â Â /* Determine if already appraised/measured based on bitmask
> + Â Â Â Â* (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED) */
> + Â Â Â iint->flags |= action;
> + Â Â Â action &= ~((iint->flags & (IMA_MEASURED | IMA_APPRAISED)) >> 1);
> +
> + Â Â Â /* Nothing to do, just return existing appraised status */
> + Â Â Â if (!action) {
> + Â Â Â Â Â Â Â if (iint->flags & IMA_APPRAISED)
> + Â Â Â Â Â Â Â Â Â Â Â rc = iint->ima_status;
> Â Â Â Â Â Â Â Âgoto out;
> + Â Â Â }
>
> Â Â Â Ârc = ima_collect_measurement(iint, file);
> - Â Â Â if (!rc)
> + Â Â Â if (rc != 0)
> + Â Â Â Â Â Â Â goto out;
> + Â Â Â if (action & IMA_MEASURE)
> Â Â Â Â Â Â Â Âima_store_measurement(iint, file, filename);
> + Â Â Â if (action & IMA_APPRAISE)
> + Â Â Â Â Â Â Â rc = ima_appraise_measurement(iint, file, filename);
> Âout:
> - Â Â Â mutex_unlock(&iint->mutex);
> - Â Â Â return rc;
> + Â Â Â mutex_unlock(&inode->i_mutex);
> + Â Â Â return (rc && must_appraise) ? -EACCES : 0;
> Â}
>
> Â/**
> @@ -167,14 +195,14 @@ out:
> Â*/
> Âint ima_file_mmap(struct file *file, unsigned long prot)
> Â{
> - Â Â Â int rc;
> + Â Â Â int rc = 0;
>
> Â Â Â Âif (!file)
> Â Â Â Â Â Â Â Âreturn 0;
> Â Â Â Âif (prot & PROT_EXEC)
> Â Â Â Â Â Â Â Ârc = process_measurement(file, file->f_dentry->d_name.name,
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â MAY_EXEC, FILE_MMAP);
> - Â Â Â return 0;
> + Â Â Â return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
> Â}
> ÂEXPORT_SYMBOL_GPL(ima_file_mmap);
>
> @@ -197,7 +225,7 @@ int ima_bprm_check(struct linux_binprm *bprm)
>
> Â Â Â Ârc = process_measurement(bprm->file, bprm->filename,
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â MAY_EXEC, BPRM_CHECK);
> - Â Â Â return 0;
> + Â Â Â return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
> Â}
>
> Â/**
> @@ -218,7 +246,7 @@ int ima_file_check(struct file *file, int mask)
> Â Â Â Ârc = process_measurement(file, file->f_dentry->d_name.name,
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â FILE_CHECK);
> - Â Â Â return 0;
> + Â Â Â return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
> Â}
> ÂEXPORT_SYMBOL_GPL(ima_file_check);
>
> diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
> index d8edff2..8ee301c 100644
> --- a/security/integrity/ima/ima_policy.c
> +++ b/security/integrity/ima/ima_policy.c
> @@ -25,7 +25,13 @@
> Â#define IMA_FSMAGIC Â Â0x0004
> Â#define IMA_UID Â Â Â Â Â Â Â Â0x0008
>
> -enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE };
> +#define UNKNOWN Â Â Â Â Â Â Â Â Â Â Â Â0
> +#define MEASURE Â Â Â Â Â Â Â Â Â Â Â Â1 Â Â Â /* same as IMA_MEASURE */
> +#define DONT_MEASURE Â Â Â Â Â 2
> +#define MEASURE_MASK Â Â Â Â Â 3
> +#define APPRAISE Â Â Â Â Â Â Â 4 Â Â Â /* same as IMA_APPRAISE */
> +#define DONT_APPRAISE Â Â Â Â Â8
> +#define APPRAISE_MASK Â Â Â Â Â12
>
> Â#define MAX_LSM_RULES 6
> Âenum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
> @@ -34,7 +40,7 @@ enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
>
> Âstruct ima_measure_rule_entry {
> Â Â Â Âstruct list_head list;
> - Â Â Â enum ima_action action;
> + Â Â Â int action;
> Â Â Â Âunsigned int flags;
> Â Â Â Âenum ima_hooks func;
> Â Â Â Âint mask;
> @@ -161,18 +167,28 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
> Â* as elements in the list are never deleted, nor does the list
> Â* change.)
> Â*/
> -int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
> +int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
> + Â Â Â Â Â Â Â Â Â Âint flags)
> Â{
> Â Â Â Âstruct ima_measure_rule_entry *entry;
> + Â Â Â int action = 0, actmask = flags | (flags << 1);
>
> Â Â Â Âlist_for_each_entry(entry, ima_measure, list) {
> - Â Â Â Â Â Â Â bool rc;
>
> - Â Â Â Â Â Â Â rc = ima_match_rules(entry, inode, func, mask);
> - Â Â Â Â Â Â Â if (rc)
> - Â Â Â Â Â Â Â Â Â Â Â return entry->action;
> + Â Â Â Â Â Â Â if (!(entry->action & actmask))
> + Â Â Â Â Â Â Â Â Â Â Â continue;
> +
> + Â Â Â Â Â Â Â if (!ima_match_rules(entry, inode, func, mask))
> + Â Â Â Â Â Â Â Â Â Â Â continue;
> +
> + Â Â Â Â Â Â Â action |= (entry->action & (IMA_APPRAISE | IMA_MEASURE));
> + Â Â Â Â Â Â Â actmask &= (entry->action & APPRAISE_MASK) ?
> + Â Â Â Â Â Â Â Â Â ~APPRAISE_MASK : ~MEASURE_MASK;
> + Â Â Â Â Â Â Â if (!actmask)
> + Â Â Â Â Â Â Â Â Â Â Â break;
> Â Â Â Â}
> - Â Â Â return 0;
> +
> + Â Â Â return action;
> Â}
>
> Â/**
> diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
> index 7a25ece..295702d 100644
> --- a/security/integrity/integrity.h
> +++ b/security/integrity/integrity.h
> @@ -16,7 +16,11 @@
> Â#include <crypto/sha.h>
>
> Â/* iint cache flags */
> -#define IMA_MEASURED Â Â Â Â Â 0x01
> +#define IMA_MEASURE Â Â Â Â Â Â1
> +#define IMA_MEASURED Â Â Â Â Â 2
> +#define IMA_APPRAISE Â Â Â Â Â 4
> +#define IMA_APPRAISED Â Â Â Â Â8
> +#define IMA_COLLECTED Â Â Â Â Â16
>
> Âenum evm_ima_xattr_type {
> Â Â Â ÂIMA_XATTR_DIGEST = 0x01,
> @@ -36,7 +40,7 @@ struct integrity_iint_cache {
> Â Â Â Âu64 version; Â Â Â Â Â Â/* track inode changes */
> Â Â Â Âunsigned char flags;
> Â Â Â Âu8 digest[SHA1_DIGEST_SIZE];
> - Â Â Â struct mutex mutex; Â Â /* protects: version, flags, digest */
> + Â Â Â enum integrity_status ima_status;
> Â Â Â Âenum integrity_status evm_status;
> Â};
>
> --
> 1.7.6.5
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/