Signed-off-by: Denis Semakin <denis.semakin@xxxxxxxxxx> --- security/integrity/ima/ima.h | 1 + security/integrity/ima/ima_fs.c | 164 ++++++++++++++++++++++- security/integrity/ima/ima_init_ima_ns.c | 2 + security/integrity/ima/ima_ns.c | 31 +++++ 4 files changed, 193 insertions(+), 5 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index a717be9685ed..b46d6944f6ca 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -178,6 +178,7 @@ struct ima_namespace { int ima_extra_slots; struct vpcr_entry vpcr; uuid_t uuid; + struct dentry *parent_dentry; } __randomize_layout; extern struct ima_namespace init_ima_ns; diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 570e5d00a454..78d9f967e1f4 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -24,6 +24,7 @@ #include <linux/ima.h> #include "ima.h" +#define INUM_BUF_SIZE 16 extern struct list_head vpcr_list; static bool vpcr_mutex_acquired; @@ -84,9 +85,8 @@ static const struct file_operations ima_measurements_count_ops = { }; /* returns pointer to hlist_node */ -static void *ima_measurements_start(struct seq_file *m, loff_t *pos) +static void *__ima_measurements_start(struct ima_namespace *ns, loff_t *pos) { - struct ima_namespace *ns = ima_ns_from_file(m->file); loff_t l = *pos; struct ima_queue_entry *qe; @@ -102,9 +102,16 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos) return NULL; } -static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos) +static void *ima_measurements_start(struct seq_file *m, loff_t *pos) { struct ima_namespace *ns = ima_ns_from_file(m->file); + + return __ima_measurements_start(ns, pos); +} + +static void *__ima_measurements_next(struct ima_namespace *ns, void *v, + loff_t *pos) +{ struct ima_queue_entry *qe = v; /* lock protects when reading beyond last element @@ -118,6 +125,13 @@ static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos) return (&qe->later == &ns->ima_measurements) ? NULL : qe; } +static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct ima_namespace *ns = ima_ns_from_file(m->file); + + return __ima_measurements_next(ns, v, pos); +} + static void ima_measurements_stop(struct seq_file *m, void *v) { } @@ -237,10 +251,10 @@ void ima_print_digest(struct seq_file *m, u8 *digest, u32 size) } /* print in ascii */ -static int ima_ascii_measurements_show(struct seq_file *m, void *v) +static int __ima_ascii_measurements_show(struct ima_namespace *ns, + struct seq_file *m, void *v) { /* the list never shrinks, so we don't need a lock here */ - struct ima_namespace *ns = ima_ns_from_file(m->file); struct ima_queue_entry *qe = v; struct ima_template_entry *e; char *template_name; @@ -276,6 +290,13 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) return 0; } +static int ima_ascii_measurements_show(struct seq_file *m, void *v) +{ + struct ima_namespace *ns = ima_ns_from_file(m->file); + + return __ima_ascii_measurements_show(ns, m, v); +} + static const struct seq_operations ima_ascii_measurements_seqops = { .start = ima_measurements_start, .next = ima_measurements_next, @@ -706,6 +727,94 @@ static const struct file_operations ima_ascii_vpcr_fops = { .release = seq_release, }; +static void *child_ns_measure_start(struct seq_file *m, loff_t *pos) +{ + struct ima_namespace *ns = (struct ima_namespace *)m->private; + + return __ima_measurements_start(ns, pos); +} + +static void *child_ns_measure_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct ima_namespace *ns = (struct ima_namespace *)m->private; + + return __ima_measurements_next(ns, v, pos); +} + +static void child_ns_measure_stop(struct seq_file *m, void *v) +{ +} + +static int ascii_child_ns_measure_show(struct seq_file *m, void *v) +{ + struct ima_namespace *ns = (struct ima_namespace *)m->private; + + return __ima_ascii_measurements_show(ns, m, v); +} + +static int child_ns_measure_show(struct seq_file *m, void *v) +{ + struct ima_namespace *ns = (struct ima_namespace *)m->private; + + return ima_ns_measurements_show(ns, m, v); +} + + +const struct seq_operations ascii_child_ns_measure_seqops = { + .start = child_ns_measure_start, + .next = child_ns_measure_next, + .stop = child_ns_measure_stop, + .show = ascii_child_ns_measure_show, +}; + +const struct seq_operations child_ns_measure_seqops = { + .start = child_ns_measure_start, + .next = child_ns_measure_next, + .stop = child_ns_measure_stop, + .show = child_ns_measure_show, +}; + +static int child_ns_measure_open(struct inode *inode, struct file *filp, + struct seq_operations *seq_ops) +{ + int ret; + struct seq_file *m; + + ret = seq_open(filp, seq_ops); + if (ret) + return ret; + + m = filp->private_data; + m->private = inode->i_private; + + return 0; +} + +static int ascii_child_ns_measure_open(struct inode *inode, struct file *filp) +{ + return child_ns_measure_open(inode, filp, + &ascii_child_ns_measure_seqops); +} +static int bin_child_ns_measure_open(struct inode *inode, struct file *filp) +{ + return child_ns_measure_open(inode, filp, &child_ns_measure_seqops); +} + + +static const struct file_operations ascii_ima_child_ns_measure_fops = { + .open = ascii_child_ns_measure_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static const struct file_operations ima_child_ns_measure_fops = { + .open = bin_child_ns_measure_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root) { struct ima_namespace *ns = ima_ns_from_user_ns(user_ns); @@ -718,6 +827,9 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root) struct dentry *violations = NULL; struct dentry *binary_vpcr = NULL; struct dentry *ascii_vpcr = NULL; + struct dentry *child_dir; + struct dentry *child_ascii_dentry; + struct dentry *child_bin_dentry; int ret; /* @@ -821,6 +933,12 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root) ret = PTR_ERR(ascii_vpcr); goto out; } + + ns->parent_dentry = securityfs_create_dir("children", ima_dir); + if (IS_ERR(ima_dir)) { + ret = PTR_ERR(ima_dir); + goto out; + } } if (!ns->ima_policy_removed) { @@ -834,11 +952,47 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root) } } + if (ns != &init_ima_ns) { + char buf[INUM_BUF_SIZE]; + snprintf(buf, INUM_BUF_SIZE, "%u", user_ns->ns.inum); + child_dir = + securityfs_create_dir(buf, init_ima_ns.parent_dentry); + + if (IS_ERR(child_dir)) { + ret = PTR_ERR(child_dir); + if (ret != -EEXIST) /* Ignore only EEXIST */ + goto out; + } else { + child_ascii_dentry = + securityfs_create_file("ascii_measurement", + S_IRUSR | S_IWUSR | S_IRGRP, + child_dir, ns, + &ascii_ima_child_ns_measure_fops); + if (IS_ERR(child_ascii_dentry)) { + ret = PTR_ERR(child_ascii_dentry); + goto out; + } + + child_bin_dentry = + securityfs_create_file("binary_measurement", + S_IRUSR | S_IWUSR | S_IRGRP, + child_dir, ns, + &ima_child_ns_measure_fops); + if (IS_ERR(child_bin_dentry)) { + ret = PTR_ERR(child_bin_dentry); + goto out; + } + } + } + if (!ima_ns_from_user_ns(user_ns)) user_ns_set_ima_ns(user_ns, ns); return 0; out: + securityfs_remove(child_bin_dentry); + securityfs_remove(child_ascii_dentry); + securityfs_remove(child_dir); securityfs_remove(ascii_vpcr); securityfs_remove(binary_vpcr); securityfs_remove(ns->ima_policy); diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c index 53d5539f67d6..c989bbb22a97 100644 --- a/security/integrity/ima/ima_init_ima_ns.c +++ b/security/integrity/ima/ima_init_ima_ns.c @@ -86,6 +86,8 @@ int ima_init_namespace(struct ima_namespace *ns) /* Also the child ns inherits the algo array from init ns */ ns->ima_algo_array = init_ima_ns.ima_algo_array; + ns->parent_dentry = init_ima_ns.parent_dentry; + mutex_lock(&vpcr_list_mutex); list_add_tail(&ns->vpcr.list, &vpcr_list); mutex_unlock(&vpcr_list_mutex); diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index 3f65bfbba98d..de49abfdf9e9 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -7,6 +7,7 @@ */ #include <linux/ima.h> +#include <linux/namei.h> #include "ima.h" @@ -48,11 +49,41 @@ void ima_free_ima_ns(struct ima_namespace *ns) kmem_cache_free(imans_cachep, ns); } +#define DENTRY_BUF_SIZE 16 + +static void remove_ima_child_dentry(struct user_namespace *user_ns) +{ + char inum_dir[DENTRY_BUF_SIZE]; + char ascii_fname[DENTRY_BUF_SIZE * 2]; + char bin_fname[DENTRY_BUF_SIZE * 2]; + struct dentry *dentry_dir, *ascii_file, *bin_file; + + snprintf(inum_dir, DENTRY_BUF_SIZE, "%u", user_ns->ns.inum); + snprintf(ascii_fname, DENTRY_BUF_SIZE * 2, "%u/ascii", user_ns->ns.inum); + snprintf(bin_fname, DENTRY_BUF_SIZE * 2, "%u/bin", user_ns->ns.inum); + + inode_lock(d_inode(init_ima_ns.parent_dentry)); + + dentry_dir = lookup_one_len(inum_dir, init_ima_ns.parent_dentry, + strlen(inum_dir)); + ascii_file = lookup_one_len(ascii_fname, init_ima_ns.parent_dentry, + strlen(ascii_fname)); + bin_file = lookup_one_len(bin_fname, init_ima_ns.parent_dentry, + strlen(bin_fname)); + + inode_unlock(d_inode(init_ima_ns.parent_dentry)); + + securityfs_remove(ascii_file); + securityfs_remove(bin_file); + securityfs_remove(dentry_dir); +} + void free_ima_ns(struct user_namespace *user_ns) { struct ima_namespace *ns = ima_ns_from_user_ns(user_ns); ima_free_ima_ns(ns); + remove_ima_child_dentry(user_ns); user_ns->ima_ns = NULL; } -- 2.38.GIT