On 15-10-16 22:31:31, Petko Manolov wrote:
> When in development it is useful to read back the IMA policy.  This patch
> provides the functionality.  However, this is a potential security hole so
> it should not be used in production-grade kernels.
> 
> Signed-off-by: Petko Manolov <pet...@mip-labs.com>
> ---
>  security/integrity/ima/Kconfig      |  13 +++
>  security/integrity/ima/ima.h        |  13 ++-
>  security/integrity/ima/ima_fs.c     |  29 +++++-
>  security/integrity/ima/ima_policy.c | 190 
> ++++++++++++++++++++++++++++++++++++
>  4 files changed, 239 insertions(+), 6 deletions(-)
> 
> diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> index 235b3c2..040778f 100644
> --- a/security/integrity/ima/Kconfig
> +++ b/security/integrity/ima/Kconfig
> @@ -121,6 +121,19 @@ config IMA_WRITE_POLICY
>  
>         If unsure, say N.
>  
> +config IMA_READ_POLICY
> +     bool "Enable reading back the current IMA policy"
> +     depends on IMA
> +     default n
> +     help
> +        When developing and debugging an IMA enabled system it is often
> +        useful to be able to read the IMA policy.  This patch allows
> +        for doing so.  However, being able to read the IMA policy is
> +        considered insecure and is strongly discouraged for
> +        production-grade kernels.
> +
> +        If unsure, say N.
> +
>  config IMA_APPRAISE
>       bool "Appraise integrity measurements"
>       depends on IMA
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index e2a60c3..ebc3554 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -166,6 +166,10 @@ void ima_update_policy(void);
>  void ima_update_policy_flag(void);
>  ssize_t ima_parse_add_rule(char *);
>  void ima_delete_rules(void);
> +void *ima_policy_start(struct seq_file *m, loff_t *pos);
> +void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
> +void ima_policy_stop(struct seq_file *m, void *v);
> +int ima_policy_show(struct seq_file *m, void *v);
>  
>  /* Appraise integrity measurements */
>  #define IMA_APPRAISE_ENFORCE 0x01
> @@ -263,4 +267,11 @@ static inline int ima_init_keyring(const unsigned int id)
>       return 0;
>  }
>  #endif /* CONFIG_IMA_TRUSTED_KEYRING */
> -#endif
> +
> +#ifdef       CONFIG_IMA_READ_POLICY
> +#define      POLICY_FILE_FLAGS       (S_IWUSR | S_IRUSR)
> +#else
> +#define      POLICY_FILE_FLAGS       S_IWUSR
> +#endif /* CONFIG_IMA_WRITE_POLICY */
> +
> +#endif /* __LINUX_IMA_H */
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index a3cf5c0..85bd129 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -311,14 +311,29 @@ enum ima_fs_flags {
>  
>  static unsigned long ima_fs_flags;
>  
> +#ifdef       CONFIG_IMA_READ_POLICY
> +static const struct seq_operations ima_policy_seqops = {
> +             .start = ima_policy_start,
> +             .next = ima_policy_next,
> +             .stop = ima_policy_stop,
> +             .show = ima_policy_show,
> +};
> +#endif
> +
>  /*
>   * ima_open_policy: sequentialize access to the policy file
>   */
>  static int ima_open_policy(struct inode *inode, struct file *filp)
>  {
> -     /* No point in being allowed to open it if you aren't going to write */
> -     if (!(filp->f_flags & O_WRONLY))
> +     if (!(filp->f_flags & O_WRONLY)) {
> +#ifndef      CONFIG_IMA_READ_POLICY
>               return -EACCES;
> +#else
> +             if (!capable(CAP_SYS_ADMIN))
> +                     return -EPERM;
> +             return seq_open(filp, &ima_policy_seqops);
> +#endif
> +     }
>       if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags))
>               return -EBUSY;
>       return 0;
> @@ -334,6 +349,10 @@ static int ima_open_policy(struct inode *inode, struct 
> file *filp)
>  static int ima_release_policy(struct inode *inode, struct file *file)
>  {
>       const char *cause = valid_policy ? "completed" : "failed";
> +     int policy_write = (file->f_flags & O_WRONLY);
> +
> +     if (!policy_write)
> +             return 0;

The above check is bogus so these four lines should go away too.  Silently 
returning if 'policy' is readable is now what we mean.  I guess we should check 
the operation, not the file flags.

                Petko


>       pr_info("IMA: policy update %s\n", cause);
>       integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
> @@ -342,9 +361,9 @@ static int ima_release_policy(struct inode *inode, struct 
> file *file)
>       if (!valid_policy) {
>               ima_delete_rules();
>               valid_policy = 1;
> -             clear_bit(IMA_FS_BUSY, &ima_fs_flags);
>               return 0;
>       }
> +
>       ima_update_policy();
>  #ifndef      CONFIG_IMA_WRITE_POLICY
>       securityfs_remove(ima_policy);
> @@ -358,6 +377,7 @@ static int ima_release_policy(struct inode *inode, struct 
> file *file)
>  static const struct file_operations ima_measure_policy_ops = {
>       .open = ima_open_policy,
>       .write = ima_write_policy,
> +     .read = seq_read,
>       .release = ima_release_policy,
>       .llseek = generic_file_llseek,
>  };
> @@ -395,8 +415,7 @@ int __init ima_fs_init(void)
>       if (IS_ERR(violations))
>               goto out;
>  
> -     ima_policy = securityfs_create_file("policy",
> -                                         S_IWUSR,
> +     ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
>                                           ima_dir, NULL,
>                                           &ima_measure_policy_ops);
>       if (IS_ERR(ima_policy))
> diff --git a/security/integrity/ima/ima_policy.c 
> b/security/integrity/ima/ima_policy.c
> index 7ace4e4..29961d7 100644
> --- a/security/integrity/ima/ima_policy.c
> +++ b/security/integrity/ima/ima_policy.c
> @@ -18,6 +18,7 @@
>  #include <linux/slab.h>
>  #include <linux/rculist.h>
>  #include <linux/genhd.h>
> +#include <linux/seq_file.h>
>  
>  #include "ima.h"
>  
> @@ -487,6 +488,35 @@ static match_table_t policy_tokens = {
>       {Opt_err, NULL}
>  };
>  
> +enum {
> +     mask_err = -1,
> +     mask_exec = 1, mask_write, mask_read, mask_append
> +};
> +
> +static match_table_t mask_tokens = {
> +     {mask_exec, "MAY_EXEC"},
> +     {mask_write, "MAY_WRITE"},
> +     {mask_read, "MAY_READ"},
> +     {mask_append, "MAY_APPEND"},
> +     {mask_err, NULL}
> +};
> +
> +enum {
> +     func_err = -1,
> +     func_file = 1, func_mmap, func_bprm,
> +     func_module, func_firmware, func_post
> +};
> +
> +static match_table_t func_tokens = {
> +     {func_file, "FILE_CHECK"},
> +     {func_mmap, "MMAP_CHECK"},
> +     {func_bprm, "BPRM_CHECK"},
> +     {func_module, "MODULE_CHECK"},
> +     {func_firmware, "FIRMWARE_CHECK"},
> +     {func_post, "POST_SETATTR"},
> +     {func_err, NULL}
> +};
> +
>  static int ima_lsm_rule_init(struct ima_rule_entry *entry,
>                            substring_t *args, int lsm_rule, int audit_type)
>  {
> @@ -829,3 +859,163 @@ void ima_delete_rules(void)
>               kfree(entry);
>       }
>  }
> +
> +void *ima_policy_start(struct seq_file *m, loff_t *pos)
> +{
> +     loff_t l = *pos;
> +     struct ima_rule_entry *entry;
> +
> +     rcu_read_lock();
> +     list_for_each_entry_rcu(entry, ima_rules, list) {
> +             if (!l--) {
> +                     rcu_read_unlock();
> +                     return entry;
> +             }
> +     }
> +     rcu_read_unlock();
> +     return NULL;
> +}
> +
> +void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
> +{
> +     struct ima_rule_entry *entry = v;
> +
> +     rcu_read_lock();
> +     entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list);
> +     rcu_read_unlock();
> +     (*pos)++;
> +
> +     return (&entry->list == ima_rules) ? NULL : entry;
> +}
> +
> +void ima_policy_stop(struct seq_file *m, void *v)
> +{
> +}
> +
> +#define pt(token)    policy_tokens[token-1].pattern
> +#define mt(token)    mask_tokens[token-1].pattern
> +#define ft(token)    func_tokens[token-1].pattern
> +
> +int ima_policy_show(struct seq_file *m, void *v)
> +{
> +     struct ima_rule_entry *entry = v;
> +     int i = 0;
> +
> +     rcu_read_lock();
> +
> +     if (entry->action & MEASURE)
> +             seq_puts(m, pt(Opt_measure));
> +     if (entry->action & DONT_MEASURE)
> +             seq_puts(m, pt(Opt_dont_measure));
> +     if (entry->action & APPRAISE)
> +             seq_puts(m, pt(Opt_appraise));
> +     if (entry->action & DONT_APPRAISE)
> +             seq_puts(m, pt(Opt_dont_appraise));
> +     if (entry->action & AUDIT)
> +             seq_puts(m, pt(Opt_audit));
> +
> +     seq_puts(m, " ");
> +
> +     if (entry->flags & IMA_FUNC) {
> +             switch (entry->func) {
> +             case FILE_CHECK:
> +                     seq_printf(m, pt(Opt_func), ft(func_file));
> +                     break;
> +             case MMAP_CHECK:
> +                     seq_printf(m, pt(Opt_func), ft(func_mmap));
> +                     break;
> +             case BPRM_CHECK:
> +                     seq_printf(m, pt(Opt_func), ft(func_bprm));
> +                     break;
> +             case MODULE_CHECK:
> +                     seq_printf(m, pt(Opt_func), ft(func_module));
> +                     break;
> +             case FIRMWARE_CHECK:
> +                     seq_printf(m, pt(Opt_func), ft(func_firmware));
> +                     break;
> +             case POST_SETATTR:
> +                     seq_printf(m, pt(Opt_func), ft(func_post));
> +                     break;
> +             default:
> +                     seq_printf(m, "func=%d", entry->func);
> +                     break;
> +             }
> +             seq_puts(m, " ");
> +     }
> +
> +     if (entry->flags & IMA_MASK) {
> +             if (entry->mask & MAY_EXEC)
> +                     seq_printf(m, pt(Opt_mask), mt(mask_exec));
> +             if (entry->mask & MAY_WRITE)
> +                     seq_printf(m, pt(Opt_mask), mt(mask_write));
> +             if (entry->mask & MAY_READ)
> +                     seq_printf(m, pt(Opt_mask), mt(mask_read));
> +             if (entry->mask & MAY_APPEND)
> +                     seq_printf(m, pt(Opt_mask), mt(mask_append));
> +             seq_puts(m, " ");
> +     }
> +
> +     if (entry->flags & IMA_FSMAGIC) {
> +             seq_printf(m, "fsmagic=0x%lx", entry->fsmagic);
> +             seq_puts(m, " ");
> +     }
> +
> +     if (entry->flags & IMA_FSUUID) {
> +             seq_puts(m, "fsuuid=");
> +             for (i = 0; i < ARRAY_SIZE(entry->fsuuid); ++i) {
> +                     switch (i) {
> +                     case 4:
> +                     case 6:
> +                     case 8:
> +                     case 10:
> +                             seq_puts(m, "-");
> +                     }
> +                     seq_printf(m, "%x", entry->fsuuid[i]);
> +             }
> +             seq_puts(m, " ");
> +     }
> +
> +     if (entry->flags & IMA_UID) {
> +             seq_printf(m, "uid=%d", __kuid_val(entry->uid));
> +             seq_puts(m, " ");
> +     }
> +
> +     if (entry->flags & IMA_FOWNER) {
> +             seq_printf(m, "fowner=%d", __kuid_val(entry->fowner));
> +             seq_puts(m, " ");
> +     }
> +
> +     for (i = 0; i < MAX_LSM_RULES; i++) {
> +             if (entry->lsm[i].rule) {
> +                     switch (i) {
> +                     case LSM_OBJ_USER:
> +                             seq_printf(m, pt(Opt_obj_user),
> +                                        (char *)entry->lsm[i].args_p);
> +                             break;
> +                     case LSM_OBJ_ROLE:
> +                             seq_printf(m, pt(Opt_obj_role),
> +                                        (char *)entry->lsm[i].args_p);
> +                             break;
> +                     case LSM_OBJ_TYPE:
> +                             seq_printf(m, pt(Opt_obj_type),
> +                                        (char *)entry->lsm[i].args_p);
> +                             break;
> +                     case LSM_SUBJ_USER:
> +                             seq_printf(m, pt(Opt_subj_user),
> +                                        (char *)entry->lsm[i].args_p);
> +                             break;
> +                     case LSM_SUBJ_ROLE:
> +                             seq_printf(m, pt(Opt_subj_role),
> +                                        (char *)entry->lsm[i].args_p);
> +                             break;
> +                     case LSM_SUBJ_TYPE:
> +                             seq_printf(m, pt(Opt_subj_type),
> +                                        (char *)entry->lsm[i].args_p);
> +                             break;
> +                     }
> +             }
> +     }
> +     rcu_read_unlock();
> +     seq_puts(m, "\n");
> +     return 0;
> +}
> -- 
> 2.6.1
> 
--
To unsubscribe from this list: send the line "unsubscribe 
linux-security-module" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to