Re: [RFC] ima: export the measurement list when needed

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

Have in mind that below is the first trial draft that booted and
seemingly accomplished the task once, it was not really tested at all
yet. I will make a polished and tested version if people like the
concept.

Note that the code (almost) supports pushing and pulling of the
entries. This variant is a simple pull given that the list size is
above the defined limits. Pushing can be put in place if the recursion
with the list extend_list_mutex is cleared, maybe this could be done
via another patch later on when we have a workqueue for the export
task? The workqueue might be the best context for the export job since
clearing the list is a heavy operation (and it's not entirely correct
here AFAIK, there is no rcu sync before the template free).


--
Janne

On Wed, Dec 18, 2019 at 2:53 PM Janne Karhunen <janne.karhunen@xxxxxxxxx> wrote:
>
> Some systems can end up carrying lots of entries in the ima
> measurement list. Since every entry is using a bit of kernel
> memory, add a new Kconfig variable to allow the sysadmin to
> define the maximum measurement list size and the location
> of the exported list.
>
> The list is written out in append mode, so the system will
> keep writing new entries as long as it stays running or runs
> out of space. File is also automatically truncated on startup.
>
> Signed-off-by: Janne Karhunen <janne.karhunen@xxxxxxxxx>
> ---
>  security/integrity/ima/Kconfig     |  10 ++
>  security/integrity/ima/ima.h       |   7 +-
>  security/integrity/ima/ima_fs.c    | 178 +++++++++++++++++++++++++++++
>  security/integrity/ima/ima_queue.c |   2 +-
>  4 files changed, 192 insertions(+), 5 deletions(-)
>
> diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> index 897bafc59a33..dbfb76c7b347 100644
> --- a/security/integrity/ima/Kconfig
> +++ b/security/integrity/ima/Kconfig
> @@ -310,3 +310,13 @@ config IMA_APPRAISE_SIGNED_INIT
>         default n
>         help
>            This option requires user-space init to be signed.
> +
> +config IMA_MEASUREMENT_LIST_SIZE
> +       int
> +       depends on IMA
> +       default 5000
> +       help
> +          This option defines the maximum amount of entries in the
> +          measurement list. Once the limit is reached, the entire
> +          list is exported to a user defined file and the kernel
> +          list is freed.
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index 19769bf5f6ab..cc9a8bbb723d 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -151,20 +151,19 @@ int template_desc_init_fields(const char *template_fmt,
>  struct ima_template_desc *ima_template_desc_current(void);
>  struct ima_template_desc *lookup_template_desc(const char *name);
>  bool ima_template_has_modsig(const struct ima_template_desc *ima_template);
> +void ima_free_template_entry(struct ima_template_entry *entry);
>  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);
>  int ima_init_template(void);
>  void ima_init_template_list(void);
> +int ima_export_list(void);
>  int __init ima_init_digests(void);
>  int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
>                           void *lsm_data);
>
> -/*
> - * used to protect h_table and sha_table
> - */
> -extern spinlock_t ima_queue_lock;
> +extern struct mutex ima_extend_list_mutex;
>
>  struct ima_h_table {
>         atomic_long_t len;      /* number of stored measurements in the list */
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 2000e8df0301..d938d3921c43 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -22,10 +22,17 @@
>  #include <linux/rcupdate.h>
>  #include <linux/parser.h>
>  #include <linux/vmalloc.h>
> +#include <linux/fs_struct.h>
> +#include <linux/syscalls.h>
>
>  #include "ima.h"
>
> +#define secfs_mnt      "/sys/kernel/security"
> +#define am_filename    "/integrity/ima/ascii_runtime_measurements"
> +
>  static DEFINE_MUTEX(ima_write_mutex);
> +static DEFINE_MUTEX(ima_list_mutex);
> +static char ima_msmt_list_name[255];
>
>  bool ima_canonical_fmt;
>  static int __init default_canonical_fmt_setup(char *str)
> @@ -362,6 +369,7 @@ static struct dentry *ascii_runtime_measurements;
>  static struct dentry *runtime_measurements_count;
>  static struct dentry *violations;
>  static struct dentry *ima_policy;
> +static struct dentry *ima_list_name;
>
>  enum ima_fs_flags {
>         IMA_FS_BUSY,
> @@ -449,6 +457,169 @@ static const struct file_operations ima_measure_policy_ops = {
>         .llseek = generic_file_llseek,
>  };
>
> +static void ima_free_list(void)
> +{
> +       struct ima_queue_entry *qe, *e;
> +
> +       mutex_lock(&ima_extend_list_mutex);
> +       list_for_each_entry_safe(qe, e, &ima_measurements, later) {
> +               hlist_del_rcu(&qe->hnext);
> +               atomic_long_dec(&ima_htable.len);
> +
> +               list_del_rcu(&qe->later);
> +               ima_free_template_entry(qe->entry);
> +       }
> +       mutex_unlock(&ima_extend_list_mutex);
> +}
> +
> +static int ima_unlink_file(const char *filename)
> +{
> +       struct filename *file;
> +
> +       file = getname_kernel(filename);
> +       if (IS_ERR(file))
> +               return -EINVAL;
> +
> +       return do_unlinkat(AT_FDCWD, file);
> +}
> +
> +int ima_export_list(void)
> +{
> +       static bool export_ok = true;
> +       static bool init_export = true;
> +
> +       struct file *file_out = NULL;
> +       struct file *file_in = NULL;
> +       struct path root;
> +       ssize_t bytesin, bytesout;
> +       mm_segment_t fs;
> +       loff_t offin = 0, offout = 0;
> +       char data[512];
> +       long htable_len;
> +       int err = 0;
> +
> +       mutex_lock(&ima_list_mutex);
> +       htable_len = atomic_long_read(&ima_htable.len);
> +       if (htable_len <= CONFIG_IMA_MEASUREMENT_LIST_SIZE)
> +               goto out_unlock;
> +
> +       if (strlen(ima_msmt_list_name) == 0) {
> +               err = -ENOENT;
> +               goto out_unlock;
> +       }
> +       if (!export_ok) {
> +               err = -EINVAL;
> +               goto out_unlock;
> +       }
> +
> +       fs = get_fs();
> +       set_fs(KERNEL_DS);
> +
> +       if (init_export) {
> +               pr_info("ima: list size (%ld/%ld) exceeded, exporting to %s\n",
> +                       htable_len, (long)CONFIG_IMA_MEASUREMENT_LIST_SIZE,
> +                       ima_msmt_list_name);
> +
> +               ima_unlink_file(ima_msmt_list_name);
> +               init_export = false;
> +       }
> +       /*
> +        * Use the root of the init task..
> +        */
> +       task_lock(&init_task);
> +       get_fs_root(init_task.fs, &root);
> +       task_unlock(&init_task);
> +
> +       file_out = file_open_root(root.dentry, root.mnt, ima_msmt_list_name,
> +                                 O_CREAT|O_WRONLY|O_APPEND|O_NOFOLLOW|O_EXCL,
> +                                 0400);
> +       if (IS_ERR(file_out)) {
> +               err = PTR_ERR(file_out);
> +               goto out;
> +       }
> +       file_in = file_open_root(root.dentry, root.mnt, secfs_mnt am_filename,
> +                                O_RDONLY, 0);
> +       if (IS_ERR(file_in)) {
> +               err = PTR_ERR(file_in);
> +               goto out;
> +       }
> +       do {
> +               bytesin = vfs_read(file_in, data, 512, &offin);
> +               if (bytesin < 0) {
> +                       pr_err("ima: read error at %lld\n", offin);
> +                       err = -EIO;
> +                       goto out;
> +               }
> +               bytesout = vfs_write(file_out, data, bytesin, &offout);
> +               if (bytesout < 0) {
> +                       pr_err("ima: write error at %lld\n", offout);
> +                       err = -EIO;
> +                       goto out;
> +               }
> +               if (bytesin != bytesout) {
> +                       /*
> +                        * If we fail writing, the recovery is a job for the
> +                        * admin. Keep piling things in the memory for now.
> +                        */
> +                       export_ok = false;
> +                       err = bytesout;
> +                       goto out;
> +               }
> +       } while (bytesin == 512);
> +       ima_free_list();
> +
> +out:
> +       filp_close(file_in, NULL);
> +       filp_close(file_out, NULL);
> +
> +       path_put(&root);
> +       set_fs(fs);
> +
> +out_unlock:
> +       mutex_unlock(&ima_list_mutex);
> +       return err;
> +}
> +
> +static ssize_t ima_write_list_name(struct file *filp,
> +                                  const char __user *buf,
> +                                  size_t count, loff_t *ppos)
> +{
> +       int err, len;
> +
> +       if (!capable(CAP_SYS_ADMIN))
> +               return -EPERM;
> +
> +       if ((count <= 1) || (count >= 255))
> +               return -EINVAL;
> +
> +       if (*buf != '/')
> +               return -EINVAL;
> +
> +       if (*ima_msmt_list_name == '/')
> +               goto try_export;
> +
> +       err = copy_from_user(ima_msmt_list_name, buf, count);
> +       if (err) {
> +               memset(ima_msmt_list_name, 0, 255);
> +               return -EFAULT;
> +       }
> +       len = strnlen(ima_msmt_list_name, 255);
> +       if (ima_msmt_list_name[len-1] == '\n')
> +               ima_msmt_list_name[len-1] = 0;
> +
> +try_export:
> +       err = ima_export_list();
> +       if (err) {
> +               pr_err("ima: list export failed with %d\n", err);
> +               return -1;
> +       }
> +       return count;
> +}
> +
> +static const struct file_operations ima_list_export_ops = {
> +       .write = ima_write_list_name,
> +};
> +
>  int __init ima_fs_init(void)
>  {
>         ima_dir = securityfs_create_dir("ima", integrity_dir);
> @@ -493,6 +664,11 @@ int __init ima_fs_init(void)
>         if (IS_ERR(ima_policy))
>                 goto out;
>
> +       ima_list_name = securityfs_create_file("list_name", 0600, ima_dir,
> +                                              NULL, &ima_list_export_ops);
> +       if (IS_ERR(ima_list_name))
> +               goto out;
> +
>         return 0;
>  out:
>         securityfs_remove(violations);
> @@ -502,5 +678,7 @@ int __init ima_fs_init(void)
>         securityfs_remove(ima_symlink);
>         securityfs_remove(ima_dir);
>         securityfs_remove(ima_policy);
> +       securityfs_remove(ima_list_name);
> +
>         return -1;
>  }
> diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
> index 1ce8b1701566..77c538ec8474 100644
> --- a/security/integrity/ima/ima_queue.c
> +++ b/security/integrity/ima/ima_queue.c
> @@ -44,7 +44,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);
>
>  /* lookup up the digest value in the hash table, and return the entry */
>  static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
> --
> 2.17.1
>



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux Kernel]     [Linux Kernel Hardening]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux