From 20328b56cdf8fcc79f28c6c50ad8190fc0779e80 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Wed, 22 Aug 2012 15:01:47 -0500 Subject: ima: enable the IBM vTPM as the default TPM in the PPC64 case Enable tpm_ibmvtpm driver by default when IMA is enabled on PPC64 Signed-off-by: Kent Yoder --- security/integrity/ima/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'security/integrity/ima') diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index b9c1219924f..809ccf19d09 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -11,6 +11,7 @@ config IMA select CRYPTO_SHA1 select TCG_TPM if HAS_IOMEM && !UML select TCG_TIS if TCG_TPM && X86 + select TCG_IBMVTPM if TCG_TPM && PPC64 help The Trusted Computing Group(TCG) runtime Integrity Measurement Architecture(IMA) maintains a list of hash -- cgit v1.2.3 From 2fe5d6def1672ae6635dd71867bf36dcfaa7434b Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 13 Feb 2012 10:15:05 -0500 Subject: ima: integrity appraisal extension 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'. Changelov v4: - changed iint cache flags to hex values 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 Signed-off-by: Dmitry Kasatkin --- security/integrity/ima/Kconfig | 15 +++ security/integrity/ima/Makefile | 1 + 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 | 79 +++++++++++----- security/integrity/ima/ima_policy.c | 32 +++++-- 8 files changed, 340 insertions(+), 50 deletions(-) create mode 100644 security/integrity/ima/ima_appraise.c (limited to 'security/integrity/ima') diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 809ccf19d09..d232c73647a 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -56,3 +56,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: + + If unsure, say N. diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 5f740f6971e..3f2ca6bdc38 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -8,3 +8,4 @@ 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-$(CONFIG_IMA_AUDIT) += 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 e7c99fd0d22..069a4aa63e9 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 { @@ -107,6 +108,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); @@ -123,14 +125,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 032ff03ad90..41cce84416c 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 #include - +#include +#include +#include +#include #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 00000000000..4865f61f904 --- /dev/null +++ b/security/integrity/ima/ima_appraise.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2011 IBM Corporation + * + * Author: + * Mimi Zohar + * + * 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 +#include +#include +#include +#include +#include +#include + +#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 9b3ade7468b..b21ee5b5495 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 be8294915cf..6eb28d47e74 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -22,12 +22,19 @@ #include #include #include +#include #include #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; unsigned char *pathname = NULL, *pathbuf = NULL; @@ -67,8 +74,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) @@ -100,17 +107,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); } /** @@ -140,14 +151,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; unsigned char *pathname = NULL, *pathbuf = NULL; - 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) { @@ -157,11 +171,21 @@ 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 != 0) @@ -177,11 +201,16 @@ retry: pathname = NULL; } } - ima_store_measurement(iint, file, !pathname ? filename : pathname); + if (action & IMA_MEASURE) + ima_store_measurement(iint, file, + !pathname ? filename : pathname); + if (action & IMA_APPRAISE) + rc = ima_appraise_measurement(iint, file, + !pathname ? filename : pathname); kfree(pathbuf); out: - mutex_unlock(&iint->mutex); - return rc; + mutex_unlock(&inode->i_mutex); + return (rc && must_appraise) ? -EACCES : 0; } /** @@ -197,14 +226,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; } /** @@ -228,7 +257,7 @@ int ima_bprm_check(struct linux_binprm *bprm) (strcmp(bprm->filename, bprm->interp) == 0) ? bprm->filename : bprm->interp, MAY_EXEC, BPRM_CHECK); - return 0; + return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; } /** @@ -249,7 +278,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 1a9583008aa..3e22e17da29 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; @@ -163,18 +169,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; } /** -- cgit v1.2.3 From 07f6a79415d7d502ee0c7d02ace6594a7be7429a Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 9 Mar 2011 22:25:48 -0500 Subject: ima: add appraise action keywords and default rules Unlike the IMA measurement policy, the appraise policy can not be dependent on runtime process information, such as the task uid, as the 'security.ima' xattr is written on file close and must be updated each time the file changes, regardless of the current task uid. This patch extends the policy language with 'fowner', defines an appraise policy, which appraises all files owned by root, and defines 'ima_appraise_tcb', a new boot command line option, to enable the appraise policy. Changelog v3: - separate the measure from the appraise rules in order to support measuring without appraising and appraising without measuring. - change appraisal default for filesystems without xattr support to fail - update default appraise policy for cgroups Changelog v1: - don't appraise RAMFS (Dmitry Kasatkin) - merged rest of "ima: ima_must_appraise_or_measure API change" commit (Dmtiry Kasatkin) ima_must_appraise_or_measure() called ima_match_policy twice, which searched the policy for a matching rule. Once for a matching measurement rule and subsequently for an appraisal rule. Searching the policy twice is unnecessary overhead, which could be noticeable with a large policy. The new version of ima_must_appraise_or_measure() does everything in a single iteration using a new version of ima_match_policy(). It returns IMA_MEASURE, IMA_APPRAISE mask. With the use of action mask only one efficient matching function is enough. Removed other specific versions of matching functions. Changelog: - change 'owner' to 'fowner' to conform to the new LSM conditions posted by Roberto Sassu. - fix calls to ima_log_string() Signed-off-by: Mimi Zohar Signed-off-by: Dmitry Kasatkin --- security/integrity/ima/ima_appraise.c | 5 +- security/integrity/ima/ima_policy.c | 151 +++++++++++++++++++++++++--------- 2 files changed, 116 insertions(+), 40 deletions(-) (limited to 'security/integrity/ima') diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 4865f61f904..681cb6e7225 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -36,7 +36,10 @@ __setup("ima_appraise=", default_appraise_setup); */ int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask) { - return 0; + if (!ima_appraise) + return 0; + + return ima_match_policy(inode, func, mask, IMA_APPRAISE); } static void ima_fix_xattr(struct dentry *dentry, diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 3e22e17da29..0d6d60b4ba6 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -24,6 +24,7 @@ #define IMA_MASK 0x0002 #define IMA_FSMAGIC 0x0004 #define IMA_UID 0x0008 +#define IMA_FOWNER 0x0010 #define UNKNOWN 0 #define MEASURE 1 /* same as IMA_MEASURE */ @@ -38,7 +39,7 @@ enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE }; -struct ima_measure_rule_entry { +struct ima_rule_entry { struct list_head list; int action; unsigned int flags; @@ -46,6 +47,7 @@ struct ima_measure_rule_entry { int mask; unsigned long fsmagic; uid_t uid; + uid_t fowner; struct { void *rule; /* LSM file metadata specific */ int type; /* audit type */ @@ -54,7 +56,7 @@ struct ima_measure_rule_entry { /* * Without LSM specific knowledge, the default policy can only be - * written in terms of .action, .func, .mask, .fsmagic, and .uid + * written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner */ /* @@ -63,7 +65,7 @@ struct ima_measure_rule_entry { * normal users can easily run the machine out of memory simply building * and running executables. */ -static struct ima_measure_rule_entry default_rules[] = { +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}, @@ -81,19 +83,41 @@ static struct ima_measure_rule_entry default_rules[] = { .flags = IMA_FUNC | IMA_MASK | IMA_UID}, }; -static LIST_HEAD(measure_default_rules); -static LIST_HEAD(measure_policy_rules); -static struct list_head *ima_measure; +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 = 0,.flags = IMA_FOWNER}, +}; + +static LIST_HEAD(ima_default_rules); +static LIST_HEAD(ima_policy_rules); +static struct list_head *ima_rules; -static DEFINE_MUTEX(ima_measure_mutex); +static DEFINE_MUTEX(ima_rules_mutex); static bool ima_use_tcb __initdata; -static int __init default_policy_setup(char *str) +static int __init default_measure_policy_setup(char *str) { ima_use_tcb = 1; return 1; } -__setup("ima_tcb", default_policy_setup); +__setup("ima_tcb", default_measure_policy_setup); + +static bool ima_use_appraise_tcb __initdata; +static int __init default_appraise_policy_setup(char *str) +{ + ima_use_appraise_tcb = 1; + return 1; +} +__setup("ima_appraise_tcb", default_appraise_policy_setup); /** * ima_match_rules - determine whether an inode matches the measure rule. @@ -104,7 +128,7 @@ __setup("ima_tcb", default_policy_setup); * * Returns true on rule match, false on failure. */ -static bool ima_match_rules(struct ima_measure_rule_entry *rule, +static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, enum ima_hooks func, int mask) { struct task_struct *tsk = current; @@ -120,6 +144,8 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, return false; if ((rule->flags & IMA_UID) && rule->uid != cred->uid) return false; + if ((rule->flags & IMA_FOWNER) && rule->fowner != inode->i_uid) + return false; for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; u32 osid, sid; @@ -172,10 +198,10 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int flags) { - struct ima_measure_rule_entry *entry; + struct ima_rule_entry *entry; int action = 0, actmask = flags | (flags << 1); - list_for_each_entry(entry, ima_measure, list) { + list_for_each_entry(entry, ima_rules, list) { if (!(entry->action & actmask)) continue; @@ -196,22 +222,31 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, /** * ima_init_policy - initialize the default measure rules. * - * ima_measure points to either the measure_default_rules or the - * the new measure_policy_rules. + * ima_rules points to either the ima_default_rules or the + * the new ima_policy_rules. */ void __init ima_init_policy(void) { - int i, entries; + int i, measure_entries, appraise_entries; /* if !ima_use_tcb set entries = 0 so we load NO default rules */ - if (ima_use_tcb) - entries = ARRAY_SIZE(default_rules); - else - entries = 0; - - for (i = 0; i < entries; i++) - list_add_tail(&default_rules[i].list, &measure_default_rules); - ima_measure = &measure_default_rules; + 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, + &ima_default_rules); + else { + int j = i - measure_entries; + + list_add_tail(&default_appraise_rules[j].list, + &ima_default_rules); + } + } + + ima_rules = &ima_default_rules; } /** @@ -228,8 +263,8 @@ void ima_update_policy(void) int result = 1; int audit_info = 0; - if (ima_measure == &measure_default_rules) { - ima_measure = &measure_policy_rules; + if (ima_rules == &ima_default_rules) { + ima_rules = &ima_policy_rules; cause = "complete"; result = 0; } @@ -240,14 +275,17 @@ void ima_update_policy(void) enum { Opt_err = -1, Opt_measure = 1, Opt_dont_measure, + Opt_appraise, Opt_dont_appraise, 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_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner }; static match_table_t policy_tokens = { {Opt_measure, "measure"}, {Opt_dont_measure, "dont_measure"}, + {Opt_appraise, "appraise"}, + {Opt_dont_appraise, "dont_appraise"}, {Opt_obj_user, "obj_user=%s"}, {Opt_obj_role, "obj_role=%s"}, {Opt_obj_type, "obj_type=%s"}, @@ -258,10 +296,11 @@ static match_table_t policy_tokens = { {Opt_mask, "mask=%s"}, {Opt_fsmagic, "fsmagic=%s"}, {Opt_uid, "uid=%s"}, + {Opt_fowner, "fowner=%s"}, {Opt_err, NULL} }; -static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry, +static int ima_lsm_rule_init(struct ima_rule_entry *entry, char *args, int lsm_rule, int audit_type) { int result; @@ -285,7 +324,7 @@ static void ima_log_string(struct audit_buffer *ab, char *key, char *value) audit_log_format(ab, " "); } -static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) +static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) { struct audit_buffer *ab; char *p; @@ -294,6 +333,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); entry->uid = -1; + entry->fowner = -1; entry->action = UNKNOWN; while ((p = strsep(&rule, " \t")) != NULL) { substring_t args[MAX_OPT_ARGS]; @@ -322,11 +362,27 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) entry->action = DONT_MEASURE; break; + case Opt_appraise: + ima_log_string(ab, "action", "appraise"); + + if (entry->action != UNKNOWN) + result = -EINVAL; + + entry->action = APPRAISE; + break; + case Opt_dont_appraise: + ima_log_string(ab, "action", "dont_appraise"); + + if (entry->action != UNKNOWN) + result = -EINVAL; + + entry->action = DONT_APPRAISE; + break; case Opt_func: ima_log_string(ab, "func", args[0].from); if (entry->func) - result = -EINVAL; + result = -EINVAL; if (strcmp(args[0].from, "FILE_CHECK") == 0) entry->func = FILE_CHECK; @@ -391,6 +447,23 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) entry->flags |= IMA_UID; } break; + case Opt_fowner: + ima_log_string(ab, "fowner", args[0].from); + + if (entry->fowner != -1) { + result = -EINVAL; + break; + } + + result = strict_strtoul(args[0].from, 10, &lnum); + if (!result) { + entry->fowner = (uid_t) lnum; + if (entry->fowner != lnum) + result = -EINVAL; + else + entry->flags |= IMA_FOWNER; + } + break; case Opt_obj_user: ima_log_string(ab, "obj_user", args[0].from); result = ima_lsm_rule_init(entry, args[0].from, @@ -442,7 +515,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) } /** - * ima_parse_add_rule - add a rule to measure_policy_rules + * ima_parse_add_rule - add a rule to ima_policy_rules * @rule - ima measurement policy rule * * Uses a mutex to protect the policy list from multiple concurrent writers. @@ -452,12 +525,12 @@ ssize_t ima_parse_add_rule(char *rule) { const char *op = "update_policy"; char *p; - struct ima_measure_rule_entry *entry; + struct ima_rule_entry *entry; ssize_t result, len; int audit_info = 0; /* Prevent installed policy from changing */ - if (ima_measure != &measure_default_rules) { + if (ima_rules != &ima_default_rules) { integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, op, "already exists", -EACCES, audit_info); @@ -490,9 +563,9 @@ ssize_t ima_parse_add_rule(char *rule) return result; } - mutex_lock(&ima_measure_mutex); - list_add_tail(&entry->list, &measure_policy_rules); - mutex_unlock(&ima_measure_mutex); + mutex_lock(&ima_rules_mutex); + list_add_tail(&entry->list, &ima_policy_rules); + mutex_unlock(&ima_rules_mutex); return len; } @@ -500,12 +573,12 @@ ssize_t ima_parse_add_rule(char *rule) /* ima_delete_rules called to cleanup invalid policy */ void ima_delete_rules(void) { - struct ima_measure_rule_entry *entry, *tmp; + struct ima_rule_entry *entry, *tmp; - mutex_lock(&ima_measure_mutex); - list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) { + mutex_lock(&ima_rules_mutex); + list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) { list_del(&entry->list); kfree(entry); } - mutex_unlock(&ima_measure_mutex); + mutex_unlock(&ima_rules_mutex); } -- cgit v1.2.3 From bf2276d10ce58ff44ab8857266a6718024496af6 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 19 Oct 2011 12:04:40 +0300 Subject: ima: allocating iint improvements With IMA-appraisal's removal of the iint mutex and taking the i_mutex instead, allocating the iint becomes a lot simplier, as we don't need to be concerned with two processes racing to allocate the iint. This patch cleans up and improves performance for allocating the iint. - removed redundant double i_mutex locking - combined iint allocation with tree search Changelog v2: - removed the rwlock/read_lock changes from this patch Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_main.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'security/integrity/ima') diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 6eb28d47e74..df652129605 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -162,19 +162,14 @@ static int process_measurement(struct file *file, const unsigned char *filename, if (!action) return 0; -retry: - iint = integrity_iint_find(inode); - if (!iint) { - rc = integrity_inode_alloc(inode); - if (!rc || rc == -EEXIST) - goto retry; - return rc; - } - must_appraise = action & IMA_APPRAISE; mutex_lock(&inode->i_mutex); + iint = integrity_inode_get(inode); + if (!iint) + goto out; + /* Determine if already appraised/measured based on bitmask * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED) */ iint->flags |= action; -- cgit v1.2.3 From 42c63330f2b05aa6077c1bfc2798c04afe54f6b2 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Thu, 10 Mar 2011 18:54:15 -0500 Subject: ima: add ima_inode_setxattr/removexattr function and calls Based on xattr_permission comments, the restriction to modify 'security' xattr is left up to the underlying fs or lsm. Ensure that not just anyone can modify or remove 'security.ima'. Changelog v1: - Unless IMA-APPRAISE is configured, use stub ima_inode_removexattr()/setxattr() functions. (Moved ima_inode_removexattr()/setxattr() to ima_appraise.c) Changelog: - take i_mutex to fix locking (Dmitry Kasatkin) - ima_reset_appraise_flags should only be called when modifying or removing the 'security.ima' xattr. Requires CAP_SYS_ADMIN privilege. (Incorporated fix from Roberto Sassu) - Even if allowed to update security.ima, reset the appraisal flags, forcing re-appraisal. - Replace CAP_MAC_ADMIN with CAP_SYS_ADMIN - static inline ima_inode_setxattr()/ima_inode_removexattr() stubs - ima_protect_xattr should be static Signed-off-by: Mimi Zohar Signed-off-by: Dmitry Kasatkin --- security/integrity/ima/ima_appraise.c | 57 +++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'security/integrity/ima') diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 681cb6e7225..becc7e09116 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -169,3 +169,60 @@ void ima_inode_post_setattr(struct dentry *dentry) rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA); return; } + +/* + * ima_protect_xattr - protect 'security.ima' + * + * Ensure that not just anyone can modify or remove 'security.ima'. + */ +static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name, + const void *xattr_value, size_t xattr_value_len) +{ + if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) { + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return 1; + } + return 0; +} + +static void ima_reset_appraise_flags(struct inode *inode) +{ + struct integrity_iint_cache *iint; + + if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)) + return; + + iint = integrity_iint_find(inode); + if (!iint) + return; + + iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED); + return; +} + +int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, + const void *xattr_value, size_t xattr_value_len) +{ + int result; + + result = ima_protect_xattr(dentry, xattr_name, xattr_value, + xattr_value_len); + if (result == 1) { + ima_reset_appraise_flags(dentry->d_inode); + result = 0; + } + return result; +} + +int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name) +{ + int result; + + result = ima_protect_xattr(dentry, xattr_name, NULL, 0); + if (result == 1) { + ima_reset_appraise_flags(dentry->d_inode); + result = 0; + } + return result; +} -- cgit v1.2.3 From 5a44b41207174e1882ce0c24a752f4cfb65dab07 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 9 Jan 2012 22:59:36 -0500 Subject: ima: add support for different security.ima data types IMA-appraisal currently verifies the integrity of a file based on a known 'good' measurement value. This patch reserves the first byte of 'security.ima' as a place holder for the type of method used for verifying file data integrity. Changelog v1: - Use the newly defined 'struct evm_ima_xattr_data' Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_api.c | 6 +++--- security/integrity/ima/ima_appraise.c | 23 +++++++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'security/integrity/ima') diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 41cce84416c..33d46859753 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -147,8 +147,8 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, 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); + iint->ima_xattr.type = IMA_XATTR_DIGEST; + result = ima_calc_hash(file, iint->ima_xattr.digest); if (!result) { iint->version = i_version; iint->flags |= IMA_COLLECTED; @@ -196,7 +196,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint, return; } memset(&entry->template, 0, sizeof(entry->template)); - memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE); + 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); diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index becc7e09116..f9979976aa5 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -45,9 +45,9 @@ int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask) 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); + iint->ima_xattr.type = IMA_XATTR_DIGEST; + __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, (u8 *)&iint->ima_xattr, + sizeof iint->ima_xattr, 0); } /* @@ -63,7 +63,7 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - u8 xattr_value[IMA_DIGEST_SIZE]; + struct evm_ima_xattr_data xattr_value; enum integrity_status status = INTEGRITY_UNKNOWN; const char *op = "appraise_data"; char *cause = "unknown"; @@ -77,8 +77,8 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, if (iint->flags & IMA_APPRAISED) return iint->ima_status; - rc = inode->i_op->getxattr(dentry, XATTR_NAME_IMA, xattr_value, - IMA_DIGEST_SIZE); + rc = inode->i_op->getxattr(dentry, XATTR_NAME_IMA, (u8 *)&xattr_value, + sizeof xattr_value); if (rc <= 0) { if (rc && rc != -ENODATA) goto out; @@ -89,7 +89,8 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, goto out; } - status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint); + status = evm_verifyxattr(dentry, XATTR_NAME_IMA, (u8 *)&xattr_value, + rc, iint); if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) { if ((status == INTEGRITY_NOLABEL) || (status == INTEGRITY_NOXATTRS)) @@ -99,14 +100,16 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, goto out; } - rc = memcmp(xattr_value, iint->digest, IMA_DIGEST_SIZE); + rc = memcmp(xattr_value.digest, iint->ima_xattr.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); + &xattr_value, sizeof xattr_value); print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE, - iint->digest, IMA_DIGEST_SIZE); + (u8 *)&iint->ima_xattr, + sizeof iint->ima_xattr); goto out; } status = INTEGRITY_PASS; -- cgit v1.2.3 From 8606404fa555c2ee691376fcc640ab89fe752035 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 31 Aug 2011 14:07:06 +0300 Subject: ima: digital signature verification support This patch adds support for digital signature based integrity appraisal. With this patch, 'security.ima' contains either the file data hash or a digital signature of the file data hash. The file data hash provides the security attribute of file integrity. In addition to file integrity, a digital signature provides the security attribute of authenticity. Unlike EVM, when the file metadata changes, the digital signature is replaced with an HMAC, modification of the file data does not cause the 'security.ima' digital signature to be replaced with a hash. As a result, after any modification, subsequent file integrity appraisals would fail. Although digitally signed files can be modified, but by not updating 'security.ima' to reflect these modifications, in essence digitally signed files could be considered 'immutable'. IMA uses a different keyring than EVM. While the EVM keyring should not be updated after initialization and locked, the IMA keyring should allow updating or adding new keys when upgrading or installing packages. Changelog v4: - Change IMA_DIGSIG to hex equivalent Changelog v3: - Permit files without any 'security.ima' xattr to be labeled properly. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_appraise.c | 70 +++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 19 deletions(-) (limited to 'security/integrity/ima') diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index f9979976aa5..4cdf36ad884 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -63,7 +63,7 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - struct evm_ima_xattr_data xattr_value; + struct evm_ima_xattr_data *xattr_value = NULL; enum integrity_status status = INTEGRITY_UNKNOWN; const char *op = "appraise_data"; char *cause = "unknown"; @@ -77,8 +77,8 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, if (iint->flags & IMA_APPRAISED) return iint->ima_status; - rc = inode->i_op->getxattr(dentry, XATTR_NAME_IMA, (u8 *)&xattr_value, - sizeof xattr_value); + rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value, + 0, GFP_NOFS); if (rc <= 0) { if (rc && rc != -ENODATA) goto out; @@ -89,8 +89,7 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, goto out; } - status = evm_verifyxattr(dentry, XATTR_NAME_IMA, (u8 *)&xattr_value, - rc, iint); + 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)) @@ -100,30 +99,58 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, goto out; } - rc = memcmp(xattr_value.digest, iint->ima_xattr.digest, - IMA_DIGEST_SIZE); - if (rc) { - status = INTEGRITY_FAIL; - cause = "invalid-hash"; - print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE, - &xattr_value, sizeof xattr_value); - print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE, - (u8 *)&iint->ima_xattr, - sizeof iint->ima_xattr); - goto out; + switch (xattr_value->type) { + case IMA_XATTR_DIGEST: + rc = memcmp(xattr_value->digest, iint->ima_xattr.digest, + IMA_DIGEST_SIZE); + if (rc) { + cause = "invalid-hash"; + status = INTEGRITY_FAIL; + print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE, + xattr_value, sizeof(*xattr_value)); + print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE, + (u8 *)&iint->ima_xattr, + sizeof iint->ima_xattr); + break; + } + status = INTEGRITY_PASS; + break; + 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); + if (rc == -EOPNOTSUPP) { + status = INTEGRITY_UNKNOWN; + } else if (rc) { + cause = "invalid-signature"; + status = INTEGRITY_FAIL; + } else { + status = INTEGRITY_PASS; + } + break; + default: + status = INTEGRITY_UNKNOWN; + cause = "unknown-ima-data"; + break; } - status = INTEGRITY_PASS; - iint->flags |= IMA_APPRAISED; + out: if (status != INTEGRITY_PASS) { - if (ima_appraise & IMA_APPRAISE_FIX) { + if ((ima_appraise & IMA_APPRAISE_FIX) && + (!xattr_value || + xattr_value->type != EVM_IMA_XATTR_DIGSIG)) { ima_fix_xattr(dentry, iint); status = INTEGRITY_PASS; } integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, op, cause, rc, 0); + } else { + iint->flags |= IMA_APPRAISED; } iint->ima_status = status; + kfree(xattr_value); return status; } @@ -135,9 +162,14 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) struct dentry *dentry = file->f_dentry; int rc = 0; + /* do not collect and update hash for digital signatures */ + if (iint->flags & IMA_DIGSIG) + return; + rc = ima_collect_measurement(iint, file); if (rc < 0) return; + ima_fix_xattr(dentry, iint); } -- cgit v1.2.3 From d9d300cdb6f233c4c591348919c758062198a4f4 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 27 Jun 2012 11:26:14 +0300 Subject: ima: rename ima_must_appraise_or_measure When AUDIT action support is added to the IMA, ima_must_appraise_or_measure() does not reflect the real meaning anymore. Rename it to ima_get_action(). Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 2 +- security/integrity/ima/ima_api.c | 4 ++-- security/integrity/ima/ima_main.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'security/integrity/ima') diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 069a4aa63e9..48aa0d46d3e 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -108,7 +108,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_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); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 33d46859753..f0d60e754b3 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -97,7 +97,7 @@ err_out: } /** - * ima_must_appraise_or_measure - appraise & measure decision based on policy. + * ima_get_action - 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) @@ -112,7 +112,7 @@ err_out: * Returns IMA_MEASURE, IMA_APPRAISE mask. * */ -int ima_must_appraise_or_measure(struct inode *inode, int mask, int function) +int ima_get_action(struct inode *inode, int mask, int function) { int flags = IMA_MEASURE | IMA_APPRAISE; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index df652129605..60b047e96f4 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -158,7 +158,7 @@ static int process_measurement(struct file *file, const unsigned char *filename, /* Determine if in appraise/measurement policy, * returns IMA_MEASURE, IMA_APPRAISE bitmask. */ - action = ima_must_appraise_or_measure(inode, mask, function); + action = ima_get_action(inode, mask, function); if (!action) return 0; -- cgit v1.2.3 From 45e2472e67bf66f794d507b52e82af92e0614e49 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 12 Sep 2012 20:51:32 +0300 Subject: ima: generic IMA action flag handling Make the IMA action flag handling generic in order to support additional new actions, without requiring changes to the base implementation. New actions, like audit logging, will only need to modify the define statements. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_appraise.c | 2 +- security/integrity/ima/ima_main.c | 4 ++-- security/integrity/ima/ima_policy.c | 21 +++++++++++---------- 3 files changed, 14 insertions(+), 13 deletions(-) (limited to 'security/integrity/ima') diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 4cdf36ad884..0aa43bde441 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -232,7 +232,7 @@ static void ima_reset_appraise_flags(struct inode *inode) if (!iint) return; - iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED); + iint->flags &= ~IMA_DONE_MASK; return; } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 60b047e96f4..5da08b75d36 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -117,7 +117,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, 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); + iint->flags &= ~IMA_DONE_MASK; if (iint->flags & IMA_APPRAISE) ima_update_xattr(iint, file); } @@ -173,7 +173,7 @@ static int process_measurement(struct file *file, const unsigned char *filename, /* 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); + action &= ~((iint->flags & IMA_DONE_MASK) >> 1); /* Nothing to do, just return existing appraised status */ if (!action) { diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 0d6d60b4ba6..f46f685a171 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -26,13 +26,11 @@ #define IMA_UID 0x0008 #define IMA_FOWNER 0x0010 -#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 UNKNOWN 0 +#define MEASURE 0x0001 /* same as IMA_MEASURE */ +#define DONT_MEASURE 0x0002 +#define APPRAISE 0x0004 /* same as IMA_APPRAISE */ +#define DONT_APPRAISE 0x0008 #define MAX_LSM_RULES 6 enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, @@ -209,9 +207,12 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, 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; + action |= entry->action & IMA_DO_MASK; + if (entry->action & IMA_DO_MASK) + actmask &= ~(entry->action | entry->action << 1); + else + actmask &= ~(entry->action | entry->action >> 1); + if (!actmask) break; } -- cgit v1.2.3 From e7c568e0fd0cf6d9c8ab8ea537ba8f3a3ae7c3d8 Mon Sep 17 00:00:00 2001 From: Peter Moody Date: Thu, 14 Jun 2012 10:04:36 -0700 Subject: ima: audit log hashes This adds an 'audit' policy action which audit logs file measurements. Changelog v6: - use new action flag handling (Dmitry Kasatkin). - removed whitespace (Mimi) Changelog v5: - use audit_log_untrustedstring. Changelog v4: - cleanup digest -> hash conversion. - use filename rather than d_path in ima_audit_measurement. Changelog v3: - Use newly exported audit_log_task_info for logging pid/ppid/uid/etc. - Update the ima_policy ABI documentation. Changelog v2: - Use 'audit' action rather than 'measure_and_audit' to permit auditing in the absence of measuring.. Changelog v1: - Initial posting. Signed-off-by: Peter Moody Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 2 ++ security/integrity/ima/ima_api.c | 32 +++++++++++++++++++++++++++++++- security/integrity/ima/ima_main.c | 9 ++++++--- security/integrity/ima/ima_policy.c | 11 +++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) (limited to 'security/integrity/ima') diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 48aa0d46d3e..8180adde10b 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -114,6 +114,8 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, struct file *file); void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename); +void ima_audit_measurement(struct integrity_iint_cache *iint, + const unsigned char *filename); int ima_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); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index f0d60e754b3..b356884fb3e 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -114,7 +114,7 @@ err_out: */ int ima_get_action(struct inode *inode, int mask, int function) { - int flags = IMA_MEASURE | IMA_APPRAISE; + int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE; if (!ima_appraise) flags &= ~IMA_APPRAISE; @@ -207,3 +207,33 @@ void ima_store_measurement(struct integrity_iint_cache *iint, if (result < 0) kfree(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]; + 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]); + hash[i * 2] = '\0'; + + ab = audit_log_start(current->audit_context, GFP_KERNEL, + AUDIT_INTEGRITY_RULE); + if (!ab) + return; + + audit_log_format(ab, "file="); + audit_log_untrustedstring(ab, filename); + audit_log_format(ab, " hash="); + audit_log_untrustedstring(ab, hash); + + audit_log_task_info(ab, current); + audit_log_end(ab); + + iint->flags |= IMA_AUDITED; +} diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 5da08b75d36..73c9a268253 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -156,8 +156,8 @@ static int process_measurement(struct file *file, const unsigned char *filename, if (!ima_initialized || !S_ISREG(inode->i_mode)) return 0; - /* Determine if in appraise/measurement policy, - * returns IMA_MEASURE, IMA_APPRAISE bitmask. */ + /* Determine if in appraise/audit/measurement policy, + * returns IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT bitmask. */ action = ima_get_action(inode, mask, function); if (!action) return 0; @@ -171,7 +171,8 @@ static int process_measurement(struct file *file, const unsigned char *filename, goto out; /* Determine if already appraised/measured based on bitmask - * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED) */ + * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED, + * IMA_AUDIT, IMA_AUDITED) */ iint->flags |= action; action &= ~((iint->flags & IMA_DONE_MASK) >> 1); @@ -202,6 +203,8 @@ static int process_measurement(struct file *file, const unsigned char *filename, if (action & IMA_APPRAISE) rc = ima_appraise_measurement(iint, file, !pathname ? filename : pathname); + if (action & IMA_AUDIT) + ima_audit_measurement(iint, !pathname ? filename : pathname); kfree(pathbuf); out: mutex_unlock(&inode->i_mutex); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index f46f685a171..cda903131db 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -31,6 +31,7 @@ #define DONT_MEASURE 0x0002 #define APPRAISE 0x0004 /* same as IMA_APPRAISE */ #define DONT_APPRAISE 0x0008 +#define AUDIT 0x0040 #define MAX_LSM_RULES 6 enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, @@ -277,6 +278,7 @@ enum { Opt_err = -1, Opt_measure = 1, Opt_dont_measure, Opt_appraise, Opt_dont_appraise, + Opt_audit, 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 @@ -287,6 +289,7 @@ static match_table_t policy_tokens = { {Opt_dont_measure, "dont_measure"}, {Opt_appraise, "appraise"}, {Opt_dont_appraise, "dont_appraise"}, + {Opt_audit, "audit"}, {Opt_obj_user, "obj_user=%s"}, {Opt_obj_role, "obj_role=%s"}, {Opt_obj_type, "obj_type=%s"}, @@ -379,6 +382,14 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->action = DONT_APPRAISE; break; + case Opt_audit: + ima_log_string(ab, "action", "audit"); + + if (entry->action != UNKNOWN) + result = -EINVAL; + + entry->action = AUDIT; + break; case Opt_func: ima_log_string(ab, "func", args[0].from); -- cgit v1.2.3