IMA: allow reading back the current IMA policy
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>
This commit is contained in:
parent
41c89b64d7
commit
80eae209d6
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue