Trim N entries of IMA event logs
Subsystems (5)
v3 cover Trim N entries of IMA event logs
The Integrity Measurement Architecture (IMA) maintains a measurement list —a record of system events used for integrity verification. The IMA event logs are the entries within this measurement list, each representing a specific event or measurement that contributes to the system's integrity assessment.
This update introduces the ability to trim, or remove, N entries from the current measurement list. Trimming involves deleting N entries from the list and clearing the corresponding entries from the hash table. This action atomically truncates the measurement list, ensuring that no new measurements can be added until the operation is complete. Importantly, only one writer can initiate this trimming process at a time, maintaining consistency and preventing race conditions.
A userspace interface, ima_trim_log, has been provided for this purpose. By writing a number N to this interface, userspace can request the kernel to trim N entries from the IMA event logs. When this interface is read, it returns the number of entries trimmed during the last operation. This value is not preserved across kexec soft reboots, as it is not considered important information.
To maintain a complete record, userspace is responsible for concatenating and storing the logs before initiating trimming. Userspace can then send the collected data to remote verifiers for validation. After receiving confirmation from the remote verifiers, userspace may instruct the kernel to proceed with trimming the IMA event logs accordingly.
The primary benefit of this solution is the ability to free valuable kernel memory by delegating the task of reconstructing the full measurement list from log chunks to userspace. Trust is not required in userspace for the integrity of the measurement list, as its integrity is cryptographically protected by the Trusted Platform Module (TPM).
Multiple readers are allowed to access the ima_trim_log interface concurrently, while only one writer can trigger log trimming at any time. During trimming, readers do not see the list and cannot access it while deletion is in progress, ensuring atomicity.
Introduce the new kernel option ima_flush_htable to decide whether or not the digests of measurement entries are flushed from the hash table (from reference [2]).
The ima_measure_users counter (protected by the ima_measure_lock mutex) has been introduced to protect access to the measurement list 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 (from reference [2]).
New IMA log trim event is added when trimming finish. For kernel soft reboot case, there is no need to transfer extra data from old kernel to new kernel after trimming.
The time required for trimming is minimal, and IMA event logs are briefly on hold during this process, preventing read or add operations. This short interruption has no impact on the overall functionality of IMA.
A new critical data record "ima_log_trim" is added in this solution. This record logs the trim event with number of entries deleted and time when this happened. User space can get the total number of entries trimmed by checking all "ima_log_trim" event in the measurement list.
The following are how user space to use the measurement list and ima_log_trim interface
1. check any read/write measurement list/ima_log_trim operation, if any, wait until released
2. when no app read/write measurement list/ima_log_trim operation, set read/write flag
3. get the PCR quote first
4. read the measurement list file, close the file, send for verification with other IMA information to verifier
5. wait for response from verifier, until get the good response from verifier with number N that matched the PCR quote got in step 3
6. get the number N from the above message
7. write the N to the ima_log_trim interface
8. save total N entries of the measurement list read in step 4 to userspace
9. release read/write flag
References:
-----------
[1] [PATCH 0/1] Trim N entries of IMA event logs
https://lore.kernel.org/linux-integrity/[email protected]/T/#t
[2] [RFC][PATCH] ima: Add support for staging measurements for deletion
https://lore.kernel.org/linux-integrity/[email protected]/T/#t
[3] PATCH v2 0/1] Trim N entries of IMA event logs
https://lore.kernel.org/linux-integrity/[email protected]/T/#t
Change Log v3:
- Incorporated feedback from Mimi on v2 series.
- split patch into multiple patches
- lock time performance improvement
- Updated patch descriptions as necessary.
Change Log v2:
- Incorporated feedback from the Roberto on v1 series.
- Adapted code from Roberto's RFC [Reference 2]
- Add IMA log trim event log to record trim event
- Updated patch descriptions as necessary.
steven chen (3):
ima: make ima event log trimming configurable
ima: trim N IMA event log records
ima: add new critical data record to measure log trimming
.../admin-guide/kernel-parameters.txt | 4 + security/integrity/ima/Kconfig | 12 ++ security/integrity/ima/ima.h | 2 + security/integrity/ima/ima_fs.c | 182 +++++++++++++++++- security/integrity/ima/ima_queue.c | 85 ++++++++ 5 files changed, 281 insertions(+), 4 deletions(-)
Signature · 1 line
2.43.0
PATCH v3 1/3 ima: make ima event log trimming configurable
Make ima event log trimming function configurable.
--- security/integrity/ima/Kconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+)
security/integrity/ima/Kconfig +12 -0
@@ -332,4 +332,16 @@ config IMA_KEXEC_EXTRA_MEMORY_KB If set to the default value of 0, an extra half page of memory for those additional measurements will be allocated. +config IMA_LOG_TRIMMING + bool "IMA Event Log Trimming" + default n + help + Say Y here if you want support for IMA Event Log Trimming. + This creates the file /sys/kernel/security/integrity/ima/ima_trim_log. + Userspace + - writes to this file to trigger IMA event log trimming + - reads this file to get number of entried trimming last time + + If unsure, say N. + endif
Signature · 1 line
2.43.0
PATCH v3 2/3 ima: trim N IMA event log records
In reply to steven chenTrim N entries of the IMA event logs. Clean the hash table if ima_flush_htable is set.
Provide a userspace interface ima_trim_log that can be used to input number N to let kernel to trim N entries of IMA event logs. When read this interface, it returns number of entries trimmed last time.
--- .../admin-guide/kernel-parameters.txt | 4 + security/integrity/ima/ima.h | 2 + security/integrity/ima/ima_fs.c | 164 +++++++++++++++++- security/integrity/ima/ima_queue.c | 85 +++++++++ 4 files changed, 251 insertions(+), 4 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +4 -0
@@ -2197,6 +2197,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 trim all + or a part of it for deletion. + ima_hash= [IMA] Format: { md5 | sha1 | rmd160 | sha256 | sha384 | sha512 | ... }
security/integrity/ima/ima.h +2 -0
@@ -246,8 +246,10 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,#ifdef CONFIG_IMA_KEXEC void ima_measure_kexec_event (const char *event_name); +long ima_delete_event_log (long req_val);#else static inline void ima_measure_kexec_event (const char *event_name) {} +static inline long ima_delete_event_log (long req_val) {return 0 ; }#endif /*
security/integrity/ima/ima_fs.c +160 -4
208 lines not on this page · raw message
security/integrity/ima/ima_queue.c +85 -0
97 lines not on this page · raw message
Signature · 1 line
2.43.0
Re: [PATCH v3 2/3] ima: trim N IMA event log records
Quoted 60 lines · steven chen
Trim N entries of the IMA event logs. Clean the hash table if
ima_flush_htableis set.Provide a userspace interface
ima_trim_logthat can be used to input number N to let kernel to trim N entries of IMA event logs. When read this interface, it returns number of entries trimmed last time.Signed-off-by steven chen <[email protected]>--- .../admin-guide/kernel-parameters.txt | 4 + security/integrity/ima/ima.h | 2 + security/integrity/ima/ima_fs.c | 164 +++++++++++++++++- security/integrity/ima/ima_queue.c | 85 +++++++++ 4 files changed, 251 insertions(+), 4 deletions(-)Documentation/admin-guide/kernel-parameters.txt +4
-0@@ -2197,6 +2197,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 trim all + or a part of it for deletion. + ima_hash= [IMA] Format: { md5 | sha1 | rmd160 | sha256 | sha384 | sha512 | ... }security/integrity/ima/ima.h +2
-0@@ -246,8 +246,10 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,#ifdef CONFIG_IMA_KEXEC void ima_measure_kexec_event (const char *event_name); +long ima_delete_event_log (long req_val);#else static inline void ima_measure_kexec_event (const char *event_name) {} +static inline long ima_delete_event_log (long req_val) {return 0 ; }#endif /* security/integrity/ima/ima_fs.c +3
-0@@ -21,6 +21,9 @@#include <linux/rcupdate.h> #include <linux/parser.h> #include <linux/vmalloc.h> +#include <linux/ktime.h> +#include <linux/timekeeping.h> +#include <linux/ima.h> #include "ima.h"
security/integrity/ima/ima_fs.c __setup("ima_canonical_fmt", default_canonical_fmt_setup); · line 45
Quoted 5 lines
security/integrity/ima/ima_fs.c +2
-0@@ -38,6 +41,17 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);static int valid_policy =1 ; +#define IMA_LOG_TRIM_REQ_LENGTH 11 +#define IMA_LOG_TRIM_EVENT_LEN 256
Shouldn't this belong to the next patch?
Quoted 217 lines
security/integrity/ima/ima_fs.c +155
-4+ +static long trimcount; +/* mutex protects atomicity of trimming measurement list +* and also protects atomicity the measurement list read +* write operation. +*/ +static DEFINE_MUTEX (ima_measure_lock); +static long ima_measure_users; +static ssize_t ima_show_htable_value (char __user *buf,size_t count,loff_t *ppos,atomic_long_t *val) {@@ -202,16 +216,77 @@ static const struct seq_operations ima_measurments_seqops = { .show = ima_measurements_show }; +/* +* _ima_measurements_open - open the IMA measurements file +* @inode: inode of the file being opened +* @file: file being opened +* @seq_ops: sequence operations for the file +* +* Returns 0 on success, or negative error code. +* Implements mutual exclusion between readers and writer +* of the measurements file. Multiple readers are allowed, +* but writer get exclusive access only no other readers/writers. +* Readers is not allowed when there is a writer. +*/ +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, };void ima_print_digest (struct seq_file *m,u8 *digest,u32 size)@@ -279,14 +354,83 @@ 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 int ima_log_trim_open (struct inode *inode,struct file *file) +{ +bool write = !!(file->f_mode &FMODE_WRITE ); + +if (!write &&capable (CAP_SYS_ADMIN )) +return 0 ; +else if (!capable (CAP_SYS_ADMIN )) +return -EPERM ; + +return _ima_measurements_open (inode, file, &ima_measurments_seqops); +} + +static ssize_t ima_log_trim_read (struct file *file,char __user *buf,size_t size,loff_t *ppos) +{ +char tmpbuf[IMA_LOG_TRIM_REQ_LENGTH ];/* greater than largest 'long' string value */ +ssize_t len; + + len =scnprintf (tmpbuf,sizeof (tmpbuf),"%li\n" , trimcount); +return simple_read_from_buffer (buf, size, ppos, tmpbuf, len); +} + +static ssize_t ima_log_trim_write (struct file *file, +const char __user *buf,size_t datalen,loff_t *ppos) +{ +long count, n, ret; + +if (*ppos >0 || datalen >IMA_LOG_TRIM_REQ_LENGTH || datalen <2 ) { + ret = -EINVAL ; + goto out; + } + + n = (int )datalen; + + ret =kstrtol_from_user (buf, n,10 , &count); +if (ret <0 ) + goto out; + + ret =ima_delete_event_log (count); + +if (ret <0 ) + goto out; + + trimcount = ret; + + ret = datalen; +out: +return ret; +} + +static int ima_log_trim_release (struct inode *inode,struct file *file) +{ +bool write = !!(file->f_mode &FMODE_WRITE ); + +if (!write &&capable (CAP_SYS_ADMIN )) +return 0 ; +else if (!capable (CAP_SYS_ADMIN )) +return -EPERM ; + +return ima_measurements_release (inode, file); +} + +static const struct file_operations ima_log_trim_ops = { + .open = ima_log_trim_open, + .read = ima_log_trim_read, + .write = ima_log_trim_write, + .llseek = generic_file_llseek, + .release = ima_log_trim_release };static ssize_t ima_read_policy (char *path)@@ -528,6 +672,18 @@ int __init ima_fs_init(void) goto out; } +if (IS_ENABLED (CONFIG_IMA_LOG_TRIMMING )) { + dentry =securityfs_create_file ("ima_trim_log" , +S_IRUSR |S_IRGRP |S_IWUSR |S_IWGRP , + ima_dir,NULL , &ima_log_trim_ops); +if (IS_ERR (dentry)) { + ret =PTR_ERR (dentry); + goto out; + } + } + + trimcount =0 ; + dentry =securityfs_create_file ("runtime_measurements_count" ,S_IRUSR |S_IRGRP , ima_dir,NULL , &ima_measurements_count_ops);security/integrity/ima/ima_queue.c +8
-0@@ -22,6 +22,14 @@#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;
security/integrity/ima/ima_queue.c int ima_add_template_entry(struct ima_template_entry *entry, int violation, · line 236
Quoted 9 lines
security/integrity/ima/ima_queue.c +6
-0@@ -220,6 +228,83 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,return result; } +/** +* ima_delete_event_log - delete IMA event entry +* @num_records: number of records to delete +* +* delete num_records entries off the measurement list. +* Returns the number of entries deleted, or negative error code.
This is not according to the format stated in the documentation.
Quoted 21 lines
security/integrity/ima/ima_queue.c +21
-0+*/ +long ima_delete_event_log (long num_records) +{ +long len, cur = num_records, tmp_len =0 ; +struct ima_queue_entry *qe, *qe_tmp; +LIST_HEAD (ima_measurements_staged); +struct list_head *list_ptr; + +if (num_records <=0 ) +return num_records; + +if (!IS_ENABLED (CONFIG_IMA_LOG_TRIMMING )) +return -EOPNOTSUPP ; + +mutex_lock (&ima_extend_list_mutex); + len =atomic_long_read (&ima_htable.len); + +if (num_records > len) { +mutex_unlock (&ima_extend_list_mutex); +return -ENOENT ; + }
security/integrity/ima/ima_queue.c int ima_add_template_entry(struct ima_template_entry *entry, int violation, · line 267
Quoted 10 lines
security/integrity/ima/ima_queue.c +10
-0+ + list_ptr = &ima_measurements; + +if (cur == len) { +list_replace (&ima_measurements, &ima_measurements_staged); +INIT_LIST_HEAD (&ima_measurements); +atomic_long_set (&ima_htable.len,0 ); + list_ptr = &ima_measurements_staged; +if (IS_ENABLED (CONFIG_IMA_KEXEC )) + binary_runtime_size =0 ;
Like in my patch, we should have kept the original value of binary_runtime_size, to avoid breaking the kexec critical data records.
Quoted 25 lines
security/integrity/ima/ima_queue.c +25
-0+ } + +list_for_each_entry (qe, list_ptr, later) { +if (num_records >0 ) { +if (!IS_ENABLED (CONFIG_IMA_DISABLE_HTABLE ) && ima_flush_htable) +hlist_del_rcu (&qe->hnext); + + --num_records; +if (num_records ==0 ) + qe_tmp = qe; +continue ; + } +if (len != cur &&IS_ENABLED (CONFIG_IMA_KEXEC )) + tmp_len +=get_binary_runtime_size (qe->entry); +else +break ; + } + +if (len != cur) { +__list_cut_position (&ima_measurements_staged, &ima_measurements, + &qe_tmp->later); +atomic_long_sub (cur, &ima_htable.len); +if (IS_ENABLED (CONFIG_IMA_KEXEC )) + binary_runtime_size = tmp_len; + }
security/integrity/ima/ima_queue.c int ima_add_template_entry(struct ima_template_entry *entry, int violation, · line 302
Quoted 10 lines
security/integrity/ima/ima_queue.c +10
-0+ +mutex_unlock (&ima_extend_list_mutex); + +if (ima_flush_htable) +synchronize_rcu (); + +list_for_each_entry_safe (qe, qe_tmp, &ima_measurements_staged, later) { +ima_free_template_entry (qe->entry); +list_del (&qe->later); +kfree (qe);
If you don't flush the hash table, you cannot delete the entry.
Roberto
Quoted 8 lines
security/integrity/ima/ima_queue.c +5
-0+ } + +return cur; +} +int ima_restore_measurement_entry (struct ima_template_entry *entry) {int result =0 ;
Re: [PATCH v3 2/3] ima: trim N IMA event log records
Quoted 60 lines · Roberto Sassu
Quoted 59 lines · steven chen
Trim N entries of the IMA event logs. Clean the hash table if
ima_flush_htableis set.Provide a userspace interface
ima_trim_logthat can be used to input number N to let kernel to trim N entries of IMA event logs. When read this interface, it returns number of entries trimmed last time.Signed-off-by steven chen <[email protected]>--- .../admin-guide/kernel-parameters.txt | 4 + security/integrity/ima/ima.h | 2 + security/integrity/ima/ima_fs.c | 164 +++++++++++++++++- security/integrity/ima/ima_queue.c | 85 +++++++++ 4 files changed, 251 insertions(+), 4 deletions(-)Documentation/admin-guide/kernel-parameters.txt +4
-0@@ -2197,6 +2197,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 trim all + or a part of it for deletion. + ima_hash= [IMA] Format: { md5 | sha1 | rmd160 | sha256 | sha384 | sha512 | ... }security/integrity/ima/ima.h +2
-0@@ -246,8 +246,10 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,#ifdef CONFIG_IMA_KEXEC void ima_measure_kexec_event (const char *event_name); +long ima_delete_event_log (long req_val);#else static inline void ima_measure_kexec_event (const char *event_name) {} +static inline long ima_delete_event_log (long req_val) {return 0 ; }#endif /* security/integrity/ima/ima_fs.c +3
-0@@ -21,6 +21,9 @@#include <linux/rcupdate.h> #include <linux/parser.h> #include <linux/vmalloc.h> +#include <linux/ktime.h> +#include <linux/timekeeping.h> +#include <linux/ima.h> #include "ima.h"
Quoted 7 lines
security/integrity/ima/ima_fs.c __setup("ima_canonical_fmt", default_canonical_fmt_setup); · line 45
Quoted 6 lines
security/integrity/ima/ima_fs.c +2
-0@@ -38,6 +41,17 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);static int valid_policy =1 ; +#define IMA_LOG_TRIM_REQ_LENGTH 11 +#define IMA_LOG_TRIM_EVENT_LEN 256Shouldn't this belong to the next patch?
Will update this. Thanks
Quoted 220 lines
Quoted 220 lines
security/integrity/ima/ima_fs.c +155
-4+ +static long trimcount; +/* mutex protects atomicity of trimming measurement list +* and also protects atomicity the measurement list read +* write operation. +*/ +static DEFINE_MUTEX (ima_measure_lock); +static long ima_measure_users; +static ssize_t ima_show_htable_value (char __user *buf,size_t count,loff_t *ppos,atomic_long_t *val) {@@ -202,16 +216,77 @@ static const struct seq_operations ima_measurments_seqops = { .show = ima_measurements_show }; +/* +* _ima_measurements_open - open the IMA measurements file +* @inode: inode of the file being opened +* @file: file being opened +* @seq_ops: sequence operations for the file +* +* Returns 0 on success, or negative error code. +* Implements mutual exclusion between readers and writer +* of the measurements file. Multiple readers are allowed, +* but writer get exclusive access only no other readers/writers. +* Readers is not allowed when there is a writer. +*/ +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, };void ima_print_digest (struct seq_file *m,u8 *digest,u32 size)@@ -279,14 +354,83 @@ 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 int ima_log_trim_open (struct inode *inode,struct file *file) +{ +bool write = !!(file->f_mode &FMODE_WRITE ); + +if (!write &&capable (CAP_SYS_ADMIN )) +return 0 ; +else if (!capable (CAP_SYS_ADMIN )) +return -EPERM ; + +return _ima_measurements_open (inode, file, &ima_measurments_seqops); +} + +static ssize_t ima_log_trim_read (struct file *file,char __user *buf,size_t size,loff_t *ppos) +{ +char tmpbuf[IMA_LOG_TRIM_REQ_LENGTH ];/* greater than largest 'long' string value */ +ssize_t len; + + len =scnprintf (tmpbuf,sizeof (tmpbuf),"%li\n" , trimcount); +return simple_read_from_buffer (buf, size, ppos, tmpbuf, len); +} + +static ssize_t ima_log_trim_write (struct file *file, +const char __user *buf,size_t datalen,loff_t *ppos) +{ +long count, n, ret; + +if (*ppos >0 || datalen >IMA_LOG_TRIM_REQ_LENGTH || datalen <2 ) { + ret = -EINVAL ; + goto out; + } + + n = (int )datalen; + + ret =kstrtol_from_user (buf, n,10 , &count); +if (ret <0 ) + goto out; + + ret =ima_delete_event_log (count); + +if (ret <0 ) + goto out; + + trimcount = ret; + + ret = datalen; +out: +return ret; +} + +static int ima_log_trim_release (struct inode *inode,struct file *file) +{ +bool write = !!(file->f_mode &FMODE_WRITE ); + +if (!write &&capable (CAP_SYS_ADMIN )) +return 0 ; +else if (!capable (CAP_SYS_ADMIN )) +return -EPERM ; + +return ima_measurements_release (inode, file); +} + +static const struct file_operations ima_log_trim_ops = { + .open = ima_log_trim_open, + .read = ima_log_trim_read, + .write = ima_log_trim_write, + .llseek = generic_file_llseek, + .release = ima_log_trim_release };static ssize_t ima_read_policy (char *path)@@ -528,6 +672,18 @@ int __init ima_fs_init(void) goto out; } +if (IS_ENABLED (CONFIG_IMA_LOG_TRIMMING )) { + dentry =securityfs_create_file ("ima_trim_log" , +S_IRUSR |S_IRGRP |S_IWUSR |S_IWGRP , + ima_dir,NULL , &ima_log_trim_ops); +if (IS_ERR (dentry)) { + ret =PTR_ERR (dentry); + goto out; + } + } + + trimcount =0 ; + dentry =securityfs_create_file ("runtime_measurements_count" ,S_IRUSR |S_IRGRP , ima_dir,NULL , &ima_measurements_count_ops);security/integrity/ima/ima_queue.c +8
-0@@ -22,6 +22,14 @@#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;@@ -220,6 +228,83 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,return result; }
Quoted 7 lines
security/integrity/ima/ima_queue.c int ima_add_template_entry(struct ima_template_entry *entry, int violation, · line 236
Quoted 6 lines
security/integrity/ima/ima_queue.c +6
-0+/** +* ima_delete_event_log - delete IMA event entry +* @num_records: number of records to delete +* +* delete num_records entries off the measurement list. +* Returns the number of entries deleted, or negative error code. This is not according to the format stated in the documentation.
Will update this. Thanks
Quoted 25 lines
Quoted 25 lines
security/integrity/ima/ima_queue.c +25
-0+*/ +long ima_delete_event_log (long num_records) +{ +long len, cur = num_records, tmp_len =0 ; +struct ima_queue_entry *qe, *qe_tmp; +LIST_HEAD (ima_measurements_staged); +struct list_head *list_ptr; + +if (num_records <=0 ) +return num_records; + +if (!IS_ENABLED (CONFIG_IMA_LOG_TRIMMING )) +return -EOPNOTSUPP ; + +mutex_lock (&ima_extend_list_mutex); + len =atomic_long_read (&ima_htable.len); + +if (num_records > len) { +mutex_unlock (&ima_extend_list_mutex); +return -ENOENT ; + } + + list_ptr = &ima_measurements; + +if (cur == len) {
Quoted 8 lines
security/integrity/ima/ima_queue.c int ima_add_template_entry(struct ima_template_entry *entry, int violation, · line 267
Quoted 6 lines
security/integrity/ima/ima_queue.c +6
-0+list_replace (&ima_measurements, &ima_measurements_staged); +INIT_LIST_HEAD (&ima_measurements); +atomic_long_set (&ima_htable.len,0 ); + list_ptr = &ima_measurements_staged; +if (IS_ENABLED (CONFIG_IMA_KEXEC )) + binary_runtime_size =0 ;Like in my patch, we should have kept the original value of
binary_runtime_size, to avoid breaking the kexec critical data records.
I think this value is updated when trimming finish.
Quoted 37 lines
Quoted 25 lines
security/integrity/ima/ima_queue.c +25
-0+ } + +list_for_each_entry (qe, list_ptr, later) { +if (num_records >0 ) { +if (!IS_ENABLED (CONFIG_IMA_DISABLE_HTABLE ) && ima_flush_htable) +hlist_del_rcu (&qe->hnext); + + --num_records; +if (num_records ==0 ) + qe_tmp = qe; +continue ; + } +if (len != cur &&IS_ENABLED (CONFIG_IMA_KEXEC )) + tmp_len +=get_binary_runtime_size (qe->entry); +else +break ; + } + +if (len != cur) { +__list_cut_position (&ima_measurements_staged, &ima_measurements, + &qe_tmp->later); +atomic_long_sub (cur, &ima_htable.len); +if (IS_ENABLED (CONFIG_IMA_KEXEC )) + binary_runtime_size = tmp_len; + }security/integrity/ima/ima_queue.c int ima_add_template_entry(struct ima_template_entry *entry, int violation, · line 302
Quoted 10 lines
security/integrity/ima/ima_queue.c +10
-0+ +mutex_unlock (&ima_extend_list_mutex); + +if (ima_flush_htable) +synchronize_rcu (); + +list_for_each_entry_safe (qe, qe_tmp, &ima_measurements_staged, later) { +ima_free_template_entry (qe->entry); +list_del (&qe->later); +kfree (qe);If you don't flush the hash table, you cannot delete the entry.
Quoted 1 line
Roberto
Will update this. Thanks
Steven
Quoted 8 lines
Quoted 8 lines
security/integrity/ima/ima_queue.c +5
-0+ } + +return cur; +} +int ima_restore_measurement_entry (struct ima_template_entry *entry) {int result =0 ;
PATCH v3 3/3 ima: add new critical data record to measure log trimming
In reply to steven chenAdd a new critical data record to measure the trimming event when ima event records are deleted for this time.
If all IMA event logs are saved in the userspace, use this type of logs to get total numbers of records deleted from beginning.
--- security/integrity/ima/ima_fs.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
security/integrity/ima/ima_fs.c +18 -0
@@ -364,6 +364,22 @@ static const struct file_operations ima_ascii_measurements_ops = { .release = ima_measurements_release, }; +static void ima_measure_trim_event (const long number_logs) +{ +char ima_log_trim_event[IMA_LOG_TRIM_EVENT_LEN ]; +struct timespec64 ts; +u64 time_ns; +int n; + +ktime_get_real_ts64 (&ts); + time_ns = (u64 )ts.tv_sec *1000000000ULL + ts.tv_nsec; + n =scnprintf (ima_log_trim_event,IMA_LOG_TRIM_EVENT_LEN , +"time= %llu; number= %lu;" , time_ns, number_logs); + +ima_measure_critical_data ("ima_log_trim" ,"trim ima event logs" , + ima_log_trim_event, n, false,NULL ,0 ); +} +static int ima_log_trim_open (struct inode *inode,struct file *file) {bool write = !!(file->f_mode &FMODE_WRITE );
@@ -407,6 +423,8 @@ static ssize_t ima_log_trim_write(struct file *file, goto out; trimcount = ret; +if (trimcount >0 ) +ima_measure_trim_event (trimcount); ret = datalen; out:
Signature · 1 line
2.43.0