Home Home > GIT Browse > SLE15
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Thumshirn <jthumshirn@suse.de>2018-09-14 16:41:07 +0200
committerJohannes Thumshirn <jthumshirn@suse.de>2018-09-14 16:41:07 +0200
commit596983f9886b236e3314d994667ba40319e61e49 (patch)
tree026cd541ef869eaba23aff6e6239682d982c7d68
parente0e199a58f607aa5f111c2b95900564c67dda52e (diff)
parentff0b38137c8c279654b9aee2dd83cfb4addd53f1 (diff)
Merge remote-tracking branch 'origin/users/rgoldwyn/SLE12-SP4/for-next' into SLE12-SP4rpm-4.12.14-94.37
Pull AppArmor updates from Goldwyn Rodrigues. Conflicts: series.conf suse-commit: 07d5db05cd6058a13afaa801d110a4206abdd0a5
-rw-r--r--include/linux/security.h12
-rw-r--r--include/uapi/linux/magic.h2
-rw-r--r--security/apparmor/Makefile12
-rw-r--r--security/apparmor/apparmorfs.c1784
-rw-r--r--security/apparmor/audit.c27
-rw-r--r--security/apparmor/capability.c61
-rw-r--r--security/apparmor/context.c87
-rw-r--r--security/apparmor/domain.c1446
-rw-r--r--security/apparmor/file.c517
-rw-r--r--security/apparmor/include/apparmor.h8
-rw-r--r--security/apparmor/include/apparmorfs.h67
-rw-r--r--security/apparmor/include/audit.h50
-rw-r--r--security/apparmor/include/capability.h8
-rw-r--r--security/apparmor/include/context.h201
-rw-r--r--security/apparmor/include/domain.h18
-rw-r--r--security/apparmor/include/file.h114
-rw-r--r--security/apparmor/include/ipc.h22
-rw-r--r--security/apparmor/include/label.h442
-rw-r--r--security/apparmor/include/lib.h120
-rw-r--r--security/apparmor/include/mount.h54
-rw-r--r--security/apparmor/include/net.h6
-rw-r--r--security/apparmor/include/path.h7
-rw-r--r--security/apparmor/include/perms.h158
-rw-r--r--security/apparmor/include/policy.h131
-rw-r--r--security/apparmor/include/policy_ns.h21
-rw-r--r--security/apparmor/include/policy_unpack.h68
-rw-r--r--security/apparmor/include/procattr.h8
-rw-r--r--security/apparmor/include/resource.h6
-rw-r--r--security/apparmor/include/sig_names.h100
-rw-r--r--security/apparmor/ipc.c238
-rw-r--r--security/apparmor/label.c2122
-rw-r--r--security/apparmor/lib.c368
-rw-r--r--security/apparmor/lsm.c341
-rw-r--r--security/apparmor/match.c2
-rw-r--r--security/apparmor/mount.c706
-rw-r--r--security/apparmor/net.c32
-rw-r--r--security/apparmor/path.c130
-rw-r--r--security/apparmor/policy.c553
-rw-r--r--security/apparmor/policy_ns.c92
-rw-r--r--security/apparmor/policy_unpack.c188
-rw-r--r--security/apparmor/procattr.c71
-rw-r--r--security/apparmor/resource.c116
-rw-r--r--security/inode.c144
43 files changed, 1980 insertions, 8680 deletions
diff --git a/include/linux/security.h b/include/linux/security.h
index d0d8580b84dd..19fffd5f336e 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1701,10 +1701,6 @@ extern struct dentry *securityfs_create_file(const char *name, umode_t mode,
struct dentry *parent, void *data,
const struct file_operations *fops);
extern struct dentry *securityfs_create_dir(const char *name, struct dentry *parent);
-struct dentry *securityfs_create_symlink(const char *name,
- struct dentry *parent,
- const char *target,
- const struct inode_operations *iops);
extern void securityfs_remove(struct dentry *dentry);
#else /* CONFIG_SECURITYFS */
@@ -1724,14 +1720,6 @@ static inline struct dentry *securityfs_create_file(const char *name,
return ERR_PTR(-ENODEV);
}
-static inline struct dentry *securityfs_create_symlink(const char *name,
- struct dentry *parent,
- const char *target,
- const struct inode_operations *iops)
-{
- return ERR_PTR(-ENODEV);
-}
-
static inline void securityfs_remove(struct dentry *dentry)
{}
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index a0908f1d2760..e230af2e6855 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -80,8 +80,6 @@
#define BTRFS_TEST_MAGIC 0x73727279
#define NSFS_MAGIC 0x6e736673
#define BPF_FS_MAGIC 0xcafe4a11
-#define AAFS_MAGIC 0x5a3c69f0
-
/* Since UDF 2.01 is ISO 13346 based... */
#define UDF_SUPER_MAGIC 0x15013346
#define BALLOON_KVM_MAGIC 0x13661366
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index 589a6c25a7cb..a7dc10be232d 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
- resource.o secid.o file.o policy_ns.o label.o mount.o net.o
+ resource.o secid.o file.o policy_ns.o net.o
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
-clean-files := capability_names.h rlim_names.h
+clean-files := capability_names.h rlim_names.h net_names.h
# Build a lower case string table of capability names
@@ -20,7 +20,7 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\
sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \
-e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\
echo "};" >> $@ ;\
- printf '%s' '\#define AA_SFS_CAPS_MASK "' >> $@ ;\
+ echo -n '\#define AA_FS_CAPS_MASK "' >> $@ ;\
sed $< -r -n -e '/CAP_FS_MASK/d' \
-e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \
tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
@@ -43,7 +43,7 @@ cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \
's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
echo "};" >> $@ ;\
- echo -n '\#define AA_SFS_AF_MASK "' >> $@ ;\
+ echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\
sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\
$< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
@@ -78,7 +78,7 @@ cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\
# #define RLIMIT_FSIZE 1 /* Maximum filesize */
# #define RLIMIT_STACK 3 /* max stack size */
# to
-# #define AA_SFS_RLIMIT_MASK "fsize stack"
+# #define AA_FS_RLIMIT_MASK "fsize stack"
quiet_cmd_make-rlim = GEN $@
cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \
> $@ ;\
@@ -88,7 +88,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \
echo "static const int rlim_map[RLIM_NLIMITS] = {" >> $@ ;\
sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\
echo "};" >> $@ ; \
- printf '%s' '\#define AA_SFS_RLIMIT_MASK "' >> $@ ;\
+ echo -n '\#define AA_FS_RLIMIT_MASK "' >> $@ ;\
sed -r -n 's/^\# ?define[ \t]+RLIMIT_([A-Z0-9_]+).*/\L\1/p' $< | \
tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 6e3c1cf29dee..4b121211e5e7 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -22,53 +22,19 @@
#include <linux/namei.h>
#include <linux/capability.h>
#include <linux/rcupdate.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
#include <uapi/linux/major.h>
-#include <uapi/linux/magic.h>
+#include <linux/fs.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/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)
@@ -108,268 +74,6 @@ 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)) {
- error = PTR_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
- * @private: private data
- * @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,
- void *private,
- const struct inode_operations *iops)
-{
- struct dentry *dent;
- char *link = NULL;
-
- if (target) {
- if (!link)
- return ERR_PTR(-ENOMEM);
- }
- dent = aafs_create(name, S_IFLNK | 0444, parent, private, 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)
@@ -394,11 +98,14 @@ 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 = aa_loaddata_alloc(alloc_size);
- if (IS_ERR(data))
- return data;
-
+ 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;
+
if (copy_from_user(data->data, userbuf, copy_size)) {
kvfree(data);
return ERR_PTR(-EFAULT);
@@ -407,29 +114,27 @@ static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
return data;
}
-static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
+static ssize_t policy_update(int binop, const char __user *buf, size_t size,
loff_t *pos, struct aa_ns *ns)
{
- struct aa_loaddata *data;
- struct aa_label *label;
ssize_t error;
-
- label = begin_current_label_crit_section();
-
+ struct aa_loaddata *data;
+ struct aa_profile *profile = aa_current_profile();
+ const char *op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL;
/* high level check about policy management - fine grained in
* below after unpack
*/
- error = aa_may_manage_policy(label, ns, mask);
+ error = aa_may_manage_policy(profile, ns, op);
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, label, mask, data);
+ error = aa_replace_profiles(ns ? ns : profile->ns, profile,
+ binop, data);
aa_put_loaddata(data);
}
- end_current_label_crit_section(label);
return error;
}
@@ -439,7 +144,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(AA_MAY_LOAD_POLICY, buf, size, pos, ns);
+ int error = policy_update(PROF_ADD, buf, size, pos, ns);
aa_put_ns(ns);
@@ -456,8 +161,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(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY,
- buf, size, pos, ns);
+ int error = policy_update(PROF_REPLACE, buf, size, pos, ns);
+
aa_put_ns(ns);
return error;
@@ -473,15 +178,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_label *label;
+ struct aa_profile *profile;
ssize_t error;
struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
- label = begin_current_label_crit_section();
+ profile = aa_current_profile();
/* high level check about policy management - fine grained in
* below after unpack
*/
- error = aa_may_manage_policy(label, ns, AA_MAY_REMOVE_POLICY);
+ error = aa_may_manage_policy(profile, ns, OP_PROF_RM);
if (error)
goto out;
@@ -494,11 +199,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, label, data->data, size);
+ error = aa_remove_profiles(ns ? ns : profile->ns, profile,
+ data->data, size);
aa_put_loaddata(data);
}
out:
- end_current_label_crit_section(label);
aa_put_ns(ns);
return error;
}
@@ -508,136 +213,6 @@ 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_nested(&rev->ns->lock, rev->ns->level);
- 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_nested(&rev->ns->lock, rev->ns->level);
- }
-
- 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_nested(&rev->ns->lock, rev->ns->level);
- 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)
@@ -661,8 +236,6 @@ 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;
@@ -680,11 +253,7 @@ static ssize_t query_data(char *buf, size_t buf_len,
if (buf_len < sizeof(bytes) + sizeof(blocks))
return -EINVAL; /* not enough space */
- 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);
+ profile = aa_current_profile();
/* 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
@@ -698,19 +267,13 @@ static ssize_t query_data(char *buf, size_t buf_len,
out = buf + sizeof(bytes) + sizeof(blocks);
blocks = 0;
- label_for_each_confined(i, label, profile) {
- if (!profile->data)
- continue;
-
+ if (profile->data) {
data = rhashtable_lookup_fast(profile->data, &key,
profile->data->p);
if (data) {
- if (out + sizeof(outle32) + data->size > buf +
- buf_len) {
- aa_put_label(label);
+ if (out + sizeof(outle32) + data->size > buf + buf_len)
return -EINVAL; /* not enough space */
- }
outle32 = __cpu_to_le32(data->size);
memcpy(out, &outle32, sizeof(outle32));
out += sizeof(outle32);
@@ -719,7 +282,6 @@ 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));
@@ -729,182 +291,6 @@ 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
@@ -932,72 +318,54 @@ static int multi_transaction_release(struct inode *inode, struct file *file)
static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
- struct multi_transaction *t;
+ char *buf;
ssize_t len;
if (*ppos)
return -ESPIPE;
- 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,
+ 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,
count - QUERY_CMD_DATA_LEN);
} else
len = -EINVAL;
- if (len < 0) {
- put_multi_transaction(t);
+ if (len < 0)
return len;
- }
- multi_transaction_set(file, t, len);
+ simple_transaction_set(file, len);
return count;
}
-static const struct file_operations aa_sfs_access = {
+static const struct file_operations aa_fs_access = {
.write = aa_write_access,
- .read = multi_transaction_read,
- .release = multi_transaction_release,
+ .read = simple_transaction_read,
+ .release = simple_transaction_release,
.llseek = generic_file_llseek,
};
-static int aa_sfs_seq_show(struct seq_file *seq, void *v)
+static int aa_fs_seq_show(struct seq_file *seq, void *v)
{
- struct aa_sfs_entry *fs_file = seq->private;
+ struct aa_fs_entry *fs_file = seq->private;
if (!fs_file)
return 0;
switch (fs_file->v_type) {
- case AA_SFS_TYPE_BOOLEAN:
+ case AA_FS_TYPE_BOOLEAN:
seq_printf(seq, "%s\n", fs_file->v.boolean ? "yes" : "no");
break;
- case AA_SFS_TYPE_STRING:
+ case AA_FS_TYPE_STRING:
seq_printf(seq, "%s\n", fs_file->v.string);
break;
- case AA_SFS_TYPE_U64:
+ case AA_FS_TYPE_U64:
seq_printf(seq, "%#08lx\n", fs_file->v.u64);
break;
default:
@@ -1008,40 +376,21 @@ static int aa_sfs_seq_show(struct seq_file *seq, void *v)
return 0;
}
-static int aa_sfs_seq_open(struct inode *inode, struct file *file)
+static int aa_fs_seq_open(struct inode *inode, struct file *file)
{
- return single_open(file, aa_sfs_seq_show, inode->i_private);
+ return single_open(file, aa_fs_seq_show, inode->i_private);
}
-const struct file_operations aa_sfs_seq_file_ops = {
+const struct file_operations aa_fs_seq_file_ops = {
.owner = THIS_MODULE,
- .open = aa_sfs_seq_open,
+ .open = aa_fs_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
-/*
- * 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 *))
+static int aa_fs_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);
@@ -1054,7 +403,7 @@ static int seq_profile_open(struct inode *inode, struct file *file,
return error;
}
-static int seq_profile_release(struct inode *inode, struct file *file)
+static int aa_fs_seq_profile_release(struct inode *inode, struct file *file)
{
struct seq_file *seq = (struct seq_file *) file->private_data;
if (seq)
@@ -1062,227 +411,217 @@ static int seq_profile_release(struct inode *inode, struct file *file)
return single_release(inode, file);
}
-static int seq_profile_name_show(struct seq_file *seq, void *v)
+static int aa_fs_seq_profname_show(struct seq_file *seq, void *v)
{
struct aa_proxy *proxy = seq->private;
- struct aa_label *label = aa_get_label_rcu(&proxy->label);
- struct aa_profile *profile = labels_profile(label);
+ struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
seq_printf(seq, "%s\n", profile->base.name);
- aa_put_label(label);
+ aa_put_profile(profile);
return 0;
}
-static int seq_profile_mode_show(struct seq_file *seq, void *v)
+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)
{
struct aa_proxy *proxy = seq->private;
- struct aa_label *label = aa_get_label_rcu(&proxy->label);
- struct aa_profile *profile = labels_profile(label);
+ struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]);
- aa_put_label(label);
+ aa_put_profile(profile);
return 0;
}
-static int seq_profile_attach_show(struct seq_file *seq, void *v)
+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)
{
struct aa_proxy *proxy = seq->private;
- struct aa_label *label = aa_get_label_rcu(&proxy->label);
- struct aa_profile *profile = labels_profile(label);
+ struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
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_label(label);
+ aa_put_profile(profile);
return 0;
}
-static int seq_profile_hash_show(struct seq_file *seq, void *v)
+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)
{
struct aa_proxy *proxy = seq->private;
- struct aa_label *label = aa_get_label_rcu(&proxy->label);
- struct aa_profile *profile = labels_profile(label);
+ struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
unsigned int i, size = aa_hash_size();
if (profile->hash) {
for (i = 0; i < size; i++)
seq_printf(seq, "%.2x", profile->hash[i]);
- seq_putc(seq, '\n');
+ seq_puts(seq, "\n");
}
- aa_put_label(label);
+ aa_put_profile(profile);
return 0;
}
-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)
+static int aa_fs_seq_hash_open(struct inode *inode, struct file *file)
{
- struct aa_label *label;
-
- label = begin_current_label_crit_section();
- seq_printf(seq, "%s\n", label->size > 1 ? "yes" : "no");
- end_current_label_crit_section(label);
-
- return 0;
+ return single_open(file, aa_fs_seq_hash_show, inode->i_private);
}
-static int seq_ns_nsstacked_show(struct seq_file *seq, void *v)
-{
- struct aa_label *label;
- struct aa_profile *profile;
- struct label_it it;
- int count = 1;
+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();
- if (label->size > 1) {
- label_for_each(it, label, profile)
- if (profile->ns != labels_ns(label)) {
- count++;
- break;
- }
- }
+static int aa_fs_seq_show_ns_level(struct seq_file *seq, void *v)
+{
+ struct aa_ns *ns = aa_current_profile()->ns;
- seq_printf(seq, "%s\n", count > 1 ? "yes" : "no");
- end_current_label_crit_section(label);
+ seq_printf(seq, "%d\n", ns->level);
return 0;
}
-static int seq_ns_level_show(struct seq_file *seq, void *v)
+static int aa_fs_seq_open_ns_level(struct inode *inode, struct file *file)
{
- struct aa_label *label;
-
- label = begin_current_label_crit_section();
- seq_printf(seq, "%d\n", labels_ns(label)->level);
- end_current_label_crit_section(label);
-
- return 0;
+ return single_open(file, aa_fs_seq_show_ns_level, inode->i_private);
}
-static int seq_ns_name_show(struct seq_file *seq, void *v)
+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,
+};
+
+static int aa_fs_seq_show_ns_name(struct seq_file *seq, void *v)
{
- struct aa_label *label = begin_current_label_crit_section();
- seq_printf(seq, "%s\n", labels_ns(label)->base.name);
- end_current_label_crit_section(label);
+ struct aa_ns *ns = aa_current_profile()->ns;
+
+ seq_printf(seq, "%s\n", ns->base.name);
return 0;
}
-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 *))
+static int aa_fs_seq_open_ns_name(struct inode *inode, struct file *file)
{
- struct aa_loaddata *data = __aa_get_loaddata(inode->i_private);
- int error;
-
- if (!data)
- /* lost race this ent is being reaped */
- return -ENOENT;
-
- 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;
+ return single_open(file, aa_fs_seq_show_ns_name, inode->i_private);
}
-static int seq_rawdata_release(struct inode *inode, struct file *file)
-{
- struct seq_file *seq = (struct seq_file *) file->private_data;
+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 (seq)
- aa_put_loaddata(seq->private);
+static int rawdata_release(struct inode *inode, struct file *file)
+{
+ /* TODO: switch to loaddata when profile switched to symlink */
+ aa_put_loaddata(file->private_data);
- return single_release(inode, file);
+ return 0;
}
-static int seq_rawdata_abi_show(struct seq_file *seq, void *v)
+static int aa_fs_seq_raw_abi_show(struct seq_file *seq, void *v)
{
- struct aa_loaddata *data = seq->private;
+ struct aa_proxy *proxy = seq->private;
+ struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
- seq_printf(seq, "v%d\n", data->abi);
+ if (profile->rawdata->abi) {
+ seq_printf(seq, "v%d", profile->rawdata->abi);
+ seq_puts(seq, "\n");
+ }
+ aa_put_profile(profile);
return 0;
}
-static int seq_rawdata_revision_show(struct seq_file *seq, void *v)
+static int aa_fs_seq_raw_abi_open(struct inode *inode, struct file *file)
{
- struct aa_loaddata *data = seq->private;
-
- seq_printf(seq, "%ld\n", data->revision);
-
- return 0;
+ return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_abi_show);
}
-static int seq_rawdata_hash_show(struct seq_file *seq, void *v)
+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,
+};
+
+static int aa_fs_seq_raw_hash_show(struct seq_file *seq, void *v)
{
- struct aa_loaddata *data = seq->private;
+ struct aa_proxy *proxy = seq->private;
+ struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
unsigned int i, size = aa_hash_size();
- if (data->hash) {
+ if (profile->rawdata->hash) {
for (i = 0; i < size; i++)
- seq_printf(seq, "%.2x", data->hash[i]);
- seq_putc(seq, '\n');
+ seq_printf(seq, "%.2x", profile->rawdata->hash[i]);
+ seq_puts(seq, "\n");
}
+ aa_put_profile(profile);
return 0;
}
-SEQ_RAWDATA_FOPS(abi);
-SEQ_RAWDATA_FOPS(revision);
-SEQ_RAWDATA_FOPS(hash);
+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,
+};
static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
loff_t *ppos)
@@ -1293,127 +632,29 @@ static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
rawdata->size);
}
-static int rawdata_release(struct inode *inode, struct file *file)
-{
- aa_put_loaddata(file->private_data);
-
- return 0;
-}
-
static int rawdata_open(struct inode *inode, struct file *file)
{
+ struct aa_proxy *proxy = inode->i_private;
+ struct aa_profile *profile;
+
if (!policy_view_capable(NULL))
return -EACCES;
- file->private_data = __aa_get_loaddata(inode->i_private);
- if (!file->private_data)
- /* lost race: this entry is being reaped */
- return -ENOENT;
+ profile = aa_get_profile_rcu(&proxy->profile);
+ file->private_data = aa_get_loaddata(profile->rawdata);
+ aa_put_profile(profile);
return 0;
}
-static const struct file_operations rawdata_fops = {
+static const struct file_operations aa_fs_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 **/
-
-/**
- *
- * Requires: @profile->ns->lock held
- */
-void __aafs_profile_rmdir(struct aa_profile *profile)
+void __aa_fs_profile_rmdir(struct aa_profile *profile)
{
struct aa_profile *child;
int i;
@@ -1422,7 +663,7 @@ void __aafs_profile_rmdir(struct aa_profile *profile)
return;
list_for_each_entry(child, &profile->base.profiles, base.list)
- __aafs_profile_rmdir(child);
+ __aa_fs_profile_rmdir(child);
for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) {
struct aa_proxy *proxy;
@@ -1430,25 +671,17 @@ void __aafs_profile_rmdir(struct aa_profile *profile)
continue;
proxy = d_inode(profile->dents[i])->i_private;
- aafs_remove(profile->dents[i]);
+ securityfs_remove(profile->dents[i]);
aa_put_proxy(proxy);
profile->dents[i] = NULL;
}
}
-/**
- *
- * Requires: @old->ns->lock held
- */
-void __aafs_profile_migrate_dents(struct aa_profile *old,
- struct aa_profile *new)
+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])
@@ -1461,137 +694,29 @@ 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->label.proxy);
+ struct aa_proxy *proxy = aa_get_proxy(profile->proxy);
struct dentry *dent;
- dent = aafs_create_file(name, S_IFREG | 0444, dir, proxy, fops);
+ dent = securityfs_create_file(name, S_IFREG | 0444, dir, proxy, fops);
if (IS_ERR(dent))
aa_put_proxy(proxy);
return dent;
}
-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 char *gen_symlink_name(int depth, const char *dirname, const char *fname)
-{
- char *buffer, *s;
- int error;
- int size = depth * 6 + strlen(dirname) + strlen(fname) + 11;
-
- s = buffer = kmalloc(size, GFP_KERNEL);
- if (!buffer)
- return ERR_PTR(-ENOMEM);
-
- for (; depth > 0; depth--) {
- strcpy(s, "../../");
- s += 6;
- size -= 6;
- }
-
- error = snprintf(s, size, "raw_data/%s/%s", dirname, fname);
- if (error >= size || error < 0) {
- kfree(buffer);
- return ERR_PTR(-ENAMETOOLONG);
- }
-
- return buffer;
-}
-
-static void rawdata_link_cb(void *arg)
-{
- kfree(arg);
-}
-
-static const char *rawdata_get_link_base(struct dentry *dentry,
- struct inode *inode,
- struct delayed_call *done,
- const char *name)
-{
- struct aa_proxy *proxy = inode->i_private;
- struct aa_label *label;
- struct aa_profile *profile;
- char *target;
- int depth;
-
- if (!dentry)
- return ERR_PTR(-ECHILD);
-
- label = aa_get_label_rcu(&proxy->label);
- profile = labels_profile(label);
- depth = profile_depth(profile);
- target = gen_symlink_name(depth, profile->rawdata->name, name);
- aa_put_label(label);
-
- if (IS_ERR(target))
- return target;
-
- set_delayed_call(done, rawdata_link_cb, target);
-
- return target;
-}
-
-static const char *rawdata_get_link_sha1(struct dentry *dentry,
- struct inode *inode,
- struct delayed_call *done)
-{
- return rawdata_get_link_base(dentry, inode, done, "sha1");
-}
-
-static const char *rawdata_get_link_abi(struct dentry *dentry,
- struct inode *inode,
- struct delayed_call *done)
-{
- return rawdata_get_link_base(dentry, inode, done, "abi");
-}
-
-static const char *rawdata_get_link_data(struct dentry *dentry,
- struct inode *inode,
- struct delayed_call *done)
-{
- return rawdata_get_link_base(dentry, inode, done, "raw_data");
-}
-
-static const struct inode_operations rawdata_link_sha1_iops = {
- .get_link = rawdata_get_link_sha1,
-};
-
-static const struct inode_operations rawdata_link_abi_iops = {
- .get_link = rawdata_get_link_abi,
-};
-static const struct inode_operations rawdata_link_data_iops = {
- .get_link = rawdata_get_link_data,
-};
-
-
-/*
- * Requires: @profile->ns->lock held
- */
-int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
+/* requires lock be 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);
dent = prof_dir(p);
/* adding to parent that previously didn't have children */
- dent = aafs_create_dir("profiles", dent);
+ dent = securityfs_create_dir("profiles", dent);
if (IS_ERR(dent))
goto fail;
prof_child_dir(p) = parent = dent;
@@ -1603,74 +728,67 @@ int __aafs_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) {
- error = -ENOMEM;
- goto fail2;
- }
+ if (!profile->dirname)
+ goto fail;
mangle_name(profile->base.name, profile->dirname);
sprintf(profile->dirname + len, ".%ld", profile->ns->uniq_id++);
}
- dent = aafs_create_dir(profile->dirname, parent);
+ dent = securityfs_create_dir(profile->dirname, parent);
if (IS_ERR(dent))
goto fail;
prof_dir(profile) = dir = dent;
- dent = create_profile_file(dir, "name", profile,
- &seq_profile_name_fops);
+ dent = create_profile_file(dir, "name", profile, &aa_fs_profname_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_NAME] = dent;
- dent = create_profile_file(dir, "mode", profile,
- &seq_profile_mode_fops);
+ dent = create_profile_file(dir, "mode", profile, &aa_fs_profmode_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_MODE] = dent;
dent = create_profile_file(dir, "attach", profile,
- &seq_profile_attach_fops);
+ &aa_fs_profattach_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_ATTACH] = dent;
if (profile->hash) {
dent = create_profile_file(dir, "sha1", profile,
- &seq_profile_hash_fops);
+ &aa_fs_seq_hash_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_HASH] = dent;
}
if (profile->rawdata) {
- dent = aafs_create_symlink("raw_sha1", dir, NULL,
- profile->label.proxy,
- &rawdata_link_sha1_iops);
+ dent = create_profile_file(dir, "raw_sha1", profile,
+ &aa_fs_seq_raw_hash_fops);
if (IS_ERR(dent))
goto fail;
- aa_get_proxy(profile->label.proxy);
profile->dents[AAFS_PROF_RAW_HASH] = dent;
- dent = aafs_create_symlink("raw_abi", dir, NULL,
- profile->label.proxy,
- &rawdata_link_abi_iops);
+ dent = create_profile_file(dir, "raw_abi", profile,
+ &aa_fs_seq_raw_abi_fops);
if (IS_ERR(dent))
goto fail;
- aa_get_proxy(profile->label.proxy);
profile->dents[AAFS_PROF_RAW_ABI] = dent;
- dent = aafs_create_symlink("raw_data", dir, NULL,
- profile->label.proxy,
- &rawdata_link_data_iops);
+ dent = securityfs_create_file("raw_data", S_IFREG | 0444, dir,
+ profile->proxy,
+ &aa_fs_rawdata_fops);
if (IS_ERR(dent))
goto fail;
- aa_get_proxy(profile->label.proxy);
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 = __aafs_profile_mkdir(child, prof_child_dir(profile));
+ error = __aa_fs_profile_mkdir(child, prof_child_dir(profile));
if (error)
goto fail2;
}
@@ -1681,123 +799,12 @@ fail:
error = PTR_ERR(dent);
fail2:
- __aafs_profile_rmdir(profile);
-
- return error;
-}
-
-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_nested(&parent->lock, parent->level);
- 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_nested(&parent->lock, parent->level);
- 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);
+ __aa_fs_profile_rmdir(profile);
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)
+void __aa_fs_ns_rmdir(struct aa_ns *ns)
{
struct aa_ns *sub;
struct aa_profile *child;
@@ -1805,19 +812,16 @@ void __aafs_ns_rmdir(struct aa_ns *ns)
if (!ns)
return;
- AA_BUG(!mutex_is_locked(&ns->lock));
list_for_each_entry(child, &ns->base.profiles, base.list)
- __aafs_profile_rmdir(child);
+ __aa_fs_profile_rmdir(child);
list_for_each_entry(sub, &ns->sub_ns, base.list) {
- mutex_lock_nested(&sub->lock, sub->level);
- __aafs_ns_rmdir(sub);
+ mutex_lock(&sub->lock);
+ __aa_fs_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);
@@ -1834,66 +838,53 @@ void __aafs_ns_rmdir(struct aa_ns *ns)
sub = d_inode(ns_subremove(ns))->i_private;
aa_put_ns(sub);
}
- if (ns_subrevision(ns)) {
- sub = d_inode(ns_subrevision(ns))->i_private;
- aa_put_ns(sub);
- }
for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) {
- aafs_remove(ns->dents[i]);
+ securityfs_remove(ns->dents[i]);
ns->dents[i] = NULL;
}
}
/* assumes cleanup in caller */
-static int __aafs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
+static int __aa_fs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
{
struct dentry *dent;
AA_BUG(!ns);
AA_BUG(!dir);
- dent = aafs_create_dir("profiles", dir);
+ dent = securityfs_create_dir("profiles", dir);
if (IS_ERR(dent))
return PTR_ERR(dent);
ns_subprofs_dir(ns) = dent;
- dent = aafs_create_dir("raw_data", dir);
+ dent = securityfs_create_dir("raw_data", dir);
if (IS_ERR(dent))
return PTR_ERR(dent);
ns_subdata_dir(ns) = dent;
- dent = aafs_create_file("revision", 0444, dir, ns,
- &aa_fs_ns_revision_fops);
- if (IS_ERR(dent))
- return PTR_ERR(dent);
- aa_get_ns(ns);
- ns_subrevision(ns) = dent;
-
- dent = aafs_create_file(".load", 0640, dir, ns,
+ dent = securityfs_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 = aafs_create_file(".replace", 0640, dir, ns,
+ dent = securityfs_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 = aafs_create_file(".remove", 0640, dir, ns,
+ dent = securityfs_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;
- /* use create_dentry so we can supply private data */
- dent = aafs_create("namespaces", S_IFDIR | 0755, dir, ns, NULL, NULL,
- &ns_dir_inode_operations);
+ dent = securityfs_create_dir("namespaces", dir);
if (IS_ERR(dent))
return PTR_ERR(dent);
aa_get_ns(ns);
@@ -1902,15 +893,11 @@ static int __aafs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
return 0;
}
-/*
- * Requires: @ns->lock held
- */
-int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
- struct dentry *dent)
+int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name)
{
struct aa_ns *sub;
struct aa_profile *child;
- struct dentry *dir;
+ struct dentry *dent, *dir;
int error;
AA_BUG(!ns);
@@ -1920,29 +907,27 @@ int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
if (!name)
name = ns->base.name;
- 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);
+ /* create ns dir if it doesn't already exist */
+ dent = securityfs_create_dir(name, parent);
+ if (IS_ERR(dent))
+ goto fail;
+
ns_dir(ns) = dir = dent;
- error = __aafs_ns_mkdir_entries(ns, dir);
+ error = __aa_fs_ns_mkdir_entries(ns, dir);
if (error)
goto fail2;
/* profiles */
list_for_each_entry(child, &ns->base.profiles, base.list) {
- error = __aafs_profile_mkdir(child, ns_subprofs_dir(ns));
+ error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns));
if (error)
goto fail2;
}
/* subnamespaces */
list_for_each_entry(sub, &ns->sub_ns, base.list) {
- mutex_lock_nested(&sub->lock, sub->level);
- error = __aafs_ns_mkdir(sub, ns_subns_dir(ns), NULL, NULL);
+ mutex_lock(&sub->lock);
+ error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL);
mutex_unlock(&sub->lock);
if (error)
goto fail2;
@@ -1954,7 +939,7 @@ fail:
error = PTR_ERR(dent);
fail2:
- __aafs_ns_rmdir(ns);
+ __aa_fs_ns_rmdir(ns);
return error;
}
@@ -1978,14 +963,10 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
{
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)) {
next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
- mutex_lock_nested(&next->lock, next->level);
+ mutex_lock(&next->lock);
return next;
}
@@ -1995,7 +976,7 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
mutex_unlock(&ns->lock);
next = list_next_entry(ns, base.list);
if (!list_entry_is_head(next, &parent->sub_ns, base.list)) {
- mutex_lock_nested(&next->lock, next->level);
+ mutex_lock(&next->lock);
return next;
}
ns = parent;
@@ -2016,9 +997,6 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
static struct aa_profile *__first_profile(struct aa_ns *root,
struct aa_ns *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,
@@ -2041,8 +1019,6 @@ static struct aa_profile *__next_profile(struct aa_profile *p)
struct aa_profile *parent;
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))
return list_first_entry(&p->base.profiles, typeof(*p),
@@ -2098,12 +1074,13 @@ 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_get_current_ns();
+ struct aa_ns *root = aa_current_profile()->ns;
loff_t l = *pos;
- f->private = root;
+ f->private = aa_get_ns(root);
+
/* find the first profile */
- mutex_lock_nested(&root->lock, root->level);
+ mutex_lock(&root->lock);
profile = __first_profile(root, root);
/* skip to position */
@@ -2164,14 +1141,15 @@ 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;
- aa_label_seq_xprint(f, root, &profile->label,
- FLAG_SHOW_MODE | FLAG_VIEW_SUBNS, GFP_KERNEL);
- seq_putc(f, '\n');
+ 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]);
return 0;
}
-static const struct seq_operations aa_sfs_profiles_op = {
+static const struct seq_operations aa_fs_profiles_op = {
.start = p_start,
.next = p_next,
.stop = p_stop,
@@ -2183,7 +1161,7 @@ static int profiles_open(struct inode *inode, struct file *file)
if (!policy_view_capable(NULL))
return -EACCES;
- return seq_open(file, &aa_sfs_profiles_op);
+ return seq_open(file, &aa_fs_profiles_op);
}
static int profiles_release(struct inode *inode, struct file *file)
@@ -2191,7 +1169,7 @@ static int profiles_release(struct inode *inode, struct file *file)
return seq_release(inode, file);
}
-static const struct file_operations aa_sfs_profiles_fops = {
+static const struct file_operations aa_fs_profiles_fops = {
.open = profiles_open,
.read = seq_read,
.llseek = seq_lseek,
@@ -2200,107 +1178,65 @@ static const struct file_operations aa_sfs_profiles_fops = {
/** Base file system setup **/
-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_signal[] = {
- AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK),
- { }
-};
-
-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_file[] = {
+ AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \
+ "link lock"),
{ }
};
-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_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_mount[] = {
- AA_SFS_FILE_STRING("mask", "mount umount pivot_root"),
+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", 0),
+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_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("network", aa_sfs_entry_network),
- AA_SFS_DIR("mount", aa_sfs_entry_mount),
- 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("signal", aa_sfs_entry_signal),
- AA_SFS_DIR("query", aa_sfs_entry_query),
+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_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_apparmor[] = {
- AA_SFS_FILE_FOPS(".access", 0666, &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", 0444, &seq_ns_level_fops),
- AA_SFS_FILE_FOPS(".ns_name", 0444, &seq_ns_name_fops),
- AA_SFS_FILE_FOPS("profiles", 0444, &aa_sfs_profiles_fops),
- AA_SFS_DIR("features", aa_sfs_entry_features),
+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 =
- AA_SFS_DIR("apparmor", aa_sfs_entry_apparmor);
+static struct aa_fs_entry aa_fs_entry =
+ AA_FS_DIR("apparmor", aa_fs_entry_apparmor);
/**
- * entry_create_file - create a file entry in the apparmor securityfs
- * @fs_file: aa_sfs_entry to build an entry for (NOT NULL)
+ * aafs_create_file - create a file entry in the apparmor securityfs
+ * @fs_file: aa_fs_entry to build an entry for (NOT NULL)
* @parent: the parent dentry in the securityfs
*
- * Use entry_remove_file to remove entries created with this fn.
+ * Use aafs_remove_file to remove entries created with this fn.
*/
-static int __init entry_create_file(struct aa_sfs_entry *fs_file,
- struct dentry *parent)
+static int __init aafs_create_file(struct aa_fs_entry *fs_file,
+ struct dentry *parent)
{
int error = 0;
@@ -2315,18 +1251,18 @@ static int __init entry_create_file(struct aa_sfs_entry *fs_file,
return error;
}
-static void __init entry_remove_dir(struct aa_sfs_entry *fs_dir);
+static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir);
/**
- * entry_create_dir - recursively create a directory entry in the securityfs
- * @fs_dir: aa_sfs_entry (and all child entries) to build (NOT NULL)
+ * aafs_create_dir - recursively create a directory entry in the securityfs
+ * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL)
* @parent: the parent dentry in the securityfs
*
- * Use entry_remove_dir to remove entries created with this fn.
+ * Use aafs_remove_dir to remove entries created with this fn.
*/
-static int __init entry_create_dir(struct aa_sfs_entry *fs_dir,
- struct dentry *parent)
+static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
+ struct dentry *parent)
{
- struct aa_sfs_entry *fs_file;
+ struct aa_fs_entry *fs_file;
struct dentry *dir;
int error;
@@ -2336,10 +1272,10 @@ static int __init entry_create_dir(struct aa_sfs_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_SFS_TYPE_DIR)
- error = entry_create_dir(fs_file, fs_dir->dentry);
+ if (fs_file->v_type == AA_FS_TYPE_DIR)
+ error = aafs_create_dir(fs_file, fs_dir->dentry);
else
- error = entry_create_file(fs_file, fs_dir->dentry);
+ error = aafs_create_file(fs_file, fs_dir->dentry);
if (error)
goto failed;
}
@@ -2347,16 +1283,16 @@ static int __init entry_create_dir(struct aa_sfs_entry *fs_dir,
return 0;
failed:
- entry_remove_dir(fs_dir);
+ aafs_remove_dir(fs_dir);
return error;
}
/**
- * entry_remove_file - drop a single file entry in the apparmor securityfs
- * @fs_file: aa_sfs_entry to detach from the securityfs (NOT NULL)
+ * aafs_remove_file - drop a single file entry in the apparmor securityfs
+ * @fs_file: aa_fs_entry to detach from the securityfs (NOT NULL)
*/
-static void __init entry_remove_file(struct aa_sfs_entry *fs_file)
+static void __init aafs_remove_file(struct aa_fs_entry *fs_file)
{
if (!fs_file->dentry)
return;
@@ -2366,21 +1302,21 @@ static void __init entry_remove_file(struct aa_sfs_entry *fs_file)
}
/**
- * entry_remove_dir - recursively drop a directory entry from the securityfs
- * @fs_dir: aa_sfs_entry (and all child entries) to detach (NOT NULL)
+ * aafs_remove_dir - recursively drop a directory entry from the securityfs
+ * @fs_dir: aa_fs_entry (and all child entries) to detach (NOT NULL)
*/
-static void __init entry_remove_dir(struct aa_sfs_entry *fs_dir)
+static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir)
{
- struct aa_sfs_entry *fs_file;
+ struct aa_fs_entry *fs_file;
for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
- if (fs_file->v_type == AA_SFS_TYPE_DIR)
- entry_remove_dir(fs_file);
+ if (fs_file->v_type == AA_FS_TYPE_DIR)
+ aafs_remove_dir(fs_file);
else
- entry_remove_file(fs_file);
+ aafs_remove_file(fs_file);
}
- entry_remove_file(fs_dir);
+ aafs_remove_file(fs_dir);
}
/**
@@ -2390,7 +1326,7 @@ static void __init entry_remove_dir(struct aa_sfs_entry *fs_dir)
*/
void __init aa_destroy_aafs(void)
{
- entry_remove_dir(&aa_sfs_entry);
+ aafs_remove_dir(&aa_fs_entry);
}
@@ -2439,59 +1375,6 @@ 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
*
@@ -2507,23 +1390,17 @@ static int __init aa_create_aafs(void)
if (!apparmor_initialized)
return 0;
- if (aa_sfs_entry.dentry) {
+ if (aa_fs_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 = entry_create_dir(&aa_sfs_entry, NULL);
+ error = aafs_create_dir(&aa_fs_entry, NULL);
if (error)
goto error;
- dent = securityfs_create_file(".load", 0666, aa_sfs_entry.dentry,
+ dent = securityfs_create_file(".load", 0666, aa_fs_entry.dentry,
NULL, &aa_fs_profile_load);
if (IS_ERR(dent)) {
error = PTR_ERR(dent);
@@ -2531,7 +1408,7 @@ static int __init aa_create_aafs(void)
}
ns_subload(root_ns) = dent;
- dent = securityfs_create_file(".replace", 0666, aa_sfs_entry.dentry,
+ dent = securityfs_create_file(".replace", 0666, aa_fs_entry.dentry,
NULL, &aa_fs_profile_replace);
if (IS_ERR(dent)) {
error = PTR_ERR(dent);
@@ -2539,7 +1416,7 @@ static int __init aa_create_aafs(void)
}
ns_subreplace(root_ns) = dent;
- dent = securityfs_create_file(".remove", 0666, aa_sfs_entry.dentry,
+ dent = securityfs_create_file(".remove", 0666, aa_fs_entry.dentry,
NULL, &aa_fs_profile_remove);
if (IS_ERR(dent)) {
error = PTR_ERR(dent);
@@ -2547,31 +1424,14 @@ 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_nested(&root_ns->lock, root_ns->level);
- error = __aafs_ns_mkdir(root_ns, aafs_mnt->mnt_root, ".policy",
- aafs_mnt->mnt_root);
+ 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;
- /* 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);
+ if (error)
goto error;
- }
- error = aa_mk_null_file(aa_sfs_entry.dentry);
+ error = aa_mk_null_file(aa_fs_entry.dentry);
if (error)
goto error;
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
index 8f9ecac7f8de..87f40fa8c431 100644
--- a/security/apparmor/audit.c
+++ b/security/apparmor/audit.c
@@ -77,24 +77,14 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
audit_log_format(ab, " error=%d", aad(sa)->error);
}
- if (aad(sa)->label) {
- struct aa_label *label = aad(sa)->label;
-
- if (label_isprofile(label)) {
- struct aa_profile *profile = labels_profile(label);
-
- if (profile->ns != root_ns) {
- audit_log_format(ab, " namespace=");
- audit_log_untrustedstring(ab,
- profile->ns->base.hname);
- }
- audit_log_format(ab, " profile=");
- audit_log_untrustedstring(ab, profile->base.hname);
- } else {
- audit_log_format(ab, " label=");
- aa_label_xaudit(ab, root_ns, label, FLAG_VIEW_SUBNS,
- GFP_ATOMIC);
+ if (aad(sa)->profile) {
+ struct aa_profile *profile = aad(sa)->profile;
+ if (profile->ns != root_ns) {
+ audit_log_format(ab, " namespace=");
+ audit_log_untrustedstring(ab, profile->ns->base.hname);
}
+ audit_log_format(ab, " profile=");
+ audit_log_untrustedstring(ab, profile->base.hname);
}
if (aad(sa)->name) {
@@ -149,7 +139,8 @@ int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa,
if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED)
type = AUDIT_APPARMOR_KILL;
- aad(sa)->label = &profile->label;
+ if (!unconfined(profile))
+ aad(sa)->profile = profile;
aa_audit_msg(type, sa, cb);
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c
index 67e347192a55..ed0a3e6b8022 100644
--- a/security/apparmor/capability.c
+++ b/security/apparmor/capability.c
@@ -28,8 +28,8 @@
*/
#include "capability_names.h"
-struct aa_sfs_entry aa_sfs_entry_caps[] = {
- AA_SFS_FILE_STRING("mask", AA_SFS_CAPS_MASK),
+struct aa_fs_entry aa_fs_entry_caps[] = {
+ AA_FS_FILE_STRING("mask", AA_FS_CAPS_MASK),
{ }
};
@@ -48,16 +48,15 @@ static DEFINE_PER_CPU(struct audit_cache, audit_cache);
static void audit_cb(struct audit_buffer *ab, void *va)
{
struct common_audit_data *sa = va;
-
audit_log_format(ab, " capname=");
audit_log_untrustedstring(ab, capability_names[sa->u.cap]);
}
/**
* audit_caps - audit a capability
- * @sa: audit data
* @profile: profile being tested for confinement (NOT NULL)
* @cap: capability tested
+ @audit: whether an audit record should be generated
* @error: error code returned by test
*
* Do auditing of capability and handle, audit/complain/kill modes switching
@@ -65,13 +64,16 @@ static void audit_cb(struct audit_buffer *ab, void *va)
*
* Returns: 0 or sa->error on success, error code on failure
*/
-static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
- int cap, int error)
+static int audit_caps(struct aa_profile *profile, int cap, int audit,
+ int error)
{
struct audit_cache *ent;
int type = AUDIT_APPARMOR_AUTO;
-
- aad(sa)->error = error;
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE);
+ sa.u.cap = cap;
+ aad(&sa)->error = error;
+ if (audit == SECURITY_CAP_NOAUDIT)
+ aad(&sa)->info = "optional: no audit";
if (likely(!error)) {
/* test if auditing is being forced */
@@ -103,44 +105,24 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
}
put_cpu_var(audit_cache);
- return aa_audit(type, profile, sa, audit_cb);
+ return aa_audit(type, profile, &sa, audit_cb);
}
/**
* profile_capable - test if profile allows use of capability @cap
* @profile: profile being enforced (NOT NULL, NOT unconfined)
* @cap: capability to test if allowed
- * @audit: whether an audit record should be generated
- * @sa: audit data (MAY BE NULL indicating no auditing)
*
* Returns: 0 if allowed else -EPERM
*/
-static int profile_capable(struct aa_profile *profile, int cap, int audit,
- struct common_audit_data *sa)
+static int profile_capable(struct aa_profile *profile, int cap)
{
- int error;
-
- if (cap_raised(profile->caps.allow, cap) &&
- !cap_raised(profile->caps.denied, cap))
- error = 0;
- else
- error = -EPERM;
-
- if (audit == SECURITY_CAP_NOAUDIT) {
- if (!COMPLAIN_MODE(profile))
- return error;
- /* audit the cap request in complain mode but note that it
- * should be optional.
- */
- aad(sa)->info = "optional: no audit";
- }
-
- return audit_caps(sa, profile, cap, error);
+ return cap_raised(profile->caps.allow, cap) ? 0 : -EPERM;
}
/**
* aa_capable - test permission to use capability
- * @label: label being tested for capability (NOT NULL)
+ * @profile: profile being tested against (NOT NULL)
* @cap: capability to be tested
* @audit: whether an audit record should be generated
*
@@ -148,15 +130,14 @@ static int profile_capable(struct aa_profile *profile, int cap, int audit,
*
* Returns: 0 on success, or else an error code.
*/
-int aa_capable(struct aa_label *label, int cap, int audit)
+int aa_capable(struct aa_profile *profile, int cap, int audit)
{
- struct aa_profile *profile;
- int error = 0;
- DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE);
+ int error = profile_capable(profile, cap);
- sa.u.cap = cap;
- error = fn_for_each_confined(label, profile,
- profile_capable(profile, cap, audit, &sa));
+ if (audit == SECURITY_CAP_NOAUDIT) {
+ if (!COMPLAIN_MODE(profile))
+ return error;
+ }
- return error;
+ return audit_caps(profile, cap, audit, error);
}
diff --git a/security/apparmor/context.c b/security/apparmor/context.c
index c95f1ac6190b..1fc16b88efbf 100644
--- a/security/apparmor/context.c
+++ b/security/apparmor/context.c
@@ -14,9 +14,9 @@
*
*
* AppArmor sets confinement on every task, via the the aa_task_ctx and
- * the aa_task_ctx.label, both of which are required and are not allowed
+ * the aa_task_ctx.profile, both of which are required and are not allowed
* to be NULL. The aa_task_ctx is not reference counted and is unique
- * to each cred (which is reference count). The label pointed to by
+ * to each cred (which is reference count). The profile pointed to by
* the task_ctx is reference counted.
*
* TODO
@@ -47,9 +47,9 @@ struct aa_task_ctx *aa_alloc_task_context(gfp_t flags)
void aa_free_task_context(struct aa_task_ctx *ctx)
{
if (ctx) {
- aa_put_label(ctx->label);
- aa_put_label(ctx->previous);
- aa_put_label(ctx->onexec);
+ aa_put_profile(ctx->profile);
+ aa_put_profile(ctx->previous);
+ aa_put_profile(ctx->onexec);
kzfree(ctx);
}
@@ -63,41 +63,41 @@ void aa_free_task_context(struct aa_task_ctx *ctx)
void aa_dup_task_context(struct aa_task_ctx *new, const struct aa_task_ctx *old)
{
*new = *old;
- aa_get_label(new->label);
- aa_get_label(new->previous);
- aa_get_label(new->onexec);
+ aa_get_profile(new->profile);
+ aa_get_profile(new->previous);
+ aa_get_profile(new->onexec);
}
/**
- * aa_get_task_label - Get another task's label
+ * aa_get_task_profile - Get another task's profile
* @task: task to query (NOT NULL)
*
- * Returns: counted reference to @task's label
+ * Returns: counted reference to @task's profile
*/
-struct aa_label *aa_get_task_label(struct task_struct *task)
+struct aa_profile *aa_get_task_profile(struct task_struct *task)
{
- struct aa_label *p;
+ struct aa_profile *p;
rcu_read_lock();
- p = aa_get_newest_label(__aa_task_raw_label(task));
+ p = aa_get_profile(__aa_task_profile(task));
rcu_read_unlock();
return p;
}
/**
- * aa_replace_current_label - replace the current tasks label
- * @label: new label (NOT NULL)
+ * aa_replace_current_profile - replace the current tasks profiles
+ * @profile: new profile (NOT NULL)
*
* Returns: 0 or error on failure
*/
-int aa_replace_current_label(struct aa_label *label)
+int aa_replace_current_profile(struct aa_profile *profile)
{
struct aa_task_ctx *ctx = current_ctx();
struct cred *new;
- AA_BUG(!label);
+ AA_BUG(!profile);
- if (ctx->label == label)
+ if (ctx->profile == profile)
return 0;
if (current_cred() != current_real_cred())
@@ -108,8 +108,8 @@ int aa_replace_current_label(struct aa_label *label)
return -ENOMEM;
ctx = cred_ctx(new);
- if (unconfined(label) || (labels_ns(ctx->label) != labels_ns(label)))
- /* if switching to unconfined or a different label namespace
+ if (unconfined(profile) || (ctx->profile->ns != profile->ns))
+ /* if switching to unconfined or a different profile namespace
* clear out context state
*/
aa_clear_task_ctx_trans(ctx);
@@ -120,9 +120,9 @@ int aa_replace_current_label(struct aa_label *label)
* keeping @profile valid, so make sure to get its reference before
* dropping the reference on ctx->profile
*/
- aa_get_label(label);
- aa_put_label(ctx->label);
- ctx->label = label;
+ aa_get_profile(profile);
+ aa_put_profile(ctx->profile);
+ ctx->profile = profile;
commit_creds(new);
return 0;
@@ -130,11 +130,11 @@ int aa_replace_current_label(struct aa_label *label)
/**
* aa_set_current_onexec - set the tasks change_profile to happen onexec
- * @label: system label to set at exec (MAYBE NULL to clear value)
- * @stack: whether stacking should be done
+ * @profile: system profile to set at exec (MAYBE NULL to clear value)
+ *
* Returns: 0 or error on failure
*/
-int aa_set_current_onexec(struct aa_label *label, bool stack)
+int aa_set_current_onexec(struct aa_profile *profile)
{
struct aa_task_ctx *ctx;
struct cred *new = prepare_creds();
@@ -142,10 +142,9 @@ int aa_set_current_onexec(struct aa_label *label, bool stack)
return -ENOMEM;
ctx = cred_ctx(new);
- aa_get_label(label);
- aa_clear_task_ctx_trans(ctx);
- ctx->onexec = label;
- ctx->token = stack;
+ aa_get_profile(profile);
+ aa_put_profile(ctx->onexec);
+ ctx->onexec = profile;
commit_creds(new);
return 0;
@@ -153,7 +152,7 @@ int aa_set_current_onexec(struct aa_label *label, bool stack)
/**
* aa_set_current_hat - set the current tasks hat
- * @label: label to set as the current hat (NOT NULL)
+ * @profile: profile to set as the current hat (NOT NULL)
* @token: token value that must be specified to change from the hat
*
* Do switch of tasks hat. If the task is currently in a hat
@@ -161,29 +160,29 @@ int aa_set_current_onexec(struct aa_label *label, bool stack)
*
* Returns: 0 or error on failure
*/
-int aa_set_current_hat(struct aa_label *label, u64 token)
+int aa_set_current_hat(struct aa_profile *profile, u64 token)
{
struct aa_task_ctx *ctx;
struct cred *new = prepare_creds();
if (!new)
return -ENOMEM;
- AA_BUG(!label);
+ AA_BUG(!profile);
ctx = cred_ctx(new);
if (!ctx->previous) {
/* transfer refcount */
- ctx->previous = ctx->label;
+ ctx->previous = ctx->profile;
ctx->token = token;
} else if (ctx->token == token) {
- aa_put_label(ctx->label);
+ aa_put_profile(ctx->profile);
} else {
/* previous_profile && ctx->token != token */
abort_creds(new);
return -EACCES;
}
- ctx->label = aa_get_newest_label(label);
+ ctx->profile = aa_get_newest_profile(profile);
/* clear exec on switching context */
- aa_put_label(ctx->onexec);
+ aa_put_profile(ctx->onexec);
ctx->onexec = NULL;
commit_creds(new);
@@ -191,15 +190,15 @@ int aa_set_current_hat(struct aa_label *label, u64 token)
}
/**
- * aa_restore_previous_label - exit from hat context restoring previous label
+ * aa_restore_previous_profile - exit from hat context restoring the profile
* @token: the token that must be matched to exit hat context
*
- * Attempt to return out of a hat to the previous label. The token
+ * Attempt to return out of a hat to the previous profile. The token
* must match the stored token value.
*
* Returns: 0 or error of failure
*/
-int aa_restore_previous_label(u64 token)
+int aa_restore_previous_profile(u64 token)
{
struct aa_task_ctx *ctx;
struct cred *new = prepare_creds();
@@ -211,15 +210,15 @@ int aa_restore_previous_label(u64 token)
abort_creds(new);
return -EACCES;
}
- /* ignore restores when there is no saved label */
+ /* ignore restores when there is no saved profile */
if (!ctx->previous) {
abort_creds(new);
return 0;
}
- aa_put_label(ctx->label);
- ctx->label = aa_get_newest_label(ctx->previous);
- AA_BUG(!ctx->label);
+ aa_put_profile(ctx->profile);
+ ctx->profile = aa_get_newest_profile(ctx->previous);
+ AA_BUG(!ctx->profile);
/* clear exec && prev information when restoring to previous context */
aa_clear_task_ctx_trans(ctx);
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index ad780473706b..001e133a3c8c 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -51,261 +51,82 @@ void aa_free_domain_entries(struct aa_domain *domain)
/**
* may_change_ptraced_domain - check if can change profile on ptraced task
- * @to_label: profile to change to (NOT NULL)
- * @info: message if there is an error
+ * @to_profile: profile to change to (NOT NULL)
*
* Check if current is ptraced and if so if the tracing task is allowed
* to trace the new domain
*
* Returns: %0 or error if change not allowed
*/
-static int may_change_ptraced_domain(struct aa_label *to_label,
- const char **info)
+static int may_change_ptraced_domain(struct aa_profile *to_profile)
{
struct task_struct *tracer;
- struct aa_label *tracerl = NULL;
+ struct aa_profile *tracerp = NULL;
int error = 0;
rcu_read_lock();
tracer = ptrace_parent(current);
if (tracer)
/* released below */
- tracerl = aa_get_task_label(tracer);
+ tracerp = aa_get_task_profile(tracer);
/* not ptraced */
- if (!tracer || unconfined(tracerl))
+ if (!tracer || unconfined(tracerp))
goto out;
- error = aa_may_ptrace(tracerl, to_label, PTRACE_MODE_ATTACH);
+ error = aa_may_ptrace(tracerp, to_profile, PTRACE_MODE_ATTACH);
out:
rcu_read_unlock();
- aa_put_label(tracerl);
+ aa_put_profile(tracerp);
- if (error)
- *info = "ptrace prevents transition";
return error;
}
-/**** TODO: dedup to aa_label_match - needs perm and dfa, merging
- * specifically this is an exact copy of aa_label_match except
- * aa_compute_perms is replaced with aa_compute_fperms
- * and policy.dfa with file.dfa
- ****/
-/* match a profile and its associated ns component if needed
- * Assumes visibility test has already been done.
- * If a subns profile is not to be matched should be prescreened with
- * visibility test.
- */
-static inline unsigned int match_component(struct aa_profile *profile,
- struct aa_profile *tp,
- bool stack, unsigned int state)
-{
- const char *ns_name;
-
- if (stack)
- state = aa_dfa_match(profile->file.dfa, state, "&");
- if (profile->ns == tp->ns)
- return aa_dfa_match(profile->file.dfa, state, tp->base.hname);
-
- /* try matching with namespace name and then profile */
- ns_name = aa_ns_name(profile->ns, tp->ns, true);
- state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
- state = aa_dfa_match(profile->file.dfa, state, ns_name);
- state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
- return aa_dfa_match(profile->file.dfa, state, tp->base.hname);
-}
-
-/**
- * label_compound_match - find perms for full compound label
- * @profile: profile to find perms for
- * @label: label to check access permissions for
- * @stack: whether this is a stacking request
- * @start: state to start match in
- * @subns: whether to do permission checks on components in a subns
- * @request: permissions to request
- * @perms: perms struct to set
- *
- * Returns: 0 on success else ERROR
- *
- * For the label A//&B//&C this does the perm match for A//&B//&C
- * @perms should be preinitialized with allperms OR a previous permission
- * check to be stacked.
- */
-static int label_compound_match(struct aa_profile *profile,
- struct aa_label *label, bool stack,
- unsigned int state, bool subns, u32 request,
- struct aa_perms *perms)
-{
- struct aa_profile *tp;
- struct label_it i;
- struct path_cond cond = { };
-
- /* find first subcomponent that is visible */
- label_for_each(i, label, tp) {
- if (!aa_ns_visible(profile->ns, tp->ns, subns))
- continue;
- state = match_component(profile, tp, stack, state);
- if (!state)
- goto fail;
- goto next;
- }
-
- /* no component visible */
- *perms = allperms;
- return 0;
-
-next:
- label_for_each_cont(i, label, tp) {
- if (!aa_ns_visible(profile->ns, tp->ns, subns))
- continue;
- state = aa_dfa_match(profile->file.dfa, state, "//&");
- state = match_component(profile, tp, false, state);
- if (!state)
- goto fail;
- }
- *perms = aa_compute_fperms(profile->file.dfa, state, &cond);
- aa_apply_modes_to_perms(profile, perms);
- if ((perms->allow & request) != request)
- return -EACCES;
-
- return 0;
-
-fail:
- *perms = nullperms;
- return -EACCES;
-}
-
-/**
- * label_components_match - find perms for all subcomponents of a label
- * @profile: profile to find perms for
- * @label: label to check access permissions for
- * @stack: whether this is a stacking request
- * @start: state to start match in
- * @subns: whether to do permission checks on components in a subns
- * @request: permissions to request
- * @perms: an initialized perms struct to add accumulation to
- *
- * Returns: 0 on success else ERROR
- *
- * For the label A//&B//&C this does the perm match for each of A and B and C
- * @perms should be preinitialized with allperms OR a previous permission
- * check to be stacked.
- */
-static int label_components_match(struct aa_profile *profile,
- struct aa_label *label, bool stack,
- unsigned int start, bool subns, u32 request,
- struct aa_perms *perms)
-{
- struct aa_profile *tp;
- struct label_it i;
- struct aa_perms tmp;
- struct path_cond cond = { };
- unsigned int state = 0;
-
- /* find first subcomponent to test */
- label_for_each(i, label, tp) {
- if (!aa_ns_visible(profile->ns, tp->ns, subns))
- continue;
- state = match_component(profile, tp, stack, start);
- if (!state)
- goto fail;
- goto next;
- }
-
- /* no subcomponents visible - no change in perms */
- return 0;
-
-next:
- tmp = aa_compute_fperms(profile->file.dfa, state, &cond);
- aa_apply_modes_to_perms(profile, &tmp);
- aa_perms_accum(perms, &tmp);
- label_for_each_cont(i, label, tp) {
- if (!aa_ns_visible(profile->ns, tp->ns, subns))
- continue;
- state = match_component(profile, tp, stack, start);
- if (!state)
- goto fail;
- tmp = aa_compute_fperms(profile->file.dfa, state, &cond);
- aa_apply_modes_to_perms(profile, &tmp);
- aa_perms_accum(perms, &tmp);
- }
-
- if ((perms->allow & request) != request)
- return -EACCES;
-
- return 0;
-
-fail:
- *perms = nullperms;
- return -EACCES;
-}
-
-/**
- * label_match - do a multi-component label match
- * @profile: profile to match against (NOT NULL)
- * @label: label to match (NOT NULL)
- * @stack: whether this is a stacking request
- * @state: state to start in
- * @subns: whether to match subns components
- * @request: permission request
- * @perms: Returns computed perms (NOT NULL)
- *
- * Returns: the state the match finished in, may be the none matching state
- */
-static int label_match(struct aa_profile *profile, struct aa_label *label,
- bool stack, unsigned int state, bool subns, u32 request,
- struct aa_perms *perms)
-{
- int error;
-
- *perms = nullperms;
- error = label_compound_match(profile, label, stack, state, subns,
- request, perms);
- if (!error)
- return error;
-
- *perms = allperms;
- return label_components_match(profile, label, stack, state, subns,
- request, perms);
-}
-
-/******* end TODO: dedup *****/
-
/**
* change_profile_perms - find permissions for change_profile
* @profile: the current profile (NOT NULL)
- * @target: label to transition to (NOT NULL)
- * @stack: whether this is a stacking request
+ * @ns: the namespace being switched to (NOT NULL)
+ * @name: the name of the profile to change to (NOT NULL)
* @request: requested perms
* @start: state to start matching in
*
- *
* Returns: permission set
- *
- * currently only matches full label A//&B//&C or individual components A, B, C
- * not arbitrary combinations. Eg. A//&B, C
*/
-static int change_profile_perms(struct aa_profile *profile,
- struct aa_label *target, bool stack,
- u32 request, unsigned int start,
- struct aa_perms *perms)
+static struct file_perms change_profile_perms(struct aa_profile *profile,
+ struct aa_ns *ns,
+ const char *name, u32 request,
+ unsigned int start)
{
- if (profile_unconfined(profile)) {
- perms->allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC;
- perms->audit = perms->quiet = perms->kill = 0;
- return 0;
+ struct file_perms perms;
+ struct path_cond cond = { };
+ unsigned int state;
+
+ if (unconfined(profile)) {
+ perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC;
+ perms.audit = perms.quiet = perms.kill = 0;
+ return perms;
+ } else if (!profile->file.dfa) {
+ return nullperms;
+ } else if ((ns == profile->ns)) {
+ /* try matching against rules with out namespace prepended */
+ aa_str_perms(profile->file.dfa, start, name, &cond, &perms);
+ if (COMBINED_PERM_MASK(perms) & request)
+ return perms;
}
- /* TODO: add profile in ns screening */
- return label_match(profile, target, stack, start, true, request, perms);
+ /* try matching with namespace name and then profile */
+ state = aa_dfa_match(profile->file.dfa, start, ns->base.name);
+ state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
+ aa_str_perms(profile->file.dfa, state, name, &cond, &perms);
+
+ return perms;
}
/**
* __attach_match_ - find an attachment match
* @name - to match against (NOT NULL)
* @head - profile list to walk (NOT NULL)
- * @info - info message if there was an error (NOT NULL)
*
* Do a linear search on the profiles in the list. There is a matching
* preference where an exact match is preferred over a name which uses
@@ -317,47 +138,28 @@ static int change_profile_perms(struct aa_profile *profile,
* Returns: profile or NULL if no match found
*/
static struct aa_profile *__attach_match(const char *name,
- struct list_head *head,
- const char **info)
+ struct list_head *head)
{
int len = 0;
- bool conflict = false;
struct aa_profile *profile, *candidate = NULL;
list_for_each_entry_rcu(profile, head, base.list) {
- if (profile->label.flags & FLAG_NULL &&
- &profile->label == ns_unconfined(profile->ns))
+ if (profile->flags & PFLAG_NULL)
continue;
-
- if (profile->xmatch) {
- if (profile->xmatch_len >= len) {
- unsigned int state;
- u32 perm;
-
- state = aa_dfa_match(profile->xmatch,
- DFA_START, name);
- perm = dfa_user_allow(profile->xmatch, state);
- /* any accepting state means a valid match. */
- if (perm & MAY_EXEC) {
- if (profile->xmatch_len == len) {
- conflict = true;
- continue;
- }
- candidate = profile;
- len = profile->xmatch_len;
- conflict = false;
- }
+ if (profile->xmatch && profile->xmatch_len > len) {
+ unsigned int state = aa_dfa_match(profile->xmatch,
+ DFA_START, name);
+ u32 perm = dfa_user_allow(profile->xmatch, state);
+ /* any accepting state means a valid match. */
+ if (perm & MAY_EXEC) {
+ candidate = profile;
+ len = profile->xmatch_len;
}
} else if (!strcmp(profile->base.name, name))
/* exact non-re match, no more searching required */
return profile;
}
- if (conflict) {
- *info = "conflicting profile attachments";
- return NULL;
- }
-
return candidate;
}
@@ -366,20 +168,61 @@ static struct aa_profile *__attach_match(const char *name,
* @ns: the current namespace (NOT NULL)
* @list: list to search (NOT NULL)
* @name: the executable name to match against (NOT NULL)
- * @info: info message if there was an error
*
- * Returns: label or NULL if no match found
+ * Returns: profile or NULL if no match found
*/
-static struct aa_label *find_attach(struct aa_ns *ns, struct list_head *list,
- const char *name, const char **info)
+static struct aa_profile *find_attach(struct aa_ns *ns,
+ struct list_head *list, const char *name)
{
struct aa_profile *profile;
rcu_read_lock();
- profile = aa_get_profile(__attach_match(name, list, info));
+ profile = aa_get_profile(__attach_match(name, list));
rcu_read_unlock();
- return profile ? &profile->label : NULL;
+ return profile;
+}
+
+/**
+ * separate_fqname - separate the namespace and profile names
+ * @fqname: the fqname name to split (NOT NULL)
+ * @ns_name: the namespace name if it exists (NOT NULL)
+ *
+ * This is the xtable equivalent routine of aa_split_fqname. It finds the
+ * split in an xtable fqname which contains an embedded \0 instead of a :
+ * if a namespace is specified. This is done so the xtable is constant and
+ * isn't re-split on every lookup.
+ *
+ * Either the profile or namespace name may be optional but if the namespace
+ * is specified the profile name termination must be present. This results
+ * in the following possible encodings:
+ * profile_name\0
+ * :ns_name\0profile_name\0
+ * :ns_name\0\0
+ *
+ * NOTE: the xtable fqname is pre-validated at load time in unpack_trans_table
+ *
+ * Returns: profile name if it is specified else NULL
+ */
+static const char *separate_fqname(const char *fqname, const char **ns_name)
+{
+ const char *name;
+
+ if (fqname[0] == ':') {
+ /* In this case there is guaranteed to be two \0 terminators
+ * in the string. They are verified at load time by
+ * by unpack_trans_table
+ */
+ *ns_name = fqname + 1; /* skip : */
+ name = *ns_name + strlen(*ns_name) + 1;
+ if (!*name)
+ name = NULL;
+ } else {
+ *ns_name = NULL;
+ name = fqname;
+ }
+
+ return name;
}
static const char *next_name(int xtype, const char *name)
@@ -391,489 +234,290 @@ static const char *next_name(int xtype, const char *name)
* x_table_lookup - lookup an x transition name via transition table
* @profile: current profile (NOT NULL)
* @xindex: index into x transition table
- * @name: returns: name tested to find label (NOT NULL)
*
- * Returns: refcounted label, or NULL on failure (MAYBE NULL)
+ * Returns: refcounted profile, or NULL on failure (MAYBE NULL)
*/
-struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
- const char **name)
+static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
{
- struct aa_label *label = NULL;
+ struct aa_profile *new_profile = NULL;
+ struct aa_ns *ns = profile->ns;
u32 xtype = xindex & AA_X_TYPE_MASK;
int index = xindex & AA_X_INDEX_MASK;
-
- AA_BUG(!name);
+ const char *name;
/* index is guaranteed to be in range, validated at load time */
- /* TODO: move lookup parsing to unpack time so this is a straight
- * index into the resultant label
- */
- for (*name = profile->file.trans.table[index]; !label && *name;
- *name = next_name(xtype, *name)) {
+ for (name = profile->file.trans.table[index]; !new_profile && name;
+ name = next_name(xtype, name)) {
+ struct aa_ns *new_ns;
+ const char *xname = NULL;
+
+ new_ns = NULL;
if (xindex & AA_X_CHILD) {
- struct aa_profile *new_profile;
/* release by caller */
- new_profile = aa_find_child(profile, *name);
- if (new_profile)
- label = &new_profile->label;
+ new_profile = aa_find_child(profile, name);
continue;
+ } else if (*name == ':') {
+ /* switching namespace */
+ const char *ns_name;
+ xname = name = separate_fqname(name, &ns_name);
+ if (!xname)
+ /* no name so use profile name */
+ xname = profile->base.hname;
+ if (*ns_name == '@') {
+ /* TODO: variable support */
+ ;
+ }
+ /* released below */
+ new_ns = aa_find_ns(ns, ns_name);
+ if (!new_ns)
+ continue;
+ } else if (*name == '@') {
+ /* TODO: variable support */
+ continue;
+ } else {
+ /* basic namespace lookup */
+ xname = name;
}
- label = aa_label_parse(&profile->label, *name, GFP_ATOMIC,
- true, false);
- if (IS_ERR(label))
- label = NULL;
+
+ /* released by caller */
+ new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname);
+ aa_put_ns(new_ns);
}
/* released by caller */
-
- return label;
+ return new_profile;
}
/**
- * x_to_label - get target label for a given xindex
+ * x_to_profile - get target profile for a given xindex
* @profile: current profile (NOT NULL)
* @name: name to lookup (NOT NULL)
* @xindex: index into x transition table
- * @lookupname: returns: name used in lookup if one was specified (NOT NULL)
*
- * find label for a transition index
+ * find profile for a transition index
*
- * Returns: refcounted label or NULL if not found available
+ * Returns: refcounted profile or NULL if not found available
*/
-static struct aa_label *x_to_label(struct aa_profile *profile,
- const char *name, u32 xindex,
- const char **lookupname,
- const char **info)
+static struct aa_profile *x_to_profile(struct aa_profile *profile,
+ const char *name, u32 xindex)
{
- struct aa_label *new = NULL;
+ struct aa_profile *new_profile = NULL;
struct aa_ns *ns = profile->ns;
u32 xtype = xindex & AA_X_TYPE_MASK;
- const char *stack = NULL;
switch (xtype) {
case AA_X_NONE:
/* fail exec unless ix || ux fallback - handled by caller */
- *lookupname = NULL;
- break;
- case AA_X_TABLE:
- /* TODO: fix when perm mapping done at unload */
- stack = profile->file.trans.table[xindex & AA_X_INDEX_MASK];
- if (*stack != '&') {
- /* released by caller */
- new = x_table_lookup(profile, xindex, lookupname);
- stack = NULL;
- break;
- }
- /* fall through to X_NAME */
+ return NULL;
case AA_X_NAME:
if (xindex & AA_X_CHILD)
/* released by caller */
- new = find_attach(ns, &profile->base.profiles,
- name, info);
+ new_profile = find_attach(ns, &profile->base.profiles,
+ name);
else
/* released by caller */
- new = find_attach(ns, &ns->base.profiles,
- name, info);
- *lookupname = name;
+ new_profile = find_attach(ns, &ns->base.profiles,
+ name);
+ break;
+ case AA_X_TABLE:
+ /* released by caller */
+ new_profile = x_table_lookup(profile, xindex);
break;
- }
-
- if (!new) {
- if (xindex & AA_X_INHERIT) {
- /* (p|c|n)ix - don't change profile but do
- * use the newest version
- */
- *info = "ix fallback";
- /* no profile && no error */
- new = aa_get_newest_label(&profile->label);
- } else if (xindex & AA_X_UNCONFINED) {
- new = aa_get_newest_label(ns_unconfined(profile->ns));
- *info = "ux fallback";
- }
- }
-
- if (new && stack) {
- /* base the stack on post domain transition */
- struct aa_label *base = new;
-
- new = aa_label_parse(base, stack, GFP_ATOMIC, true, false);
- if (IS_ERR(new))
- new = NULL;
- aa_put_label(base);
}
/* released by caller */
- return new;
+ return new_profile;
}
-static struct aa_label *profile_transition(struct aa_profile *profile,
- const struct linux_binprm *bprm,
- char *buffer, struct path_cond *cond,
- bool *secure_exec)
+/**
+ * apparmor_bprm_set_creds - set the new creds on the bprm struct
+ * @bprm: binprm for the exec (NOT NULL)
+ *
+ * Returns: %0 or error on failure
+ */
+int apparmor_bprm_set_creds(struct linux_binprm *bprm)
{
- struct aa_label *new = NULL;
- const char *info = NULL, *name = NULL, *target = NULL;
- unsigned int state = profile->file.start;
- struct aa_perms perms = {};
- bool nonewprivs = false;
+ struct aa_task_ctx *ctx;
+ struct aa_profile *profile, *new_profile = NULL;
+ struct aa_ns *ns;
+ char *buffer = NULL;
+ unsigned int state;
+ struct file_perms perms = {};
+ struct path_cond cond = {
+ file_inode(bprm->file)->i_uid,
+ file_inode(bprm->file)->i_mode
+ };
+ const char *name = NULL, *info = NULL;
int error = 0;
- AA_BUG(!profile);
- AA_BUG(!bprm);
- AA_BUG(!buffer);
+ if (bprm->cred_prepared)
+ return 0;
- error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
- &name, &info, profile->disconnected);
+ ctx = cred_ctx(bprm->cred);
+ AA_BUG(!ctx);
+
+ profile = aa_get_newest_profile(ctx->profile);
+ /*
+ * get the namespace from the replacement profile as replacement
+ * can change the namespace
+ */
+ ns = profile->ns;
+ state = profile->file.start;
+
+ /* buffer freed below, name is pointer into buffer */
+ error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer,
+ &name, &info);
if (error) {
- if (profile_unconfined(profile) ||
- (profile->label.flags & FLAG_IX_ON_NAME_ERROR)) {
- AA_DEBUG("name lookup ix on error");
+ if (unconfined(profile) ||
+ (profile->flags & PFLAG_IX_ON_NAME_ERROR))
error = 0;
- new = aa_get_newest_label(&profile->label);
- }
name = bprm->filename;
goto audit;
}
- if (profile_unconfined(profile)) {
- new = find_attach(profile->ns, &profile->ns->base.profiles,
- name, &info);
- if (new) {
- AA_DEBUG("unconfined attached to new label");
- return new;
- }
- AA_DEBUG("unconfined exec no attachment");
- return aa_get_newest_label(&profile->label);
+ /* Test for onexec first as onexec directives override other
+ * x transitions.
+ */
+ if (unconfined(profile)) {
+ /* unconfined task */
+ if (ctx->onexec)
+ /* change_profile on exec already been granted */
+ new_profile = aa_get_profile(ctx->onexec);
+ else
+ new_profile = find_attach(ns, &ns->base.profiles, name);
+ if (!new_profile)
+ goto cleanup;
+ /*
+ * NOTE: Domain transitions from unconfined are allowed
+ * even when no_new_privs is set because this aways results
+ * in a further reduction of permissions.
+ */
+ goto apply;
}
/* find exec permissions for name */
- state = aa_str_perms(profile->file.dfa, state, name, cond, &perms);
+ state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms);
+ if (ctx->onexec) {
+ struct file_perms cp;
+ info = "change_profile onexec";
+ new_profile = aa_get_newest_profile(ctx->onexec);
+ if (!(perms.allow & AA_MAY_ONEXEC))
+ goto audit;
+
+ /* test if this exec can be paired with change_profile onexec.
+ * onexec permission is linked to exec with a standard pairing
+ * exec\0change_profile
+ */
+ state = aa_dfa_null_transition(profile->file.dfa, state);
+ cp = change_profile_perms(profile, ctx->onexec->ns,
+ ctx->onexec->base.name,
+ AA_MAY_ONEXEC, state);
+
+ if (!(cp.allow & AA_MAY_ONEXEC))
+ goto audit;
+ goto apply;
+ }
+
if (perms.allow & MAY_EXEC) {
/* exec permission determine how to transition */
- new = x_to_label(profile, name, perms.xindex, &target, &info);
- if (new && new->proxy == profile->label.proxy && info) {
- /* hack ix fallback - improve how this is detected */
- goto audit;
- } else if (!new) {
- error = -EACCES;
- info = "profile transition not found";
- /* remove MAY_EXEC to audit as failure */
- perms.allow &= ~MAY_EXEC;
+ new_profile = x_to_profile(profile, name, perms.xindex);
+ if (!new_profile) {
+ if (perms.xindex & AA_X_INHERIT) {
+ /* (p|c|n)ix - don't change profile but do
+ * use the newest version, which was picked
+ * up above when getting profile
+ */
+ info = "ix fallback";
+ new_profile = aa_get_profile(profile);
+ goto x_clear;
+ } else if (perms.xindex & AA_X_UNCONFINED) {
+ new_profile = aa_get_newest_profile(ns->unconfined);
+ info = "ux fallback";
+ } else {
+ error = -EACCES;
+ info = "profile not found";
+ /* remove MAY_EXEC to audit as failure */
+ perms.allow &= ~MAY_EXEC;
+ }
}
} else if (COMPLAIN_MODE(profile)) {
- /* no exec permission - learning mode */
- struct aa_profile *new_profile = NULL;
- char *n = kstrdup(name, GFP_ATOMIC);
-
- if (n) {
- /* name is ptr into buffer */
- long pos = name - buffer;
- /* break per cpu buffer hold */
- put_buffers(buffer);
- new_profile = aa_new_null_profile(profile, false, n,
- GFP_KERNEL);
- get_buffers(buffer);
- name = buffer + pos;
- strcpy((char *)name, n);
- kfree(n);
- }
+ /* no exec permission - are we in learning mode */
+ new_profile = aa_new_null_profile(profile, false, name,
+ GFP_ATOMIC);
if (!new_profile) {
error = -ENOMEM;
info = "could not create null profile";
- } else {
+ } else
error = -EACCES;
- new = &new_profile->label;
- }
perms.xindex |= AA_X_UNSAFE;
} else
/* fail exec */
error = -EACCES;
- if (!new)
- goto audit;
-
- /* Policy has specified a domain transitions. if no_new_privs and
- * confined and not transitioning to the current domain fail.
- *
- * NOTE: Domain transitions from unconfined and to stritly stacked
- * subsets are allowed even when no_new_privs is set because this
- * aways results in a further reduction of permissions.
+ /*
+ * Policy has specified a domain transition, if no_new_privs then
+ * fail the exec.
*/
- if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) &&
- !profile_unconfined(profile) &&
- !aa_label_is_subset(new, &profile->label)) {
+ if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) {
error = -EPERM;
- info = "no new privs";
- nonewprivs = true;
- perms.allow &= ~MAY_EXEC;
- goto audit;
- }
-
- if (!(perms.xindex & AA_X_UNSAFE)) {
- if (DEBUG_ON) {
- dbg_printk("apparmor: scrubbing environment variables"
- " for %s profile=", name);
- aa_label_printk(new, GFP_ATOMIC);
- dbg_printk("\n");
- }
- *secure_exec = true;
- }
-
-audit:
- aa_audit_file(profile, &perms, OP_EXEC, MAY_EXEC, name, target, new,
- cond->uid, info, error);
- if (!new || nonewprivs) {
- aa_put_label(new);
- return ERR_PTR(error);
- }
-
- return new;
-}
-
-static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
- bool stack, const struct linux_binprm *bprm,
- char *buffer, struct path_cond *cond,
- bool *secure_exec)
-{
- unsigned int state = profile->file.start;
- struct aa_perms perms = {};
- const char *xname = NULL, *info = "change_profile onexec";
- int error = -EACCES;
-
- AA_BUG(!profile);
- AA_BUG(!onexec);
- AA_BUG(!bprm);
- AA_BUG(!buffer);
-
- if (profile_unconfined(profile)) {
- /* change_profile on exec already granted */
- /*
- * NOTE: Domain transitions from unconfined are allowed
- * even when no_new_privs is set because this aways results
- * in a further reduction of permissions.
- */
- return 0;
- }
-
- error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
- &xname, &info, profile->disconnected);
- if (error) {
- if (profile_unconfined(profile) ||
- (profile->label.flags & FLAG_IX_ON_NAME_ERROR)) {
- AA_DEBUG("name lookup ix on error");
- error = 0;
- }
- xname = bprm->filename;
- goto audit;
+ goto cleanup;
}
- /* find exec permissions for name */
- state = aa_str_perms(profile->file.dfa, state, xname, cond, &perms);
- if (!(perms.allow & AA_MAY_ONEXEC)) {
- info = "no change_onexec valid for executable";
+ if (!new_profile)
goto audit;
- }
- /* test if this exec can be paired with change_profile onexec.
- * onexec permission is linked to exec with a standard pairing
- * exec\0change_profile
- */
- state = aa_dfa_null_transition(profile->file.dfa, state);
- error = change_profile_perms(profile, onexec, stack, AA_MAY_ONEXEC,
- state, &perms);
- if (error) {
- perms.allow &= ~AA_MAY_ONEXEC;
- goto audit;
- }
- /* Policy has specified a domain transitions. if no_new_privs and
- * confined and not transitioning to the current domain fail.
- *
- * NOTE: Domain transitions from unconfined and to stritly stacked
- * subsets are allowed even when no_new_privs is set because this
- * aways results in a further reduction of permissions.
- */
- if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) &&
- !profile_unconfined(profile) &&
- !aa_label_is_subset(onexec, &profile->label)) {
- error = -EPERM;
- info = "no new privs";
- perms.allow &= ~AA_MAY_ONEXEC;
- goto audit;
- }
-
- if (!(perms.xindex & AA_X_UNSAFE)) {
- if (DEBUG_ON) {
- dbg_printk("apparmor: scrubbing environment "
- "variables for %s label=", xname);
- aa_label_printk(onexec, GFP_ATOMIC);
- dbg_printk("\n");
- }
- *secure_exec = true;
- }
-
-audit:
- return aa_audit_file(profile, &perms, OP_EXEC, AA_MAY_ONEXEC, xname,
- NULL, onexec, cond->uid, info, error);
-}
-
-/* ensure none ns domain transitions are correctly applied with onexec */
-
-static struct aa_label *handle_onexec(struct aa_label *label,
- struct aa_label *onexec, bool stack,
- const struct linux_binprm *bprm,
- char *buffer, struct path_cond *cond,
- bool *unsafe)
-{
- struct aa_profile *profile;
- struct aa_label *new;
- int error;
-
- AA_BUG(!label);
- AA_BUG(!onexec);
- AA_BUG(!bprm);
- AA_BUG(!buffer);
-
- if (!stack) {
- error = fn_for_each_in_ns(label, profile,
- profile_onexec(profile, onexec, stack,
- bprm, buffer, cond, unsafe));
- if (error)
- return ERR_PTR(error);
- new = fn_label_build_in_ns(label, profile, GFP_ATOMIC,
- aa_get_newest_label(onexec),
- profile_transition(profile, bprm, buffer,
- cond, unsafe));
-
- } else {
- /* TODO: determine how much we want to losen this */
- error = fn_for_each_in_ns(label, profile,
- profile_onexec(profile, onexec, stack, bprm,
- buffer, cond, unsafe));
- if (error)
- return ERR_PTR(error);
- new = fn_label_build_in_ns(label, profile, GFP_ATOMIC,
- aa_label_merge(&profile->label, onexec,
- GFP_ATOMIC),
- profile_transition(profile, bprm, buffer,
- cond, unsafe));
- }
-
- if (new)
- return new;
-
- /* TODO: get rid of GLOBAL_ROOT_UID */
- error = fn_for_each_in_ns(label, profile,
- aa_audit_file(profile, &nullperms, OP_CHANGE_ONEXEC,
- AA_MAY_ONEXEC, bprm->filename, NULL,
- onexec, GLOBAL_ROOT_UID,
- "failed to build target label", -ENOMEM));
- return ERR_PTR(error);
-}
-
-/**
- * apparmor_bprm_set_creds - set the new creds on the bprm struct
- * @bprm: binprm for the exec (NOT NULL)
- *
- * Returns: %0 or error on failure
- *
- * TODO: once the other paths are done see if we can't refactor into a fn
- */
-int apparmor_bprm_set_creds(struct linux_binprm *bprm)
-{
- struct aa_task_ctx *ctx;
- struct aa_label *label, *new = NULL;
- struct aa_profile *profile;
- char *buffer = NULL;
- const char *info = NULL;
- int error = 0;
- bool unsafe = false;
- struct path_cond cond = {
- file_inode(bprm->file)->i_uid,
- file_inode(bprm->file)->i_mode
- };
-
- if (bprm->cred_prepared)
- return 0;
-
- ctx = cred_ctx(bprm->cred);
- AA_BUG(!ctx);
-
- label = aa_get_newest_label(ctx->label);
-
- /* buffer freed below, name is pointer into buffer */
- get_buffers(buffer);
- /* Test for onexec first as onexec override other x transitions. */
- if (ctx->onexec)
- new = handle_onexec(label, ctx->onexec, ctx->token,
- bprm, buffer, &cond, &unsafe);
- else
- new = fn_label_build(label, profile, GFP_ATOMIC,
- profile_transition(profile, bprm, buffer,
- &cond, &unsafe));
-
- AA_BUG(!new);
- if (IS_ERR(new)) {
- error = PTR_ERR(new);
- goto done;
- } else if (!new) {
- error = -ENOMEM;
- goto done;
- }
-
- /* TODO: Add ns level no_new_privs subset test */
if (bprm->unsafe & LSM_UNSAFE_SHARE) {
/* FIXME: currently don't mediate shared state */
;
}
- if (bprm->unsafe & (LSM_UNSAFE_PTRACE)) {
- /* TODO: test needs to be profile of label to new */
- error = may_change_ptraced_domain(new, &info);
+ if (bprm->unsafe & LSM_UNSAFE_PTRACE) {
+ error = may_change_ptraced_domain(new_profile);
if (error)
goto audit;
}
- if (unsafe) {
- if (DEBUG_ON) {
- dbg_printk("scrubbing environment variables for %s "
- "label=", bprm->filename);
- aa_label_printk(new, GFP_ATOMIC);
- dbg_printk("\n");
- }
+ /* Determine if secure exec is needed.
+ * Can be at this point for the following reasons:
+ * 1. unconfined switching to confined
+ * 2. confined switching to different confinement
+ * 3. confined switching to unconfined
+ *
+ * Cases 2 and 3 are marked as requiring secure exec
+ * (unless policy specified "unsafe exec")
+ *
+ * bprm->unsafe is used to cache the AA_X_UNSAFE permission
+ * to avoid having to recompute in secureexec
+ */
+ if (!(perms.xindex & AA_X_UNSAFE)) {
+ AA_DEBUG("scrubbing environment variables for %s profile=%s\n",
+ name, new_profile->base.hname);
bprm->unsafe |= AA_SECURE_X_NEEDED;
}
+apply:
+ /* when transitioning profiles clear unsafe personality bits */
+ bprm->per_clear |= PER_CLEAR_ON_SETID;
- if (label->proxy != new->proxy) {
- /* when transitioning clear unsafe personality bits */
- if (DEBUG_ON) {
- dbg_printk("apparmor: clearing unsafe personality "
- "bits. %s label=", bprm->filename);
- aa_label_printk(new, GFP_ATOMIC);
- dbg_printk("\n");
- }
- bprm->per_clear |= PER_CLEAR_ON_SETID;
- }
- aa_put_label(ctx->label);
- /* transfer reference, released when ctx is freed */
- ctx->label = new;
+x_clear:
+ aa_put_profile(ctx->profile);
+ /* transfer new profile reference will be released when ctx is freed */
+ ctx->profile = new_profile;
+ new_profile = NULL;
-done:
- /* clear out temporary/transitional state from the context */
+ /* clear out all temporary/transitional state from the context */
aa_clear_task_ctx_trans(ctx);
- aa_put_label(label);
- put_buffers(buffer);
+audit:
+ error = aa_audit_file(profile, &perms, OP_EXEC, MAY_EXEC, name,
+ new_profile ? new_profile->base.hname : NULL,
+ cond.uid, info, error);
+
+cleanup:
+ aa_put_profile(new_profile);
+ aa_put_profile(profile);
+ kfree(buffer);
return error;
-
-audit:
- error = fn_for_each(label, profile,
- aa_audit_file(profile, &nullperms, OP_EXEC, MAY_EXEC,
- bprm->filename, NULL, new,
- file_inode(bprm->file)->i_uid, info,
- error));
- aa_put_label(new);
- goto done;
}
/**
@@ -893,157 +537,53 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm)
return 0;
}
-/*
- * Functions for self directed profile change
- */
-
-
-/* helper fn for change_hat
- *
- * Returns: label for hat transition OR ERR_PTR. Does NOT return NULL
+/**
+ * apparmor_bprm_committing_creds - do task cleanup on committing new creds
+ * @bprm: binprm for the exec (NOT NULL)
*/
-static struct aa_label *build_change_hat(struct aa_profile *profile,
- const char *name, bool sibling)
+void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
{
- struct aa_profile *root, *hat = NULL;
- const char *info = NULL;
- int error = 0;
+ struct aa_profile *profile = __aa_current_profile();
+ struct aa_task_ctx *new_ctx = cred_ctx(bprm->cred);
- if (sibling && PROFILE_IS_HAT(profile)) {
- root = aa_get_profile_rcu(&profile->parent);
- } else if (!sibling && !PROFILE_IS_HAT(profile)) {
- root = aa_get_profile(profile);
- } else {
- info = "conflicting target types";
- error = -EPERM;
- goto audit;
- }
+ /* bail out if unconfined or not changing profile */
+ if ((new_ctx->profile == profile) ||
+ (unconfined(new_ctx->profile)))
+ return;
- hat = aa_find_child(root, name);
- if (!hat) {
- error = -ENOENT;
- if (COMPLAIN_MODE(profile)) {
- hat = aa_new_null_profile(profile, true, name,
- GFP_KERNEL);
- if (!hat) {
- info = "failed null profile create";
- error = -ENOMEM;
- }
- }
- }
- aa_put_profile(root);
+ current->pdeath_signal = 0;
-audit:
- aa_audit_file(profile, &nullperms, OP_CHANGE_HAT, AA_MAY_CHANGEHAT,
- name, hat ? hat->base.hname : NULL,
- hat ? &hat->label : NULL, GLOBAL_ROOT_UID, NULL,
- error);
- if (!hat || (error && error != -ENOENT))
- return ERR_PTR(error);
- /* if hat && error - complain mode, already audited and we adjust for
- * complain mode allow by returning hat->label
- */
- return &hat->label;
+ /* reset soft limits and set hard limits for the new profile */
+ __aa_transition_rlimits(profile, new_ctx->profile);
}
-/* helper fn for changing into a hat
- *
- * Returns: label for hat transition or ERR_PTR. Does not return NULL
+/**
+ * apparmor_bprm_commited_cred - do cleanup after new creds committed
+ * @bprm: binprm for the exec (NOT NULL)
*/
-static struct aa_label *change_hat(struct aa_label *label, const char *hats[],
- int count, int flags)
+void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
{
- struct aa_profile *profile, *root, *hat = NULL;
- struct aa_label *new;
- struct label_it it;
- bool sibling = false;
- const char *name, *info = NULL;
- int i, error;
-
- AA_BUG(!label);
- AA_BUG(!hats);
- AA_BUG(count < 1);
-
- if (PROFILE_IS_HAT(labels_profile(label)))
- sibling = true;
-
- /*find first matching hat */
- for (i = 0; i < count && !hat; i++) {
- name = hats[i];
- label_for_each_in_ns(it, labels_ns(label), label, profile) {
- if (sibling && PROFILE_IS_HAT(profile)) {
- root = aa_get_profile_rcu(&profile->parent);
- } else if (!sibling && !PROFILE_IS_HAT(profile)) {
- root = aa_get_profile(profile);
- } else { /* conflicting change type */
- info = "conflicting targets types";
- error = -EPERM;
- goto fail;
- }
- hat = aa_find_child(root, name);
- aa_put_profile(root);
- if (!hat) {
- if (!COMPLAIN_MODE(profile))
- goto outer_continue;
- /* complain mode succeed as if hat */
- } else if (!PROFILE_IS_HAT(hat)) {
- info = "target not hat";
- error = -EPERM;
- aa_put_profile(hat);
- goto fail;
- }
- aa_put_profile(hat);
- }
- /* found a hat for all profiles in ns */
- goto build;
-outer_continue:
- ;
- }
- /* no hats that match, find appropriate error
- *
- * In complain mode audit of the failure is based off of the first
- * hat supplied. This is done due how userspace interacts with
- * change_hat.
- */
- name = NULL;
- label_for_each_in_ns(it, labels_ns(label), label, profile) {
- if (!list_empty(&profile->base.profiles)) {
- info = "hat not found";
- error = -ENOENT;
- goto fail;
- }
- }
- info = "no hats defined";
- error = -ECHILD;
+ /* TODO: cleanup signals - ipc mediation */
+ return;
+}
-fail:
- label_for_each_in_ns(it, labels_ns(label), label, profile) {
- /*
- * no target as it has failed to be found or built
- *
- * change_hat uses probing and should not log failures
- * related to missing hats
- */
- /* TODO: get rid of GLOBAL_ROOT_UID */
- if (count > 1 || COMPLAIN_MODE(profile)) {
- aa_audit_file(profile, &nullperms, OP_CHANGE_HAT,
- AA_MAY_CHANGEHAT, name, NULL, NULL,
- GLOBAL_ROOT_UID, info, error);
- }
- }
- return ERR_PTR(error);
-
-build:
- new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
- build_change_hat(profile, name, sibling),
- aa_get_label(&profile->label));
- if (!new) {
- info = "label build failed";
- error = -ENOMEM;
- goto fail;
- } /* else if (IS_ERR) build_change_hat has logged error so return new */
-
- return new;
+/*
+ * Functions for self directed profile change
+ */
+
+/**
+ * new_compound_name - create an hname with @n2 appended to @n1
+ * @n1: base of hname (NOT NULL)
+ * @n2: name to append (NOT NULL)
+ *
+ * Returns: new name or NULL on error
+ */
+static char *new_compound_name(const char *n1, const char *n2)
+{
+ char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL);
+ if (name)
+ sprintf(name, "%s//%s", n1, n2);
+ return name;
}
/**
@@ -1051,26 +591,24 @@ build:
* @hats: vector of hat names to try changing into (MAYBE NULL if @count == 0)
* @count: number of hat names in @hats
* @token: magic value to validate the hat change
- * @flags: flags affecting behavior of the change
- *
- * Returns %0 on success, error otherwise.
+ * @permtest: true if this is just a permission test
*
* Change to the first profile specified in @hats that exists, and store
* the @hat_magic in the current task context. If the count == 0 and the
* @token matches that stored in the current task context, return to the
* top level profile.
*
- * change_hat only applies to profiles in the current ns, and each profile
- * in the ns must make the same transition otherwise change_hat will fail.
+ * Returns %0 on success, error otherwise.
*/
-int aa_change_hat(const char *hats[], int count, u64 token, int flags)
+int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
{
const struct cred *cred;
struct aa_task_ctx *ctx;
- struct aa_label *label, *previous, *new = NULL, *target = NULL;
- struct aa_profile *profile;
- struct aa_perms perms = {};
- const char *info = NULL;
+ struct aa_profile *profile, *previous_profile, *hat = NULL;
+ char *name = NULL;
+ int i;
+ struct file_perms perms = {};
+ const char *target = NULL, *info = NULL;
int error = 0;
/*
@@ -1078,120 +616,122 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
* There is no exception for unconfined as change_hat is not
* available.
*/
- if (task_no_new_privs(current)) {
- /* not an apparmor denial per se, so don't log it */
- AA_DEBUG("no_new_privs - change_hat denied");
+ if (task_no_new_privs(current))
return -EPERM;
- }
/* released below */
cred = get_current_cred();
ctx = cred_ctx(cred);
- label = aa_get_newest_cred_label(cred);
- previous = aa_get_newest_label(ctx->previous);
+ profile = aa_get_newest_profile(aa_cred_profile(cred));
+ previous_profile = aa_get_newest_profile(ctx->previous);
- if (unconfined(label)) {
- info = "unconfined can not change_hat";
+ if (unconfined(profile)) {
+ info = "unconfined";
error = -EPERM;
- goto fail;
+ goto audit;
}
if (count) {
- new = change_hat(label, hats, count, flags);
- AA_BUG(!new);
- if (IS_ERR(new)) {
- error = PTR_ERR(new);
- new = NULL;
- /* already audited */
- goto out;
+ /* attempting to change into a new hat or switch to a sibling */
+ struct aa_profile *root;
+ if (PROFILE_IS_HAT(profile))
+ root = aa_get_profile_rcu(&profile->parent);
+ else
+ root = aa_get_profile(profile);
+
+ /* find first matching hat */
+ for (i = 0; i < count && !hat; i++)
+ /* released below */
+ hat = aa_find_child(root, hats[i]);
+ if (!hat) {
+ if (!COMPLAIN_MODE(root) || permtest) {
+ if (list_empty(&root->base.profiles))
+ error = -ECHILD;
+ else
+ error = -ENOENT;
+ aa_put_profile(root);
+ goto out;
+ }
+
+ /*
+ * In complain mode and failed to match any hats.
+ * Audit the failure is based off of the first hat
+ * supplied. This is done due how userspace
+ * interacts with change_hat.
+ *
+ * TODO: Add logging of all failed hats
+ */
+
+ /* freed below */
+ name = new_compound_name(root->base.hname, hats[0]);
+ aa_put_profile(root);
+ target = name;
+ /* released below */
+ hat = aa_new_null_profile(profile, true, hats[0],
+ GFP_KERNEL);
+ if (!hat) {
+ info = "failed null profile create";
+ error = -ENOMEM;
+ goto audit;
+ }
+ } else {
+ aa_put_profile(root);
+ target = hat->base.hname;
+ if (!PROFILE_IS_HAT(hat)) {
+ info = "target not hat";
+ error = -EPERM;
+ goto audit;
+ }
}
- error = may_change_ptraced_domain(new, &info);
- if (error)
- goto fail;
-
- if (flags & AA_CHANGE_TEST)
- goto out;
-
- target = new;
- error = aa_set_current_hat(new, token);
- if (error == -EACCES)
- /* kill task in case of brute force attacks */
- goto kill;
- } else if (previous && !(flags & AA_CHANGE_TEST)) {
- /* Return to saved label. Kill task if restore fails
- * to avoid brute force attacks
- */
- target = previous;
- error = aa_restore_previous_label(token);
+ error = may_change_ptraced_domain(hat);
if (error) {
+ info = "ptraced";
+ error = -EPERM;
+ goto audit;
+ }
+
+ if (!permtest) {
+ error = aa_set_current_hat(hat, token);
if (error == -EACCES)
- goto kill;
- goto fail;
+ /* kill task in case of brute force attacks */
+ perms.kill = AA_MAY_CHANGEHAT;
+ else if (name && !error)
+ /* reset error for learning of new hats */
+ error = -ENOENT;
}
- } /* else ignore @flags && restores when there is no saved profile */
+ } else if (previous_profile) {
+ /* Return to saved profile. Kill task if restore fails
+ * to avoid brute force attacks
+ */
+ target = previous_profile->base.hname;
+ error = aa_restore_previous_profile(token);
+ perms.kill = AA_MAY_CHANGEHAT;
+ } else
+ /* ignore restores when there is no saved profile */
+ goto out;
+
+audit:
+ if (!permtest)
+ error = aa_audit_file(profile, &perms, OP_CHANGE_HAT,
+ AA_MAY_CHANGEHAT, NULL, target,
+ GLOBAL_ROOT_UID, info, error);
out:
- aa_put_label(new);
- aa_put_label(previous);
- aa_put_label(label);
+ aa_put_profile(hat);
+ kfree(name);
+ aa_put_profile(profile);
+ aa_put_profile(previous_profile);
put_cred(cred);
return error;
-
-kill:
- info = "failed token match";
- perms.kill = AA_MAY_CHANGEHAT;
-
-fail:
- fn_for_each_in_ns(label, profile,
- aa_audit_file(profile, &perms, OP_CHANGE_HAT,
- AA_MAY_CHANGEHAT, NULL, NULL, target,
- GLOBAL_ROOT_UID, info, error));
-
- goto out;
-}
-
-
-static int change_profile_perms_wrapper(const char *op, const char *name,
- struct aa_profile *profile,
- struct aa_label *target, bool stack,
- u32 request, struct aa_perms *perms)
-{
- const char *info = NULL;
- int error = 0;
-
- /*
- * Fail explicitly requested domain transitions when no_new_privs
- * and not unconfined OR the transition results in a stack on
- * the current label.
- * Stacking domain transitions and transitions from unconfined are
- * allowed even when no_new_privs is set because this aways results
- * in a reduction of permissions.
- */
- if (task_no_new_privs(current) && !stack &&
- !profile_unconfined(profile) &&
- !aa_label_is_subset(target, &profile->label)) {
- info = "no new privs";
- error = -EPERM;
- }
-
- if (!error)
- error = change_profile_perms(profile, target, stack, request,
- profile->file.start, perms);
- if (error)
- error = aa_audit_file(profile, perms, op, request, name,
- NULL, target, GLOBAL_ROOT_UID, info,
- error);
-
- return error;
}
/**
* aa_change_profile - perform a one-way profile transition
* @fqname: name of profile may include namespace (NOT NULL)
* @onexec: whether this transition is to take place immediately or at exec
- * @flags: flags affecting change behavior
+ * @permtest: true if this is just a permission test
*
* Change to new profile @name. Unlike with hats, there is no way
* to change back. If @name isn't specified the current profile name is
@@ -1201,16 +741,14 @@ static int change_profile_perms_wrapper(const char *op, const char *name,
*
* Returns %0 on success, error otherwise.
*/
-int aa_change_profile(const char *fqname, int flags)
+int aa_change_profile(const char *fqname, bool onexec,
+ bool permtest, bool stack)
{
- struct aa_label *label, *new = NULL, *target = NULL;
- struct aa_profile *profile;
- struct aa_perms perms = {};
- const char *info = NULL;
- const char *auditname = fqname; /* retain leading & if stack */
- bool stack = flags & AA_CHANGE_STACK;
+ const struct cred *cred;
+ struct aa_profile *profile, *target = NULL;
+ struct file_perms perms = {};
+ const char *info = NULL, *op;
int error = 0;
- char *op;
u32 request;
if (!fqname || !*fqname) {
@@ -1218,118 +756,74 @@ int aa_change_profile(const char *fqname, int flags)
return -EINVAL;
}
- if (flags & AA_CHANGE_ONEXEC) {
+ if (onexec) {
request = AA_MAY_ONEXEC;
- if (stack)
- op = OP_STACK_ONEXEC;
- else
- op = OP_CHANGE_ONEXEC;
+ op = OP_CHANGE_ONEXEC;
} else {
request = AA_MAY_CHANGE_PROFILE;
- if (stack)
- op = OP_STACK;
- else
- op = OP_CHANGE_PROFILE;
+ op = OP_CHANGE_PROFILE;
}
- label = aa_get_current_label();
+ cred = get_current_cred();
+ profile = aa_cred_profile(cred);
- if (*fqname == '&') {
- stack = true;
- /* don't have label_parse() do stacking */
- fqname++;
+ /*
+ * Fail explicitly requested domain transitions if no_new_privs
+ * and not unconfined.
+ * Domain transitions from unconfined are allowed even when
+ * no_new_privs is set because this aways results in a reduction
+ * of permissions.
+ */
+ if (task_no_new_privs(current) && !unconfined(profile)) {
+ put_cred(cred);
+ return -EPERM;
}
- target = aa_label_parse(label, fqname, GFP_KERNEL, true, false);
- if (IS_ERR(target)) {
- struct aa_profile *tprofile;
- info = "label not found";
- error = PTR_ERR(target);
- target = NULL;
- /*
- * TODO: fixme using labels_profile is not right - do profile
- * per complain profile
- */
- if ((flags & AA_CHANGE_TEST) ||
- !COMPLAIN_MODE(labels_profile(label)))
+ target = aa_fqlookupn_profile(profile, fqname, strlen(fqname));
+ if (!target) {
+ info = "profile not found";
+ error = -ENOENT;
+ if (permtest || !COMPLAIN_MODE(profile))
goto audit;
/* released below */
- tprofile = aa_new_null_profile(labels_profile(label), false,
- fqname, GFP_KERNEL);
- if (!tprofile) {
+ target = aa_new_null_profile(profile, false, fqname,
+ GFP_KERNEL);
+ if (!target) {
info = "failed null profile create";
error = -ENOMEM;
goto audit;
}
- target = &tprofile->label;
- goto check;
}
- /*
- * self directed transitions only apply to current policy ns
- * TODO: currently requiring perms for stacking and straight change
- * stacking doesn't strictly need this. Determine how much
- * we want to loosen this restriction for stacking
- *
- * if (!stack) {
- */
- error = fn_for_each_in_ns(label, profile,
- change_profile_perms_wrapper(op, auditname,
- profile, target, stack,
- request, &perms));
- if (error)
- /* auditing done in change_profile_perms_wrapper */
- goto out;
-
- /* } */
+ perms = change_profile_perms(profile, target->ns, target->base.hname,
+ request, profile->file.start);
+ if (!(perms.allow & request)) {
+ error = -EACCES;
+ goto audit;
+ }
-check:
/* check if tracing task is allowed to trace target domain */
- error = may_change_ptraced_domain(target, &info);
- if (error && !fn_for_each_in_ns(label, profile,
- COMPLAIN_MODE(profile)))
+ error = may_change_ptraced_domain(target);
+ if (error) {
+ info = "ptrace prevents transition";
goto audit;
+ }
- /* TODO: add permission check to allow this
- * if ((flags & AA_CHANGE_ONEXEC) && !current_is_single_threaded()) {
- * info = "not a single threaded task";
- * error = -EACCES;
- * goto audit;
- * }
- */
- if (flags & AA_CHANGE_TEST)
- goto out;
+ if (permtest)
+ goto audit;
- if (!(flags & AA_CHANGE_ONEXEC)) {
- /* only transition profiles in the current ns */
- if (stack)
- new = aa_label_merge(label, target, GFP_KERNEL);
- else
- new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
- aa_get_label(target),
- aa_get_label(&profile->label));
- if (IS_ERR_OR_NULL(new)) {
- info = "failed to build target label";
- error = PTR_ERR(new);
- new = NULL;
- perms.allow = 0;
- goto audit;
- }
- error = aa_replace_current_label(new);
- } else
- /* full transition will be built in exec path */
- error = aa_set_current_onexec(target, stack);
+ if (onexec)
+ error = aa_set_current_onexec(target);
+ else
+ error = aa_replace_current_profile(target);
audit:
- error = fn_for_each_in_ns(label, profile,
- aa_audit_file(profile, &perms, op, request, auditname,
- NULL, new ? new : target,
- GLOBAL_ROOT_UID, info, error));
+ if (!permtest)
+ error = aa_audit_file(profile, &perms, op, request, NULL,
+ fqname, GLOBAL_ROOT_UID, info, error);
-out:
- aa_put_label(new);
- aa_put_label(target);
- aa_put_label(label);
+ aa_put_profile(target);
+ put_cred(cred);
return error;
}
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index 3382518b87fa..750564c3ab71 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -12,30 +12,15 @@
* License.
*/
-#include <linux/tty.h>
-#include <linux/fdtable.h>
-#include <linux/file.h>
-
#include "include/apparmor.h"
#include "include/audit.h"
-#include "include/context.h"
#include "include/file.h"
#include "include/match.h"
#include "include/path.h"
#include "include/policy.h"
-#include "include/label.h"
-static u32 map_mask_to_chr_mask(u32 mask)
-{
- u32 m = mask & PERMS_CHRS_MASK;
+struct file_perms nullperms;
- if (mask & AA_MAY_GETATTR)
- m |= MAY_READ;
- if (mask & (AA_MAY_SETATTR | AA_MAY_CHMOD | AA_MAY_CHOWN))
- m |= MAY_WRITE;
-
- return m;
-}
/**
* audit_file_mask - convert mask to permission string
@@ -46,7 +31,29 @@ static void audit_file_mask(struct audit_buffer *ab, u32 mask)
{
char str[10];
- aa_perm_mask_to_str(str, aa_file_perm_chrs, map_mask_to_chr_mask(mask));
+ char *m = str;
+
+ if (mask & AA_EXEC_MMAP)
+ *m++ = 'm';
+ if (mask & (MAY_READ | AA_MAY_META_READ))
+ *m++ = 'r';
+ if (mask & (MAY_WRITE | AA_MAY_META_WRITE | AA_MAY_CHMOD |
+ AA_MAY_CHOWN))
+ *m++ = 'w';
+ else if (mask & MAY_APPEND)
+ *m++ = 'a';
+ if (mask & AA_MAY_CREATE)
+ *m++ = 'c';
+ if (mask & AA_MAY_DELETE)
+ *m++ = 'd';
+ if (mask & AA_MAY_LINK)
+ *m++ = 'l';
+ if (mask & AA_MAY_LOCK)
+ *m++ = 'k';
+ if (mask & MAY_EXEC)
+ *m++ = 'x';
+ *m = '\0';
+
audit_log_string(ab, str);
}
@@ -60,26 +67,22 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
struct common_audit_data *sa = va;
kuid_t fsuid = current_fsuid();
- if (aad(sa)->request & AA_AUDIT_FILE_MASK) {
+ if (aad(sa)->fs.request & AA_AUDIT_FILE_MASK) {
audit_log_format(ab, " requested_mask=");
- audit_file_mask(ab, aad(sa)->request);
+ audit_file_mask(ab, aad(sa)->fs.request);
}
- if (aad(sa)->denied & AA_AUDIT_FILE_MASK) {
+ if (aad(sa)->fs.denied & AA_AUDIT_FILE_MASK) {
audit_log_format(ab, " denied_mask=");
- audit_file_mask(ab, aad(sa)->denied);
+ audit_file_mask(ab, aad(sa)->fs.denied);
}
- if (aad(sa)->request & AA_AUDIT_FILE_MASK) {
+ if (aad(sa)->fs.request & AA_AUDIT_FILE_MASK) {
audit_log_format(ab, " fsuid=%d",
from_kuid(&init_user_ns, fsuid));
audit_log_format(ab, " ouid=%d",
from_kuid(&init_user_ns, aad(sa)->fs.ouid));
}
- if (aad(sa)->peer) {
- audit_log_format(ab, " target=");
- aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
- FLAG_VIEW_SUBNS, GFP_ATOMIC);
- } else if (aad(sa)->fs.target) {
+ if (aad(sa)->fs.target) {
audit_log_format(ab, " target=");
audit_log_untrustedstring(ab, aad(sa)->fs.target);
}
@@ -89,30 +92,28 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
* aa_audit_file - handle the auditing of file operations
* @profile: the profile being enforced (NOT NULL)
* @perms: the permissions computed for the request (NOT NULL)
+ * @gfp: allocation flags
* @op: operation being mediated
* @request: permissions requested
* @name: name of object being mediated (MAYBE NULL)
* @target: name of target (MAYBE NULL)
- * @tlabel: target label (MAY BE NULL)
* @ouid: object uid
* @info: extra information message (MAYBE NULL)
* @error: 0 if operation allowed else failure error code
*
* Returns: %0 or error on failure
*/
-int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
+int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
const char *op, u32 request, const char *name,
- const char *target, struct aa_label *tlabel,
- kuid_t ouid, const char *info, int error)
+ const char *target, kuid_t ouid, const char *info, int error)
{
int type = AUDIT_APPARMOR_AUTO;
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, op);
sa.u.tsk = NULL;
- aad(&sa)->request = request;
+ aad(&sa)->fs.request = request;
aad(&sa)->name = name;
aad(&sa)->fs.target = target;
- aad(&sa)->peer = tlabel;
aad(&sa)->fs.ouid = ouid;
aad(&sa)->info = info;
aad(&sa)->error = error;
@@ -125,67 +126,34 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
mask = 0xffff;
/* mask off perms that are not being force audited */
- aad(&sa)->request &= mask;
+ aad(&sa)->fs.request &= mask;
- if (likely(!aad(&sa)->request))
+ if (likely(!aad(&sa)->fs.request))
return 0;
type = AUDIT_APPARMOR_AUDIT;
} else {
/* only report permissions that were denied */
- aad(&sa)->request = aad(&sa)->request & ~perms->allow;
- AA_BUG(!aad(&sa)->request);
+ aad(&sa)->fs.request = aad(&sa)->fs.request & ~perms->allow;
+ AA_BUG(!aad(&sa)->fs.request);
- if (aad(&sa)->request & perms->kill)
+ if (aad(&sa)->fs.request & perms->kill)
type = AUDIT_APPARMOR_KILL;
/* quiet known rejects, assumes quiet and kill do not overlap */
- if ((aad(&sa)->request & perms->quiet) &&
+ if ((aad(&sa)->fs.request & perms->quiet) &&
AUDIT_MODE(profile) != AUDIT_NOQUIET &&
AUDIT_MODE(profile) != AUDIT_ALL)
- aad(&sa)->request &= ~perms->quiet;
+ aad(&sa)->fs.request &= ~perms->quiet;
- if (!aad(&sa)->request)
- return aad(&sa)->error;
+ if (!aad(&sa)->fs.request)
+ return COMPLAIN_MODE(profile) ? 0 : aad(&sa)->error;
}
- aad(&sa)->denied = aad(&sa)->request & ~perms->allow;
+ aad(&sa)->fs.denied = aad(&sa)->fs.request & ~perms->allow;
return aa_audit(type, profile, &sa, file_audit_cb);
}
/**
- * is_deleted - test if a file has been completely unlinked
- * @dentry: dentry of file to test for deletion (NOT NULL)
- *
- * Returns: %1 if deleted else %0
- */
-static inline bool is_deleted(struct dentry *dentry)
-{
- if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0)
- return 1;
- return 0;
-}
-
-static int path_name(const char *op, struct aa_label *label,
- const struct path *path, int flags, char *buffer,
- const char **name, struct path_cond *cond, u32 request)
-{
- struct aa_profile *profile;
- const char *info = NULL;
- int error;
-
- error = aa_path_name(path, flags, buffer, name, &info,
- labels_profile(label)->disconnected);
- if (error) {
- fn_for_each_confined(label, profile,
- aa_audit_file(profile, &nullperms, op, request, *name,
- NULL, NULL, cond->uid, info, error));
- return error;
- }
-
- return 0;
-}
-
-/**
* map_old_perms - map old file perms layout to the new layout
* @old: permission set in old mapping
*
@@ -195,10 +163,10 @@ static u32 map_old_perms(u32 old)
{
u32 new = old & 0xf;
if (old & MAY_READ)
- new |= AA_MAY_GETATTR | AA_MAY_OPEN;
+ new |= AA_MAY_META_READ;
if (old & MAY_WRITE)
- new |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE |
- AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN;
+ new |= AA_MAY_META_WRITE | AA_MAY_CREATE | AA_MAY_DELETE |
+ AA_MAY_CHMOD | AA_MAY_CHOWN;
if (old & 0x10)
new |= AA_MAY_LINK;
/* the old mapping lock and link_subset flags where overlaid
@@ -213,7 +181,7 @@ static u32 map_old_perms(u32 old)
}
/**
- * aa_compute_fperms - convert dfa compressed perms to internal perms
+ * compute_perms - convert dfa compressed perms to internal perms
* @dfa: dfa to compute perms for (NOT NULL)
* @state: state in dfa
* @cond: conditions to consider (NOT NULL)
@@ -223,21 +191,17 @@ static u32 map_old_perms(u32 old)
*
* Returns: computed permission set
*/
-struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state,
- struct path_cond *cond)
+static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state,
+ struct path_cond *cond)
{
- struct aa_perms perms;
+ struct file_perms perms;
/* FIXME: change over to new dfa format
* currently file perms are encoded in the dfa, new format
* splits the permissions from the dfa. This mapping can be
* done at profile load
*/
- perms.deny = 0;
- perms.kill = perms.stop = 0;
- perms.complain = perms.cond = 0;
- perms.hide = 0;
- perms.prompt = 0;
+ perms.kill = 0;
if (uid_eq(current_fsuid(), cond->uid)) {
perms.allow = map_old_perms(dfa_user_allow(dfa, state));
@@ -250,7 +214,7 @@ struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state,
perms.quiet = map_old_perms(dfa_other_quiet(dfa, state));
perms.xindex = dfa_other_xindex(dfa, state);
}
- perms.allow |= AA_MAY_GETATTR;
+ perms.allow |= AA_MAY_META_READ;
/* change_profile wasn't determined by ownership in old mapping */
if (ACCEPT_TABLE(dfa)[state] & 0x80000000)
@@ -273,55 +237,37 @@ struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state,
*/
unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
const char *name, struct path_cond *cond,
- struct aa_perms *perms)
+ struct file_perms *perms)
{
unsigned int state;
+ if (!dfa) {
+ *perms = nullperms;
+ return DFA_NOMATCH;
+ }
+
state = aa_dfa_match(dfa, start, name);
- *perms = aa_compute_fperms(dfa, state, cond);
+ *perms = compute_perms(dfa, state, cond);
return state;
}
-int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name,
- u32 request, struct path_cond *cond, int flags,
- struct aa_perms *perms)
-{
- int e = 0;
-
- if (profile_unconfined(profile))
- return 0;
- aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms);
- if (request & ~perms->allow)
- e = -EACCES;
- return aa_audit_file(profile, perms, op, request, name, NULL, NULL,
- cond->uid, NULL, e);
-}
-
-
-static int profile_path_perm(const char *op, struct aa_profile *profile,
- const struct path *path, char *buffer, u32 request,
- struct path_cond *cond, int flags,
- struct aa_perms *perms)
+/**
+ * is_deleted - test if a file has been completely unlinked
+ * @dentry: dentry of file to test for deletion (NOT NULL)
+ *
+ * Returns: %1 if deleted else %0
+ */
+static inline bool is_deleted(struct dentry *dentry)
{
- const char *name;
- int error;
-
- if (profile_unconfined(profile))
- return 0;
-
- error = path_name(op, &profile->label, path,
- flags | profile->path_flags, buffer, &name, cond,
- request);
- if (error)
- return error;
- return __aa_path_perm(op, profile, name, request, cond, flags,
- perms);
+ if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0)
+ return 1;
+ return 0;
}
/**
* aa_path_perm - do permissions check & audit for @path
* @op: operation being checked
- * @label: profile being enforced (NOT NULL)
+ * @profile: profile being enforced (NOT NULL)
* @path: path to check permissions of (NOT NULL)
* @flags: any additional path flags beyond what the profile specifies
* @request: requested permissions
@@ -329,23 +275,35 @@ static int profile_path_perm(const char *op, struct aa_profile *profile,
*
* Returns: %0 else error if access denied or other error
*/
-int aa_path_perm(const char *op, struct aa_label *label,
+int aa_path_perm(const char *op, struct aa_profile *profile,
const struct path *path, int flags, u32 request,
struct path_cond *cond)
{
- struct aa_perms perms = {};
- struct aa_profile *profile;
char *buffer = NULL;
+ struct file_perms perms = {};
+ const char *name, *info = NULL;
int error;
- flags |= PATH_DELEGATE_DELETED | (S_ISDIR(cond->mode) ? PATH_IS_DIR :
- 0);
- get_buffers(buffer);
- error = fn_for_each_confined(label, profile,
- profile_path_perm(op, profile, path, buffer, request,
- cond, flags, &perms));
-
- put_buffers(buffer);
+ flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0);
+ error = aa_path_name(path, flags, &buffer, &name, &info);
+ if (error) {
+ if (error == -ENOENT && is_deleted(path->dentry)) {
+ /* Access to open files that are deleted are
+ * give a pass (implicit delegation)
+ */
+ error = 0;
+ info = NULL;
+ perms.allow = request;
+ }
+ } else {
+ aa_str_perms(profile->file.dfa, profile->file.start, name, cond,
+ &perms);
+ if (request & ~perms.allow)
+ error = -EACCES;
+ }
+ error = aa_audit_file(profile, &perms, op, request, name, NULL,
+ cond->uid, info, error);
+ kfree(buffer);
return error;
}
@@ -370,40 +328,65 @@ static inline bool xindex_is_subset(u32 link, u32 target)
return 1;
}
-static int profile_path_link(struct aa_profile *profile,
- const struct path *link, char *buffer,
- const struct path *target, char *buffer2,
- struct path_cond *cond)
+/**
+ * aa_path_link - Handle hard link permission check
+ * @profile: the profile being enforced (NOT NULL)
+ * @old_dentry: the target dentry (NOT NULL)
+ * @new_dir: directory the new link will be created in (NOT NULL)
+ * @new_dentry: the link being created (NOT NULL)
+ *
+ * Handle the permission test for a link & target pair. Permission
+ * is encoded as a pair where the link permission is determined
+ * first, and if allowed, the target is tested. The target test
+ * is done from the point of the link match (not start of DFA)
+ * making the target permission dependent on the link permission match.
+ *
+ * The subset test if required forces that permissions granted
+ * on link are a subset of the permission granted to target.
+ *
+ * Returns: %0 if allowed else error
+ */
+int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
+ const struct path *new_dir, struct dentry *new_dentry)
{
- const char *lname, *tname = NULL;
- struct aa_perms lperms = {}, perms;
- const char *info = NULL;
+ struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry };
+ struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry };
+ struct path_cond cond = {
+ d_backing_inode(old_dentry)->i_uid,
+ d_backing_inode(old_dentry)->i_mode
+ };
+ char *buffer = NULL, *buffer2 = NULL;
+ const char *lname, *tname = NULL, *info = NULL;
+ struct file_perms lperms, perms;
u32 request = AA_MAY_LINK;
unsigned int state;
int error;
- error = path_name(OP_LINK, &profile->label, link, profile->path_flags,
- buffer, &lname, cond, AA_MAY_LINK);
+ lperms = nullperms;
+
+ /* buffer freed below, lname is pointer in buffer */
+ error = aa_path_name(&link, profile->path_flags, &buffer, &lname,
+ &info);
if (error)
goto audit;
/* buffer2 freed below, tname is pointer in buffer2 */
- error = path_name(OP_LINK, &profile->label, target, profile->path_flags,
- buffer2, &tname, cond, AA_MAY_LINK);
+ error = aa_path_name(&target, profile->path_flags, &buffer2, &tname,
+ &info);
if (error)
goto audit;
error = -EACCES;
/* aa_str_perms - handles the case of the dfa being NULL */
state = aa_str_perms(profile->file.dfa, profile->file.start, lname,
- cond, &lperms);
+ &cond, &lperms);
if (!(lperms.allow & AA_MAY_LINK))
goto audit;
/* test to see if target can be paired with link */
state = aa_dfa_null_transition(profile->file.dfa, state);
- aa_str_perms(profile->file.dfa, state, tname, cond, &perms);
+ aa_str_perms(profile->file.dfa, state, tname, &cond, &perms);
/* force audit/quiet masks for link are stored in the second entry
* in the link pair.
@@ -414,7 +397,6 @@ static int profile_path_link(struct aa_profile *profile,
if (!(perms.allow & AA_MAY_LINK)) {
info = "target restricted";
- lperms = perms;
goto audit;
}
@@ -422,10 +404,10 @@ static int profile_path_link(struct aa_profile *profile,
if (!(perms.allow & AA_LINK_SUBSET))
goto done_tests;
- /* Do link perm subset test requiring allowed permission on link are
- * a subset of the allowed permissions on target.
+ /* Do link perm subset test requiring allowed permission on link are a
+ * subset of the allowed permissions on target.
*/
- aa_str_perms(profile->file.dfa, profile->file.start, tname, cond,
+ aa_str_perms(profile->file.dfa, profile->file.start, tname, &cond,
&perms);
/* AA_MAY_LINK is not considered in the subset test */
@@ -447,121 +429,10 @@ done_tests:
error = 0;
audit:
- return aa_audit_file(profile, &lperms, OP_LINK, request, lname, tname,
- NULL, cond->uid, info, error);
-}
-
-/**
- * aa_path_link - Handle hard link permission check
- * @label: the label being enforced (NOT NULL)
- * @old_dentry: the target dentry (NOT NULL)
- * @new_dir: directory the new link will be created in (NOT NULL)
- * @new_dentry: the link being created (NOT NULL)
- *
- * Handle the permission test for a link & target pair. Permission
- * is encoded as a pair where the link permission is determined
- * first, and if allowed, the target is tested. The target test
- * is done from the point of the link match (not start of DFA)
- * making the target permission dependent on the link permission match.
- *
- * The subset test if required forces that permissions granted
- * on link are a subset of the permission granted to target.
- *
- * Returns: %0 if allowed else error
- */
-int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
- const struct path *new_dir, struct dentry *new_dentry)
-{
- struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry };
- struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry };
- struct path_cond cond = {
- d_backing_inode(old_dentry)->i_uid,
- d_backing_inode(old_dentry)->i_mode
- };
- char *buffer = NULL, *buffer2 = NULL;
- struct aa_profile *profile;
- int error;
-
- /* buffer freed below, lname is pointer in buffer */
- get_buffers(buffer, buffer2);
- error = fn_for_each_confined(label, profile,
- profile_path_link(profile, &link, buffer, &target,
- buffer2, &cond));
- put_buffers(buffer, buffer2);
-
- return error;
-}
-
-static void update_file_ctx(struct aa_file_ctx *fctx, struct aa_label *label,
- u32 request)
-{
- struct aa_label *l, *old;
-
- /* update caching of label on file_ctx */
- spin_lock(&fctx->lock);
- old = rcu_dereference_protected(fctx->label,
- spin_is_locked(&fctx->lock));
- l = aa_label_merge(old, label, GFP_ATOMIC);
- if (l) {
- if (l != old) {
- rcu_assign_pointer(fctx->label, l);
- aa_put_label(old);
- } else
- aa_put_label(l);
- fctx->allow |= request;
- }
- spin_unlock(&fctx->lock);
-}
-
-static int __file_path_perm(const char *op, struct aa_label *label,
- struct aa_label *flabel, struct file *file,
- u32 request, u32 denied)
-{
- struct aa_profile *profile;
- struct aa_perms perms = {};
- struct path_cond cond = {
- .uid = file_inode(file)->i_uid,
- .mode = file_inode(file)->i_mode
- };
- char *buffer;
- int flags, error;
-
- /* revalidation due to label out of date. No revocation at this time */
- if (!denied && aa_label_is_subset(flabel, label))
- /* TODO: check for revocation on stale profiles */
- return 0;
-
- flags = PATH_DELEGATE_DELETED | (S_ISDIR(cond.mode) ? PATH_IS_DIR : 0);
- get_buffers(buffer);
-
- /* check every profile in task label not in current cache */
- error = fn_for_each_not_in_set(flabel, label, profile,
- profile_path_perm(op, profile, &file->f_path, buffer,
- request, &cond, flags, &perms));
- if (denied && !error) {
- /*
- * check every profile in file label that was not tested
- * in the initial check above.
- *
- * TODO: cache full perms so this only happens because of
- * conditionals
- * TODO: don't audit here
- */
- if (label == flabel)
- error = fn_for_each(label, profile,
- profile_path_perm(op, profile, &file->f_path,
- buffer, request, &cond, flags,
- &perms));
- else
- error = fn_for_each_not_in_set(label, flabel, profile,
- profile_path_perm(op, profile, &file->f_path,
- buffer, request, &cond, flags,
- &perms));
- }
- if (!error)
- update_file_ctx(file_ctx(file), label, request);
-
- put_buffers(buffer);
+ error = aa_audit_file(profile, &lperms, OP_LINK, request,
+ lname, tname, cond.uid, info, error);
+ kfree(buffer);
+ kfree(buffer2);
return error;
}
@@ -569,114 +440,20 @@ static int __file_path_perm(const char *op, struct aa_label *label,
/**
* aa_file_perm - do permission revalidation check & audit for @file
* @op: operation being checked
- * @label: label being enforced (NOT NULL)
+ * @profile: profile being enforced (NOT NULL)
* @file: file to revalidate access permissions on (NOT NULL)
* @request: requested permissions
*
* Returns: %0 if access allowed else error
*/
-int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
+int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file,
u32 request)
{
- struct aa_file_ctx *fctx;
- struct aa_label *flabel;
- u32 denied;
- int error = 0;
-
- AA_BUG(!label);
- AA_BUG(!file);
-
- fctx = file_ctx(file);
-
- rcu_read_lock();
- flabel = rcu_dereference(fctx->label);
- AA_BUG(!flabel);
-
- /* revalidate access, if task is unconfined, or the cached cred
- * doesn't match or if the request is for more permissions than
- * was granted.
- *
- * Note: the test for !unconfined(flabel) is to handle file
- * delegation from unconfined tasks
- */
- denied = request & ~fctx->allow;
- if (unconfined(label) || unconfined(flabel) ||
- (!denied && aa_label_is_subset(flabel, label)))
- goto done;
-
- /* TODO: label cross check */
-
- if (file->f_path.mnt && path_mediated_fs(file->f_path.dentry))
- error = __file_path_perm(op, label, flabel, file, request,
- denied);
-
-done:
- rcu_read_unlock();
-
- return error;
-}
-
-static void revalidate_tty(struct aa_label *label)
-{
- struct tty_struct *tty;
- int drop_tty = 0;
-
- tty = get_current_tty();
- if (!tty)
- return;
-
- spin_lock(&tty->files_lock);
- if (!list_empty(&tty->tty_files)) {
- struct tty_file_private *file_priv;
- struct file *file;
- /* TODO: Revalidate access to controlling tty. */
- file_priv = list_first_entry(&tty->tty_files,
- struct tty_file_private, list);
- file = file_priv->file;
-
- if (aa_file_perm(OP_INHERIT, label, file, MAY_READ | MAY_WRITE))
- drop_tty = 1;
- }
- spin_unlock(&tty->files_lock);
- tty_kref_put(tty);
-
- if (drop_tty)
- no_tty();
-}
-
-static int match_file(const void *p, struct file *file, unsigned int fd)
-{
- struct aa_label *label = (struct aa_label *)p;
-
- if (aa_file_perm(OP_INHERIT, label, file, aa_map_file_to_perms(file)))
- return fd + 1;
- return 0;
-}
-
+ struct path_cond cond = {
+ .uid = file_inode(file)->i_uid,
+ .mode = file_inode(file)->i_mode
+ };
-/* based on selinux's flush_unauthorized_files */
-void aa_inherit_files(const struct cred *cred, struct files_struct *files)
-{
- struct aa_label *label = aa_get_newest_cred_label(cred);
- struct file *devnull = NULL;
- unsigned int n;
-
- revalidate_tty(label);
-
- /* Revalidate access to inherited open files. */
- n = iterate_fd(files, 0, match_file, label);
- if (!n) /* none found? */
- goto out;
-
- devnull = dentry_open(&aa_null, O_RDWR, cred);
- if (IS_ERR(devnull))
- devnull = NULL;
- /* replace all the matching ones with this */
- do {
- replace_fd(n - 1, devnull, 0);
- } while ((n = iterate_fd(files, n, match_file, label)) != 0);
- if (devnull)
- fput(devnull);
-out:
- aa_put_label(label);
+ return aa_path_perm(op, profile, &file->f_path, PATH_DELEGATE_DELETED,
+ request, &cond);
}
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
index 829082c35faa..1750cc0721c1 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -4,7 +4,7 @@
* This file contains AppArmor basic global
*
* Copyright (C) 1998-2008 Novell/SUSE
- * Copyright 2009-2017 Canonical Ltd.
+ * Copyright 2009-2010 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -27,12 +27,8 @@
#define AA_CLASS_NET 4
#define AA_CLASS_RLIMITS 5
#define AA_CLASS_DOMAIN 6
-#define AA_CLASS_MOUNT 7
-#define AA_CLASS_PTRACE 9
-#define AA_CLASS_SIGNAL 10
-#define AA_CLASS_LABEL 16
-#define AA_CLASS_LAST AA_CLASS_LABEL
+#define AA_CLASS_LAST AA_CLASS_DOMAIN
/* Control parameters settable through module/boot flags */
extern enum audit_mode aa_g_audit;
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index bd689114bf93..120a798b5bb0 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -17,49 +17,49 @@
extern struct path aa_null;
-enum aa_sfs_type {
- AA_SFS_TYPE_BOOLEAN,
- AA_SFS_TYPE_STRING,
- AA_SFS_TYPE_U64,
- AA_SFS_TYPE_FOPS,
- AA_SFS_TYPE_DIR,
+enum aa_fs_type {
+ AA_FS_TYPE_BOOLEAN,
+ AA_FS_TYPE_STRING,
+ AA_FS_TYPE_U64,
+ AA_FS_TYPE_FOPS,
+ AA_FS_TYPE_DIR,
};
-struct aa_sfs_entry;
+struct aa_fs_entry;
-struct aa_sfs_entry {
+struct aa_fs_entry {
const char *name;
struct dentry *dentry;
umode_t mode;
- enum aa_sfs_type v_type;
+ enum aa_fs_type v_type;
union {
bool boolean;
char *string;
unsigned long u64;
- struct aa_sfs_entry *files;
+ struct aa_fs_entry *files;
} v;
const struct file_operations *file_ops;
};
-extern const struct file_operations aa_sfs_seq_file_ops;
+extern const struct file_operations aa_fs_seq_file_ops;
-#define AA_SFS_FILE_BOOLEAN(_name, _value) \
+#define AA_FS_FILE_BOOLEAN(_name, _value) \
{ .name = (_name), .mode = 0444, \
- .v_type = AA_SFS_TYPE_BOOLEAN, .v.boolean = (_value), \
- .file_ops = &aa_sfs_seq_file_ops }
-#define AA_SFS_FILE_STRING(_name, _value) \
+ .v_type = AA_FS_TYPE_BOOLEAN, .v.boolean = (_value), \
+ .file_ops = &aa_fs_seq_file_ops }
+#define AA_FS_FILE_STRING(_name, _value) \
{ .name = (_name), .mode = 0444, \
- .v_type = AA_SFS_TYPE_STRING, .v.string = (_value), \
- .file_ops = &aa_sfs_seq_file_ops }
-#define AA_SFS_FILE_U64(_name, _value) \
+ .v_type = AA_FS_TYPE_STRING, .v.string = (_value), \
+ .file_ops = &aa_fs_seq_file_ops }
+#define AA_FS_FILE_U64(_name, _value) \
{ .name = (_name), .mode = 0444, \
- .v_type = AA_SFS_TYPE_U64, .v.u64 = (_value), \
- .file_ops = &aa_sfs_seq_file_ops }
-#define AA_SFS_FILE_FOPS(_name, _mode, _fops) \
- { .name = (_name), .v_type = AA_SFS_TYPE_FOPS, \
+ .v_type = AA_FS_TYPE_U64, .v.u64 = (_value), \
+ .file_ops = &aa_fs_seq_file_ops }
+#define AA_FS_FILE_FOPS(_name, _mode, _fops) \
+ { .name = (_name), .v_type = AA_FS_TYPE_FOPS, \
.mode = (_mode), .file_ops = (_fops) }
-#define AA_SFS_DIR(_name, _value) \
- { .name = (_name), .v_type = AA_SFS_TYPE_DIR, .v.files = (_value) }
+#define AA_FS_DIR(_name, _value) \
+ { .name = (_name), .v_type = AA_FS_TYPE_DIR, .v.files = (_value) }
extern void __init aa_destroy_aafs(void);
@@ -74,7 +74,6 @@ enum aafs_ns_type {
AAFS_NS_LOAD,
AAFS_NS_REPLACE,
AAFS_NS_REMOVE,
- AAFS_NS_REVISION,
AAFS_NS_COUNT,
AAFS_NS_MAX_COUNT,
AAFS_NS_SIZE,
@@ -103,22 +102,16 @@ enum aafs_prof_type {
#define ns_subload(X) ((X)->dents[AAFS_NS_LOAD])
#define ns_subreplace(X) ((X)->dents[AAFS_NS_REPLACE])
#define ns_subremove(X) ((X)->dents[AAFS_NS_REMOVE])
-#define ns_subrevision(X) ((X)->dents[AAFS_NS_REVISION])
#define prof_dir(X) ((X)->dents[AAFS_PROF_DIR])
#define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS])
-void __aa_bump_ns_revision(struct aa_ns *ns);
-void __aafs_profile_rmdir(struct aa_profile *profile);
-void __aafs_profile_migrate_dents(struct aa_profile *old,
+void __aa_fs_profile_rmdir(struct aa_profile *profile);
+void __aa_fs_profile_migrate_dents(struct aa_profile *old,
struct aa_profile *new);
-int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
-void __aafs_ns_rmdir(struct aa_ns *ns);
-int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
- struct dentry *dent);
-
-struct aa_loaddata;
-void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata);
-int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata);
+int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
+void __aa_fs_ns_rmdir(struct aa_ns *ns);
+int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent,
+ const char *name);
#endif /* __AA_APPARMORFS_H */
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
index eb7170ccbd65..947b0447c9bf 100644
--- a/security/apparmor/include/audit.h
+++ b/security/apparmor/include/audit.h
@@ -22,7 +22,8 @@
#include <linux/slab.h>
#include "file.h"
-#include "label.h"
+
+struct aa_profile;
extern const char *const audit_mode_names[];
#define AUDIT_MAX_INDEX 5
@@ -64,16 +65,10 @@ enum audit_type {
#define OP_GETATTR "getattr"
#define OP_OPEN "open"
-#define OP_FRECEIVE "file_receive"
#define OP_FPERM "file_perm"
#define OP_FLOCK "file_lock"
#define OP_FMMAP "file_mmap"
#define OP_FMPROT "file_mprotect"
-#define OP_INHERIT "file_inherit"
-
-#define OP_PIVOTROOT "pivotroot"
-#define OP_MOUNT "mount"
-#define OP_UMOUNT "umount"
#define OP_CREATE "create"
#define OP_POST_CREATE "post_create"
@@ -90,15 +85,12 @@ enum audit_type {
#define OP_SHUTDOWN "socket_shutdown"
#define OP_PTRACE "ptrace"
-#define OP_SIGNAL "signal"
#define OP_EXEC "exec"
#define OP_CHANGE_HAT "change_hat"
#define OP_CHANGE_PROFILE "change_profile"
#define OP_CHANGE_ONEXEC "change_onexec"
-#define OP_STACK "stack"
-#define OP_STACK_ONEXEC "stack_onexec"
#define OP_SETPROCATTR "setprocattr"
#define OP_SETRLIMIT "setrlimit"
@@ -110,45 +102,35 @@ enum audit_type {
struct apparmor_audit_data {
int error;
- int type;
const char *op;
- struct aa_label *label;
+ int type;
+ void *profile;
const char *name;
const char *info;
- u32 request;
- u32 denied;
union {
/* these entries require a custom callback fn */
struct {
- struct aa_label *peer;
- union {
- struct {
- const char *target;
- kuid_t ouid;
- } fs;
- struct {
- int rlim;
- unsigned long max;
- } rlim;
- int signal;
- };
+ struct aa_profile *peer;
+ struct {
+ const char *target;
+ u32 request;
+ u32 denied;
+ kuid_t ouid;
+ } fs;
struct {
int type, protocol;
struct sock *sk;
} net;
};
struct {
- struct aa_profile *profile;
- const char *ns;
+ const char *name;
long pos;
+ const char *ns;
} iface;
struct {
- const char *src_name;
- const char *type;
- const char *trans;
- const char *data;
- unsigned long flags;
- } mnt;
+ int rlim;
+ unsigned long max;
+ } rlim;
};
};
diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h
index e0304e2aeb7f..fc3fa381d850 100644
--- a/security/apparmor/include/capability.h
+++ b/security/apparmor/include/capability.h
@@ -19,12 +19,11 @@
#include "apparmorfs.h"
-struct aa_label;
+struct aa_profile;
/* aa_caps - confinement data for capabilities
* @allowed: capabilities mask
* @audit: caps that are to be audited
- * @denied: caps that are explicitly denied
* @quiet: caps that should not be audited
* @kill: caps that when requested will result in the task being killed
* @extended: caps that are subject finer grained mediation
@@ -32,15 +31,14 @@ struct aa_label;
struct aa_caps {
kernel_cap_t allow;
kernel_cap_t audit;
- kernel_cap_t denied;
kernel_cap_t quiet;
kernel_cap_t kill;
kernel_cap_t extended;
};
-extern struct aa_sfs_entry aa_sfs_entry_caps[];
+extern struct aa_fs_entry aa_fs_entry_caps[];
-int aa_capable(struct aa_label *label, int cap, int audit);
+int aa_capable(struct aa_profile *profile, int cap, int audit);
static inline void aa_free_cap_rules(struct aa_caps *caps)
{
diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h
index 6ae07e9aaa17..5b18fedab4c8 100644
--- a/security/apparmor/include/context.h
+++ b/security/apparmor/include/context.h
@@ -19,28 +19,60 @@
#include <linux/slab.h>
#include <linux/sched.h>
-#include "label.h"
+#include "policy.h"
#include "policy_ns.h"
#define cred_ctx(X) ((X)->security)
#define current_ctx() cred_ctx(current_cred())
+/* struct aa_file_ctx - the AppArmor context the file was opened in
+ * @perms: the permission the file was opened with
+ *
+ * The file_ctx could currently be directly stored in file->f_security
+ * as the profile reference is now stored in the f_cred. However the
+ * ctx struct will expand in the future so we keep the struct.
+ */
+struct aa_file_ctx {
+ u16 allow;
+};
+
+/**
+ * aa_alloc_file_context - allocate file_ctx
+ * @gfp: gfp flags for allocation
+ *
+ * Returns: file_ctx or NULL on failure
+ */
+static inline struct aa_file_ctx *aa_alloc_file_context(gfp_t gfp)
+{
+ return kzalloc(sizeof(struct aa_file_ctx), gfp);
+}
+
+/**
+ * aa_free_file_context - free a file_ctx
+ * @ctx: file_ctx to free (MAYBE_NULL)
+ */
+static inline void aa_free_file_context(struct aa_file_ctx *ctx)
+{
+ if (ctx)
+ kzfree(ctx);
+}
+
/**
* struct aa_task_ctx - primary label for confined tasks
- * @label: the current label (NOT NULL)
- * @exec: label to transition to on next exec (MAYBE NULL)
- * @previous: label the task may return to (MAYBE NULL)
- * @token: magic value the task must know for returning to @previous
+ * @profile: the current profile (NOT NULL)
+ * @exec: profile to transition to on next exec (MAYBE NULL)
+ * @previous: profile the task may return to (MAYBE NULL)
+ * @token: magic value the task must know for returning to @previous_profile
*
- * Contains the task's current label (which could change due to
+ * Contains the task's current profile (which could change due to
* change_hat). Plus the hat_magic needed during change_hat.
*
* TODO: make so a task can be confined by a stack of contexts
*/
struct aa_task_ctx {
- struct aa_label *label;
- struct aa_label *onexec;
- struct aa_label *previous;
+ struct aa_profile *profile;
+ struct aa_profile *onexec;
+ struct aa_profile *previous;
u64 token;
};
@@ -48,51 +80,40 @@ struct aa_task_ctx *aa_alloc_task_context(gfp_t flags);
void aa_free_task_context(struct aa_task_ctx *ctx);
void aa_dup_task_context(struct aa_task_ctx *new,
const struct aa_task_ctx *old);
-int aa_replace_current_label(struct aa_label *label);
-int aa_set_current_onexec(struct aa_label *label, bool stack);
-int aa_set_current_hat(struct aa_label *label, u64 token);
-int aa_restore_previous_label(u64 cookie);
-struct aa_label *aa_get_task_label(struct task_struct *task);
+int aa_replace_current_profile(struct aa_profile *profile);
+int aa_set_current_onexec(struct aa_profile *profile);
+int aa_set_current_hat(struct aa_profile *profile, u64 token);
+int aa_restore_previous_profile(u64 cookie);
+struct aa_profile *aa_get_task_profile(struct task_struct *task);
/**
- * aa_cred_raw_label - obtain cred's label
- * @cred: cred to obtain label from (NOT NULL)
+ * aa_cred_profile - obtain cred's profiles
+ * @cred: cred to obtain profiles from (NOT NULL)
*
- * Returns: confining label
+ * Returns: confining profile
*
* does NOT increment reference count
*/
-static inline struct aa_label *aa_cred_raw_label(const struct cred *cred)
+static inline struct aa_profile *aa_cred_profile(const struct cred *cred)
{
struct aa_task_ctx *ctx = cred_ctx(cred);
- AA_BUG(!ctx || !ctx->label);
- return ctx->label;
-}
-
-/**
- * aa_get_newest_cred_label - obtain the newest label on a cred
- * @cred: cred to obtain label from (NOT NULL)
- *
- * Returns: newest version of confining label
- */
-static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred)
-{
- return aa_get_newest_label(aa_cred_raw_label(cred));
+ AA_BUG(!ctx || !ctx->profile);
+ return ctx->profile;
}
/**
- * __aa_task_raw_label - retrieve another task's label
+ * __aa_task_profile - retrieve another task's profile
* @task: task to query (NOT NULL)
*
- * Returns: @task's label without incrementing its ref count
+ * Returns: @task's profile without incrementing its ref count
*
* If @task != current needs to be called in RCU safe critical section
*/
-static inline struct aa_label *__aa_task_raw_label(struct task_struct *task)
+static inline struct aa_profile *__aa_task_profile(struct task_struct *task)
{
- return aa_cred_raw_label(__task_cred(task));
+ return aa_cred_profile(__task_cred(task));
}
/**
@@ -103,114 +124,50 @@ static inline struct aa_label *__aa_task_raw_label(struct task_struct *task)
*/
static inline bool __aa_task_is_confined(struct task_struct *task)
{
- return !unconfined(__aa_task_raw_label(task));
+ return !unconfined(__aa_task_profile(task));
}
/**
- * aa_current_raw_label - find the current tasks confining label
+ * __aa_current_profile - find the current tasks confining profile
*
- * Returns: up to date confining label or the ns unconfined label (NOT NULL)
+ * Returns: up to date confining profile or the ns unconfined profile (NOT NULL)
*
* This fn will not update the tasks cred to the most up to date version
- * of the label so it is safe to call when inside of locks.
+ * of the profile so it is safe to call when inside of locks.
*/
-static inline struct aa_label *aa_current_raw_label(void)
+static inline struct aa_profile *__aa_current_profile(void)
{
- return aa_cred_raw_label(current_cred());
+ return aa_cred_profile(current_cred());
}
/**
- * aa_get_current_label - get the newest version of the current tasks label
- *
- * Returns: newest version of confining label (NOT NULL)
+ * aa_current_profile - find the current tasks confining profile and do updates
*
- * This fn will not update the tasks cred, so it is safe inside of locks
+ * Returns: up to date confining profile or the ns unconfined profile (NOT NULL)
*
- * The returned reference must be put with aa_put_label()
+ * This fn will update the tasks cred structure if the profile has been
+ * replaced. Not safe to call inside locks
*/
-static inline struct aa_label *aa_get_current_label(void)
+static inline struct aa_profile *aa_current_profile(void)
{
- struct aa_label *l = aa_current_raw_label();
+ const struct aa_task_ctx *ctx = current_ctx();
+ struct aa_profile *profile;
- if (label_is_stale(l))
- return aa_get_newest_label(l);
- return aa_get_label(l);
-}
-
-#define __end_current_label_crit_section(X) end_current_label_crit_section(X)
+ AA_BUG(!ctx || !ctx->profile);
-/**
- * end_label_crit_section - put a reference found with begin_current_label..
- * @label: label reference to put
- *
- * Should only be used with a reference obtained with
- * begin_current_label_crit_section and never used in situations where the
- * task cred may be updated
- */
-static inline void end_current_label_crit_section(struct aa_label *label)
-{
- if (label != aa_current_raw_label())
- aa_put_label(label);
-}
-
-/**
- * __begin_current_label_crit_section - current's confining label
- *
- * Returns: up to date confining label or the ns unconfined label (NOT NULL)
- *
- * safe to call inside locks
- *
- * The returned reference must be put with __end_current_label_crit_section()
- * This must NOT be used if the task cred could be updated within the
- * critical section between __begin_current_label_crit_section() ..
- * __end_current_label_crit_section()
- */
-static inline struct aa_label *__begin_current_label_crit_section(void)
-{
- struct aa_label *label = aa_current_raw_label();
-
- if (label_is_stale(label))
- label = aa_get_newest_label(label);
-
- return label;
-}
-
-/**
- * begin_current_label_crit_section - current's confining label and update it
- *
- * Returns: up to date confining label or the ns unconfined label (NOT NULL)
- *
- * Not safe to call inside locks
- *
- * The returned reference must be put with end_current_label_crit_section()
- * This must NOT be used if the task cred could be updated within the
- * critical section between begin_current_label_crit_section() ..
- * end_current_label_crit_section()
- */
-static inline struct aa_label *begin_current_label_crit_section(void)
-{
- struct aa_label *label = aa_current_raw_label();
-
- if (label_is_stale(label)) {
- label = aa_get_newest_label(label);
- if (aa_replace_current_label(label) == 0)
- /* task cred will keep the reference */
- aa_put_label(label);
+ if (profile_is_stale(ctx->profile)) {
+ profile = aa_get_newest_profile(ctx->profile);
+ aa_replace_current_profile(profile);
+ aa_put_profile(profile);
+ ctx = current_ctx();
}
- return label;
+ return ctx->profile;
}
static inline struct aa_ns *aa_get_current_ns(void)
{
- struct aa_label *label;
- struct aa_ns *ns;
-
- label = __begin_current_label_crit_section();
- ns = aa_get_ns(labels_ns(label));
- __end_current_label_crit_section(label);
-
- return ns;
+ return aa_get_ns(__aa_current_profile()->ns);
}
/**
@@ -219,8 +176,8 @@ static inline struct aa_ns *aa_get_current_ns(void)
*/
static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx)
{
- aa_put_label(ctx->previous);
- aa_put_label(ctx->onexec);
+ aa_put_profile(ctx->previous);
+ aa_put_profile(ctx->onexec);
ctx->previous = NULL;
ctx->onexec = NULL;
ctx->token = 0;
diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h
index db27403346c5..30544729878a 100644
--- a/security/apparmor/include/domain.h
+++ b/security/apparmor/include/domain.h
@@ -15,8 +15,6 @@
#include <linux/binfmts.h>
#include <linux/types.h>
-#include "label.h"
-
#ifndef __AA_DOMAIN_H
#define __AA_DOMAIN_H
@@ -25,20 +23,14 @@ struct aa_domain {
char **table;
};
-#define AA_CHANGE_NOFLAGS 0
-#define AA_CHANGE_TEST 1
-#define AA_CHANGE_CHILD 2
-#define AA_CHANGE_ONEXEC 4
-#define AA_CHANGE_STACK 8
-
-struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
- const char **name);
-
int apparmor_bprm_set_creds(struct linux_binprm *bprm);
int apparmor_bprm_secureexec(struct linux_binprm *bprm);
+void apparmor_bprm_committing_creds(struct linux_binprm *bprm);
+void apparmor_bprm_committed_creds(struct linux_binprm *bprm);
void aa_free_domain_entries(struct aa_domain *domain);
-int aa_change_hat(const char *hats[], int count, u64 token, int flags);
-int aa_change_profile(const char *fqname, int flags);
+int aa_change_hat(const char *hats[], int count, u64 token, bool permtest);
+int aa_change_profile(const char *fqname, bool onexec, bool permtest,
+ bool stack);
#endif /* __AA_DOMAIN_H */
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h
index 001e40073ff9..38f821bf49b6 100644
--- a/security/apparmor/include/file.h
+++ b/security/apparmor/include/file.h
@@ -15,73 +15,38 @@
#ifndef __AA_FILE_H
#define __AA_FILE_H
-#include <linux/spinlock.h>
-
#include "domain.h"
#include "match.h"
-#include "perms.h"
struct aa_profile;
struct path;
-#define mask_mode_t(X) (X & (MAY_EXEC | MAY_WRITE | MAY_READ | MAY_APPEND))
+/*
+ * We use MAY_EXEC, MAY_WRITE, MAY_READ, MAY_APPEND and the following flags
+ * for profile permissions
+ */
+#define AA_MAY_CREATE 0x0010
+#define AA_MAY_DELETE 0x0020
+#define AA_MAY_META_WRITE 0x0040
+#define AA_MAY_META_READ 0x0080
+
+#define AA_MAY_CHMOD 0x0100
+#define AA_MAY_CHOWN 0x0200
+#define AA_MAY_LOCK 0x0400
+#define AA_EXEC_MMAP 0x0800
+
+#define AA_MAY_LINK 0x1000
+#define AA_LINK_SUBSET AA_MAY_LOCK /* overlaid */
+#define AA_MAY_ONEXEC 0x40000000 /* exec allows onexec */
+#define AA_MAY_CHANGE_PROFILE 0x80000000
+#define AA_MAY_CHANGEHAT 0x80000000 /* ctrl auditing only */
#define AA_AUDIT_FILE_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND |\
AA_MAY_CREATE | AA_MAY_DELETE | \
- AA_MAY_GETATTR | AA_MAY_SETATTR | \
+ AA_MAY_META_READ | AA_MAY_META_WRITE | \
AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \
AA_EXEC_MMAP | AA_MAY_LINK)
-#define file_ctx(X) ((struct aa_file_ctx *)(X)->f_security)
-
-/* struct aa_file_ctx - the AppArmor context the file was opened in
- * @lock: lock to update the ctx
- * @label: label currently cached on the ctx
- * @perms: the permission the file was opened with
- */
-struct aa_file_ctx {
- spinlock_t lock;
- struct aa_label __rcu *label;
- u32 allow;
-};
-
-/**
- * aa_alloc_file_ctx - allocate file_ctx
- * @label: initial label of task creating the file
- * @gfp: gfp flags for allocation
- *
- * Returns: file_ctx or NULL on failure
- */
-static inline struct aa_file_ctx *aa_alloc_file_ctx(struct aa_label *label,
- gfp_t gfp)
-{
- struct aa_file_ctx *ctx;
-
- ctx = kzalloc(sizeof(struct aa_file_ctx), gfp);
- if (ctx) {
- spin_lock_init(&ctx->lock);
- rcu_assign_pointer(ctx->label, aa_get_label(label));
- }
- return ctx;
-}
-
-/**
- * aa_free_file_ctx - free a file_ctx
- * @ctx: file_ctx to free (MAYBE_NULL)
- */
-static inline void aa_free_file_ctx(struct aa_file_ctx *ctx)
-{
- if (ctx) {
- aa_put_label(rcu_access_pointer(ctx->label));
- kzfree(ctx);
- }
-}
-
-static inline struct aa_label *aa_get_file_label(struct aa_file_ctx *ctx)
-{
- return aa_get_label_rcu(&ctx->label);
-}
-
/*
* The xindex is broken into 3 parts
* - index - an index into either the exec name table or the variable table
@@ -110,6 +75,25 @@ struct path_cond {
umode_t mode;
};
+/* struct file_perms - file permission
+ * @allow: mask of permissions that are allowed
+ * @audit: mask of permissions to force an audit message for
+ * @quiet: mask of permissions to quiet audit messages for
+ * @kill: mask of permissions that when matched will kill the task
+ * @xindex: exec transition index if @allow contains MAY_EXEC
+ *
+ * The @audit and @queit mask should be mutually exclusive.
+ */
+struct file_perms {
+ u32 allow;
+ u32 audit;
+ u32 quiet;
+ u32 kill;
+ u16 xindex;
+};
+
+extern struct file_perms nullperms;
+
#define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill)
/* FIXME: split perms from dfa and match this to description
@@ -160,10 +144,9 @@ static inline u16 dfa_map_xindex(u16 mask)
#define dfa_other_xindex(dfa, state) \
dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff)
-int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
+int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
const char *op, u32 request, const char *name,
- const char *target, struct aa_label *tlabel, kuid_t ouid,
- const char *info, int error);
+ const char *target, kuid_t ouid, const char *info, int error);
/**
* struct aa_file_rules - components used for file rule permissions
@@ -184,27 +167,20 @@ struct aa_file_rules {
/* TODO: add delegate table */
};
-struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state,
- struct path_cond *cond);
unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
const char *name, struct path_cond *cond,
- struct aa_perms *perms);
+ struct file_perms *perms);
-int __aa_path_perm(const char *op, struct aa_profile *profile,
- const char *name, u32 request, struct path_cond *cond,
- int flags, struct aa_perms *perms);
-int aa_path_perm(const char *op, struct aa_label *label,
+int aa_path_perm(const char *op, struct aa_profile *profile,
const struct path *path, int flags, u32 request,
struct path_cond *cond);
-int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
+int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry);
-int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
+int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file,
u32 request);
-void aa_inherit_files(const struct cred *cred, struct files_struct *files);
-
static inline void aa_free_file_rules(struct aa_file_rules *rules)
{
aa_put_dfa(rules->dfa);
diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h
index 5ffc218d1e74..288ca76e2fb1 100644
--- a/security/apparmor/include/ipc.h
+++ b/security/apparmor/include/ipc.h
@@ -4,7 +4,7 @@
* This file contains AppArmor ipc mediation function definitions.
*
* Copyright (C) 1998-2008 Novell/SUSE
- * Copyright 2009-2017 Canonical Ltd.
+ * Copyright 2009-2010 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -19,22 +19,10 @@
struct aa_profile;
-#define AA_PTRACE_TRACE MAY_WRITE
-#define AA_PTRACE_READ MAY_READ
-#define AA_MAY_BE_TRACED AA_MAY_APPEND
-#define AA_MAY_BE_READ AA_MAY_CREATE
-#define PTRACE_PERM_SHIFT 2
+int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee,
+ unsigned int mode);
-#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \
- AA_MAY_BE_READ | AA_MAY_BE_TRACED)
-#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE)
-
-#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \
- "segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \
- "xcpu xfsz vtalrm prof winch io pwr sys emt lost"
-
-int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
- u32 request);
-int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig);
+int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,
+ unsigned int mode);
#endif /* __AA_IPC_H */
diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h
deleted file mode 100644
index af22dcbbcb8a..000000000000
--- a/security/apparmor/include/label.h
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * AppArmor security module
- *
- * This file contains AppArmor label definitions
- *
- * Copyright 2017 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
- */
-
-#ifndef __AA_LABEL_H
-#define __AA_LABEL_H
-
-#include <linux/atomic.h>
-#include <linux/audit.h>
-#include <linux/rbtree.h>
-#include <linux/rcupdate.h>
-
-#include "apparmor.h"
-#include "lib.h"
-
-struct aa_ns;
-
-#define LOCAL_VEC_ENTRIES 8
-#define DEFINE_VEC(T, V) \
- struct aa_ ## T *(_ ## V ## _localtmp)[LOCAL_VEC_ENTRIES]; \
- struct aa_ ## T **(V)
-
-#define vec_setup(T, V, N, GFP) \
-({ \
- if ((N) <= LOCAL_VEC_ENTRIES) { \
- typeof(N) i; \
- (V) = (_ ## V ## _localtmp); \
- for (i = 0; i < (N); i++) \
- (V)[i] = NULL; \
- } else \
- (V) = kzalloc(sizeof(struct aa_ ## T *) * (N), (GFP)); \
- (V) ? 0 : -ENOMEM; \
-})
-
-#define vec_cleanup(T, V, N) \
-do { \
- int i; \
- for (i = 0; i < (N); i++) { \
- if (!IS_ERR_OR_NULL((V)[i])) \
- aa_put_ ## T((V)[i]); \
- } \
- if ((V) != _ ## V ## _localtmp) \
- kfree(V); \
-} while (0)
-
-#define vec_last(VEC, SIZE) ((VEC)[(SIZE) - 1])
-#define vec_ns(VEC, SIZE) (vec_last((VEC), (SIZE))->ns)
-#define vec_labelset(VEC, SIZE) (&vec_ns((VEC), (SIZE))->labels)
-#define cleanup_domain_vec(V, L) cleanup_label_vec((V), (L)->size)
-
-struct aa_profile;
-#define VEC_FLAG_TERMINATE 1
-int aa_vec_unique(struct aa_profile **vec, int n, int flags);
-struct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len,
- gfp_t gfp);
-#define aa_sort_and_merge_vec(N, V) \
- aa_sort_and_merge_profiles((N), (struct aa_profile **)(V))
-
-
-/* struct aa_labelset - set of labels for a namespace
- *
- * Labels are reference counted; aa_labelset does not contribute to label
- * reference counts. Once a label's last refcount is put it is removed from
- * the set.
- */
-struct aa_labelset {
- rwlock_t lock;
-
- struct rb_root root;
-};
-
-#define __labelset_for_each(LS, N) \
- for ((N) = rb_first(&(LS)->root); (N); (N) = rb_next(N))
-
-void aa_labelset_destroy(struct aa_labelset *ls);
-void aa_labelset_init(struct aa_labelset *ls);
-
-
-enum label_flags {
- FLAG_HAT = 1, /* profile is a hat */
- FLAG_UNCONFINED = 2, /* label unconfined only if all */
- FLAG_NULL = 4, /* profile is null learning profile */
- FLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */
- FLAG_IMMUTIBLE = 0x10, /* don't allow changes/replacement */
- FLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */
- FLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */
- FLAG_NS_COUNT = 0x80, /* carries NS ref count */
- FLAG_IN_TREE = 0x100, /* label is in tree */
- FLAG_PROFILE = 0x200, /* label is a profile */
- FLAG_EXPLICIT = 0x400, /* explicit static label */
- FLAG_STALE = 0x800, /* replaced/removed */
- FLAG_RENAMED = 0x1000, /* label has renaming in it */
- FLAG_REVOKED = 0x2000, /* label has revocation in it */
-
- /* These flags must correspond with PATH_flags */
- /* TODO: add new path flags */
-};
-
-struct aa_label;
-struct aa_proxy {
- struct kref count;
- struct aa_label __rcu *label;
-};
-
-struct label_it {
- int i, j;
-};
-
-/* struct aa_label - lazy labeling struct
- * @count: ref count of active users
- * @node: rbtree position
- * @rcu: rcu callback struct
- * @proxy: is set to the label that replaced this label
- * @hname: text representation of the label (MAYBE_NULL)
- * @flags: stale and other flags - values may change under label set lock
- * @secid: secid that references this label
- * @size: number of entries in @ent[]
- * @ent: set of profiles for label, actual size determined by @size
- */
-struct aa_label {
- struct kref count;
- struct rb_node node;
- struct rcu_head rcu;
- struct aa_proxy *proxy;
- __counted char *hname;
- long flags;
- u32 secid;
- int size;
- struct aa_profile *vec[];
-};
-
-#define last_error(E, FN) \
-do { \
- int __subE = (FN); \
- if (__subE) \
- (E) = __subE; \
-} while (0)
-
-#define label_isprofile(X) ((X)->flags & FLAG_PROFILE)
-#define label_unconfined(X) ((X)->flags & FLAG_UNCONFINED)
-#define unconfined(X) label_unconfined(X)
-#define label_is_stale(X) ((X)->flags & FLAG_STALE)
-#define __label_make_stale(X) ((X)->flags |= FLAG_STALE)
-#define labels_ns(X) (vec_ns(&((X)->vec[0]), (X)->size))
-#define labels_set(X) (&labels_ns(X)->labels)
-#define labels_profile(X) ((X)->vec[(X)->size - 1])
-
-
-int aa_label_next_confined(struct aa_label *l, int i);
-
-/* for each profile in a label */
-#define label_for_each(I, L, P) \
- for ((I).i = 0; ((P) = (L)->vec[(I).i]); ++((I).i))
-
-/* assumes break/goto ended label_for_each */
-#define label_for_each_cont(I, L, P) \
- for (++((I).i); ((P) = (L)->vec[(I).i]); ++((I).i))
-
-#define next_comb(I, L1, L2) \
-do { \
- (I).j++; \
- if ((I).j >= (L2)->size) { \
- (I).i++; \
- (I).j = 0; \
- } \
-} while (0)
-
-
-/* for each combination of P1 in L1, and P2 in L2 */
-#define label_for_each_comb(I, L1, L2, P1, P2) \
-for ((I).i = (I).j = 0; \
- ((P1) = (L1)->vec[(I).i]) && ((P2) = (L2)->vec[(I).j]); \
- (I) = next_comb(I, L1, L2))
-
-#define fn_for_each_comb(L1, L2, P1, P2, FN) \
-({ \
- struct label_it i; \
- int __E = 0; \
- label_for_each_comb(i, (L1), (L2), (P1), (P2)) { \
- last_error(__E, (FN)); \
- } \
- __E; \
-})
-
-/* for each profile that is enforcing confinement in a label */
-#define label_for_each_confined(I, L, P) \
- for ((I).i = aa_label_next_confined((L), 0); \
- ((P) = (L)->vec[(I).i]); \
- (I).i = aa_label_next_confined((L), (I).i + 1))
-
-#define label_for_each_in_merge(I, A, B, P) \
- for ((I).i = (I).j = 0; \
- ((P) = aa_label_next_in_merge(&(I), (A), (B))); \
- )
-
-#define label_for_each_not_in_set(I, SET, SUB, P) \
- for ((I).i = (I).j = 0; \
- ((P) = __aa_label_next_not_in_set(&(I), (SET), (SUB))); \
- )
-
-#define next_in_ns(i, NS, L) \
-({ \
- typeof(i) ___i = (i); \
- while ((L)->vec[___i] && (L)->vec[___i]->ns != (NS)) \
- (___i)++; \
- (___i); \
-})
-
-#define label_for_each_in_ns(I, NS, L, P) \
- for ((I).i = next_in_ns(0, (NS), (L)); \
- ((P) = (L)->vec[(I).i]); \
- (I).i = next_in_ns((I).i + 1, (NS), (L)))
-
-#define fn_for_each_in_ns(L, P, FN) \
-({ \
- struct label_it __i; \
- struct aa_ns *__ns = labels_ns(L); \
- int __E = 0; \
- label_for_each_in_ns(__i, __ns, (L), (P)) { \
- last_error(__E, (FN)); \
- } \
- __E; \
-})
-
-
-#define fn_for_each_XXX(L, P, FN, ...) \
-({ \
- struct label_it i; \
- int __E = 0; \
- label_for_each ## __VA_ARGS__(i, (L), (P)) { \
- last_error(__E, (FN)); \
- } \
- __E; \
-})
-
-#define fn_for_each(L, P, FN) fn_for_each_XXX(L, P, FN)
-#define fn_for_each_confined(L, P, FN) fn_for_each_XXX(L, P, FN, _confined)
-
-#define fn_for_each2_XXX(L1, L2, P, FN, ...) \
-({ \
- struct label_it i; \
- int __E = 0; \
- label_for_each ## __VA_ARGS__(i, (L1), (L2), (P)) { \
- last_error(__E, (FN)); \
- } \
- __E; \
-})
-
-#define fn_for_each_in_merge(L1, L2, P, FN) \
- fn_for_each2_XXX((L1), (L2), P, FN, _in_merge)
-#define fn_for_each_not_in_set(L1, L2, P, FN) \
- fn_for_each2_XXX((L1), (L2), P, FN, _not_in_set)
-
-#define LABEL_MEDIATES(L, C) \
-({ \
- struct aa_profile *profile; \
- struct label_it i; \
- int ret = 0; \
- label_for_each(i, (L), profile) { \
- if (PROFILE_MEDIATES(profile, (C))) { \
- ret = 1; \
- break; \
- } \
- } \
- ret; \
-})
-
-
-void aa_labelset_destroy(struct aa_labelset *ls);
-void aa_labelset_init(struct aa_labelset *ls);
-void __aa_labelset_update_subtree(struct aa_ns *ns);
-
-void aa_label_free(struct aa_label *label);
-void aa_label_kref(struct kref *kref);
-bool aa_label_init(struct aa_label *label, int size);
-struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp);
-
-bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub);
-struct aa_profile *__aa_label_next_not_in_set(struct label_it *I,
- struct aa_label *set,
- struct aa_label *sub);
-bool aa_label_remove(struct aa_label *label);
-struct aa_label *aa_label_insert(struct aa_labelset *ls, struct aa_label *l);
-bool aa_label_replace(struct aa_label *old, struct aa_label *new);
-bool aa_label_make_newest(struct aa_labelset *ls, struct aa_label *old,
- struct aa_label *new);
-
-struct aa_label *aa_label_find(struct aa_label *l);
-
-struct aa_profile *aa_label_next_in_merge(struct label_it *I,
- struct aa_label *a,
- struct aa_label *b);
-struct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b);
-struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b,
- gfp_t gfp);
-
-
-bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp);
-
-#define FLAGS_NONE 0
-#define FLAG_SHOW_MODE 1
-#define FLAG_VIEW_SUBNS 2
-#define FLAG_HIDDEN_UNCONFINED 4
-#define FLAG_ABS_ROOT 8
-int aa_label_snxprint(char *str, size_t size, struct aa_ns *view,
- struct aa_label *label, int flags);
-int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label,
- int flags, gfp_t gfp);
-int aa_label_acntsxprint(char __counted **strp, struct aa_ns *ns,
- struct aa_label *label, int flags, gfp_t gfp);
-void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns,
- struct aa_label *label, int flags, gfp_t gfp);
-void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
- struct aa_label *label, int flags, gfp_t gfp);
-void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
- gfp_t gfp);
-void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp);
-void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp);
-void aa_label_printk(struct aa_label *label, gfp_t gfp);
-
-struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
- gfp_t gfp, bool create, bool force_stack);
-
-
-struct aa_perms;
-int aa_label_match(struct aa_profile *profile, struct aa_label *label,
- unsigned int state, bool subns, u32 request,
- struct aa_perms *perms);
-
-
-/**
- * __aa_get_label - get a reference count to uncounted label reference
- * @l: reference to get a count on
- *
- * Returns: pointer to reference OR NULL if race is lost and reference is
- * being repeated.
- * Requires: lock held, and the return code MUST be checked
- */
-static inline struct aa_label *__aa_get_label(struct aa_label *l)
-{
- if (l && kref_get_unless_zero(&l->count))
- return l;
-
- return NULL;
-}
-
-static inline struct aa_label *aa_get_label(struct aa_label *l)
-{
- if (l)
- kref_get(&(l->count));
-
- return l;
-}
-
-
-/**
- * aa_get_label_rcu - increment refcount on a label that can be replaced
- * @l: pointer to label that can be replaced (NOT NULL)
- *
- * Returns: pointer to a refcounted label.
- * else NULL if no label
- */
-static inline struct aa_label *aa_get_label_rcu(struct aa_label __rcu **l)
-{
- struct aa_label *c;
-
- rcu_read_lock();
- do {
- c = rcu_dereference(*l);
- } while (c && !kref_get_unless_zero(&c->count));
- rcu_read_unlock();
-
- return c;
-}
-
-/**
- * aa_get_newest_label - find the newest version of @l
- * @l: the label to check for newer versions of
- *
- * Returns: refcounted newest version of @l taking into account
- * replacement, renames and removals
- * return @l.
- */
-static inline struct aa_label *aa_get_newest_label(struct aa_label *l)
-{
- if (!l)
- return NULL;
-
- if (label_is_stale(l)) {
- struct aa_label *tmp;
-
- AA_BUG(!l->proxy);
- AA_BUG(!l->proxy->label);
- /* BUG: only way this can happen is @l ref count and its
- * replacement count have gone to 0 and are on their way
- * to destruction. ie. we have a refcounting error
- */
- tmp = aa_get_label_rcu(&l->proxy->label);
- AA_BUG(!tmp);
-
- return tmp;
- }
-
- return aa_get_label(l);
-}
-
-static inline void aa_put_label(struct aa_label *l)
-{
- if (l)
- kref_put(&l->count, aa_label_kref);
-}
-
-
-struct aa_proxy *aa_alloc_proxy(struct aa_label *l, gfp_t gfp);
-void aa_proxy_kref(struct kref *kref);
-
-static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *proxy)
-{
- if (proxy)
- kref_get(&(proxy->count));
-
- return proxy;
-}
-
-static inline void aa_put_proxy(struct aa_proxy *proxy)
-{
- if (proxy)
- kref_put(&proxy->count, aa_proxy_kref);
-}
-
-void __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new);
-
-#endif /* __AA_LABEL_H */
diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h
index 436b3a722357..550a700563b4 100644
--- a/security/apparmor/include/lib.h
+++ b/security/apparmor/include/lib.h
@@ -60,7 +60,6 @@
extern int apparmor_initialized;
/* fn's in lib */
-const char *skipn_spaces(const char *str, size_t n);
char *aa_split_fqname(char *args, char **ns_name);
const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
size_t *ns_len);
@@ -100,36 +99,6 @@ static inline bool path_mediated_fs(struct dentry *dentry)
return !(dentry->d_sb->s_flags & MS_NOUSER);
}
-
-struct counted_str {
- struct kref count;
- char name[];
-};
-
-#define str_to_counted(str) \
- ((struct counted_str *)(str - offsetof(struct counted_str, name)))
-
-#define __counted /* atm just a notation */
-
-void aa_str_kref(struct kref *kref);
-char *aa_str_alloc(int size, gfp_t gfp);
-
-
-static inline __counted char *aa_get_str(__counted char *str)
-{
- if (str)
- kref_get(&(str_to_counted(str)->count));
-
- return str;
-}
-
-static inline void aa_put_str(__counted char *str)
-{
- if (str)
- kref_put(&str_to_counted(str)->count, aa_str_kref);
-}
-
-
/* struct aa_policy - common part of both namespaces and profiles
* @name: name of the object
* @hname - The hierarchical name
@@ -138,7 +107,7 @@ static inline void aa_put_str(__counted char *str)
*/
struct aa_policy {
const char *name;
- __counted char *hname;
+ const char *hname;
struct list_head list;
struct list_head profiles;
};
@@ -211,89 +180,4 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix,
const char *name, gfp_t gfp);
void aa_policy_destroy(struct aa_policy *policy);
-
-/*
- * fn_label_build - abstract out the build of a label transition
- * @L: label the transition is being computed for
- * @P: profile parameter derived from L by this macro, can be passed to FN
- * @GFP: memory allocation type to use
- * @FN: fn to call for each profile transition. @P is set to the profile
- *
- * Returns: new label on success
- * ERR_PTR if build @FN fails
- * NULL if label_build fails due to low memory conditions
- *
- * @FN must return a label or ERR_PTR on failure. NULL is not allowed
- */
-#define fn_label_build(L, P, GFP, FN) \
-({ \
- __label__ __cleanup, __done; \
- struct aa_label *__new_; \
- \
- if ((L)->size > 1) { \
- /* TODO: add cache of transitions already done */ \
- struct label_it __i; \
- int __j, __k, __count; \
- DEFINE_VEC(label, __lvec); \
- DEFINE_VEC(profile, __pvec); \
- if (vec_setup(label, __lvec, (L)->size, (GFP))) { \
- __new_ = NULL; \
- goto __done; \
- } \
- __j = 0; \
- label_for_each(__i, (L), (P)) { \
- __new_ = (FN); \
- AA_BUG(!__new_); \
- if (IS_ERR(__new_)) \
- goto __cleanup; \
- __lvec[__j++] = __new_; \
- } \
- for (__j = __count = 0; __j < (L)->size; __j++) \
- __count += __lvec[__j]->size; \
- if (!vec_setup(profile, __pvec, __count, (GFP))) { \
- for (__j = __k = 0; __j < (L)->size; __j++) { \
- label_for_each(__i, __lvec[__j], (P)) \
- __pvec[__k++] = aa_get_profile(P); \
- } \
- __count -= aa_vec_unique(__pvec, __count, 0); \
- if (__count > 1) { \
- __new_ = aa_vec_find_or_create_label(__pvec,\
- __count, (GFP)); \
- /* only fails if out of Mem */ \
- if (!__new_) \
- __new_ = NULL; \
- } else \
- __new_ = aa_get_label(&__pvec[0]->label); \
- vec_cleanup(profile, __pvec, __count); \
- } else \
- __new_ = NULL; \
-__cleanup: \
- vec_cleanup(label, __lvec, (L)->size); \
- } else { \
- (P) = labels_profile(L); \
- __new_ = (FN); \
- } \
-__done: \
- if (!__new_) \
- AA_DEBUG("label build failed\n"); \
- (__new_); \
-})
-
-
-#define __fn_build_in_ns(NS, P, NS_FN, OTHER_FN) \
-({ \
- struct aa_label *__new; \
- if ((P)->ns != (NS)) \
- __new = (OTHER_FN); \
- else \
- __new = (NS_FN); \
- (__new); \
-})
-
-#define fn_label_build_in_ns(L, P, GFP, NS_FN, OTHER_FN) \
-({ \
- fn_label_build((L), (P), (GFP), \
- __fn_build_in_ns(labels_ns(L), (P), (NS_FN), (OTHER_FN))); \
-})
-
-#endif /* __AA_LIB_H */
+#endif /* AA_LIB_H */
diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h
deleted file mode 100644
index 25d6067fa6ef..000000000000
--- a/security/apparmor/include/mount.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * AppArmor security module
- *
- * This file contains AppArmor file mediation function definitions.
- *
- * Copyright 2017 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
- */
-
-#ifndef __AA_MOUNT_H
-#define __AA_MOUNT_H
-
-#include <linux/fs.h>
-#include <linux/path.h>
-
-#include "domain.h"
-#include "policy.h"
-
-/* mount perms */
-#define AA_MAY_PIVOTROOT 0x01
-#define AA_MAY_MOUNT 0x02
-#define AA_MAY_UMOUNT 0x04
-#define AA_AUDIT_DATA 0x40
-#define AA_MNT_CONT_MATCH 0x40
-
-#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN)
-
-int aa_remount(struct aa_label *label, const struct path *path,
- unsigned long flags, void *data);
-
-int aa_bind_mount(struct aa_label *label, const struct path *path,
- const char *old_name, unsigned long flags);
-
-
-int aa_mount_change_type(struct aa_label *label, const struct path *path,
- unsigned long flags);
-
-int aa_move_mount(struct aa_label *label, const struct path *path,
- const char *old_name);
-
-int aa_new_mount(struct aa_label *label, const char *dev_name,
- const struct path *path, const char *type, unsigned long flags,
- void *data);
-
-int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags);
-
-int aa_pivotroot(struct aa_label *label, const struct path *old_path,
- const struct path *new_path);
-
-#endif /* __AA_MOUNT_H */
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
index 580232f20047..2f40d7e54398 100644
--- a/security/apparmor/include/net.h
+++ b/security/apparmor/include/net.h
@@ -30,11 +30,11 @@ struct aa_net {
u16 quiet[AF_MAX];
};
-extern struct aa_sfs_entry aa_sfs_entry_network[];
+extern struct aa_fs_entry aa_fs_entry_network[];
-int aa_label_net_perm(struct aa_label *label, const char *op, u16 family,
+extern int aa_net_perm(const char *op, struct aa_profile *profile, u16 family,
int type, int protocol, struct sock *sk);
-int aa_revalidate_sk(const char *op, struct sock *sk);
+extern int aa_revalidate_sk(const char *op, struct sock *sk);
static inline void aa_free_net_rules(struct aa_net *new)
{
diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h
index 05fb3305671e..0444fdde3918 100644
--- a/security/apparmor/include/path.h
+++ b/security/apparmor/include/path.h
@@ -23,12 +23,11 @@ enum path_flags {
PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */
PATH_DELEGATE_DELETED = 0x08000, /* delegate deleted files */
- PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */
+ PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */
};
-int aa_path_name(const struct path *path, int flags, char *buffer,
- const char **name, const char **info,
- const char *disconnected);
+int aa_path_name(const struct path *path, int flags, char **buffer,
+ const char **name, const char **info);
#define MAX_PATH_BUFFERS 2
diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h
deleted file mode 100644
index d7b7e7115160..000000000000
--- a/security/apparmor/include/perms.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * AppArmor security module
- *
- * This file contains AppArmor basic permission sets definitions.
- *
- * Copyright 2017 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
- */
-
-#ifndef __AA_PERM_H
-#define __AA_PERM_H
-
-#include <linux/fs.h>
-#include "label.h"
-
-#define AA_MAY_EXEC MAY_EXEC
-#define AA_MAY_WRITE MAY_WRITE
-#define AA_MAY_READ MAY_READ
-#define AA_MAY_APPEND MAY_APPEND
-
-#define AA_MAY_CREATE 0x0010
-#define AA_MAY_DELETE 0x0020
-#define AA_MAY_OPEN 0x0040
-#define AA_MAY_RENAME 0x0080 /* pair */
-
-#define AA_MAY_SETATTR 0x0100 /* meta write */
-#define AA_MAY_GETATTR 0x0200 /* meta read */
-#define AA_MAY_SETCRED 0x0400 /* security cred/attr */
-#define AA_MAY_GETCRED 0x0800
-
-#define AA_MAY_CHMOD 0x1000 /* pair */
-#define AA_MAY_CHOWN 0x2000 /* pair */
-#define AA_MAY_CHGRP 0x4000 /* pair */
-#define AA_MAY_LOCK 0x8000 /* LINK_SUBSET overlaid */
-
-#define AA_EXEC_MMAP 0x00010000
-#define AA_MAY_MPROT 0x00020000 /* extend conditions */
-#define AA_MAY_LINK 0x00040000 /* pair */
-#define AA_MAY_SNAPSHOT 0x00080000 /* pair */
-
-#define AA_MAY_DELEGATE
-#define AA_CONT_MATCH 0x08000000
-
-#define AA_MAY_STACK 0x10000000
-#define AA_MAY_ONEXEC 0x20000000 /* either stack or change_profile */
-#define AA_MAY_CHANGE_PROFILE 0x40000000
-#define AA_MAY_CHANGEHAT 0x80000000
-
-#define AA_LINK_SUBSET AA_MAY_LOCK /* overlaid */
-
-
-#define PERMS_CHRS_MASK (MAY_READ | MAY_WRITE | AA_MAY_CREATE | \
- AA_MAY_DELETE | AA_MAY_LINK | AA_MAY_LOCK | \
- AA_MAY_EXEC | AA_EXEC_MMAP | AA_MAY_APPEND)
-
-#define PERMS_NAMES_MASK (PERMS_CHRS_MASK | AA_MAY_OPEN | AA_MAY_RENAME | \
- AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_SETCRED | \
- AA_MAY_GETCRED | AA_MAY_CHMOD | AA_MAY_CHOWN | \
- AA_MAY_CHGRP | AA_MAY_MPROT | AA_MAY_SNAPSHOT | \
- AA_MAY_STACK | AA_MAY_ONEXEC | \
- AA_MAY_CHANGE_PROFILE | AA_MAY_CHANGEHAT)
-
-extern const char aa_file_perm_chrs[];
-extern const char *aa_file_perm_names[];
-
-struct aa_perms {
- u32 allow;
- u32 audit; /* set only when allow is set */
-
- u32 deny; /* explicit deny, or conflict if allow also set */
- u32 quiet; /* set only when ~allow | deny */
- u32 kill; /* set only when ~allow | deny */
- u32 stop; /* set only when ~allow | deny */
-
- u32 complain; /* accumulates only used when ~allow & ~deny */
- u32 cond; /* set only when ~allow and ~deny */
-
- u32 hide; /* set only when ~allow | deny */
- u32 prompt; /* accumulates only used when ~allow & ~deny */
-
- /* Reserved:
- * u32 subtree; / * set only when allow is set * /
- */
- u16 xindex;
-};
-
-#define ALL_PERMS_MASK 0xffffffff
-extern struct aa_perms nullperms;
-extern struct aa_perms allperms;
-
-
-#define xcheck(FN1, FN2) \
-({ \
- int e, error = FN1; \
- e = FN2; \
- if (e) \
- error = e; \
- error; \
-})
-
-
-/*
- * TODO: update for labels pointing to labels instead of profiles
- * TODO: optimize the walk, currently does subwalk of L2 for each P in L1
- * gah this doesn't allow for label compound check!!!!
- */
-#define xcheck_ns_profile_profile(P1, P2, FN, args...) \
-({ \
- int ____e = 0; \
- if (P1->ns == P2->ns) \
- ____e = FN((P1), (P2), args); \
- (____e); \
-})
-
-#define xcheck_ns_profile_label(P, L, FN, args...) \
-({ \
- struct aa_profile *__p2; \
- fn_for_each((L), __p2, \
- xcheck_ns_profile_profile((P), __p2, (FN), args)); \
-})
-
-#define xcheck_ns_labels(L1, L2, FN, args...) \
-({ \
- struct aa_profile *__p1; \
- fn_for_each((L1), __p1, FN(__p1, (L2), args)); \
-})
-
-/* Do the cross check but applying FN at the profiles level */
-#define xcheck_labels_profiles(L1, L2, FN, args...) \
- xcheck_ns_labels((L1), (L2), xcheck_ns_profile_label, (FN), args)
-
-#define xcheck_labels(L1, L2, P, FN1, FN2) \
- xcheck(fn_for_each((L1), (P), (FN1)), fn_for_each((L2), (P), (FN2)))
-
-
-void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask);
-void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask);
-void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
- u32 chrsmask, const char **names, u32 namesmask);
-void aa_apply_modes_to_perms(struct aa_profile *profile,
- struct aa_perms *perms);
-void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
- struct aa_perms *perms);
-void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend);
-void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend);
-void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label,
- int type, u32 request, struct aa_perms *perms);
-int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
- u32 request, int type, u32 *deny,
- struct common_audit_data *sa);
-int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
- u32 request, struct common_audit_data *sa,
- void (*cb)(struct audit_buffer *, void *));
-#endif /* __AA_PERM_H */
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index de69b0a68d22..a3d18ea8d730 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -30,8 +30,6 @@
#include "file.h"
#include "net.h"
#include "lib.h"
-#include "label.h"
-#include "perms.h"
#include "resource.h"
@@ -50,9 +48,9 @@ extern const char *const aa_profile_mode_names[];
#define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL)
-#define PROFILE_IS_HAT(_profile) ((_profile)->label.flags & FLAG_HAT)
+#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
-#define profile_is_stale(_profile) (label_is_stale(&(_profile)->label))
+#define profile_is_stale(_profile) ((_profile)->flags & PFLAG_STALE)
#define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2)
@@ -69,6 +67,22 @@ enum profile_mode {
APPARMOR_UNCONFINED, /* profile set to unconfined */
};
+enum profile_flags {
+ PFLAG_HAT = 1, /* profile is a hat */
+ PFLAG_NULL = 4, /* profile is null learning profile */
+ PFLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */
+ PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */
+ PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */
+ PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */
+ PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */
+ PFLAG_STALE = 0x200, /* profile replaced/removed */
+ PFLAG_NS_COUNT = 0x400, /* carries NS ref count */
+
+ /* These flags must correspond with PATH_flags */
+ PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */
+};
+
+struct aa_profile;
/* struct aa_policydb - match engine for a policy
* dfa: dfa pattern match
@@ -81,6 +95,11 @@ struct aa_policydb {
};
+struct aa_proxy {
+ struct kref count;
+ struct aa_profile __rcu *profile;
+};
+
/* struct aa_data - generic data structure
* key: name for retrieving this data
* size: size of data in bytes
@@ -97,17 +116,19 @@ struct aa_data {
/* struct aa_profile - basic confinement data
* @base - base components of the profile (name, refcount, lists, lock ...)
- * @label - label this profile is an extension of
+ * @count: reference count of the obj
+ * @rcu: rcu head used when removing from @list
* @parent: parent of profile
* @ns: namespace the profile is in
+ * @proxy: is set to the profile that replaced this profile
* @rename: optional profile name that this profile renamed
* @attach: human readable attachment string
* @xmatch: optional extended matching for unconfined executables names
* @xmatch_len: xmatch prefix len, used to determine xmatch priority
* @audit: the auditing mode of the profile
* @mode: the enforcement mode of the profile
+ * @flags: flags controlling profile behavior
* @path_flags: flags controlling path generation behavior
- * @disconnected: what to prepend if attach_disconnected is specified
* @size: the memory consumed by this profiles rules
* @policy: general match rules governing policy
* @file: The set of rules governing basic file access and domain transitions
@@ -124,6 +145,8 @@ struct aa_data {
* used to determine profile attachment against unconfined tasks. All other
* attachments are determined by profile X transition rules.
*
+ * The @proxy struct is write protected by the profile lock.
+ *
* Profiles have a hierarchy where hats and children profiles keep
* a reference to their parent.
*
@@ -133,9 +156,12 @@ struct aa_data {
*/
struct aa_profile {
struct aa_policy base;
+ struct kref count;
+ struct rcu_head rcu;
struct aa_profile __rcu *parent;
struct aa_ns *ns;
+ struct aa_proxy *proxy;
const char *rename;
const char *attach;
@@ -143,8 +169,8 @@ struct aa_profile {
int xmatch_len;
enum audit_mode audit;
long mode;
+ long flags;
u32 path_flags;
- const char *disconnected;
int size;
struct aa_policydb policy;
@@ -158,24 +184,17 @@ struct aa_profile {
char *dirname;
struct dentry *dents[AAFS_PROF_SIZEOF];
struct rhashtable *data;
- struct aa_label label;
};
extern enum profile_mode aa_g_profile_mode;
-#define AA_MAY_LOAD_POLICY AA_MAY_APPEND
-#define AA_MAY_REPLACE_POLICY AA_MAY_WRITE
-#define AA_MAY_REMOVE_POLICY AA_MAY_DELETE
-
-#define profiles_ns(P) ((P)->ns)
-#define name_is_shared(A, B) ((A)->hname && (A)->hname == (B)->hname)
+void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new);
void aa_add_profile(struct aa_policy *common, struct aa_profile *profile);
void aa_free_proxy_kref(struct kref *kref);
-struct aa_profile *aa_alloc_profile(const char *name, struct aa_proxy *proxy,
- gfp_t gfp);
+struct aa_profile *aa_alloc_profile(const char *name, gfp_t gfp);
struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
const char *base, gfp_t gfp);
void aa_free_profile(struct aa_profile *profile);
@@ -184,44 +203,21 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name);
struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname,
size_t n);
struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name);
-struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
+struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base,
const char *fqname, size_t n);
struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name);
-ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_label *label,
- u32 mask, struct aa_loaddata *udata);
-ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_label *label,
- char *name, size_t size);
+ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile,
+ bool noreplace, struct aa_loaddata *udata);
+ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *profile,
+ char *name, size_t size);
void __aa_profile_list_release(struct list_head *head);
#define PROF_ADD 1
#define PROF_REPLACE 0
-#define profile_unconfined(X) ((X)->mode == APPARMOR_UNCONFINED)
-
-/**
- * aa_get_newest_profile - simple wrapper fn to wrap the label version
- * @p: profile (NOT NULL)
- *
- * Returns refcount to newest version of the profile (maybe @p)
- *
- * Requires: @p must be held with a valid refcount
- */
-static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p)
-{
- return labels_profile(aa_get_newest_label(&p->label));
-}
+#define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED)
-#define PROFILE_MEDIATES(P, T) ((P)->policy.start[(T)])
-/* safe version of POLICY_MEDIATES for full range input */
-static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile,
- unsigned char class)
-{
- if (profile->policy.dfa)
- return aa_dfa_match_len(profile->policy.dfa,
- profile->policy.start[0], &class, 1);
- return 0;
-}
/**
* aa_get_profile - increment refcount on profile @p
@@ -233,7 +229,7 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile,
static inline struct aa_profile *aa_get_profile(struct aa_profile *p)
{
if (p)
- kref_get(&(p->label.count));
+ kref_get(&(p->count));
return p;
}
@@ -247,7 +243,7 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *p)
*/
static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p)
{
- if (p && kref_get_unless_zero(&p->label.count))
+ if (p && kref_get_unless_zero(&p->count))
return p;
return NULL;
@@ -267,20 +263,53 @@ static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p)
rcu_read_lock();
do {
c = rcu_dereference(*p);
- } while (c && !kref_get_unless_zero(&c->label.count));
+ } while (c && !kref_get_unless_zero(&c->count));
rcu_read_unlock();
return c;
}
/**
+ * aa_get_newest_profile - find the newest version of @profile
+ * @profile: the profile to check for newer versions of
+ *
+ * Returns: refcounted newest version of @profile taking into account
+ * replacement, renames and removals
+ * return @profile.
+ */
+static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p)
+{
+ if (!p)
+ return NULL;
+
+ if (profile_is_stale(p))
+ return aa_get_profile_rcu(&p->proxy->profile);
+
+ return aa_get_profile(p);
+}
+
+/**
* aa_put_profile - decrement refcount on profile @p
* @p: profile (MAYBE NULL)
*/
static inline void aa_put_profile(struct aa_profile *p)
{
if (p)
- kref_put(&p->label.count, aa_label_kref);
+ kref_put(&p->count, aa_free_profile_kref);
+}
+
+static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *p)
+{
+ if (p)
+ kref_get(&(p->count));
+
+ return p;
+}
+
+static inline void aa_put_proxy(struct aa_proxy *p)
+{
+ if (p)
+ kref_put(&p->count, aa_free_proxy_kref);
}
static inline int AUDIT_MODE(struct aa_profile *profile)
@@ -293,7 +322,7 @@ static inline int AUDIT_MODE(struct aa_profile *profile)
bool policy_view_capable(struct aa_ns *ns);
bool policy_admin_capable(struct aa_ns *ns);
-int aa_may_manage_policy(struct aa_label *label, struct aa_ns *ns,
- u32 mask);
+int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns,
+ const char *op);
#endif /* __AA_POLICY_H */
diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h
index 9605f18624e2..89cffddd7e75 100644
--- a/security/apparmor/include/policy_ns.h
+++ b/security/apparmor/include/policy_ns.h
@@ -19,7 +19,6 @@
#include "apparmor.h"
#include "apparmorfs.h"
-#include "label.h"
#include "policy.h"
@@ -69,11 +68,6 @@ struct aa_ns {
atomic_t uniq_null;
long uniq_id;
int level;
- long revision;
- wait_queue_head_t wait;
-
- struct aa_labelset labels;
- struct list_head rawdata_list;
struct dentry *dents[AAFS_NS_SIZEOF];
};
@@ -82,8 +76,6 @@ extern struct aa_ns *root_ns;
extern const char *aa_hidden_ns_name;
-#define ns_unconfined(NS) (&(NS)->unconfined->label)
-
bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns);
const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child, bool subns);
void aa_free_ns(struct aa_ns *ns);
@@ -93,8 +85,6 @@ void aa_free_ns_kref(struct kref *kref);
struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name);
struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n);
-struct aa_ns *__aa_lookupn_ns(struct aa_ns *view, const char *hname, size_t n);
-struct aa_ns *aa_lookupn_ns(struct aa_ns *view, const char *name, size_t n);
struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name,
struct dentry *dir);
struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name);
@@ -154,15 +144,4 @@ static inline struct aa_ns *__aa_find_ns(struct list_head *head,
return __aa_findn_ns(head, name, strlen(name));
}
-static inline struct aa_ns *__aa_lookup_ns(struct aa_ns *base,
- const char *hname)
-{
- return __aa_lookupn_ns(base, hname, strlen(hname));
-}
-
-static inline struct aa_ns *aa_lookup_ns(struct aa_ns *view, const char *name)
-{
- return aa_lookupn_ns(view, name, strlen(name));
-}
-
#endif /* AA_NAMESPACE_H */
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h
index be6cd69ac319..4c1319eebc42 100644
--- a/security/apparmor/include/policy_unpack.h
+++ b/security/apparmor/include/policy_unpack.h
@@ -17,8 +17,6 @@
#include <linux/list.h>
#include <linux/kref.h>
-#include <linux/dcache.h>
-#include <linux/workqueue.h>
struct aa_load_ent {
struct list_head list;
@@ -38,84 +36,26 @@ struct aa_load_ent *aa_load_ent_alloc(void);
#define PACKED_MODE_KILL 2
#define PACKED_MODE_UNCONFINED 3
-struct aa_ns;
-
-enum {
- AAFS_LOADDATA_ABI = 0,
- AAFS_LOADDATA_REVISION,
- AAFS_LOADDATA_HASH,
- AAFS_LOADDATA_DATA,
- AAFS_LOADDATA_DIR, /* must be last actual entry */
- AAFS_LOADDATA_NDENTS /* count of entries */
-};
-
-/*
- * struct aa_loaddata - buffer of policy raw_data set
- *
- * there is no loaddata ref for being on ns list, nor a ref from
- * d_inode(@dentry) when grab a ref from these, @ns->lock must be held
- * && __aa_get_loaddata() needs to be used, and the return value
- * checked, if NULL the loaddata is already being reaped and should be
- * considered dead.
- */
+/* struct aa_loaddata - buffer of policy load data set */
struct aa_loaddata {
struct kref count;
- struct list_head list;
- struct work_struct work;
- struct dentry *dents[AAFS_LOADDATA_NDENTS];
- struct aa_ns *ns;
- char *name;
size_t size;
- long revision; /* the ns policy revision this caused */
int abi;
unsigned char *hash;
-
char data[];
};
int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns);
-/**
- * __aa_get_loaddata - get a reference count to uncounted data reference
- * @data: reference to get a count on
- *
- * Returns: pointer to reference OR NULL if race is lost and reference is
- * being repeated.
- * Requires: @data->ns->lock held, and the return code MUST be checked
- *
- * Use only from inode->i_private and @data->list found references
- */
-static inline struct aa_loaddata *
-__aa_get_loaddata(struct aa_loaddata *data)
-{
- if (data && kref_get_unless_zero(&(data->count)))
- return data;
-
- return NULL;
-}
-
-/**
- * aa_get_loaddata - get a reference count from a counted data reference
- * @data: reference to get a count on
- *
- * Returns: point to reference
- * Requires: @data to have a valid reference count on it. It is a bug
- * if the race to reap can be encountered when it is used.
- */
static inline struct aa_loaddata *
aa_get_loaddata(struct aa_loaddata *data)
{
- struct aa_loaddata *tmp = __aa_get_loaddata(data);
-
- AA_BUG(data && !tmp);
-
- return tmp;
+ if (data)
+ kref_get(&(data->count));
+ return data;
}
-void __aa_loaddata_update(struct aa_loaddata *data, long revision);
-bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r);
void aa_loaddata_kref(struct kref *kref);
-struct aa_loaddata *aa_loaddata_alloc(size_t size);
static inline void aa_put_loaddata(struct aa_loaddata *data)
{
if (data)
diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h
index c8fd99c9357d..6bd5f33d9533 100644
--- a/security/apparmor/include/procattr.h
+++ b/security/apparmor/include/procattr.h
@@ -15,7 +15,11 @@
#ifndef __AA_PROCATTR_H
#define __AA_PROCATTR_H
-int aa_getprocattr(struct aa_label *label, char **string);
-int aa_setprocattr_changehat(char *args, size_t size, int flags);
+#define AA_DO_TEST 1
+#define AA_ONEXEC 1
+
+int aa_getprocattr(struct aa_profile *profile, char **string);
+int aa_setprocattr_changehat(char *args, size_t size, int test);
+int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test);
#endif /* __AA_PROCATTR_H */
diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h
index 76f1586c9adb..d3f4cf027957 100644
--- a/security/apparmor/include/resource.h
+++ b/security/apparmor/include/resource.h
@@ -34,13 +34,13 @@ struct aa_rlimit {
struct rlimit limits[RLIM_NLIMITS];
};
-extern struct aa_sfs_entry aa_sfs_entry_rlimit[];
+extern struct aa_fs_entry aa_fs_entry_rlimit[];
int aa_map_resource(int resource);
-int aa_task_setrlimit(struct aa_label *label, struct task_struct *task,
+int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *,
unsigned int resource, struct rlimit *new_rlim);
-void __aa_transition_rlimits(struct aa_label *old, struct aa_label *new);
+void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new);
static inline void aa_free_rlimit_rules(struct aa_rlimit *rlims)
{
diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h
deleted file mode 100644
index 5ca47c50dfa7..000000000000
--- a/security/apparmor/include/sig_names.h
+++ /dev/null
@@ -1,100 +0,0 @@
-#include <linux/signal.h>
-
-#define SIGUNKNOWN 0
-#define MAXMAPPED_SIG 35
-#define MAXMAPPED_SIGNAME (MAXMAPPED_SIG + 1)
-
-/* provide a mapping of arch signal to internal signal # for mediation
- * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO
- * map to the same entry those that may/or may not get a separate entry
- */
-static const int sig_map[MAXMAPPED_SIG] = {
- [0] = MAXMAPPED_SIG, /* existence test */
- [SIGHUP] = 1,
- [SIGINT] = 2,
- [SIGQUIT] = 3,
- [SIGILL] = 4,
- [SIGTRAP] = 5, /* -, 5, - */
- [SIGABRT] = 6, /* SIGIOT: -, 6, - */
- [SIGBUS] = 7, /* 10, 7, 10 */
- [SIGFPE] = 8,
- [SIGKILL] = 9,
- [SIGUSR1] = 10, /* 30, 10, 16 */
- [SIGSEGV] = 11,
- [SIGUSR2] = 12, /* 31, 12, 17 */
- [SIGPIPE] = 13,
- [SIGALRM] = 14,
- [SIGTERM] = 15,
-#ifdef SIGSTKFLT
- [SIGSTKFLT] = 16, /* -, 16, - */
-#endif
- [SIGCHLD] = 17, /* 20, 17, 18. SIGCHLD -, -, 18 */
- [SIGCONT] = 18, /* 19, 18, 25 */
- [SIGSTOP] = 19, /* 17, 19, 23 */
- [SIGTSTP] = 20, /* 18, 20, 24 */
- [SIGTTIN] = 21, /* 21, 21, 26 */
- [SIGTTOU] = 22, /* 22, 22, 27 */
- [SIGURG] = 23, /* 16, 23, 21 */
- [SIGXCPU] = 24, /* 24, 24, 30 */
- [SIGXFSZ] = 25, /* 25, 25, 31 */
- [SIGVTALRM] = 26, /* 26, 26, 28 */
- [SIGPROF] = 27, /* 27, 27, 29 */
- [SIGWINCH] = 28, /* 28, 28, 20 */
- [SIGIO] = 29, /* SIGPOLL: 23, 29, 22 */
- [SIGPWR] = 30, /* 29, 30, 19. SIGINFO 29, -, - */
-#ifdef SIGSYS
- [SIGSYS] = 31, /* 12, 31, 12. often SIG LOST/UNUSED */
-#endif
-#ifdef SIGEMT
- [SIGEMT] = 32, /* 7, - , 7 */
-#endif
-#if defined(SIGLOST) && SIGPWR != SIGLOST /* sparc */
- [SIGLOST] = 33, /* unused on Linux */
-#endif
-#if defined(SIGUNUSED) && \
- defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS
- [SIGUNUSED] = 34, /* -, 31, - */
-#endif
-};
-
-/* this table is ordered post sig_map[sig] mapping */
-static const char *const sig_names[MAXMAPPED_SIGNAME] = {
- "unknown",
- "hup",
- "int",
- "quit",
- "ill",
- "trap",
- "abrt",
- "bus",
- "fpe",
- "kill",
- "usr1",
- "segv",
- "usr2",
- "pipe",
- "alrm",
- "term",
- "stkflt",
- "chld",
- "cont",
- "stop",
- "stp",
- "ttin",
- "ttou",
- "urg",
- "xcpu",
- "xfsz",
- "vtalrm",
- "prof",
- "winch",
- "io",
- "pwr",
- "sys",
- "emt",
- "lost",
- "unused",
-
- "exists", /* always last existence test mapped to MAXMAPPED_SIG */
-};
-
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c
index 586facd35f7c..edac790923c3 100644
--- a/security/apparmor/ipc.c
+++ b/security/apparmor/ipc.c
@@ -4,7 +4,7 @@
* This file contains AppArmor ipc mediation
*
* Copyright (C) 1998-2008 Novell/SUSE
- * Copyright 2009-2017 Canonical Ltd.
+ * Copyright 2009-2010 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -20,214 +20,88 @@
#include "include/context.h"
#include "include/policy.h"
#include "include/ipc.h"
-#include "include/sig_names.h"
-
-/**
- * audit_ptrace_mask - convert mask to permission string
- * @buffer: buffer to write string to (NOT NULL)
- * @mask: permission mask to convert
- */
-static void audit_ptrace_mask(struct audit_buffer *ab, u32 mask)
-{
- switch (mask) {
- case MAY_READ:
- audit_log_string(ab, "read");
- break;
- case MAY_WRITE:
- audit_log_string(ab, "trace");
- break;
- case AA_MAY_BE_READ:
- audit_log_string(ab, "readby");
- break;
- case AA_MAY_BE_TRACED:
- audit_log_string(ab, "tracedby");
- break;
- }
-}
/* call back to audit ptrace fields */
-static void audit_ptrace_cb(struct audit_buffer *ab, void *va)
+static void audit_cb(struct audit_buffer *ab, void *va)
{
struct common_audit_data *sa = va;
-
- if (aad(sa)->request & AA_PTRACE_PERM_MASK) {
- audit_log_format(ab, " requested_mask=");
- audit_ptrace_mask(ab, aad(sa)->request);
-
- if (aad(sa)->denied & AA_PTRACE_PERM_MASK) {
- audit_log_format(ab, " denied_mask=");
- audit_ptrace_mask(ab, aad(sa)->denied);
- }
- }
audit_log_format(ab, " peer=");
- aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
- FLAGS_NONE, GFP_ATOMIC);
-}
-
-/* assumes check for PROFILE_MEDIATES is already done */
-/* TODO: conditionals */
-static int profile_ptrace_perm(struct aa_profile *profile,
- struct aa_label *peer, u32 request,
- struct common_audit_data *sa)
-{
- struct aa_perms perms = { };
-
- aad(sa)->peer = peer;
- aa_profile_match_label(profile, peer, AA_CLASS_PTRACE, request,
- &perms);
- aa_apply_modes_to_perms(profile, &perms);
- return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb);
-}
-
-static int profile_tracee_perm(struct aa_profile *tracee,
- struct aa_label *tracer, u32 request,
- struct common_audit_data *sa)
-{
- if (profile_unconfined(tracee) || unconfined(tracer) ||
- !PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE))
- return 0;
-
- return profile_ptrace_perm(tracee, tracer, request, sa);
-}
-
-static int profile_tracer_perm(struct aa_profile *tracer,
- struct aa_label *tracee, u32 request,
- struct common_audit_data *sa)
-{
- if (profile_unconfined(tracer))
- return 0;
-
- if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE))
- return profile_ptrace_perm(tracer, tracee, request, sa);
-
- /* profile uses the old style capability check for ptrace */
- if (&tracer->label == tracee)
- return 0;
-
- aad(sa)->label = &tracer->label;
- aad(sa)->peer = tracee;
- aad(sa)->request = 0;
- aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, 1);
-
- return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb);
+ audit_log_untrustedstring(ab, aad(sa)->peer->base.hname);
}
/**
- * aa_may_ptrace - test if tracer task can trace the tracee
- * @tracer: label of the task doing the tracing (NOT NULL)
- * @tracee: task label to be traced
- * @request: permission request
+ * aa_audit_ptrace - do auditing for ptrace
+ * @profile: profile being enforced (NOT NULL)
+ * @target: profile being traced (NOT NULL)
+ * @error: error condition
*
- * Returns: %0 else error code if permission denied or error
+ * Returns: %0 or error code
*/
-int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
- u32 request)
+static int aa_audit_ptrace(struct aa_profile *profile,
+ struct aa_profile *target, int error)
{
- struct aa_profile *profile;
- u32 xrequest = request << PTRACE_PERM_SHIFT;
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE);
- return xcheck_labels(tracer, tracee, profile,
- profile_tracer_perm(profile, tracee, request, &sa),
- profile_tracee_perm(profile, tracer, xrequest, &sa));
-}
-
+ aad(&sa)->peer = target;
+ aad(&sa)->error = error;
-static inline int map_signal_num(int sig)
-{
- if (sig > SIGRTMAX)
- return SIGUNKNOWN;
- else if (sig >= SIGRTMIN)
- return sig - SIGRTMIN + 128; /* rt sigs mapped to 128 */
- else if (sig < MAXMAPPED_SIG)
- return sig_map[sig];
- return SIGUNKNOWN;
+ return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb);
}
/**
- * audit_file_mask - convert mask to permission string
- * @buffer: buffer to write string to (NOT NULL)
- * @mask: permission mask to convert
+ * aa_may_ptrace - test if tracer task can trace the tracee
+ * @tracer: profile of the task doing the tracing (NOT NULL)
+ * @tracee: task to be traced
+ * @mode: whether PTRACE_MODE_READ || PTRACE_MODE_ATTACH
+ *
+ * Returns: %0 else error code if permission denied or error
*/
-static void audit_signal_mask(struct audit_buffer *ab, u32 mask)
+int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee,
+ unsigned int mode)
{
- if (mask & MAY_READ)
- audit_log_string(ab, "receive");
- if (mask & MAY_WRITE)
- audit_log_string(ab, "send");
+ /* TODO: currently only based on capability, not extended ptrace
+ * rules,
+ * Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH
+ */
+
+ if (unconfined(tracer) || tracer == tracee)
+ return 0;
+ /* log this capability request */
+ return aa_capable(tracer, CAP_SYS_PTRACE, 1);
}
/**
- * audit_cb - call back for signal specific audit fields
- * @ab: audit_buffer (NOT NULL)
- * @va: audit struct to audit values of (NOT NULL)
+ * aa_ptrace - do ptrace permission check and auditing
+ * @tracer: task doing the tracing (NOT NULL)
+ * @tracee: task being traced (NOT NULL)
+ * @mode: ptrace mode either PTRACE_MODE_READ || PTRACE_MODE_ATTACH
+ *
+ * Returns: %0 else error code if permission denied or error
*/
-static void audit_signal_cb(struct audit_buffer *ab, void *va)
-{
- struct common_audit_data *sa = va;
-
- if (aad(sa)->request & AA_SIGNAL_PERM_MASK) {
- audit_log_format(ab, " requested_mask=");
- audit_signal_mask(ab, aad(sa)->request);
- if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) {
- audit_log_format(ab, " denied_mask=");
- audit_signal_mask(ab, aad(sa)->denied);
- }
- }
- if (aad(sa)->signal < MAXMAPPED_SIGNAME)
- audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]);
- else
- audit_log_format(ab, " signal=rtmin+%d",
- aad(sa)->signal - 128);
- audit_log_format(ab, " peer=");
- aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
- FLAGS_NONE, GFP_ATOMIC);
-}
-
-/* TODO: update to handle compound name&name2, conditionals */
-static void profile_match_signal(struct aa_profile *profile, const char *label,
- int signal, struct aa_perms *perms)
-{
- unsigned int state;
-
- /* TODO: secondary cache check <profile, profile, perm> */
- state = aa_dfa_next(profile->policy.dfa,
- profile->policy.start[AA_CLASS_SIGNAL],
- signal);
- state = aa_dfa_match(profile->policy.dfa, state, label);
- aa_compute_perms(profile->policy.dfa, state, perms);
-}
-
-static int profile_signal_perm(struct aa_profile *profile,
- struct aa_profile *peer, u32 request,
- struct common_audit_data *sa)
+int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,
+ unsigned int mode)
{
- struct aa_perms perms;
+ /*
+ * tracer can ptrace tracee when
+ * - tracer is unconfined ||
+ * - tracer is in complain mode
+ * - tracer has rules allowing it to trace tracee currently this is:
+ * - confined by the same profile ||
+ * - tracer profile has CAP_SYS_PTRACE
+ */
- if (profile_unconfined(profile) ||
- !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
- return 0;
+ struct aa_profile *tracer_p = aa_get_task_profile(tracer);
+ int error = 0;
- aad(sa)->peer = &peer->label;
- profile_match_signal(profile, peer->base.hname, aad(sa)->signal,
- &perms);
- aa_apply_modes_to_perms(profile, &perms);
- return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
-}
+ if (!unconfined(tracer_p)) {
+ struct aa_profile *tracee_p = aa_get_task_profile(tracee);
-static int aa_signal_cross_perm(struct aa_profile *sender,
- struct aa_profile *target,
- struct common_audit_data *sa)
-{
- return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa),
- profile_signal_perm(target, sender, MAY_READ, sa));
-}
+ error = aa_may_ptrace(tracer_p, tracee_p, mode);
+ error = aa_audit_ptrace(tracer_p, tracee_p, error);
-int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
-{
- DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL);
+ aa_put_profile(tracee_p);
+ }
+ aa_put_profile(tracer_p);
- aad(&sa)->signal = map_signal_num(sig);
- return xcheck_labels_profiles(sender, target, aa_signal_cross_perm,
- &sa);
+ return error;
}
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
deleted file mode 100644
index f5df9eaba4df..000000000000
--- a/security/apparmor/label.c
+++ /dev/null
@@ -1,2122 +0,0 @@
-/*
- * AppArmor security module
- *
- * This file contains AppArmor label definitions
- *
- * Copyright 2017 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
- */
-
-#include <linux/audit.h>
-#include <linux/seq_file.h>
-#include <linux/sort.h>
-
-#include "include/apparmor.h"
-#include "include/context.h"
-#include "include/label.h"
-#include "include/policy.h"
-#include "include/secid.h"
-
-
-/*
- * the aa_label represents the set of profiles confining an object
- *
- * Labels maintain a reference count to the set of pointers they reference
- * Labels are ref counted by
- * tasks and object via the security field/security context off the field
- * code - will take a ref count on a label if it needs the label
- * beyond what is possible with an rcu_read_lock.
- * profiles - each profile is a label
- * secids - a pinned secid will keep a refcount of the label it is
- * referencing
- * objects - inode, files, sockets, ...
- *
- * Labels are not ref counted by the label set, so they maybe removed and
- * freed when no longer in use.
- *
- */
-
-#define PROXY_POISON 97
-#define LABEL_POISON 100
-
-static void free_proxy(struct aa_proxy *proxy)
-{
- if (proxy) {
- /* p->label will not updated any more as p is dead */
- aa_put_label(rcu_dereference_protected(proxy->label, true));
- memset(proxy, 0, sizeof(*proxy));
- RCU_INIT_POINTER(proxy->label, (struct aa_label *)PROXY_POISON);
- kfree(proxy);
- }
-}
-
-void aa_proxy_kref(struct kref *kref)
-{
- struct aa_proxy *proxy = container_of(kref, struct aa_proxy, count);
-
- free_proxy(proxy);
-}
-
-struct aa_proxy *aa_alloc_proxy(struct aa_label *label, gfp_t gfp)
-{
- struct aa_proxy *new;
-
- new = kzalloc(sizeof(struct aa_proxy), gfp);
- if (new) {
- kref_init(&new->count);
- rcu_assign_pointer(new->label, aa_get_label(label));
- }
- return new;
-}
-
-/* requires profile list write lock held */
-void __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new)
-{
- struct aa_label *tmp;
-
- AA_BUG(!orig);
- AA_BUG(!new);
- AA_BUG(!write_is_locked(&labels_set(orig)->lock));
-
- tmp = rcu_dereference_protected(orig->proxy->label,
- &labels_ns(orig)->lock);
- rcu_assign_pointer(orig->proxy->label, aa_get_label(new));
- orig->flags |= FLAG_STALE;
- aa_put_label(tmp);
-}
-
-static void __proxy_share(struct aa_label *old, struct aa_label *new)
-{
- struct aa_proxy *proxy = new->proxy;
-
- new->proxy = aa_get_proxy(old->proxy);
- __aa_proxy_redirect(old, new);
- aa_put_proxy(proxy);
-}
-
-
-/**
- * ns_cmp - compare ns for label set ordering
- * @a: ns to compare (NOT NULL)
- * @b: ns to compare (NOT NULL)
- *
- * Returns: <0 if a < b
- * ==0 if a == b
- * >0 if a > b
- */
-static int ns_cmp(struct aa_ns *a, struct aa_ns *b)
-{
- int res;
-
- AA_BUG(!a);
- AA_BUG(!b);
- AA_BUG(!a->base.hname);
- AA_BUG(!b->base.hname);
-
- if (a == b)
- return 0;
-
- res = a->level - b->level;
- if (res)
- return res;
-
- return strcmp(a->base.hname, b->base.hname);
-}
-
-/**
- * profile_cmp - profile comparision for set ordering
- * @a: profile to compare (NOT NULL)
- * @b: profile to compare (NOT NULL)
- *
- * Returns: <0 if a < b
- * ==0 if a == b
- * >0 if a > b
- */
-static int profile_cmp(struct aa_profile *a, struct aa_profile *b)
-{
- int res;
-
- AA_BUG(!a);
- AA_BUG(!b);
- AA_BUG(!a->ns);
- AA_BUG(!b->ns);
- AA_BUG(!a->base.hname);
- AA_BUG(!b->base.hname);
-
- if (a == b || a->base.hname == b->base.hname)
- return 0;
- res = ns_cmp(a->ns, b->ns);
- if (res)
- return res;
-
- return strcmp(a->base.hname, b->base.hname);
-}
-
-/**
- * vec_cmp - label comparision for set ordering
- * @a: label to compare (NOT NULL)
- * @vec: vector of profiles to compare (NOT NULL)
- * @n: length of @vec
- *
- * Returns: <0 if a < vec
- * ==0 if a == vec
- * >0 if a > vec
- */
-static int vec_cmp(struct aa_profile **a, int an, struct aa_profile **b, int bn)
-{
- int i;
-
- AA_BUG(!a);
- AA_BUG(!*a);
- AA_BUG(!b);
- AA_BUG(!*b);
- AA_BUG(an <= 0);
- AA_BUG(bn <= 0);
-
- for (i = 0; i < an && i < bn; i++) {
- int res = profile_cmp(a[i], b[i]);
-
- if (res != 0)
- return res;
- }
-
- return an - bn;
-}
-
-static bool vec_is_stale(struct aa_profile **vec, int n)
-{
- int i;
-
- AA_BUG(!vec);
-
- for (i = 0; i < n; i++) {
- if (profile_is_stale(vec[i]))
- return true;
- }
-
- return false;
-}
-
-static bool vec_unconfined(struct aa_profile **vec, int n)
-{
- int i;
-
- AA_BUG(!vec);
-
- for (i = 0; i < n; i++) {
- if (!profile_unconfined(vec[i]))
- return false;
- }
-
- return true;
-}
-
-static int sort_cmp(const void *a, const void *b)
-{
- return profile_cmp(*(struct aa_profile **)a, *(struct aa_profile **)b);
-}
-
-/*
- * assumes vec is sorted
- * Assumes @vec has null terminator at vec[n], and will null terminate
- * vec[n - dups]
- */
-static inline int unique(struct aa_profile **vec, int n)
-{
- int i, pos, dups = 0;
-
- AA_BUG(n < 1);
- AA_BUG(!vec);
-
- pos = 0;
- for (i = 1; i < n; i++) {
- int res = profile_cmp(vec[pos], vec[i]);
-
- AA_BUG(res > 0, "vec not sorted");
- if (res == 0) {
- /* drop duplicate */
- aa_put_profile(vec[i]);
- dups++;
- continue;
- }
- pos++;
- if (dups)
- vec[pos] = vec[i];
- }
-
- AA_BUG(dups < 0);
-
- return dups;
-}
-
-/**
- * aa_vec_unique - canonical sort and unique a list of profiles
- * @n: number of refcounted profiles in the list (@n > 0)
- * @vec: list of profiles to sort and merge
- *
- * Returns: the number of duplicates eliminated == references put
- *
- * If @flags & VEC_FLAG_TERMINATE @vec has null terminator at vec[n], and will
- * null terminate vec[n - dups]
- */
-int aa_vec_unique(struct aa_profile **vec, int n, int flags)
-{
- int i, dups = 0;
-
- AA_BUG(n < 1);
- AA_BUG(!vec);
-
- /* vecs are usually small and inorder, have a fallback for larger */
- if (n > 8) {
- sort(vec, n, sizeof(struct aa_profile *), sort_cmp, NULL);
- dups = unique(vec, n);
- goto out;
- }
-
- /* insertion sort + unique in one */
- for (i = 1; i < n; i++) {
- struct aa_profile *tmp = vec[i];
- int pos, j;
-
- for (pos = i - 1 - dups; pos >= 0; pos--) {
- int res = profile_cmp(vec[pos], tmp);
-
- if (res == 0) {
- /* drop duplicate entry */
- aa_put_profile(tmp);
- dups++;
- goto continue_outer;
- } else if (res < 0)
- break;
- }
- /* pos is at entry < tmp, or index -1. Set to insert pos */
- pos++;
-
- for (j = i - dups; j > pos; j--)
- vec[j] = vec[j - 1];
- vec[pos] = tmp;
-continue_outer:
- ;
- }
-
- AA_BUG(dups < 0);
-
-out:
- if (flags & VEC_FLAG_TERMINATE)
- vec[n - dups] = NULL;
-
- return dups;
-}
-
-
-static void label_destroy(struct aa_label *label)
-{
- struct aa_label *tmp;
-
- AA_BUG(!label);
-
- if (!label_isprofile(label)) {
- struct aa_profile *profile;
- struct label_it i;
-
- aa_put_str(label->hname);
-
- label_for_each(i, label, profile) {
- aa_put_profile(profile);
- label->vec[i.i] = (struct aa_profile *)
- (LABEL_POISON + (long) i.i);
- }
- }
-
- if (rcu_dereference_protected(label->proxy->label, true) == label)
- rcu_assign_pointer(label->proxy->label, NULL);
-
- aa_free_secid(label->secid);
-
- tmp = rcu_dereference_protected(label->proxy->label, true);
- if (tmp == label)
- rcu_assign_pointer(label->proxy->label, NULL);
-
- aa_put_proxy(label->proxy);
- label->proxy = (struct aa_proxy *) PROXY_POISON + 1;
-}
-
-void aa_label_free(struct aa_label *label)
-{
- if (!label)
- return;
-
- label_destroy(label);
- kfree(label);
-}
-
-static void label_free_switch(struct aa_label *label)
-{
- if (label->flags & FLAG_NS_COUNT)
- aa_free_ns(labels_ns(label));
- else if (label_isprofile(label))
- aa_free_profile(labels_profile(label));
- else
- aa_label_free(label);
-}
-
-static void label_free_rcu(struct rcu_head *head)
-{
- struct aa_label *label = container_of(head, struct aa_label, rcu);
-
- if (label->flags & FLAG_IN_TREE)
- (void) aa_label_remove(label);
- label_free_switch(label);
-}
-
-void aa_label_kref(struct kref *kref)
-{
- struct aa_label *label = container_of(kref, struct aa_label, count);
- struct aa_ns *ns = labels_ns(label);
-
- if (!ns) {
- /* never live, no rcu callback needed, just using the fn */
- label_free_switch(label);
- return;
- }
- /* TODO: update labels_profile macro so it works here */
- AA_BUG(label_isprofile(label) &&
- on_list_rcu(&label->vec[0]->base.profiles));
- AA_BUG(label_isprofile(label) &&
- on_list_rcu(&label->vec[0]->base.list));
-
- /* TODO: if compound label and not stale add to reclaim cache */
- call_rcu(&label->rcu, label_free_rcu);
-}
-
-static void label_free_or_put_new(struct aa_label *label, struct aa_label *new)
-{
- if (label != new)
- /* need to free directly to break circular ref with proxy */
- aa_label_free(new);
- else
- aa_put_label(new);
-}
-
-bool aa_label_init(struct aa_label *label, int size)
-{
- AA_BUG(!label);
- AA_BUG(size < 1);
-
- label->secid = aa_alloc_secid();
- if (label->secid == AA_SECID_INVALID)
- return false;
-
- label->size = size; /* doesn't include null */
- label->vec[size] = NULL; /* null terminate */
- kref_init(&label->count);
- RB_CLEAR_NODE(&label->node);
-
- return true;
-}
-
-/**
- * aa_label_alloc - allocate a label with a profile vector of @size length
- * @size: size of profile vector in the label
- * @proxy: proxy to use OR null if to allocate a new one
- * @gfp: memory allocation type
- *
- * Returns: new label
- * else NULL if failed
- */
-struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp)
-{
- struct aa_label *new;
-
- AA_BUG(size < 1);
-
- /* + 1 for null terminator entry on vec */
- new = kzalloc(sizeof(*new) + sizeof(struct aa_profile *) * (size + 1),
- gfp);
- AA_DEBUG("%s (%p)\n", __func__, new);
- if (!new)
- goto fail;
-
- if (!aa_label_init(new, size))
- goto fail;
-
- if (!proxy) {
- proxy = aa_alloc_proxy(new, gfp);
- if (!proxy)
- goto fail;
- } else
- aa_get_proxy(proxy);
- /* just set new's proxy, don't redirect proxy here if it was passed in*/
- new->proxy = proxy;
-
- return new;
-
-fail:
- kfree(new);
-
- return NULL;
-}
-
-
-/**
- * label_cmp - label comparision for set ordering
- * @a: label to compare (NOT NULL)
- * @b: label to compare (NOT NULL)
- *
- * Returns: <0 if a < b
- * ==0 if a == b
- * >0 if a > b
- */
-static int label_cmp(struct aa_label *a, struct aa_label *b)
-{
- AA_BUG(!b);
-
- if (a == b)
- return 0;
-
- return vec_cmp(a->vec, a->size, b->vec, b->size);
-}
-
-/* helper fn for label_for_each_confined */
-int aa_label_next_confined(struct aa_label *label, int i)
-{
- AA_BUG(!label);
- AA_BUG(i < 0);
-
- for (; i < label->size; i++) {
- if (!profile_unconfined(label->vec[i]))
- return i;
- }
-
- return i;
-}
-
-/**
- * aa_label_next_not_in_set - return the next profile of @sub not in @set
- * @I: label iterator
- * @set: label to test against
- * @sub: label to if is subset of @set
- *
- * Returns: profile in @sub that is not in @set, with iterator set pos after
- * else NULL if @sub is a subset of @set
- */
-struct aa_profile *__aa_label_next_not_in_set(struct label_it *I,
- struct aa_label *set,
- struct aa_label *sub)
-{
- AA_BUG(!set);
- AA_BUG(!I);
- AA_BUG(I->i < 0);
- AA_BUG(I->i > set->size);
- AA_BUG(!sub);
- AA_BUG(I->j < 0);
- AA_BUG(I->j > sub->size);
-
- while (I->j < sub->size && I->i < set->size) {
- int res = profile_cmp(sub->vec[I->j], set->vec[I->i]);
-
- if (res == 0) {
- (I->j)++;
- (I->i)++;
- } else if (res > 0)
- (I->i)++;
- else
- return sub->vec[(I->j)++];
- }
-
- if (I->j < sub->size)
- return sub->vec[(I->j)++];
-
- return NULL;
-}
-
-/**
- * aa_label_is_subset - test if @sub is a subset of @set
- * @set: label to test against
- * @sub: label to test if is subset of @set
- *
- * Returns: true if @sub is subset of @set
- * else false
- */
-bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub)
-{
- struct label_it i = { };
-
- AA_BUG(!set);
- AA_BUG(!sub);
-
- if (sub == set)
- return true;
-
- return __aa_label_next_not_in_set(&i, set, sub) == NULL;
-}
-
-
-
-/**
- * __label_remove - remove @label from the label set
- * @l: label to remove
- * @new: label to redirect to
- *
- * Requires: labels_set(@label)->lock write_lock
- * Returns: true if the label was in the tree and removed
- */
-static bool __label_remove(struct aa_label *label, struct aa_label *new)
-{
- struct aa_labelset *ls = labels_set(label);
-
- AA_BUG(!ls);
- AA_BUG(!label);
- AA_BUG(!write_is_locked(&ls->lock));
-
- if (new)
- __aa_proxy_redirect(label, new);
-
- if (!label_is_stale(label))
- __label_make_stale(label);
-
- if (label->flags & FLAG_IN_TREE) {
- rb_erase(&label->node, &ls->root);
- label->flags &= ~FLAG_IN_TREE;
- return true;
- }
-
- return false;
-}
-
-/**
- * __label_replace - replace @old with @new in label set
- * @old: label to remove from label set
- * @new: label to replace @old with
- *
- * Requires: labels_set(@old)->lock write_lock
- * valid ref count be held on @new
- * Returns: true if @old was in set and replaced by @new
- *
- * Note: current implementation requires label set be order in such a way
- * that @new directly replaces @old position in the set (ie.
- * using pointer comparison of the label address would not work)
- */
-static bool __label_replace(struct aa_label *old, struct aa_label *new)
-{
- struct aa_labelset *ls = labels_set(old);
-
- AA_BUG(!ls);
- AA_BUG(!old);
- AA_BUG(!new);
- AA_BUG(!write_is_locked(&ls->lock));
- AA_BUG(new->flags & FLAG_IN_TREE);
-
- if (!label_is_stale(old))
- __label_make_stale(old);
-
- if (old->flags & FLAG_IN_TREE) {
- rb_replace_node(&old->node, &new->node, &ls->root);
- old->flags &= ~FLAG_IN_TREE;
- new->flags |= FLAG_IN_TREE;
- return true;
- }
-
- return false;
-}
-
-/**
- * __label_insert - attempt to insert @l into a label set
- * @ls: set of labels to insert @l into (NOT NULL)
- * @label: new label to insert (NOT NULL)
- * @replace: whether insertion should replace existing entry that is not stale
- *
- * Requires: @ls->lock
- * caller to hold a valid ref on l
- * if @replace is true l has a preallocated proxy associated
- * Returns: @l if successful in inserting @l - with additional refcount
- * else ref counted equivalent label that is already in the set,
- * the else condition only happens if @replace is false
- */
-static struct aa_label *__label_insert(struct aa_labelset *ls,
- struct aa_label *label, bool replace)
-{
- struct rb_node **new, *parent = NULL;
-
- AA_BUG(!ls);
- AA_BUG(!label);
- AA_BUG(labels_set(label) != ls);
- AA_BUG(!write_is_locked(&ls->lock));
- AA_BUG(label->flags & FLAG_IN_TREE);
-
- /* Figure out where to put new node */
- new = &ls->root.rb_node;
- while (*new) {
- struct aa_label *this = rb_entry(*new, struct aa_label, node);
- int result = label_cmp(label, this);
-
- parent = *new;
- if (result == 0) {
- /* !__aa_get_label means queued for destruction,
- * so replace in place, however the label has
- * died before the replacement so do not share
- * the proxy
- */
- if (!replace && !label_is_stale(this)) {
- if (__aa_get_label(this))
- return this;
- } else
- __proxy_share(this, label);
- AA_BUG(!__label_replace(this, label));
- return aa_get_label(label);
- } else if (result < 0)
- new = &((*new)->rb_left);
- else /* (result > 0) */
- new = &((*new)->rb_right);
- }
-
- /* Add new node and rebalance tree. */
- rb_link_node(&label->node, parent, new);
- rb_insert_color(&label->node, &ls->root);
- label->flags |= FLAG_IN_TREE;
-
- return aa_get_label(label);
-}
-
-/**
- * __vec_find - find label that matches @vec in label set
- * @vec: vec of profiles to find matching label for (NOT NULL)
- * @n: length of @vec
- *
- * Requires: @vec_labelset(vec) lock held
- * caller to hold a valid ref on l
- *
- * Returns: ref counted @label if matching label is in tree
- * ref counted label that is equiv to @l in tree
- * else NULL if @vec equiv is not in tree
- */
-static struct aa_label *__vec_find(struct aa_profile **vec, int n)
-{
- struct rb_node *node;
-
- AA_BUG(!vec);
- AA_BUG(!*vec);
- AA_BUG(n <= 0);
-
- node = vec_labelset(vec, n)->root.rb_node;
- while (node) {
- struct aa_label *this = rb_entry(node, struct aa_label, node);
- int result = vec_cmp(this->vec, this->size, vec, n);
-
- if (result > 0)
- node = node->rb_left;
- else if (result < 0)
- node = node->rb_right;
- else
- return __aa_get_label(this);
- }
-
- return NULL;
-}
-
-/**
- * __label_find - find label @label in label set
- * @label: label to find (NOT NULL)
- *
- * Requires: labels_set(@label)->lock held
- * caller to hold a valid ref on l
- *
- * Returns: ref counted @label if @label is in tree OR
- * ref counted label that is equiv to @label in tree
- * else NULL if @label or equiv is not in tree
- */
-static struct aa_label *__label_find(struct aa_label *label)
-{
- AA_BUG(!label);
-
- return __vec_find(label->vec, label->size);
-}
-
-
-/**
- * aa_label_remove - remove a label from the labelset
- * @label: label to remove
- *
- * Returns: true if @label was removed from the tree
- * else @label was not in tree so it could not be removed
- */
-bool aa_label_remove(struct aa_label *label)
-{
- struct aa_labelset *ls = labels_set(label);
- unsigned long flags;
- bool res;
-
- AA_BUG(!ls);
-
- write_lock_irqsave(&ls->lock, flags);
- res = __label_remove(label, ns_unconfined(labels_ns(label)));
- write_unlock_irqrestore(&ls->lock, flags);
-
- return res;
-}
-
-/**
- * aa_label_replace - replace a label @old with a new version @new
- * @old: label to replace
- * @new: label replacing @old
- *
- * Returns: true if @old was in tree and replaced
- * else @old was not in tree, and @new was not inserted
- */
-bool aa_label_replace(struct aa_label *old, struct aa_label *new)
-{
- unsigned long flags;
- bool res;
-
- if (name_is_shared(old, new) && labels_ns(old) == labels_ns(new)) {
- write_lock_irqsave(&labels_set(old)->lock, flags);
- if (old->proxy != new->proxy)
- __proxy_share(old, new);
- else
- __aa_proxy_redirect(old, new);
- res = __label_replace(old, new);
- write_unlock_irqrestore(&labels_set(old)->lock, flags);
- } else {
- struct aa_label *l;
- struct aa_labelset *ls = labels_set(old);
-
- write_lock_irqsave(&ls->lock, flags);
- res = __label_remove(old, new);
- if (labels_ns(old) != labels_ns(new)) {
- write_unlock_irqrestore(&ls->lock, flags);
- ls = labels_set(new);
- write_lock_irqsave(&ls->lock, flags);
- }
- l = __label_insert(ls, new, true);
- res = (l == new);
- write_unlock_irqrestore(&ls->lock, flags);
- aa_put_label(l);
- }
-
- return res;
-}
-
-/**
- * vec_find - find label @l in label set
- * @vec: array of profiles to find equiv label for (NOT NULL)
- * @n: length of @vec
- *
- * Returns: refcounted label if @vec equiv is in tree
- * else NULL if @vec equiv is not in tree
- */
-static struct aa_label *vec_find(struct aa_profile **vec, int n)
-{
- struct aa_labelset *ls;
- struct aa_label *label;
- unsigned long flags;
-
- AA_BUG(!vec);
- AA_BUG(!*vec);
- AA_BUG(n <= 0);
-
- ls = vec_labelset(vec, n);
- read_lock_irqsave(&ls->lock, flags);
- label = __vec_find(vec, n);
- read_unlock_irqrestore(&ls->lock, flags);
-
- return label;
-}
-
-/* requires sort and merge done first */
-static struct aa_label *vec_create_and_insert_label(struct aa_profile **vec,
- int len, gfp_t gfp)
-{
- struct aa_label *label = NULL;
- struct aa_labelset *ls;
- unsigned long flags;
- struct aa_label *new;
- int i;
-
- AA_BUG(!vec);
-
- if (len == 1)
- return aa_get_label(&vec[0]->label);
-
- ls = labels_set(&vec[len - 1]->label);
-
- /* TODO: enable when read side is lockless
- * check if label exists before taking locks
- */
- new = aa_label_alloc(len, NULL, gfp);
- if (!new)
- return NULL;
-
- for (i = 0; i < len; i++)
- new->vec[i] = aa_get_profile(vec[i]);
-
- write_lock_irqsave(&ls->lock, flags);
- label = __label_insert(ls, new, false);
- write_unlock_irqrestore(&ls->lock, flags);
- label_free_or_put_new(label, new);
-
- return label;
-}
-
-struct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len,
- gfp_t gfp)
-{
- struct aa_label *label = vec_find(vec, len);
-
- if (label)
- return label;
-
- return vec_create_and_insert_label(vec, len, gfp);
-}
-
-/**
- * aa_label_find - find label @label in label set
- * @label: label to find (NOT NULL)
- *
- * Requires: caller to hold a valid ref on l
- *
- * Returns: refcounted @label if @label is in tree
- * refcounted label that is equiv to @label in tree
- * else NULL if @label or equiv is not in tree
- */
-struct aa_label *aa_label_find(struct aa_label *label)
-{
- AA_BUG(!label);
-
- return vec_find(label->vec, label->size);
-}
-
-
-/**
- * aa_label_insert - insert label @label into @ls or return existing label
- * @ls - labelset to insert @label into
- * @label - label to insert
- *
- * Requires: caller to hold a valid ref on @label
- *
- * Returns: ref counted @label if successful in inserting @label
- * else ref counted equivalent label that is already in the set
- */
-struct aa_label *aa_label_insert(struct aa_labelset *ls, struct aa_label *label)
-{
- struct aa_label *l;
- unsigned long flags;
-
- AA_BUG(!ls);
- AA_BUG(!label);
-
- /* check if label exists before taking lock */
- if (!label_is_stale(label)) {
- read_lock_irqsave(&ls->lock, flags);
- l = __label_find(label);
- read_unlock_irqrestore(&ls->lock, flags);
- if (l)
- return l;
- }
-
- write_lock_irqsave(&ls->lock, flags);
- l = __label_insert(ls, label, false);
- write_unlock_irqrestore(&ls->lock, flags);
-
- return l;
-}
-
-
-/**
- * aa_label_next_in_merge - find the next profile when merging @a and @b
- * @I: label iterator
- * @a: label to merge
- * @b: label to merge
- *
- * Returns: next profile
- * else null if no more profiles
- */
-struct aa_profile *aa_label_next_in_merge(struct label_it *I,
- struct aa_label *a,
- struct aa_label *b)
-{
- AA_BUG(!a);
- AA_BUG(!b);
- AA_BUG(!I);
- AA_BUG(I->i < 0);
- AA_BUG(I->i > a->size);
- AA_BUG(I->j < 0);
- AA_BUG(I->j > b->size);
-
- if (I->i < a->size) {
- if (I->j < b->size) {
- int res = profile_cmp(a->vec[I->i], b->vec[I->j]);
-
- if (res > 0)
- return b->vec[(I->j)++];
- if (res == 0)
- (I->j)++;
- }
-
- return a->vec[(I->i)++];
- }
-
- if (I->j < b->size)
- return b->vec[(I->j)++];
-
- return NULL;
-}
-
-/**
- * label_merge_cmp - cmp of @a merging with @b against @z for set ordering
- * @a: label to merge then compare (NOT NULL)
- * @b: label to merge then compare (NOT NULL)
- * @z: label to compare merge against (NOT NULL)
- *
- * Assumes: using the most recent versions of @a, @b, and @z
- *
- * Returns: <0 if a < b
- * ==0 if a == b
- * >0 if a > b
- */
-static int label_merge_cmp(struct aa_label *a, struct aa_label *b,
- struct aa_label *z)
-{
- struct aa_profile *p = NULL;
- struct label_it i = { };
- int k;
-
- AA_BUG(!a);
- AA_BUG(!b);
- AA_BUG(!z);
-
- for (k = 0;
- k < z->size && (p = aa_label_next_in_merge(&i, a, b));
- k++) {
- int res = profile_cmp(p, z->vec[k]);
-
- if (res != 0)
- return res;
- }
-
- if (p)
- return 1;
- else if (k < z->size)
- return -1;
- return 0;
-}
-
-/**
- * label_merge_insert - create a new label by merging @a and @b
- * @new: preallocated label to merge into (NOT NULL)
- * @a: label to merge with @b (NOT NULL)
- * @b: label to merge with @a (NOT NULL)
- *
- * Requires: preallocated proxy
- *
- * Returns: ref counted label either @new if merge is unique
- * @a if @b is a subset of @a
- * @b if @a is a subset of @b
- *
- * NOTE: will not use @new if the merge results in @new == @a or @b
- *
- * Must be used within labelset write lock to avoid racing with
- * setting labels stale.
- */
-static struct aa_label *label_merge_insert(struct aa_label *new,
- struct aa_label *a,
- struct aa_label *b)
-{
- struct aa_label *label;
- struct aa_labelset *ls;
- struct aa_profile *next;
- struct label_it i;
- unsigned long flags;
- int k = 0, invcount = 0;
- bool stale = false;
-
- AA_BUG(!a);
- AA_BUG(a->size < 0);
- AA_BUG(!b);
- AA_BUG(b->size < 0);
- AA_BUG(!new);
- AA_BUG(new->size < a->size + b->size);
-
- label_for_each_in_merge(i, a, b, next) {
- AA_BUG(!next);
- if (profile_is_stale(next)) {
- new->vec[k] = aa_get_newest_profile(next);
- AA_BUG(!new->vec[k]->label.proxy);
- AA_BUG(!new->vec[k]->label.proxy->label);
- if (next->label.proxy != new->vec[k]->label.proxy)
- invcount++;
- k++;
- stale = true;
- } else
- new->vec[k++] = aa_get_profile(next);
- }
- /* set to actual size which is <= allocated len */
- new->size = k;
- new->vec[k] = NULL;
-
- if (invcount) {
- new->size -= aa_vec_unique(&new->vec[0], new->size,
- VEC_FLAG_TERMINATE);
- /* TODO: deal with reference labels */
- if (new->size == 1) {
- label = aa_get_label(&new->vec[0]->label);
- return label;
- }
- } else if (!stale) {
- /*
- * merge could be same as a || b, note: it is not possible
- * for new->size == a->size == b->size unless a == b
- */
- if (k == a->size)
- return aa_get_label(a);
- else if (k == b->size)
- return aa_get_label(b);
- }
- if (vec_unconfined(new->vec, new->size))
- new->flags |= FLAG_UNCONFINED;
- ls = labels_set(new);
- write_lock_irqsave(&ls->lock, flags);
- label = __label_insert(labels_set(new), new, false);
- write_unlock_irqrestore(&ls->lock, flags);
-
- return label;
-}
-
-/**
- * labelset_of_merge - find which labelset a merged label should be inserted
- * @a: label to merge and insert
- * @b: label to merge and insert
- *
- * Returns: labelset that the merged label should be inserted into
- */
-static struct aa_labelset *labelset_of_merge(struct aa_label *a,
- struct aa_label *b)
-{
- struct aa_ns *nsa = labels_ns(a);
- struct aa_ns *nsb = labels_ns(b);
-
- if (ns_cmp(nsa, nsb) <= 0)
- return &nsa->labels;
- return &nsb->labels;
-}
-
-/**
- * __label_find_merge - find label that is equiv to merge of @a and @b
- * @ls: set of labels to search (NOT NULL)
- * @a: label to merge with @b (NOT NULL)
- * @b: label to merge with @a (NOT NULL)
- *
- * Requires: ls->lock read_lock held
- *
- * Returns: ref counted label that is equiv to merge of @a and @b
- * else NULL if merge of @a and @b is not in set
- */
-static struct aa_label *__label_find_merge(struct aa_labelset *ls,
- struct aa_label *a,
- struct aa_label *b)
-{
- struct rb_node *node;
-
- AA_BUG(!ls);
- AA_BUG(!a);
- AA_BUG(!b);
-
- if (a == b)
- return __label_find(a);
-
- node = ls->root.rb_node;
- while (node) {
- struct aa_label *this = container_of(node, struct aa_label,
- node);
- int result = label_merge_cmp(a, b, this);
-
- if (result < 0)
- node = node->rb_left;
- else if (result > 0)
- node = node->rb_right;
- else
- return __aa_get_label(this);
- }
-
- return NULL;
-}
-
-
-/**
- * aa_label_find_merge - find label that is equiv to merge of @a and @b
- * @a: label to merge with @b (NOT NULL)
- * @b: label to merge with @a (NOT NULL)
- *
- * Requires: labels be fully constructed with a valid ns
- *
- * Returns: ref counted label that is equiv to merge of @a and @b
- * else NULL if merge of @a and @b is not in set
- */
-struct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b)
-{
- struct aa_labelset *ls;
- struct aa_label *label, *ar = NULL, *br = NULL;
- unsigned long flags;
-
- AA_BUG(!a);
- AA_BUG(!b);
-
- if (label_is_stale(a))
- a = ar = aa_get_newest_label(a);
- if (label_is_stale(b))
- b = br = aa_get_newest_label(b);
- ls = labelset_of_merge(a, b);
- read_lock_irqsave(&ls->lock, flags);
- label = __label_find_merge(ls, a, b);
- read_unlock_irqrestore(&ls->lock, flags);
- aa_put_label(ar);
- aa_put_label(br);
-
- return label;
-}
-
-/**
- * aa_label_merge - attempt to insert new merged label of @a and @b
- * @ls: set of labels to insert label into (NOT NULL)
- * @a: label to merge with @b (NOT NULL)
- * @b: label to merge with @a (NOT NULL)
- * @gfp: memory allocation type
- *
- * Requires: caller to hold valid refs on @a and @b
- * labels be fully constructed with a valid ns
- *
- * Returns: ref counted new label if successful in inserting merge of a & b
- * else ref counted equivalent label that is already in the set.
- * else NULL if could not create label (-ENOMEM)
- */
-struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b,
- gfp_t gfp)
-{
- struct aa_label *label = NULL;
-
- AA_BUG(!a);
- AA_BUG(!b);
-
- if (a == b)
- return aa_get_newest_label(a);
-
- /* TODO: enable when read side is lockless
- * check if label exists before taking locks
- if (!label_is_stale(a) && !label_is_stale(b))
- label = aa_label_find_merge(a, b);
- */
-
- if (!label) {
- struct aa_label *new;
-
- a = aa_get_newest_label(a);
- b = aa_get_newest_label(b);
-
- /* could use label_merge_len(a, b), but requires double
- * comparison for small savings
- */
- new = aa_label_alloc(a->size + b->size, NULL, gfp);
- if (!new)
- goto out;
-
- label = label_merge_insert(new, a, b);
- label_free_or_put_new(label, new);
-out:
- aa_put_label(a);
- aa_put_label(b);
- }
-
- return label;
-}
-
-static inline bool label_is_visible(struct aa_profile *profile,
- struct aa_label *label)
-{
- return aa_ns_visible(profile->ns, labels_ns(label), true);
-}
-
-/* match a profile and its associated ns component if needed
- * Assumes visibility test has already been done.
- * If a subns profile is not to be matched should be prescreened with
- * visibility test.
- */
-static inline unsigned int match_component(struct aa_profile *profile,
- struct aa_profile *tp,
- unsigned int state)
-{
- const char *ns_name;
-
- if (profile->ns == tp->ns)
- return aa_dfa_match(profile->policy.dfa, state, tp->base.hname);
-
- /* try matching with namespace name and then profile */
- ns_name = aa_ns_name(profile->ns, tp->ns, true);
- state = aa_dfa_match_len(profile->policy.dfa, state, ":", 1);
- state = aa_dfa_match(profile->policy.dfa, state, ns_name);
- state = aa_dfa_match_len(profile->policy.dfa, state, ":", 1);
- return aa_dfa_match(profile->policy.dfa, state, tp->base.hname);
-}
-
-/**
- * label_compound_match - find perms for full compound label
- * @profile: profile to find perms for
- * @label: label to check access permissions for
- * @start: state to start match in
- * @subns: whether to do permission checks on components in a subns
- * @request: permissions to request
- * @perms: perms struct to set
- *
- * Returns: 0 on success else ERROR
- *
- * For the label A//&B//&C this does the perm match for A//&B//&C
- * @perms should be preinitialized with allperms OR a previous permission
- * check to be stacked.
- */
-static int label_compound_match(struct aa_profile *profile,
- struct aa_label *label,
- unsigned int state, bool subns, u32 request,
- struct aa_perms *perms)
-{
- struct aa_profile *tp;
- struct label_it i;
-
- /* find first subcomponent that is visible */
- label_for_each(i, label, tp) {
- if (!aa_ns_visible(profile->ns, tp->ns, subns))
- continue;
- state = match_component(profile, tp, state);
- if (!state)
- goto fail;
- goto next;
- }
-
- /* no component visible */
- *perms = allperms;
- return 0;
-
-next:
- label_for_each_cont(i, label, tp) {
- if (!aa_ns_visible(profile->ns, tp->ns, subns))
- continue;
- state = aa_dfa_match(profile->policy.dfa, state, "//&");
- state = match_component(profile, tp, state);
- if (!state)
- goto fail;
- }
- aa_compute_perms(profile->policy.dfa, state, perms);
- aa_apply_modes_to_perms(profile, perms);
- if ((perms->allow & request) != request)
- return -EACCES;
-
- return 0;
-
-fail:
- *perms = nullperms;
- return state;
-}
-
-/**
- * label_components_match - find perms for all subcomponents of a label
- * @profile: profile to find perms for
- * @label: label to check access permissions for
- * @start: state to start match in
- * @subns: whether to do permission checks on components in a subns
- * @request: permissions to request
- * @perms: an initialized perms struct to add accumulation to
- *
- * Returns: 0 on success else ERROR
- *
- * For the label A//&B//&C this does the perm match for each of A and B and C
- * @perms should be preinitialized with allperms OR a previous permission
- * check to be stacked.
- */
-static int label_components_match(struct aa_profile *profile,
- struct aa_label *label, unsigned int start,
- bool subns, u32 request,
- struct aa_perms *perms)
-{
- struct aa_profile *tp;
- struct label_it i;
- struct aa_perms tmp;
- unsigned int state = 0;
-
- /* find first subcomponent to test */
- label_for_each(i, label, tp) {
- if (!aa_ns_visible(profile->ns, tp->ns, subns))
- continue;
- state = match_component(profile, tp, start);
- if (!state)
- goto fail;
- goto next;
- }
-
- /* no subcomponents visible - no change in perms */
- return 0;
-
-next:
- aa_compute_perms(profile->policy.dfa, state, &tmp);
- aa_apply_modes_to_perms(profile, &tmp);
- aa_perms_accum(perms, &tmp);
- label_for_each_cont(i, label, tp) {
- if (!aa_ns_visible(profile->ns, tp->ns, subns))
- continue;
- state = match_component(profile, tp, start);
- if (!state)
- goto fail;
- aa_compute_perms(profile->policy.dfa, state, &tmp);
- aa_apply_modes_to_perms(profile, &tmp);
- aa_perms_accum(perms, &tmp);
- }
-
- if ((perms->allow & request) != request)
- return -EACCES;
-
- return 0;
-
-fail:
- *perms = nullperms;
- return -EACCES;
-}
-
-/**
- * aa_label_match - do a multi-component label match
- * @profile: profile to match against (NOT NULL)
- * @label: label to match (NOT NULL)
- * @state: state to start in
- * @subns: whether to match subns components
- * @request: permission request
- * @perms: Returns computed perms (NOT NULL)
- *
- * Returns: the state the match finished in, may be the none matching state
- */
-int aa_label_match(struct aa_profile *profile, struct aa_label *label,
- unsigned int state, bool subns, u32 request,
- struct aa_perms *perms)
-{
- int error = label_compound_match(profile, label, state, subns, request,
- perms);
- if (!error)
- return error;
-
- *perms = allperms;
- return label_components_match(profile, label, state, subns, request,
- perms);
-}
-
-
-/**
- * aa_update_label_name - update a label to have a stored name
- * @ns: ns being viewed from (NOT NULL)
- * @label: label to update (NOT NULL)
- * @gfp: type of memory allocation
- *
- * Requires: labels_set(label) not locked in caller
- *
- * note: only updates the label name if it does not have a name already
- * and if it is in the labelset
- */
-bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp)
-{
- struct aa_labelset *ls;
- unsigned long flags;
- char __counted *name;
- bool res = false;
-
- AA_BUG(!ns);
- AA_BUG(!label);
-
- if (label->hname || labels_ns(label) != ns)
- return res;
-
- if (aa_label_acntsxprint(&name, ns, label, FLAGS_NONE, gfp) == -1)
- return res;
-
- ls = labels_set(label);
- write_lock_irqsave(&ls->lock, flags);
- if (!label->hname && label->flags & FLAG_IN_TREE) {
- label->hname = name;
- res = true;
- } else
- aa_put_str(name);
- write_unlock_irqrestore(&ls->lock, flags);
-
- return res;
-}
-
-/*
- * cached label name is present and visible
- * @label->hname only exists if label is namespace hierachical
- */
-static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label,
- int flags)
-{
- if (label->hname && (!ns || labels_ns(label) == ns) &&
- !(flags & ~FLAG_SHOW_MODE))
- return true;
-
- return false;
-}
-
-/* helper macro for snprint routines */
-#define update_for_len(total, len, size, str) \
-do { \
- AA_BUG(len < 0); \
- total += len; \
- len = min(len, size); \
- size -= len; \
- str += len; \
-} while (0)
-
-/**
- * aa_profile_snxprint - print a profile name to a buffer
- * @str: buffer to write to. (MAY BE NULL if @size == 0)
- * @size: size of buffer
- * @view: namespace profile is being viewed from
- * @profile: profile to view (NOT NULL)
- * @flags: whether to include the mode string
- * @prev_ns: last ns printed when used in compound print
- *
- * Returns: size of name written or would be written if larger than
- * available buffer
- *
- * Note: will not print anything if the profile is not visible
- */
-static int aa_profile_snxprint(char *str, size_t size, struct aa_ns *view,
- struct aa_profile *profile, int flags,
- struct aa_ns **prev_ns)
-{
- const char *ns_name = NULL;
-
- AA_BUG(!str && size != 0);
- AA_BUG(!profile);
-
- if (!view)
- view = profiles_ns(profile);
-
- if (view != profile->ns &&
- (!prev_ns || (*prev_ns != profile->ns))) {
- if (prev_ns)
- *prev_ns = profile->ns;
- ns_name = aa_ns_name(view, profile->ns,
- flags & FLAG_VIEW_SUBNS);
- if (ns_name == aa_hidden_ns_name) {
- if (flags & FLAG_HIDDEN_UNCONFINED)
- return snprintf(str, size, "%s", "unconfined");
- return snprintf(str, size, "%s", ns_name);
- }
- }
-
- if ((flags & FLAG_SHOW_MODE) && profile != profile->ns->unconfined) {
- const char *modestr = aa_profile_mode_names[profile->mode];
-
- if (ns_name)
- return snprintf(str, size, ":%s:%s (%s)", ns_name,
- profile->base.hname, modestr);
- return snprintf(str, size, "%s (%s)", profile->base.hname,
- modestr);
- }
-
- if (ns_name)
- return snprintf(str, size, ":%s:%s", ns_name,
- profile->base.hname);
- return snprintf(str, size, "%s", profile->base.hname);
-}
-
-static const char *label_modename(struct aa_ns *ns, struct aa_label *label,
- int flags)
-{
- struct aa_profile *profile;
- struct label_it i;
- int mode = -1, count = 0;
-
- label_for_each(i, label, profile) {
- if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
- if (profile->mode == APPARMOR_UNCONFINED)
- /* special case unconfined so stacks with
- * unconfined don't report as mixed. ie.
- * profile_foo//&:ns1:unconfined (mixed)
- */
- continue;
- count++;
- if (mode == -1)
- mode = profile->mode;
- else if (mode != profile->mode)
- return "mixed";
- }
- }
-
- if (count == 0)
- return "-";
- if (mode == -1)
- /* everything was unconfined */
- mode = APPARMOR_UNCONFINED;
-
- return aa_profile_mode_names[mode];
-}
-
-/* if any visible label is not unconfined the display_mode returns true */
-static inline bool display_mode(struct aa_ns *ns, struct aa_label *label,
- int flags)
-{
- if ((flags & FLAG_SHOW_MODE)) {
- struct aa_profile *profile;
- struct label_it i;
-
- label_for_each(i, label, profile) {
- if (aa_ns_visible(ns, profile->ns,
- flags & FLAG_VIEW_SUBNS) &&
- profile != profile->ns->unconfined)
- return true;
- }
- /* only ns->unconfined in set of profiles in ns */
- return false;
- }
-
- return false;
-}
-
-/**
- * aa_label_snxprint - print a label name to a string buffer
- * @str: buffer to write to. (MAY BE NULL if @size == 0)
- * @size: size of buffer
- * @ns: namespace profile is being viewed from
- * @label: label to view (NOT NULL)
- * @flags: whether to include the mode string
- *
- * Returns: size of name written or would be written if larger than
- * available buffer
- *
- * Note: labels do not have to be strictly hierarchical to the ns as
- * objects may be shared across different namespaces and thus
- * pickup labeling from each ns. If a particular part of the
- * label is not visible it will just be excluded. And if none
- * of the label is visible "---" will be used.
- */
-int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns,
- struct aa_label *label, int flags)
-{
- struct aa_profile *profile;
- struct aa_ns *prev_ns = NULL;
- struct label_it i;
- int count = 0, total = 0;
- size_t len;
-
- AA_BUG(!str && size != 0);
- AA_BUG(!label);
-
- if (flags & FLAG_ABS_ROOT) {
- ns = root_ns;
- len = snprintf(str, size, "=");
- update_for_len(total, len, size, str);
- } else if (!ns) {
- ns = labels_ns(label);
- }
-
- label_for_each(i, label, profile) {
- if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
- if (count > 0) {
- len = snprintf(str, size, "//&");
- update_for_len(total, len, size, str);
- }
- len = aa_profile_snxprint(str, size, ns, profile,
- flags & FLAG_VIEW_SUBNS,
- &prev_ns);
- update_for_len(total, len, size, str);
- count++;
- }
- }
-
- if (count == 0) {
- if (flags & FLAG_HIDDEN_UNCONFINED)
- return snprintf(str, size, "%s", "unconfined");
- return snprintf(str, size, "%s", aa_hidden_ns_name);
- }
-
- /* count == 1 && ... is for backwards compat where the mode
- * is not displayed for 'unconfined' in the current ns
- */
- if (display_mode(ns, label, flags)) {
- len = snprintf(str, size, " (%s)",
- label_modename(ns, label, flags));
- update_for_len(total, len, size, str);
- }
-
- return total;
-}
-#undef update_for_len
-
-/**
- * aa_label_asxprint - allocate a string buffer and print label into it
- * @strp: Returns - the allocated buffer with the label name. (NOT NULL)
- * @ns: namespace profile is being viewed from
- * @label: label to view (NOT NULL)
- * @flags: flags controlling what label info is printed
- * @gfp: kernel memory allocation type
- *
- * Returns: size of name written or would be written if larger than
- * available buffer
- */
-int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label,
- int flags, gfp_t gfp)
-{
- int size;
-
- AA_BUG(!strp);
- AA_BUG(!label);
-
- size = aa_label_snxprint(NULL, 0, ns, label, flags);
- if (size < 0)
- return size;
-
- *strp = kmalloc(size + 1, gfp);
- if (!*strp)
- return -ENOMEM;
- return aa_label_snxprint(*strp, size + 1, ns, label, flags);
-}
-
-/**
- * aa_label_acntsxprint - allocate a __counted string buffer and print label
- * @strp: buffer to write to. (MAY BE NULL if @size == 0)
- * @ns: namespace profile is being viewed from
- * @label: label to view (NOT NULL)
- * @flags: flags controlling what label info is printed
- * @gfp: kernel memory allocation type
- *
- * Returns: size of name written or would be written if larger than
- * available buffer
- */
-int aa_label_acntsxprint(char __counted **strp, struct aa_ns *ns,
- struct aa_label *label, int flags, gfp_t gfp)
-{
- int size;
-
- AA_BUG(!strp);
- AA_BUG(!label);
-
- size = aa_label_snxprint(NULL, 0, ns, label, flags);
- if (size < 0)
- return size;
-
- *strp = aa_str_alloc(size + 1, gfp);
- if (!*strp)
- return -ENOMEM;
- return aa_label_snxprint(*strp, size + 1, ns, label, flags);
-}
-
-
-void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns,
- struct aa_label *label, int flags, gfp_t gfp)
-{
- const char *str;
- char *name = NULL;
- int len;
-
- AA_BUG(!ab);
- AA_BUG(!label);
-
- if (!use_label_hname(ns, label, flags) ||
- display_mode(ns, label, flags)) {
- len = aa_label_asxprint(&name, ns, label, flags, gfp);
- if (len == -1) {
- AA_DEBUG("label print error");
- return;
- }
- str = name;
- } else {
- str = (char *) label->hname;
- len = strlen(str);
- }
- if (audit_string_contains_control(str, len))
- audit_log_n_hex(ab, str, len);
- else
- audit_log_n_string(ab, str, len);
-
- kfree(name);
-}
-
-void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
- struct aa_label *label, int flags, gfp_t gfp)
-{
- AA_BUG(!f);
- AA_BUG(!label);
-
- if (!use_label_hname(ns, label, flags)) {
- char *str;
- int len;
-
- len = aa_label_asxprint(&str, ns, label, flags, gfp);
- if (len == -1) {
- AA_DEBUG("label print error");
- return;
- }
- seq_printf(f, "%s", str);
- kfree(str);
- } else if (display_mode(ns, label, flags))
- seq_printf(f, "%s (%s)", label->hname,
- label_modename(ns, label, flags));
- else
- seq_printf(f, "%s", label->hname);
-}
-
-void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
- gfp_t gfp)
-{
- AA_BUG(!label);
-
- if (!use_label_hname(ns, label, flags)) {
- char *str;
- int len;
-
- len = aa_label_asxprint(&str, ns, label, flags, gfp);
- if (len == -1) {
- AA_DEBUG("label print error");
- return;
- }
- pr_info("%s", str);
- kfree(str);
- } else if (display_mode(ns, label, flags))
- pr_info("%s (%s)", label->hname,
- label_modename(ns, label, flags));
- else
- pr_info("%s", label->hname);
-}
-
-void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp)
-{
- struct aa_ns *ns = aa_get_current_ns();
-
- aa_label_xaudit(ab, ns, label, FLAG_VIEW_SUBNS, gfp);
- aa_put_ns(ns);
-}
-
-void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp)
-{
- struct aa_ns *ns = aa_get_current_ns();
-
- aa_label_seq_xprint(f, ns, label, FLAG_VIEW_SUBNS, gfp);
- aa_put_ns(ns);
-}
-
-void aa_label_printk(struct aa_label *label, gfp_t gfp)
-{
- struct aa_ns *ns = aa_get_current_ns();
-
- aa_label_xprintk(ns, label, FLAG_VIEW_SUBNS, gfp);
- aa_put_ns(ns);
-}
-
-static int label_count_str_entries(const char *str)
-{
- const char *split;
- int count = 1;
-
- AA_BUG(!str);
-
- for (split = strstr(str, "//&"); split; split = strstr(str, "//&")) {
- count++;
- str = split + 3;
- }
-
- return count;
-}
-
-/*
- * ensure stacks with components like
- * :ns:A//&B
- * have :ns: applied to both 'A' and 'B' by making the lookup relative
- * to the base if the lookup specifies an ns, else making the stacked lookup
- * relative to the last embedded ns in the string.
- */
-static struct aa_profile *fqlookupn_profile(struct aa_label *base,
- struct aa_label *currentbase,
- const char *str, size_t n)
-{
- const char *first = skipn_spaces(str, n);
-
- if (first && *first == ':')
- return aa_fqlookupn_profile(base, str, n);
-
- return aa_fqlookupn_profile(currentbase, str, n);
-}
-
-/**
- * aa_label_parse - parse, validate and convert a text string to a label
- * @base: base label to use for lookups (NOT NULL)
- * @str: null terminated text string (NOT NULL)
- * @gfp: allocation type
- * @create: true if should create compound labels if they don't exist
- * @force_stack: true if should stack even if no leading &
- *
- * Returns: the matching refcounted label if present
- * else ERRPTR
- */
-struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
- gfp_t gfp, bool create, bool force_stack)
-{
- DEFINE_VEC(profile, vec);
- struct aa_label *label, *currbase = base;
- int i, len, stack = 0, error;
- char *split;
-
- AA_BUG(!base);
- AA_BUG(!str);
-
- str = skip_spaces(str);
- len = label_count_str_entries(str);
- if (*str == '&' || force_stack) {
- /* stack on top of base */
- stack = base->size;
- len += stack;
- if (*str == '&')
- str++;
- }
- if (*str == '=')
- base = &root_ns->unconfined->label;
-
- error = vec_setup(profile, vec, len, gfp);
- if (error)
- return ERR_PTR(error);
-
- for (i = 0; i < stack; i++)
- vec[i] = aa_get_profile(base->vec[i]);
-
- for (split = strstr(str, "//&"), i = stack; split && i < len; i++) {
- vec[i] = fqlookupn_profile(base, currbase, str, split - str);
- if (!vec[i])
- goto fail;
- /*
- * if component specified a new ns it becomes the new base
- * so that subsequent lookups are relative to it
- */
- if (vec[i]->ns != labels_ns(currbase))
- currbase = &vec[i]->label;
- str = split + 3;
- split = strstr(str, "//&");
- }
- /* last element doesn't have a split */
- if (i < len) {
- vec[i] = fqlookupn_profile(base, currbase, str, strlen(str));
- if (!vec[i])
- goto fail;
- }
- if (len == 1)
- /* no need to free vec as len < LOCAL_VEC_ENTRIES */
- return &vec[0]->label;
-
- len -= aa_vec_unique(vec, len, VEC_FLAG_TERMINATE);
- /* TODO: deal with reference labels */
- if (len == 1) {
- label = aa_get_label(&vec[0]->label);
- goto out;
- }
-
- if (create)
- label = aa_vec_find_or_create_label(vec, len, gfp);
- else
- label = vec_find(vec, len);
- if (!label)
- goto fail;
-
-out:
- /* use adjusted len from after vec_unique, not original */
- vec_cleanup(profile, vec, len);
- return label;
-
-fail:
- label = ERR_PTR(-ENOENT);
- goto out;
-}
-
-
-/**
- * aa_labelset_destroy - remove all labels from the label set
- * @ls: label set to cleanup (NOT NULL)
- *
- * Labels that are removed from the set may still exist beyond the set
- * being destroyed depending on their reference counting
- */
-void aa_labelset_destroy(struct aa_labelset *ls)
-{
- struct rb_node *node;
- unsigned long flags;
-
- AA_BUG(!ls);
-
- write_lock_irqsave(&ls->lock, flags);
- for (node = rb_first(&ls->root); node; node = rb_first(&ls->root)) {
- struct aa_label *this = rb_entry(node, struct aa_label, node);
-
- if (labels_ns(this) != root_ns)
- __label_remove(this,
- ns_unconfined(labels_ns(this)->parent));
- else
- __label_remove(this, NULL);
- }
- write_unlock_irqrestore(&ls->lock, flags);
-}
-
-/*
- * @ls: labelset to init (NOT NULL)
- */
-void aa_labelset_init(struct aa_labelset *ls)
-{
- AA_BUG(!ls);
-
- rwlock_init(&ls->lock);
- ls->root = RB_ROOT;
-}
-
-static struct aa_label *labelset_next_stale(struct aa_labelset *ls)
-{
- struct aa_label *label;
- struct rb_node *node;
- unsigned long flags;
-
- AA_BUG(!ls);
-
- read_lock_irqsave(&ls->lock, flags);
-
- __labelset_for_each(ls, node) {
- label = rb_entry(node, struct aa_label, node);
- if ((label_is_stale(label) ||
- vec_is_stale(label->vec, label->size)) &&
- __aa_get_label(label))
- goto out;
-
- }
- label = NULL;
-
-out:
- read_unlock_irqrestore(&ls->lock, flags);
-
- return label;
-}
-
-/**
- * __label_update - insert updated version of @label into labelset
- * @label - the label to update/repace
- *
- * Returns: new label that is up to date
- * else NULL on failure
- *
- * Requires: @ns lock be held
- *
- * Note: worst case is the stale @label does not get updated and has
- * to be updated at a later time.
- */
-static struct aa_label *__label_update(struct aa_label *label)
-{
- struct aa_label *new, *tmp;
- struct aa_labelset *ls;
- unsigned long flags;
- int i, invcount = 0;
-
- AA_BUG(!label);
- AA_BUG(!mutex_is_locked(&labels_ns(label)->lock));
-
- new = aa_label_alloc(label->size, label->proxy, GFP_KERNEL);
- if (!new)
- return NULL;
-
- /*
- * while holding the ns_lock will stop profile replacement, removal,
- * and label updates, label merging and removal can be occurring
- */
- ls = labels_set(label);
- write_lock_irqsave(&ls->lock, flags);
- for (i = 0; i < label->size; i++) {
- AA_BUG(!label->vec[i]);
- new->vec[i] = aa_get_newest_profile(label->vec[i]);
- AA_BUG(!new->vec[i]);
- AA_BUG(!new->vec[i]->label.proxy);
- AA_BUG(!new->vec[i]->label.proxy->label);
- if (new->vec[i]->label.proxy != label->vec[i]->label.proxy)
- invcount++;
- }
-
- /* updated stale label by being removed/renamed from labelset */
- if (invcount) {
- new->size -= aa_vec_unique(&new->vec[0], new->size,
- VEC_FLAG_TERMINATE);
- /* TODO: deal with reference labels */
- if (new->size == 1) {
- tmp = aa_get_label(&new->vec[0]->label);
- AA_BUG(tmp == label);
- goto remove;
- }
- if (labels_set(label) != labels_set(new)) {
- write_unlock_irqrestore(&ls->lock, flags);
- tmp = aa_label_insert(labels_set(new), new);
- write_lock_irqsave(&ls->lock, flags);
- goto remove;
- }
- } else
- AA_BUG(labels_ns(label) != labels_ns(new));
-
- tmp = __label_insert(labels_set(label), new, true);
-remove:
- /* ensure label is removed, and redirected correctly */
- __label_remove(label, tmp);
- write_unlock_irqrestore(&ls->lock, flags);
- label_free_or_put_new(tmp, new);
-
- return tmp;
-}
-
-/**
- * __labelset_update - update labels in @ns
- * @ns: namespace to update labels in (NOT NULL)
- *
- * Requires: @ns lock be held
- *
- * Walk the labelset ensuring that all labels are up to date and valid
- * Any label that has a stale component is marked stale and replaced and
- * by an updated version.
- *
- * If failures happen due to memory pressures then stale labels will
- * be left in place until the next pass.
- */
-static void __labelset_update(struct aa_ns *ns)
-{
- struct aa_label *label;
-
- AA_BUG(!ns);
- AA_BUG(!mutex_is_locked(&ns->lock));
-
- do {
- label = labelset_next_stale(&ns->labels);
- if (label) {
- struct aa_label *l = __label_update(label);
-
- aa_put_label(l);
- aa_put_label(label);
- }
- } while (label);
-}
-
-/**
- * __aa_labelset_udate_subtree - update all labels with a stale component
- * @ns: ns to start update at (NOT NULL)
- *
- * Requires: @ns lock be held
- *
- * Invalidates labels based on @p in @ns and any children namespaces.
- */
-void __aa_labelset_update_subtree(struct aa_ns *ns)
-{
- struct aa_ns *child;
-
- AA_BUG(!ns);
- AA_BUG(!mutex_is_locked(&ns->lock));
-
- __labelset_update(ns);
-
- list_for_each_entry(child, &ns->sub_ns, base.list) {
- mutex_lock_nested(&child->lock, child->level);
- __aa_labelset_update_subtree(child);
- mutex_unlock(&child->lock);
- }
-}
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index 08ca26bcca77..7cd788a9445b 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -21,14 +21,8 @@
#include "include/audit.h"
#include "include/apparmor.h"
#include "include/lib.h"
-#include "include/perms.h"
#include "include/policy.h"
-struct aa_perms nullperms;
-struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
- .quiet = ALL_PERMS_MASK,
- .hide = ALL_PERMS_MASK };
-
/**
* aa_split_fqname - split a fqname into a profile and namespace name
* @fqname: a full qualified name in namespace profile format (NOT NULL)
@@ -75,7 +69,7 @@ char *aa_split_fqname(char *fqname, char **ns_name)
* if all whitespace will return NULL
*/
-const char *skipn_spaces(const char *str, size_t n)
+static const char *skipn_spaces(const char *str, size_t n)
{
for (; n && isspace(*str); --n)
++str;
@@ -134,350 +128,11 @@ void aa_info_message(const char *str)
printk(KERN_INFO "AppArmor: %s\n", str);
}
-__counted char *aa_str_alloc(int size, gfp_t gfp)
-{
- struct counted_str *str;
-
- str = kmalloc(sizeof(struct counted_str) + size, gfp);
- if (!str)
- return NULL;
-
- kref_init(&str->count);
- return str->name;
-}
-
-void aa_str_kref(struct kref *kref)
-{
- kfree(container_of(kref, struct counted_str, count));
-}
-
-
-const char aa_file_perm_chrs[] = "xwracd km l ";
-const char *aa_file_perm_names[] = {
- "exec",
- "write",
- "read",
- "append",
-
- "create",
- "delete",
- "open",
- "rename",
-
- "setattr",
- "getattr",
- "setcred",
- "getcred",
-
- "chmod",
- "chown",
- "chgrp",
- "lock",
-
- "mmap",
- "mprot",
- "link",
- "snapshot",
-
- "unknown",
- "unknown",
- "unknown",
- "unknown",
-
- "unknown",
- "unknown",
- "unknown",
- "unknown",
-
- "stack",
- "change_onexec",
- "change_profile",
- "change_hat",
-};
-
-/**
- * aa_perm_mask_to_str - convert a perm mask to its short string
- * @str: character buffer to store string in (at least 10 characters)
- * @mask: permission mask to convert
- */
-void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask)
-{
- unsigned int i, perm = 1;
-
- for (i = 0; i < 32; perm <<= 1, i++) {
- if (mask & perm)
- *str++ = chrs[i];
- }
- *str = '\0';
-}
-
-void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask)
-{
- const char *fmt = "%s";
- unsigned int i, perm = 1;
- bool prev = false;
-
- for (i = 0; i < 32; perm <<= 1, i++) {
- if (mask & perm) {
- audit_log_format(ab, fmt, names[i]);
- if (!prev) {
- prev = true;
- fmt = " %s";
- }
- }
- }
-}
-
-void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
- u32 chrsmask, const char **names, u32 namesmask)
-{
- char str[33];
-
- audit_log_format(ab, "\"");
- if ((mask & chrsmask) && chrs) {
- aa_perm_mask_to_str(str, chrs, mask & chrsmask);
- mask &= ~chrsmask;
- audit_log_format(ab, "%s", str);
- if (mask & namesmask)
- audit_log_format(ab, " ");
- }
- if ((mask & namesmask) && names)
- aa_audit_perm_names(ab, names, mask & namesmask);
- audit_log_format(ab, "\"");
-}
-
-/**
- * aa_audit_perms_cb - generic callback fn for auditing perms
- * @ab: audit buffer (NOT NULL)
- * @va: audit struct to audit values of (NOT NULL)
- */
-static void aa_audit_perms_cb(struct audit_buffer *ab, void *va)
-{
- struct common_audit_data *sa = va;
-
- if (aad(sa)->request) {
- audit_log_format(ab, " requested_mask=");
- aa_audit_perm_mask(ab, aad(sa)->request, aa_file_perm_chrs,
- PERMS_CHRS_MASK, aa_file_perm_names,
- PERMS_NAMES_MASK);
- }
- if (aad(sa)->denied) {
- audit_log_format(ab, "denied_mask=");
- aa_audit_perm_mask(ab, aad(sa)->denied, aa_file_perm_chrs,
- PERMS_CHRS_MASK, aa_file_perm_names,
- PERMS_NAMES_MASK);
- }
- audit_log_format(ab, " peer=");
- aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
- FLAGS_NONE, GFP_ATOMIC);
-}
-
-/**
- * aa_apply_modes_to_perms - apply namespace and profile flags to perms
- * @profile: that perms where computed from
- * @perms: perms to apply mode modifiers to
- *
- * TODO: split into profile and ns based flags for when accumulating perms
- */
-void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
-{
- switch (AUDIT_MODE(profile)) {
- case AUDIT_ALL:
- perms->audit = ALL_PERMS_MASK;
- /* fall through */
- case AUDIT_NOQUIET:
- perms->quiet = 0;
- break;
- case AUDIT_QUIET:
- perms->audit = 0;
- /* fall through */
- case AUDIT_QUIET_DENIED:
- perms->quiet = ALL_PERMS_MASK;
- break;
- }
-
- if (KILL_MODE(profile))
- perms->kill = ALL_PERMS_MASK;
- else if (COMPLAIN_MODE(profile))
- perms->complain = ALL_PERMS_MASK;
-/*
- * TODO:
- * else if (PROMPT_MODE(profile))
- * perms->prompt = ALL_PERMS_MASK;
- */
-}
-
-static u32 map_other(u32 x)
-{
- return ((x & 0x3) << 8) | /* SETATTR/GETATTR */
- ((x & 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */
- ((x & 0x60) << 19); /* SETOPT/GETOPT */
-}
-
-void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
- struct aa_perms *perms)
-{
- perms->deny = 0;
- perms->kill = perms->stop = 0;
- perms->complain = perms->cond = 0;
- perms->hide = 0;
- perms->prompt = 0;
- perms->allow = dfa_user_allow(dfa, state);
- perms->audit = dfa_user_audit(dfa, state);
- perms->quiet = dfa_user_quiet(dfa, state);
-
- /* for v5 perm mapping in the policydb, the other set is used
- * to extend the general perm set
- */
- perms->allow |= map_other(dfa_other_allow(dfa, state));
- perms->audit |= map_other(dfa_other_audit(dfa, state));
- perms->quiet |= map_other(dfa_other_quiet(dfa, state));
-// perms->xindex = dfa_user_xindex(dfa, state);
-}
-
-/**
- * aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
- * @accum - perms struct to accumulate into
- * @addend - perms struct to add to @accum
- */
-void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend)
-{
- accum->deny |= addend->deny;
- accum->allow &= addend->allow & ~addend->deny;
- accum->audit |= addend->audit & addend->allow;
- accum->quiet &= addend->quiet & ~addend->allow;
- accum->kill |= addend->kill & ~addend->allow;
- accum->stop |= addend->stop & ~addend->allow;
- accum->complain |= addend->complain & ~addend->allow & ~addend->deny;
- accum->cond |= addend->cond & ~addend->allow & ~addend->deny;
- accum->hide &= addend->hide & ~addend->allow;
- accum->prompt |= addend->prompt & ~addend->allow & ~addend->deny;
-}
-
-/**
- * aa_perms_accum - accumulate perms, masking off overlapping perms
- * @accum - perms struct to accumulate into
- * @addend - perms struct to add to @accum
- */
-void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend)
-{
- accum->deny |= addend->deny;
- accum->allow &= addend->allow & ~accum->deny;
- accum->audit |= addend->audit & accum->allow;
- accum->quiet &= addend->quiet & ~accum->allow;
- accum->kill |= addend->kill & ~accum->allow;
- accum->stop |= addend->stop & ~accum->allow;
- accum->complain |= addend->complain & ~accum->allow & ~accum->deny;
- accum->cond |= addend->cond & ~accum->allow & ~accum->deny;
- accum->hide &= addend->hide & ~accum->allow;
- accum->prompt |= addend->prompt & ~accum->allow & ~accum->deny;
-}
-
-void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label,
- int type, u32 request, struct aa_perms *perms)
-{
- /* TODO: doesn't yet handle extended types */
- unsigned int state;
-
- state = aa_dfa_next(profile->policy.dfa,
- profile->policy.start[AA_CLASS_LABEL],
- type);
- aa_label_match(profile, label, state, false, request, perms);
-}
-
-
-/* currently unused */
-int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
- u32 request, int type, u32 *deny,
- struct common_audit_data *sa)
-{
- struct aa_perms perms;
-
- aad(sa)->label = &profile->label;
- aad(sa)->peer = &target->label;
- aad(sa)->request = request;
-
- aa_profile_match_label(profile, &target->label, type, request, &perms);
- aa_apply_modes_to_perms(profile, &perms);
- *deny |= request & perms.deny;
- return aa_check_perms(profile, &perms, request, sa, aa_audit_perms_cb);
-}
-
-/**
- * aa_check_perms - do audit mode selection based on perms set
- * @profile: profile being checked
- * @perms: perms computed for the request
- * @request: requested perms
- * @deny: Returns: explicit deny set
- * @sa: initialized audit structure (MAY BE NULL if not auditing)
- * @cb: callback fn for tpye specific fields (MAY BE NULL)
- *
- * Returns: 0 if permission else error code
- *
- * Note: profile audit modes need to be set before calling by setting the
- * perm masks appropriately.
- *
- * If not auditing then complain mode is not enabled and the
- * error code will indicate whether there was an explicit deny
- * with a positive value.
- */
-int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
- u32 request, struct common_audit_data *sa,
- void (*cb)(struct audit_buffer *, void *))
-{
- int type, error;
- bool stop = false;
- u32 denied = request & (~perms->allow | perms->deny);
-
- if (likely(!denied)) {
- /* mask off perms that are not being force audited */
- request &= perms->audit;
- if (!request || !sa)
- return 0;
-
- type = AUDIT_APPARMOR_AUDIT;
- error = 0;
- } else {
- error = -EACCES;
-
- if (denied & perms->kill)
- type = AUDIT_APPARMOR_KILL;
- else if (denied == (denied & perms->complain))
- type = AUDIT_APPARMOR_ALLOWED;
- else
- type = AUDIT_APPARMOR_DENIED;
-
- if (denied & perms->stop)
- stop = true;
- if (denied == (denied & perms->hide))
- error = -ENOENT;
-
- denied &= ~perms->quiet;
- if (!sa || !denied)
- return error;
- }
-
- if (sa) {
- aad(sa)->label = &profile->label;
- aad(sa)->request = request;
- aad(sa)->denied = denied;
- aad(sa)->error = error;
- aa_audit_msg(type, sa, cb);
- }
-
- if (type == AUDIT_APPARMOR_ALLOWED)
- error = 0;
-
- return error;
-}
-
-
/**
* aa_policy_init - initialize a policy structure
* @policy: policy to initialize (NOT NULL)
* @prefix: prefix name if any is required. (MAYBE NULL)
* @name: name of the policy, init will make a copy of it (NOT NULL)
- * @gfp: allocation mode
*
* Note: this fn creates a copy of strings passed in
*
@@ -486,21 +141,16 @@ int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
bool aa_policy_init(struct aa_policy *policy, const char *prefix,
const char *name, gfp_t gfp)
{
- char *hname;
-
/* freed by policy_free */
if (prefix) {
- hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
- if (hname)
- sprintf(hname, "%s//%s", prefix, name);
- } else {
- hname = aa_str_alloc(strlen(name) + 1, gfp);
- if (hname)
- strcpy(hname, name);
- }
- if (!hname)
+ policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3,
+ gfp);
+ if (policy->hname)
+ sprintf((char *)policy->hname, "%s//%s", prefix, name);
+ } else
+ policy->hname = kstrdup(name, gfp);
+ if (!policy->hname)
return false;
- policy->hname = hname;
/* base.name is a substring of fqname */
policy->name = basename(policy->hname);
INIT_LIST_HEAD(&policy->list);
@@ -519,5 +169,5 @@ void aa_policy_destroy(struct aa_policy *policy)
AA_BUG(on_list_rcu(&policy->list));
/* don't free name as its a subset of hname */
- aa_put_str(policy->hname);
+ kzfree(policy->hname);
}
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 7ac9df64e7a3..758ddf4a0791 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -35,11 +35,9 @@
#include "include/ipc.h"
#include "include/net.h"
#include "include/path.h"
-#include "include/label.h"
#include "include/policy.h"
#include "include/policy_ns.h"
#include "include/procattr.h"
-#include "include/mount.h"
/* Flag indicating whether initialization completed */
int apparmor_initialized;
@@ -52,7 +50,7 @@ DEFINE_PER_CPU(struct aa_buffers, aa_buffers);
*/
/*
- * free the associated aa_task_ctx and put its labels
+ * free the associated aa_task_ctx and put its profiles
*/
static void apparmor_cred_free(struct cred *cred)
{
@@ -106,63 +104,34 @@ static void apparmor_cred_transfer(struct cred *new, const struct cred *old)
static int apparmor_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
- struct aa_label *tracer, *tracee;
- int error;
-
- tracer = begin_current_label_crit_section();
- tracee = aa_get_task_label(child);
- error = aa_may_ptrace(tracer, tracee,
- mode == PTRACE_MODE_READ ? AA_PTRACE_READ : AA_PTRACE_TRACE);
- aa_put_label(tracee);
- end_current_label_crit_section(tracer);
-
- return error;
+ return aa_ptrace(current, child, mode);
}
static int apparmor_ptrace_traceme(struct task_struct *parent)
{
- struct aa_label *tracer, *tracee;
- int error;
-
- tracee = begin_current_label_crit_section();
- tracer = aa_get_task_label(parent);
- error = aa_may_ptrace(tracer, tracee, AA_PTRACE_TRACE);
- aa_put_label(tracer);
- end_current_label_crit_section(tracee);
-
- return error;
+ return aa_ptrace(parent, current, PTRACE_MODE_ATTACH);
}
/* Derived from security/commoncap.c:cap_capget */
static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
- struct aa_label *label;
+ struct aa_profile *profile;
const struct cred *cred;
rcu_read_lock();
cred = __task_cred(target);
- label = aa_get_newest_cred_label(cred);
+ profile = aa_cred_profile(cred);
/*
* cap_capget is stacked ahead of this and will
* initialize effective and permitted.
*/
- if (!unconfined(label)) {
- struct aa_profile *profile;
- struct label_it i;
-
- label_for_each_confined(i, label, profile) {
- if (COMPLAIN_MODE(profile))
- continue;
- *effective = cap_intersect(*effective,
- profile->caps.allow);
- *permitted = cap_intersect(*permitted,
- profile->caps.allow);
- }
+ if (!unconfined(profile) && !COMPLAIN_MODE(profile)) {
+ *effective = cap_intersect(*effective, profile->caps.allow);
+ *permitted = cap_intersect(*permitted, profile->caps.allow);
}
rcu_read_unlock();
- aa_put_label(label);
return 0;
}
@@ -170,14 +139,12 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
int cap, int audit)
{
- struct aa_label *label;
+ struct aa_profile *profile;
int error = 0;
- label = aa_get_newest_cred_label(cred);
- if (!unconfined(label))
- error = aa_capable(label, cap, audit);
- aa_put_label(label);
-
+ profile = aa_cred_profile(cred);
+ if (!unconfined(profile))
+ error = aa_capable(profile, cap, audit);
return error;
}
@@ -193,13 +160,12 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
static int common_perm(const char *op, const struct path *path, u32 mask,
struct path_cond *cond)
{
- struct aa_label *label;
+ struct aa_profile *profile;
int error = 0;
- label = __begin_current_label_crit_section();
- if (!unconfined(label))
- error = aa_path_perm(op, label, path, 0, mask, cond);
- __end_current_label_crit_section(label);
+ profile = __aa_current_profile();
+ if (!unconfined(profile))
+ error = aa_path_perm(op, profile, path, 0, mask, cond);
return error;
}
@@ -313,7 +279,7 @@ static int apparmor_path_mknod(const struct path *dir, struct dentry *dentry,
static int apparmor_path_truncate(const struct path *path)
{
- return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_SETATTR);
+ return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE);
}
static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry,
@@ -326,31 +292,29 @@ static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry,
static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_dir,
struct dentry *new_dentry)
{
- struct aa_label *label;
+ struct aa_profile *profile;
int error = 0;
if (!path_mediated_fs(old_dentry))
return 0;
- label = begin_current_label_crit_section();
- if (!unconfined(label))
- error = aa_path_link(label, old_dentry, new_dir, new_dentry);
- end_current_label_crit_section(label);
-
+ profile = aa_current_profile();
+ if (!unconfined(profile))
+ error = aa_path_link(profile, old_dentry, new_dir, new_dentry);
return error;
}
static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry)
{
- struct aa_label *label;
+ struct aa_profile *profile;
int error = 0;
if (!path_mediated_fs(old_dentry))
return 0;
- label = begin_current_label_crit_section();
- if (!unconfined(label)) {
+ profile = aa_current_profile();
+ if (!unconfined(profile)) {
struct path old_path = { .mnt = old_dir->mnt,
.dentry = old_dentry };
struct path new_path = { .mnt = new_dir->mnt,
@@ -359,18 +323,16 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
d_backing_inode(old_dentry)->i_mode
};
- error = aa_path_perm(OP_RENAME_SRC, label, &old_path, 0,
- MAY_READ | AA_MAY_GETATTR | MAY_WRITE |
- AA_MAY_SETATTR | AA_MAY_DELETE,
+ error = aa_path_perm(OP_RENAME_SRC, profile, &old_path, 0,
+ MAY_READ | AA_MAY_META_READ | MAY_WRITE |
+ AA_MAY_META_WRITE | AA_MAY_DELETE,
&cond);
if (!error)
- error = aa_path_perm(OP_RENAME_DEST, label, &new_path,
- 0, MAY_WRITE | AA_MAY_SETATTR |
+ error = aa_path_perm(OP_RENAME_DEST, profile, &new_path,
+ 0, MAY_WRITE | AA_MAY_META_WRITE |
AA_MAY_CREATE, &cond);
}
- end_current_label_crit_section(label);
-
return error;
}
@@ -386,13 +348,13 @@ static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
static int apparmor_inode_getattr(const struct path *path)
{
- return common_perm_cond(OP_GETATTR, path, AA_MAY_GETATTR);
+ return common_perm_cond(OP_GETATTR, path, AA_MAY_META_READ);
}
static int apparmor_file_open(struct file *file, const struct cred *cred)
{
- struct aa_file_ctx *fctx = file_ctx(file);
- struct aa_label *label;
+ struct aa_file_ctx *fctx = file->f_security;
+ struct aa_profile *profile;
int error = 0;
if (!path_mediated_fs(file->f_path.dentry))
@@ -408,59 +370,63 @@ static int apparmor_file_open(struct file *file, const struct cred *cred)
return 0;
}
- label = aa_get_newest_cred_label(cred);
- if (!unconfined(label)) {
+ profile = aa_cred_profile(cred);
+ if (!unconfined(profile)) {
struct inode *inode = file_inode(file);
struct path_cond cond = { inode->i_uid, inode->i_mode };
- error = aa_path_perm(OP_OPEN, label, &file->f_path, 0,
+ error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0,
aa_map_file_to_perms(file), &cond);
/* todo cache full allowed permissions set and state */
fctx->allow = aa_map_file_to_perms(file);
}
- aa_put_label(label);
return error;
}
static int apparmor_file_alloc_security(struct file *file)
{
- int error = 0;
-
/* freed by apparmor_file_free_security */
- struct aa_label *label = begin_current_label_crit_section();
- file->f_security = aa_alloc_file_ctx(label, GFP_KERNEL);
- if (!file_ctx(file))
- error = -ENOMEM;
- end_current_label_crit_section(label);
+ file->f_security = aa_alloc_file_context(GFP_KERNEL);
+ if (!file->f_security)
+ return -ENOMEM;
+ return 0;
- return error;
}
static void apparmor_file_free_security(struct file *file)
{
- aa_free_file_ctx(file_ctx(file));
+ struct aa_file_ctx *ctx = file->f_security;
+
+ aa_free_file_context(ctx);
}
static int common_file_perm(const char *op, struct file *file, u32 mask)
{
- struct aa_label *label;
+ struct aa_file_ctx *fctx = file->f_security;
+ struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred);
int error = 0;
- /* don't reaudit files closed during inheritance */
- if (file->f_path.dentry == aa_null.dentry)
- return -EACCES;
+ AA_BUG(!fprofile);
- label = __begin_current_label_crit_section();
- error = aa_file_perm(op, label, file, mask);
- __end_current_label_crit_section(label);
+ if (!file->f_path.mnt ||
+ !path_mediated_fs(file->f_path.dentry))
+ return 0;
- return error;
-}
+ profile = __aa_current_profile();
-static int apparmor_file_receive(struct file *file)
-{
- return common_file_perm(OP_FRECEIVE, file, aa_map_file_to_perms(file));
+ /* revalidate access, if task is unconfined, or the cached cred
+ * doesn't match or if the request is for more permissions than
+ * was granted.
+ *
+ * Note: the test for !unconfined(fprofile) is to handle file
+ * delegation from unconfined tasks
+ */
+ if (!unconfined(profile) && !unconfined(fprofile) &&
+ ((fprofile != profile) || (mask & ~fctx->allow)))
+ error = aa_file_perm(op, profile, file, mask);
+
+ return error;
}
static int apparmor_file_permission(struct file *file, int mask)
@@ -483,7 +449,7 @@ static int common_mmap(const char *op, struct file *file, unsigned long prot,
{
int mask = 0;
- if (!file || !file_ctx(file))
+ if (!file || !file->f_security)
return 0;
if (prot & PROT_READ)
@@ -513,65 +479,6 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma,
!(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
}
-static int apparmor_sb_mount(const char *dev_name, const struct path *path,
- const char *type, unsigned long flags, void *data)
-{
- struct aa_label *label;
- int error = 0;
-
- /* Discard magic */
- if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
- flags &= ~MS_MGC_MSK;
-
- flags &= ~AA_MS_IGNORE_MASK;
-
- label = __begin_current_label_crit_section();
- if (!unconfined(label)) {
- if (flags & MS_REMOUNT)
- error = aa_remount(label, path, flags, data);
- else if (flags & MS_BIND)
- error = aa_bind_mount(label, path, dev_name, flags);
- else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE |
- MS_UNBINDABLE))
- error = aa_mount_change_type(label, path, flags);
- else if (flags & MS_MOVE)
- error = aa_move_mount(label, path, dev_name);
- else
- error = aa_new_mount(label, dev_name, path, type,
- flags, data);
- }
- __end_current_label_crit_section(label);
-
- return error;
-}
-
-static int apparmor_sb_umount(struct vfsmount *mnt, int flags)
-{
- struct aa_label *label;
- int error = 0;
-
- label = __begin_current_label_crit_section();
- if (!unconfined(label))
- error = aa_umount(label, mnt, flags);
- __end_current_label_crit_section(label);
-
- return error;
-}
-
-static int apparmor_sb_pivotroot(const struct path *old_path,
- const struct path *new_path)
-{
- struct aa_label *label;
- int error = 0;
-
- label = aa_get_current_label();
- if (!unconfined(label))
- error = aa_pivotroot(label, old_path, new_path);
- aa_put_label(label);
-
- return error;
-}
-
static int apparmor_getprocattr(struct task_struct *task, char *name,
char **value)
{
@@ -579,21 +486,21 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
/* released below */
const struct cred *cred = get_task_cred(task);
struct aa_task_ctx *ctx = cred_ctx(cred);
- struct aa_label *label = NULL;
+ struct aa_profile *profile = NULL;
if (strcmp(name, "current") == 0)
- label = aa_get_newest_label(ctx->label);
+ profile = aa_get_newest_profile(ctx->profile);
else if (strcmp(name, "prev") == 0 && ctx->previous)
- label = aa_get_newest_label(ctx->previous);
+ profile = aa_get_newest_profile(ctx->previous);
else if (strcmp(name, "exec") == 0 && ctx->onexec)
- label = aa_get_newest_label(ctx->onexec);
+ profile = aa_get_newest_profile(ctx->onexec);
else
error = -EINVAL;
- if (label)
- error = aa_getprocattr(label, value);
+ if (profile)
+ error = aa_getprocattr(profile, value);
- aa_put_label(label);
+ aa_put_profile(profile);
put_cred(cred);
return error;
@@ -633,24 +540,22 @@ static int apparmor_setprocattr(const char *name, void *value,
if (strcmp(name, "current") == 0) {
if (strcmp(command, "changehat") == 0) {
error = aa_setprocattr_changehat(args, arg_size,
- AA_CHANGE_NOFLAGS);
+ !AA_DO_TEST);
} else if (strcmp(command, "permhat") == 0) {
error = aa_setprocattr_changehat(args, arg_size,
- AA_CHANGE_TEST);
+ AA_DO_TEST);
} else if (strcmp(command, "changeprofile") == 0) {
- error = aa_change_profile(args, AA_CHANGE_NOFLAGS);
+ error = aa_change_profile(args, !AA_ONEXEC,
+ !AA_DO_TEST, false);
} else if (strcmp(command, "permprofile") == 0) {
- error = aa_change_profile(args, AA_CHANGE_TEST);
- } else if (strcmp(command, "stack") == 0) {
- error = aa_change_profile(args, AA_CHANGE_STACK);
+ error = aa_change_profile(args, !AA_ONEXEC, AA_DO_TEST,
+ false);
} else
goto fail;
} else if (strcmp(name, "exec") == 0) {
if (strcmp(command, "exec") == 0)
- error = aa_change_profile(args, AA_CHANGE_ONEXEC);
- else if (strcmp(command, "stack") == 0)
- error = aa_change_profile(args, (AA_CHANGE_ONEXEC |
- AA_CHANGE_STACK));
+ error = aa_change_profile(args, AA_ONEXEC, !AA_DO_TEST,
+ false);
else
goto fail;
} else
@@ -664,92 +569,37 @@ out:
return error;
fail:
- aad(&sa)->label = begin_current_label_crit_section();
+ aad(&sa)->profile = aa_current_profile();
aad(&sa)->info = name;
aad(&sa)->error = error = -EINVAL;
aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL);
- end_current_label_crit_section(aad(&sa)->label);
goto out;
}
-/**
- * apparmor_bprm_committing_creds - do task cleanup on committing new creds
- * @bprm: binprm for the exec (NOT NULL)
- */
-static void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
-{
- struct aa_label *label = aa_current_raw_label();
- struct aa_task_ctx *new_ctx = cred_ctx(bprm->cred);
-
- /* bail out if unconfined or not changing profile */
- if ((new_ctx->label->proxy == label->proxy) ||
- (unconfined(new_ctx->label)))
- return;
-
- aa_inherit_files(bprm->cred, current->files);
-
- current->pdeath_signal = 0;
-
- /* reset soft limits and set hard limits for the new label */
- __aa_transition_rlimits(label, new_ctx->label);
-}
-
-/**
- * apparmor_bprm_committed_cred - do cleanup after new creds committed
- * @bprm: binprm for the exec (NOT NULL)
- */
-static void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
-{
- /* TODO: cleanup signals - ipc mediation */
- return;
-}
-
static int apparmor_task_setrlimit(struct task_struct *task,
unsigned int resource, struct rlimit *new_rlim)
{
- struct aa_label *label = __begin_current_label_crit_section();
+ struct aa_profile *profile = __aa_current_profile();
int error = 0;
- if (!unconfined(label))
- error = aa_task_setrlimit(label, task, resource, new_rlim);
- __end_current_label_crit_section(label);
-
- return error;
-}
-
-static int apparmor_task_kill(struct task_struct *target, struct siginfo *info,
- int sig, u32 secid)
-{
- struct aa_label *cl, *tl;
- int error;
-
- if (secid)
- /* TODO: after secid to label mapping is done.
- * Dealing with USB IO specific behavior
- */
- return 0;
- cl = __begin_current_label_crit_section();
- tl = aa_get_task_label(target);
- error = aa_may_signal(cl, tl, sig);
- aa_put_label(tl);
- __end_current_label_crit_section(cl);
+ if (!unconfined(profile))
+ error = aa_task_setrlimit(profile, task, resource, new_rlim);
return error;
}
static int apparmor_socket_create(int family, int type, int protocol, int kern)
{
- struct aa_label *label;
+ struct aa_profile *profile;
int error = 0;
if (kern)
return 0;
- label = begin_current_label_crit_section();
- if (!unconfined(label))
- error = aa_label_net_perm(label, OP_CREATE,
- family, type, protocol, NULL);
- end_current_label_crit_section(label);
+ profile = __aa_current_profile();
+ if (!unconfined(profile))
+ error = aa_net_perm(OP_CREATE, profile, family, type, protocol,
+ NULL);
return error;
}
@@ -842,10 +692,6 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(capget, apparmor_capget),
LSM_HOOK_INIT(capable, apparmor_capable),
- LSM_HOOK_INIT(sb_mount, apparmor_sb_mount),
- LSM_HOOK_INIT(sb_umount, apparmor_sb_umount),
- LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot),
-
LSM_HOOK_INIT(path_link, apparmor_path_link),
LSM_HOOK_INIT(path_unlink, apparmor_path_unlink),
LSM_HOOK_INIT(path_symlink, apparmor_path_symlink),
@@ -859,7 +705,6 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(inode_getattr, apparmor_inode_getattr),
LSM_HOOK_INIT(file_open, apparmor_file_open),
- LSM_HOOK_INIT(file_receive, apparmor_file_receive),
LSM_HOOK_INIT(file_permission, apparmor_file_permission),
LSM_HOOK_INIT(file_alloc_security, apparmor_file_alloc_security),
LSM_HOOK_INIT(file_free_security, apparmor_file_free_security),
@@ -894,7 +739,6 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(bprm_secureexec, apparmor_bprm_secureexec),
LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
- LSM_HOOK_INIT(task_kill, apparmor_task_kill),
};
/*
@@ -1042,18 +886,11 @@ static int param_get_aabool(char *buffer, const struct kernel_param *kp)
static int param_set_aauint(const char *val, const struct kernel_param *kp)
{
- int error;
-
if (!apparmor_enabled)
return -EINVAL;
- /* file is ro but enforce 2nd line check */
- if (apparmor_initialized)
+ if (apparmor_initialized && !policy_admin_capable(NULL))
return -EPERM;
-
- error = param_set_uint(val, kp);
- pr_info("AppArmor: buffer size set to %d bytes\n", aa_g_path_max);
-
- return error;
+ return param_set_uint(val, kp);
}
static int param_get_aauint(char *buffer, const struct kernel_param *kp)
@@ -1144,7 +981,7 @@ static int __init set_init_ctx(void)
if (!ctx)
return -ENOMEM;
- ctx->label = aa_get_label(ns_unconfined(root_ns));
+ ctx->profile = aa_get_profile(root_ns->unconfined);
cred_ctx(cred) = ctx;
return 0;
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index 72c604350e80..960c913381e2 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -226,7 +226,7 @@ void aa_dfa_free_kref(struct kref *kref)
* @flags: flags controlling what type of accept tables are acceptable
*
* Unpack a dfa that has been serialized. To find information on the dfa
- * format look in Documentation/admin-guide/LSM/apparmor.rst
+ * format look in Documentation/security/apparmor.txt
* Assumes the dfa @blob stream has been aligned on a 8 byte boundary
*
* Returns: an unpacked dfa ready for matching or ERR_PTR on failure
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
deleted file mode 100644
index e395137ecff1..000000000000
--- a/security/apparmor/mount.c
+++ /dev/null
@@ -1,706 +0,0 @@
-/*
- * AppArmor security module
- *
- * This file contains AppArmor mediation of files
- *
- * Copyright (C) 1998-2008 Novell/SUSE
- * Copyright 2009-2017 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
- */
-
-#include <linux/fs.h>
-#include <linux/mount.h>
-#include <linux/namei.h>
-
-#include "include/apparmor.h"
-#include "include/audit.h"
-#include "include/context.h"
-#include "include/domain.h"
-#include "include/file.h"
-#include "include/match.h"
-#include "include/mount.h"
-#include "include/path.h"
-#include "include/policy.h"
-
-
-static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
-{
- if (flags & MS_RDONLY)
- audit_log_format(ab, "ro");
- else
- audit_log_format(ab, "rw");
- if (flags & MS_NOSUID)
- audit_log_format(ab, ", nosuid");
- if (flags & MS_NODEV)
- audit_log_format(ab, ", nodev");
- if (flags & MS_NOEXEC)
- audit_log_format(ab, ", noexec");
- if (flags & MS_SYNCHRONOUS)
- audit_log_format(ab, ", sync");
- if (flags & MS_REMOUNT)
- audit_log_format(ab, ", remount");
- if (flags & MS_MANDLOCK)
- audit_log_format(ab, ", mand");
- if (flags & MS_DIRSYNC)
- audit_log_format(ab, ", dirsync");
- if (flags & MS_NOATIME)
- audit_log_format(ab, ", noatime");
- if (flags & MS_NODIRATIME)
- audit_log_format(ab, ", nodiratime");
- if (flags & MS_BIND)
- audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind");
- if (flags & MS_MOVE)
- audit_log_format(ab, ", move");
- if (flags & MS_SILENT)
- audit_log_format(ab, ", silent");
- if (flags & MS_POSIXACL)
- audit_log_format(ab, ", acl");
- if (flags & MS_UNBINDABLE)
- audit_log_format(ab, flags & MS_REC ? ", runbindable" :
- ", unbindable");
- if (flags & MS_PRIVATE)
- audit_log_format(ab, flags & MS_REC ? ", rprivate" :
- ", private");
- if (flags & MS_SLAVE)
- audit_log_format(ab, flags & MS_REC ? ", rslave" :
- ", slave");
- if (flags & MS_SHARED)
- audit_log_format(ab, flags & MS_REC ? ", rshared" :
- ", shared");
- if (flags & MS_RELATIME)
- audit_log_format(ab, ", relatime");
- if (flags & MS_I_VERSION)
- audit_log_format(ab, ", iversion");
- if (flags & MS_STRICTATIME)
- audit_log_format(ab, ", strictatime");
- if (flags & MS_NOUSER)
- audit_log_format(ab, ", nouser");
-}
-
-/**
- * audit_cb - call back for mount specific audit fields
- * @ab: audit_buffer (NOT NULL)
- * @va: audit struct to audit values of (NOT NULL)
- */
-static void audit_cb(struct audit_buffer *ab, void *va)
-{
- struct common_audit_data *sa = va;
-
- if (aad(sa)->mnt.type) {
- audit_log_format(ab, " fstype=");
- audit_log_untrustedstring(ab, aad(sa)->mnt.type);
- }
- if (aad(sa)->mnt.src_name) {
- audit_log_format(ab, " srcname=");
- audit_log_untrustedstring(ab, aad(sa)->mnt.src_name);
- }
- if (aad(sa)->mnt.trans) {
- audit_log_format(ab, " trans=");
- audit_log_untrustedstring(ab, aad(sa)->mnt.trans);
- }
- if (aad(sa)->mnt.flags) {
- audit_log_format(ab, " flags=\"");
- audit_mnt_flags(ab, aad(sa)->mnt.flags);
- audit_log_format(ab, "\"");
- }
- if (aad(sa)->mnt.data) {
- audit_log_format(ab, " options=");
- audit_log_untrustedstring(ab, aad(sa)->mnt.data);
- }
-}
-
-/**
- * audit_mount - handle the auditing of mount operations
- * @profile: the profile being enforced (NOT NULL)
- * @op: operation being mediated (NOT NULL)
- * @name: name of object being mediated (MAYBE NULL)
- * @src_name: src_name of object being mediated (MAYBE_NULL)
- * @type: type of filesystem (MAYBE_NULL)
- * @trans: name of trans (MAYBE NULL)
- * @flags: filesystem idependent mount flags
- * @data: filesystem mount flags
- * @request: permissions requested
- * @perms: the permissions computed for the request (NOT NULL)
- * @info: extra information message (MAYBE NULL)
- * @error: 0 if operation allowed else failure error code
- *
- * Returns: %0 or error on failure
- */
-static int audit_mount(struct aa_profile *profile, const char *op,
- const char *name, const char *src_name,
- const char *type, const char *trans,
- unsigned long flags, const void *data, u32 request,
- struct aa_perms *perms, const char *info, int error)
-{
- int audit_type = AUDIT_APPARMOR_AUTO;
- DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
-
- if (likely(!error)) {
- u32 mask = perms->audit;
-
- if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
- mask = 0xffff;
-
- /* mask off perms that are not being force audited */
- request &= mask;
-
- if (likely(!request))
- return 0;
- audit_type = AUDIT_APPARMOR_AUDIT;
- } else {
- /* only report permissions that were denied */
- request = request & ~perms->allow;
-
- if (request & perms->kill)
- audit_type = AUDIT_APPARMOR_KILL;
-
- /* quiet known rejects, assumes quiet and kill do not overlap */
- if ((request & perms->quiet) &&
- AUDIT_MODE(profile) != AUDIT_NOQUIET &&
- AUDIT_MODE(profile) != AUDIT_ALL)
- request &= ~perms->quiet;
-
- if (!request)
- return error;
- }
-
- aad(&sa)->name = name;
- aad(&sa)->mnt.src_name = src_name;
- aad(&sa)->mnt.type = type;
- aad(&sa)->mnt.trans = trans;
- aad(&sa)->mnt.flags = flags;
- if (data && (perms->audit & AA_AUDIT_DATA))
- aad(&sa)->mnt.data = data;
- aad(&sa)->info = info;
- aad(&sa)->error = error;
-
- return aa_audit(audit_type, profile, &sa, audit_cb);
-}
-
-/**
- * match_mnt_flags - Do an ordered match on mount flags
- * @dfa: dfa to match against
- * @state: state to start in
- * @flags: mount flags to match against
- *
- * Mount flags are encoded as an ordered match. This is done instead of
- * checking against a simple bitmask, to allow for logical operations
- * on the flags.
- *
- * Returns: next state after flags match
- */
-static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state,
- unsigned long flags)
-{
- unsigned int i;
-
- for (i = 0; i <= 31 ; ++i) {
- if ((1 << i) & flags)
- state = aa_dfa_next(dfa, state, i + 1);
- }
-
- return state;
-}
-
-/**
- * compute_mnt_perms - compute mount permission associated with @state
- * @dfa: dfa to match against (NOT NULL)
- * @state: state match finished in
- *
- * Returns: mount permissions
- */
-static struct aa_perms compute_mnt_perms(struct aa_dfa *dfa,
- unsigned int state)
-{
- struct aa_perms perms;
-
- perms.kill = 0;
- perms.allow = dfa_user_allow(dfa, state);
- perms.audit = dfa_user_audit(dfa, state);
- perms.quiet = dfa_user_quiet(dfa, state);
- perms.xindex = dfa_user_xindex(dfa, state);
-
- return perms;
-}
-
-static const char * const mnt_info_table[] = {
- "match succeeded",
- "failed mntpnt match",
- "failed srcname match",
- "failed type match",
- "failed flags match",
- "failed data match"
-};
-
-/*
- * Returns 0 on success else element that match failed in, this is the
- * index into the mnt_info_table above
- */
-static int do_match_mnt(struct aa_dfa *dfa, unsigned int start,
- const char *mntpnt, const char *devname,
- const char *type, unsigned long flags,
- void *data, bool binary, struct aa_perms *perms)
-{
- unsigned int state;
-
- AA_BUG(!dfa);
- AA_BUG(!perms);
-
- state = aa_dfa_match(dfa, start, mntpnt);
- state = aa_dfa_null_transition(dfa, state);
- if (!state)
- return 1;
-
- if (devname)
- state = aa_dfa_match(dfa, state, devname);
- state = aa_dfa_null_transition(dfa, state);
- if (!state)
- return 2;
-
- if (type)
- state = aa_dfa_match(dfa, state, type);
- state = aa_dfa_null_transition(dfa, state);
- if (!state)
- return 3;
-
- state = match_mnt_flags(dfa, state, flags);
- if (!state)
- return 4;
- *perms = compute_mnt_perms(dfa, state);
- if (perms->allow & AA_MAY_MOUNT)
- return 0;
-
- /* only match data if not binary and the DFA flags data is expected */
- if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) {
- state = aa_dfa_null_transition(dfa, state);
- if (!state)
- return 4;
-
- state = aa_dfa_match(dfa, state, data);
- if (!state)
- return 5;
- *perms = compute_mnt_perms(dfa, state);
- if (perms->allow & AA_MAY_MOUNT)
- return 0;
- }
-
- /* failed at end of flags match */
- return 4;
-}
-
-
-static int path_flags(struct aa_profile *profile, const struct path *path)
-{
- AA_BUG(!profile);
- AA_BUG(!path);
-
- return profile->path_flags |
- (S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0);
-}
-
-/**
- * match_mnt_path_str - handle path matching for mount
- * @profile: the confining profile
- * @mntpath: for the mntpnt (NOT NULL)
- * @buffer: buffer to be used to lookup mntpath
- * @devnme: string for the devname/src_name (MAY BE NULL OR ERRPTR)
- * @type: string for the dev type (MAYBE NULL)
- * @flags: mount flags to match
- * @data: fs mount data (MAYBE NULL)
- * @binary: whether @data is binary
- * @devinfo: error str if (IS_ERR(@devname))
- *
- * Returns: 0 on success else error
- */
-static int match_mnt_path_str(struct aa_profile *profile,
- const struct path *mntpath, char *buffer,
- const char *devname, const char *type,
- unsigned long flags, void *data, bool binary,
- const char *devinfo)
-{
- struct aa_perms perms = { };
- const char *mntpnt = NULL, *info = NULL;
- int pos, error;
-
- AA_BUG(!profile);
- AA_BUG(!mntpath);
- AA_BUG(!buffer);
-
- if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
- return 0;
-
- error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer,
- &mntpnt, &info, profile->disconnected);
- if (error)
- goto audit;
- if (IS_ERR(devname)) {
- error = PTR_ERR(devname);
- devname = NULL;
- info = devinfo;
- goto audit;
- }
-
- error = -EACCES;
- pos = do_match_mnt(profile->policy.dfa,
- profile->policy.start[AA_CLASS_MOUNT],
- mntpnt, devname, type, flags, data, binary, &perms);
- if (pos) {
- info = mnt_info_table[pos];
- goto audit;
- }
- error = 0;
-
-audit:
- return audit_mount(profile, OP_MOUNT, mntpnt, devname, type, NULL,
- flags, data, AA_MAY_MOUNT, &perms, info, error);
-}
-
-/**
- * match_mnt - handle path matching for mount
- * @profile: the confining profile
- * @mntpath: for the mntpnt (NOT NULL)
- * @buffer: buffer to be used to lookup mntpath
- * @devpath: path devname/src_name (MAYBE NULL)
- * @devbuffer: buffer to be used to lookup devname/src_name
- * @type: string for the dev type (MAYBE NULL)
- * @flags: mount flags to match
- * @data: fs mount data (MAYBE NULL)
- * @binary: whether @data is binary
- *
- * Returns: 0 on success else error
- */
-static int match_mnt(struct aa_profile *profile, const struct path *path,
- char *buffer, struct path *devpath, char *devbuffer,
- const char *type, unsigned long flags, void *data,
- bool binary)
-{
- const char *devname = NULL, *info = NULL;
- int error = -EACCES;
-
- AA_BUG(!profile);
- AA_BUG(devpath && !devbuffer);
-
- if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
- return 0;
-
- if (devpath) {
- error = aa_path_name(devpath, path_flags(profile, devpath),
- devbuffer, &devname, &info,
- profile->disconnected);
- if (error)
- devname = ERR_PTR(error);
- }
-
- return match_mnt_path_str(profile, path, buffer, devname, type, flags,
- data, binary, info);
-}
-
-int aa_remount(struct aa_label *label, const struct path *path,
- unsigned long flags, void *data)
-{
- struct aa_profile *profile;
- char *buffer = NULL;
- bool binary;
- int error;
-
- AA_BUG(!label);
- AA_BUG(!path);
-
- binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA;
-
- get_buffers(buffer);
- error = fn_for_each_confined(label, profile,
- match_mnt(profile, path, buffer, NULL, NULL, NULL,
- flags, data, binary));
- put_buffers(buffer);
-
- return error;
-}
-
-int aa_bind_mount(struct aa_label *label, const struct path *path,
- const char *dev_name, unsigned long flags)
-{
- struct aa_profile *profile;
- char *buffer = NULL, *old_buffer = NULL;
- struct path old_path;
- int error;
-
- AA_BUG(!label);
- AA_BUG(!path);
-
- if (!dev_name || !*dev_name)
- return -EINVAL;
-
- flags &= MS_REC | MS_BIND;
-
- error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
- if (error)
- return error;
-
- get_buffers(buffer, old_buffer);
- error = fn_for_each_confined(label, profile,
- match_mnt(profile, path, buffer, &old_path, old_buffer,
- NULL, flags, NULL, false));
- put_buffers(buffer, old_buffer);
- path_put(&old_path);
-
- return error;
-}
-
-int aa_mount_change_type(struct aa_label *label, const struct path *path,
- unsigned long flags)
-{
- struct aa_profile *profile;
- char *buffer = NULL;
- int error;
-
- AA_BUG(!label);
- AA_BUG(!path);
-
- /* These are the flags allowed by do_change_type() */
- flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
- MS_UNBINDABLE);
-
- get_buffers(buffer);
- error = fn_for_each_confined(label, profile,
- match_mnt(profile, path, buffer, NULL, NULL, NULL,
- flags, NULL, false));
- put_buffers(buffer);
-
- return error;
-}
-
-int aa_move_mount(struct aa_label *label, const struct path *path,
- const char *orig_name)
-{
- struct aa_profile *profile;
- char *buffer = NULL, *old_buffer = NULL;
- struct path old_path;
- int error;
-
- AA_BUG(!label);
- AA_BUG(!path);
-
- if (!orig_name || !*orig_name)
- return -EINVAL;
-
- error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path);
- if (error)
- return error;
-
- get_buffers(buffer, old_buffer);
- error = fn_for_each_confined(label, profile,
- match_mnt(profile, path, buffer, &old_path, old_buffer,
- NULL, MS_MOVE, NULL, false));
- put_buffers(buffer, old_buffer);
- path_put(&old_path);
-
- return error;
-}
-
-int aa_new_mount(struct aa_label *label, const char *dev_name,
- const struct path *path, const char *type, unsigned long flags,
- void *data)
-{
- struct aa_profile *profile;
- char *buffer = NULL, *dev_buffer = NULL;
- bool binary = true;
- int error;
- int requires_dev = 0;
- struct path tmp_path, *dev_path = NULL;
-
- AA_BUG(!label);
- AA_BUG(!path);
-
- if (type) {
- struct file_system_type *fstype;
-
- fstype = get_fs_type(type);
- if (!fstype)
- return -ENODEV;
- binary = fstype->fs_flags & FS_BINARY_MOUNTDATA;
- requires_dev = fstype->fs_flags & FS_REQUIRES_DEV;
- put_filesystem(fstype);
-
- if (requires_dev) {
- if (!dev_name || !*dev_name)
- return -ENOENT;
-
- error = kern_path(dev_name, LOOKUP_FOLLOW, &tmp_path);
- if (error)
- return error;
- dev_path = &tmp_path;
- }
- }
-
- get_buffers(buffer, dev_buffer);
- if (dev_path) {
- error = fn_for_each_confined(label, profile,
- match_mnt(profile, path, buffer, dev_path, dev_buffer,
- type, flags, data, binary));
- } else {
- error = fn_for_each_confined(label, profile,
- match_mnt_path_str(profile, path, buffer, dev_name,
- type, flags, data, binary, NULL));
- }
- put_buffers(buffer, dev_buffer);
- if (dev_path)
- path_put(dev_path);
-
- return error;
-}
-
-static int profile_umount(struct aa_profile *profile, struct path *path,
- char *buffer)
-{
- struct aa_perms perms = { };
- const char *name = NULL, *info = NULL;
- unsigned int state;
- int error;
-
- AA_BUG(!profile);
- AA_BUG(!path);
-
- if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
- return 0;
-
- error = aa_path_name(path, path_flags(profile, path), buffer, &name,
- &info, profile->disconnected);
- if (error)
- goto audit;
-
- state = aa_dfa_match(profile->policy.dfa,
- profile->policy.start[AA_CLASS_MOUNT],
- name);
- perms = compute_mnt_perms(profile->policy.dfa, state);
- if (AA_MAY_UMOUNT & ~perms.allow)
- error = -EACCES;
-
-audit:
- return audit_mount(profile, OP_UMOUNT, name, NULL, NULL, NULL, 0, NULL,
- AA_MAY_UMOUNT, &perms, info, error);
-}
-
-int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags)
-{
- struct aa_profile *profile;
- char *buffer = NULL;
- int error;
- struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
-
- AA_BUG(!label);
- AA_BUG(!mnt);
-
- get_buffers(buffer);
- error = fn_for_each_confined(label, profile,
- profile_umount(profile, &path, buffer));
- put_buffers(buffer);
-
- return error;
-}
-
-/* helper fn for transition on pivotroot
- *
- * Returns: label for transition or ERR_PTR. Does not return NULL
- */
-static struct aa_label *build_pivotroot(struct aa_profile *profile,
- const struct path *new_path,
- char *new_buffer,
- const struct path *old_path,
- char *old_buffer)
-{
- const char *old_name, *new_name = NULL, *info = NULL;
- const char *trans_name = NULL;
- struct aa_perms perms = { };
- unsigned int state;
- int error;
-
- AA_BUG(!profile);
- AA_BUG(!new_path);
- AA_BUG(!old_path);
-
- if (profile_unconfined(profile) ||
- !PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
- return aa_get_newest_label(&profile->label);
-
- error = aa_path_name(old_path, path_flags(profile, old_path),
- old_buffer, &old_name, &info,
- profile->disconnected);
- if (error)
- goto audit;
- error = aa_path_name(new_path, path_flags(profile, new_path),
- new_buffer, &new_name, &info,
- profile->disconnected);
- if (error)
- goto audit;
-
- error = -EACCES;
- state = aa_dfa_match(profile->policy.dfa,
- profile->policy.start[AA_CLASS_MOUNT],
- new_name);
- state = aa_dfa_null_transition(profile->policy.dfa, state);
- state = aa_dfa_match(profile->policy.dfa, state, old_name);
- perms = compute_mnt_perms(profile->policy.dfa, state);
-
- if (AA_MAY_PIVOTROOT & perms.allow)
- error = 0;
-
-audit:
- error = audit_mount(profile, OP_PIVOTROOT, new_name, old_name,
- NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT,
- &perms, info, error);
- if (error)
- return ERR_PTR(error);
-
- return aa_get_newest_label(&profile->label);
-}
-
-int aa_pivotroot(struct aa_label *label, const struct path *old_path,
- const struct path *new_path)
-{
- struct aa_profile *profile;
- struct aa_label *target = NULL;
- char *old_buffer = NULL, *new_buffer = NULL, *info = NULL;
- int error;
-
- AA_BUG(!label);
- AA_BUG(!old_path);
- AA_BUG(!new_path);
-
- get_buffers(old_buffer, new_buffer);
- target = fn_label_build(label, profile, GFP_ATOMIC,
- build_pivotroot(profile, new_path, new_buffer,
- old_path, old_buffer));
- if (!target) {
- info = "label build failed";
- error = -ENOMEM;
- goto fail;
- } else if (!IS_ERR(target)) {
- error = aa_replace_current_label(target);
- if (error) {
- /* TODO: audit target */
- aa_put_label(target);
- goto out;
- }
- } else
- /* already audited error */
- error = PTR_ERR(target);
-out:
- put_buffers(old_buffer, new_buffer);
-
- return error;
-
-fail:
- /* TODO: add back in auditing of new_name and old_name */
- error = fn_for_each(label, profile,
- audit_mount(profile, OP_PIVOTROOT, NULL /*new_name */,
- NULL /* old_name */,
- NULL, NULL,
- 0, NULL, AA_MAY_PIVOTROOT, &nullperms, info,
- error));
- goto out;
-}
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
index 95d6e3b9c71d..a3161ec4f82f 100644
--- a/security/apparmor/net.c
+++ b/security/apparmor/net.c
@@ -15,14 +15,13 @@
#include "include/apparmor.h"
#include "include/audit.h"
#include "include/context.h"
-#include "include/label.h"
#include "include/net.h"
#include "include/policy.h"
#include "net_names.h"
-struct aa_sfs_entry aa_sfs_entry_network[] = {
- AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK),
+struct aa_fs_entry aa_fs_entry_network[] = {
+ AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK),
{ }
};
@@ -72,6 +71,7 @@ static int audit_net(struct aa_profile *profile, const char *op,
sa.type = LSM_AUDIT_DATA_NONE;
}
/* todo fill in socket addr info */
+
aad(&sa) = &aad;
sa.u.net = &net;
aad(&sa)->op = op,
@@ -90,7 +90,7 @@ static int audit_net(struct aa_profile *profile, const char *op,
} else {
u16 quiet_mask = profile->net.quiet[sa.u.net->family];
u16 kill_mask = 0;
- u16 denied = (1 << aad(&sa)->net.type);
+ u16 denied = (1 << aad(&sa)->net.type) & ~quiet_mask;
if (denied & kill_mask)
audit_type = AUDIT_APPARMOR_KILL;
@@ -114,7 +114,7 @@ static int audit_net(struct aa_profile *profile, const char *op,
*
* Returns: %0 else error if permission denied
*/
-static int aa_net_perm(const char *op, struct aa_profile *profile, u16 family,
+int aa_net_perm(const char *op, struct aa_profile *profile, u16 family,
int type, int protocol, struct sock *sk)
{
u16 family_mask;
@@ -137,18 +137,6 @@ static int aa_net_perm(const char *op, struct aa_profile *profile, u16 family,
return audit_net(profile, op, family, type, protocol, sk, error);
}
-int aa_label_net_perm(struct aa_label *label, const char *op, u16 family,
- int type, int protocol, struct sock *sk)
-{
- struct aa_profile *profile;
-
- if (!unconfined(label))
- return 0;
-
- return fn_for_each_confined(label, profile,
- aa_net_perm(op, profile, family, type, protocol, sk));
-}
-
/**
* aa_revalidate_sk - Revalidate access to a sock
* @op: operation being checked
@@ -158,7 +146,7 @@ int aa_label_net_perm(struct aa_label *label, const char *op, u16 family,
*/
int aa_revalidate_sk(const char *op, struct sock *sk)
{
- struct aa_label *label;
+ struct aa_profile *profile;
int error = 0;
/* aa_revalidate_sk should not be called from interrupt context
@@ -167,10 +155,10 @@ int aa_revalidate_sk(const char *op, struct sock *sk)
if (in_interrupt())
return 0;
- label = begin_current_label_crit_section();
- error = aa_label_net_perm(label, op, sk->sk_family, sk->sk_type,
- sk->sk_protocol, sk);
- end_current_label_crit_section(label);
+ profile = __aa_current_profile();
+ if (!unconfined(profile))
+ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type,
+ sk->sk_protocol, sk);
return error;
}
diff --git a/security/apparmor/path.c b/security/apparmor/path.c
index 9d5de1d05be4..a8fc7d08c144 100644
--- a/security/apparmor/path.c
+++ b/security/apparmor/path.c
@@ -50,7 +50,7 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen)
* namespace root.
*/
static int disconnect(const struct path *path, char *buf, char **name,
- int flags, const char *disconnected)
+ int flags)
{
int error = 0;
@@ -63,14 +63,9 @@ static int disconnect(const struct path *path, char *buf, char **name,
error = -EACCES;
if (**name == '/')
*name = *name + 1;
- } else {
- if (**name != '/')
- /* CONNECT_PATH with missing root */
- error = prepend(name, *name - buf, "/", 1);
- if (!error && disconnected)
- error = prepend(name, *name - buf, disconnected,
- strlen(disconnected));
- }
+ } else if (**name != '/')
+ /* CONNECT_PATH with missing root */
+ error = prepend(name, *name - buf, "/", 1);
return error;
}
@@ -79,9 +74,9 @@ static int disconnect(const struct path *path, char *buf, char **name,
* d_namespace_path - lookup a name associated with a given path
* @path: path to lookup (NOT NULL)
* @buf: buffer to store path to (NOT NULL)
+ * @buflen: length of @buf
* @name: Returns - pointer for start of path name with in @buf (NOT NULL)
* @flags: flags controlling path lookup
- * @disconnected: string to prefix to disconnected paths
*
* Handle path name lookup.
*
@@ -89,14 +84,12 @@ static int disconnect(const struct path *path, char *buf, char **name,
* When no error the path name is returned in @name which points to
* to a position in @buf
*/
-static int d_namespace_path(const struct path *path, char *buf, char **name,
- int flags, const char *disconnected)
+static int d_namespace_path(const struct path *path, char *buf, int buflen,
+ char **name, int flags)
{
char *res;
int error = 0;
int connected = 1;
- int isdir = (flags & PATH_IS_DIR) ? 1 : 0;
- int buflen = aa_g_path_max - isdir;
if (path->mnt->mnt_flags & MNT_INTERNAL) {
/* it's not mounted anywhere */
@@ -111,12 +104,10 @@ static int d_namespace_path(const struct path *path, char *buf, char **name,
/* TODO: convert over to using a per namespace
* control instead of hard coded /proc
*/
- error = prepend(name, *name - buf, "/proc", 5);
- goto out;
+ return prepend(name, *name - buf, "/proc", 5);
} else
- error = disconnect(path, buf, name, flags,
- disconnected);
- goto out;
+ return disconnect(path, buf, name, flags);
+ return 0;
}
/* resolve paths relative to chroot?*/
@@ -135,11 +126,8 @@ static int d_namespace_path(const struct path *path, char *buf, char **name,
* be returned.
*/
if (!res || IS_ERR(res)) {
- if (PTR_ERR(res) == -ENAMETOOLONG) {
- error = -ENAMETOOLONG;
- *name = buf;
- goto out;
- }
+ if (PTR_ERR(res) == -ENAMETOOLONG)
+ return -ENAMETOOLONG;
connected = 0;
res = dentry_path_raw(path->dentry, buf, buflen);
if (IS_ERR(res)) {
@@ -152,9 +140,6 @@ static int d_namespace_path(const struct path *path, char *buf, char **name,
*name = res;
- if (!connected)
- error = disconnect(path, buf, name, flags, disconnected);
-
/* Handle two cases:
* 1. A deleted dentry && profile is not allowing mediation of deleted
* 2. On some filesystems, newly allocated dentries appear to the
@@ -162,30 +147,62 @@ static int d_namespace_path(const struct path *path, char *buf, char **name,
* allocated.
*/
if (d_unlinked(path->dentry) && d_is_positive(path->dentry) &&
- !(flags & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))) {
+ !(flags & PATH_MEDIATE_DELETED)) {
error = -ENOENT;
goto out;
}
+ if (!connected)
+ error = disconnect(path, buf, name, flags);
+
out:
- /*
- * Append "/" to the pathname. The root directory is a special
- * case; it already ends in slash.
- */
- if (!error && isdir && ((*name)[1] != '\0' || (*name)[0] != '/'))
- strcpy(&buf[aa_g_path_max - 2], "/");
+ return error;
+}
+
+/**
+ * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended
+ * @path: path to get name for (NOT NULL)
+ * @flags: flags controlling path lookup
+ * @buffer: buffer to put name in (NOT NULL)
+ * @size: size of buffer
+ * @name: Returns - contains position of path name in @buffer (NOT NULL)
+ *
+ * Returns: %0 else error on failure
+ */
+static int get_name_to_buffer(const struct path *path, int flags, char *buffer,
+ int size, char **name, const char **info)
+{
+ int adjust = (flags & PATH_IS_DIR) ? 1 : 0;
+ int error = d_namespace_path(path, buffer, size - adjust, name, flags);
+
+ if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0')
+ /*
+ * Append "/" to the pathname. The root directory is a special
+ * case; it already ends in slash.
+ */
+ strcpy(&buffer[size - 2], "/");
+
+ if (info && error) {
+ if (error == -ENOENT)
+ *info = "Failed name lookup - deleted entry";
+ else if (error == -EACCES)
+ *info = "Failed name lookup - disconnected path";
+ else if (error == -ENAMETOOLONG)
+ *info = "Failed name lookup - name too long";
+ else
+ *info = "Failed name lookup";
+ }
return error;
}
/**
- * aa_path_name - get the pathname to a buffer ensure dir / is appended
+ * aa_path_name - compute the pathname of a file
* @path: path the file (NOT NULL)
* @flags: flags controlling path name generation
- * @buffer: buffer to put name in (NOT NULL)
+ * @buffer: buffer that aa_get_name() allocated (NOT NULL)
* @name: Returns - the generated path name if !error (NOT NULL)
* @info: Returns - information on why the path lookup failed (MAYBE NULL)
- * @disconnected: string to prepend to disconnected paths
*
* @name is a pointer to the beginning of the pathname (which usually differs
* from the beginning of the buffer), or NULL. If there is an error @name
@@ -198,23 +215,32 @@ out:
*
* Returns: %0 else error code if could retrieve name
*/
-int aa_path_name(const struct path *path, int flags, char *buffer,
- const char **name, const char **info, const char *disconnected)
+int aa_path_name(const struct path *path, int flags, char **buffer,
+ const char **name, const char **info)
{
- char *str = NULL;
- int error = d_namespace_path(path, buffer, &str, flags, disconnected);
-
- if (info && error) {
- if (error == -ENOENT)
- *info = "Failed name lookup - deleted entry";
- else if (error == -EACCES)
- *info = "Failed name lookup - disconnected path";
- else if (error == -ENAMETOOLONG)
- *info = "Failed name lookup - name too long";
- else
- *info = "Failed name lookup";
+ char *buf, *str = NULL;
+ int size = 256;
+ int error;
+
+ *name = NULL;
+ *buffer = NULL;
+ for (;;) {
+ /* freed by caller */
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ error = get_name_to_buffer(path, flags, buf, size, &str, info);
+ if (error != -ENAMETOOLONG)
+ break;
+
+ kfree(buf);
+ size <<= 1;
+ if (size > aa_g_path_max)
+ return -ENAMETOOLONG;
+ *info = NULL;
}
-
+ *buffer = buf;
*name = str;
return error;
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index a6e84d19bc0b..0eea92aeb02d 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -101,9 +101,20 @@ const char *const aa_profile_mode_names[] = {
"unconfined",
};
+/* requires profile list write lock held */
+void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new)
+{
+ struct aa_profile *tmp;
+
+ tmp = rcu_dereference_protected(orig->proxy->profile,
+ mutex_is_locked(&orig->ns->lock));
+ rcu_assign_pointer(orig->proxy->profile, aa_get_profile(new));
+ orig->flags |= PFLAG_STALE;
+ aa_put_profile(tmp);
+}
/**
- * __add_profile - add a profiles to list and label tree
+ * __list_add_profile - add a profile to a list
* @list: list to add it to (NOT NULL)
* @profile: the profile to add (NOT NULL)
*
@@ -111,21 +122,12 @@ const char *const aa_profile_mode_names[] = {
*
* Requires: namespace lock be held, or list not be shared
*/
-static void __add_profile(struct list_head *list, struct aa_profile *profile)
+static void __list_add_profile(struct list_head *list,
+ struct aa_profile *profile)
{
- struct aa_label *l;
-
- AA_BUG(!list);
- AA_BUG(!profile);
- AA_BUG(!profile->ns);
- AA_BUG(!mutex_is_locked(&profile->ns->lock));
-
list_add_rcu(&profile->base.list, list);
/* get list reference */
aa_get_profile(profile);
- l = aa_label_insert(&profile->ns->labels, &profile->label);
- AA_BUG(l != &profile->label);
- aa_put_label(l);
}
/**
@@ -142,10 +144,6 @@ static void __add_profile(struct list_head *list, struct aa_profile *profile)
*/
static void __list_remove_profile(struct aa_profile *profile)
{
- AA_BUG(!profile);
- AA_BUG(!profile->ns);
- AA_BUG(!mutex_is_locked(&profile->ns->lock));
-
list_del_rcu(&profile->base.list);
aa_put_profile(profile);
}
@@ -158,15 +156,11 @@ static void __list_remove_profile(struct aa_profile *profile)
*/
static void __remove_profile(struct aa_profile *profile)
{
- AA_BUG(!profile);
- AA_BUG(!profile->ns);
- AA_BUG(!mutex_is_locked(&profile->ns->lock));
-
/* release any children lists first */
__aa_profile_list_release(&profile->base.profiles);
/* released by free_profile */
- aa_label_remove(&profile->label);
- __aafs_profile_rmdir(profile);
+ __aa_update_proxy(profile, profile->ns->unconfined);
+ __aa_fs_profile_rmdir(profile);
__list_remove_profile(profile);
}
@@ -183,6 +177,24 @@ void __aa_profile_list_release(struct list_head *head)
__remove_profile(profile);
}
+
+static void free_proxy(struct aa_proxy *p)
+{
+ if (p) {
+ /* r->profile will not be updated any more as r is dead */
+ aa_put_profile(rcu_dereference_protected(p->profile, true));
+ kzfree(p);
+ }
+}
+
+
+void aa_free_proxy_kref(struct kref *kref)
+{
+ struct aa_proxy *p = container_of(kref, struct aa_proxy, count);
+
+ free_proxy(p);
+}
+
/**
* aa_free_data - free a data blob
* @ptr: data to free
@@ -231,6 +243,7 @@ void aa_free_profile(struct aa_profile *profile)
kzfree(profile->dirname);
aa_put_dfa(profile->xmatch);
aa_put_dfa(profile->policy.dfa);
+ aa_put_proxy(profile->proxy);
if (profile->data) {
rht = profile->data;
@@ -241,85 +254,177 @@ void aa_free_profile(struct aa_profile *profile)
kzfree(profile->hash);
aa_put_loaddata(profile->rawdata);
-
kzfree(profile);
}
/**
+ * aa_free_profile_rcu - free aa_profile by rcu (called by aa_free_profile_kref)
+ * @head: rcu_head callback for freeing of a profile (NOT NULL)
+ */
+static void aa_free_profile_rcu(struct rcu_head *head)
+{
+ struct aa_profile *p = container_of(head, struct aa_profile, rcu);
+ if (p->flags & PFLAG_NS_COUNT)
+ aa_free_ns(p->ns);
+ else
+ aa_free_profile(p);
+}
+
+/**
+ * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile)
+ * @kr: kref callback for freeing of a profile (NOT NULL)
+ */
+void aa_free_profile_kref(struct kref *kref)
+{
+ struct aa_profile *p = container_of(kref, struct aa_profile, count);
+ call_rcu(&p->rcu, aa_free_profile_rcu);
+}
+
+/**
* aa_alloc_profile - allocate, initialize and return a new profile
* @hname: name of the profile (NOT NULL)
* @gfp: allocation type
*
* Returns: refcount profile or NULL on failure
*/
-struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
- gfp_t gfp)
+struct aa_profile *aa_alloc_profile(const char *hname, gfp_t gfp)
{
struct aa_profile *profile;
/* freed by free_profile - usually through aa_put_profile */
- profile = kzalloc(sizeof(*profile) + sizeof(struct aa_profile *) * 2,
- gfp);
+ profile = kzalloc(sizeof(*profile), gfp);
if (!profile)
return NULL;
+ profile->proxy = kzalloc(sizeof(struct aa_proxy), gfp);
+ if (!profile->proxy)
+ goto fail;
+ kref_init(&profile->proxy->count);
+
if (!aa_policy_init(&profile->base, NULL, hname, gfp))
goto fail;
- if (!aa_label_init(&profile->label, 1))
+ kref_init(&profile->count);
+
+ /* refcount released by caller */
+ return profile;
+
+fail:
+ kzfree(profile->proxy);
+ kzfree(profile);
+
+ return NULL;
+}
+
+/**
+ * aa_new_null_profile - create or find a null-X learning profile
+ * @parent: profile that caused this profile to be created (NOT NULL)
+ * @hat: true if the null- learning profile is a hat
+ * @base: name to base the null profile off of
+ * @gfp: type of allocation
+ *
+ * Find/Create a null- complain mode profile used in learning mode. The
+ * name of the profile is unique and follows the format of parent//null-XXX.
+ * where XXX is based on the @name or if that fails or is not supplied
+ * a unique number
+ *
+ * null profiles are added to the profile list but the list does not
+ * hold a count on them so that they are automatically released when
+ * not in use.
+ *
+ * Returns: new refcounted profile else NULL on failure
+ */
+struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
+ const char *base, gfp_t gfp)
+{
+ struct aa_profile *profile;
+ char *name;
+
+ AA_BUG(!parent);
+
+ if (base) {
+ name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base),
+ gfp);
+ if (name) {
+ sprintf(name, "%s//null-%s", parent->base.hname, base);
+ goto name;
+ }
+ /* fall through to try shorter uniq */
+ }
+
+ name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp);
+ if (!name)
+ return NULL;
+ sprintf(name, "%s//null-%x", parent->base.hname,
+ atomic_inc_return(&parent->ns->uniq_null));
+
+name:
+ /* lookup to see if this is a dup creation */
+ profile = aa_find_child(parent, basename(name));
+ if (profile)
+ goto out;
+
+ profile = aa_alloc_profile(name, gfp);
+ if (!profile)
goto fail;
- /* update being set needed by fs interface */
- if (!proxy) {
- proxy = aa_alloc_proxy(&profile->label, gfp);
- if (!proxy)
- goto fail;
- } else
- aa_get_proxy(proxy);
- profile->label.proxy = proxy;
+ profile->mode = APPARMOR_COMPLAIN;
+ profile->flags |= PFLAG_NULL;
+ if (hat)
+ profile->flags |= PFLAG_HAT;
+ profile->path_flags = parent->path_flags;
- profile->label.hname = profile->base.hname;
- profile->label.flags |= FLAG_PROFILE;
- profile->label.vec[0] = profile;
+ /* released on free_profile */
+ rcu_assign_pointer(profile->parent, aa_get_profile(parent));
+ profile->ns = aa_get_ns(parent->ns);
+ profile->file.dfa = aa_get_dfa(nulldfa);
+ profile->policy.dfa = aa_get_dfa(nulldfa);
+
+ mutex_lock(&profile->ns->lock);
+ __list_add_profile(&parent->base.profiles, profile);
+ mutex_unlock(&profile->ns->lock);
/* refcount released by caller */
+out:
+ kfree(name);
+
return profile;
fail:
+ kfree(name);
aa_free_profile(profile);
-
return NULL;
}
/* TODO: profile accounting - setup in remove */
/**
- * __strn_find_child - find a profile on @head list using substring of @name
+ * __find_child - find a profile on @head list with a name matching @name
* @head: list to search (NOT NULL)
* @name: name of profile (NOT NULL)
- * @len: length of @name substring to match
*
* Requires: rcu_read_lock be held
*
* Returns: unrefcounted profile ptr, or NULL if not found
*/
-static struct aa_profile *__strn_find_child(struct list_head *head,
- const char *name, int len)
+static struct aa_profile *__find_child(struct list_head *head, const char *name)
{
- return (struct aa_profile *)__policy_strn_find(head, name, len);
+ return (struct aa_profile *)__policy_find(head, name);
}
/**
- * __find_child - find a profile on @head list with a name matching @name
+ * __strn_find_child - find a profile on @head list using substring of @name
* @head: list to search (NOT NULL)
* @name: name of profile (NOT NULL)
+ * @len: length of @name substring to match
*
* Requires: rcu_read_lock be held
*
* Returns: unrefcounted profile ptr, or NULL if not found
*/
-static struct aa_profile *__find_child(struct list_head *head, const char *name)
+static struct aa_profile *__strn_find_child(struct list_head *head,
+ const char *name, int len)
{
- return __strn_find_child(head, name, strlen(name));
+ return (struct aa_profile *)__policy_strn_find(head, name, len);
}
/**
@@ -452,7 +557,7 @@ struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname)
return aa_lookupn_profile(ns, hname, strlen(hname));
}
-struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
+struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base,
const char *fqname, size_t n)
{
struct aa_profile *profile;
@@ -462,11 +567,11 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
name = aa_splitn_fqname(fqname, n, &ns_name, &ns_len);
if (ns_name) {
- ns = aa_lookupn_ns(labels_ns(base), ns_name, ns_len);
+ ns = aa_findn_ns(base->ns, ns_name, ns_len);
if (!ns)
return NULL;
} else
- ns = aa_get_ns(labels_ns(base));
+ ns = aa_get_ns(base->ns);
if (name)
profile = aa_lookupn_profile(ns, name, n - (name - fqname));
@@ -481,94 +586,6 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
}
/**
- * aa_new_null_profile - create or find a null-X learning profile
- * @parent: profile that caused this profile to be created (NOT NULL)
- * @hat: true if the null- learning profile is a hat
- * @base: name to base the null profile off of
- * @gfp: type of allocation
- *
- * Find/Create a null- complain mode profile used in learning mode. The
- * name of the profile is unique and follows the format of parent//null-XXX.
- * where XXX is based on the @name or if that fails or is not supplied
- * a unique number
- *
- * null profiles are added to the profile list but the list does not
- * hold a count on them so that they are automatically released when
- * not in use.
- *
- * Returns: new refcounted profile else NULL on failure
- */
-struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
- const char *base, gfp_t gfp)
-{
- struct aa_profile *p, *profile;
- const char *bname;
- char *name = NULL;
-
- AA_BUG(!parent);
-
- if (base) {
- name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base),
- gfp);
- if (name) {
- sprintf(name, "%s//null-%s", parent->base.hname, base);
- goto name;
- }
- /* fall through to try shorter uniq */
- }
-
- name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp);
- if (!name)
- return NULL;
- sprintf(name, "%s//null-%x", parent->base.hname,
- atomic_inc_return(&parent->ns->uniq_null));
-
-name:
- /* lookup to see if this is a dup creation */
- bname = basename(name);
- profile = aa_find_child(parent, bname);
- if (profile)
- goto out;
-
- profile = aa_alloc_profile(name, NULL, gfp);
- if (!profile)
- goto fail;
-
- profile->mode = APPARMOR_COMPLAIN;
- profile->label.flags |= FLAG_NULL;
- if (hat)
- profile->label.flags |= FLAG_HAT;
- profile->path_flags = parent->path_flags;
-
- /* released on free_profile */
- rcu_assign_pointer(profile->parent, aa_get_profile(parent));
- profile->ns = aa_get_ns(parent->ns);
- profile->file.dfa = aa_get_dfa(nulldfa);
- profile->policy.dfa = aa_get_dfa(nulldfa);
-
- mutex_lock_nested(&profile->ns->lock, profile->ns->level);
- p = __find_child(&parent->base.profiles, bname);
- if (p) {
- aa_free_profile(profile);
- profile = aa_get_profile(p);
- } else {
- __add_profile(&parent->base.profiles, profile);
- }
- mutex_unlock(&profile->ns->lock);
-
- /* refcount released by caller */
-out:
- kfree(name);
-
- return profile;
-
-fail:
- kfree(name);
- aa_free_profile(profile);
- return NULL;
-}
-
-/**
* replacement_allowed - test to see if replacement is allowed
* @profile: profile to test if it can be replaced (MAYBE NULL)
* @noreplace: true if replacement shouldn't be allowed but addition is okay
@@ -580,7 +597,7 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace,
const char **info)
{
if (profile) {
- if (profile->label.flags & FLAG_IMMUTIBLE) {
+ if (profile->flags & PFLAG_IMMUTABLE) {
*info = "cannot replace immutible profile";
return -EPERM;
} else if (noreplace) {
@@ -603,31 +620,29 @@ static void audit_cb(struct audit_buffer *ab, void *va)
}
/**
- * audit_policy - Do auditing of policy changes
- * @label: label to check if it can manage policy
+ * aa_audit_policy - Do auditing of policy changes
+ * @profile: profile to check if it can manage policy
* @op: policy operation being performed
- * @ns_name: name of namespace being manipulated
+ * @gfp: memory allocation flags
+ * @nsname: name of the ns being manipulated (MAY BE NULL)
* @name: name of profile being manipulated (NOT NULL)
* @info: any extra information to be audited (MAYBE NULL)
* @error: error code
*
* Returns: the error to be returned after audit is done
*/
-static int audit_policy(struct aa_label *label, const char *op,
- const char *ns_name, const char *name,
+static int audit_policy(struct aa_profile *profile, const char *op,
+ const char *nsname, const char *name,
const char *info, int error)
{
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
- aad(&sa)->iface.ns = ns_name;
+ aad(&sa)->iface.ns = nsname;
aad(&sa)->name = name;
aad(&sa)->info = info;
aad(&sa)->error = error;
- aad(&sa)->label = label;
-
- aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, audit_cb);
- return error;
+ return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb);
}
/**
@@ -671,30 +686,22 @@ bool policy_admin_capable(struct aa_ns *ns)
/**
* aa_may_manage_policy - can the current task manage policy
- * @label: label to check if it can manage policy
+ * @profile: profile to check if it can manage policy
* @op: the policy manipulation operation being done
*
* Returns: 0 if the task is allowed to manipulate policy else error
*/
-int aa_may_manage_policy(struct aa_label *label, struct aa_ns *ns, u32 mask)
+int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns,
+ const char *op)
{
- const char *op;
-
- if (mask & AA_MAY_REMOVE_POLICY)
- op = OP_PROF_RM;
- else if (mask & AA_MAY_REPLACE_POLICY)
- op = OP_PROF_REPL;
- else
- op = OP_PROF_LOAD;
-
/* check if loading policy is locked out */
if (aa_g_lock_policy)
- return audit_policy(label, op, NULL, NULL, "policy_locked",
- -EACCES);
+ return audit_policy(profile, op, NULL, NULL,
+ "policy_locked", -EACCES);
if (!policy_admin_capable(ns))
- return audit_policy(label, op, NULL, NULL, "not policy admin",
- -EACCES);
+ return audit_policy(profile, op, NULL, NULL,
+ "not policy admin", -EACCES);
/* TODO: add fine grained mediation of policy loads */
return 0;
@@ -736,7 +743,8 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh,
*
* Requires: namespace list lock be held, or list not be shared
*/
-static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
+static void __replace_profile(struct aa_profile *old, struct aa_profile *new,
+ bool share_proxy)
{
struct aa_profile *child, *tmp;
@@ -751,7 +759,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
p = __find_child(&new->base.profiles, child->base.name);
if (p) {
/* @p replaces @child */
- __replace_profile(child, p);
+ __replace_profile(child, p, share_proxy);
continue;
}
@@ -769,9 +777,15 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
struct aa_profile *parent = aa_deref_parent(old);
rcu_assign_pointer(new->parent, aa_get_profile(parent));
}
- aa_label_replace(&old->label, &new->label);
- /* migrate dents must come after label replacement b/c update */
- __aafs_profile_migrate_dents(old, new);
+ __aa_update_proxy(old, new);
+ if (share_proxy) {
+ aa_put_proxy(new->proxy);
+ new->proxy = aa_get_proxy(old->proxy);
+ } else if (!rcu_access_pointer(new->proxy->profile))
+ /* aafs interface uses proxy */
+ rcu_assign_pointer(new->proxy->profile,
+ aa_get_profile(new));
+ __aa_fs_profile_migrate_dents(old, new);
if (list_empty(&new->base.list)) {
/* new is not on a list already */
@@ -808,41 +822,11 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname,
return 0;
}
-static void share_name(struct aa_profile *old, struct aa_profile *new)
-{
- aa_put_str(new->base.hname);
- aa_get_str(old->base.hname);
- new->base.hname = old->base.hname;
- new->base.name = old->base.name;
- new->label.hname = old->label.hname;
-}
-
-/* Update to newest version of parent after previous replacements
- * Returns: unrefcount newest version of parent
- */
-static struct aa_profile *update_to_newest_parent(struct aa_profile *new)
-{
- struct aa_profile *parent, *newest;
-
- parent = rcu_dereference_protected(new->parent,
- mutex_is_locked(&new->ns->lock));
- newest = aa_get_newest_profile(parent);
-
- /* parent replaced in this atomic set? */
- if (newest != parent) {
- aa_put_profile(parent);
- rcu_assign_pointer(new->parent, newest);
- } else
- aa_put_profile(newest);
-
- return newest;
-}
-
/**
* aa_replace_profiles - replace profile(s) on the profile list
- * @policy_ns: namespace load is occurring on
+ * @view: namespace load is viewed from
* @label: label that is attempting to load/replace policy
- * @mask: permission mask
+ * @noreplace: true if only doing addition, no replacement allowed
* @udata: serialized data stream (NOT NULL)
*
* unpack and replace a profile on the profile list and uses of that profile
@@ -851,19 +835,16 @@ static struct aa_profile *update_to_newest_parent(struct aa_profile *new)
*
* Returns: size of data consumed else error code on failure.
*/
-ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
- u32 mask, struct aa_loaddata *udata)
+ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile,
+ bool noreplace, struct aa_loaddata *udata)
{
const char *ns_name, *info = NULL;
struct aa_ns *ns = NULL;
struct aa_load_ent *ent, *tmp;
- struct aa_loaddata *rawdata_ent;
- const char *op;
+ const char *op = OP_PROF_REPL;
ssize_t count, error;
LIST_HEAD(lh);
- op = mask & AA_MAY_REPLACE_POLICY ? OP_PROF_REPL : OP_PROF_LOAD;
- aa_get_loaddata(udata);
/* released below */
error = aa_unpack(udata, &lh, &ns_name);
if (error)
@@ -894,8 +875,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
count++;
}
if (ns_name) {
- ns = aa_prepare_ns(policy_ns ? policy_ns : labels_ns(label),
- ns_name);
+ ns = aa_prepare_ns(view, ns_name);
if (IS_ERR(ns)) {
op = OP_PROF_LOAD;
info = "failed to prepare namespace";
@@ -905,38 +885,22 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
goto fail;
}
} else
- ns = aa_get_ns(policy_ns ? policy_ns : labels_ns(label));
-
- mutex_lock_nested(&ns->lock, ns->level);
- /* check for duplicate rawdata blobs: space and file dedup */
- list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) {
- if (aa_rawdata_eq(rawdata_ent, udata)) {
- struct aa_loaddata *tmp;
-
- tmp = __aa_get_loaddata(rawdata_ent);
- /* check we didn't fail the race */
- if (tmp) {
- aa_put_loaddata(udata);
- udata = tmp;
- break;
- }
- }
- }
+ ns = aa_get_ns(view);
+
+ mutex_lock(&ns->lock);
/* setup parent and ns info */
list_for_each_entry(ent, &lh, list) {
struct aa_policy *policy;
-
ent->new->rawdata = aa_get_loaddata(udata);
- error = __lookup_replace(ns, ent->new->base.hname,
- !(mask & AA_MAY_REPLACE_POLICY),
+ error = __lookup_replace(ns, ent->new->base.hname, noreplace,
&ent->old, &info);
if (error)
goto fail_lock;
if (ent->new->rename) {
error = __lookup_replace(ns, ent->new->rename,
- !(mask & AA_MAY_REPLACE_POLICY),
- &ent->rename, &info);
+ noreplace, &ent->rename,
+ &info);
if (error)
goto fail_lock;
}
@@ -966,16 +930,15 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
}
/* create new fs entries for introspection if needed */
- if (!udata->dents[AAFS_LOADDATA_DIR]) {
- error = __aa_fs_create_rawdata(ns, udata);
- if (error) {
- info = "failed to create raw_data dir and files";
- ent = NULL;
- goto fail_lock;
- }
- }
list_for_each_entry(ent, &lh, list) {
- if (!ent->old) {
+ if (ent->old) {
+ /* inherit old interface files */
+
+ /* if (ent->rename)
+ TODO: support rename */
+ /* } else if (ent->rename) {
+ TODO: support rename */
+ } else {
struct dentry *parent;
if (rcu_access_pointer(ent->new->parent)) {
struct aa_profile *p;
@@ -983,61 +946,65 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
parent = prof_child_dir(p);
} else
parent = ns_subprofs_dir(ent->new->ns);
- error = __aafs_profile_mkdir(ent->new, parent);
+ error = __aa_fs_profile_mkdir(ent->new, parent);
}
if (error) {
- info = "failed to create";
+ info = "failed to create ";
goto fail_lock;
}
}
/* Done with checks that may fail - do actual replacement */
- __aa_bump_ns_revision(ns);
- __aa_loaddata_update(udata, ns->revision);
list_for_each_entry_safe(ent, tmp, &lh, list) {
list_del_init(&ent->list);
op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL;
- if (ent->old && ent->old->rawdata == ent->new->rawdata) {
- /* dedup actual profile replacement */
- audit_policy(label, op, ns_name, ent->new->base.hname,
- "same as current profile, skipping",
- error);
- goto skip;
- }
-
- /*
- * TODO: finer dedup based on profile range in data. Load set
- * can differ but profile may remain unchanged
- */
- audit_policy(label, op, ns_name, ent->new->base.hname, NULL,
- error);
+ audit_policy(profile, op, NULL, ent->new->base.hname,
+ NULL, error);
if (ent->old) {
- share_name(ent->old, ent->new);
- __replace_profile(ent->old, ent->new);
+ __replace_profile(ent->old, ent->new, 1);
+ if (ent->rename) {
+ /* aafs interface uses proxy */
+ struct aa_proxy *r = ent->new->proxy;
+ rcu_assign_pointer(r->profile,
+ aa_get_profile(ent->new));
+ __replace_profile(ent->rename, ent->new, 0);
+ }
+ } else if (ent->rename) {
+ /* aafs interface uses proxy */
+ rcu_assign_pointer(ent->new->proxy->profile,
+ aa_get_profile(ent->new));
+ __replace_profile(ent->rename, ent->new, 0);
+ } else if (ent->new->parent) {
+ struct aa_profile *parent, *newest;
+ parent = aa_deref_parent(ent->new);
+ newest = aa_get_newest_profile(parent);
+
+ /* parent replaced in this atomic set? */
+ if (newest != parent) {
+ aa_get_profile(newest);
+ rcu_assign_pointer(ent->new->parent, newest);
+ aa_put_profile(parent);
+ }
+ /* aafs interface uses proxy */
+ rcu_assign_pointer(ent->new->proxy->profile,
+ aa_get_profile(ent->new));
+ __list_add_profile(&newest->base.profiles, ent->new);
+ aa_put_profile(newest);
} else {
- struct list_head *lh;
-
- if (rcu_access_pointer(ent->new->parent)) {
- struct aa_profile *parent;
-
- parent = update_to_newest_parent(ent->new);
- lh = &parent->base.profiles;
- } else
- lh = &ns->base.profiles;
- __add_profile(lh, ent->new);
+ /* aafs interface uses proxy */
+ rcu_assign_pointer(ent->new->proxy->profile,
+ aa_get_profile(ent->new));
+ __list_add_profile(&ns->base.profiles, ent->new);
}
- skip:
aa_load_ent_free(ent);
}
- __aa_labelset_update_subtree(ns);
mutex_unlock(&ns->lock);
out:
aa_put_ns(ns);
- aa_put_loaddata(udata);
if (error)
return error;
@@ -1047,10 +1014,10 @@ fail_lock:
mutex_unlock(&ns->lock);
/* audit cause of failure */
- op = (ent && !ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
+ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
fail:
- audit_policy(label, op, ns_name, ent ? ent->new->base.hname : NULL,
- info, error);
+ audit_policy(profile, op, ns_name, ent ? ent->new->base.hname : NULL,
+ info, error);
/* audit status that rest of profiles in the atomic set failed too */
info = "valid profile in failed atomic policy load";
list_for_each_entry(tmp, &lh, list) {
@@ -1060,8 +1027,8 @@ fail:
continue;
}
op = (!tmp->old) ? OP_PROF_LOAD : OP_PROF_REPL;
- audit_policy(label, op, ns_name, tmp->new->base.hname, info,
- error);
+ audit_policy(profile, op, ns_name,
+ tmp->new->base.hname, info, error);
}
list_for_each_entry_safe(ent, tmp, &lh, list) {
list_del_init(&ent->list);
@@ -1073,8 +1040,8 @@ fail:
/**
* aa_remove_profiles - remove profile(s) from the system
- * @policy_ns: namespace the remove is being done from
- * @subj: label attempting to remove policy
+ * @view: namespace the remove is being done from
+ * @subj: profile attempting to remove policy
* @fqname: name of the profile or namespace to remove (NOT NULL)
* @size: size of the name
*
@@ -1085,13 +1052,13 @@ fail:
*
* Returns: size of data consume else error code if fails
*/
-ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj,
+ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *subj,
char *fqname, size_t size)
{
- struct aa_ns *ns = NULL;
+ struct aa_ns *root = NULL, *ns = NULL;
struct aa_profile *profile = NULL;
const char *name = fqname, *info = NULL;
- const char *ns_name = NULL;
+ char *ns_name = NULL;
ssize_t error = 0;
if (*fqname == 0) {
@@ -1100,13 +1067,12 @@ ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj,
goto fail;
}
- if (fqname[0] == ':') {
- size_t ns_len;
+ root = view;
- name = aa_splitn_fqname(fqname, size, &ns_name, &ns_len);
+ if (fqname[0] == ':') {
+ name = aa_split_fqname(fqname, &ns_name);
/* released below */
- ns = aa_lookupn_ns(policy_ns ? policy_ns : labels_ns(subj),
- ns_name, ns_len);
+ ns = aa_find_ns(root, ns_name);
if (!ns) {
info = "namespace does not exist";
error = -ENOENT;
@@ -1114,17 +1080,16 @@ ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj,
}
} else
/* released below */
- ns = aa_get_ns(policy_ns ? policy_ns : labels_ns(subj));
+ ns = aa_get_ns(root);
if (!name) {
/* remove namespace - can only happen if fqname[0] == ':' */
- mutex_lock_nested(&ns->parent->lock, ns->level);
+ mutex_lock(&ns->parent->lock);
__aa_remove_ns(ns);
- __aa_bump_ns_revision(ns);
mutex_unlock(&ns->parent->lock);
} else {
/* remove profile */
- mutex_lock_nested(&ns->lock, ns->level);
+ mutex_lock(&ns->lock);
profile = aa_get_profile(__lookup_profile(&ns->base, name));
if (!profile) {
error = -ENOENT;
@@ -1133,8 +1098,6 @@ ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj,
}
name = profile->base.hname;
__remove_profile(profile);
- __aa_labelset_update_subtree(ns);
- __aa_bump_ns_revision(ns);
mutex_unlock(&ns->lock);
}
diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c
index 8d8ef6d6d885..93d1826c4b09 100644
--- a/security/apparmor/policy_ns.c
+++ b/security/apparmor/policy_ns.c
@@ -23,7 +23,6 @@
#include "include/apparmor.h"
#include "include/context.h"
#include "include/policy_ns.h"
-#include "include/label.h"
#include "include/policy.h"
/* root profile namespace */
@@ -100,28 +99,22 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name)
goto fail_ns;
INIT_LIST_HEAD(&ns->sub_ns);
- INIT_LIST_HEAD(&ns->rawdata_list);
mutex_init(&ns->lock);
- init_waitqueue_head(&ns->wait);
/* released by aa_free_ns() */
- ns->unconfined = aa_alloc_profile("unconfined", NULL, GFP_KERNEL);
+ ns->unconfined = aa_alloc_profile("unconfined", GFP_KERNEL);
if (!ns->unconfined)
goto fail_unconfined;
- ns->unconfined->label.flags |= FLAG_IX_ON_NAME_ERROR |
- FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED;
+ ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR |
+ PFLAG_IMMUTABLE | PFLAG_NS_COUNT;
ns->unconfined->mode = APPARMOR_UNCONFINED;
- ns->unconfined->file.dfa = aa_get_dfa(nulldfa);
- ns->unconfined->policy.dfa = aa_get_dfa(nulldfa);
/* ns and ns->unconfined share ns->unconfined refcount */
ns->unconfined->ns = ns;
atomic_set(&ns->uniq_null, 0);
- aa_labelset_init(&ns->labels);
-
return ns;
fail_unconfined:
@@ -144,7 +137,6 @@ void aa_free_ns(struct aa_ns *ns)
return;
aa_policy_destroy(&ns->base);
- aa_labelset_destroy(&ns->labels);
aa_put_ns(ns->parent);
ns->unconfined->ns = NULL;
@@ -189,60 +181,6 @@ struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name)
return aa_findn_ns(root, name, strlen(name));
}
-/**
- * __aa_lookupn_ns - lookup the namespace matching @hname
- * @base: base list to start looking up profile name from (NOT NULL)
- * @hname: hierarchical ns name (NOT NULL)
- * @n: length of @hname
- *
- * Requires: rcu_read_lock be held
- *
- * Returns: unrefcounted ns pointer or NULL if not found
- *
- * Do a relative name lookup, recursing through profile tree.
- */
-struct aa_ns *__aa_lookupn_ns(struct aa_ns *view, const char *hname, size_t n)
-{
- struct aa_ns *ns = view;
- const char *split;
-
- for (split = strnstr(hname, "//", n); split;
- split = strnstr(hname, "//", n)) {
- ns = __aa_findn_ns(&ns->sub_ns, hname, split - hname);
- if (!ns)
- return NULL;
-
- n -= split + 2 - hname;
- hname = split + 2;
- }
-
- if (n)
- return __aa_findn_ns(&ns->sub_ns, hname, n);
- return NULL;
-}
-
-/**
- * aa_lookupn_ns - look up a policy namespace relative to @view
- * @view: namespace to search in (NOT NULL)
- * @name: name of namespace to find (NOT NULL)
- * @n: length of @name
- *
- * Returns: a refcounted namespace on the list, or NULL if no namespace
- * called @name exists.
- *
- * refcount released by caller
- */
-struct aa_ns *aa_lookupn_ns(struct aa_ns *view, const char *name, size_t n)
-{
- struct aa_ns *ns = NULL;
-
- rcu_read_lock();
- ns = aa_get_ns(__aa_lookupn_ns(view, name, n));
- rcu_read_unlock();
-
- return ns;
-}
-
static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name,
struct dentry *dir)
{
@@ -255,10 +193,9 @@ static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name,
ns = alloc_ns(parent->base.hname, name);
if (!ns)
- return ERR_PTR(-ENOMEM);
- ns->level = parent->level + 1;
- mutex_lock_nested(&ns->lock, ns->level);
- error = __aafs_ns_mkdir(ns, ns_subns_dir(parent), name, dir);
+ return NULL;
+ mutex_lock(&ns->lock);
+ error = __aa_fs_ns_mkdir(ns, ns_subns_dir(parent), name);
if (error) {
AA_ERROR("Failed to create interface for ns %s\n",
ns->base.name);
@@ -267,6 +204,7 @@ static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name,
return ERR_PTR(error);
}
ns->parent = aa_get_ns(parent);
+ ns->level = parent->level + 1;
list_add_rcu(&ns->base.list, &parent->sub_ns);
/* add list ref */
aa_get_ns(ns);
@@ -313,7 +251,7 @@ struct aa_ns *aa_prepare_ns(struct aa_ns *parent, const char *name)
{
struct aa_ns *ns;
- mutex_lock_nested(&parent->lock, parent->level);
+ mutex_lock(&parent->lock);
/* try and find the specified ns and if it doesn't exist create it */
/* released by caller */
ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name));
@@ -336,22 +274,16 @@ static void destroy_ns(struct aa_ns *ns)
if (!ns)
return;
- mutex_lock_nested(&ns->lock, ns->level);
+ mutex_lock(&ns->lock);
/* release all profiles in this namespace */
__aa_profile_list_release(&ns->base.profiles);
/* release all sub namespaces */
__ns_list_release(&ns->sub_ns);
- if (ns->parent) {
- unsigned long flags;
-
- write_lock_irqsave(&ns->labels.lock, flags);
- __aa_proxy_redirect(ns_unconfined(ns),
- ns_unconfined(ns->parent));
- write_unlock_irqrestore(&ns->labels.lock, flags);
- }
- __aafs_ns_rmdir(ns);
+ if (ns->parent)
+ __aa_update_proxy(ns->unconfined, ns->parent->unconfined);
+ __aa_fs_ns_rmdir(ns);
mutex_unlock(&ns->lock);
}
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 4c041a44dac8..92d02d77c0d6 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -13,7 +13,7 @@
* License.
*
* AppArmor uses a serialized binary format for loading policy. To find
- * policy format documentation see Documentation/admin-guide/LSM/apparmor.rst
+ * policy format documentation look in Documentation/security/apparmor.txt
* All policy is validated before it is used.
*/
@@ -26,7 +26,6 @@
#include "include/context.h"
#include "include/crypto.h"
#include "include/match.h"
-#include "include/path.h"
#include "include/policy.h"
#include "include/policy_unpack.h"
@@ -85,9 +84,9 @@ static void audit_cb(struct audit_buffer *ab, void *va)
audit_log_format(ab, " ns=");
audit_log_untrustedstring(ab, aad(sa)->iface.ns);
}
- if (aad(sa)->name) {
+ if (aad(sa)->iface.name) {
audit_log_format(ab, " name=");
- audit_log_untrustedstring(ab, aad(sa)->name);
+ audit_log_untrustedstring(ab, aad(sa)->iface.name);
}
if (aad(sa)->iface.pos)
audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos);
@@ -108,88 +107,31 @@ static int audit_iface(struct aa_profile *new, const char *ns_name,
const char *name, const char *info, struct aa_ext *e,
int error)
{
- struct aa_profile *profile = labels_profile(aa_current_raw_label());
+ struct aa_profile *profile = __aa_current_profile();
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
if (e)
aad(&sa)->iface.pos = e->pos - e->start;
aad(&sa)->iface.ns = ns_name;
if (new)
- aad(&sa)->name = new->base.hname;
+ aad(&sa)->iface.name = new->base.hname;
else
- aad(&sa)->name = name;
+ aad(&sa)->iface.name = name;
aad(&sa)->info = info;
aad(&sa)->error = error;
return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb);
}
-void __aa_loaddata_update(struct aa_loaddata *data, long revision)
-{
- AA_BUG(!data);
- AA_BUG(!data->ns);
- AA_BUG(!data->dents[AAFS_LOADDATA_REVISION]);
- AA_BUG(!mutex_is_locked(&data->ns->lock));
- AA_BUG(data->revision > revision);
-
- data->revision = revision;
- d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime =
- current_time(d_inode(data->dents[AAFS_LOADDATA_DIR]));
- d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime =
- current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION]));
-}
-
-bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r)
-{
- if (l->size != r->size)
- return false;
- if (aa_g_hash_policy && memcmp(l->hash, r->hash, aa_hash_size()) != 0)
- return false;
- return memcmp(l->data, r->data, r->size) == 0;
-}
-
-/*
- * need to take the ns mutex lock which is NOT safe most places that
- * put_loaddata is called, so we have to delay freeing it
- */
-static void do_loaddata_free(struct work_struct *work)
-{
- struct aa_loaddata *d = container_of(work, struct aa_loaddata, work);
- struct aa_ns *ns = aa_get_ns(d->ns);
-
- if (ns) {
- mutex_lock_nested(&ns->lock, ns->level);
- __aa_fs_remove_rawdata(d);
- mutex_unlock(&ns->lock);
- aa_put_ns(ns);
- }
-
- kzfree(d->hash);
- kfree(d->name);
- kvfree(d);
-}
-
void aa_loaddata_kref(struct kref *kref)
{
struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count);
if (d) {
- INIT_WORK(&d->work, do_loaddata_free);
- schedule_work(&d->work);
+ kzfree(d->hash);
+ kvfree(d);
}
}
-struct aa_loaddata *aa_loaddata_alloc(size_t size)
-{
- struct aa_loaddata *d = kvzalloc(sizeof(*d) + size, GFP_KERNEL);
-
- if (d == NULL)
- return ERR_PTR(-ENOMEM);
- kref_init(&d->count);
- INIT_LIST_HEAD(&d->list);
-
- return d;
-}
-
/* test if read will be in packed data bounds */
static bool inbounds(struct aa_ext *e, size_t size)
{
@@ -385,6 +327,32 @@ static int unpack_strdup(struct aa_ext *e, char **string, const char *name)
return res;
}
+#define DFA_VALID_PERM_MASK 0xffffffff
+#define DFA_VALID_PERM2_MASK 0xffffffff
+
+/**
+ * verify_accept - verify the accept tables of a dfa
+ * @dfa: dfa to verify accept tables of (NOT NULL)
+ * @flags: flags governing dfa
+ *
+ * Returns: 1 if valid accept tables else 0 if error
+ */
+static bool verify_accept(struct aa_dfa *dfa, int flags)
+{
+ int i;
+
+ /* verify accept permissions */
+ for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) {
+ int mode = ACCEPT_TABLE(dfa)[i];
+
+ if (mode & ~DFA_VALID_PERM_MASK)
+ return 0;
+
+ if (ACCEPT_TABLE2(dfa)[i] & ~DFA_VALID_PERM2_MASK)
+ return 0;
+ }
+ return 1;
+}
/**
* unpack_dfa - unpack a file rule dfa
@@ -415,9 +383,15 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
if (IS_ERR(dfa))
return dfa;
+ if (!verify_accept(dfa, flags))
+ goto fail;
}
return dfa;
+
+fail:
+ aa_put_dfa(dfa);
+ return ERR_PTR(-EPROTO);
}
/**
@@ -429,7 +403,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
*/
static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
{
- void *saved_pos = e->pos;
+ void *pos = e->pos;
/* exec table is optional */
if (unpack_nameX(e, AA_STRUCT, "xtable")) {
@@ -447,7 +421,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
profile->file.trans.size = size;
for (i = 0; i < size; i++) {
char *str;
- int c, j, pos, size2 = unpack_strdup(e, &str, NULL);
+ int c, j, size2 = unpack_strdup(e, &str, NULL);
/* unpack_strdup verifies that the last character is
* null termination byte.
*/
@@ -459,25 +433,19 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
goto fail;
/* count internal # of internal \0 */
- for (c = j = 0; j < size2 - 1; j++) {
- if (!str[j]) {
- pos = j;
+ for (c = j = 0; j < size2 - 2; j++) {
+ if (!str[j])
c++;
- }
}
if (*str == ':') {
- /* first character after : must be valid */
- if (!str[1])
- goto fail;
/* beginning with : requires an embedded \0,
* verify that exactly 1 internal \0 exists
* trailing \0 already verified by unpack_strdup
- *
- * convert \0 back to : for label_parse
*/
- if (c == 1)
- str[pos] = ':';
- else if (c > 1)
+ if (c != 1)
+ goto fail;
+ /* first character after : must be valid */
+ if (!str[1])
goto fail;
} else if (c)
/* fail - all other cases with embedded \0 */
@@ -492,7 +460,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
fail:
aa_free_domain_entries(&profile->file.trans);
- e->pos = saved_pos;
+ e->pos = pos;
return 0;
}
@@ -564,7 +532,6 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
{
struct aa_profile *profile = NULL;
const char *tmpname, *tmpns = NULL, *name = NULL;
- const char *info = "failed to unpack profile";
size_t ns_len;
struct rhashtable_params params = { 0 };
char *key = NULL;
@@ -587,14 +554,12 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len);
if (tmpns) {
*ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
- if (!*ns_name) {
- info = "out of memory";
+ if (!*ns_name)
goto fail;
- }
name = tmpname;
}
- profile = aa_alloc_profile(name, NULL, GFP_KERNEL);
+ profile = aa_alloc_profile(name, GFP_KERNEL);
if (!profile)
return ERR_PTR(-ENOMEM);
@@ -609,31 +574,22 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (IS_ERR(profile->xmatch)) {
error = PTR_ERR(profile->xmatch);
profile->xmatch = NULL;
- info = "bad xmatch";
goto fail;
}
/* xmatch_len is not optional if xmatch is set */
if (profile->xmatch) {
- if (!unpack_u32(e, &tmp, NULL)) {
- info = "missing xmatch len";
+ if (!unpack_u32(e, &tmp, NULL))
goto fail;
- }
profile->xmatch_len = tmp;
}
- /* disconnected attachment string is optional */
- (void) unpack_str(e, &profile->disconnected, "disconnected");
-
/* per profile debug flags (complain, audit) */
- if (!unpack_nameX(e, AA_STRUCT, "flags")) {
- info = "profile missing flags";
+ if (!unpack_nameX(e, AA_STRUCT, "flags"))
goto fail;
- }
- info = "failed to unpack profile flags";
if (!unpack_u32(e, &tmp, NULL))
goto fail;
if (tmp & PACKED_FLAG_HAT)
- profile->label.flags |= FLAG_HAT;
+ profile->flags |= PFLAG_HAT;
if (!unpack_u32(e, &tmp, NULL))
goto fail;
if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG))
@@ -652,13 +608,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
/* path_flags is optional */
if (unpack_u32(e, &profile->path_flags, "path_flags"))
- profile->path_flags |= profile->label.flags &
- PATH_MEDIATE_DELETED;
+ profile->path_flags |= profile->flags & PFLAG_MEDIATE_DELETED;
else
/* set a default value if path_flags field is not present */
- profile->path_flags = PATH_MEDIATE_DELETED;
+ profile->path_flags = PFLAG_MEDIATE_DELETED;
- info = "failed to unpack profile capabilities";
if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL))
goto fail;
if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL))
@@ -668,7 +622,6 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (!unpack_u32(e, &tmpcap.cap[0], NULL))
goto fail;
- info = "failed to unpack upper profile capabilities";
if (unpack_nameX(e, AA_STRUCT, "caps64")) {
/* optional upper half of 64 bit caps */
if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL))
@@ -683,7 +636,6 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
goto fail;
}
- info = "failed to unpack extended profile capabilities";
if (unpack_nameX(e, AA_STRUCT, "capsx")) {
/* optional extended caps mediation mask */
if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL))
@@ -694,10 +646,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
goto fail;
}
- if (!unpack_rlimits(e, profile)) {
- info = "failed to unpack profile rlimits";
+ if (!unpack_rlimits(e, profile))
goto fail;
- }
size = unpack_array(e, "net_allowed_af");
if (size) {
@@ -733,7 +683,6 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (unpack_nameX(e, AA_STRUCT, "policydb")) {
/* generic policy dfa - optional and may be NULL */
- info = "failed to unpack policydb";
profile->policy.dfa = unpack_dfa(e);
if (IS_ERR(profile->policy.dfa)) {
error = PTR_ERR(profile->policy.dfa);
@@ -763,7 +712,6 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (IS_ERR(profile->file.dfa)) {
error = PTR_ERR(profile->file.dfa);
profile->file.dfa = NULL;
- info = "failed to unpack profile file rules";
goto fail;
} else if (profile->file.dfa) {
if (!unpack_u32(e, &profile->file.start, "dfa_start"))
@@ -776,13 +724,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
} else
profile->file.dfa = aa_get_dfa(nulldfa);
- if (!unpack_trans_table(e, profile)) {
- info = "failed to unpack profile transition table";
+ if (!unpack_trans_table(e, profile))
goto fail;
- }
if (unpack_nameX(e, AA_STRUCT, "data")) {
- info = "out of memory";
profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
if (!profile->data)
goto fail;
@@ -794,10 +739,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
params.hashfn = strhash;
params.obj_cmpfn = datacmp;
- if (rhashtable_init(profile->data, &params)) {
- info = "failed to init key, value hash table";
+ if (rhashtable_init(profile->data, &params))
goto fail;
- }
while (unpack_strdup(e, &key, NULL)) {
data = kzalloc(sizeof(*data), GFP_KERNEL);
@@ -819,16 +762,12 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
profile->data->p);
}
- if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
- info = "failed to unpack end of key, value data table";
+ if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
- }
}
- if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
- info = "failed to unpack end of profile";
+ if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
- }
return profile;
@@ -837,7 +776,8 @@ fail:
name = NULL;
else if (!name)
name = "unknown";
- audit_iface(profile, NULL, name, info, e, error);
+ audit_iface(profile, NULL, name, "failed to unpack profile", e,
+ error);
aa_free_profile(profile);
return ERR_PTR(error);
@@ -870,7 +810,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
* if not specified use previous version
* Mask off everything that is not kernel abi version
*/
- if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v7)) {
+ if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) {
audit_iface(NULL, NULL, NULL, "unsupported interface version",
e, error);
return error;
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c
index d81617379d63..3466a27bca09 100644
--- a/security/apparmor/procattr.c
+++ b/security/apparmor/procattr.c
@@ -34,41 +34,50 @@
*
* Returns: size of string placed in @string else error code on failure
*/
-int aa_getprocattr(struct aa_label *label, char **string)
+int aa_getprocattr(struct aa_profile *profile, char **string)
{
- struct aa_ns *ns = labels_ns(label);
- struct aa_ns *current_ns = aa_get_current_ns();
- int len;
-
- if (!aa_ns_visible(current_ns, ns, true)) {
- aa_put_ns(current_ns);
+ char *str;
+ int len = 0, mode_len = 0, ns_len = 0, name_len;
+ const char *mode_str = aa_profile_mode_names[profile->mode];
+ const char *ns_name = NULL;
+ struct aa_ns *ns = profile->ns;
+ struct aa_ns *current_ns = __aa_current_profile()->ns;
+ char *s;
+
+ if (!aa_ns_visible(current_ns, ns, true))
return -EACCES;
- }
- len = aa_label_snxprint(NULL, 0, current_ns, label,
- FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
- FLAG_HIDDEN_UNCONFINED);
- AA_BUG(len < 0);
+ ns_name = aa_ns_name(current_ns, ns, true);
+ ns_len = strlen(ns_name);
- *string = kmalloc(len + 2, GFP_KERNEL);
- if (!*string) {
- aa_put_ns(current_ns);
- return -ENOMEM;
- }
+ /* if the visible ns_name is > 0 increase size for : :// seperator */
+ if (ns_len)
+ ns_len += 4;
- len = aa_label_snxprint(*string, len + 2, current_ns, label,
- FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
- FLAG_HIDDEN_UNCONFINED);
- if (len < 0) {
- aa_put_ns(current_ns);
- return len;
- }
+ /* unconfined profiles don't have a mode string appended */
+ if (!unconfined(profile))
+ mode_len = strlen(mode_str) + 3; /* + 3 for _() */
- (*string)[len] = '\n';
- (*string)[len + 1] = 0;
+ name_len = strlen(profile->base.hname);
+ len = mode_len + ns_len + name_len + 1; /* + 1 for \n */
+ s = str = kmalloc(len + 1, GFP_KERNEL); /* + 1 \0 */
+ if (!str)
+ return -ENOMEM;
- aa_put_ns(current_ns);
- return len + 1;
+ if (ns_len) {
+ /* skip over prefix current_ns->base.hname and separating // */
+ sprintf(s, ":%s://", ns_name);
+ s += ns_len;
+ }
+ if (unconfined(profile))
+ /* mode string not being appended */
+ sprintf(s, "%s\n", profile->base.hname);
+ else
+ sprintf(s, "%s (%s)\n", profile->base.hname, mode_str);
+ *string = str;
+
+ /* NOTE: len does not include \0 of string, not saved as part of file */
+ return len;
}
/**
@@ -99,11 +108,11 @@ static char *split_token_from_name(const char *op, char *args, u64 *token)
* aa_setprocattr_chagnehat - handle procattr interface to change_hat
* @args: args received from writing to /proc/<pid>/attr/current (NOT NULL)
* @size: size of the args
- * @flags: set of flags governing behavior
+ * @test: true if this is a test of change_hat permissions
*
* Returns: %0 or error code if change_hat fails
*/
-int aa_setprocattr_changehat(char *args, size_t size, int flags)
+int aa_setprocattr_changehat(char *args, size_t size, int test)
{
char *hat;
u64 token;
@@ -138,5 +147,5 @@ int aa_setprocattr_changehat(char *args, size_t size, int flags)
AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d Hat '%s'\n",
__func__, current->pid, token, count, "<NULL>");
- return aa_change_hat(hats, count, token, flags);
+ return aa_change_hat(hats, count, token, test);
}
diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c
index d8bc842594ed..86a941afd956 100644
--- a/security/apparmor/resource.c
+++ b/security/apparmor/resource.c
@@ -13,7 +13,6 @@
*/
#include <linux/audit.h>
-#include <linux/security.h>
#include "include/audit.h"
#include "include/context.h"
@@ -25,8 +24,8 @@
*/
#include "rlim_names.h"
-struct aa_sfs_entry aa_sfs_entry_rlimit[] = {
- AA_SFS_FILE_STRING("mask", AA_SFS_RLIMIT_MASK),
+struct aa_fs_entry aa_fs_entry_rlimit[] = {
+ AA_FS_FILE_STRING("mask", AA_FS_RLIMIT_MASK),
{ }
};
@@ -37,11 +36,6 @@ static void audit_cb(struct audit_buffer *ab, void *va)
audit_log_format(ab, " rlimit=%s value=%lu",
rlim_names[aad(sa)->rlim.rlim], aad(sa)->rlim.max);
- if (aad(sa)->peer) {
- audit_log_format(ab, " peer=");
- aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
- FLAGS_NONE, GFP_ATOMIC);
- }
}
/**
@@ -54,17 +48,13 @@ static void audit_cb(struct audit_buffer *ab, void *va)
* Returns: 0 or sa->error else other error code on failure
*/
static int audit_resource(struct aa_profile *profile, unsigned int resource,
- unsigned long value, struct aa_label *peer,
- const char *info, int error)
+ unsigned long value, int error)
{
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETRLIMIT);
aad(&sa)->rlim.rlim = resource;
aad(&sa)->rlim.max = value;
- aad(&sa)->peer = peer;
- aad(&sa)->info = info;
aad(&sa)->error = error;
-
return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb);
}
@@ -82,21 +72,9 @@ int aa_map_resource(int resource)
return rlim_map[resource];
}
-static int profile_setrlimit(struct aa_profile *profile, unsigned int resource,
- struct rlimit *new_rlim)
-{
- int e = 0;
-
- if (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max >
- profile->rlimits.limits[resource].rlim_max)
- e = -EACCES;
- return audit_resource(profile, resource, new_rlim->rlim_max, NULL, NULL,
- e);
-}
-
/**
* aa_task_setrlimit - test permission to set an rlimit
- * @label - label confining the task (NOT NULL)
+ * @profile - profile confining the task (NOT NULL)
* @task - task the resource is being set on
* @resource - the resource being set
* @new_rlim - the new resource limit (NOT NULL)
@@ -105,15 +83,14 @@ static int profile_setrlimit(struct aa_profile *profile, unsigned int resource,
*
* Returns: 0 or error code if setting resource failed
*/
-int aa_task_setrlimit(struct aa_label *label, struct task_struct *task,
+int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task,
unsigned int resource, struct rlimit *new_rlim)
{
- struct aa_profile *profile;
- struct aa_label *peer;
+ struct aa_profile *task_profile;
int error = 0;
rcu_read_lock();
- peer = aa_get_newest_cred_label(__task_cred(task));
+ task_profile = aa_get_profile(aa_cred_profile(__task_cred(task)));
rcu_read_unlock();
/* TODO: extend resource control to handle other (non current)
@@ -122,70 +99,53 @@ int aa_task_setrlimit(struct aa_label *label, struct task_struct *task,
* the same profile or that the task setting the resource of another
* task has CAP_SYS_RESOURCE.
*/
+ if ((profile != task_profile &&
+ aa_capable(profile, CAP_SYS_RESOURCE, 1)) ||
+ (profile->rlimits.mask & (1 << resource) &&
+ new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max))
+ error = -EACCES;
- if (label != peer &&
- !aa_capable(label, CAP_SYS_RESOURCE, SECURITY_CAP_NOAUDIT))
- error = fn_for_each(label, profile,
- audit_resource(profile, resource,
- new_rlim->rlim_max, peer,
- "cap_sys_resoure", -EACCES));
- else
- error = fn_for_each_confined(label, profile,
- profile_setrlimit(profile, resource, new_rlim));
- aa_put_label(peer);
-
- return error;
+ aa_put_profile(task_profile);
+
+ return audit_resource(profile, resource, new_rlim->rlim_max, error);
}
/**
* __aa_transition_rlimits - apply new profile rlimits
- * @old_l: old label on task (NOT NULL)
- * @new_l: new label with rlimits to apply (NOT NULL)
+ * @old: old profile on task (NOT NULL)
+ * @new: new profile with rlimits to apply (NOT NULL)
*/
-void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l)
+void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new)
{
unsigned int mask = 0;
struct rlimit *rlim, *initrlim;
- struct aa_profile *old, *new;
- struct label_it i;
-
- old = labels_profile(old_l);
- new = labels_profile(new_l);
+ int i;
- /* for any rlimits the profile controlled, reset the soft limit
- * to the lesser of the tasks hard limit and the init tasks soft limit
+ /* for any rlimits the profile controlled reset the soft limit
+ * to the less of the tasks hard limit and the init tasks soft limit
*/
- label_for_each_confined(i, old_l, old) {
- if (old->rlimits.mask) {
- int j;
-
- for (j = 0, mask = 1; j < RLIM_NLIMITS; j++,
- mask <<= 1) {
- if (old->rlimits.mask & mask) {
- rlim = current->signal->rlim + j;
- initrlim = init_task.signal->rlim + j;
- rlim->rlim_cur = min(rlim->rlim_max,
- initrlim->rlim_cur);
- }
+ if (old->rlimits.mask) {
+ for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) {
+ if (old->rlimits.mask & mask) {
+ rlim = current->signal->rlim + i;
+ initrlim = init_task.signal->rlim + i;
+ rlim->rlim_cur = min(rlim->rlim_max,
+ initrlim->rlim_cur);
}
}
}
/* set any new hard limits as dictated by the new profile */
- label_for_each_confined(i, new_l, new) {
- int j;
-
- if (!new->rlimits.mask)
+ if (!new->rlimits.mask)
+ return;
+ for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) {
+ if (!(new->rlimits.mask & mask))
continue;
- for (j = 0, mask = 1; j < RLIM_NLIMITS; j++, mask <<= 1) {
- if (!(new->rlimits.mask & mask))
- continue;
-
- rlim = current->signal->rlim + j;
- rlim->rlim_max = min(rlim->rlim_max,
- new->rlimits.limits[j].rlim_max);
- /* soft limit should not exceed hard limit */
- rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max);
- }
+
+ rlim = current->signal->rlim + i;
+ rlim->rlim_max = min(rlim->rlim_max,
+ new->rlimits.limits[i].rlim_max);
+ /* soft limit should not exceed hard limit */
+ rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max);
}
}
diff --git a/security/inode.c b/security/inode.c
index 8dd9ca8848e4..eccd58ef2ae8 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -26,31 +26,11 @@
static struct vfsmount *mount;
static int mount_count;
-static void securityfs_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 securityfs_super_operations = {
- .statfs = simple_statfs,
- .evict_inode = securityfs_evict_inode,
-};
-
static int fill_super(struct super_block *sb, void *data, int silent)
{
static const struct tree_descr files[] = {{""}};
- int error;
-
- error = simple_fill_super(sb, SECURITYFS_MAGIC, files);
- if (error)
- return error;
-
- sb->s_op = &securityfs_super_operations;
- return 0;
+ return simple_fill_super(sb, SECURITYFS_MAGIC, files);
}
static struct dentry *get_sb(struct file_system_type *fs_type,
@@ -68,7 +48,7 @@ static struct file_system_type fs_type = {
};
/**
- * securityfs_create_dentry - create a dentry in the securityfs filesystem
+ * securityfs_create_file - create a file in the securityfs filesystem
*
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
@@ -80,35 +60,34 @@ static struct file_system_type fs_type = {
* the open() call.
* @fops: a pointer to a struct file_operations that should be used for
* this file.
- * @iops: a point to a struct of inode_operations that should be used for
- * this file/dir
*
- * This is the basic "create a file/dir/symlink" function for
- * securityfs. It allows for a wide range of flexibility in creating
- * a file, or a directory (if you want to create a directory, the
- * securityfs_create_dir() function is recommended to be used
- * instead).
+ * This is the basic "create a file" function for securityfs. It allows for a
+ * wide range of flexibility in creating a file, or a directory (if you
+ * want to create a directory, the securityfs_create_dir() function is
+ * recommended to be used instead).
*
* This function returns a pointer to a dentry if it succeeds. This
- * pointer must be passed to the securityfs_remove() function when the
- * file is to be removed (no automatic cleanup happens if your module
- * is unloaded, you are responsible here). If an error occurs, the
- * function will return the error value (via ERR_PTR).
+ * pointer must be passed to the securityfs_remove() function when the file is
+ * to be removed (no automatic cleanup happens if your module is unloaded,
+ * you are responsible here). If an error occurs, the function will return
+ * the error value (via ERR_PTR).
*
* If securityfs is not enabled in the kernel, the value %-ENODEV is
* returned.
*/
-static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
- struct dentry *parent, void *data,
- const struct file_operations *fops,
- const struct inode_operations *iops)
+struct dentry *securityfs_create_file(const char *name, umode_t mode,
+ struct dentry *parent, void *data,
+ const struct file_operations *fops)
{
struct dentry *dentry;
+ int is_dir = S_ISDIR(mode);
struct inode *dir, *inode;
int error;
- if (!(mode & S_IFMT))
+ if (!is_dir) {
+ BUG_ON(!fops);
mode = (mode & S_IALLUGO) | S_IFREG;
+ }
pr_debug("securityfs: creating file '%s'\n",name);
@@ -141,14 +120,11 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
inode->i_mode = mode;
inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
inode->i_private = data;
- if (S_ISDIR(mode)) {
+ if (is_dir) {
inode->i_op = &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 = data;
} else {
inode->i_fop = fops;
}
@@ -165,38 +141,6 @@ out:
simple_release_fs(&mount, &mount_count);
return dentry;
}
-
-/**
- * securityfs_create_file - create a file in the securityfs filesystem
- *
- * @name: a pointer to a string containing the name of the file to create.
- * @mode: the permission that the file should have
- * @parent: a pointer to the parent dentry for this file. This should be a
- * directory dentry if set. If this parameter is %NULL, then the
- * file will be created in the root of the securityfs filesystem.
- * @data: a pointer to something that the caller will want to get to later
- * on. The inode.i_private pointer will point to this value on
- * the open() call.
- * @fops: a pointer to a struct file_operations that should be used for
- * this file.
- *
- * This function creates a file in securityfs with the given @name.
- *
- * This function returns a pointer to a dentry if it succeeds. This
- * pointer must be passed to the securityfs_remove() function when the file is
- * to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here). If an error occurs, the function will return
- * the error value (via ERR_PTR).
- *
- * If securityfs is not enabled in the kernel, the value %-ENODEV is
- * returned.
- */
-struct dentry *securityfs_create_file(const char *name, umode_t mode,
- struct dentry *parent, void *data,
- const struct file_operations *fops)
-{
- return securityfs_create_dentry(name, mode, parent, data, fops, NULL);
-}
EXPORT_SYMBOL_GPL(securityfs_create_file);
/**
@@ -221,59 +165,13 @@ EXPORT_SYMBOL_GPL(securityfs_create_file);
*/
struct dentry *securityfs_create_dir(const char *name, struct dentry *parent)
{
- return securityfs_create_file(name, S_IFDIR | 0755, parent, NULL, NULL);
+ return securityfs_create_file(name,
+ S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
+ parent, NULL, NULL);
}
EXPORT_SYMBOL_GPL(securityfs_create_dir);
/**
- * securityfs_create_symlink - create a symlink in the securityfs filesystem
- *
- * @name: a pointer to a string containing the name of the symlink to
- * create.
- * @parent: a pointer to the parent dentry for the symlink. This should be a
- * directory dentry if set. If this parameter is %NULL, then the
- * directory will be created in the root of the securityfs filesystem.
- * @target: a pointer to a string containing the name of the symlink's target.
- * If this parameter is %NULL, then the @iops parameter needs to be
- * setup to handle .readlink and .get_link inode_operations.
- * @iops: a pointer to the struct inode_operations to use for the symlink. If
- * this parameter is %NULL, then the default simple_symlink_inode
- * operations will be used.
- *
- * This function creates a symlink in securityfs with the given @name.
- *
- * This function returns a pointer to a dentry if it succeeds. This
- * pointer must be passed to the securityfs_remove() function when the file is
- * to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here). If an error occurs, the function will return
- * the error value (via ERR_PTR).
- *
- * If securityfs is not enabled in the kernel, the value %-ENODEV is
- * returned.
- */
-struct dentry *securityfs_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 = securityfs_create_dentry(name, S_IFLNK | 0444, parent,
- link, NULL, iops);
- if (IS_ERR(dent))
- kfree(link);
-
- return dent;
-}
-EXPORT_SYMBOL_GPL(securityfs_create_symlink);
-
-/**
* securityfs_remove - removes a file or directory from the securityfs filesystem
*
* @dentry: a pointer to a the dentry of the file or directory to be removed.