lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <b701686d-212e-4152-9db9-0c56f21e1fdc@linux.microsoft.com>
Date: Thu, 11 Dec 2025 11:20:40 -0800
From: steven chen <chenste@...ux.microsoft.com>
To: Roberto Sassu <roberto.sassu@...weicloud.com>, corbet@....net,
 zohar@...ux.ibm.com, dmitry.kasatkin@...il.com, eric.snowberg@...cle.com,
 paul@...l-moore.com, jmorris@...ei.org, serge@...lyn.com
Cc: linux-doc@...r.kernel.org, linux-kernel@...r.kernel.org,
 linux-integrity@...r.kernel.org, linux-security-module@...r.kernel.org,
 gregorylumen@...ux.microsoft.com, nramas@...ux.microsoft.com,
 Roberto Sassu <roberto.sassu@...wei.com>
Subject: Re: [RFC][PATCH] ima: Add support for staging measurements for
 deletion

On 12/11/2025 2:18 AM, Roberto Sassu wrote:
> On Wed, 2025-12-10 at 16:03 -0800, steven chen wrote:
>> On 12/9/2025 2:17 AM, Roberto Sassu wrote:
>>> From: Roberto Sassu <roberto.sassu@...wei.com>
>>>
>>> Introduce the ability of staging the entire of the IMA measurement list, or
>>> a portion, for deletion. Staging means moving the current content of the
>>> measurement list to a separate location, and allowing users to read and
>>> delete it. This causes the measurement list to be atomically truncated
>>> before new measurements can be added. Staging can be done only once at a
>>> time.
>>>
>>> User space is responsible to concatenate the staged IMA measurements list
>>> portions following the temporal order in which the operations were done,
>>> together with the current measurement list. Then, it can send the collected
>>> data to the remote verifiers.
>>>
>>> The benefit of this solution is the ability to free precious kernel memory,
>>> in exchange of delegating user space to reconstruct the full measurement
>>> list from the chunks. No trust needs to be given to user space, since the
>>> integrity of the measurement list is protected by the TPM.
>>>
>>> By default, staging the measurements list for deletion does not alter the
>>> hash table. When staging is done, IMA is still able to detect collisions on
>>> the staged and later deleted measurement entries, by keeping the entry
>>> digests (only template data are freed).
>>>
>>> However, since during the measurements list serialization only the SHA1
>>> digest is passed, and since there are no template data to recalculate the
>>> other digests from, the hash table is currently not populated with digests
>>> from staged/deleted entries after kexec().
>>>
>>> Introduce the new kernel option ima_flush_htable to decide whether or not
>>> the digests of staged measurement entries are flushed from the hash table.
>>>
>>> Then, introduce ascii_runtime_measurements_staged_<algo> and
>>> binary_runtime_measurement_staged_<algo> interfaces to stage/delete the
>>> measurements. Use 'echo A > <IMA interface>' and 'echo D > <IMA interface>'
>>> to respectively stage and delete the entire measurements list. Use
>>> 'echo N > <IMA interface>', with N between 1 and ULONG_MAX, to stage the
>>> selected portion of the measurements list.
>>>
>>> The ima_measure_users counter (protected by the ima_measure_lock mutex) has
>>> been introduced to protect access to the measurement list and the staged
>>> part. The open method of all the measurement interfaces has been extended
>>> to allow only one writer at a time or, in alternative, multiple readers.
>>> The write permission is used to stage/delete the measurements, the read
>>> permission to read them. Write requires also the CAP_SYS_ADMIN capability.
>> Hi Roberto,
>>
>> I released version 2 of trim N entries patch as bellow:
>>
>> [PATCH v2 0/1] Trim N entries of IMA event logs
>> <https://lore.kernel.org/linux-integrity/20251210235314.3341-1-chenste@linux.microsoft.com/T/#t>
>>
>> I adapted some of your idea and I think trim N has following advantages:
>> 1: less measurement list hold time than your current implementation
>> 2. operation much simple for user space
>> 3. less kernel code change
>> 4. no potential issue as Gregory mentioned.
> Please have a look at:
>
> https://marc.info/?l=linux-integrity&m=176545085325473&w=2
>
> and let me know if I'm missing something.
>
> Thanks
>
> Roberto

Hi Roberto,

what does this staging solution do that's not achieved by trim N entries 
solution?

You did not address all my comments and your other idea make things more 
complex.

