Commit 80a17a5f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'apparmor-pr-2018-04-10' of...

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
  ...
parents edda4153 588558eb
......@@ -934,8 +934,8 @@ F: drivers/char/apm-emulation.c
APPARMOR SECURITY MODULE
M: John Johansen <john.johansen@canonical.com>
L: apparmor@lists.ubuntu.com (subscribers-only, general discussion)
W: apparmor.wiki.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git
W: wiki.apparmor.net
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor
S: Supported
F: security/apparmor/
F: Documentation/admin-guide/LSM/apparmor.rst
......
#
# Generated include files
#
net_names.h
capability_names.h
rlim_names.h
......@@ -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)
......@@ -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");
......
......@@ -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"
......
......@@ -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"
......@@ -301,8 +302,71 @@ static int change_profile_perms(struct aa_profile *profile,
return label_match(profile, target, stack, start, true, request, perms);
}
/**
* 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 @@ static struct aa_label *change_hat(struct aa_label *label, const char *hats[],
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 @@ int aa_change_profile(const char *fqname, int flags)
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 @@ int aa_change_profile(const char *fqname, int flags)
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,
......
......@@ -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();
......
......@@ -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
......
......@@ -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 {
......@@ -134,10 +147,6 @@ struct apparmor_audit_data {
const char *ns;
long pos;
} iface;
struct {
int rlim;
unsigned long max;
} rlim;
struct {
const char *src_name;
const char *type;
......
......@@ -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;
}
/**
......@@ -95,17 +66,6 @@ static inline struct aa_label *__aa_task_raw_label(struct task_struct *task)
return aa_cred_raw_label(__task_cred(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
*
......@@ -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 */
......@@ -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,
......
......@@ -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 */
/*
* 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 */
......@@ -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,
......
......@@ -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);
}
/**
......
......@@ -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);
......
......@@ -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",
......
/*
* 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 */
......@@ -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));
}
......@@ -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 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
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
......
......@@ -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];
......
......@@ -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 @@ static int apparmor_setprocattr(const char *name, void *value,
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;
}
......
......@@ -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 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
}
/**
* 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 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
*
* 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 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
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);
}
......@@ -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"
......
/*
* 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);
}
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
......@@ -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.
*/
......
......@@ -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"
......
......@@ -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 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
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 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
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;
......
......@@ -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"
......
......@@ -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"
......
/* 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
/*
* 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment