summaryrefslogtreecommitdiff
path: root/security/apparmor
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-04-14 01:38:53 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2018-04-14 01:38:53 +0300
commit80a17a5f501ea048d86f81d629c94062b76610d4 (patch)
tree55dd0a1490d1e6631ca319a1205f974f041b494f /security/apparmor
parentedda415314804c29fa07e538938fa07947012d8f (diff)
parent588558eb6d0e0b6edfa65a67e906c2ffeba63ff1 (diff)
downloadlinux-80a17a5f501ea048d86f81d629c94062b76610d4.tar.xz
Merge tag 'apparmor-pr-2018-04-10' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor
Pull apparmor updates from John Johansen: "Features: - add base infrastructure for socket mediation. ABI bump and additional checks to ensure only v8 compliant policy uses socket af mediation. - improve and cleanup dfa verification - improve profile attachment logic - improve overlapping expression handling - add the xattr matching to the attachment logic - improve signal mediation handling with stacked labels - improve handling of no_new_privs in a label stack Cleanups and changes: - use dfa to parse string split - bounded version of label_parse - proper line wrap nulldfa.in - split context out into task and cred naming to better match usage - simplify code in aafs Bug fixes: - fix display of .ns_name for containers - fix resource audit messages when auditing peer - fix logging of the existence test for signals - fix resource audit messages when auditing peer - fix display of .ns_name for containers - fix an error code in verify_table_headers() - fix memory leak on buffer on error exit path - fix error returns checks by making size a ssize_t" * tag 'apparmor-pr-2018-04-10' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: (36 commits) apparmor: fix memory leak on buffer on error exit path apparmor: fix dangling symlinks to policy rawdata after replacement apparmor: Fix an error code in verify_table_headers() apparmor: fix error returns checks by making size a ssize_t apparmor: update MAINTAINERS file git and wiki locations apparmor: remove POLICY_MEDIATES_SAFE apparmor: add base infastructure for socket mediation apparmor: improve overlapping domain attachment resolution apparmor: convert attaching profiles via xattrs to use dfa matching apparmor: Add support for attaching profiles via xattr, presence and value apparmor: cleanup: simplify code to get ns symlink name apparmor: cleanup create_aafs() error path apparmor: dfa split verification of table headers apparmor: dfa add support for state differential encoding apparmor: dfa move character match into a macro apparmor: update domain transitions that are subsets of confinement at nnp apparmor: move context.h to cred.h apparmor: move task related defines and fns to task.X files apparmor: cleanup, drop unused fn __aa_task_is_confined() apparmor: cleanup fixup description of aa_replace_profiles ...
Diffstat (limited to 'security/apparmor')
-rw-r--r--security/apparmor/.gitignore1
-rw-r--r--security/apparmor/Makefile45
-rw-r--r--security/apparmor/apparmorfs.c203
-rw-r--r--security/apparmor/capability.c2
-rw-r--r--security/apparmor/domain.c355
-rw-r--r--security/apparmor/file.c32
-rw-r--r--security/apparmor/include/apparmor.h3
-rw-r--r--security/apparmor/include/audit.h19
-rw-r--r--security/apparmor/include/cred.h (renamed from security/apparmor/include/context.h)63
-rw-r--r--security/apparmor/include/label.h28
-rw-r--r--security/apparmor/include/match.h28
-rw-r--r--security/apparmor/include/net.h106
-rw-r--r--security/apparmor/include/perms.h5
-rw-r--r--security/apparmor/include/policy.h23
-rw-r--r--security/apparmor/include/policy_unpack.h2
-rw-r--r--security/apparmor/include/sig_names.h5
-rw-r--r--security/apparmor/include/task.h94
-rw-r--r--security/apparmor/ipc.c52
-rw-r--r--security/apparmor/label.c42
-rw-r--r--security/apparmor/lib.c5
-rw-r--r--security/apparmor/lsm.c467
-rw-r--r--security/apparmor/match.c423
-rw-r--r--security/apparmor/mount.c2
-rw-r--r--security/apparmor/net.c187
-rw-r--r--security/apparmor/nulldfa.in108
-rw-r--r--security/apparmor/policy.c11
-rw-r--r--security/apparmor/policy_ns.c2
-rw-r--r--security/apparmor/policy_unpack.c70
-rw-r--r--security/apparmor/procattr.c2
-rw-r--r--security/apparmor/resource.c2
-rw-r--r--security/apparmor/stacksplitdfa.in114
-rw-r--r--security/apparmor/task.c (renamed from security/apparmor/context.c)139
32 files changed, 2117 insertions, 523 deletions
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore
index 9cdec70d72b8..d5b291e94264 100644
--- a/security/apparmor/.gitignore
+++ b/security/apparmor/.gitignore
@@ -1,5 +1,6 @@
#
# Generated include files
#
+net_names.h
capability_names.h
rlim_names.h
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index 9a6b4033d52b..ff23fcfefe19 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -3,13 +3,46 @@
#
obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
-apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
+apparmor-y := apparmorfs.o audit.o capability.o task.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
+ resource.o secid.o file.o policy_ns.o label.o mount.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 address family names
+# Transform lines from
+# #define AF_LOCAL 1 /* POSIX name for AF_UNIX */
+# #define AF_INET 2 /* Internet IP Protocol */
+# to
+# [1] = "local",
+# [2] = "inet",
+#
+# and build the securityfs entries for the mapping.
+# Transforms lines from
+# #define AF_INET 2 /* Internet IP Protocol */
+# to
+# #define AA_SFS_AF_MASK "local inet"
+quiet_cmd_make-af = GEN $@
+cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
+ sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \
+ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
+ echo "};" >> $@ ;\
+ printf '%s' '\#define AA_SFS_AF_MASK "' >> $@ ;\
+ sed -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \
+ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\
+ $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
+
+# Build a lower case string table of sock type names
+# Transform lines from
+# SOCK_STREAM = 1,
+# to
+# [1] = "stream",
+quiet_cmd_make-sock = GEN $@
+cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\
+ sed $^ >>$@ -r -n \
+ -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
+ echo "};" >> $@
# Build a lower case string table of capability names
# Transforms lines from
@@ -62,6 +95,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \
tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
$(obj)/capability.o : $(obj)/capability_names.h
+$(obj)/net.o : $(obj)/net_names.h
$(obj)/resource.o : $(obj)/rlim_names.h
$(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
$(src)/Makefile
@@ -69,3 +103,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
$(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \
$(src)/Makefile
$(call cmd,make-rlim)
+$(obj)/net_names.h : $(srctree)/include/linux/socket.h \
+ $(srctree)/include/linux/net.h \
+ $(src)/Makefile
+ $(call cmd,make-af)
+ $(call cmd,make-sock)
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index a9428daa69f3..949dd8a48164 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -30,10 +30,9 @@
#include "include/apparmor.h"
#include "include/apparmorfs.h"
#include "include/audit.h"
-#include "include/context.h"
+#include "include/cred.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"
@@ -120,9 +119,7 @@ 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);
+ seq_printf(seq, "%s:[%lu]", AAFS_NAME, d_inode(dentry)->i_ino);
return 0;
}
@@ -313,6 +310,7 @@ static struct dentry *aafs_create_dir(const char *name, struct dentry *parent)
* @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
@@ -321,17 +319,17 @@ static struct dentry *aafs_create_dir(const char *name, struct dentry *parent)
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) {
- link = kstrdup(target, GFP_KERNEL);
if (!link)
return ERR_PTR(-ENOMEM);
}
- dent = aafs_create(name, S_IFLNK | 0444, parent, NULL, link, NULL,
+ dent = aafs_create(name, S_IFLNK | 0444, parent, private, link, NULL,
iops);
if (IS_ERR(dent))
kfree(link);
@@ -622,7 +620,7 @@ static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
tmp = aa_compute_fperms(dfa, state, &cond);
}
} else if (profile->policy.dfa) {
- if (!PROFILE_MEDIATES_SAFE(profile, *match_str))
+ if (!PROFILE_MEDIATES(profile, *match_str))
return; /* no change to current perms */
dfa = profile->policy.dfa;
state = aa_dfa_match_len(dfa, profile->policy.start[0],
@@ -1189,9 +1187,7 @@ static int seq_ns_level_show(struct seq_file *seq, void *v)
static int seq_ns_name_show(struct seq_file *seq, void *v)
{
struct aa_label *label = begin_current_label_crit_section();
-
- seq_printf(seq, "%s\n", aa_ns_name(labels_ns(label),
- labels_ns(label), true));
+ seq_printf(seq, "%s\n", labels_ns(label)->base.name);
end_current_label_crit_section(label);
return 0;
@@ -1484,26 +1480,97 @@ static int profile_depth(struct aa_profile *profile)
return depth;
}
-static int gen_symlink_name(char *buffer, size_t bsize, int depth,
- const char *dirname, const char *fname)
+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--) {
- if (bsize < 7)
- return -ENAMETOOLONG;
- strcpy(buffer, "../../");
- buffer += 6;
- bsize -= 6;
+ strcpy(s, "../../");
+ s += 6;
+ size -= 6;
}
- error = snprintf(buffer, bsize, "raw_data/%s/%s", dirname, fname);
- if (error >= bsize || error < 0)
- return -ENAMETOOLONG;
+ error = snprintf(s, size, "raw_data/%s/%s", dirname, fname);
+ if (error >= size || error < 0) {
+ kfree(buffer);
+ return ERR_PTR(-ENAMETOOLONG);
+ }
- return 0;
+ 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
*/
@@ -1574,34 +1641,28 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
}
if (profile->rawdata) {
- char target[64];
- int depth = profile_depth(profile);
-
- error = gen_symlink_name(target, sizeof(target), depth,
- profile->rawdata->name, "sha1");
- if (error < 0)
- goto fail2;
- dent = aafs_create_symlink("raw_sha1", dir, target, NULL);
+ dent = aafs_create_symlink("raw_sha1", dir, NULL,
+ profile->label.proxy,
+ &rawdata_link_sha1_iops);
if (IS_ERR(dent))
goto fail;
+ aa_get_proxy(profile->label.proxy);
profile->dents[AAFS_PROF_RAW_HASH] = dent;
- error = gen_symlink_name(target, sizeof(target), depth,
- profile->rawdata->name, "abi");
- if (error < 0)
- goto fail2;
- dent = aafs_create_symlink("raw_abi", dir, target, NULL);
+ dent = aafs_create_symlink("raw_abi", dir, NULL,
+ profile->label.proxy,
+ &rawdata_link_abi_iops);
if (IS_ERR(dent))
goto fail;
+ aa_get_proxy(profile->label.proxy);
profile->dents[AAFS_PROF_RAW_ABI] = dent;
- error = gen_symlink_name(target, sizeof(target), depth,
- profile->rawdata->name, "raw_data");
- if (error < 0)
- goto fail2;
- dent = aafs_create_symlink("raw_data", dir, target, NULL);
+ dent = aafs_create_symlink("raw_data", dir, NULL,
+ profile->label.proxy,
+ &rawdata_link_data_iops);
if (IS_ERR(dent))
goto fail;
+ aa_get_proxy(profile->label.proxy);
profile->dents[AAFS_PROF_RAW_DATA] = dent;
}
@@ -2152,6 +2213,10 @@ static struct aa_sfs_entry aa_sfs_entry_signal[] = {
{ }
};
+static struct aa_sfs_entry aa_sfs_entry_attach[] = {
+ AA_SFS_FILE_BOOLEAN("xattr", 1),
+ { }
+};
static struct aa_sfs_entry aa_sfs_entry_domain[] = {
AA_SFS_FILE_BOOLEAN("change_hat", 1),
AA_SFS_FILE_BOOLEAN("change_hatv", 1),
@@ -2159,6 +2224,9 @@ static struct aa_sfs_entry aa_sfs_entry_domain[] = {
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_BOOLEAN("post_nnp_subset", 1),
+ AA_SFS_FILE_BOOLEAN("computed_longest_left", 1),
+ AA_SFS_DIR("attach_conditions", aa_sfs_entry_attach),
AA_SFS_FILE_STRING("version", "1.2"),
{ }
};
@@ -2167,6 +2235,7 @@ 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),
+ AA_SFS_FILE_BOOLEAN("v8", 1),
{ }
};
@@ -2202,6 +2271,7 @@ 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_v8", 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),
@@ -2394,29 +2464,18 @@ static const char *policy_get_link(struct dentry *dentry,
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 = snprintf(name, sizeof(name), "%s:[%lu]", AAFS_NAME,
+ d_inode(dentry)->i_ino);
+ if (res > 0 && res < sizeof(name))
res = readlink_copy(buffer, buflen, name);
- aa_put_ns(ns);
+ else
+ res = -ENOENT;
return res;
}
@@ -2460,34 +2519,26 @@ static int __init aa_create_aafs(void)
dent = securityfs_create_file(".load", 0666, aa_sfs_entry.dentry,
NULL, &aa_fs_profile_load);
- if (IS_ERR(dent)) {
- error = PTR_ERR(dent);
- goto error;
- }
+ if (IS_ERR(dent))
+ goto dent_error;
ns_subload(root_ns) = dent;
dent = securityfs_create_file(".replace", 0666, aa_sfs_entry.dentry,
NULL, &aa_fs_profile_replace);
- if (IS_ERR(dent)) {
- error = PTR_ERR(dent);
- goto error;
- }
+ if (IS_ERR(dent))
+ goto dent_error;
ns_subreplace(root_ns) = dent;
dent = securityfs_create_file(".remove", 0666, aa_sfs_entry.dentry,
NULL, &aa_fs_profile_remove);
- if (IS_ERR(dent)) {
- error = PTR_ERR(dent);
- goto error;
- }
+ if (IS_ERR(dent))
+ goto dent_error;
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;
- }
+ if (IS_ERR(dent))
+ goto dent_error;
ns_subrevision(root_ns) = dent;
/* policy tree referenced by magic policy symlink */
@@ -2501,10 +2552,8 @@ static int __init aa_create_aafs(void)
/* magic symlink similar to nsfs redirects based on task policy */
dent = securityfs_create_symlink("policy", aa_sfs_entry.dentry,
NULL, &policy_link_iops);
- if (IS_ERR(dent)) {
- error = PTR_ERR(dent);
- goto error;
- }
+ if (IS_ERR(dent))
+ goto dent_error;
error = aa_mk_null_file(aa_sfs_entry.dentry);
if (error)
@@ -2516,6 +2565,8 @@ static int __init aa_create_aafs(void)
aa_info_message("AppArmor Filesystem Enabled");
return 0;
+dent_error:
+ error = PTR_ERR(dent);
error:
aa_destroy_aafs();
AA_ERROR("Error creating AppArmor securityfs\n");
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c
index 67e347192a55..253ef6e9d445 100644
--- a/security/apparmor/capability.c
+++ b/security/apparmor/capability.c
@@ -19,7 +19,7 @@
#include "include/apparmor.h"
#include "include/capability.h"
-#include "include/context.h"
+#include "include/cred.h"
#include "include/policy.h"
#include "include/audit.h"
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 6a54d2ffa840..590b7e8cd21c 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -19,10 +19,11 @@
#include <linux/syscalls.h>
#include <linux/tracehook.h>
#include <linux/personality.h>
+#include <linux/xattr.h>
#include "include/audit.h"
#include "include/apparmorfs.h"
-#include "include/context.h"
+#include "include/cred.h"
#include "include/domain.h"
#include "include/file.h"
#include "include/ipc.h"
@@ -302,7 +303,70 @@ static int change_profile_perms(struct aa_profile *profile,
}
/**
+ * aa_xattrs_match - check whether a file matches the xattrs defined in profile
+ * @bprm: binprm struct for the process to validate
+ * @profile: profile to match against (NOT NULL)
+ * @state: state to start match in
+ *
+ * Returns: number of extended attributes that matched, or < 0 on error
+ */
+static int aa_xattrs_match(const struct linux_binprm *bprm,
+ struct aa_profile *profile, unsigned int state)
+{
+ int i;
+ ssize_t size;
+ struct dentry *d;
+ char *value = NULL;
+ int value_size = 0, ret = profile->xattr_count;
+
+ if (!bprm || !profile->xattr_count)
+ return 0;
+
+ /* transition from exec match to xattr set */
+ state = aa_dfa_null_transition(profile->xmatch, state);
+
+ d = bprm->file->f_path.dentry;
+
+ for (i = 0; i < profile->xattr_count; i++) {
+ size = vfs_getxattr_alloc(d, profile->xattrs[i], &value,
+ value_size, GFP_KERNEL);
+ if (size >= 0) {
+ u32 perm;
+
+ /* Check the xattr value, not just presence */
+ state = aa_dfa_match_len(profile->xmatch, state, value,
+ size);
+ perm = dfa_user_allow(profile->xmatch, state);
+ if (!(perm & MAY_EXEC)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+ /* transition to next element */
+ state = aa_dfa_null_transition(profile->xmatch, state);
+ if (size < 0) {
+ /*
+ * No xattr match, so verify if transition to
+ * next element was valid. IFF so the xattr
+ * was optional.
+ */
+ if (!state) {
+ ret = -EINVAL;
+ goto out;
+ }
+ /* don't count missing optional xattr as matched */
+ ret--;
+ }
+ }
+
+out:
+ kfree(value);
+ return ret;
+}
+
+/**
* __attach_match_ - find an attachment match
+ * @bprm - binprm structure of transitioning task
* @name - to match against (NOT NULL)
* @head - profile list to walk (NOT NULL)
* @info - info message if there was an error (NOT NULL)
@@ -316,40 +380,80 @@ 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,
+static struct aa_profile *__attach_match(const struct linux_binprm *bprm,
+ const char *name,
struct list_head *head,
const char **info)
{
- int len = 0;
+ int candidate_len = 0, candidate_xattrs = 0;
bool conflict = false;
struct aa_profile *profile, *candidate = NULL;
+ AA_BUG(!name);
+ AA_BUG(!head);
+
list_for_each_entry_rcu(profile, head, base.list) {
if (profile->label.flags & FLAG_NULL &&
&profile->label == ns_unconfined(profile->ns))
continue;
+ /* Find the "best" matching profile. Profiles must
+ * match the path and extended attributes (if any)
+ * associated with the file. A more specific path
+ * match will be preferred over a less specific one,
+ * and a match with more matching extended attributes
+ * will be preferred over one with fewer. If the best
+ * match has both the same level of path specificity
+ * and the same number of matching extended attributes
+ * as another profile, signal a conflict and refuse to
+ * match.
+ */
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) {
+ unsigned int state, count;
+ u32 perm;
+
+ state = aa_dfa_leftmatch(profile->xmatch, DFA_START,
+ name, &count);
+ perm = dfa_user_allow(profile->xmatch, state);
+ /* any accepting state means a valid match. */
+ if (perm & MAY_EXEC) {
+ int ret;
+
+ if (count < candidate_len)
+ continue;
+
+ ret = aa_xattrs_match(bprm, profile, state);
+ /* Fail matching if the xattrs don't match */
+ if (ret < 0)
+ continue;
+
+ /*
+ * TODO: allow for more flexible best match
+ *
+ * The new match isn't more specific
+ * than the current best match
+ */
+ if (count == candidate_len &&
+ ret <= candidate_xattrs) {
+ /* Match is equivalent, so conflict */
+ if (ret == candidate_xattrs)
conflict = true;
- continue;
- }
- candidate = profile;
- len = profile->xmatch_len;
- conflict = false;
+ continue;
}
+
+ /* Either the same length with more matching
+ * xattrs, or a longer match
+ */
+ candidate = profile;
+ candidate_len = profile->xmatch_len;
+ candidate_xattrs = ret;
+ conflict = false;
}
} else if (!strcmp(profile->base.name, name))
- /* exact non-re match, no more searching required */
+ /*
+ * old exact non-re match, without conditionals such
+ * as xattrs. no more searching required
+ */
return profile;
}
@@ -363,6 +467,7 @@ static struct aa_profile *__attach_match(const char *name,
/**
* find_attach - do attachment search for unconfined processes
+ * @bprm - binprm structure of transitioning task
* @ns: the current namespace (NOT NULL)
* @list: list to search (NOT NULL)
* @name: the executable name to match against (NOT NULL)
@@ -370,13 +475,14 @@ static struct aa_profile *__attach_match(const char *name,
*
* Returns: label or NULL if no match found
*/
-static struct aa_label *find_attach(struct aa_ns *ns, struct list_head *list,
+static struct aa_label *find_attach(const struct linux_binprm *bprm,
+ struct aa_ns *ns, struct list_head *list,
const char *name, const char **info)
{
struct aa_profile *profile;
rcu_read_lock();
- profile = aa_get_profile(__attach_match(name, list, info));
+ profile = aa_get_profile(__attach_match(bprm, name, list, info));
rcu_read_unlock();
return profile ? &profile->label : NULL;
@@ -432,6 +538,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
/**
* x_to_label - get target label for a given xindex
* @profile: current profile (NOT NULL)
+ * @bprm: binprm structure of transitioning task
* @name: name to lookup (NOT NULL)
* @xindex: index into x transition table
* @lookupname: returns: name used in lookup if one was specified (NOT NULL)
@@ -441,6 +548,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
* Returns: refcounted label or NULL if not found available
*/
static struct aa_label *x_to_label(struct aa_profile *profile,
+ const struct linux_binprm *bprm,
const char *name, u32 xindex,
const char **lookupname,
const char **info)
@@ -468,11 +576,11 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
case AA_X_NAME:
if (xindex & AA_X_CHILD)
/* released by caller */
- new = find_attach(ns, &profile->base.profiles,
+ new = find_attach(bprm, ns, &profile->base.profiles,
name, info);
else
/* released by caller */
- new = find_attach(ns, &ns->base.profiles,
+ new = find_attach(bprm, ns, &ns->base.profiles,
name, info);
*lookupname = name;
break;
@@ -512,6 +620,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
bool *secure_exec)
{
struct aa_label *new = NULL;
+ struct aa_profile *component;
+ struct label_it i;
const char *info = NULL, *name = NULL, *target = NULL;
unsigned int state = profile->file.start;
struct aa_perms perms = {};
@@ -536,8 +646,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
}
if (profile_unconfined(profile)) {
- new = find_attach(profile->ns, &profile->ns->base.profiles,
- name, &info);
+ new = find_attach(bprm, profile->ns,
+ &profile->ns->base.profiles, name, &info);
if (new) {
AA_DEBUG("unconfined attached to new label");
return new;
@@ -550,7 +660,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
state = aa_str_perms(profile->file.dfa, state, name, cond, &perms);
if (perms.allow & MAY_EXEC) {
/* exec permission determine how to transition */
- new = x_to_label(profile, name, perms.xindex, &target, &info);
+ new = x_to_label(profile, bprm, name, perms.xindex, &target,
+ &info);
if (new && new->proxy == profile->label.proxy && info) {
/* hack ix fallback - improve how this is detected */
goto audit;
@@ -559,6 +670,21 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
info = "profile transition not found";
/* remove MAY_EXEC to audit as failure */
perms.allow &= ~MAY_EXEC;
+ } else {
+ /* verify that each component's xattr requirements are
+ * met, and fail execution otherwise
+ */
+ label_for_each(i, new, component) {
+ if (aa_xattrs_match(bprm, component, state) <
+ 0) {
+ error = -EACCES;
+ info = "required xattrs not present";
+ perms.allow &= ~MAY_EXEC;
+ aa_put_label(new);
+ new = NULL;
+ goto audit;
+ }
+ }
}
} else if (COMPLAIN_MODE(profile)) {
/* no exec permission - learning mode */
@@ -592,22 +718,6 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
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.
- */
- if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) &&
- !profile_unconfined(profile) &&
- !aa_label_is_subset(new, &profile->label)) {
- error = -EPERM;
- info = "no new privs";
- nonewprivs = true;
- perms.allow &= ~MAY_EXEC;
- goto audit;
- }
if (!(perms.xindex & AA_X_UNSAFE)) {
if (DEBUG_ON) {
@@ -684,21 +794,6 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
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) {
@@ -794,10 +889,22 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
if (bprm->called_set_creds)
return 0;
- ctx = cred_ctx(bprm->cred);
+ ctx = task_ctx(current);
+ AA_BUG(!cred_label(bprm->cred));
AA_BUG(!ctx);
- label = aa_get_newest_label(ctx->label);
+ label = aa_get_newest_label(cred_label(bprm->cred));
+
+ /*
+ * Detect no new privs being set, and store the label it
+ * occurred under. Ideally this would happen when nnp
+ * is set but there isn't a good way to do that yet.
+ *
+ * Testing for unconfined must be done before the subset test
+ */
+ if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && !unconfined(label) &&
+ !ctx->nnp)
+ ctx->nnp = aa_get_label(label);
/* buffer freed below, name is pointer into buffer */
get_buffers(buffer);
@@ -819,7 +926,20 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
goto done;
}
- /* TODO: Add ns level no_new_privs subset test */
+ /* Policy has specified a domain transitions. If no_new_privs and
+ * confined ensure the transition is to confinement that is subset
+ * of the confinement when the task entered no new privs.
+ *
+ * NOTE: Domain transitions from unconfined and to 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) &&
+ !unconfined(label) && !aa_label_is_subset(new, ctx->nnp)) {
+ error = -EPERM;
+ info = "no new privs";
+ goto audit;
+ }
if (bprm->unsafe & LSM_UNSAFE_SHARE) {
/* FIXME: currently don't mediate shared state */
@@ -853,14 +973,11 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
}
bprm->per_clear |= PER_CLEAR_ON_SETID;
}
- aa_put_label(ctx->label);
- /* transfer reference, released when ctx is freed */
- ctx->label = new;
+ aa_put_label(cred_label(bprm->cred));
+ /* transfer reference, released when cred is freed */
+ cred_label(bprm->cred) = new;
done:
- /* clear out temporary/transitional state from the context */
- aa_clear_task_ctx_trans(ctx);
-
aa_put_label(label);
put_buffers(buffer);
@@ -1049,30 +1166,28 @@ build:
int aa_change_hat(const char *hats[], int count, u64 token, int flags)
{
const struct cred *cred;
- struct aa_task_ctx *ctx;
+ struct aa_task_ctx *ctx = task_ctx(current);
struct aa_label *label, *previous, *new = NULL, *target = NULL;
struct aa_profile *profile;
struct aa_perms perms = {};
const char *info = NULL;
int error = 0;
- /*
- * Fail explicitly requested domain transitions if no_new_privs.
- * 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");
- 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);
+ /*
+ * Detect no new privs being set, and store the label it
+ * occurred under. Ideally this would happen when nnp
+ * is set but there isn't a good way to do that yet.
+ *
+ * Testing for unconfined must be done before the subset test
+ */
+ if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp)
+ ctx->nnp = aa_get_label(label);
+
if (unconfined(label)) {
info = "unconfined can not change_hat";
error = -EPERM;
@@ -1093,6 +1208,18 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
if (error)
goto fail;
+ /*
+ * no new privs prevents domain transitions that would
+ * reduce restrictions.
+ */
+ if (task_no_new_privs(current) && !unconfined(label) &&
+ !aa_label_is_subset(new, ctx->nnp)) {
+ /* not an apparmor denial per se, so don't log it */
+ AA_DEBUG("no_new_privs - change_hat denied");
+ error = -EPERM;
+ goto out;
+ }
+
if (flags & AA_CHANGE_TEST)
goto out;
@@ -1102,6 +1229,18 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
/* kill task in case of brute force attacks */
goto kill;
} else if (previous && !(flags & AA_CHANGE_TEST)) {
+ /*
+ * no new privs prevents domain transitions that would
+ * reduce restrictions.
+ */
+ if (task_no_new_privs(current) && !unconfined(label) &&
+ !aa_label_is_subset(previous, ctx->nnp)) {
+ /* not an apparmor denial per se, so don't log it */
+ AA_DEBUG("no_new_privs - change_hat denied");
+ error = -EPERM;
+ goto out;
+ }
+
/* Return to saved label. Kill task if restore fails
* to avoid brute force attacks
*/
@@ -1144,21 +1283,6 @@ static int change_profile_perms_wrapper(const char *op, const char *name,
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);
@@ -1192,10 +1316,23 @@ int aa_change_profile(const char *fqname, int flags)
const char *info = NULL;
const char *auditname = fqname; /* retain leading & if stack */
bool stack = flags & AA_CHANGE_STACK;
+ struct aa_task_ctx *ctx = task_ctx(current);
int error = 0;
char *op;
u32 request;
+ label = aa_get_current_label();
+
+ /*
+ * Detect no new privs being set, and store the label it
+ * occurred under. Ideally this would happen when nnp
+ * is set but there isn't a good way to do that yet.
+ *
+ * Testing for unconfined must be done before the subset test
+ */
+ if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp)
+ ctx->nnp = aa_get_label(label);
+
if (!fqname || !*fqname) {
AA_DEBUG("no profile name");
return -EINVAL;
@@ -1283,14 +1420,28 @@ check:
if (flags & AA_CHANGE_TEST)
goto out;
+ /* stacking is always a subset, so only check the nonstack case */
+ if (!stack) {
+ new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
+ aa_get_label(target),
+ aa_get_label(&profile->label));
+ /*
+ * no new privs prevents domain transitions that would
+ * reduce restrictions.
+ */
+ if (task_no_new_privs(current) && !unconfined(label) &&
+ !aa_label_is_subset(new, ctx->nnp)) {
+ /* not an apparmor denial per se, so don't log it */
+ AA_DEBUG("no_new_privs - change_hat denied");
+ error = -EPERM;
+ goto out;
+ }
+ }
+
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);
@@ -1299,9 +1450,15 @@ check:
goto audit;
}
error = aa_replace_current_label(new);
- } else
+ } else {
+ if (new) {
+ aa_put_label(new);
+ new = NULL;
+ }
+
/* full transition will be built in exec path */
error = aa_set_current_onexec(target, stack);
+ }
audit:
error = fn_for_each_in_ns(label, profile,
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index e79bf44396a3..224b2fef93ca 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -18,9 +18,10 @@
#include "include/apparmor.h"
#include "include/audit.h"
-#include "include/context.h"
+#include "include/cred.h"
#include "include/file.h"
#include "include/match.h"
+#include "include/net.h"
#include "include/path.h"
#include "include/policy.h"
#include "include/label.h"
@@ -560,6 +561,32 @@ static int __file_path_perm(const char *op, struct aa_label *label,
return error;
}
+static int __file_sock_perm(const char *op, struct aa_label *label,
+ struct aa_label *flabel, struct file *file,
+ u32 request, u32 denied)
+{
+ struct socket *sock = (struct socket *) file->private_data;
+ int error;
+
+ AA_BUG(!sock);
+
+ /* revalidation due to label out of date. No revocation at this time */
+ if (!denied && aa_label_is_subset(flabel, label))
+ return 0;
+
+ /* TODO: improve to skip profiles cached in flabel */
+ error = aa_sock_file_perm(label, op, request, sock);
+ if (denied) {
+ /* TODO: improve to skip profiles checked above */
+ /* check every profile in file label to is cached */
+ last_error(error, aa_sock_file_perm(flabel, op, request, sock));
+ }
+ if (!error)
+ update_file_ctx(file_ctx(file), label, request);
+
+ return error;
+}
+
/**
* aa_file_perm - do permission revalidation check & audit for @file
* @op: operation being checked
@@ -604,6 +631,9 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
error = __file_path_perm(op, label, flabel, file, request,
denied);
+ else if (S_ISSOCK(file_inode(file)->i_mode))
+ error = __file_sock_perm(op, label, flabel, file, request,
+ denied);
done:
rcu_read_unlock();
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
index 829082c35faa..73d63b58d875 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -24,12 +24,13 @@
#define AA_CLASS_UNKNOWN 1
#define AA_CLASS_FILE 2
#define AA_CLASS_CAP 3
-#define AA_CLASS_NET 4
+#define AA_CLASS_DEPRECATED 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_NET 14
#define AA_CLASS_LABEL 16
#define AA_CLASS_LAST AA_CLASS_LABEL
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
index 4ac095118717..9c9be9c98c15 100644
--- a/security/apparmor/include/audit.h
+++ b/security/apparmor/include/audit.h
@@ -126,7 +126,20 @@ struct apparmor_audit_data {
const char *target;
kuid_t ouid;
} fs;
- int signal;
+ struct {
+ int rlim;
+ unsigned long max;
+ } rlim;
+ struct {
+ int signal;
+ int unmappedsig;
+ };
+ struct {
+ int type, protocol;
+ struct sock *peer_sk;
+ void *addr;
+ int addrlen;
+ } net;
};
};
struct {
@@ -135,10 +148,6 @@ struct apparmor_audit_data {
long pos;
} iface;
struct {
- int rlim;
- unsigned long max;
- } rlim;
- struct {
const char *src_name;
const char *type;
const char *trans;
diff --git a/security/apparmor/include/context.h b/security/apparmor/include/cred.h
index 6ae07e9aaa17..e287b7d0d4be 100644
--- a/security/apparmor/include/context.h
+++ b/security/apparmor/include/cred.h
@@ -21,38 +21,9 @@
#include "label.h"
#include "policy_ns.h"
+#include "task.h"
-#define cred_ctx(X) ((X)->security)
-#define current_ctx() cred_ctx(current_cred())
-
-/**
- * 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
- *
- * Contains the task's current label (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;
- u64 token;
-};
-
-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);
+#define cred_label(X) ((X)->security)
/**
@@ -65,10 +36,10 @@ struct aa_label *aa_get_task_label(struct task_struct *task);
*/
static inline struct aa_label *aa_cred_raw_label(const struct cred *cred)
{
- struct aa_task_ctx *ctx = cred_ctx(cred);
+ struct aa_label *label = cred_label(cred);
- AA_BUG(!ctx || !ctx->label);
- return ctx->label;
+ AA_BUG(!label);
+ return label;
}
/**
@@ -96,17 +67,6 @@ static inline struct aa_label *__aa_task_raw_label(struct task_struct *task)
}
/**
- * __aa_task_is_confined - determine if @task has any confinement
- * @task: task to check confinement of (NOT NULL)
- *
- * If @task != current needs to be called in RCU safe critical section
- */
-static inline bool __aa_task_is_confined(struct task_struct *task)
-{
- return !unconfined(__aa_task_raw_label(task));
-}
-
-/**
* aa_current_raw_label - find the current tasks confining label
*
* Returns: up to date confining label or the ns unconfined label (NOT NULL)
@@ -213,17 +173,4 @@ static inline struct aa_ns *aa_get_current_ns(void)
return ns;
}
-/**
- * aa_clear_task_ctx_trans - clear transition tracking info from the ctx
- * @ctx: task context to clear (NOT NULL)
- */
-static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx)
-{
- aa_put_label(ctx->previous);
- aa_put_label(ctx->onexec);
- ctx->previous = NULL;
- ctx->onexec = NULL;
- ctx->token = 0;
-}
-
#endif /* __AA_CONTEXT_H */
diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h
index af22dcbbcb8a..d871e7ff0952 100644
--- a/security/apparmor/include/label.h
+++ b/security/apparmor/include/label.h
@@ -327,9 +327,37 @@ 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_strn_parse(struct aa_label *base, const char *str,
+ size_t n, gfp_t gfp, bool create,
+ bool force_stack);
struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
gfp_t gfp, bool create, bool force_stack);
+static inline const char *aa_label_strn_split(const char *str, int n)
+{
+ const char *pos;
+ unsigned int state;
+
+ state = aa_dfa_matchn_until(stacksplitdfa, DFA_START, str, n, &pos);
+ if (!ACCEPT_TABLE(stacksplitdfa)[state])
+ return NULL;
+
+ return pos - 3;
+}
+
+static inline const char *aa_label_str_split(const char *str)
+{
+ const char *pos;
+ unsigned int state;
+
+ state = aa_dfa_match_until(stacksplitdfa, DFA_START, str, &pos);
+ if (!ACCEPT_TABLE(stacksplitdfa)[state])
+ return NULL;
+
+ return pos - 3;
+}
+
+
struct aa_perms;
int aa_label_match(struct aa_profile *profile, struct aa_label *label,
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h
index add4c6726558..958d2b52a7b7 100644
--- a/security/apparmor/include/match.h
+++ b/security/apparmor/include/match.h
@@ -40,6 +40,7 @@
*/
#define YYTH_MAGIC 0x1B5E783D
+#define YYTH_FLAG_DIFF_ENCODE 1
struct table_set_header {
u32 th_magic; /* YYTH_MAGIC */
@@ -101,6 +102,7 @@ struct aa_dfa {
};
extern struct aa_dfa *nulldfa;
+extern struct aa_dfa *stacksplitdfa;
#define byte_to_byte(X) (X)
@@ -129,9 +131,32 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
const char *str);
unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
const char c);
+unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start,
+ const char *str, const char **retpos);
+unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start,
+ const char *str, int n, const char **retpos);
void aa_dfa_free_kref(struct kref *kref);
+#define WB_HISTORY_SIZE 8
+struct match_workbuf {
+ unsigned int count;
+ unsigned int pos;
+ unsigned int len;
+ unsigned int size; /* power of 2, same as history size */
+ unsigned int history[WB_HISTORY_SIZE];
+};
+#define DEFINE_MATCH_WB(N) \
+struct match_workbuf N = { \
+ .count = 0, \
+ .pos = 0, \
+ .len = 0, \
+ .size = WB_HISTORY_SIZE, \
+}
+
+unsigned int aa_dfa_leftmatch(struct aa_dfa *dfa, unsigned int start,
+ const char *str, unsigned int *count);
+
/**
* aa_get_dfa - increment refcount on dfa @p
* @dfa: dfa (MAYBE NULL)
@@ -159,4 +184,7 @@ static inline void aa_put_dfa(struct aa_dfa *dfa)
kref_put(&dfa->count, aa_dfa_free_kref);
}
+#define MATCH_FLAG_DIFF_ENCODE 0x80000000
+#define MARK_DIFF_ENCODE 0x40000000
+
#endif /* __AA_MATCH_H */
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
new file mode 100644
index 000000000000..ec7228e857a9
--- /dev/null
+++ b/security/apparmor/include/net.h
@@ -0,0 +1,106 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor network mediation definitions.
+ *
+ * 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.
+ */
+
+#ifndef __AA_NET_H
+#define __AA_NET_H
+
+#include <net/sock.h>
+#include <linux/path.h>
+
+#include "apparmorfs.h"
+#include "label.h"
+#include "perms.h"
+#include "policy.h"
+
+#define AA_MAY_SEND AA_MAY_WRITE
+#define AA_MAY_RECEIVE AA_MAY_READ
+
+#define AA_MAY_SHUTDOWN AA_MAY_DELETE
+
+#define AA_MAY_CONNECT AA_MAY_OPEN
+#define AA_MAY_ACCEPT 0x00100000
+
+#define AA_MAY_BIND 0x00200000
+#define AA_MAY_LISTEN 0x00400000
+
+#define AA_MAY_SETOPT 0x01000000
+#define AA_MAY_GETOPT 0x02000000
+
+#define NET_PERMS_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \
+ AA_MAY_SHUTDOWN | AA_MAY_BIND | AA_MAY_LISTEN | \
+ AA_MAY_CONNECT | AA_MAY_ACCEPT | AA_MAY_SETATTR | \
+ AA_MAY_GETATTR | AA_MAY_SETOPT | AA_MAY_GETOPT)
+
+#define NET_FS_PERMS (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \
+ AA_MAY_SHUTDOWN | AA_MAY_CONNECT | AA_MAY_RENAME |\
+ AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_CHMOD | \
+ AA_MAY_CHOWN | AA_MAY_CHGRP | AA_MAY_LOCK | \
+ AA_MAY_MPROT)
+
+#define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \
+ AA_MAY_ACCEPT)
+struct aa_sk_ctx {
+ struct aa_label *label;
+ struct aa_label *peer;
+};
+
+#define SK_CTX(X) ((X)->sk_security)
+#define SOCK_ctx(X) SOCK_INODE(X)->i_security
+#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \
+ struct lsm_network_audit NAME ## _net = { .sk = (SK), \
+ .family = (F)}; \
+ DEFINE_AUDIT_DATA(NAME, \
+ ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \
+ LSM_AUDIT_DATA_NONE, \
+ OP); \
+ NAME.u.net = &(NAME ## _net); \
+ aad(&NAME)->net.type = (T); \
+ aad(&NAME)->net.protocol = (P)
+
+#define DEFINE_AUDIT_SK(NAME, OP, SK) \
+ DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \
+ (SK)->sk_protocol)
+
+
+#define af_select(FAMILY, FN, DEF_FN) \
+({ \
+ int __e; \
+ switch ((FAMILY)) { \
+ default: \
+ __e = DEF_FN; \
+ } \
+ __e; \
+})
+
+extern struct aa_sfs_entry aa_sfs_entry_network[];
+
+void audit_net_cb(struct audit_buffer *ab, void *va);
+int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
+ u32 request, u16 family, int type);
+int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family,
+ int type, int protocol);
+static inline int aa_profile_af_sk_perm(struct aa_profile *profile,
+ struct common_audit_data *sa,
+ u32 request,
+ struct sock *sk)
+{
+ return aa_profile_af_perm(profile, sa, request, sk->sk_family,
+ sk->sk_type);
+}
+int aa_sk_perm(const char *op, u32 request, struct sock *sk);
+
+int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
+ struct socket *sock);
+
+#endif /* __AA_NET_H */
diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h
index d7b7e7115160..38aa6247d00f 100644
--- a/security/apparmor/include/perms.h
+++ b/security/apparmor/include/perms.h
@@ -138,9 +138,10 @@ extern struct aa_perms allperms;
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_names(struct audit_buffer *ab, const char * const *names,
+ u32 mask);
void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
- u32 chrsmask, const char **names, u32 namesmask);
+ u32 chrsmask, const char * const *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,
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index 17fe41a9cac3..ab64c6b5db5a 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -30,6 +30,7 @@
#include "file.h"
#include "lib.h"
#include "label.h"
+#include "net.h"
#include "perms.h"
#include "resource.h"
@@ -148,6 +149,10 @@ struct aa_profile {
struct aa_policydb policy;
struct aa_file_rules file;
struct aa_caps caps;
+
+ int xattr_count;
+ char **xattrs;
+
struct aa_rlimit rlimits;
struct aa_loaddata *rawdata;
@@ -209,15 +214,15 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p)
return labels_profile(aa_get_newest_label(&p->label));
}
-#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;
+#define PROFILE_MEDIATES(P, T) ((P)->policy.start[(unsigned char) (T)])
+static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile,
+ u16 AF) {
+ unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET);
+ __be16 be_af = cpu_to_be16(AF);
+
+ if (!state)
+ return 0;
+ return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2);
}
/**
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h
index be6cd69ac319..8db4ab759e80 100644
--- a/security/apparmor/include/policy_unpack.h
+++ b/security/apparmor/include/policy_unpack.h
@@ -70,7 +70,7 @@ struct aa_loaddata {
int abi;
unsigned char *hash;
- char data[];
+ char *data;
};
int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns);
diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h
index 92e62fe95292..cbf7a997ed84 100644
--- a/security/apparmor/include/sig_names.h
+++ b/security/apparmor/include/sig_names.h
@@ -2,6 +2,9 @@
#define SIGUNKNOWN 0
#define MAXMAPPED_SIG 35
+#define MAXMAPPED_SIGNAME (MAXMAPPED_SIG + 1)
+#define SIGRT_BASE 128
+
/* 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
@@ -56,7 +59,7 @@ static const int sig_map[MAXMAPPED_SIG] = {
};
/* this table is ordered post sig_map[sig] mapping */
-static const char *const sig_names[MAXMAPPED_SIG + 1] = {
+static const char *const sig_names[MAXMAPPED_SIGNAME] = {
"unknown",
"hup",
"int",
diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h
new file mode 100644
index 000000000000..55edaa1d83f8
--- /dev/null
+++ b/security/apparmor/include/task.h
@@ -0,0 +1,94 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor task related definitions and mediation
+ *
+ * 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_TASK_H
+#define __AA_TASK_H
+
+#define task_ctx(X) ((X)->security)
+
+/*
+ * struct aa_task_ctx - information for current task label change
+ * @nnp: snapshot of label at time of no_new_privs
+ * @onexec: profile to transition to on next exec (MAY BE NULL)
+ * @previous: profile the task may return to (MAY BE NULL)
+ * @token: magic value the task must know for returning to @previous_profile
+ */
+struct aa_task_ctx {
+ struct aa_label *nnp;
+ struct aa_label *onexec;
+ struct aa_label *previous;
+ u64 token;
+};
+
+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);
+
+/**
+ * aa_alloc_task_ctx - allocate a new task_ctx
+ * @flags: gfp flags for allocation
+ *
+ * Returns: allocated buffer or NULL on failure
+ */
+static inline struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags)
+{
+ return kzalloc(sizeof(struct aa_task_ctx), flags);
+}
+
+/**
+ * aa_free_task_ctx - free a task_ctx
+ * @ctx: task_ctx to free (MAYBE NULL)
+ */
+static inline void aa_free_task_ctx(struct aa_task_ctx *ctx)
+{
+ if (ctx) {
+ aa_put_label(ctx->nnp);
+ aa_put_label(ctx->previous);
+ aa_put_label(ctx->onexec);
+
+ kzfree(ctx);
+ }
+}
+
+/**
+ * aa_dup_task_ctx - duplicate a task context, incrementing reference counts
+ * @new: a blank task context (NOT NULL)
+ * @old: the task context to copy (NOT NULL)
+ */
+static inline void aa_dup_task_ctx(struct aa_task_ctx *new,
+ const struct aa_task_ctx *old)
+{
+ *new = *old;
+ aa_get_label(new->nnp);
+ aa_get_label(new->previous);
+ aa_get_label(new->onexec);
+}
+
+/**
+ * aa_clear_task_ctx_trans - clear transition tracking info from the ctx
+ * @ctx: task context to clear (NOT NULL)
+ */
+static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx)
+{
+ AA_BUG(!ctx);
+
+ aa_put_label(ctx->previous);
+ aa_put_label(ctx->onexec);
+ ctx->previous = NULL;
+ ctx->onexec = NULL;
+ ctx->token = 0;
+}
+
+#endif /* __AA_TASK_H */
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c
index b40678f3c1d5..527ea1557120 100644
--- a/security/apparmor/ipc.c
+++ b/security/apparmor/ipc.c
@@ -17,7 +17,7 @@
#include "include/audit.h"
#include "include/capability.h"
-#include "include/context.h"
+#include "include/cred.h"
#include "include/policy.h"
#include "include/ipc.h"
#include "include/sig_names.h"
@@ -138,7 +138,7 @@ 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 */
+ return sig - SIGRTMIN + SIGRT_BASE;
else if (sig < MAXMAPPED_SIG)
return sig_map[sig];
return SIGUNKNOWN;
@@ -174,60 +174,48 @@ static void audit_signal_cb(struct audit_buffer *ab, void *va)
audit_signal_mask(ab, aad(sa)->denied);
}
}
- if (aad(sa)->signal < MAXMAPPED_SIG)
+ if (aad(sa)->signal == SIGUNKNOWN)
+ audit_log_format(ab, "signal=unknown(%d)",
+ aad(sa)->unmappedsig);
+ else 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);
+ aad(sa)->signal - SIGRT_BASE);
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 aa_label *peer, u32 request,
struct common_audit_data *sa)
{
struct aa_perms perms;
+ unsigned int state;
if (profile_unconfined(profile) ||
!PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
return 0;
- aad(sa)->peer = &peer->label;
- profile_match_signal(profile, peer->base.hname, aad(sa)->signal,
- &perms);
+ aad(sa)->peer = peer;
+ /* TODO: secondary cache check <profile, profile, perm> */
+ state = aa_dfa_next(profile->policy.dfa,
+ profile->policy.start[AA_CLASS_SIGNAL],
+ aad(sa)->signal);
+ aa_label_match(profile, peer, state, false, request, &perms);
aa_apply_modes_to_perms(profile, &perms);
return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
}
-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));
-}
-
int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
{
+ struct aa_profile *profile;
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL);
aad(&sa)->signal = map_signal_num(sig);
- return xcheck_labels_profiles(sender, target, aa_signal_cross_perm,
- &sa);
+ aad(&sa)->unmappedsig = sig;
+ return xcheck_labels(sender, target, profile,
+ profile_signal_perm(profile, target, MAY_WRITE, &sa),
+ profile_signal_perm(profile, sender, MAY_READ, &sa));
}
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
index 324fe5c60f87..523250e34837 100644
--- a/security/apparmor/label.c
+++ b/security/apparmor/label.c
@@ -16,7 +16,7 @@
#include <linux/sort.h>
#include "include/apparmor.h"
-#include "include/context.h"
+#include "include/cred.h"
#include "include/label.h"
#include "include/policy.h"
#include "include/secid.h"
@@ -1808,14 +1808,17 @@ void aa_label_printk(struct aa_label *label, gfp_t gfp)
aa_put_ns(ns);
}
-static int label_count_str_entries(const char *str)
+static int label_count_strn_entries(const char *str, size_t n)
{
+ const char *end = str + n;
const char *split;
int count = 1;
AA_BUG(!str);
- for (split = strstr(str, "//&"); split; split = strstr(str, "//&")) {
+ for (split = aa_label_strn_split(str, end - str);
+ split;
+ split = aa_label_strn_split(str, end - str)) {
count++;
str = split + 3;
}
@@ -1843,9 +1846,10 @@ static struct aa_profile *fqlookupn_profile(struct aa_label *base,
}
/**
- * aa_label_parse - parse, validate and convert a text string to a label
+ * aa_label_strn_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)
+ * @n: length of str to parse, will stop at \0 if encountered before n
* @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 &
@@ -1853,19 +1857,24 @@ static struct aa_profile *fqlookupn_profile(struct aa_label *base,
* 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)
+struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str,
+ size_t n, 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;
+ const char *end = str + n;
+ const char *split;
AA_BUG(!base);
AA_BUG(!str);
- str = skip_spaces(str);
- len = label_count_str_entries(str);
+ str = skipn_spaces(str, n);
+ if (str == NULL || (*str == '=' && base != &root_ns->unconfined->label))
+ return ERR_PTR(-EINVAL);
+
+ len = label_count_strn_entries(str, end - str);
if (*str == '&' || force_stack) {
/* stack on top of base */
stack = base->size;
@@ -1873,8 +1882,6 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
if (*str == '&')
str++;
}
- if (*str == '=')
- base = &root_ns->unconfined->label;
error = vec_setup(profile, vec, len, gfp);
if (error)
@@ -1883,7 +1890,8 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
for (i = 0; i < stack; i++)
vec[i] = aa_get_profile(base->vec[i]);
- for (split = strstr(str, "//&"), i = stack; split && i < len; i++) {
+ for (split = aa_label_strn_split(str, end - str), i = stack;
+ split && i < len; i++) {
vec[i] = fqlookupn_profile(base, currbase, str, split - str);
if (!vec[i])
goto fail;
@@ -1894,11 +1902,11 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
if (vec[i]->ns != labels_ns(currbase))
currbase = &vec[i]->label;
str = split + 3;
- split = strstr(str, "//&");
+ split = aa_label_strn_split(str, end - str);
}
/* last element doesn't have a split */
if (i < len) {
- vec[i] = fqlookupn_profile(base, currbase, str, strlen(str));
+ vec[i] = fqlookupn_profile(base, currbase, str, end - str);
if (!vec[i])
goto fail;
}
@@ -1930,6 +1938,12 @@ fail:
goto out;
}
+struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
+ gfp_t gfp, bool create, bool force_stack)
+{
+ return aa_label_strn_parse(base, str, strlen(str), gfp, create,
+ force_stack);
+}
/**
* aa_labelset_destroy - remove all labels from the label set
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index 4d5e98e49d5e..068a9f471f77 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -211,7 +211,8 @@ void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask)
*str = '\0';
}
-void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask)
+void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
+ u32 mask)
{
const char *fmt = "%s";
unsigned int i, perm = 1;
@@ -229,7 +230,7 @@ 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)
+ u32 chrsmask, const char * const *names, u32 namesmask)
{
char str[33];
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 528f59b580a8..ce2b89e9ad94 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -29,9 +29,10 @@
#include "include/apparmorfs.h"
#include "include/audit.h"
#include "include/capability.h"
-#include "include/context.h"
+#include "include/cred.h"
#include "include/file.h"
#include "include/ipc.h"
+#include "include/net.h"
#include "include/path.h"
#include "include/label.h"
#include "include/policy.h"
@@ -50,12 +51,12 @@ DEFINE_PER_CPU(struct aa_buffers, aa_buffers);
*/
/*
- * free the associated aa_task_ctx and put its labels
+ * put the associated labels
*/
static void apparmor_cred_free(struct cred *cred)
{
- aa_free_task_context(cred_ctx(cred));
- cred_ctx(cred) = NULL;
+ aa_put_label(cred_label(cred));
+ cred_label(cred) = NULL;
}
/*
@@ -63,30 +64,17 @@ static void apparmor_cred_free(struct cred *cred)
*/
static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
- /* freed by apparmor_cred_free */
- struct aa_task_ctx *ctx = aa_alloc_task_context(gfp);
-
- if (!ctx)
- return -ENOMEM;
-
- cred_ctx(cred) = ctx;
+ cred_label(cred) = NULL;
return 0;
}
/*
- * prepare new aa_task_ctx for modification by prepare_cred block
+ * prepare new cred label for modification by prepare_cred block
*/
static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
- /* freed by apparmor_cred_free */
- struct aa_task_ctx *ctx = aa_alloc_task_context(gfp);
-
- if (!ctx)
- return -ENOMEM;
-
- aa_dup_task_context(ctx, cred_ctx(old));
- cred_ctx(new) = ctx;
+ cred_label(new) = aa_get_newest_label(cred_label(old));
return 0;
}
@@ -95,10 +83,28 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
*/
static void apparmor_cred_transfer(struct cred *new, const struct cred *old)
{
- const struct aa_task_ctx *old_ctx = cred_ctx(old);
- struct aa_task_ctx *new_ctx = cred_ctx(new);
+ cred_label(new) = aa_get_newest_label(cred_label(old));
+}
+
+static void apparmor_task_free(struct task_struct *task)
+{
+
+ aa_free_task_ctx(task_ctx(task));
+ task_ctx(task) = NULL;
+}
+
+static int apparmor_task_alloc(struct task_struct *task,
+ unsigned long clone_flags)
+{
+ struct aa_task_ctx *new = aa_alloc_task_ctx(GFP_KERNEL);
+
+ if (!new)
+ return -ENOMEM;
+
+ aa_dup_task_ctx(new, task_ctx(current));
+ task_ctx(task) = new;
- aa_dup_task_context(new_ctx, old_ctx);
+ return 0;
}
static int apparmor_ptrace_access_check(struct task_struct *child,
@@ -576,11 +582,11 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
int error = -ENOENT;
/* released below */
const struct cred *cred = get_task_cred(task);
- struct aa_task_ctx *ctx = cred_ctx(cred);
+ struct aa_task_ctx *ctx = task_ctx(current);
struct aa_label *label = NULL;
if (strcmp(name, "current") == 0)
- label = aa_get_newest_label(ctx->label);
+ label = aa_get_newest_label(cred_label(cred));
else if (strcmp(name, "prev") == 0 && ctx->previous)
label = aa_get_newest_label(ctx->previous);
else if (strcmp(name, "exec") == 0 && ctx->onexec)
@@ -677,11 +683,11 @@ fail:
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);
+ struct aa_label *new_label = cred_label(bprm->cred);
/* bail out if unconfined or not changing profile */
- if ((new_ctx->label->proxy == label->proxy) ||
- (unconfined(new_ctx->label)))
+ if ((new_label->proxy == label->proxy) ||
+ (unconfined(new_label)))
return;
aa_inherit_files(bprm->cred, current->files);
@@ -689,7 +695,7 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
current->pdeath_signal = 0;
/* reset soft limits and set hard limits for the new label */
- __aa_transition_rlimits(label, new_ctx->label);
+ __aa_transition_rlimits(label, new_label);
}
/**
@@ -698,7 +704,9 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
*/
static void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
{
- /* TODO: cleanup signals - ipc mediation */
+ /* clear out temporary/transitional state from the context */
+ aa_clear_task_ctx_trans(task_ctx(current));
+
return;
}
@@ -742,6 +750,373 @@ static int apparmor_task_kill(struct task_struct *target, struct siginfo *info,
return error;
}
+/**
+ * apparmor_sk_alloc_security - allocate and attach the sk_security field
+ */
+static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags)
+{
+ struct aa_sk_ctx *ctx;
+
+ ctx = kzalloc(sizeof(*ctx), flags);
+ if (!ctx)
+ return -ENOMEM;
+
+ SK_CTX(sk) = ctx;
+
+ return 0;
+}
+
+/**
+ * apparmor_sk_free_security - free the sk_security field
+ */
+static void apparmor_sk_free_security(struct sock *sk)
+{
+ struct aa_sk_ctx *ctx = SK_CTX(sk);
+
+ SK_CTX(sk) = NULL;
+ aa_put_label(ctx->label);
+ aa_put_label(ctx->peer);
+ kfree(ctx);
+}
+
+/**
+ * apparmor_clone_security - clone the sk_security field
+ */
+static void apparmor_sk_clone_security(const struct sock *sk,
+ struct sock *newsk)
+{
+ struct aa_sk_ctx *ctx = SK_CTX(sk);
+ struct aa_sk_ctx *new = SK_CTX(newsk);
+
+ new->label = aa_get_label(ctx->label);
+ new->peer = aa_get_label(ctx->peer);
+}
+
+/**
+ * apparmor_socket_create - check perms before creating a new socket
+ */
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
+{
+ struct aa_label *label;
+ int error = 0;
+
+ AA_BUG(in_interrupt());
+
+ label = begin_current_label_crit_section();
+ if (!(kern || unconfined(label)))
+ error = af_select(family,
+ create_perm(label, family, type, protocol),
+ aa_af_perm(label, OP_CREATE, AA_MAY_CREATE,
+ family, type, protocol));
+ end_current_label_crit_section(label);
+
+ return error;
+}
+
+/**
+ * apparmor_socket_post_create - setup the per-socket security struct
+ *
+ * Note:
+ * - kernel sockets currently labeled unconfined but we may want to
+ * move to a special kernel label
+ * - socket may not have sk here if created with sock_create_lite or
+ * sock_alloc. These should be accept cases which will be handled in
+ * sock_graft.
+ */
+static int apparmor_socket_post_create(struct socket *sock, int family,
+ int type, int protocol, int kern)
+{
+ struct aa_label *label;
+
+ if (kern) {
+ struct aa_ns *ns = aa_get_current_ns();
+
+ label = aa_get_label(ns_unconfined(ns));
+ aa_put_ns(ns);
+ } else
+ label = aa_get_current_label();
+
+ if (sock->sk) {
+ struct aa_sk_ctx *ctx = SK_CTX(sock->sk);
+
+ aa_put_label(ctx->label);
+ ctx->label = aa_get_label(label);
+ }
+ aa_put_label(label);
+
+ return 0;
+}
+
+/**
+ * apparmor_socket_bind - check perms before bind addr to socket
+ */
+static int apparmor_socket_bind(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ AA_BUG(!sock);
+ AA_BUG(!sock->sk);
+ AA_BUG(!address);
+ AA_BUG(in_interrupt());
+
+ return af_select(sock->sk->sk_family,
+ bind_perm(sock, address, addrlen),
+ aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk));
+}
+
+/**
+ * apparmor_socket_connect - check perms before connecting @sock to @address
+ */
+static int apparmor_socket_connect(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ AA_BUG(!sock);
+ AA_BUG(!sock->sk);
+ AA_BUG(!address);
+ AA_BUG(in_interrupt());
+
+ return af_select(sock->sk->sk_family,
+ connect_perm(sock, address, addrlen),
+ aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk));
+}
+
+/**
+ * apparmor_socket_list - check perms before allowing listen
+ */
+static int apparmor_socket_listen(struct socket *sock, int backlog)
+{
+ AA_BUG(!sock);
+ AA_BUG(!sock->sk);
+ AA_BUG(in_interrupt());
+
+ return af_select(sock->sk->sk_family,
+ listen_perm(sock, backlog),
+ aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk));
+}
+
+/**
+ * apparmor_socket_accept - check perms before accepting a new connection.
+ *
+ * Note: while @newsock is created and has some information, the accept
+ * has not been done.
+ */
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ AA_BUG(!sock);
+ AA_BUG(!sock->sk);
+ AA_BUG(!newsock);
+ AA_BUG(in_interrupt());
+
+ return af_select(sock->sk->sk_family,
+ accept_perm(sock, newsock),
+ aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk));
+}
+
+static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock,
+ struct msghdr *msg, int size)
+{
+ AA_BUG(!sock);
+ AA_BUG(!sock->sk);
+ AA_BUG(!msg);
+ AA_BUG(in_interrupt());
+
+ return af_select(sock->sk->sk_family,
+ msg_perm(op, request, sock, msg, size),
+ aa_sk_perm(op, request, sock->sk));
+}
+
+/**
+ * apparmor_socket_sendmsg - check perms before sending msg to another socket
+ */
+static int apparmor_socket_sendmsg(struct socket *sock,
+ struct msghdr *msg, int size)
+{
+ return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size);
+}
+
+/**
+ * apparmor_socket_recvmsg - check perms before receiving a message
+ */
+static int apparmor_socket_recvmsg(struct socket *sock,
+ struct msghdr *msg, int size, int flags)
+{
+ return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size);
+}
+
+/* revaliation, get/set attr, shutdown */
+static int aa_sock_perm(const char *op, u32 request, struct socket *sock)
+{
+ AA_BUG(!sock);
+ AA_BUG(!sock->sk);
+ AA_BUG(in_interrupt());
+
+ return af_select(sock->sk->sk_family,
+ sock_perm(op, request, sock),
+ aa_sk_perm(op, request, sock->sk));
+}
+
+/**
+ * apparmor_socket_getsockname - check perms before getting the local address
+ */
+static int apparmor_socket_getsockname(struct socket *sock)
+{
+ return aa_sock_perm(OP_GETSOCKNAME, AA_MAY_GETATTR, sock);
+}
+
+/**
+ * apparmor_socket_getpeername - check perms before getting remote address
+ */
+static int apparmor_socket_getpeername(struct socket *sock)
+{
+ return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock);
+}
+
+/* revaliation, get/set attr, opt */
+static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock,
+ int level, int optname)
+{
+ AA_BUG(!sock);
+ AA_BUG(!sock->sk);
+ AA_BUG(in_interrupt());
+
+ return af_select(sock->sk->sk_family,
+ opt_perm(op, request, sock, level, optname),
+ aa_sk_perm(op, request, sock->sk));
+}
+
+/**
+ * apparmor_getsockopt - check perms before getting socket options
+ */
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
+ int optname)
+{
+ return aa_sock_opt_perm(OP_GETSOCKOPT, AA_MAY_GETOPT, sock,
+ level, optname);
+}
+
+/**
+ * apparmor_setsockopt - check perms before setting socket options
+ */
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
+ int optname)
+{
+ return aa_sock_opt_perm(OP_SETSOCKOPT, AA_MAY_SETOPT, sock,
+ level, optname);
+}
+
+/**
+ * apparmor_socket_shutdown - check perms before shutting down @sock conn
+ */
+static int apparmor_socket_shutdown(struct socket *sock, int how)
+{
+ return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock);
+}
+
+/**
+ * apparmor_socket_sock_recv_skb - check perms before associating skb to sk
+ *
+ * Note: can not sleep may be called with locks held
+ *
+ * dont want protocol specific in __skb_recv_datagram()
+ * to deny an incoming connection socket_sock_rcv_skb()
+ */
+static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ return 0;
+}
+
+
+static struct aa_label *sk_peer_label(struct sock *sk)
+{
+ struct aa_sk_ctx *ctx = SK_CTX(sk);
+
+ if (ctx->peer)
+ return ctx->peer;
+
+ return ERR_PTR(-ENOPROTOOPT);
+}
+
+/**
+ * apparmor_socket_getpeersec_stream - get security context of peer
+ *
+ * Note: for tcp only valid if using ipsec or cipso on lan
+ */
+static int apparmor_socket_getpeersec_stream(struct socket *sock,
+ char __user *optval,
+ int __user *optlen,
+ unsigned int len)
+{
+ char *name;
+ int slen, error = 0;
+ struct aa_label *label;
+ struct aa_label *peer;
+
+ label = begin_current_label_crit_section();
+ peer = sk_peer_label(sock->sk);
+ if (IS_ERR(peer)) {
+ error = PTR_ERR(peer);
+ goto done;
+ }
+ slen = aa_label_asxprint(&name, labels_ns(label), peer,
+ FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
+ FLAG_HIDDEN_UNCONFINED, GFP_KERNEL);
+ /* don't include terminating \0 in slen, it breaks some apps */
+ if (slen < 0) {
+ error = -ENOMEM;
+ } else {
+ if (slen > len) {
+ error = -ERANGE;
+ } else if (copy_to_user(optval, name, slen)) {
+ error = -EFAULT;
+ goto out;
+ }
+ if (put_user(slen, optlen))
+ error = -EFAULT;
+out:
+ kfree(name);
+
+ }
+
+done:
+ end_current_label_crit_section(label);
+
+ return error;
+}
+
+/**
+ * apparmor_socket_getpeersec_dgram - get security label of packet
+ * @sock: the peer socket
+ * @skb: packet data
+ * @secid: pointer to where to put the secid of the packet
+ *
+ * Sets the netlabel socket state on sk from parent
+ */
+static int apparmor_socket_getpeersec_dgram(struct socket *sock,
+ struct sk_buff *skb, u32 *secid)
+
+{
+ /* TODO: requires secid support */
+ return -ENOPROTOOPT;
+}
+
+/**
+ * apparmor_sock_graft - Initialize newly created socket
+ * @sk: child sock
+ * @parent: parent socket
+ *
+ * Note: could set off of SOCK_CTX(parent) but need to track inode and we can
+ * just set sk security information off of current creating process label
+ * Labeling of sk for accept case - probably should be sock based
+ * instead of task, because of the case where an implicitly labeled
+ * socket is shared by different tasks.
+ */
+static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
+{
+ struct aa_sk_ctx *ctx = SK_CTX(sk);
+
+ if (!ctx->label)
+ ctx->label = aa_get_current_label();
+}
+
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
@@ -776,6 +1151,30 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
+ LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security),
+ LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security),
+ LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security),
+
+ LSM_HOOK_INIT(socket_create, apparmor_socket_create),
+ LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create),
+ LSM_HOOK_INIT(socket_bind, apparmor_socket_bind),
+ LSM_HOOK_INIT(socket_connect, apparmor_socket_connect),
+ LSM_HOOK_INIT(socket_listen, apparmor_socket_listen),
+ LSM_HOOK_INIT(socket_accept, apparmor_socket_accept),
+ LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg),
+ LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg),
+ LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname),
+ LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername),
+ LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt),
+ LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt),
+ LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown),
+ LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb),
+ LSM_HOOK_INIT(socket_getpeersec_stream,
+ apparmor_socket_getpeersec_stream),
+ LSM_HOOK_INIT(socket_getpeersec_dgram,
+ apparmor_socket_getpeersec_dgram),
+ LSM_HOOK_INIT(sock_graft, apparmor_sock_graft),
+
LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank),
LSM_HOOK_INIT(cred_free, apparmor_cred_free),
LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare),
@@ -785,6 +1184,8 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(bprm_committing_creds, apparmor_bprm_committing_creds),
LSM_HOOK_INIT(bprm_committed_creds, apparmor_bprm_committed_creds),
+ LSM_HOOK_INIT(task_free, apparmor_task_free),
+ LSM_HOOK_INIT(task_alloc, apparmor_task_alloc),
LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
LSM_HOOK_INIT(task_kill, apparmor_task_kill),
};
@@ -1032,12 +1433,12 @@ static int __init set_init_ctx(void)
struct cred *cred = (struct cred *)current->real_cred;
struct aa_task_ctx *ctx;
- ctx = aa_alloc_task_context(GFP_KERNEL);
+ ctx = aa_alloc_task_ctx(GFP_KERNEL);
if (!ctx)
return -ENOMEM;
- ctx->label = aa_get_label(ns_unconfined(root_ns));
- cred_ctx(cred) = ctx;
+ cred_label(cred) = aa_get_label(ns_unconfined(root_ns));
+ task_ctx(current) = ctx;
return 0;
}
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index 72c604350e80..280eba082c7b 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -30,6 +30,11 @@ static char nulldfa_src[] = {
};
struct aa_dfa *nulldfa;
+static char stacksplitdfa_src[] = {
+ #include "stacksplitdfa.in"
+};
+struct aa_dfa *stacksplitdfa;
+
int aa_setup_dfa_engine(void)
{
int error;
@@ -37,19 +42,31 @@ int aa_setup_dfa_engine(void)
nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src),
TO_ACCEPT1_FLAG(YYTD_DATA32) |
TO_ACCEPT2_FLAG(YYTD_DATA32));
- if (!IS_ERR(nulldfa))
- return 0;
+ if (IS_ERR(nulldfa)) {
+ error = PTR_ERR(nulldfa);
+ nulldfa = NULL;
+ return error;
+ }
- error = PTR_ERR(nulldfa);
- nulldfa = NULL;
+ stacksplitdfa = aa_dfa_unpack(stacksplitdfa_src,
+ sizeof(stacksplitdfa_src),
+ TO_ACCEPT1_FLAG(YYTD_DATA32) |
+ TO_ACCEPT2_FLAG(YYTD_DATA32));
+ if (IS_ERR(stacksplitdfa)) {
+ aa_put_dfa(nulldfa);
+ nulldfa = NULL;
+ error = PTR_ERR(stacksplitdfa);
+ stacksplitdfa = NULL;
+ return error;
+ }
- return error;
+ return 0;
}
void aa_teardown_dfa_engine(void)
{
+ aa_put_dfa(stacksplitdfa);
aa_put_dfa(nulldfa);
- nulldfa = NULL;
}
/**
@@ -119,8 +136,8 @@ fail:
}
/**
- * verify_dfa - verify that transitions and states in the tables are in bounds.
- * @dfa: dfa to test (NOT NULL)
+ * verify_table_headers - verify that the tables headers are as expected
+ * @tables - array of dfa tables to check (NOT NULL)
* @flags: flags controlling what type of accept table are acceptable
*
* Assumes dfa has gone through the first pass verification done by unpacking
@@ -128,64 +145,98 @@ fail:
*
* Returns: %0 else error code on failure to verify
*/
-static int verify_dfa(struct aa_dfa *dfa, int flags)
+static int verify_table_headers(struct table_header **tables, int flags)
{
- size_t i, state_count, trans_count;
+ size_t state_count, trans_count;
int error = -EPROTO;
/* check that required tables exist */
- if (!(dfa->tables[YYTD_ID_DEF] &&
- dfa->tables[YYTD_ID_BASE] &&
- dfa->tables[YYTD_ID_NXT] && dfa->tables[YYTD_ID_CHK]))
+ if (!(tables[YYTD_ID_DEF] && tables[YYTD_ID_BASE] &&
+ tables[YYTD_ID_NXT] && tables[YYTD_ID_CHK]))
goto out;
/* accept.size == default.size == base.size */
- state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
+ state_count = tables[YYTD_ID_BASE]->td_lolen;
if (ACCEPT1_FLAGS(flags)) {
- if (!dfa->tables[YYTD_ID_ACCEPT])
+ if (!tables[YYTD_ID_ACCEPT])
goto out;
- if (state_count != dfa->tables[YYTD_ID_ACCEPT]->td_lolen)
+ if (state_count != tables[YYTD_ID_ACCEPT]->td_lolen)
goto out;
}
if (ACCEPT2_FLAGS(flags)) {
- if (!dfa->tables[YYTD_ID_ACCEPT2])
+ if (!tables[YYTD_ID_ACCEPT2])
goto out;
- if (state_count != dfa->tables[YYTD_ID_ACCEPT2]->td_lolen)
+ if (state_count != tables[YYTD_ID_ACCEPT2]->td_lolen)
goto out;
}
- if (state_count != dfa->tables[YYTD_ID_DEF]->td_lolen)
+ if (state_count != tables[YYTD_ID_DEF]->td_lolen)
goto out;
/* next.size == chk.size */
- trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen;
- if (trans_count != dfa->tables[YYTD_ID_CHK]->td_lolen)
+ trans_count = tables[YYTD_ID_NXT]->td_lolen;
+ if (trans_count != tables[YYTD_ID_CHK]->td_lolen)
goto out;
/* if equivalence classes then its table size must be 256 */
- if (dfa->tables[YYTD_ID_EC] &&
- dfa->tables[YYTD_ID_EC]->td_lolen != 256)
+ if (tables[YYTD_ID_EC] && tables[YYTD_ID_EC]->td_lolen != 256)
goto out;
- if (flags & DFA_FLAG_VERIFY_STATES) {
- for (i = 0; i < state_count; i++) {
- if (DEFAULT_TABLE(dfa)[i] >= state_count)
- goto out;
- if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) {
- printk(KERN_ERR "AppArmor DFA next/check upper "
- "bounds error\n");
- goto out;
- }
+ error = 0;
+out:
+ return error;
+}
+
+/**
+ * verify_dfa - verify that transitions and states in the tables are in bounds.
+ * @dfa: dfa to test (NOT NULL)
+ *
+ * Assumes dfa has gone through the first pass verification done by unpacking
+ * NOTE: this does not valid accept table values
+ *
+ * Returns: %0 else error code on failure to verify
+ */
+static int verify_dfa(struct aa_dfa *dfa)
+{
+ size_t i, state_count, trans_count;
+ int error = -EPROTO;
+
+ state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
+ trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen;
+ for (i = 0; i < state_count; i++) {
+ if (!(BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE) &&
+ (DEFAULT_TABLE(dfa)[i] >= state_count))
+ goto out;
+ if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) {
+ pr_err("AppArmor DFA next/check upper bounds error\n");
+ goto out;
}
+ }
- for (i = 0; i < trans_count; i++) {
- if (NEXT_TABLE(dfa)[i] >= state_count)
- goto out;
- if (CHECK_TABLE(dfa)[i] >= state_count)
+ for (i = 0; i < trans_count; i++) {
+ if (NEXT_TABLE(dfa)[i] >= state_count)
+ goto out;
+ if (CHECK_TABLE(dfa)[i] >= state_count)
+ goto out;
+ }
+
+ /* Now that all the other tables are verified, verify diffencoding */
+ for (i = 0; i < state_count; i++) {
+ size_t j, k;
+
+ for (j = i;
+ (BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE) &&
+ !(BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE);
+ j = k) {
+ k = DEFAULT_TABLE(dfa)[j];
+ if (j == k)
goto out;
+ if (k < j)
+ break; /* already verified */
+ BASE_TABLE(dfa)[j] |= MARK_DIFF_ENCODE;
}
}
-
error = 0;
+
out:
return error;
}
@@ -257,6 +308,9 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
goto fail;
dfa->flags = ntohs(*(__be16 *) (data + 12));
+ if (dfa->flags != 0 && dfa->flags != YYTH_FLAG_DIFF_ENCODE)
+ goto fail;
+
data += hsize;
size -= hsize;
@@ -299,11 +353,16 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
size -= table_size(table->td_lolen, table->td_flags);
table = NULL;
}
-
- error = verify_dfa(dfa, flags);
+ error = verify_table_headers(dfa->tables, flags);
if (error)
goto fail;
+ if (flags & DFA_FLAG_VERIFY_STATES) {
+ error = verify_dfa(dfa);
+ if (error)
+ goto fail;
+ }
+
return dfa;
fail:
@@ -312,6 +371,20 @@ fail:
return ERR_PTR(error);
}
+#define match_char(state, def, base, next, check, C) \
+do { \
+ u32 b = (base)[(state)]; \
+ unsigned int pos = base_idx(b) + (C); \
+ if ((check)[pos] != (state)) { \
+ (state) = (def)[(state)]; \
+ if (b & MATCH_FLAG_DIFF_ENCODE) \
+ continue; \
+ break; \
+ } \
+ (state) = (next)[pos]; \
+ break; \
+} while (1)
+
/**
* aa_dfa_match_len - traverse @dfa to find state @str stops at
* @dfa: the dfa to match @str against (NOT NULL)
@@ -335,6 +408,118 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
u32 *base = BASE_TABLE(dfa);
u16 *next = NEXT_TABLE(dfa);
u16 *check = CHECK_TABLE(dfa);
+ unsigned int state = start;
+
+ if (state == 0)
+ return 0;
+
+ /* current state is <state>, matching character *str */
+ if (dfa->tables[YYTD_ID_EC]) {
+ /* Equivalence class table defined */
+ u8 *equiv = EQUIV_TABLE(dfa);
+ for (; len; len--)
+ match_char(state, def, base, next, check,
+ equiv[(u8) *str++]);
+ } else {
+ /* default is direct to next state */
+ for (; len; len--)
+ match_char(state, def, base, next, check, (u8) *str++);
+ }
+
+ return state;
+}
+
+/**
+ * aa_dfa_match - traverse @dfa to find state @str stops at
+ * @dfa: the dfa to match @str against (NOT NULL)
+ * @start: the state of the dfa to start matching in
+ * @str: the null terminated string of bytes to match against the dfa (NOT NULL)
+ *
+ * aa_dfa_match will match @str against the dfa and return the state it
+ * finished matching in. The final state can be used to look up the accepting
+ * label, or as the start state of a continuing match.
+ *
+ * Returns: final state reached after input is consumed
+ */
+unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
+ const char *str)
+{
+ u16 *def = DEFAULT_TABLE(dfa);
+ u32 *base = BASE_TABLE(dfa);
+ u16 *next = NEXT_TABLE(dfa);
+ u16 *check = CHECK_TABLE(dfa);
+ unsigned int state = start;
+
+ if (state == 0)
+ return 0;
+
+ /* current state is <state>, matching character *str */
+ if (dfa->tables[YYTD_ID_EC]) {
+ /* Equivalence class table defined */
+ u8 *equiv = EQUIV_TABLE(dfa);
+ /* default is direct to next state */
+ while (*str)
+ match_char(state, def, base, next, check,
+ equiv[(u8) *str++]);
+ } else {
+ /* default is direct to next state */
+ while (*str)
+ match_char(state, def, base, next, check, (u8) *str++);
+ }
+
+ return state;
+}
+
+/**
+ * aa_dfa_next - step one character to the next state in the dfa
+ * @dfa: the dfa to tranverse (NOT NULL)
+ * @state: the state to start in
+ * @c: the input character to transition on
+ *
+ * aa_dfa_match will step through the dfa by one input character @c
+ *
+ * Returns: state reach after input @c
+ */
+unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
+ const char c)
+{
+ u16 *def = DEFAULT_TABLE(dfa);
+ u32 *base = BASE_TABLE(dfa);
+ u16 *next = NEXT_TABLE(dfa);
+ u16 *check = CHECK_TABLE(dfa);
+
+ /* current state is <state>, matching character *str */
+ if (dfa->tables[YYTD_ID_EC]) {
+ /* Equivalence class table defined */
+ u8 *equiv = EQUIV_TABLE(dfa);
+ match_char(state, def, base, next, check, equiv[(u8) c]);
+ } else
+ match_char(state, def, base, next, check, (u8) c);
+
+ return state;
+}
+
+/**
+ * aa_dfa_match_until - traverse @dfa until accept state or end of input
+ * @dfa: the dfa to match @str against (NOT NULL)
+ * @start: the state of the dfa to start matching in
+ * @str: the null terminated string of bytes to match against the dfa (NOT NULL)
+ * @retpos: first character in str after match OR end of string
+ *
+ * aa_dfa_match will match @str against the dfa and return the state it
+ * finished matching in. The final state can be used to look up the accepting
+ * label, or as the start state of a continuing match.
+ *
+ * Returns: final state reached after input is consumed
+ */
+unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start,
+ const char *str, const char **retpos)
+{
+ u16 *def = DEFAULT_TABLE(dfa);
+ u32 *base = BASE_TABLE(dfa);
+ u16 *next = NEXT_TABLE(dfa);
+ u16 *check = CHECK_TABLE(dfa);
+ u32 *accept = ACCEPT_TABLE(dfa);
unsigned int state = start, pos;
if (state == 0)
@@ -345,48 +530,60 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
/* Equivalence class table defined */
u8 *equiv = EQUIV_TABLE(dfa);
/* default is direct to next state */
- for (; len; len--) {
+ while (*str) {
pos = base_idx(base[state]) + equiv[(u8) *str++];
if (check[pos] == state)
state = next[pos];
else
state = def[state];
+ if (accept[state])
+ break;
}
} else {
/* default is direct to next state */
- for (; len; len--) {
+ while (*str) {
pos = base_idx(base[state]) + (u8) *str++;
if (check[pos] == state)
state = next[pos];
else
state = def[state];
+ if (accept[state])
+ break;
}
}
+ *retpos = str;
return state;
}
/**
- * aa_dfa_match - traverse @dfa to find state @str stops at
+ * aa_dfa_matchn_until - traverse @dfa until accept or @n bytes consumed
* @dfa: the dfa to match @str against (NOT NULL)
* @start: the state of the dfa to start matching in
- * @str: the null terminated string of bytes to match against the dfa (NOT NULL)
+ * @str: the string of bytes to match against the dfa (NOT NULL)
+ * @n: length of the string of bytes to match
+ * @retpos: first character in str after match OR str + n
*
- * aa_dfa_match will match @str against the dfa and return the state it
+ * aa_dfa_match_len will match @str against the dfa and return the state it
* finished matching in. The final state can be used to look up the accepting
* label, or as the start state of a continuing match.
*
+ * This function will happily match again the 0 byte and only finishes
+ * when @n input is consumed.
+ *
* Returns: final state reached after input is consumed
*/
-unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
- const char *str)
+unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start,
+ const char *str, int n, const char **retpos)
{
u16 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
u16 *next = NEXT_TABLE(dfa);
u16 *check = CHECK_TABLE(dfa);
+ u32 *accept = ACCEPT_TABLE(dfa);
unsigned int state = start, pos;
+ *retpos = NULL;
if (state == 0)
return 0;
@@ -395,65 +592,149 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
/* Equivalence class table defined */
u8 *equiv = EQUIV_TABLE(dfa);
/* default is direct to next state */
- while (*str) {
+ for (; n; n--) {
pos = base_idx(base[state]) + equiv[(u8) *str++];
if (check[pos] == state)
state = next[pos];
else
state = def[state];
+ if (accept[state])
+ break;
}
} else {
/* default is direct to next state */
- while (*str) {
+ for (; n; n--) {
pos = base_idx(base[state]) + (u8) *str++;
if (check[pos] == state)
state = next[pos];
else
state = def[state];
+ if (accept[state])
+ break;
}
}
+ *retpos = str;
return state;
}
-/**
- * aa_dfa_next - step one character to the next state in the dfa
- * @dfa: the dfa to tranverse (NOT NULL)
- * @state: the state to start in
- * @c: the input character to transition on
- *
- * aa_dfa_match will step through the dfa by one input character @c
- *
- * Returns: state reach after input @c
- */
-unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
- const char c)
+#define inc_wb_pos(wb) \
+do { \
+ wb->pos = (wb->pos + 1) & (wb->size - 1); \
+ wb->len = (wb->len + 1) & (wb->size - 1); \
+} while (0)
+
+/* For DFAs that don't support extended tagging of states */
+static bool is_loop(struct match_workbuf *wb, unsigned int state,
+ unsigned int *adjust)
+{
+ unsigned int pos = wb->pos;
+ unsigned int i;
+
+ if (wb->history[pos] < state)
+ return false;
+
+ for (i = 0; i <= wb->len; i++) {
+ if (wb->history[pos] == state) {
+ *adjust = i;
+ return true;
+ }
+ if (pos == 0)
+ pos = wb->size;
+ pos--;
+ }
+
+ *adjust = i;
+ return true;
+}
+
+static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start,
+ const char *str, struct match_workbuf *wb,
+ unsigned int *count)
{
u16 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
u16 *next = NEXT_TABLE(dfa);
u16 *check = CHECK_TABLE(dfa);
- unsigned int pos;
+ unsigned int state = start, pos;
+
+ AA_BUG(!dfa);
+ AA_BUG(!str);
+ AA_BUG(!wb);
+ AA_BUG(!count);
+
+ *count = 0;
+ if (state == 0)
+ return 0;
/* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC]) {
/* Equivalence class table defined */
u8 *equiv = EQUIV_TABLE(dfa);
/* default is direct to next state */
+ while (*str) {
+ unsigned int adjust;
- pos = base_idx(base[state]) + equiv[(u8) c];
- if (check[pos] == state)
- state = next[pos];
- else
- state = def[state];
+ wb->history[wb->pos] = state;
+ pos = base_idx(base[state]) + equiv[(u8) *str++];
+ if (check[pos] == state)
+ state = next[pos];
+ else
+ state = def[state];
+ if (is_loop(wb, state, &adjust)) {
+ state = aa_dfa_match(dfa, state, str);
+ *count -= adjust;
+ goto out;
+ }
+ inc_wb_pos(wb);
+ (*count)++;
+ }
} else {
/* default is direct to next state */
- pos = base_idx(base[state]) + (u8) c;
- if (check[pos] == state)
- state = next[pos];
- else
- state = def[state];
+ while (*str) {
+ unsigned int adjust;
+
+ wb->history[wb->pos] = state;
+ pos = base_idx(base[state]) + (u8) *str++;
+ if (check[pos] == state)
+ state = next[pos];
+ else
+ state = def[state];
+ if (is_loop(wb, state, &adjust)) {
+ state = aa_dfa_match(dfa, state, str);
+ *count -= adjust;
+ goto out;
+ }
+ inc_wb_pos(wb);
+ (*count)++;
+ }
}
+out:
+ if (!state)
+ *count = 0;
return state;
}
+
+/**
+ * aa_dfa_leftmatch - traverse @dfa to find state @str stops at
+ * @dfa: the dfa to match @str against (NOT NULL)
+ * @start: the state of the dfa to start matching in
+ * @str: the null terminated string of bytes to match against the dfa (NOT NULL)
+ * @count: current count of longest left.
+ *
+ * aa_dfa_match will match @str against the dfa and return the state it
+ * finished matching in. The final state can be used to look up the accepting
+ * label, or as the start state of a continuing match.
+ *
+ * Returns: final state reached after input is consumed
+ */
+unsigned int aa_dfa_leftmatch(struct aa_dfa *dfa, unsigned int start,
+ const char *str, unsigned int *count)
+{
+ DEFINE_MATCH_WB(wb);
+
+ /* TODO: match for extended state dfas */
+
+ return leftmatch_fb(dfa, start, str, &wb, count);
+}
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
index 8c558cbce930..6e8c7ac0b33d 100644
--- a/security/apparmor/mount.c
+++ b/security/apparmor/mount.c
@@ -18,7 +18,7 @@
#include "include/apparmor.h"
#include "include/audit.h"
-#include "include/context.h"
+#include "include/cred.h"
#include "include/domain.h"
#include "include/file.h"
#include "include/match.h"
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
new file mode 100644
index 000000000000..bb24cfa0a164
--- /dev/null
+++ b/security/apparmor/net.c
@@ -0,0 +1,187 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor network mediation
+ *
+ * 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 "include/apparmor.h"
+#include "include/audit.h"
+#include "include/cred.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),
+ { }
+};
+
+static const char * const net_mask_names[] = {
+ "unknown",
+ "send",
+ "receive",
+ "unknown",
+
+ "create",
+ "shutdown",
+ "connect",
+ "unknown",
+
+ "setattr",
+ "getattr",
+ "setcred",
+ "getcred",
+
+ "chmod",
+ "chown",
+ "chgrp",
+ "lock",
+
+ "mmap",
+ "mprot",
+ "unknown",
+ "unknown",
+
+ "accept",
+ "bind",
+ "listen",
+ "unknown",
+
+ "setopt",
+ "getopt",
+ "unknown",
+ "unknown",
+
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown",
+};
+
+
+/* audit callback for net specific fields */
+void audit_net_cb(struct audit_buffer *ab, void *va)
+{
+ struct common_audit_data *sa = va;
+
+ audit_log_format(ab, " family=");
+ if (address_family_names[sa->u.net->family])
+ audit_log_string(ab, address_family_names[sa->u.net->family]);
+ else
+ audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family);
+ audit_log_format(ab, " sock_type=");
+ if (sock_type_names[aad(sa)->net.type])
+ audit_log_string(ab, sock_type_names[aad(sa)->net.type]);
+ else
+ audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type);
+ audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol);
+
+ if (aad(sa)->request & NET_PERMS_MASK) {
+ audit_log_format(ab, " requested_mask=");
+ aa_audit_perm_mask(ab, aad(sa)->request, NULL, 0,
+ net_mask_names, NET_PERMS_MASK);
+
+ if (aad(sa)->denied & NET_PERMS_MASK) {
+ audit_log_format(ab, " denied_mask=");
+ aa_audit_perm_mask(ab, aad(sa)->denied, NULL, 0,
+ net_mask_names, NET_PERMS_MASK);
+ }
+ }
+ 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);
+ }
+}
+
+/* Generic af perm */
+int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
+ u32 request, u16 family, int type)
+{
+ struct aa_perms perms = { };
+ unsigned int state;
+ __be16 buffer[2];
+
+ AA_BUG(family >= AF_MAX);
+ AA_BUG(type < 0 || type >= SOCK_MAX);
+
+ if (profile_unconfined(profile))
+ return 0;
+ state = PROFILE_MEDIATES(profile, AA_CLASS_NET);
+ if (!state)
+ return 0;
+
+ buffer[0] = cpu_to_be16(family);
+ buffer[1] = cpu_to_be16((u16) type);
+ state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer,
+ 4);
+ aa_compute_perms(profile->policy.dfa, state, &perms);
+ aa_apply_modes_to_perms(profile, &perms);
+
+ return aa_check_perms(profile, &perms, request, sa, audit_net_cb);
+}
+
+int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family,
+ int type, int protocol)
+{
+ struct aa_profile *profile;
+ DEFINE_AUDIT_NET(sa, op, NULL, family, type, protocol);
+
+ return fn_for_each_confined(label, profile,
+ aa_profile_af_perm(profile, &sa, request, family,
+ type));
+}
+
+static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request,
+ struct sock *sk)
+{
+ struct aa_profile *profile;
+ DEFINE_AUDIT_SK(sa, op, sk);
+
+ AA_BUG(!label);
+ AA_BUG(!sk);
+
+ if (unconfined(label))
+ return 0;
+
+ return fn_for_each_confined(label, profile,
+ aa_profile_af_sk_perm(profile, &sa, request, sk));
+}
+
+int aa_sk_perm(const char *op, u32 request, struct sock *sk)
+{
+ struct aa_label *label;
+ int error;
+
+ AA_BUG(!sk);
+ AA_BUG(in_interrupt());
+
+ /* TODO: switch to begin_current_label ???? */
+ label = begin_current_label_crit_section();
+ error = aa_label_sk_perm(label, op, request, sk);
+ end_current_label_crit_section(label);
+
+ return error;
+}
+
+
+int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
+ struct socket *sock)
+{
+ AA_BUG(!label);
+ AA_BUG(!sock);
+ AA_BUG(!sock->sk);
+
+ return aa_label_sk_perm(label, op, request, sock->sk);
+}
diff --git a/security/apparmor/nulldfa.in b/security/apparmor/nulldfa.in
index 3cb38022902e..095f42a24cbc 100644
--- a/security/apparmor/nulldfa.in
+++ b/security/apparmor/nulldfa.in
@@ -1 +1,107 @@
-0x1B, 0x5E, 0x78, 0x3D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x04, 0x90, 0x00, 0x00, 0x6E, 0x6F, 0x74, 0x66, 0x6C, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+0x1B, 0x5E, 0x78, 0x3D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x04,
+0x90, 0x00, 0x00, 0x6E, 0x6F, 0x74, 0x66, 0x6C, 0x65, 0x78, 0x00,
+0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index b0b58848c248..c07493ce2376 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -82,7 +82,7 @@
#include "include/apparmor.h"
#include "include/capability.h"
-#include "include/context.h"
+#include "include/cred.h"
#include "include/file.h"
#include "include/ipc.h"
#include "include/match.h"
@@ -210,6 +210,7 @@ static void aa_free_data(void *ptr, void *arg)
void aa_free_profile(struct aa_profile *profile)
{
struct rhashtable *rht;
+ int i;
AA_DEBUG("%s(%p)\n", __func__, profile);
@@ -227,6 +228,9 @@ void aa_free_profile(struct aa_profile *profile)
aa_free_cap_rules(&profile->caps);
aa_free_rlimit_rules(&profile->rlimits);
+ for (i = 0; i < profile->xattr_count; i++)
+ kzfree(profile->xattrs[i]);
+ kzfree(profile->xattrs);
kzfree(profile->dirname);
aa_put_dfa(profile->xmatch);
aa_put_dfa(profile->policy.dfa);
@@ -845,8 +849,9 @@ static struct aa_profile *update_to_newest_parent(struct aa_profile *new)
* @udata: serialized data stream (NOT NULL)
*
* unpack and replace a profile on the profile list and uses of that profile
- * by any aa_task_ctx. If the profile does not exist on the profile list
- * it is added.
+ * by any task creds via invalidating the old version of the profile, which
+ * tasks will notice to update their own cred. If the profile does not exist
+ * on the profile list it is added.
*
* Returns: size of data consumed else error code on failure.
*/
diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c
index b1e629cba70b..b0f9dc3f765a 100644
--- a/security/apparmor/policy_ns.c
+++ b/security/apparmor/policy_ns.c
@@ -21,7 +21,7 @@
#include <linux/string.h>
#include "include/apparmor.h"
-#include "include/context.h"
+#include "include/cred.h"
#include "include/policy_ns.h"
#include "include/label.h"
#include "include/policy.h"
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 59a1a25b7d43..b9e6b2cafa69 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -23,7 +23,7 @@
#include "include/apparmor.h"
#include "include/audit.h"
-#include "include/context.h"
+#include "include/cred.h"
#include "include/crypto.h"
#include "include/match.h"
#include "include/path.h"
@@ -37,7 +37,8 @@
#define v5 5 /* base version */
#define v6 6 /* per entry policydb mediation check */
-#define v7 7 /* full network masking */
+#define v7 7
+#define v8 8 /* full network masking */
/*
* The AppArmor interface treats data as a type byte followed by the
@@ -164,8 +165,9 @@ static void do_loaddata_free(struct work_struct *work)
}
kzfree(d->hash);
- kfree(d->name);
- kvfree(d);
+ kzfree(d->name);
+ kvfree(d->data);
+ kzfree(d);
}
void aa_loaddata_kref(struct kref *kref)
@@ -180,10 +182,16 @@ void aa_loaddata_kref(struct kref *kref)
struct aa_loaddata *aa_loaddata_alloc(size_t size)
{
- struct aa_loaddata *d = kvzalloc(sizeof(*d) + size, GFP_KERNEL);
+ struct aa_loaddata *d;
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
if (d == NULL)
return ERR_PTR(-ENOMEM);
+ d->data = kvzalloc(size, GFP_KERNEL);
+ if (!d->data) {
+ kfree(d);
+ return ERR_PTR(-ENOMEM);
+ }
kref_init(&d->count);
INIT_LIST_HEAD(&d->list);
@@ -196,6 +204,15 @@ static bool inbounds(struct aa_ext *e, size_t size)
return (size <= e->end - e->pos);
}
+static void *kvmemdup(const void *src, size_t len)
+{
+ void *p = kvmalloc(len, GFP_KERNEL);
+
+ if (p)
+ memcpy(p, src, len);
+ return p;
+}
+
/**
* aa_u16_chunck - test and do bounds checking for a u16 size based chunk
* @e: serialized data read head (NOT NULL)
@@ -515,6 +532,35 @@ fail:
return 0;
}
+static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
+{
+ void *pos = e->pos;
+
+ if (unpack_nameX(e, AA_STRUCT, "xattrs")) {
+ int i, size;
+
+ size = unpack_array(e, NULL);
+ profile->xattr_count = size;
+ profile->xattrs = kcalloc(size, sizeof(char *), GFP_KERNEL);
+ if (!profile->xattrs)
+ goto fail;
+ for (i = 0; i < size; i++) {
+ if (!unpack_strdup(e, &profile->xattrs[i], NULL))
+ goto fail;
+ }
+ if (!unpack_nameX(e, AA_ARRAYEND, NULL))
+ goto fail;
+ if (!unpack_nameX(e, AA_STRUCTEND, NULL))
+ goto fail;
+ }
+
+ return 1;
+
+fail:
+ e->pos = pos;
+ return 0;
+}
+
static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
{
void *pos = e->pos;
@@ -549,15 +595,6 @@ fail:
return 0;
}
-static void *kvmemdup(const void *src, size_t len)
-{
- void *p = kvmalloc(len, GFP_KERNEL);
-
- if (p)
- memcpy(p, src, len);
- return p;
-}
-
static u32 strhash(const void *data, u32 len, u32 seed)
{
const char * const *key = data;
@@ -712,6 +749,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
goto fail;
}
+ if (!unpack_xattrs(e, profile)) {
+ info = "failed to unpack profile xattrs";
+ goto fail;
+ }
+
if (!unpack_rlimits(e, profile)) {
info = "failed to unpack profile rlimits";
goto fail;
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c
index d81617379d63..80c34ed373c3 100644
--- a/security/apparmor/procattr.c
+++ b/security/apparmor/procattr.c
@@ -13,7 +13,7 @@
*/
#include "include/apparmor.h"
-#include "include/context.h"
+#include "include/cred.h"
#include "include/policy.h"
#include "include/policy_ns.h"
#include "include/domain.h"
diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c
index cf4d234febe9..d022137143b9 100644
--- a/security/apparmor/resource.c
+++ b/security/apparmor/resource.c
@@ -16,7 +16,7 @@
#include <linux/security.h>
#include "include/audit.h"
-#include "include/context.h"
+#include "include/cred.h"
#include "include/resource.h"
#include "include/policy.h"
diff --git a/security/apparmor/stacksplitdfa.in b/security/apparmor/stacksplitdfa.in
new file mode 100644
index 000000000000..4bddd10b62a9
--- /dev/null
+++ b/security/apparmor/stacksplitdfa.in
@@ -0,0 +1,114 @@
+/* 0x1 [^\000]*[^/\000]//& */ 0x1B, 0x5E, 0x78, 0x3D, 0x00, 0x00,
+0x00, 0x18, 0x00, 0x00, 0x04, 0xD8, 0x00, 0x00, 0x6E, 0x6F, 0x74,
+0x66, 0x6C, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02,
+0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x08, 0x00,
+0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00,
+0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
+0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
+0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00
diff --git a/security/apparmor/context.c b/security/apparmor/task.c
index c95f1ac6190b..c6b78a14da91 100644
--- a/security/apparmor/context.c
+++ b/security/apparmor/task.c
@@ -1,72 +1,23 @@
/*
* AppArmor security module
*
- * This file contains AppArmor functions used to manipulate object security
- * contexts.
+ * This file contains AppArmor task related definitions and mediation
*
- * Copyright (C) 1998-2008 Novell/SUSE
- * Copyright 2009-2010 Canonical Ltd.
+ * 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.
*
- *
- * 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
- * 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
- * the task_ctx is reference counted.
- *
* TODO
* If a task uses change_hat it currently does not return to the old
* cred or task context but instead creates a new one. Ideally the task
* should return to the previous cred if it has not been modified.
- *
*/
-#include "include/context.h"
-#include "include/policy.h"
-
-/**
- * aa_alloc_task_context - allocate a new task_ctx
- * @flags: gfp flags for allocation
- *
- * Returns: allocated buffer or NULL on failure
- */
-struct aa_task_ctx *aa_alloc_task_context(gfp_t flags)
-{
- return kzalloc(sizeof(struct aa_task_ctx), flags);
-}
-
-/**
- * aa_free_task_context - free a task_ctx
- * @ctx: task_ctx to free (MAYBE NULL)
- */
-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);
-
- kzfree(ctx);
- }
-}
-
-/**
- * aa_dup_task_context - duplicate a task context, incrementing reference counts
- * @new: a blank task context (NOT NULL)
- * @old: the task context to copy (NOT NULL)
- */
-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);
-}
+#include "include/cred.h"
+#include "include/task.h"
/**
* aa_get_task_label - Get another task's label
@@ -93,11 +44,13 @@ struct aa_label *aa_get_task_label(struct task_struct *task)
*/
int aa_replace_current_label(struct aa_label *label)
{
- struct aa_task_ctx *ctx = current_ctx();
+ struct aa_label *old = aa_current_raw_label();
+ struct aa_task_ctx *ctx = task_ctx(current);
struct cred *new;
+
AA_BUG(!label);
- if (ctx->label == label)
+ if (old == label)
return 0;
if (current_cred() != current_real_cred())
@@ -107,27 +60,34 @@ int aa_replace_current_label(struct aa_label *label)
if (!new)
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 (ctx->nnp && label_is_stale(ctx->nnp)) {
+ struct aa_label *tmp = ctx->nnp;
+
+ ctx->nnp = aa_get_newest_label(tmp);
+ aa_put_label(tmp);
+ }
+ if (unconfined(label) || (labels_ns(old) != labels_ns(label)))
+ /*
+ * if switching to unconfined or a different label namespace
* clear out context state
*/
- aa_clear_task_ctx_trans(ctx);
+ aa_clear_task_ctx_trans(task_ctx(current));
/*
- * be careful switching ctx->profile, when racing replacement it
- * is possible that ctx->profile->proxy->profile is the reference
- * keeping @profile valid, so make sure to get its reference before
- * dropping the reference on ctx->profile
+ * be careful switching cred label, when racing replacement it
+ * is possible that the cred labels's->proxy->label is the reference
+ * keeping @label valid, so make sure to get its reference before
+ * dropping the reference on the cred's label
*/
aa_get_label(label);
- aa_put_label(ctx->label);
- ctx->label = label;
+ aa_put_label(cred_label(new));
+ cred_label(new) = label;
commit_creds(new);
return 0;
}
+
/**
* aa_set_current_onexec - set the tasks change_profile to happen onexec
* @label: system label to set at exec (MAYBE NULL to clear value)
@@ -136,18 +96,13 @@ int aa_replace_current_label(struct aa_label *label)
*/
int aa_set_current_onexec(struct aa_label *label, bool stack)
{
- struct aa_task_ctx *ctx;
- struct cred *new = prepare_creds();
- if (!new)
- return -ENOMEM;
+ struct aa_task_ctx *ctx = task_ctx(current);
- ctx = cred_ctx(new);
aa_get_label(label);
- aa_clear_task_ctx_trans(ctx);
+ aa_put_label(ctx->onexec);
ctx->onexec = label;
ctx->token = stack;
- commit_creds(new);
return 0;
}
@@ -163,25 +118,27 @@ int aa_set_current_onexec(struct aa_label *label, bool stack)
*/
int aa_set_current_hat(struct aa_label *label, u64 token)
{
- struct aa_task_ctx *ctx;
- struct cred *new = prepare_creds();
+ struct aa_task_ctx *ctx = task_ctx(current);
+ struct cred *new;
+
+ new = prepare_creds();
if (!new)
return -ENOMEM;
AA_BUG(!label);
- ctx = cred_ctx(new);
if (!ctx->previous) {
/* transfer refcount */
- ctx->previous = ctx->label;
+ ctx->previous = cred_label(new);
ctx->token = token;
} else if (ctx->token == token) {
- aa_put_label(ctx->label);
+ aa_put_label(cred_label(new));
} else {
/* previous_profile && ctx->token != token */
abort_creds(new);
return -EACCES;
}
- ctx->label = aa_get_newest_label(label);
+
+ cred_label(new) = aa_get_newest_label(label);
/* clear exec on switching context */
aa_put_label(ctx->onexec);
ctx->onexec = NULL;
@@ -201,28 +158,26 @@ int aa_set_current_hat(struct aa_label *label, u64 token)
*/
int aa_restore_previous_label(u64 token)
{
- struct aa_task_ctx *ctx;
- struct cred *new = prepare_creds();
- if (!new)
- return -ENOMEM;
+ struct aa_task_ctx *ctx = task_ctx(current);
+ struct cred *new;
- ctx = cred_ctx(new);
- if (ctx->token != token) {
- abort_creds(new);
+ if (ctx->token != token)
return -EACCES;
- }
/* ignore restores when there is no saved label */
- if (!ctx->previous) {
- abort_creds(new);
+ if (!ctx->previous)
return 0;
- }
- aa_put_label(ctx->label);
- ctx->label = aa_get_newest_label(ctx->previous);
- AA_BUG(!ctx->label);
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
+ aa_put_label(cred_label(new));
+ cred_label(new) = aa_get_newest_label(ctx->previous);
+ AA_BUG(!cred_label(new));
/* clear exec && prev information when restoring to previous context */
aa_clear_task_ctx_trans(ctx);
commit_creds(new);
+
return 0;
}