diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/apparmor/apparmorfs.c | 113 | ||||
-rw-r--r-- | security/apparmor/include/apparmorfs.h | 2 | ||||
-rw-r--r-- | security/apparmor/include/policy_ns.h | 1 | ||||
-rw-r--r-- | security/apparmor/policy_ns.c | 1 |
4 files changed, 116 insertions, 1 deletions
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 570d6b58b159..8c413333726b 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -23,6 +23,7 @@ #include <linux/capability.h> #include <linux/rcupdate.h> #include <linux/fs.h> +#include <linux/poll.h> #include <uapi/linux/major.h> #include <uapi/linux/magic.h> @@ -31,6 +32,7 @@ #include "include/audit.h" #include "include/context.h" #include "include/crypto.h" +#include "include/policy_ns.h" #include "include/policy.h" #include "include/policy_ns.h" #include "include/resource.h" @@ -498,11 +500,101 @@ static const struct file_operations aa_fs_profile_remove = { .llseek = default_llseek, }; +struct aa_revision { + struct aa_ns *ns; + long last_read; +}; + +/* revision file hook fn for policy loads */ +static int ns_revision_release(struct inode *inode, struct file *file) +{ + struct aa_revision *rev = file->private_data; + + if (rev) { + aa_put_ns(rev->ns); + kfree(rev); + } + + return 0; +} + +static ssize_t ns_revision_read(struct file *file, char __user *buf, + size_t size, loff_t *ppos) +{ + struct aa_revision *rev = file->private_data; + char buffer[32]; + long last_read; + int avail; + + mutex_lock(&rev->ns->lock); + last_read = rev->last_read; + if (last_read == rev->ns->revision) { + mutex_unlock(&rev->ns->lock); + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + if (wait_event_interruptible(rev->ns->wait, + last_read != + READ_ONCE(rev->ns->revision))) + return -ERESTARTSYS; + mutex_lock(&rev->ns->lock); + } + + avail = sprintf(buffer, "%ld\n", rev->ns->revision); + if (*ppos + size > avail) { + rev->last_read = rev->ns->revision; + *ppos = 0; + } + mutex_unlock(&rev->ns->lock); + + return simple_read_from_buffer(buf, size, ppos, buffer, avail); +} + +static int ns_revision_open(struct inode *inode, struct file *file) +{ + struct aa_revision *rev = kzalloc(sizeof(*rev), GFP_KERNEL); + + if (!rev) + return -ENOMEM; + + rev->ns = aa_get_ns(inode->i_private); + if (!rev->ns) + rev->ns = aa_get_current_ns(); + file->private_data = rev; + + return 0; +} + +static unsigned int ns_revision_poll(struct file *file, poll_table *pt) +{ + struct aa_revision *rev = file->private_data; + unsigned int mask = 0; + + if (rev) { + mutex_lock(&rev->ns->lock); + poll_wait(file, &rev->ns->wait, pt); + if (rev->last_read < rev->ns->revision) + mask |= POLLIN | POLLRDNORM; + mutex_unlock(&rev->ns->lock); + } + + return mask; +} + void __aa_bump_ns_revision(struct aa_ns *ns) { ns->revision++; + wake_up_interruptible(&ns->wait); } +static const struct file_operations aa_fs_ns_revision_fops = { + .owner = THIS_MODULE, + .open = ns_revision_open, + .poll = ns_revision_poll, + .read = ns_revision_read, + .llseek = generic_file_llseek, + .release = ns_revision_release, +}; + /** * query_data - queries a policy and writes its data to buf * @buf: the resulting data is stored here (NOT NULL) @@ -1280,6 +1372,10 @@ void __aafs_ns_rmdir(struct aa_ns *ns) sub = d_inode(ns_subremove(ns))->i_private; aa_put_ns(sub); } + if (ns_subrevision(ns)) { + sub = d_inode(ns_subrevision(ns))->i_private; + aa_put_ns(sub); + } for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) { aafs_remove(ns->dents[i]); @@ -1305,6 +1401,13 @@ static int __aafs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir) return PTR_ERR(dent); ns_subdata_dir(ns) = dent; + dent = aafs_create_file("revision", 0444, dir, ns, + &aa_fs_ns_revision_fops); + if (IS_ERR(dent)) + return PTR_ERR(dent); + aa_get_ns(ns); + ns_subrevision(ns) = dent; + dent = aafs_create_file(".load", 0640, dir, ns, &aa_fs_profile_load); if (IS_ERR(dent)) @@ -1930,11 +2033,19 @@ static int __init aa_create_aafs(void) } ns_subremove(root_ns) = dent; + dent = securityfs_create_file("revision", 0444, aa_sfs_entry.dentry, + NULL, &aa_fs_ns_revision_fops); + if (IS_ERR(dent)) { + error = PTR_ERR(dent); + goto error; + } + ns_subrevision(root_ns) = dent; + + /* policy tree referenced by magic policy symlink */ mutex_lock(&root_ns->lock); error = __aafs_ns_mkdir(root_ns, aafs_mnt->mnt_root, ".policy", aafs_mnt->mnt_root); mutex_unlock(&root_ns->lock); - if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 071a59a1f056..bd689114bf93 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -74,6 +74,7 @@ enum aafs_ns_type { AAFS_NS_LOAD, AAFS_NS_REPLACE, AAFS_NS_REMOVE, + AAFS_NS_REVISION, AAFS_NS_COUNT, AAFS_NS_MAX_COUNT, AAFS_NS_SIZE, @@ -102,6 +103,7 @@ enum aafs_prof_type { #define ns_subload(X) ((X)->dents[AAFS_NS_LOAD]) #define ns_subreplace(X) ((X)->dents[AAFS_NS_REPLACE]) #define ns_subremove(X) ((X)->dents[AAFS_NS_REMOVE]) +#define ns_subrevision(X) ((X)->dents[AAFS_NS_REVISION]) #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index d7a07ac96168..23e7cb770226 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -69,6 +69,7 @@ struct aa_ns { long uniq_id; int level; long revision; + wait_queue_head_t wait; struct list_head rawdata_list; diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 7d7c23705be2..f3418a9e59b1 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -101,6 +101,7 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) INIT_LIST_HEAD(&ns->sub_ns); INIT_LIST_HEAD(&ns->rawdata_list); mutex_init(&ns->lock); + init_waitqueue_head(&ns->wait); /* released by aa_free_ns() */ ns->unconfined = aa_alloc_profile("unconfined", GFP_KERNEL); |