IMA: allow reading back the current IMA policy
authorPetko Manolov <petkan@mip-labs.com>
Wed, 2 Dec 2015 15:47:56 +0000 (17:47 +0200)
committerMimi Zohar <zohar@linux.vnet.ibm.com>
Tue, 15 Dec 2015 15:01:43 +0000 (10:01 -0500)
It is often useful to be able to read back the IMA policy.  It is
even more important after introducing CONFIG_IMA_WRITE_POLICY.
This option allows the root user to see the current policy rules.

Signed-off-by: Zbigniew Jasinski <z.jasinski@samsung.com>
Signed-off-by: Petko Manolov <petkan@mip-labs.com>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
security/integrity/ima/Kconfig
security/integrity/ima/ima.h
security/integrity/ima/ima_fs.c
security/integrity/ima/ima_policy.c

index 8d5e6e0e0937c0d0a4467a4fb1f80fc74decafb7..e54a8a8dae941f1e2bbff37962184f4f1c84c192 100644 (file)
@@ -118,6 +118,16 @@ config IMA_WRITE_POLICY
 
          If unsure, say N.
 
+config IMA_READ_POLICY
+       bool "Enable reading back the current IMA policy"
+       depends on IMA
+       default y if IMA_WRITE_POLICY
+       default n if !IMA_WRITE_POLICY
+       help
+          It is often useful to be able to read back the IMA policy.  It is
+          even more important after introducing CONFIG_IMA_WRITE_POLICY.
+          This option allows the root user to see the current policy rules.
+
 config IMA_APPRAISE
        bool "Appraise integrity measurements"
        depends on IMA
index 9e82367f519068a8ef10da53fbf6096f232b5fad..917407fb7e941db411780154a31f9dfb25911378 100644 (file)
@@ -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
@@ -250,5 +254,12 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
 {
        return -EINVAL;
 }
-#endif /* CONFIG_IMA_LSM_RULES */
-#endif
+#endif /* CONFIG_IMA_TRUSTED_KEYRING */
+
+#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 */
index a3cf5c0ab501c18d27c5f152cdfe0ae5a1ad4394..eebb985fd0832b6cfcec494475bdc5f99a0042f3 100644 (file)
@@ -311,14 +311,31 @@ 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 ((filp->f_flags & O_ACCMODE) != O_RDONLY)
+                       return -EACCES;
+               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;
@@ -335,6 +352,9 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 {
        const char *cause = valid_policy ? "completed" : "failed";
 
+       if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+               return 0;
+
        pr_info("IMA: policy update %s\n", cause);
        integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
                            "policy_update", cause, !valid_policy, 0);
@@ -345,6 +365,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
                clear_bit(IMA_FS_BUSY, &ima_fs_flags);
                return 0;
        }
+
        ima_update_policy();
 #ifndef        CONFIG_IMA_WRITE_POLICY
        securityfs_remove(ima_policy);
@@ -358,6 +379,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 +417,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))
index 10a0a9b9e22d328d91b2f67bff729cf979fed14c..2f4e0f5f31e282610b25a408cedb93f957a6628c 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <linux/rculist.h>
 #include <linux/genhd.h>
+#include <linux/seq_file.h>
 
 #include "ima.h"
 
@@ -458,8 +459,8 @@ enum {
        Opt_obj_user, Opt_obj_role, Opt_obj_type,
        Opt_subj_user, Opt_subj_role, Opt_subj_type,
        Opt_func, Opt_mask, Opt_fsmagic,
-       Opt_uid, Opt_euid, Opt_fowner,
-       Opt_appraise_type, Opt_fsuuid, Opt_permit_directio
+       Opt_fsuuid, Opt_uid, Opt_euid, Opt_fowner,
+       Opt_appraise_type, Opt_permit_directio
 };
 
 static match_table_t policy_tokens = {
@@ -828,3 +829,205 @@ void ima_delete_rules(void)
                kfree(entry);
        }
 }
+
+#ifdef CONFIG_IMA_READ_POLICY
+enum {
+       mask_exec = 0, mask_write, mask_read, mask_append
+};
+
+static char *mask_tokens[] = {
+       "MAY_EXEC",
+       "MAY_WRITE",
+       "MAY_READ",
+       "MAY_APPEND"
+};
+
+enum {
+       func_file = 0, func_mmap, func_bprm,
+       func_module, func_firmware, func_post
+};
+
+static char *func_tokens[] = {
+       "FILE_CHECK",
+       "MMAP_CHECK",
+       "BPRM_CHECK",
+       "MODULE_CHECK",
+       "FIRMWARE_CHECK",
+       "POST_SETATTR"
+};
+
+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 + Opt_err].pattern
+#define mt(token)      mask_tokens[token]
+#define ft(token)      func_tokens[token]
+
+int ima_policy_show(struct seq_file *m, void *v)
+{
+       struct ima_rule_entry *entry = v;
+       int i = 0;
+       char tbuf[64] = {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:
+                       snprintf(tbuf, sizeof(tbuf), "%d", entry->func);
+                       seq_printf(m, pt(Opt_func), tbuf);
+                       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) {
+               snprintf(tbuf, sizeof(tbuf), "0x%lx", entry->fsmagic);
+               seq_printf(m, pt(Opt_fsmagic), tbuf);
+               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) {
+               snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
+               seq_printf(m, pt(Opt_uid), tbuf);
+               seq_puts(m, " ");
+       }
+
+       if (entry->flags & IMA_EUID) {
+               snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
+               seq_printf(m, pt(Opt_euid), tbuf);
+               seq_puts(m, " ");
+       }
+
+       if (entry->flags & IMA_FOWNER) {
+               snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner));
+               seq_printf(m, pt(Opt_fowner), tbuf);
+               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;
+                       }
+               }
+       }
+       if (entry->flags & IMA_DIGSIG_REQUIRED)
+               seq_puts(m, "appraise_type=imasig ");
+       if (entry->flags & IMA_PERMIT_DIRECTIO)
+               seq_puts(m, "permit_directio ");
+       rcu_read_unlock();
+       seq_puts(m, "\n");
+       return 0;
+}
+#endif /* CONFIG_IMA_READ_POLICY */