Also, Trim N solution is simple and will bring following two good points:
     easy for future IMA development
     easy for code maintenance

Could you also add your comments on the trim N solution?

Thanks,

Steven

>
>> Thanks,
>>
>> Steven
>>
>>> Finally, introduce the _notrim version of the run-time measurements count
>>> and the binary measurements list size, to display them in the kexec-related
>>> critical data records.
>>>
>>> Note: This code derives from the Alt-IMA Huawei project, and is being
>>>         released under the dual license model (GPL-2.0 OR MIT).
>>>
>>> Link: https://github.com/linux-integrity/linux/issues/1
>>> Signed-off-by: Roberto Sassu <roberto.sassu@...wei.com>
>>> ---
>>>    .../admin-guide/kernel-parameters.txt         |   4 +
>>>    security/integrity/ima/ima.h                  |  10 +-
>>>    security/integrity/ima/ima_fs.c               | 222 +++++++++++++++++-
>>>    security/integrity/ima/ima_kexec.c            |  13 +-
>>>    security/integrity/ima/ima_queue.c            | 111 ++++++++-
>>>    5 files changed, 340 insertions(+), 20 deletions(-)
>>>
>>> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
>>> index 6c42061ca20e..355d8930e3ac 100644
>>> --- a/Documentation/admin-guide/kernel-parameters.txt
>>> +++ b/Documentation/admin-guide/kernel-parameters.txt
>>> @@ -2215,6 +2215,10 @@
>>>    			Use the canonical format for the binary runtime
>>>    			measurements, instead of host native format.
>>>    
>>> +	ima_flush_htable  [IMA]
>>> +			Flush the measurement list hash table when staging all
>>> +			or a part of it for deletion.
>>> +
>>>    	ima_hash=	[IMA]
>>>    			Format: { md5 | sha1 | rmd160 | sha256 | sha384
>>>    				   | sha512 | ... }
>>> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
>>> index e3d71d8d56e3..d7aa4a0f79b1 100644
>>> --- a/security/integrity/ima/ima.h
>>> +++ b/security/integrity/ima/ima.h
>>> @@ -117,6 +117,8 @@ struct ima_queue_entry {
>>>    	struct ima_template_entry *entry;
>>>    };
>>>    extern struct list_head ima_measurements;	/* list of all measurements */
>>> +extern struct list_head ima_measurements_staged; /* list of staged meas. */
>>> +extern bool ima_measurements_staged_exist;	/* If there are staged meas. */
>>>    
>>>    /* Some details preceding the binary serialized measurement list */
>>>    struct ima_kexec_hdr {
>>> @@ -281,10 +283,12 @@ struct ima_template_desc *ima_template_desc_current(void);
>>>    struct ima_template_desc *ima_template_desc_buf(void);
>>>    struct ima_template_desc *lookup_template_desc(const char *name);
>>>    bool ima_template_has_modsig(const struct ima_template_desc *ima_template);
>>> +int ima_queue_stage(unsigned long req_value);
>>> +int ima_queue_delete_staged(void);
>>>    int ima_restore_measurement_entry(struct ima_template_entry *entry);
>>>    int ima_restore_measurement_list(loff_t bufsize, void *buf);
>>>    int ima_measurements_show(struct seq_file *m, void *v);
>>> -unsigned long ima_get_binary_runtime_size(void);
>>> +unsigned long ima_get_binary_runtime_size(bool notrim);
>>>    int ima_init_template(void);
>>>    void ima_init_template_list(void);
>>>    int __init ima_init_digests(void);
>>> @@ -298,11 +302,13 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
>>>    extern spinlock_t ima_queue_lock;
>>>    
>>>    struct ima_h_table {
>>> -	atomic_long_t len;	/* number of stored measurements in the list */
>>> +	atomic_long_t len;	/* current num of stored meas. in the list */
>>> +	atomic_long_t len_notrim; /* total num of stored meas. in the list */
>>>    	atomic_long_t violations;
>>>    	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
>>>    };
>>>    extern struct ima_h_table ima_htable;
>>> +extern struct mutex ima_extend_list_mutex;
>>>    
>>>    static inline unsigned int ima_hash_key(u8 *digest)
>>>    {
>>> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
>>> index 87045b09f120..321c98ae0e55 100644
>>> --- a/security/integrity/ima/ima_fs.c
>>> +++ b/security/integrity/ima/ima_fs.c
>>> @@ -24,7 +24,12 @@
>>>    
>>>    #include "ima.h"
>>>    
>>> +/* Requests: ('A', [1, ULONG_MAX])\n (stage all/N) or D\n (delete staged) */
>>> +#define STAGED_REQ_LENGTH 21
>>> +
>>>    static DEFINE_MUTEX(ima_write_mutex);
>>> +static DEFINE_MUTEX(ima_measure_lock);
>>> +static long ima_measure_users;
>>>    
>>>    bool ima_canonical_fmt;
>>>    static int __init default_canonical_fmt_setup(char *str)
>>> @@ -74,14 +79,15 @@ static const struct file_operations ima_measurements_count_ops = {
>>>    };
>>>    
>>>    /* returns pointer to hlist_node */
>>> -static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
>>> +static void *_ima_measurements_start(struct seq_file *m, loff_t *pos,
>>> +				     struct list_head *head)
>>>    {
>>>    	loff_t l = *pos;
>>>    	struct ima_queue_entry *qe;
>>>    
>>>    	/* we need a lock since pos could point beyond last element */
>>>    	rcu_read_lock();
>>> -	list_for_each_entry_rcu(qe, &ima_measurements, later) {
>>> +	list_for_each_entry_rcu(qe, head, later) {
>>>    		if (!l--) {
>>>    			rcu_read_unlock();
>>>    			return qe;
>>> @@ -91,7 +97,18 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
>>>    	return NULL;
>>>    }
>>>    
>>> -static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
>>> +static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
>>> +{
>>> +	return _ima_measurements_start(m, pos, &ima_measurements);
>>> +}
>>> +
>>> +static void *ima_measurements_staged_start(struct seq_file *m, loff_t *pos)
>>> +{
>>> +	return _ima_measurements_start(m, pos, &ima_measurements_staged);
>>> +}
>>> +
>>> +static void *_ima_measurements_next(struct seq_file *m, void *v, loff_t *pos,
>>> +				    struct list_head *head)
>>>    {
>>>    	struct ima_queue_entry *qe = v;
>>>    
>>> @@ -103,7 +120,18 @@ static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
>>>    	rcu_read_unlock();
>>>    	(*pos)++;
>>>    
>>> -	return (&qe->later == &ima_measurements) ? NULL : qe;
>>> +	return (&qe->later == head) ? NULL : qe;
>>> +}
>>> +
>>> +static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
>>> +{
>>> +	return _ima_measurements_next(m, v, pos, &ima_measurements);
>>> +}
>>> +
>>> +static void *ima_measurements_staged_next(struct seq_file *m, void *v,
>>> +					  loff_t *pos)
>>> +{
>>> +	return _ima_measurements_next(m, v, pos, &ima_measurements_staged);
>>>    }
>>>    
>>>    static void ima_measurements_stop(struct seq_file *m, void *v)
>>> @@ -202,16 +230,138 @@ static const struct seq_operations ima_measurments_seqops = {
>>>    	.show = ima_measurements_show
>>>    };
>>>    
>>> +static int _ima_measurements_open(struct inode *inode, struct file *file,
>>> +				  const struct seq_operations *seq_ops)
>>> +{
>>> +	bool write = !!(file->f_mode & FMODE_WRITE);
>>> +	int ret;
>>> +
>>> +	if (write && !capable(CAP_SYS_ADMIN))
>>> +		return -EPERM;
>>> +
>>> +	mutex_lock(&ima_measure_lock);
>>> +	if ((write && ima_measure_users != 0) ||
>>> +	    (!write && ima_measure_users < 0)) {
>>> +		mutex_unlock(&ima_measure_lock);
>>> +		return -EBUSY;
>>> +	}
>>> +
>>> +	ret = seq_open(file, seq_ops);
>>> +	if (ret < 0) {
>>> +		mutex_unlock(&ima_measure_lock);
>>> +		return ret;
>>> +	}
>>> +
>>> +	if (write)
>>> +		ima_measure_users--;
>>> +	else
>>> +		ima_measure_users++;
>>> +
>>> +	mutex_unlock(&ima_measure_lock);
>>> +	return ret;
>>> +}
>>> +
>>>    static int ima_measurements_open(struct inode *inode, struct file *file)
>>>    {
>>> -	return seq_open(file, &ima_measurments_seqops);
>>> +	return _ima_measurements_open(inode, file, &ima_measurments_seqops);
>>> +}
>>> +
>>> +static int ima_measurements_release(struct inode *inode, struct file *file)
>>> +{
>>> +	bool write = !!(file->f_mode & FMODE_WRITE);
>>> +	int ret;
>>> +
>>> +	mutex_lock(&ima_measure_lock);
>>> +	ret = seq_release(inode, file);
>>> +	if (!ret) {
>>> +		if (write)
>>> +			ima_measure_users++;
>>> +		else
>>> +			ima_measure_users--;
>>> +	}
>>> +
>>> +	mutex_unlock(&ima_measure_lock);
>>> +	return ret;
>>>    }
>>>    
>>>    static const struct file_operations ima_measurements_ops = {
>>>    	.open = ima_measurements_open,
>>>    	.read = seq_read,
>>>    	.llseek = seq_lseek,
>>> -	.release = seq_release,
>>> +	.release = ima_measurements_release,
>>> +};
>>> +
>>> +static const struct seq_operations ima_measurments_staged_seqops = {
>>> +	.start = ima_measurements_staged_start,
>>> +	.next = ima_measurements_staged_next,
>>> +	.stop = ima_measurements_stop,
>>> +	.show = ima_measurements_show
>>> +};
>>> +
>>> +static int ima_measurements_staged_open(struct inode *inode, struct file *file)
>>> +{
>>> +	return _ima_measurements_open(inode, file,
>>> +				      &ima_measurments_staged_seqops);
>>> +}
>>> +
>>> +static ssize_t ima_measurements_staged_read(struct file *file, char __user *buf,
>>> +					    size_t size, loff_t *ppos)
>>> +{
>>> +	if (!ima_measurements_staged_exist)
>>> +		return -ENOENT;
>>> +
>>> +	return seq_read(file, buf, size, ppos);
>>> +}
>>> +
>>> +static ssize_t ima_measurements_staged_write(struct file *file,
>>> +					     const char __user *buf,
>>> +					     size_t datalen, loff_t *ppos)
>>> +{
>>> +	char req[STAGED_REQ_LENGTH], *req_ptr = req;
>>> +	unsigned long req_value;
>>> +	int ret;
>>> +
>>> +	if (*ppos > 0 || datalen < 2 || datalen > STAGED_REQ_LENGTH)
>>> +		return -EINVAL;
>>> +
>>> +	ret = copy_from_user(req, buf, datalen);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	if (strsep(&req_ptr, "\n") == NULL)
>>> +		return -EINVAL;
>>> +
>>> +	switch (req[0]) {
>>> +	case 'A':
>>> +		if (datalen != 2 || req[1] != '\0')
>>> +			return -EINVAL;
>>> +
>>> +		ret = ima_queue_stage(ULONG_MAX);
>>> +		break;
>>> +	case 'D':
>>> +		if (datalen != 2 || req[1] != '\0')
>>> +			return -EINVAL;
>>> +
>>> +		ret = ima_queue_delete_staged();
>>> +		break;
>>> +	default:
>>> +		ret = kstrtoul(req, 0, &req_value);
>>> +		if (!ret)
>>> +			ret = ima_queue_stage(req_value);
>>> +	}
>>> +
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	return datalen;
>>> +}
>>> +
>>> +static const struct file_operations ima_measurements_staged_ops = {
>>> +	.open = ima_measurements_staged_open,
>>> +	.read = ima_measurements_staged_read,
>>> +	.write = ima_measurements_staged_write,
>>> +	.llseek = seq_lseek,
>>> +	.release = ima_measurements_release,
>>>    };
>>>    
>>>    void ima_print_digest(struct seq_file *m, u8 *digest, u32 size)
>>> @@ -279,14 +429,37 @@ static const struct seq_operations ima_ascii_measurements_seqops = {
>>>    
>>>    static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
>>>    {
>>> -	return seq_open(file, &ima_ascii_measurements_seqops);
>>> +	return _ima_measurements_open(inode, file,
>>> +				      &ima_ascii_measurements_seqops);
>>>    }
>>>    
>>>    static const struct file_operations ima_ascii_measurements_ops = {
>>>    	.open = ima_ascii_measurements_open,
>>>    	.read = seq_read,
>>>    	.llseek = seq_lseek,
>>> -	.release = seq_release,
>>> +	.release = ima_measurements_release,
>>> +};
>>> +
>>> +static const struct seq_operations ima_ascii_measurements_staged_seqops = {
>>> +	.start = ima_measurements_staged_start,
>>> +	.next = ima_measurements_staged_next,
>>> +	.stop = ima_measurements_stop,
>>> +	.show = ima_ascii_measurements_show
>>> +};
>>> +
>>> +static int ima_ascii_measurements_staged_open(struct inode *inode,
>>> +					      struct file *file)
>>> +{
>>> +	return _ima_measurements_open(inode, file,
>>> +				      &ima_ascii_measurements_staged_seqops);
>>> +}
>>> +
>>> +static const struct file_operations ima_ascii_measurements_staged_ops = {
>>> +	.open = ima_ascii_measurements_staged_open,
>>> +	.read = ima_measurements_staged_read,
>>> +	.write = ima_measurements_staged_write,
>>> +	.llseek = seq_lseek,
>>> +	.release = ima_measurements_release,
>>>    };
>>>    
>>>    static ssize_t ima_read_policy(char *path)
>>> @@ -419,6 +592,25 @@ static int __init create_securityfs_measurement_lists(void)
>>>    						&ima_measurements_ops);
>>>    		if (IS_ERR(dentry))
>>>    			return PTR_ERR(dentry);
>>> +
>>> +		sprintf(file_name, "ascii_runtime_measurements_staged_%s",
>>> +			hash_algo_name[algo]);
>>> +		dentry = securityfs_create_file(file_name,
>>> +					S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP,
>>> +					ima_dir, (void *)(uintptr_t)i,
>>> +					&ima_ascii_measurements_staged_ops);
>>> +		if (IS_ERR(dentry))
>>> +			return PTR_ERR(dentry);
>>> +
>>> +		sprintf(file_name, "binary_runtime_measurements_staged_%s",
>>> +			hash_algo_name[algo]);
>>> +		dentry = securityfs_create_file(file_name,
>>> +						S_IRUSR | S_IRGRP |
>>> +						S_IWUSR | S_IWGRP,
>>> +						ima_dir, (void *)(uintptr_t)i,
>>> +						&ima_measurements_staged_ops);
>>> +		if (IS_ERR(dentry))
>>> +			return PTR_ERR(dentry);
>>>    	}
>>>    
>>>    	return 0;
>>> @@ -528,6 +720,20 @@ int __init ima_fs_init(void)
>>>    		goto out;
>>>    	}
>>>    
>>> +	dentry = securityfs_create_symlink("binary_runtime_measurements_staged",
>>> +		ima_dir, "binary_runtime_measurements_staged_sha1", NULL);
>>> +	if (IS_ERR(dentry)) {
>>> +		ret = PTR_ERR(dentry);
>>> +		goto out;
>>> +	}
>>> +
>>> +	dentry = securityfs_create_symlink("ascii_runtime_measurements_staged",
>>> +		ima_dir, "ascii_runtime_measurements_staged_sha1", NULL);
>>> +	if (IS_ERR(dentry)) {
>>> +		ret = PTR_ERR(dentry);
>>> +		goto out;
>>> +	}
>>> +
>>>    	dentry = securityfs_create_file("runtime_measurements_count",
>>>    				   S_IRUSR | S_IRGRP, ima_dir, NULL,
>>>    				   &ima_measurements_count_ops);
>>> diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
>>> index 7362f68f2d8b..23a20300da7b 100644
>>> --- a/security/integrity/ima/ima_kexec.c
>>> +++ b/security/integrity/ima/ima_kexec.c
>>> @@ -40,8 +40,8 @@ void ima_measure_kexec_event(const char *event_name)
>>>    	long len;
>>>    	int n;
>>>    
>>> -	buf_size = ima_get_binary_runtime_size();
>>> -	len = atomic_long_read(&ima_htable.len);
>>> +	buf_size = ima_get_binary_runtime_size(true);
>>> +	len = atomic_long_read(&ima_htable.len_notrim);
>>>    
>>>    	n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN,
>>>    		      "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;"
>>> @@ -93,8 +93,10 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
>>>    
>>>    	memset(&khdr, 0, sizeof(khdr));
>>>    	khdr.version = 1;
>>> -	/* This is an append-only list, no need to hold the RCU read lock */
>>> -	list_for_each_entry_rcu(qe, &ima_measurements, later, true) {
>>> +
>>> +	/* It can race with ima_queue_stage(). */
>>> +	mutex_lock(&ima_extend_list_mutex);
>>> +	list_for_each_entry(qe, &ima_measurements, later) {
>>>    		if (ima_kexec_file.count < ima_kexec_file.size) {
>>>    			khdr.count++;
>>>    			ima_measurements_show(&ima_kexec_file, qe);
>>> @@ -103,6 +105,7 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
>>>    			break;
>>>    		}
>>>    	}
>>> +	mutex_unlock(&ima_extend_list_mutex);
>>>    
>>>    	/*
>>>    	 * fill in reserved space with some buffer details
>>> @@ -157,7 +160,7 @@ void ima_add_kexec_buffer(struct kimage *image)
>>>    	else
>>>    		extra_memory = CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB * 1024;
>>>    
>>> -	binary_runtime_size = ima_get_binary_runtime_size() + extra_memory;
>>> +	binary_runtime_size = ima_get_binary_runtime_size(false) + extra_memory;
>>>    
>>>    	if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE)
>>>    		kexec_segment_size = ULONG_MAX;
>>> diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
>>> index 590637e81ad1..868f216ac343 100644
>>> --- a/security/integrity/ima/ima_queue.c
>>> +++ b/security/integrity/ima/ima_queue.c
>>> @@ -22,19 +22,32 @@
>>>    
>>>    #define AUDIT_CAUSE_LEN_MAX 32
>>>    
>>> +bool ima_flush_htable;
>>> +static int __init ima_flush_htable_setup(char *str)
>>> +{
>>> +	ima_flush_htable = true;
>>> +	return 1;
>>> +}
>>> +__setup("ima_flush_htable", ima_flush_htable_setup);
>>> +
>>>    /* pre-allocated array of tpm_digest structures to extend a PCR */
>>>    static struct tpm_digest *digests;
>>>    
>>>    LIST_HEAD(ima_measurements);	/* list of all measurements */
>>> +LIST_HEAD(ima_measurements_staged); /* list of staged measurements */
>>> +bool ima_measurements_staged_exist; /* If there are staged measurements */
>>>    #ifdef CONFIG_IMA_KEXEC
>>>    static unsigned long binary_runtime_size;
>>> +static unsigned long binary_runtime_size_notrim;
>>>    #else
>>>    static unsigned long binary_runtime_size = ULONG_MAX;
>>> +static unsigned long binary_runtime_size_notrim = ULONG_MAX;
>>>    #endif
>>>    
>>>    /* key: inode (before secure-hashing a file) */
>>>    struct ima_h_table ima_htable = {
>>>    	.len = ATOMIC_LONG_INIT(0),
>>> +	.len_notrim = ATOMIC_LONG_INIT(0),
>>>    	.violations = ATOMIC_LONG_INIT(0),
>>>    	.queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
>>>    };
>>> @@ -43,7 +56,7 @@ struct ima_h_table ima_htable = {
>>>     * and extending the TPM PCR aggregate. Since tpm_extend can take
>>>     * long (and the tpm driver uses a mutex), we can't use the spinlock.
>>>     */
>>> -static DEFINE_MUTEX(ima_extend_list_mutex);
>>> +DEFINE_MUTEX(ima_extend_list_mutex);
>>>    
>>>    /*
>>>     * Used internally by the kernel to suspend measurements.
>>> @@ -114,15 +127,19 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
>>>    	list_add_tail_rcu(&qe->later, &ima_measurements);
>>>    
>>>    	atomic_long_inc(&ima_htable.len);
>>> +	atomic_long_inc(&ima_htable.len_notrim);
>>>    	if (update_htable) {
>>>    		key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest);
>>>    		hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
>>>    	}
>>>    
>>> -	if (binary_runtime_size != ULONG_MAX) {
>>> +	if (binary_runtime_size_notrim != ULONG_MAX) {
>>>    		int size;
>>>    
>>>    		size = get_binary_runtime_size(entry);
>>> +		binary_runtime_size_notrim =
>>> +		     (binary_runtime_size_notrim < ULONG_MAX - size) ?
>>> +		     binary_runtime_size_notrim + size : ULONG_MAX;
>>>    		binary_runtime_size = (binary_runtime_size < ULONG_MAX - size) ?
>>>    		     binary_runtime_size + size : ULONG_MAX;
>>>    	}
>>> @@ -134,12 +151,18 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
>>>     * entire binary_runtime_measurement list, including the ima_kexec_hdr
>>>     * structure.
>>>     */
>>> -unsigned long ima_get_binary_runtime_size(void)
>>> +unsigned long ima_get_binary_runtime_size(bool notrim)
>>>    {
>>> -	if (binary_runtime_size >= (ULONG_MAX - sizeof(struct ima_kexec_hdr)))
>>> +	unsigned long *val;
>>> +
>>> +	mutex_lock(&ima_extend_list_mutex);
>>> +	val = (notrim) ? &binary_runtime_size_notrim : &binary_runtime_size;
>>> +	mutex_unlock(&ima_extend_list_mutex);
>>> +
>>> +	if (*val >= (ULONG_MAX - sizeof(struct ima_kexec_hdr)))
>>>    		return ULONG_MAX;
>>>    	else
>>> -		return binary_runtime_size + sizeof(struct ima_kexec_hdr);
>>> +		return *val + sizeof(struct ima_kexec_hdr);
>>>    }
>>>    
>>>    static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
>>> @@ -220,6 +243,84 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
>>>    	return result;
>>>    }
>>>    
>>> +int ima_queue_stage(unsigned long req_value)
>>> +{
>>> +	unsigned long req_value_copy = req_value, to_remove = 0;
>>> +	struct ima_queue_entry *qe;
>>> +
>>> +	if (ima_measurements_staged_exist)
>>> +		return -EEXIST;
>>> +
>>> +	mutex_lock(&ima_extend_list_mutex);
>>> +	if (list_empty(&ima_measurements)) {
>>> +		mutex_unlock(&ima_extend_list_mutex);
>>> +		return -ENOENT;
>>> +	}
>>> +
>>> +	if (req_value == ULONG_MAX) {
>>> +		list_replace(&ima_measurements, &ima_measurements_staged);
>>> +		INIT_LIST_HEAD(&ima_measurements);
>>> +		atomic_long_set(&ima_htable.len, 0);
>>> +		if (IS_ENABLED(CONFIG_IMA_KEXEC))
>>> +			binary_runtime_size = 0;
>>> +	} else {
>>> +		list_for_each_entry(qe, &ima_measurements, later) {
>>> +			to_remove += get_binary_runtime_size(qe->entry);
>>> +			if (--req_value_copy == 0)
>>> +				break;
>>> +		}
>>> +
>>> +		if (req_value_copy > 0) {
>>> +			mutex_unlock(&ima_extend_list_mutex);
>>> +			return -ENOENT;
>>> +		}
>>> +
>>> +		__list_cut_position(&ima_measurements_staged, &ima_measurements,
>>> +				    &qe->later);
>>> +		atomic_long_sub(req_value, &ima_htable.len);
>>> +		if (IS_ENABLED(CONFIG_IMA_KEXEC))
>>> +			binary_runtime_size -= to_remove;
>>> +	}
>>> +
>>> +	if (ima_flush_htable) {
>>> +		list_for_each_entry(qe, &ima_measurements_staged, later)
>>> +			/* It can race with ima_lookup_digest_entry(). */
>>> +			hlist_del_rcu(&qe->hnext);
>>> +	}
>>> +
>>> +	mutex_unlock(&ima_extend_list_mutex);
>>> +	ima_measurements_staged_exist = true;
>>> +	return 0;
>>> +}
>>> +
>>> +int ima_queue_delete_staged(void)
>>> +{
>>> +	struct ima_queue_entry *qe, *qe_tmp;
>>> +	unsigned int i;
>>> +
>>> +	if (!ima_measurements_staged_exist)
>>> +		return -ENOENT;
>>> +
>>> +	list_for_each_entry_safe(qe, qe_tmp, &ima_measurements_staged, later) {
>>> +		for (i = 0; i < qe->entry->template_desc->num_fields; i++) {
>>> +			kfree(qe->entry->template_data[i].data);
>>> +			qe->entry->template_data[i].data = NULL;
>>> +			qe->entry->template_data[i].len = 0;
>>> +		}
>>> +
>>> +		list_del(&qe->later);
>>> +
>>> +		if (ima_flush_htable) {
>>> +			kfree(qe->entry->digests);
>>> +			kfree(qe->entry);
>>> +			kfree(qe);
>>> +		}
>>> +	}
>>> +
>>> +	ima_measurements_staged_exist = false;
>>> +	return 0;
>>> +}
>>> +
>>>    int ima_restore_measurement_entry(struct ima_template_entry *entry)
>>>    {
>>>    	int result = 0;



Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