summaryrefslogtreecommitdiffstats
path: root/security/apparmor/apparmorfs.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-07-05 11:26:35 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-07-05 11:26:35 -0700
commite24dd9ee5399747b71c1d982a484fc7601795f31 (patch)
tree14fcec8728916092a9f6dbeb0f2b8d5c5a4e5c9a /security/apparmor/apparmorfs.c
parent7391786a64dcfe9c609a1f8e2204c1abf42ded23 (diff)
parentc4758fa59285fe4dbfeab4364a6957936d040fbf (diff)
downloadlinux-e24dd9ee5399747b71c1d982a484fc7601795f31.tar.gz
linux-e24dd9ee5399747b71c1d982a484fc7601795f31.tar.bz2
linux-e24dd9ee5399747b71c1d982a484fc7601795f31.zip
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security layer updates from James Morris: - a major update for AppArmor. From JJ: * several bug fixes and cleanups * the patch to add symlink support to securityfs that was floated on the list earlier and the apparmorfs changes that make use of securityfs symlinks * it introduces the domain labeling base code that Ubuntu has been carrying for several years, with several cleanups applied. And it converts the current mediation over to using the domain labeling base, which brings domain stacking support with it. This finally will bring the base upstream code in line with Ubuntu and provide a base to upstream the new feature work that Ubuntu carries. * This does _not_ contain any of the newer apparmor mediation features/controls (mount, signals, network, keys, ...) that Ubuntu is currently carrying, all of which will be RFC'd on top of this. - Notable also is the Infiniband work in SELinux, and the new file:map permission. From Paul: "While we're down to 21 patches for v4.13 (it was 31 for v4.12), the diffstat jumps up tremendously with over 2k of line changes. Almost all of these changes are the SELinux/IB work done by Daniel Jurgens; some other noteworthy changes include a NFS v4.2 labeling fix, a new file:map permission, and reporting of policy capabilities on policy load" There's also now genfscon labeling support for tracefs, which was lost in v4.1 with the separation from debugfs. - Smack incorporates a safer socket check in file_receive, and adds a cap_capable call in privilege check. - TPM as usual has a bunch of fixes and enhancements. - Multiple calls to security_add_hooks() can now be made for the same LSM, to allow LSMs to have hook declarations across multiple files. - IMA now supports different "ima_appraise=" modes (eg. log, fix) from the boot command line. * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (126 commits) apparmor: put back designators in struct initialisers seccomp: Switch from atomic_t to recount_t seccomp: Adjust selftests to avoid double-join seccomp: Clean up core dump logic IMA: update IMA policy documentation to include pcr= option ima: Log the same audit cause whenever a file has no signature ima: Simplify policy_func_show. integrity: Small code improvements ima: fix get_binary_runtime_size() ima: use ima_parse_buf() to parse template data ima: use ima_parse_buf() to parse measurements headers ima: introduce ima_parse_buf() ima: Add cgroups2 to the defaults list ima: use memdup_user_nul ima: fix up #endif comments IMA: Correct Kconfig dependencies for hash selection ima: define is_ima_appraise_enabled() ima: define Kconfig IMA_APPRAISE_BOOTPARAM option ima: define a set of appraisal rules requiring file signatures ima: extend the "ima_policy" boot command line to support multiple policies ...
Diffstat (limited to 'security/apparmor/apparmorfs.c')
-rw-r--r--security/apparmor/apparmorfs.c1672
1 files changed, 1358 insertions, 314 deletions
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 4f6ac9dbc65d..853c2ec8e0c9 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -22,19 +22,52 @@
#include <linux/namei.h>
#include <linux/capability.h>
#include <linux/rcupdate.h>
-#include <uapi/linux/major.h>
#include <linux/fs.h>
+#include <linux/poll.h>
+#include <uapi/linux/major.h>
+#include <uapi/linux/magic.h>
#include "include/apparmor.h"
#include "include/apparmorfs.h"
#include "include/audit.h"
#include "include/context.h"
#include "include/crypto.h"
+#include "include/policy_ns.h"
+#include "include/label.h"
#include "include/policy.h"
#include "include/policy_ns.h"
#include "include/resource.h"
#include "include/policy_unpack.h"
+/*
+ * The apparmor filesystem interface used for policy load and introspection
+ * The interface is split into two main components based on their function
+ * a securityfs component:
+ * used for static files that are always available, and which allows
+ * userspace to specificy the location of the security filesystem.
+ *
+ * fns and data are prefixed with
+ * aa_sfs_
+ *
+ * an apparmorfs component:
+ * used loaded policy content and introspection. It is not part of a
+ * regular mounted filesystem and is available only through the magic
+ * policy symlink in the root of the securityfs apparmor/ directory.
+ * Tasks queries will be magically redirected to the correct portion
+ * of the policy tree based on their confinement.
+ *
+ * fns and data are prefixed with
+ * aafs_
+ *
+ * The aa_fs_ prefix is used to indicate the fn is used by both the
+ * securityfs and apparmorfs filesystems.
+ */
+
+
+/*
+ * support fns
+ */
+
/**
* aa_mangle_name - mangle a profile name to std profile layout form
* @name: profile name to mangle (NOT NULL)
@@ -74,6 +107,265 @@ static int mangle_name(const char *name, char *target)
return t - target;
}
+
+/*
+ * aafs - core fns and data for the policy tree
+ */
+
+#define AAFS_NAME "apparmorfs"
+static struct vfsmount *aafs_mnt;
+static int aafs_count;
+
+
+static int aafs_show_path(struct seq_file *seq, struct dentry *dentry)
+{
+ struct inode *inode = d_inode(dentry);
+
+ seq_printf(seq, "%s:[%lu]", AAFS_NAME, inode->i_ino);
+ return 0;
+}
+
+static void aafs_evict_inode(struct inode *inode)
+{
+ truncate_inode_pages_final(&inode->i_data);
+ clear_inode(inode);
+ if (S_ISLNK(inode->i_mode))
+ kfree(inode->i_link);
+}
+
+static const struct super_operations aafs_super_ops = {
+ .statfs = simple_statfs,
+ .evict_inode = aafs_evict_inode,
+ .show_path = aafs_show_path,
+};
+
+static int fill_super(struct super_block *sb, void *data, int silent)
+{
+ static struct tree_descr files[] = { {""} };
+ int error;
+
+ error = simple_fill_super(sb, AAFS_MAGIC, files);
+ if (error)
+ return error;
+ sb->s_op = &aafs_super_ops;
+
+ return 0;
+}
+
+static struct dentry *aafs_mount(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data)
+{
+ return mount_single(fs_type, flags, data, fill_super);
+}
+
+static struct file_system_type aafs_ops = {
+ .owner = THIS_MODULE,
+ .name = AAFS_NAME,
+ .mount = aafs_mount,
+ .kill_sb = kill_anon_super,
+};
+
+/**
+ * __aafs_setup_d_inode - basic inode setup for apparmorfs
+ * @dir: parent directory for the dentry
+ * @dentry: dentry we are seting the inode up for
+ * @mode: permissions the file should have
+ * @data: data to store on inode.i_private, available in open()
+ * @link: if symlink, symlink target string
+ * @fops: struct file_operations that should be used
+ * @iops: struct of inode_operations that should be used
+ */
+static int __aafs_setup_d_inode(struct inode *dir, struct dentry *dentry,
+ umode_t mode, void *data, char *link,
+ const struct file_operations *fops,
+ const struct inode_operations *iops)
+{
+ struct inode *inode = new_inode(dir->i_sb);
+
+ AA_BUG(!dir);
+ AA_BUG(!dentry);
+
+ if (!inode)
+ return -ENOMEM;
+
+ inode->i_ino = get_next_ino();
+ inode->i_mode = mode;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
+ inode->i_private = data;
+ if (S_ISDIR(mode)) {
+ inode->i_op = iops ? iops : &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+ inc_nlink(inode);
+ inc_nlink(dir);
+ } else if (S_ISLNK(mode)) {
+ inode->i_op = iops ? iops : &simple_symlink_inode_operations;
+ inode->i_link = link;
+ } else {
+ inode->i_fop = fops;
+ }
+ d_instantiate(dentry, inode);
+ dget(dentry);
+
+ return 0;
+}
+
+/**
+ * aafs_create - create a dentry in the apparmorfs filesystem
+ *
+ * @name: name of dentry to create
+ * @mode: permissions the file should have
+ * @parent: parent directory for this dentry
+ * @data: data to store on inode.i_private, available in open()
+ * @link: if symlink, symlink target string
+ * @fops: struct file_operations that should be used for
+ * @iops: struct of inode_operations that should be used
+ *
+ * This is the basic "create a xxx" function for apparmorfs.
+ *
+ * Returns a pointer to a dentry if it succeeds, that must be free with
+ * aafs_remove(). Will return ERR_PTR on failure.
+ */
+static struct dentry *aafs_create(const char *name, umode_t mode,
+ struct dentry *parent, void *data, void *link,
+ const struct file_operations *fops,
+ const struct inode_operations *iops)
+{
+ struct dentry *dentry;
+ struct inode *dir;
+ int error;
+
+ AA_BUG(!name);
+ AA_BUG(!parent);
+
+ if (!(mode & S_IFMT))
+ mode = (mode & S_IALLUGO) | S_IFREG;
+
+ error = simple_pin_fs(&aafs_ops, &aafs_mnt, &aafs_count);
+ if (error)
+ return ERR_PTR(error);
+
+ dir = d_inode(parent);
+
+ inode_lock(dir);
+ dentry = lookup_one_len(name, parent, strlen(name));
+ if (IS_ERR(dentry))
+ goto fail_lock;
+
+ if (d_really_is_positive(dentry)) {
+ error = -EEXIST;
+ goto fail_dentry;
+ }
+
+ error = __aafs_setup_d_inode(dir, dentry, mode, data, link, fops, iops);
+ if (error)
+ goto fail_dentry;
+ inode_unlock(dir);
+
+ return dentry;
+
+fail_dentry:
+ dput(dentry);
+
+fail_lock:
+ inode_unlock(dir);
+ simple_release_fs(&aafs_mnt, &aafs_count);
+
+ return ERR_PTR(error);
+}
+
+/**
+ * aafs_create_file - create a file in the apparmorfs filesystem
+ *
+ * @name: name of dentry to create
+ * @mode: permissions the file should have
+ * @parent: parent directory for this dentry
+ * @data: data to store on inode.i_private, available in open()
+ * @fops: struct file_operations that should be used for
+ *
+ * see aafs_create
+ */
+static struct dentry *aafs_create_file(const char *name, umode_t mode,
+ struct dentry *parent, void *data,
+ const struct file_operations *fops)
+{
+ return aafs_create(name, mode, parent, data, NULL, fops, NULL);
+}
+
+/**
+ * aafs_create_dir - create a directory in the apparmorfs filesystem
+ *
+ * @name: name of dentry to create
+ * @parent: parent directory for this dentry
+ *
+ * see aafs_create
+ */
+static struct dentry *aafs_create_dir(const char *name, struct dentry *parent)
+{
+ return aafs_create(name, S_IFDIR | 0755, parent, NULL, NULL, NULL,
+ NULL);
+}
+
+/**
+ * aafs_create_symlink - create a symlink in the apparmorfs filesystem
+ * @name: name of dentry to create
+ * @parent: parent directory for this dentry
+ * @target: if symlink, symlink target string
+ * @iops: struct of inode_operations that should be used
+ *
+ * If @target parameter is %NULL, then the @iops parameter needs to be
+ * setup to handle .readlink and .get_link inode_operations.
+ */
+static struct dentry *aafs_create_symlink(const char *name,
+ struct dentry *parent,
+ const char *target,
+ const struct inode_operations *iops)
+{
+ struct dentry *dent;
+ char *link = NULL;
+
+ if (target) {
+ link = kstrdup(target, GFP_KERNEL);
+ if (!link)
+ return ERR_PTR(-ENOMEM);
+ }
+ dent = aafs_create(name, S_IFLNK | 0444, parent, NULL, link, NULL,
+ iops);
+ if (IS_ERR(dent))
+ kfree(link);
+
+ return dent;
+}
+
+/**
+ * aafs_remove - removes a file or directory from the apparmorfs filesystem
+ *
+ * @dentry: dentry of the file/directory/symlink to removed.
+ */
+static void aafs_remove(struct dentry *dentry)
+{
+ struct inode *dir;
+
+ if (!dentry || IS_ERR(dentry))
+ return;
+
+ dir = d_inode(dentry->d_parent);
+ inode_lock(dir);
+ if (simple_positive(dentry)) {
+ if (d_is_dir(dentry))
+ simple_rmdir(dir, dentry);
+ else
+ simple_unlink(dir, dentry);
+ dput(dentry);
+ }
+ inode_unlock(dir);
+ simple_release_fs(&aafs_mnt, &aafs_count);
+}
+
+
+/*
+ * aa_fs - policy load/replace/remove
+ */
+
/**
* aa_simple_write_to_buffer - common routine for getting policy from user
* @userbuf: user buffer to copy data from (NOT NULL)
@@ -98,14 +390,11 @@ static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
return ERR_PTR(-ESPIPE);
/* freed by caller to simple_write_to_buffer */
- data = kvmalloc(sizeof(*data) + alloc_size, GFP_KERNEL);
- if (data == NULL)
- return ERR_PTR(-ENOMEM);
- kref_init(&data->count);
- data->size = copy_size;
- data->hash = NULL;
- data->abi = 0;
+ data = aa_loaddata_alloc(alloc_size);
+ if (IS_ERR(data))
+ return data;
+ data->size = copy_size;
if (copy_from_user(data->data, userbuf, copy_size)) {
kvfree(data);
return ERR_PTR(-EFAULT);
@@ -114,27 +403,29 @@ static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
return data;
}
-static ssize_t policy_update(int binop, const char __user *buf, size_t size,
+static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
loff_t *pos, struct aa_ns *ns)
{
- ssize_t error;
struct aa_loaddata *data;
- struct aa_profile *profile = aa_current_profile();
- const char *op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL;
+ struct aa_label *label;
+ ssize_t error;
+
+ label = begin_current_label_crit_section();
+
/* high level check about policy management - fine grained in
* below after unpack
*/
- error = aa_may_manage_policy(profile, ns, op);
+ error = aa_may_manage_policy(label, ns, mask);
if (error)
return error;
data = aa_simple_write_to_buffer(buf, size, size, pos);
error = PTR_ERR(data);
if (!IS_ERR(data)) {
- error = aa_replace_profiles(ns ? ns : profile->ns, profile,
- binop, data);
+ error = aa_replace_profiles(ns, label, mask, data);
aa_put_loaddata(data);
}
+ end_current_label_crit_section(label);
return error;
}
@@ -144,7 +435,7 @@ static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
loff_t *pos)
{
struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
- int error = policy_update(PROF_ADD, buf, size, pos, ns);
+ int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns);
aa_put_ns(ns);
@@ -161,8 +452,8 @@ static ssize_t profile_replace(struct file *f, const char __user *buf,
size_t size, loff_t *pos)
{
struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
- int error = policy_update(PROF_REPLACE, buf, size, pos, ns);
-
+ int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY,
+ buf, size, pos, ns);
aa_put_ns(ns);
return error;
@@ -178,15 +469,15 @@ static ssize_t profile_remove(struct file *f, const char __user *buf,
size_t size, loff_t *pos)
{
struct aa_loaddata *data;
- struct aa_profile *profile;
+ struct aa_label *label;
ssize_t error;
struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
- profile = aa_current_profile();
+ label = begin_current_label_crit_section();
/* high level check about policy management - fine grained in
* below after unpack
*/
- error = aa_may_manage_policy(profile, ns, OP_PROF_RM);
+ error = aa_may_manage_policy(label, ns, AA_MAY_REMOVE_POLICY);
if (error)
goto out;
@@ -199,11 +490,11 @@ static ssize_t profile_remove(struct file *f, const char __user *buf,
error = PTR_ERR(data);
if (!IS_ERR(data)) {
data->data[size] = 0;
- error = aa_remove_profiles(ns ? ns : profile->ns, profile,
- data->data, size);
+ error = aa_remove_profiles(ns, label, data->data, size);
aa_put_loaddata(data);
}
out:
+ end_current_label_crit_section(label);
aa_put_ns(ns);
return error;
}
@@ -213,6 +504,136 @@ 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,
+};
+
+static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
+ const char *match_str, size_t match_len)
+{
+ struct aa_perms tmp;
+ struct aa_dfa *dfa;
+ unsigned int state = 0;
+
+ if (profile_unconfined(profile))
+ return;
+ if (profile->file.dfa && *match_str == AA_CLASS_FILE) {
+ dfa = profile->file.dfa;
+ state = aa_dfa_match_len(dfa, profile->file.start,
+ match_str + 1, match_len - 1);
+ tmp = nullperms;
+ if (state) {
+ struct path_cond cond = { };
+
+ tmp = aa_compute_fperms(dfa, state, &cond);
+ }
+ } else if (profile->policy.dfa) {
+ if (!PROFILE_MEDIATES_SAFE(profile, *match_str))
+ return; /* no change to current perms */
+ dfa = profile->policy.dfa;
+ state = aa_dfa_match_len(dfa, profile->policy.start[0],
+ match_str, match_len);
+ if (state)
+ aa_compute_perms(dfa, state, &tmp);
+ else
+ tmp = nullperms;
+ }
+ aa_apply_modes_to_perms(profile, &tmp);
+ aa_perms_accum_raw(perms, &tmp);
+}
+
+
/**
* query_data - queries a policy and writes its data to buf
* @buf: the resulting data is stored here (NOT NULL)
@@ -236,6 +657,8 @@ static ssize_t query_data(char *buf, size_t buf_len,
{
char *out;
const char *key;
+ struct label_it i;
+ struct aa_label *label, *curr;
struct aa_profile *profile;
struct aa_data *data;
u32 bytes, blocks;
@@ -253,7 +676,11 @@ static ssize_t query_data(char *buf, size_t buf_len,
if (buf_len < sizeof(bytes) + sizeof(blocks))
return -EINVAL; /* not enough space */
- profile = aa_current_profile();
+ curr = begin_current_label_crit_section();
+ label = aa_label_parse(curr, query, GFP_KERNEL, false, false);
+ end_current_label_crit_section(curr);
+ if (IS_ERR(label))
+ return PTR_ERR(label);
/* We are going to leave space for two numbers. The first is the total
* number of bytes we are writing after the first number. This is so
@@ -267,13 +694,19 @@ static ssize_t query_data(char *buf, size_t buf_len,
out = buf + sizeof(bytes) + sizeof(blocks);
blocks = 0;
- if (profile->data) {
+ label_for_each_confined(i, label, profile) {
+ if (!profile->data)
+ continue;
+
data = rhashtable_lookup_fast(profile->data, &key,
profile->data->p);
if (data) {
- if (out + sizeof(outle32) + data->size > buf + buf_len)
+ if (out + sizeof(outle32) + data->size > buf +
+ buf_len) {
+ aa_put_label(label);
return -EINVAL; /* not enough space */
+ }
outle32 = __cpu_to_le32(data->size);
memcpy(out, &outle32, sizeof(outle32));
out += sizeof(outle32);
@@ -282,6 +715,7 @@ static ssize_t query_data(char *buf, size_t buf_len,
blocks++;
}
}
+ aa_put_label(label);
outle32 = __cpu_to_le32(out - buf - sizeof(bytes));
memcpy(buf, &outle32, sizeof(outle32));
@@ -291,6 +725,182 @@ static ssize_t query_data(char *buf, size_t buf_len,
return out - buf;
}
+/**
+ * query_label - queries a label and writes permissions to buf
+ * @buf: the resulting permissions string is stored here (NOT NULL)
+ * @buf_len: size of buf
+ * @query: binary query string to match against the dfa
+ * @query_len: size of query
+ * @view_only: only compute for querier's view
+ *
+ * The buffers pointed to by buf and query may overlap. The query buffer is
+ * parsed before buf is written to.
+ *
+ * The query should look like "LABEL_NAME\0DFA_STRING" where LABEL_NAME is
+ * the name of the label, in the current namespace, that is to be queried and
+ * DFA_STRING is a binary string to match against the label(s)'s DFA.
+ *
+ * LABEL_NAME must be NUL terminated. DFA_STRING may contain NUL characters
+ * but must *not* be NUL terminated.
+ *
+ * Returns: number of characters written to buf or -errno on failure
+ */
+static ssize_t query_label(char *buf, size_t buf_len,
+ char *query, size_t query_len, bool view_only)
+{
+ struct aa_profile *profile;
+ struct aa_label *label, *curr;
+ char *label_name, *match_str;
+ size_t label_name_len, match_len;
+ struct aa_perms perms;
+ struct label_it i;
+
+ if (!query_len)
+ return -EINVAL;
+
+ label_name = query;
+ label_name_len = strnlen(query, query_len);
+ if (!label_name_len || label_name_len == query_len)
+ return -EINVAL;
+
+ /**
+ * The extra byte is to account for the null byte between the
+ * profile name and dfa string. profile_name_len is greater
+ * than zero and less than query_len, so a byte can be safely
+ * added or subtracted.
+ */
+ match_str = label_name + label_name_len + 1;
+ match_len = query_len - label_name_len - 1;
+
+ curr = begin_current_label_crit_section();
+ label = aa_label_parse(curr, label_name, GFP_KERNEL, false, false);
+ end_current_label_crit_section(curr);
+ if (IS_ERR(label))
+ return PTR_ERR(label);
+
+ perms = allperms;
+ if (view_only) {
+ label_for_each_in_ns(i, labels_ns(label), label, profile) {
+ profile_query_cb(profile, &perms, match_str, match_len);
+ }
+ } else {
+ label_for_each(i, label, profile) {
+ profile_query_cb(profile, &perms, match_str, match_len);
+ }
+ }
+ aa_put_label(label);
+
+ return scnprintf(buf, buf_len,
+ "allow 0x%08x\ndeny 0x%08x\naudit 0x%08x\nquiet 0x%08x\n",
+ perms.allow, perms.deny, perms.audit, perms.quiet);
+}
+
+/*
+ * Transaction based IO.
+ * The file expects a write which triggers the transaction, and then
+ * possibly a read(s) which collects the result - which is stored in a
+ * file-local buffer. Once a new write is performed, a new set of results
+ * are stored in the file-local buffer.
+ */
+struct multi_transaction {
+ struct kref count;
+ ssize_t size;
+ char data[0];
+};
+
+#define MULTI_TRANSACTION_LIMIT (PAGE_SIZE - sizeof(struct multi_transaction))
+/* TODO: replace with per file lock */
+static DEFINE_SPINLOCK(multi_transaction_lock);
+
+static void multi_transaction_kref(struct kref *kref)
+{
+ struct multi_transaction *t;
+
+ t = container_of(kref, struct multi_transaction, count);
+ free_page((unsigned long) t);
+}
+
+static struct multi_transaction *
+get_multi_transaction(struct multi_transaction *t)
+{
+ if (t)
+ kref_get(&(t->count));
+
+ return t;
+}
+
+static void put_multi_transaction(struct multi_transaction *t)
+{
+ if (t)
+ kref_put(&(t->count), multi_transaction_kref);
+}
+
+/* does not increment @new's count */
+static void multi_transaction_set(struct file *file,
+ struct multi_transaction *new, size_t n)
+{
+ struct multi_transaction *old;
+
+ AA_BUG(n > MULTI_TRANSACTION_LIMIT);
+
+ new->size = n;
+ spin_lock(&multi_transaction_lock);
+ old = (struct multi_transaction *) file->private_data;
+ file->private_data = new;
+ spin_unlock(&multi_transaction_lock);
+ put_multi_transaction(old);
+}
+
+static struct multi_transaction *multi_transaction_new(struct file *file,
+ const char __user *buf,
+ size_t size)
+{
+ struct multi_transaction *t;
+
+ if (size > MULTI_TRANSACTION_LIMIT - 1)
+ return ERR_PTR(-EFBIG);
+
+ t = (struct multi_transaction *)get_zeroed_page(GFP_KERNEL);
+ if (!t)
+ return ERR_PTR(-ENOMEM);
+ kref_init(&t->count);
+ if (copy_from_user(t->data, buf, size))
+ return ERR_PTR(-EFAULT);
+
+ return t;
+}
+
+static ssize_t multi_transaction_read(struct file *file, char __user *buf,
+ size_t size, loff_t *pos)
+{
+ struct multi_transaction *t;
+ ssize_t ret;
+
+ spin_lock(&multi_transaction_lock);
+ t = get_multi_transaction(file->private_data);
+ spin_unlock(&multi_transaction_lock);
+ if (!t)
+ return 0;
+
+ ret = simple_read_from_buffer(buf, size, pos, t->data, t->size);
+ put_multi_transaction(t);
+
+ return ret;
+}
+
+static int multi_transaction_release(struct inode *inode, struct file *file)
+{
+ put_multi_transaction(file->private_data);
+
+ return 0;
+}
+
+#define QUERY_CMD_LABEL "label\0"
+#define QUERY_CMD_LABEL_LEN 6
+#define QUERY_CMD_PROFILE "profile\0"
+#define QUERY_CMD_PROFILE_LEN 8
+#define QUERY_CMD_LABELALL "labelall\0"
+#define QUERY_CMD_LABELALL_LEN 9
#define QUERY_CMD_DATA "data\0"
#define QUERY_CMD_DATA_LEN 5
@@ -318,54 +928,72 @@ static ssize_t query_data(char *buf, size_t buf_len,
static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
- char *buf;
+ struct multi_transaction *t;
ssize_t len;
if (*ppos)
return -ESPIPE;
- buf = simple_transaction_get(file, ubuf, count);
- if (IS_ERR(buf))
- return PTR_ERR(buf);
-
- if (count > QUERY_CMD_DATA_LEN &&
- !memcmp(buf, QUERY_CMD_DATA, QUERY_CMD_DATA_LEN)) {
- len = query_data(buf, SIMPLE_TRANSACTION_LIMIT,
- buf + QUERY_CMD_DATA_LEN,
+ t = multi_transaction_new(file, ubuf, count);
+ if (IS_ERR(t))
+ return PTR_ERR(t);
+
+ if (count > QUERY_CMD_PROFILE_LEN &&
+ !memcmp(t->data, QUERY_CMD_PROFILE, QUERY_CMD_PROFILE_LEN)) {
+ len = query_label(t->data, MULTI_TRANSACTION_LIMIT,
+ t->data + QUERY_CMD_PROFILE_LEN,
+ count - QUERY_CMD_PROFILE_LEN, true);
+ } else if (count > QUERY_CMD_LABEL_LEN &&
+ !memcmp(t->data, QUERY_CMD_LABEL, QUERY_CMD_LABEL_LEN)) {
+ len = query_label(t->data, MULTI_TRANSACTION_LIMIT,
+ t->data + QUERY_CMD_LABEL_LEN,
+ count - QUERY_CMD_LABEL_LEN, true);
+ } else if (count > QUERY_CMD_LABELALL_LEN &&
+ !memcmp(t->data, QUERY_CMD_LABELALL,
+ QUERY_CMD_LABELALL_LEN)) {
+ len = query_label(t->data, MULTI_TRANSACTION_LIMIT,
+ t->data + QUERY_CMD_LABELALL_LEN,
+ count - QUERY_CMD_LABELALL_LEN, false);
+ } else if (count > QUERY_CMD_DATA_LEN &&
+ !memcmp(t->data, QUERY_CMD_DATA, QUERY_CMD_DATA_LEN)) {
+ len = query_data(t->data, MULTI_TRANSACTION_LIMIT,
+ t->data + QUERY_CMD_DATA_LEN,
count - QUERY_CMD_DATA_LEN);
} else
len = -EINVAL;
- if (len < 0)
+ if (len < 0) {
+ put_multi_transaction(t);
return len;
+ }
- simple_transaction_set(file, len);
+ multi_transaction_set(file, t, len);
return count;
}
-static const struct file_operations aa_fs_access = {
+static const struct file_operations aa_sfs_access = {
.write = aa_write_access,
- .read = simple_transaction_read,
- .release = simple_transaction_release,
+ .read = multi_transaction_read,
+ .release = multi_transaction_release,
.llseek = generic_file_llseek,
};
-static int aa_fs_seq_show(struct seq_file *seq, void *v)
+static int aa_sfs_seq_show(struct seq_file *seq, void *v)
{
- struct aa_fs_entry *fs_file = seq->private;
+ struct aa_sfs_entry *fs_file = seq->private;
if (!fs_file)
return 0;
switch (fs_file->v_type) {
- case AA_FS_TYPE_BOOLEAN:
+ case AA_SFS_TYPE_BOOLEAN:
seq_printf(seq, "%s\n", fs_file->v.boolean ? "yes" : "no");
break;
- case AA_FS_TYPE_STRING:
+ case AA_SFS_TYPE_STRING:
seq_printf(seq, "%s\n", fs_file->v.string);
break;
- case AA_FS_TYPE_U64:
+ case AA_SFS_TYPE_U64:
seq_printf(seq, "%#08lx\n", fs_file->v.u64);
break;
default:
@@ -376,21 +1004,40 @@ static int aa_fs_seq_show(struct seq_file *seq, void *v)
return 0;
}
-static int aa_fs_seq_open(struct inode *inode, struct file *file)
+static int aa_sfs_seq_open(struct inode *inode, struct file *file)
{
- return single_open(file, aa_fs_seq_show, inode->i_private);
+ return single_open(file, aa_sfs_seq_show, inode->i_private);
}
-const struct file_operations aa_fs_seq_file_ops = {
+const struct file_operations aa_sfs_seq_file_ops = {
.owner = THIS_MODULE,
- .open = aa_fs_seq_open,
+ .open = aa_sfs_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
-static int aa_fs_seq_profile_open(struct inode *inode, struct file *file,
- int (*show)(struct seq_file *, void *))
+/*
+ * profile based file operations
+ * policy/profiles/XXXX/profiles/ *
+ */
+
+#define SEQ_PROFILE_FOPS(NAME) \
+static int seq_profile_ ##NAME ##_open(struct inode *inode, struct file *file)\
+{ \
+ return seq_profile_open(inode, file, seq_profile_ ##NAME ##_show); \
+} \
+ \
+static const struct file_operations seq_profile_ ##NAME ##_fops = { \
+ .owner = THIS_MODULE, \
+ .open = seq_profile_ ##NAME ##_open, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .release = seq_profile_release, \
+} \
+
+static int seq_profile_open(struct inode *inode, struct file *file,
+ int (*show)(struct seq_file *, void *))
{
struct aa_proxy *proxy = aa_get_proxy(inode->i_private);
int error = single_open(file, show, proxy);
@@ -403,7 +1050,7 @@ static int aa_fs_seq_profile_open(struct inode *inode, struct file *file,
return error;
}
-static int aa_fs_seq_profile_release(struct inode *inode, struct file *file)
+static int seq_profile_release(struct inode *inode, struct file *file)
{
struct seq_file *seq = (struct seq_file *) file->private_data;
if (seq)
@@ -411,217 +1058,229 @@ static int aa_fs_seq_profile_release(struct inode *inode, struct file *file)
return single_release(inode, file);
}
-static int aa_fs_seq_profname_show(struct seq_file *seq, void *v)
+static int seq_profile_name_show(struct seq_file *seq, void *v)
{
struct aa_proxy *proxy = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
+ struct aa_label *label = aa_get_label_rcu(&proxy->label);
+ struct aa_profile *profile = labels_profile(label);
seq_printf(seq, "%s\n", profile->base.name);
- aa_put_profile(profile);
+ aa_put_label(label);
return 0;
}
-static int aa_fs_seq_profname_open(struct inode *inode, struct file *file)
-{
- return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profname_show);
-}
-
-static const struct file_operations aa_fs_profname_fops = {
- .owner = THIS_MODULE,
- .open = aa_fs_seq_profname_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = aa_fs_seq_profile_release,
-};
-
-static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v)
+static int seq_profile_mode_show(struct seq_file *seq, void *v)
{
struct aa_proxy *proxy = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
+ struct aa_label *label = aa_get_label_rcu(&proxy->label);
+ struct aa_profile *profile = labels_profile(label);
seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]);
- aa_put_profile(profile);
+ aa_put_label(label);
return 0;
}
-static int aa_fs_seq_profmode_open(struct inode *inode, struct file *file)
-{
- return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profmode_show);
-}
-
-static const struct file_operations aa_fs_profmode_fops = {
- .owner = THIS_MODULE,
- .open = aa_fs_seq_profmode_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = aa_fs_seq_profile_release,
-};
-
-static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v)
+static int seq_profile_attach_show(struct seq_file *seq, void *v)
{
struct aa_proxy *proxy = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
+ struct aa_label *label = aa_get_label_rcu(&proxy->label);
+ struct aa_profile *profile = labels_profile(label);
if (profile->attach)
seq_printf(seq, "%s\n", profile->attach);
else if (profile->xmatch)
seq_puts(seq, "<unknown>\n");
else
seq_printf(seq, "%s\n", profile->base.name);
- aa_put_profile(profile);
+ aa_put_label(label);
return 0;
}
-static int aa_fs_seq_profattach_open(struct inode *inode, struct file *file)
-{
- return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profattach_show);
-}
-
-static const struct file_operations aa_fs_profattach_fops = {
- .owner = THIS_MODULE,
- .open = aa_fs_seq_profattach_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = aa_fs_seq_profile_release,
-};
-
-static int aa_fs_seq_hash_show(struct seq_file *seq, void *v)
+static int seq_profile_hash_show(struct seq_file *seq, void *v)
{
struct aa_proxy *proxy = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
+ struct aa_label *label = aa_get_label_rcu(&proxy->label);
+ struct aa_profile *profile = labels_profile(label);
unsigned int i, size = aa_hash_size();
if (profile->hash) {
for (i = 0; i < size; i++)
seq_printf(seq, "%.2x", profile->hash[i]);
- seq_puts(seq, "\n");
+ seq_putc(seq, '\n');
}
- aa_put_profile(profile);
+ aa_put_label(label);
return 0;
}
-static int aa_fs_seq_hash_open(struct inode *inode, struct file *file)
+SEQ_PROFILE_FOPS(name);
+SEQ_PROFILE_FOPS(mode);
+SEQ_PROFILE_FOPS(attach);
+SEQ_PROFILE_FOPS(hash);
+
+/*
+ * namespace based files
+ * several root files and
+ * policy/ *
+ */
+
+#define SEQ_NS_FOPS(NAME) \
+static int seq_ns_ ##NAME ##_open(struct inode *inode, struct file *file) \
+{ \
+ return single_open(file, seq_ns_ ##NAME ##_show, inode->i_private); \
+} \
+ \
+static const struct file_operations seq_ns_ ##NAME ##_fops = { \
+ .owner = THIS_MODULE, \
+ .open = seq_ns_ ##NAME ##_open, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .release = single_release, \
+} \
+
+static int seq_ns_stacked_show(struct seq_file *seq, void *v)
{
- return single_open(file, aa_fs_seq_hash_show, inode->i_private);
-}
+ struct aa_label *label;
-static const struct file_operations aa_fs_seq_hash_fops = {
- .owner = THIS_MODULE,
- .open = aa_fs_seq_hash_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+ label = begin_current_label_crit_section();
+ seq_printf(seq, "%s\n", label->size > 1 ? "yes" : "no");
+ end_current_label_crit_section(label);
+ return 0;
+}
-static int aa_fs_seq_show_ns_level(struct seq_file *seq, void *v)
+static int seq_ns_nsstacked_show(struct seq_file *seq, void *v)
{
- struct aa_ns *ns = aa_current_profile()->ns;
+ struct aa_label *label;
+ struct aa_profile *profile;
+ struct label_it it;
+ int count = 1;
- seq_printf(seq, "%d\n", ns->level);
+ label = begin_current_label_crit_section();
+
+ if (label->size > 1) {
+ label_for_each(it, label, profile)
+ if (profile->ns != labels_ns(label)) {
+ count++;
+ break;
+ }
+ }
+
+ seq_printf(seq, "%s\n", count > 1 ? "yes" : "no");
+ end_current_label_crit_section(label);
return 0;
}
-static int aa_fs_seq_open_ns_level(struct inode *inode, struct file *file)
+static int seq_ns_level_show(struct seq_file *seq, void *v)
{
- return single_open(file, aa_fs_seq_show_ns_level, inode->i_private);
-}
+ struct aa_label *label;
-static const struct file_operations aa_fs_ns_level = {
- .owner = THIS_MODULE,
- .open = aa_fs_seq_open_ns_level,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+ label = begin_current_label_crit_section();
+ seq_printf(seq, "%d\n", labels_ns(label)->level);
+ end_current_label_crit_section(label);
+
+ return 0;
+}
-static int aa_fs_seq_show_ns_name(struct seq_file *seq, void *v)
+static int seq_ns_name_show(struct seq_file *seq, void *v)
{
- struct aa_ns *ns = aa_current_profile()->ns;
+ struct aa_label *label = begin_current_label_crit_section();
- seq_printf(seq, "%s\n", ns->base.name);
+ seq_printf(seq, "%s\n", aa_ns_name(labels_ns(label),
+ labels_ns(label), true));
+ end_current_label_crit_section(label);
return 0;
}
-static int aa_fs_seq_open_ns_name(struct inode *inode, struct file *file)
+SEQ_NS_FOPS(stacked);
+SEQ_NS_FOPS(nsstacked);
+SEQ_NS_FOPS(level);
+SEQ_NS_FOPS(name);
+
+
+/* policy/raw_data/ * file ops */
+
+#define SEQ_RAWDATA_FOPS(NAME) \
+static int seq_rawdata_ ##NAME ##_open(struct inode *inode, struct file *file)\
+{ \
+ return seq_rawdata_open(inode, file, seq_rawdata_ ##NAME ##_show); \
+} \
+ \
+static const struct file_operations seq_rawdata_ ##NAME ##_fops = { \
+ .owner = THIS_MODULE, \
+ .open = seq_rawdata_ ##NAME ##_open, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .release = seq_rawdata_release, \
+} \
+
+static int seq_rawdata_open(struct inode *inode, struct file *file,
+ int (*show)(struct seq_file *, void *))
{
- return single_open(file, aa_fs_seq_show_ns_name, inode->i_private);
-}
+ struct aa_loaddata *data = __aa_get_loaddata(inode->i_private);
+ int error;
-static const struct file_operations aa_fs_ns_name = {
- .owner = THIS_MODULE,
- .open = aa_fs_seq_open_ns_name,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+ if (!data)
+ /* lost race this ent is being reaped */
+ return -ENOENT;
-static int rawdata_release(struct inode *inode, struct file *file)
+ error = single_open(file, show, data);
+ if (error) {
+ AA_BUG(file->private_data &&
+ ((struct seq_file *)file->private_data)->private);
+ aa_put_loaddata(data);
+ }
+
+ return error;
+}
+
+static int seq_rawdata_release(struct inode *inode, struct file *file)
{
- /* TODO: switch to loaddata when profile switched to symlink */
- aa_put_loaddata(file->private_data);
+ struct seq_file *seq = (struct seq_file *) file->private_data;
- return 0;
+ if (seq)
+ aa_put_loaddata(seq->private);
+
+ return single_release(inode, file);
}
-static int aa_fs_seq_raw_abi_show(struct seq_file *seq, void *v)
+static int seq_rawdata_abi_show(struct seq_file *seq, void *v)
{
- struct aa_proxy *proxy = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
+ struct aa_loaddata *data = seq->private;
- if (profile->rawdata->abi) {
- seq_printf(seq, "v%d", profile->rawdata->abi);
- seq_puts(seq, "\n");
- }
- aa_put_profile(profile);
+ seq_printf(seq, "v%d\n", data->abi);
return 0;
}
-static int aa_fs_seq_raw_abi_open(struct inode *inode, struct file *file)
+static int seq_rawdata_revision_show(struct seq_file *seq, void *v)
{
- return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_abi_show);
-}
+ struct aa_loaddata *data = seq->private;
-static const struct file_operations aa_fs_seq_raw_abi_fops = {
- .owner = THIS_MODULE,
- .open = aa_fs_seq_raw_abi_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = aa_fs_seq_profile_release,
-};
+ seq_printf(seq, "%ld\n", data->revision);
+
+ return 0;
+}
-static int aa_fs_seq_raw_hash_show(struct seq_file *seq, void *v)
+static int seq_rawdata_hash_show(struct seq_file *seq, void *v)
{
- struct aa_proxy *proxy = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
+ struct aa_loaddata *data = seq->private;
unsigned int i, size = aa_hash_size();
- if (profile->rawdata->hash) {
+ if (data->hash) {
for (i = 0; i < size; i++)
- seq_printf(seq, "%.2x", profile->rawdata->hash[i]);
- seq_puts(seq, "\n");
+ seq_printf(seq, "%.2x", data->hash[i]);
+ seq_putc(seq, '\n');
}
- aa_put_profile(profile);
return 0;
}
-static int aa_fs_seq_raw_hash_open(struct inode *inode, struct file *file)
-{
- return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_hash_show);
-}
-
-static const struct file_operations aa_fs_seq_raw_hash_fops = {
- .owner = THIS_MODULE,
- .open = aa_fs_seq_raw_hash_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = aa_fs_seq_profile_release,
-};
+SEQ_RAWDATA_FOPS(abi);
+SEQ_RAWDATA_FOPS(revision);
+SEQ_RAWDATA_FOPS(hash);
static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
loff_t *ppos)
@@ -632,29 +1291,127 @@ static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
rawdata->size);
}
-static int rawdata_open(struct inode *inode, struct file *file)
+static int rawdata_release(struct inode *inode, struct file *file)
{
- struct aa_proxy *proxy = inode->i_private;
- struct aa_profile *profile;
+ aa_put_loaddata(file->private_data);
+ return 0;
+}
+
+static int rawdata_open(struct inode *inode, struct file *file)
+{
if (!policy_view_capable(NULL))
return -EACCES;
- profile = aa_get_profile_rcu(&proxy->profile);
- file->private_data = aa_get_loaddata(profile->rawdata);
- aa_put_profile(profile);
+ file->private_data = __aa_get_loaddata(inode->i_private);
+ if (!file->private_data)
+ /* lost race: this entry is being reaped */
+ return -ENOENT;
return 0;
}
-static const struct file_operations aa_fs_rawdata_fops = {
+static const struct file_operations rawdata_fops = {
.open = rawdata_open,
.read = rawdata_read,
.llseek = generic_file_llseek,
.release = rawdata_release,
};
+static void remove_rawdata_dents(struct aa_loaddata *rawdata)
+{
+ int i;
+
+ for (i = 0; i < AAFS_LOADDATA_NDENTS; i++) {
+ if (!IS_ERR_OR_NULL(rawdata->dents[i])) {
+ /* no refcounts on i_private */
+ aafs_remove(rawdata->dents[i]);
+ rawdata->dents[i] = NULL;
+ }
+ }
+}
+
+void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata)
+{
+ AA_BUG(rawdata->ns && !mutex_is_locked(&rawdata->ns->lock));
+
+ if (rawdata->ns) {
+ remove_rawdata_dents(rawdata);
+ list_del_init(&rawdata->list);
+ aa_put_ns(rawdata->ns);
+ rawdata->ns = NULL;
+ }
+}
+
+int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata)
+{
+ struct dentry *dent, *dir;
+
+ AA_BUG(!ns);
+ AA_BUG(!rawdata);
+ AA_BUG(!mutex_is_locked(&ns->lock));
+ AA_BUG(!ns_subdata_dir(ns));
+
+ /*
+ * just use ns revision dir was originally created at. This is
+ * under ns->lock and if load is successful revision will be
+ * bumped and is guaranteed to be unique
+ */
+ rawdata->name = kasprintf(GFP_KERNEL, "%ld", ns->revision);
+ if (!rawdata->name)
+ return -ENOMEM;
+
+ dir = aafs_create_dir(rawdata->name, ns_subdata_dir(ns));
+ if (IS_ERR(dir))
+ /* ->name freed when rawdata freed */
+ return PTR_ERR(dir);
+ rawdata->dents[AAFS_LOADDATA_DIR] = dir;
+
+ dent = aafs_create_file("abi", S_IFREG | 0444, dir, rawdata,
+ &seq_rawdata_abi_fops);
+ if (IS_ERR(dent))
+ goto fail;
+ rawdata->dents[AAFS_LOADDATA_ABI] = dent;
+
+ dent = aafs_create_file("revision", S_IFREG | 0444, dir, rawdata,
+ &seq_rawdata_revision_fops);
+ if (IS_ERR(dent))
+ goto fail;
+ rawdata->dents[AAFS_LOADDATA_REVISION] = dent;
+
+ if (aa_g_hash_policy) {
+ dent = aafs_create_file("sha1", S_IFREG | 0444, dir,
+ rawdata, &seq_rawdata_hash_fops);
+ if (IS_ERR(dent))
+ goto fail;
+ rawdata->dents[AAFS_LOADDATA_HASH] = dent;
+ }
+
+ dent = aafs_create_file("raw_data", S_IFREG | 0444,
+ dir, rawdata, &rawdata_fops);
+ if (IS_ERR(dent))
+ goto fail;
+ rawdata->dents[AAFS_LOADDATA_DATA] = dent;
+ d_inode(dent)->i_size = rawdata->size;
+
+ rawdata->ns = aa_get_ns(ns);
+ list_add(&rawdata->list, &ns->rawdata_list);
+ /* no refcount on inode rawdata */
+
+ return 0;
+
+fail:
+ remove_rawdata_dents(rawdata);
+
+ return PTR_ERR(dent);
+}
+
/** fns to setup dynamic per profile/namespace files **/
-void __aa_fs_profile_rmdir(struct aa_profile *profile)
+
+/**
+ *
+ * Requires: @profile->ns->lock held
+ */
+void __aafs_profile_rmdir(struct aa_profile *profile)
{
struct aa_profile *child;
int i;
@@ -663,7 +1420,7 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile)
return;
list_for_each_entry(child, &profile->base.profiles, base.list)
- __aa_fs_profile_rmdir(child);
+ __aafs_profile_rmdir(child);
for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) {
struct aa_proxy *proxy;
@@ -671,14 +1428,18 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile)
continue;
proxy = d_inode(profile->dents[i])->i_private;
- securityfs_remove(profile->dents[i]);
+ aafs_remove(profile->dents[i]);
aa_put_proxy(proxy);
profile->dents[i] = NULL;
}
}
-void __aa_fs_profile_migrate_dents(struct aa_profile *old,
- struct aa_profile *new)
+/**
+ *
+ * Requires: @old->ns->lock held
+ */
+void __aafs_profile_migrate_dents(struct aa_profile *old,
+ struct aa_profile *new)
{
int i;
@@ -694,18 +1455,52 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name,
struct aa_profile *profile,
const struct file_operations *fops)
{
- struct aa_proxy *proxy = aa_get_proxy(profile->proxy);
+ struct aa_proxy *proxy = aa_get_proxy(profile->label.proxy);
struct dentry *dent;
- dent = securityfs_create_file(name, S_IFREG | 0444, dir, proxy, fops);
+ dent = aafs_create_file(name, S_IFREG | 0444, dir, proxy, fops);
if (IS_ERR(dent))
aa_put_proxy(proxy);
return dent;
}
-/* requires lock be held */
-int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
+static int profile_depth(struct aa_profile *profile)
+{
+ int depth = 0;
+
+ rcu_read_lock();
+ for (depth = 0; profile; profile = rcu_access_pointer(profile->parent))
+ depth++;
+ rcu_read_unlock();
+
+ return depth;
+}
+
+static int gen_symlink_name(char *buffer, size_t bsize, int depth,
+ const char *dirname, const char *fname)
+{
+ int error;
+
+ for (; depth > 0; depth--) {
+ if (bsize < 7)
+ return -ENAMETOOLONG;
+ strcpy(buffer, "../../");
+ buffer += 6;
+ bsize -= 6;
+ }
+
+ error = snprintf(buffer, bsize, "raw_data/%s/%s", dirname, fname);
+ if (error >= bsize || error < 0)
+ return -ENAMETOOLONG;
+
+ return 0;
+}
+
+/*
+ * Requires: @profile->ns->lock held
+ */
+int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
{
struct aa_profile *child;
struct dentry *dent = NULL, *dir;
@@ -716,7 +1511,7 @@ int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
p = aa_deref_parent(profile);
dent = prof_dir(p);
/* adding to parent that previously didn't have children */
- dent = securityfs_create_dir("profiles", dent);
+ dent = aafs_create_dir("profiles", dent);
if (IS_ERR(dent))
goto fail;
prof_child_dir(p) = parent = dent;
@@ -728,67 +1523,80 @@ int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
id_len = snprintf(NULL, 0, ".%ld", profile->ns->uniq_id);
profile->dirname = kmalloc(len + id_len + 1, GFP_KERNEL);
- if (!profile->dirname)
- goto fail;
+ if (!profile->dirname) {
+ error = -ENOMEM;
+ goto fail2;
+ }
mangle_name(profile->base.name, profile->dirname);
sprintf(profile->dirname + len, ".%ld", profile->ns->uniq_id++);
}
- dent = securityfs_create_dir(profile->dirname, parent);
+ dent = aafs_create_dir(profile->dirname, parent);
if (IS_ERR(dent))
goto fail;
prof_dir(profile) = dir = dent;
- dent = create_profile_file(dir, "name", profile, &aa_fs_profname_fops);
+ dent = create_profile_file(dir, "name", profile,
+ &seq_profile_name_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_NAME] = dent;
- dent = create_profile_file(dir, "mode", profile, &aa_fs_profmode_fops);
+ dent = create_profile_file(dir, "mode", profile,
+ &seq_profile_mode_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_MODE] = dent;
dent = create_profile_file(dir, "attach", profile,
- &aa_fs_profattach_fops);
+ &seq_profile_attach_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_ATTACH] = dent;
if (profile->hash) {
dent = create_profile_file(dir, "sha1", profile,
- &aa_fs_seq_hash_fops);
+ &seq_profile_hash_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_HASH] = dent;
}
if (profile->rawdata) {
- dent = create_profile_file(dir, "raw_sha1", profile,
- &aa_fs_seq_raw_hash_fops);
+ char target[64];
+ int depth = profile_depth(profile);
+
+ error = gen_symlink_name(target, sizeof(target), depth,
+ profile->rawdata->name, "sha1");
+ if (error < 0)
+ goto fail2;
+ dent = aafs_create_symlink("raw_sha1", dir, target, NULL);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_RAW_HASH] = dent;
- dent = create_profile_file(dir, "raw_abi", profile,
- &aa_fs_seq_raw_abi_fops);
+ error = gen_symlink_name(target, sizeof(target), depth,
+ profile->rawdata->name, "abi");
+ if (error < 0)
+ goto fail2;
+ dent = aafs_create_symlink("raw_abi", dir, target, NULL);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_RAW_ABI] = dent;
- dent = securityfs_create_file("raw_data", S_IFREG | 0444, dir,
- profile->proxy,
- &aa_fs_rawdata_fops);
+ error = gen_symlink_name(target, sizeof(target), depth,
+ profile->rawdata->name, "raw_data");
+ if (error < 0)
+ goto fail2;
+ dent = aafs_create_symlink("raw_data", dir, target, NULL);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_RAW_DATA] = dent;
- d_inode(dent)->i_size = profile->rawdata->size;
- aa_get_proxy(profile->proxy);
}
list_for_each_entry(child, &profile->base.profiles, base.list) {
- error = __aa_fs_profile_mkdir(child, prof_child_dir(profile));
+ error = __aafs_profile_mkdir(child, prof_child_dir(profile));
if (error)
goto fail2;
}
@@ -799,12 +1607,123 @@ fail:
error = PTR_ERR(dent);
fail2:
- __aa_fs_profile_rmdir(profile);
+ __aafs_profile_rmdir(profile);
return error;
}
-void __aa_fs_ns_rmdir(struct aa_ns *ns)
+static int ns_mkdir_op(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ struct aa_ns *ns, *parent;
+ /* TODO: improve permission check */
+ struct aa_label *label;
+ int error;
+
+ label = begin_current_label_crit_section();
+ error = aa_may_manage_policy(label, NULL, AA_MAY_LOAD_POLICY);
+ end_current_label_crit_section(label);
+ if (error)
+ return error;
+
+ parent = aa_get_ns(dir->i_private);
+ AA_BUG(d_inode(ns_subns_dir(parent)) != dir);
+
+ /* we have to unlock and then relock to get locking order right
+ * for pin_fs
+ */
+ inode_unlock(dir);
+ error = simple_pin_fs(&aafs_ops, &aafs_mnt, &aafs_count);
+ mutex_lock(&parent->lock);
+ inode_lock_nested(dir, I_MUTEX_PARENT);
+ if (error)
+ goto out;
+
+ error = __aafs_setup_d_inode(dir, dentry, mode | S_IFDIR, NULL,
+ NULL, NULL, NULL);
+ if (error)
+ goto out_pin;
+
+ ns = __aa_find_or_create_ns(parent, READ_ONCE(dentry->d_name.name),
+ dentry);
+ if (IS_ERR(ns)) {
+ error = PTR_ERR(ns);
+ ns = NULL;
+ }
+
+ aa_put_ns(ns); /* list ref remains */
+out_pin:
+ if (error)
+ simple_release_fs(&aafs_mnt, &aafs_count);
+out:
+ mutex_unlock(&parent->lock);
+ aa_put_ns(parent);
+
+ return error;
+}
+
+static int ns_rmdir_op(struct inode *dir, struct dentry *dentry)
+{
+ struct aa_ns *ns, *parent;
+ /* TODO: improve permission check */
+ struct aa_label *label;
+ int error;
+
+ label = begin_current_label_crit_section();
+ error = aa_may_manage_policy(label, NULL, AA_MAY_LOAD_POLICY);
+ end_current_label_crit_section(label);
+ if (error)
+ return error;
+
+ parent = aa_get_ns(dir->i_private);
+ /* rmdir calls the generic securityfs functions to remove files
+ * from the apparmor dir. It is up to the apparmor ns locking
+ * to avoid races.
+ */
+ inode_unlock(dir);
+ inode_unlock(dentry->d_inode);
+
+ mutex_lock(&parent->lock);
+ ns = aa_get_ns(__aa_findn_ns(&parent->sub_ns, dentry->d_name.name,
+ dentry->d_name.len));
+ if (!ns) {
+ error = -ENOENT;
+ goto out;
+ }
+ AA_BUG(ns_dir(ns) != dentry);
+
+ __aa_remove_ns(ns);
+ aa_put_ns(ns);
+
+out:
+ mutex_unlock(&parent->lock);
+ inode_lock_nested(dir, I_MUTEX_PARENT);
+ inode_lock(dentry->d_inode);
+ aa_put_ns(parent);
+
+ return error;
+}
+
+static const struct inode_operations ns_dir_inode_operations = {
+ .lookup = simple_lookup,
+ .mkdir = ns_mkdir_op,
+ .rmdir = ns_rmdir_op,
+};
+
+static void __aa_fs_list_remove_rawdata(struct aa_ns *ns)
+{
+ struct aa_loaddata *ent, *tmp;
+
+ AA_BUG(!mutex_is_locked(&ns->lock));
+
+ list_for_each_entry_safe(ent, tmp, &ns->rawdata_list, list)
+ __aa_fs_remove_rawdata(ent);
+}
+
+/**
+ *
+ * Requires: @ns->lock held
+ */
+void __aafs_ns_rmdir(struct aa_ns *ns)
{
struct aa_ns *sub;
struct aa_profile *child;
@@ -814,14 +1733,16 @@ void __aa_fs_ns_rmdir(struct aa_ns *ns)
return;
list_for_each_entry(child, &ns->base.profiles, base.list)
- __aa_fs_profile_rmdir(child);
+ __aafs_profile_rmdir(child);
list_for_each_entry(sub, &ns->sub_ns, base.list) {
mutex_lock(&sub->lock);
- __aa_fs_ns_rmdir(sub);
+ __aafs_ns_rmdir(sub);
mutex_unlock(&sub->lock);
}
+ __aa_fs_list_remove_rawdata(ns);
+
if (ns_subns_dir(ns)) {
sub = d_inode(ns_subns_dir(ns))->i_private;
aa_put_ns(sub);
@@ -838,53 +1759,66 @@ void __aa_fs_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) {
- securityfs_remove(ns->dents[i]);
+ aafs_remove(ns->dents[i]);
ns->dents[i] = NULL;
}
}
/* assumes cleanup in caller */
-static int __aa_fs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
+static int __aafs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
{
struct dentry *dent;
AA_BUG(!ns);
AA_BUG(!dir);
- dent = securityfs_create_dir("profiles", dir);
+ dent = aafs_create_dir("profiles", dir);
if (IS_ERR(dent))
return PTR_ERR(dent);
ns_subprofs_dir(ns) = dent;
- dent = securityfs_create_dir("raw_data", dir);
+ dent = aafs_create_dir("raw_data", dir);
if (IS_ERR(dent))
return PTR_ERR(dent);
ns_subdata_dir(ns) = dent;
- dent = securityfs_create_file(".load", 0640, dir, ns,
+ 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))
return PTR_ERR(dent);
aa_get_ns(ns);
ns_subload(ns) = dent;
- dent = securityfs_create_file(".replace", 0640, dir, ns,
+ dent = aafs_create_file(".replace", 0640, dir, ns,
&aa_fs_profile_replace);
if (IS_ERR(dent))
return PTR_ERR(dent);
aa_get_ns(ns);
ns_subreplace(ns) = dent;
- dent = securityfs_create_file(".remove", 0640, dir, ns,
+ dent = aafs_create_file(".remove", 0640, dir, ns,
&aa_fs_profile_remove);
if (IS_ERR(dent))
return PTR_ERR(dent);
aa_get_ns(ns);
ns_subremove(ns) = dent;
- dent = securityfs_create_dir("namespaces", dir);
+ /* use create_dentry so we can supply private data */
+ dent = aafs_create("namespaces", S_IFDIR | 0755, dir, ns, NULL, NULL,
+ &ns_dir_inode_operations);
if (IS_ERR(dent))
return PTR_ERR(dent);
aa_get_ns(ns);
@@ -893,11 +1827,15 @@ static int __aa_fs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
return 0;
}
-int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name)
+/*
+ * Requires: @ns->lock held
+ */
+int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
+ struct dentry *dent)
{
struct aa_ns *sub;
struct aa_profile *child;
- struct dentry *dent, *dir;
+ struct dentry *dir;
int error;
AA_BUG(!ns);
@@ -907,19 +1845,21 @@ int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name)
if (!name)
name = ns->base.name;
- /* create ns dir if it doesn't already exist */
- dent = securityfs_create_dir(name, parent);
- if (IS_ERR(dent))
- goto fail;
-
+ if (!dent) {
+ /* create ns dir if it doesn't already exist */
+ dent = aafs_create_dir(name, parent);
+ if (IS_ERR(dent))
+ goto fail;
+ } else
+ dget(dent);
ns_dir(ns) = dir = dent;
- error = __aa_fs_ns_mkdir_entries(ns, dir);
+ error = __aafs_ns_mkdir_entries(ns, dir);
if (error)
goto fail2;
/* profiles */
list_for_each_entry(child, &ns->base.profiles, base.list) {
- error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns));
+ error = __aafs_profile_mkdir(child, ns_subprofs_dir(ns));
if (error)
goto fail2;
}
@@ -927,7 +1867,7 @@ int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name)
/* subnamespaces */
list_for_each_entry(sub, &ns->sub_ns, base.list) {
mutex_lock(&sub->lock);
- error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL);
+ error = __aafs_ns_mkdir(sub, ns_subns_dir(ns), NULL, NULL);
mutex_unlock(&sub->lock);
if (error)
goto fail2;
@@ -939,7 +1879,7 @@ fail:
error = PTR_ERR(dent);
fail2:
- __aa_fs_ns_rmdir(ns);
+ __aafs_ns_rmdir(ns);
return error;
}
@@ -1074,10 +2014,9 @@ static struct aa_profile *next_profile(struct aa_ns *root,
static void *p_start(struct seq_file *f, loff_t *pos)
{
struct aa_profile *profile = NULL;
- struct aa_ns *root = aa_current_profile()->ns;
+ struct aa_ns *root = aa_get_current_ns();
loff_t l = *pos;
- f->private = aa_get_ns(root);
-
+ f->private = root;
/* find the first profile */
mutex_lock(&root->lock);
@@ -1141,15 +2080,14 @@ static int seq_show_profile(struct seq_file *f, void *p)
struct aa_profile *profile = (struct aa_profile *)p;
struct aa_ns *root = f->private;
- if (profile->ns != root)
- seq_printf(f, ":%s://", aa_ns_name(root, profile->ns, true));
- seq_printf(f, "%s (%s)\n", profile->base.hname,
- aa_profile_mode_names[profile->mode]);
+ aa_label_seq_xprint(f, root, &profile->label,
+ FLAG_SHOW_MODE | FLAG_VIEW_SUBNS, GFP_KERNEL);
+ seq_putc(f, '\n');
return 0;
}
-static const struct seq_operations aa_fs_profiles_op = {
+static const struct seq_operations aa_sfs_profiles_op = {
.start = p_start,
.next = p_next,
.stop = p_stop,
@@ -1161,7 +2099,7 @@ static int profiles_open(struct inode *inode, struct file *file)
if (!policy_view_capable(NULL))
return -EACCES;
- return seq_open(file, &aa_fs_profiles_op);
+ return seq_open(file, &aa_sfs_profiles_op);
}
static int profiles_release(struct inode *inode, struct file *file)
@@ -1169,7 +2107,7 @@ static int profiles_release(struct inode *inode, struct file *file)
return seq_release(inode, file);
}
-static const struct file_operations aa_fs_profiles_fops = {
+static const struct file_operations aa_sfs_profiles_fops = {
.open = profiles_open,
.read = seq_read,
.llseek = seq_lseek,
@@ -1178,64 +2116,94 @@ static const struct file_operations aa_fs_profiles_fops = {
/** Base file system setup **/
-static struct aa_fs_entry aa_fs_entry_file[] = {
- AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \
- "link lock"),
+static struct aa_sfs_entry aa_sfs_entry_file[] = {
+ AA_SFS_FILE_STRING("mask",
+ "create read write exec append mmap_exec link lock"),
+ { }
+};
+
+static struct aa_sfs_entry aa_sfs_entry_ptrace[] = {
+ AA_SFS_FILE_STRING("mask", "read trace"),
+ { }
+};
+
+static struct aa_sfs_entry aa_sfs_entry_domain[] = {
+ AA_SFS_FILE_BOOLEAN("change_hat", 1),
+ AA_SFS_FILE_BOOLEAN("change_hatv", 1),
+ AA_SFS_FILE_BOOLEAN("change_onexec", 1),
+ AA_SFS_FILE_BOOLEAN("change_profile", 1),
+ AA_SFS_FILE_BOOLEAN("stack", 1),
+ AA_SFS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1),
+ AA_SFS_FILE_STRING("version", "1.2"),
+ { }
+};
+
+static struct aa_sfs_entry aa_sfs_entry_versions[] = {
+ AA_SFS_FILE_BOOLEAN("v5", 1),
+ AA_SFS_FILE_BOOLEAN("v6", 1),
+ AA_SFS_FILE_BOOLEAN("v7", 1),
{ }
};
-static struct aa_fs_entry aa_fs_entry_domain[] = {
- AA_FS_FILE_BOOLEAN("change_hat", 1),
- AA_FS_FILE_BOOLEAN("change_hatv", 1),
- AA_FS_FILE_BOOLEAN("change_onexec", 1),
- AA_FS_FILE_BOOLEAN("change_profile", 1),
- AA_FS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1),
- AA_FS_FILE_STRING("version", "1.2"),
+static struct aa_sfs_entry aa_sfs_entry_policy[] = {
+ AA_SFS_DIR("versions", aa_sfs_entry_versions),
+ AA_SFS_FILE_BOOLEAN("set_load", 1),
{ }
};
-static struct aa_fs_entry aa_fs_entry_versions[] = {
- AA_FS_FILE_BOOLEAN("v5", 1),
+static struct aa_sfs_entry aa_sfs_entry_ns[] = {
+ AA_SFS_FILE_BOOLEAN("profile", 1),
+ AA_SFS_FILE_BOOLEAN("pivot_root", 1),
{ }
};
-static struct aa_fs_entry aa_fs_entry_policy[] = {
- AA_FS_DIR("versions", aa_fs_entry_versions),
- AA_FS_FILE_BOOLEAN("set_load", 1),
+static struct aa_sfs_entry aa_sfs_entry_query_label[] = {
+ AA_SFS_FILE_STRING("perms", "allow deny audit quiet"),
+ AA_SFS_FILE_BOOLEAN("data", 1),
+ AA_SFS_FILE_BOOLEAN("multi_transaction", 1),
{ }
};
-static struct aa_fs_entry aa_fs_entry_features[] = {
- AA_FS_DIR("policy", aa_fs_entry_policy),
- AA_FS_DIR("domain", aa_fs_entry_domain),
- AA_FS_DIR("file", aa_fs_entry_file),
- AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
- AA_FS_DIR("rlimit", aa_fs_entry_rlimit),
- AA_FS_DIR("caps", aa_fs_entry_caps),
+static struct aa_sfs_entry aa_sfs_entry_query[] = {
+ AA_SFS_DIR("label", aa_sfs_entry_query_label),
+ { }
+};
+static struct aa_sfs_entry aa_sfs_entry_features[] = {
+ AA_SFS_DIR("policy", aa_sfs_entry_policy),
+ AA_SFS_DIR("domain", aa_sfs_entry_domain),
+ AA_SFS_DIR("file", aa_sfs_entry_file),
+ AA_SFS_DIR("namespaces", aa_sfs_entry_ns),
+ AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
+ AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit),
+ AA_SFS_DIR("caps", aa_sfs_entry_caps),
+ AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace),
+ AA_SFS_DIR("query", aa_sfs_entry_query),
{ }
};
-static struct aa_fs_entry aa_fs_entry_apparmor[] = {
- AA_FS_FILE_FOPS(".access", 0640, &aa_fs_access),
- AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level),
- AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name),
- AA_FS_FILE_FOPS("profiles", 0440, &aa_fs_profiles_fops),
- AA_FS_DIR("features", aa_fs_entry_features),
+static struct aa_sfs_entry aa_sfs_entry_apparmor[] = {
+ AA_SFS_FILE_FOPS(".access", 0640, &aa_sfs_access),
+ AA_SFS_FILE_FOPS(".stacked", 0444, &seq_ns_stacked_fops),
+ AA_SFS_FILE_FOPS(".ns_stacked", 0444, &seq_ns_nsstacked_fops),
+ AA_SFS_FILE_FOPS(".ns_level", 0666, &seq_ns_level_fops),
+ AA_SFS_FILE_FOPS(".ns_name", 0640, &seq_ns_name_fops),
+ AA_SFS_FILE_FOPS("profiles", 0440, &aa_sfs_profiles_fops),
+ AA_SFS_DIR("features", aa_sfs_entry_features),
{ }
};
-static struct aa_fs_entry aa_fs_entry =
- AA_FS_DIR("apparmor", aa_fs_entry_apparmor);
+static struct aa_sfs_entry aa_sfs_entry =
+ AA_SFS_DIR("apparmor", aa_sfs_entry_apparmor);
/**
- * aafs_create_file - create a file entry in the apparmor securityfs
- * @fs_file: aa_fs_entry to build an entry for (NOT NULL)
+ * entry_create_file - create a file entry in the apparmor securityfs
+ * @fs_file: aa_sfs_entry to build an entry for (NOT NULL)
* @parent: the parent dentry in the securityfs
*
- * Use aafs_remove_file to remove entries created with this fn.
+ * Use entry_remove_file to remove entries created with this fn.
*/
-static int __init aafs_create_file(struct aa_fs_entry *fs_file,
- struct dentry *parent)
+static int __init entry_create_file(struct aa_sfs_entry *fs_file,
+ struct dentry *parent)
{
int error = 0;
@@ -1250,18 +2218,18 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file,
return error;
}
-static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir);
+static void __init entry_remove_dir(struct aa_sfs_entry *fs_dir);
/**
- * aafs_create_dir - recursively create a directory entry in the securityfs
- * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL)
+ * entry_create_dir - recursively create a directory entry in the securityfs
+ * @fs_dir: aa_sfs_entry (and all child entries) to build (NOT NULL)
* @parent: the parent dentry in the securityfs
*
- * Use aafs_remove_dir to remove entries created with this fn.
+ * Use entry_remove_dir to remove entries created with this fn.
*/
-static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
- struct dentry *parent)
+static int __init entry_create_dir(struct aa_sfs_entry *fs_dir,
+ struct dentry *parent)
{
- struct aa_fs_entry *fs_file;
+ struct aa_sfs_entry *fs_file;
struct dentry *dir;
int error;
@@ -1271,10 +2239,10 @@ static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
fs_dir->dentry = dir;
for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
- if (fs_file->v_type == AA_FS_TYPE_DIR)
- error = aafs_create_dir(fs_file, fs_dir->dentry);
+ if (fs_file->v_type == AA_SFS_TYPE_DIR)
+ error = entry_create_dir(fs_file, fs_dir->dentry);
else
- error = aafs_create_file(fs_file, fs_dir->dentry);
+ error = entry_create_file(fs_file, fs_dir->dentry);
if (error)
goto failed;
}
@@ -1282,16 +2250,16 @@ static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
return 0;
failed:
- aafs_remove_dir(fs_dir);
+ entry_remove_dir(fs_dir);
return error;
}
/**
- * aafs_remove_file - drop a single file entry in the apparmor securityfs
- * @fs_file: aa_fs_entry to detach from the securityfs (NOT NULL)
+ * entry_remove_file - drop a single file entry in the apparmor securityfs
+ * @fs_file: aa_sfs_entry to detach from the securityfs (NOT NULL)
*/
-static void __init aafs_remove_file(struct aa_fs_entry *fs_file)
+static void __init entry_remove_file(struct aa_sfs_entry *fs_file)
{
if (!fs_file->dentry)
return;
@@ -1301,21 +2269,21 @@ static void __init aafs_remove_file(struct aa_fs_entry *fs_file)
}
/**
- * aafs_remove_dir - recursively drop a directory entry from the securityfs
- * @fs_dir: aa_fs_entry (and all child entries) to detach (NOT NULL)
+ * entry_remove_dir - recursively drop a directory entry from the securityfs
+ * @fs_dir: aa_sfs_entry (and all child entries) to detach (NOT NULL)
*/
-static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir)
+static void __init entry_remove_dir(struct aa_sfs_entry *fs_dir)
{
- struct aa_fs_entry *fs_file;
+ struct aa_sfs_entry *fs_file;
for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
- if (fs_file->v_type == AA_FS_TYPE_DIR)
- aafs_remove_dir(fs_file);
+ if (fs_file->v_type == AA_SFS_TYPE_DIR)
+ entry_remove_dir(fs_file);
else
- aafs_remove_file(fs_file);
+ entry_remove_file(fs_file);
}
- aafs_remove_file(fs_dir);
+ entry_remove_file(fs_dir);
}
/**
@@ -1325,7 +2293,7 @@ static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir)
*/
void __init aa_destroy_aafs(void)
{
- aafs_remove_dir(&aa_fs_entry);
+ entry_remove_dir(&aa_sfs_entry);
}
@@ -1374,6 +2342,59 @@ out:
return error;
}
+
+
+static const char *policy_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
+{
+ struct aa_ns *ns;
+ struct path path;
+
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+ ns = aa_get_current_ns();
+ path.mnt = mntget(aafs_mnt);
+ path.dentry = dget(ns_dir(ns));
+ nd_jump_link(&path);
+ aa_put_ns(ns);
+
+ return NULL;
+}
+
+static int ns_get_name(char *buf, size_t size, struct aa_ns *ns,
+ struct inode *inode)
+{
+ int res = snprintf(buf, size, "%s:[%lu]", AAFS_NAME, inode->i_ino);
+
+ if (res < 0 || res >= size)
+ res = -ENOENT;
+
+ return res;
+}
+
+static int policy_readlink(struct dentry *dentry, char __user *buffer,
+ int buflen)
+{
+ struct aa_ns *ns;
+ char name[32];
+ int res;
+
+ ns = aa_get_current_ns();
+ res = ns_get_name(name, sizeof(name), ns, d_inode(dentry));
+ if (res >= 0)
+ res = readlink_copy(buffer, buflen, name);
+ aa_put_ns(ns);
+
+ return res;
+}
+
+static const struct inode_operations policy_link_iops = {
+ .readlink = policy_readlink,
+ .get_link = policy_get_link,
+};
+
+
/**
* aa_create_aafs - create the apparmor security filesystem
*
@@ -1389,17 +2410,23 @@ static int __init aa_create_aafs(void)
if (!apparmor_initialized)
return 0;
- if (aa_fs_entry.dentry) {
+ if (aa_sfs_entry.dentry) {
AA_ERROR("%s: AppArmor securityfs already exists\n", __func__);
return -EEXIST;
}
+ /* setup apparmorfs used to virtualize policy/ */
+ aafs_mnt = kern_mount(&aafs_ops);
+ if (IS_ERR(aafs_mnt))
+ panic("can't set apparmorfs up\n");
+ aafs_mnt->mnt_sb->s_flags &= ~MS_NOUSER;
+
/* Populate fs tree. */
- error = aafs_create_dir(&aa_fs_entry, NULL);
+ error = entry_create_dir(&aa_sfs_entry, NULL);
if (error)
goto error;
- dent = securityfs_create_file(".load", 0666, aa_fs_entry.dentry,
+ dent = securityfs_create_file(".load", 0666, aa_sfs_entry.dentry,
NULL, &aa_fs_profile_load);
if (IS_ERR(dent)) {
error = PTR_ERR(dent);
@@ -1407,7 +2434,7 @@ static int __init aa_create_aafs(void)
}
ns_subload(root_ns) = dent;
- dent = securityfs_create_file(".replace", 0666, aa_fs_entry.dentry,
+ dent = securityfs_create_file(".replace", 0666, aa_sfs_entry.dentry,
NULL, &aa_fs_profile_replace);
if (IS_ERR(dent)) {
error = PTR_ERR(dent);
@@ -1415,7 +2442,7 @@ static int __init aa_create_aafs(void)
}
ns_subreplace(root_ns) = dent;
- dent = securityfs_create_file(".remove", 0666, aa_fs_entry.dentry,
+ dent = securityfs_create_file(".remove", 0666, aa_sfs_entry.dentry,
NULL, &aa_fs_profile_remove);
if (IS_ERR(dent)) {
error = PTR_ERR(dent);
@@ -1423,14 +2450,31 @@ 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 = __aa_fs_ns_mkdir(root_ns, aa_fs_entry.dentry, "policy");
+ error = __aafs_ns_mkdir(root_ns, aafs_mnt->mnt_root, ".policy",
+ aafs_mnt->mnt_root);
mutex_unlock(&root_ns->lock);
-
if (error)
goto error;
- error = aa_mk_null_file(aa_fs_entry.dentry);
+ /* magic symlink similar to nsfs redirects based on task policy */
+ dent = securityfs_create_symlink("policy", aa_sfs_entry.dentry,
+ NULL, &policy_link_iops);
+ if (IS_ERR(dent)) {
+ error = PTR_ERR(dent);
+ goto error;
+ }
+
+ error = aa_mk_null_file(aa_sfs_entry.dentry);
if (error)
goto error;