@@ -20,6 +20,8 @@ extern int ima_bprm_check(struct linux_binprm *bprm);
extern int ima_file_check(struct file *file, int mask);
extern void ima_post_create_tmpfile(struct inode *inode);
extern void ima_file_free(struct file *file);
+extern void ima_file_update(struct file *file);
+extern void ima_delayed_update(struct file *file);
extern int ima_file_mmap(struct file *file, unsigned long prot);
extern int ima_load_data(enum kernel_load_data_id id);
extern int ima_read_file(struct file *file, enum kernel_read_file_id id);
@@ -66,6 +68,16 @@ static inline void ima_file_free(struct file *file)
return;
}
+static inline void ima_file_update(struct file *file)
+{
+ return;
+}
+
+static inline void ima_delayed_update(struct file *file)
+{
+ return;
+}
+
static inline int ima_file_mmap(struct file *file, unsigned long prot)
{
return 0;
@@ -295,3 +295,23 @@ config IMA_APPRAISE_SIGNED_INIT
default n
help
This option requires user-space init to be signed.
+
+config IMA_HASH_LATENCY
+ int
+ depends on IMA_APPRAISE
+ range 0 60000
+ default 50
+ help
+ This value defines the re-measurement interval when files are
+ being written. Value is in milliseconds.
+
+config IMA_HASH_LATENCY_CEILING
+ int
+ depends on IMA_APPRAISE
+ range 100 60000
+ default 30000
+ help
+ In order to maintain high write performance for large files,
+ IMA increases the re-measurement rate as the file size grows.
+ This value defines the ceiling for the re-measurement delay
+ in milliseconds.
@@ -234,10 +234,14 @@ int ima_appraise_measurement(enum ima_hooks func,
status = INTEGRITY_NOLABEL;
if (file->f_mode & FMODE_CREATED)
iint->flags |= IMA_NEW_FILE;
+
if ((iint->flags & IMA_NEW_FILE) &&
(!(iint->flags & IMA_DIGSIG_REQUIRED) ||
- (inode->i_size == 0)))
+ (inode->i_size == 0))) {
+ ima_fix_xattr(dentry, iint);
+ xattr_len = ima_read_xattr(dentry, &xattr_value);
status = INTEGRITY_PASS;
+ }
goto out;
}
@@ -16,7 +16,7 @@
*
* File: ima_main.c
* implements the IMA hooks: ima_bprm_check, ima_file_mmap,
- * and ima_file_check.
+ * ima_delayed_update, ima_file_update and ima_file_check.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -30,6 +30,8 @@
#include <linux/xattr.h>
#include <linux/ima.h>
#include <linux/iversion.h>
+#include <linux/workqueue.h>
+#include <linux/sizes.h>
#include <linux/fs.h>
#include "ima.h"
@@ -40,8 +42,15 @@ int ima_appraise = IMA_APPRAISE_ENFORCE;
int ima_appraise;
#endif
+#if CONFIG_IMA_HASH_LATENCY == 0
+#define IMA_LATENCY_INCREMENT 100
+#else
+#define IMA_LATENCY_INCREMENT CONFIG_IMA_HASH_LATENCY
+#endif
+
int ima_hash_algo = HASH_ALGO_SHA1;
static int hash_setup_done;
+static struct workqueue_struct *ima_update_wq;
static int __init hash_setup(char *str)
{
@@ -127,6 +136,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
{
fmode_t mode = file->f_mode;
bool update;
+ bool creq;
if (!(mode & FMODE_WRITE))
return;
@@ -322,6 +332,17 @@ static int process_measurement(struct file *file, const struct cred *cred,
return 0;
}
+static void ima_delayed_update_handler(struct work_struct *work)
+{
+ struct ima_work_entry *entry;
+
+ entry = container_of(work, typeof(*entry), work.work);
+
+ ima_file_update(entry->file);
+ fput(entry->file);
+ entry->file = NULL;
+}
+
/**
* ima_file_mmap - based on policy, collect/store measurement.
* @file: pointer to the file to be measured (May be NULL)
@@ -375,6 +396,78 @@ int ima_bprm_check(struct linux_binprm *bprm)
MAY_EXEC, CREDS_CHECK);
}
+/**
+ * ima_delayed_update - add a file to delayed update list
+ * @file: pointer to file structure being updated
+ */
+void ima_delayed_update(struct file *file)
+{
+ struct inode *inode = file_inode(file);
+ struct integrity_iint_cache *iint;
+ unsigned long blocks;
+ unsigned long msecs;
+ bool creq;
+
+ iint = integrity_iint_find(inode);
+ if (!iint)
+ return;
+
+ if (iint->ima_work.file)
+ return;
+
+ /* Slow down the samping rate per the file size */
+ blocks = inode->i_size / SZ_1M + 1;
+ msecs = blocks * IMA_LATENCY_INCREMENT;
+ if (msecs > CONFIG_IMA_HASH_LATENCY_CEILING)
+ msecs = CONFIG_IMA_HASH_LATENCY_CEILING;
+
+ get_file(file);
+ iint->ima_work.file = file;
+ INIT_DELAYED_WORK(&iint->ima_work.work, ima_delayed_update_handler);
+
+ creq = queue_delayed_work(ima_update_wq,
+ &iint->ima_work.work,
+ msecs_to_jiffies(msecs));
+ if (creq == false) {
+ iint->ima_work.file = NULL;
+ fput(file);
+ }
+}
+EXPORT_SYMBOL_GPL(ima_delayed_update);
+
+/**
+ * ima_file_update - update the file measurement
+ * @file: pointer to file structure being updated
+ */
+void ima_file_update(struct file *file)
+{
+ struct inode *inode = file_inode(file);
+ struct integrity_iint_cache *iint;
+ bool should_measure = true;
+ u64 i_version;
+
+ if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+ return;
+
+ iint = integrity_iint_find(inode);
+ if (!iint)
+ return;
+
+ mutex_lock(&iint->mutex);
+ clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
+ if (IS_I_VERSION(inode)) {
+ i_version = inode_query_iversion(inode);
+ if (i_version == iint->version)
+ should_measure = false;
+ }
+ if (should_measure) {
+ iint->flags &= ~IMA_COLLECTED;
+ ima_update_xattr(iint, file);
+ }
+ mutex_unlock(&iint->mutex);
+}
+EXPORT_SYMBOL_GPL(ima_file_update);
+
/**
* ima_path_check - based on policy, collect/store measurement.
* @file: pointer to the file to be measured
@@ -580,6 +673,12 @@ static int __init init_ima(void)
{
int error;
+ ima_update_wq = alloc_workqueue("ima-update-wq",
+ WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE,
+ 0);
+ if (!ima_update_wq)
+ return -ENOMEM;
+
ima_init_template_list();
hash_setup(CONFIG_IMA_DEFAULT_HASH);
error = ima_init();
@@ -595,6 +694,8 @@ static int __init init_ima(void)
if (!error)
ima_update_policy_flag();
+ else
+ destroy_workqueue(ima_update_wq);
return error;
}
@@ -113,6 +113,11 @@ struct signature_v2_hdr {
uint8_t sig[0]; /* signature payload */
} __packed;
+struct ima_work_entry {
+ struct delayed_work work;
+ struct file *file;
+};
+
/* integrity data associated with an inode */
struct integrity_iint_cache {
struct rb_node rb_node; /* rooted in integrity_iint_tree */
@@ -129,6 +134,7 @@ struct integrity_iint_cache {
enum integrity_status ima_creds_status:4;
enum integrity_status evm_status:4;
struct ima_digest_data *ima_hash;
+ struct ima_work_entry ima_work;
};
/* rbtree tree calls to lookup, insert, delete