summaryrefslogtreecommitdiff |
diff options
author | Johannes Thumshirn <jthumshirn@suse.de> | 2018-09-14 16:41:07 +0200 |
---|---|---|
committer | Johannes Thumshirn <jthumshirn@suse.de> | 2018-09-14 16:41:07 +0200 |
commit | 596983f9886b236e3314d994667ba40319e61e49 (patch) | |
tree | 026cd541ef869eaba23aff6e6239682d982c7d68 | |
parent | e0e199a58f607aa5f111c2b95900564c67dda52e (diff) | |
parent | ff0b38137c8c279654b9aee2dd83cfb4addd53f1 (diff) |
Merge remote-tracking branch 'origin/users/rgoldwyn/SLE12-SP4/for-next' into SLE12-SP4rpm-4.12.14-94.37--SLE-12-SP4-Server-RC2rpm-4.12.14-94.37--SLE-12-SP4-Server-RC1rpm-4.12.14-94.37--SLE-12-SP4-SAP-RC2rpm-4.12.14-94.37--SLE-12-SP4-SAP-RC1rpm-4.12.14-94.37--SLE-12-SP4-HPC-RC1rpm-4.12.14-94.37--SLE-12-SP4-Desktop-RC1rpm-4.12.14-94.37
Pull AppArmor updates from Goldwyn Rodrigues.
Conflicts:
series.conf
suse-commit: 07d5db05cd6058a13afaa801d110a4206abdd0a5
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, ¶ms)) { - info = "failed to init key, value hash table"; + if (rhashtable_init(profile->data, ¶ms)) 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. |