#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
+#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/capability.h>
#include <linux/rcupdate.h>
+#include <uapi/linux/major.h>
#include "include/apparmor.h"
#include "include/apparmorfs.h"
#include "include/audit.h"
#include "include/context.h"
#include "include/crypto.h"
+#include "include/ipc.h"
+#include "include/policy_ns.h"
+#include "include/label.h"
#include "include/policy.h"
#include "include/resource.h"
+#include "include/lib.h"
/**
* aa_mangle_name - mangle a profile name to std profile layout form
*
* Returns: length of mangled name
*/
-static int mangle_name(char *name, char *target)
+static int mangle_name(const char *name, char *target)
{
char *t = target;
/**
* aa_simple_write_to_buffer - common routine for getting policy from user
- * @op: operation doing the user buffer copy
* @userbuf: user buffer to copy data from (NOT NULL)
* @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size)
* @copy_size: size of data to copy from user buffer
* Returns: kernel buffer containing copy of user buffer data or an
* ERR_PTR on failure.
*/
-static char *aa_simple_write_to_buffer(int op, const char __user *userbuf,
+static char *aa_simple_write_to_buffer(const char __user *userbuf,
size_t alloc_size, size_t copy_size,
loff_t *pos)
{
/* only writes from pos 0, that is complete writes */
return ERR_PTR(-ESPIPE);
- /*
- * Don't allow profile load/replace/remove from profiles that don't
- * have CAP_MAC_ADMIN
- */
- if (!aa_may_manage_policy(op))
- return ERR_PTR(-EACCES);
-
/* freed by caller to simple_write_to_buffer */
data = kvmalloc(alloc_size);
if (data == NULL)
return data;
}
-
-/* .load file hook fn to load policy */
-static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
- loff_t *pos)
+static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
+ loff_t *pos)
{
- char *data;
+ struct aa_label *label;
ssize_t error;
+ char *data;
- data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos);
+ label = aa_begin_current_label(DO_UPDATE);
+ /* high level check about policy management - fine grained in
+ * below after unpack
+ */
+ error = aa_may_manage_policy(label, 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(data, size, PROF_ADD);
+ error = aa_replace_profiles(label, mask, data, size);
kvfree(data);
}
+ aa_end_current_label(label);
return error;
}
+/* .load file hook fn to load policy */
+static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
+ loff_t *pos)
+{
+ return policy_update(AA_MAY_LOAD_POLICY, buf, size, pos);
+}
+
static const struct file_operations aa_fs_profile_load = {
.write = profile_load,
.llseek = default_llseek,
static ssize_t profile_replace(struct file *f, const char __user *buf,
size_t size, loff_t *pos)
{
- char *data;
- ssize_t error;
-
- data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos);
- error = PTR_ERR(data);
- if (!IS_ERR(data)) {
- error = aa_replace_profiles(data, size, PROF_REPLACE);
- kvfree(data);
- }
-
- return error;
+ return policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY,
+ buf, size, pos);
}
static const struct file_operations aa_fs_profile_replace = {
static ssize_t profile_remove(struct file *f, const char __user *buf,
size_t size, loff_t *pos)
{
- char *data;
+ struct aa_label *label;
ssize_t error;
+ char *data;
+
+ label = aa_begin_current_label(DO_UPDATE);
+ /* high level check about policy management - fine grained in
+ * below after unpack
+ */
+ error = aa_may_manage_policy(label, AA_MAY_REMOVE_POLICY);
+ if (error)
+ return error;
/*
* aa_remove_profile needs a null terminated string so 1 extra
* byte is allocated and the copied data is null terminated.
*/
- data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos);
+ data = aa_simple_write_to_buffer(buf, size + 1, size, pos);
error = PTR_ERR(data);
if (!IS_ERR(data)) {
data[size] = 0;
- error = aa_remove_profiles(data, size);
+ error = aa_remove_profiles(label, data, size);
kvfree(data);
}
+ aa_end_current_label(label);
return error;
}
.llseek = default_llseek,
};
+
+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_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
+ *
+ * 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 ns_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 = aa_begin_current_label(DO_UPDATE);
+ label = aa_label_parse(curr, label_name, GFP_KERNEL, false, false);
+ aa_end_current_label(curr);
+ if (IS_ERR(label))
+ return PTR_ERR(label);
+
+ perms = allperms;
+ if (ns_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);
+}
+
+#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
+
+/**
+ * aa_write_access - generic permissions query
+ * @file: pointer to open apparmorfs/access file
+ * @ubuf: user buffer containing the complete query string (NOT NULL)
+ * @count: size of ubuf
+ * @ppos: position in the file (MUST BE ZERO)
+ *
+ * Allows for one permission query per open(), write(), and read() sequence.
+ * The only query currently supported is a label-based query. For this query
+ * ubuf must begin with "label\0", followed by the profile query specific
+ * format described in the query_label() function documentation.
+ *
+ * Returns: number of bytes written or -errno on failure
+ */
+static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char *buf;
+ 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_PROFILE_LEN &&
+ !memcmp(buf, QUERY_CMD_PROFILE, QUERY_CMD_PROFILE_LEN)) {
+ len = query_label(buf, SIMPLE_TRANSACTION_LIMIT,
+ buf + QUERY_CMD_PROFILE_LEN,
+ count - QUERY_CMD_PROFILE_LEN, true);
+ } else if (count > QUERY_CMD_LABEL_LEN &&
+ !memcmp(buf, QUERY_CMD_LABEL, QUERY_CMD_LABEL_LEN)) {
+ len = query_label(buf, SIMPLE_TRANSACTION_LIMIT,
+ buf + QUERY_CMD_LABEL_LEN,
+ count - QUERY_CMD_LABEL_LEN, true);
+ } else if (count > QUERY_CMD_LABELALL_LEN &&
+ !memcmp(buf, QUERY_CMD_LABELALL, QUERY_CMD_LABELALL_LEN)) {
+ len = query_label(buf, SIMPLE_TRANSACTION_LIMIT,
+ buf + QUERY_CMD_LABELALL_LEN,
+ count - QUERY_CMD_LABELALL_LEN, false);
+ } else
+ len = -EINVAL;
+
+ if (len < 0)
+ return len;
+
+ simple_transaction_set(file, len);
+
+ return count;
+}
+
+static const struct file_operations aa_fs_access = {
+ .write = aa_write_access,
+ .read = simple_transaction_read,
+ .release = simple_transaction_release,
+ .llseek = generic_file_llseek,
+};
+
static int aa_fs_seq_show(struct seq_file *seq, void *v)
{
struct aa_fs_entry *fs_file = seq->private;
static int aa_fs_seq_profile_open(struct inode *inode, struct file *file,
int (*show)(struct seq_file *, void *))
{
- struct aa_replacedby *r = aa_get_replacedby(inode->i_private);
- int error = single_open(file, show, r);
+ struct aa_proxy *proxy = aa_get_proxy(inode->i_private);
+ int error = single_open(file, show, proxy);
if (error) {
file->private_data = NULL;
- aa_put_replacedby(r);
+ aa_put_proxy(proxy);
}
return error;
{
struct seq_file *seq = (struct seq_file *) file->private_data;
if (seq)
- aa_put_replacedby(seq->private);
+ aa_put_proxy(seq->private);
return single_release(inode, file);
}
static int aa_fs_seq_profname_show(struct seq_file *seq, void *v)
{
- struct aa_replacedby *r = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
+ struct aa_proxy *proxy = seq->private;
+ 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_profmode_show(struct seq_file *seq, void *v)
{
- struct aa_replacedby *r = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
+ struct aa_proxy *proxy = seq->private;
+ 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_profattach_show(struct seq_file *seq, void *v)
{
- struct aa_replacedby *r = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
+ struct aa_proxy *proxy = seq->private;
+ 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_hash_show(struct seq_file *seq, void *v)
{
- struct aa_replacedby *r = seq->private;
- struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
+ struct aa_proxy *proxy = seq->private;
+ 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) {
seq_printf(seq, "%.2x", profile->hash[i]);
seq_puts(seq, "\n");
}
- aa_put_profile(profile);
+ aa_put_label(label);
return 0;
}
};
/** fns to setup dynamic per profile/namespace files **/
+
+/**
+ *
+ * Requires: @profile->ns->lock held
+ */
void __aa_fs_profile_rmdir(struct aa_profile *profile)
{
struct aa_profile *child;
if (!profile)
return;
+ AA_BUG(!mutex_is_locked(&profiles_ns(profile)->lock));
list_for_each_entry(child, &profile->base.profiles, base.list)
__aa_fs_profile_rmdir(child);
for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) {
- struct aa_replacedby *r;
+ struct aa_proxy *proxy;
if (!profile->dents[i])
continue;
- r = d_inode(profile->dents[i])->i_private;
+ proxy = d_inode(profile->dents[i])->i_private;
securityfs_remove(profile->dents[i]);
- aa_put_replacedby(r);
+ aa_put_proxy(proxy);
profile->dents[i] = NULL;
}
}
+/**
+ *
+ * Requires: @old->ns->lock held
+ */
void __aa_fs_profile_migrate_dents(struct aa_profile *old,
struct aa_profile *new)
{
int i;
+ AA_BUG(!old);
+ AA_BUG(!new);
+ AA_BUG(!mutex_is_locked(&profiles_ns(old)->lock));
+
for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
new->dents[i] = old->dents[i];
if (new->dents[i])
struct aa_profile *profile,
const struct file_operations *fops)
{
- struct aa_replacedby *r = aa_get_replacedby(profile->replacedby);
+ struct aa_proxy *proxy = aa_get_proxy(profile->label.proxy);
struct dentry *dent;
- dent = securityfs_create_file(name, S_IFREG | 0444, dir, r, fops);
+ dent = securityfs_create_file(name, S_IFREG | 0444, dir, proxy, fops);
if (IS_ERR(dent))
- aa_put_replacedby(r);
+ aa_put_proxy(proxy);
return dent;
}
-/* requires lock be held */
+/**
+ *
+ * Requires: @profile->ns->lock held
+ */
int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
{
struct aa_profile *child;
struct dentry *dent = NULL, *dir;
int error;
+ AA_BUG(!profile);
+ AA_BUG(!mutex_is_locked(&profiles_ns(profile)->lock));
+
if (!parent) {
struct aa_profile *p;
p = aa_deref_parent(profile);
return error;
}
-void __aa_fs_namespace_rmdir(struct aa_namespace *ns)
+/**
+ *
+ * Requires: @ns->lock held
+ */
+void __aa_fs_ns_rmdir(struct aa_ns *ns)
{
- struct aa_namespace *sub;
+ struct aa_ns *sub;
struct aa_profile *child;
int i;
if (!ns)
return;
+ AA_BUG(!mutex_is_locked(&ns->lock));
list_for_each_entry(child, &ns->base.profiles, base.list)
__aa_fs_profile_rmdir(child);
list_for_each_entry(sub, &ns->sub_ns, base.list) {
mutex_lock(&sub->lock);
- __aa_fs_namespace_rmdir(sub);
+ __aa_fs_ns_rmdir(sub);
mutex_unlock(&sub->lock);
}
}
}
-int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
- const char *name)
+/**
+ *
+ * Requires: @ns->lock held
+ */
+int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name)
{
- struct aa_namespace *sub;
+ struct aa_ns *sub;
struct aa_profile *child;
struct dentry *dent, *dir;
int error;
+ AA_BUG(!ns);
+ AA_BUG(!parent);
+ AA_BUG(!mutex_is_locked(&ns->lock));
+
if (!name)
name = ns->base.name;
list_for_each_entry(sub, &ns->sub_ns, base.list) {
mutex_lock(&sub->lock);
- error = __aa_fs_namespace_mkdir(sub, ns_subns_dir(ns), NULL);
+ error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL);
mutex_unlock(&sub->lock);
if (error)
goto fail2;
error = PTR_ERR(dent);
fail2:
- __aa_fs_namespace_rmdir(ns);
+ __aa_fs_ns_rmdir(ns);
return error;
}
#define list_entry_is_head(pos, head, member) (&pos->member == (head))
/**
- * __next_namespace - find the next namespace to list
+ * __next_ns - find the next namespace to list
* @root: root namespace to stop search at (NOT NULL)
* @ns: current ns position (NOT NULL)
*
* Requires: ns->parent->lock to be held
* NOTE: will not unlock root->lock
*/
-static struct aa_namespace *__next_namespace(struct aa_namespace *root,
- struct aa_namespace *ns)
+static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
{
- struct aa_namespace *parent, *next;
+ struct aa_ns *parent, *next;
+
+ AA_BUG(!root);
+ AA_BUG(!ns);
+ AA_BUG(ns != root && !mutex_is_locked(&ns->parent->lock));
/* is next namespace a child */
if (!list_empty(&ns->sub_ns)) {
/**
* __first_profile - find the first profile in a namespace
* @root: namespace that is root of profiles being displayed (NOT NULL)
- * @ns: namespace to start in (NOT NULL)
+ * @ns: namespace to start in (MAY BE NULL)
*
* Returns: unrefcounted profile or NULL if no profile
- * Requires: profile->ns.lock to be held
+ * Requires: ns.lock to be held
*/
-static struct aa_profile *__first_profile(struct aa_namespace *root,
- struct aa_namespace *ns)
+static struct aa_profile *__first_profile(struct aa_ns *root, struct aa_ns *ns)
{
- for (; ns; ns = __next_namespace(root, ns)) {
+ AA_BUG(!root);
+ AA_BUG(ns && !mutex_is_locked(&ns->lock));
+
+ for (; ns; ns = __next_ns(root, ns)) {
if (!list_empty(&ns->base.profiles))
return list_first_entry(&ns->base.profiles,
struct aa_profile, base.list);
static struct aa_profile *__next_profile(struct aa_profile *p)
{
struct aa_profile *parent;
- struct aa_namespace *ns = p->ns;
+ struct aa_ns *ns = p->ns;
+
+ AA_BUG(!mutex_is_locked(&profiles_ns(p)->lock));
/* is next profile a child */
if (!list_empty(&p->base.profiles))
*
* Returns: next profile or NULL if there isn't one
*/
-static struct aa_profile *next_profile(struct aa_namespace *root,
+static struct aa_profile *next_profile(struct aa_ns *root,
struct aa_profile *profile)
{
struct aa_profile *next = __next_profile(profile);
return next;
/* finished all profiles in namespace move to next namespace */
- return __first_profile(root, __next_namespace(root, profile->ns));
+ return __first_profile(root, __next_ns(root, profile->ns));
}
/**
static void *p_start(struct seq_file *f, loff_t *pos)
{
struct aa_profile *profile = NULL;
- struct aa_namespace *root = aa_current_profile()->ns;
+ struct aa_ns *root = aa_get_current_ns();
loff_t l = *pos;
- f->private = aa_get_namespace(root);
-
+ f->private = root;
/* find the first profile */
mutex_lock(&root->lock);
static void *p_next(struct seq_file *f, void *p, loff_t *pos)
{
struct aa_profile *profile = p;
- struct aa_namespace *ns = f->private;
+ struct aa_ns *ns = f->private;
(*pos)++;
return next_profile(ns, profile);
static void p_stop(struct seq_file *f, void *p)
{
struct aa_profile *profile = p;
- struct aa_namespace *root = f->private, *ns;
+ struct aa_ns *root = f->private, *ns;
if (profile) {
for (ns = profile->ns; ns && ns != root; ns = ns->parent)
mutex_unlock(&ns->lock);
}
mutex_unlock(&root->lock);
- aa_put_namespace(root);
+ aa_put_ns(root);
}
/**
static int seq_show_profile(struct seq_file *f, void *p)
{
struct aa_profile *profile = (struct aa_profile *)p;
- struct aa_namespace *root = f->private;
+ struct aa_ns *root = f->private;
- if (profile->ns != root)
- seq_printf(f, ":%s://", aa_ns_name(root, profile->ns));
- 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_printf(f, "\n");
return 0;
}
static int profiles_open(struct inode *inode, struct file *file)
{
+ if (!policy_admin_capable())
+ return -EACCES;
+
return seq_open(file, &aa_fs_profiles_op);
}
{ }
};
+static struct aa_fs_entry aa_fs_entry_ptrace[] = {
+ AA_FS_FILE_STRING("mask", "read trace"),
+ { }
+};
+
+static struct aa_fs_entry aa_fs_entry_signal[] = {
+ AA_FS_FILE_STRING("mask", AA_FS_SIG_MASK),
+ { }
+};
+
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("stack", 1),
+ { }
+};
+
+static struct aa_fs_entry aa_fs_entry_versions[] = {
+ AA_FS_FILE_BOOLEAN("v5", 1),
+ AA_FS_FILE_BOOLEAN("v6", 1),
+ AA_FS_FILE_BOOLEAN("v7", 1),
{ }
};
static struct aa_fs_entry aa_fs_entry_policy[] = {
- AA_FS_FILE_BOOLEAN("set_load", 1),
- {}
+ AA_FS_DIR("versions", aa_fs_entry_versions),
+ AA_FS_FILE_BOOLEAN("set_load", 1),
+ { }
+};
+
+static struct aa_fs_entry aa_fs_entry_mount[] = {
+ AA_FS_FILE_STRING("mask", "mount umount"),
+ { }
+};
+
+static struct aa_fs_entry aa_fs_entry_ns[] = {
+ AA_FS_FILE_BOOLEAN("profile", 1),
+ AA_FS_FILE_BOOLEAN("pivot_root", 1),
+ { }
+};
+
+static struct aa_fs_entry aa_fs_entry_dbus[] = {
+ AA_FS_FILE_STRING("mask", "acquire send receive"),
+ { }
};
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_DIR("network", aa_fs_entry_network),
+ AA_FS_DIR("mount", aa_fs_entry_mount),
+ AA_FS_DIR("namespaces", aa_fs_entry_ns),
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),
+ AA_FS_DIR("ptrace", aa_fs_entry_ptrace),
+ AA_FS_DIR("signal", aa_fs_entry_signal),
+ AA_FS_DIR("dbus", aa_fs_entry_dbus),
{ }
};
static struct aa_fs_entry aa_fs_entry_apparmor[] = {
- AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load),
- AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace),
- AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove),
- AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops),
+ AA_FS_FILE_FOPS(".load", 0666, &aa_fs_profile_load),
+ AA_FS_FILE_FOPS(".replace", 0666, &aa_fs_profile_replace),
+ AA_FS_FILE_FOPS(".remove", 0666, &aa_fs_profile_remove),
+ AA_FS_FILE_FOPS(".access", 0666, &aa_fs_access),
+ AA_FS_FILE_FOPS("profiles", 0444, &aa_fs_profiles_fops),
AA_FS_DIR("features", aa_fs_entry_features),
{ }
};
aafs_remove_dir(&aa_fs_entry);
}
+
+#define NULL_FILE_NAME ".null"
+struct path aa_null;
+
+static int aa_mk_null_file(struct dentry *parent)
+{
+ struct vfsmount *mount = NULL;
+ struct dentry *dentry;
+ struct inode *inode;
+ int count = 0;
+ int error = simple_pin_fs(parent->d_sb->s_type, &mount, &count);
+ if (error)
+ return error;
+
+ inode_lock(d_inode(parent));
+ dentry = lookup_one_len(NULL_FILE_NAME, parent, strlen(NULL_FILE_NAME));
+ if (IS_ERR(dentry)) {
+ error = PTR_ERR(dentry);
+ goto out;
+ }
+ inode = new_inode(parent->d_inode->i_sb);
+ if (!inode) {
+ error = -ENOMEM;
+ goto out1;
+ }
+
+ inode->i_ino = get_next_ino();
+ inode->i_mode = S_IFCHR | S_IRUGO | S_IWUGO;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO,
+ MKDEV(MEM_MAJOR, 3));
+ d_instantiate(dentry, inode);
+ aa_null.dentry = dget(dentry);
+ aa_null.mnt = mntget(mount);
+
+ error = 0;
+
+out1:
+ dput(dentry);
+out:
+ inode_unlock(d_inode(parent));
+ simple_release_fs(&mount, &count);
+ return error;
+}
+
/**
* aa_create_aafs - create the apparmor security filesystem
*
if (error)
goto error;
- error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry.dentry,
- "policy");
+ mutex_lock(&root_ns->lock);
+ error = __aa_fs_ns_mkdir(root_ns, aa_fs_entry.dentry, "policy");
+ mutex_unlock(&root_ns->lock);
+
+ if (error)
+ goto error;
+
+ error = aa_mk_null_file(aa_fs_entry.dentry);
if (error)
goto error;
- /* TODO: add support for apparmorfs_null and apparmorfs_mnt */
+ if (!aa_g_unconfined_init) {
+ /* TODO: add default profile to apparmorfs */
+ }
/* Report that AppArmor fs is enabled */
aa_info_message("AppArmor Filesystem Enabled");