summaryrefslogtreecommitdiff
path: root/security/apparmor
diff options
context:
space:
mode:
Diffstat (limited to 'security/apparmor')
-rw-r--r--security/apparmor/apparmorfs.c113
-rw-r--r--security/apparmor/include/apparmorfs.h2
-rw-r--r--security/apparmor/include/policy_ns.h1
-rw-r--r--security/apparmor/policy_ns.c1
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);