Skip to content

Commit

Permalink
IMA: allow reading back the current IMA policy
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Petko Manolov authored and Mimi Zohar committed Dec 15, 2015
1 parent 41c89b6 commit 80eae20
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 8 deletions.
10 changes: 10 additions & 0 deletions security/integrity/ima/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 13 additions & 2 deletions security/integrity/ima/ima.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 */
29 changes: 25 additions & 4 deletions security/integrity/ima/ima_fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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,
};
Expand Down Expand Up @@ -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))
Expand Down
207 changes: 205 additions & 2 deletions security/integrity/ima/ima_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/rculist.h>
#include <linux/genhd.h>
#include <linux/seq_file.h>

#include "ima.h"

Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 */

0 comments on commit 80eae20

Please sign in to comment.