+ 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 -----BEGIN PGP SIGNATURE----- iQIcBAABCgAGBQJazWpMAAoJEAUvNnAY1cPY2wwP/2ZmzyITY7xW3Cpz8ynKOTyZ hD2ahIjLWxcQwMZUoHXIa4TTK5EThlhKcTa0+sdMJGsIsRyXLoyBcd/VST0F9ZrA OWn1uL2ASeNroNw+88P6qU03+cT2eEohM3vvlNy2ud98EBiTyxB6L4VLpy3xDKAd zblojxqegRO7WRfEFCR2kHmnrL0Z3oxPBahnuVitfrwO76WFUSM9EYm67Xtf4yjJ qQ7ocGdhxiULNdceoIke11e8iNwiQyY4O+E24qVAJw66arxIByMKo+cLjeTxMbZR z4/pVd664wiK7mW0In7bJWOfXLJHxHALpuCc82wFgiLPdfSpJzT1nx+Xjaw8DhdZ FBoHLpHjJT3dTpYoQTjqtNdvHgXryL/OOllm+I8DPMu/nfcp8qsOru5bEXg+j/90 CRo1OqrWZhUkKHnQs12QIJS+Gt7qByQB6tDMDbjkIC71vKUWA4wnp7zLZHYd9T0L 6kZ2aWKiOXM6VRZ5V5HVLhrTajiubyBg3y3Eur4HwuGzquBmxAp1RhS8oiOpgzgW jVI92/P2XjhnU9E2J5m+mzjh11i+D51homtz1y4vB53Ye/WLy1S0o4StDAiLfgw3 q/581V342vl6X46GlgcS5G7QeIkzFiCUe5H3t2/unCRnI+PxabwRmbaTqWq47xzQ umwlYfok3ALSzdgnv2sT =XhxG -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
80a17a5f50
|
@ -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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#
|
||||
# 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;
|
||||
unsigned int state, count;
|
||||
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) {
|
||||
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 */
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* AppArmor security module
|
||||
*
|
||||
* This file contains AppArmor network mediation definitions.
|
||||
*
|
||||
* Copyright (C) 1998-2008 Novell/SUSE
|
||||
* Copyright 2009-2017 Canonical Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2 of the
|
||||
* License.
|
||||
*/
|
||||
|
||||
#ifndef __AA_NET_H
|
||||
#define __AA_NET_H
|
||||
|
||||
#include <net/sock.h>
|
||||
#include <linux/path.h>
|
||||
|
||||
#include "apparmorfs.h"
|
||||
#include "label.h"
|
||||
#include "perms.h"
|
||||
#include "policy.h"
|
||||
|
||||
#define AA_MAY_SEND AA_MAY_WRITE
|
||||
#define AA_MAY_RECEIVE AA_MAY_READ
|
||||
|
||||
#define AA_MAY_SHUTDOWN AA_MAY_DELETE
|
||||
|
||||
#define AA_MAY_CONNECT AA_MAY_OPEN
|
||||
#define AA_MAY_ACCEPT 0x00100000
|
||||
|
||||
#define AA_MAY_BIND 0x00200000
|
||||
#define AA_MAY_LISTEN 0x00400000
|
||||
|
||||
#define AA_MAY_SETOPT 0x01000000
|
||||
#define AA_MAY_GETOPT 0x02000000
|
||||
|
||||
#define NET_PERMS_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \
|
||||
AA_MAY_SHUTDOWN | AA_MAY_BIND | AA_MAY_LISTEN | \
|
||||
AA_MAY_CONNECT | AA_MAY_ACCEPT | AA_MAY_SETATTR | \
|
||||
AA_MAY_GETATTR | AA_MAY_SETOPT | AA_MAY_GETOPT)
|
||||
|
||||
#define NET_FS_PERMS (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \
|
||||
AA_MAY_SHUTDOWN | AA_MAY_CONNECT | AA_MAY_RENAME |\
|
||||
AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_CHMOD | \
|
||||
AA_MAY_CHOWN | AA_MAY_CHGRP | AA_MAY_LOCK | \
|
||||
AA_MAY_MPROT)
|
||||
|
||||
#define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \
|
||||
AA_MAY_ACCEPT)
|
||||
struct aa_sk_ctx {
|
||||
struct aa_label *label;
|
||||
struct aa_label *peer;
|
||||
};
|
||||
|
||||
#define SK_CTX(X) ((X)->sk_security)
|
||||
#define SOCK_ctx(X) SOCK_INODE(X)->i_security
|
||||
#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \
|
||||
struct lsm_network_audit NAME ## _net = { .sk = (SK), \
|
||||
.family = (F)}; \
|
||||
DEFINE_AUDIT_DATA(NAME, \
|
||||
((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \
|
||||
LSM_AUDIT_DATA_NONE, \
|
||||
OP); \
|
||||
NAME.u.net = &(NAME ## _net); \
|
||||
aad(&NAME)->net.type = (T); \
|
||||
aad(&NAME)->net.protocol = (P)
|
||||
|
||||
#define DEFINE_AUDIT_SK(NAME, OP, SK) \
|
||||
DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \
|
||||
(SK)->sk_protocol)
|
||||
|
||||
|
||||
#define af_select(FAMILY, FN, DEF_FN) \
|
||||
({ \
|
||||
int __e; \
|
||||
switch ((FAMILY)) { \
|
||||
default: \
|
||||
__e = DEF_FN; \
|
||||
} \
|
||||
__e; \
|
||||
})
|
||||
|
||||
extern struct aa_sfs_entry aa_sfs_entry_network[];
|
||||
|
||||
void audit_net_cb(struct audit_buffer *ab, void *va);
|
||||
int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
|
||||
u32 request, u16 family, int type);
|
||||
int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family,
|
||||
int type, int protocol);
|
||||
static inline int aa_profile_af_sk_perm(struct aa_profile *profile,
|
||||
struct common_audit_data *sa,
|
||||
u32 request,
|
||||
struct sock *sk)
|
||||
{
|
||||
return aa_profile_af_perm(profile, sa, request, sk->sk_family,
|
||||
sk->sk_type);
|
||||
}
|
||||
int aa_sk_perm(const char *op, u32 request, struct sock *sk);
|
||||
|
||||
int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
|
||||
struct socket *sock);
|
||||
|
||||
#endif /* __AA_NET_H */
|
|
@ -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",
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* AppArmor security module
|
||||
*
|
||||
* This file contains AppArmor task related definitions and mediation
|
||||
*
|
||||
* Copyright 2017 Canonical Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2 of the
|
||||
* License.
|
||||
*/
|
||||
|
||||
#ifndef __AA_TASK_H
|
||||
#define __AA_TASK_H
|
||||
|
||||
#define task_ctx(X) ((X)->security)
|
||||
|
||||
/*
|
||||
* struct aa_task_ctx - information for current task label change
|
||||
* @nnp: snapshot of label at time of no_new_privs
|
||||
* @onexec: profile to transition to on next exec (MAY BE NULL)
|
||||
* @previous: profile the task may return to (MAY BE NULL)
|
||||
* @token: magic value the task must know for returning to @previous_profile
|
||||
*/
|
||||
struct aa_task_ctx {
|
||||
struct aa_label *nnp;
|
||||
struct aa_label *onexec;
|
||||
struct aa_label *previous;
|
||||
u64 token;
|
||||
};
|
||||
|
||||
int aa_replace_current_label(struct aa_label *label);
|
||||
int aa_set_current_onexec(struct aa_label *label, bool stack);
|
||||
int aa_set_current_hat(struct aa_label *label, u64 token);
|
||||
int aa_restore_previous_label(u64 cookie);
|
||||
struct aa_label *aa_get_task_label(struct task_struct *task);
|
||||
|
||||
/**
|
||||
* aa_alloc_task_ctx - allocate a new task_ctx
|
||||
* @flags: gfp flags for allocation
|
||||
*
|
||||
* Returns: allocated buffer or NULL on failure
|
||||
*/
|
||||
static inline struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags)
|
||||
{
|
||||
return kzalloc(sizeof(struct aa_task_ctx), flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_free_task_ctx - free a task_ctx
|
||||
* @ctx: task_ctx to free (MAYBE NULL)
|
||||
*/
|
||||
static inline void aa_free_task_ctx(struct aa_task_ctx *ctx)
|
||||
{
|
||||
if (ctx) {
|
||||
aa_put_label(ctx->nnp);
|
||||
aa_put_label(ctx->previous);
|
||||
aa_put_label(ctx->onexec);
|
||||
|
||||
kzfree(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_dup_task_ctx - duplicate a task context, incrementing reference counts
|
||||
* @new: a blank task context (NOT NULL)
|
||||
* @old: the task context to copy (NOT NULL)
|
||||
*/
|
||||
static inline void aa_dup_task_ctx(struct aa_task_ctx *new,
|
||||
const struct aa_task_ctx *old)
|
||||
{
|
||||
*new = *old;
|
||||
aa_get_label(new->nnp);
|
||||
aa_get_label(new->previous);
|
||||
aa_get_label(new->onexec);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_clear_task_ctx_trans - clear transition tracking info from the ctx
|
||||
* @ctx: task context to clear (NOT NULL)
|
||||
*/
|
||||
static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx)
|
||||
{
|
||||
AA_BUG(!ctx);
|
||||
|
||||
aa_put_label(ctx->previous);
|
||||
aa_put_label(ctx->onexec);
|
||||
ctx->previous = NULL;
|
||||
ctx->onexec = NULL;
|
||||
ctx->token = 0;
|
||||
}
|
||||
|
||||
#endif /* __AA_TASK_H */
|
|
@ -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));
|
||||
}
|
||||
|
||||
aa_dup_task_context(new_ctx, old_ctx);
|
||||
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;
|
||||
|
||||
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 state_count, trans_count;
|
||||
int error = -EPROTO;
|
||||
|
||||
/* check that required tables exist */
|
||||
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 = tables[YYTD_ID_BASE]->td_lolen;
|
||||
if (ACCEPT1_FLAGS(flags)) {
|
||||
if (!tables[YYTD_ID_ACCEPT])
|
||||
goto out;
|
||||
if (state_count != tables[YYTD_ID_ACCEPT]->td_lolen)
|
||||
goto out;
|
||||
}
|
||||
if (ACCEPT2_FLAGS(flags)) {
|
||||
if (!tables[YYTD_ID_ACCEPT2])
|
||||
goto out;
|
||||
if (state_count != tables[YYTD_ID_ACCEPT2]->td_lolen)
|
||||
goto out;
|
||||
}
|
||||
if (state_count != tables[YYTD_ID_DEF]->td_lolen)
|
||||
goto out;
|
||||
|
||||
/* next.size == chk.size */
|
||||
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 (tables[YYTD_ID_EC] && tables[YYTD_ID_EC]->td_lolen != 256)
|
||||
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;
|
||||
|
||||
/* 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]))
|
||||
goto out;
|
||||
|
||||
/* accept.size == default.size == base.size */
|
||||
state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
|
||||
if (ACCEPT1_FLAGS(flags)) {
|
||||
if (!dfa->tables[YYTD_ID_ACCEPT])
|
||||
goto out;
|
||||
if (state_count != dfa->tables[YYTD_ID_ACCEPT]->td_lolen)
|
||||
goto out;
|
||||
}
|
||||
if (ACCEPT2_FLAGS(flags)) {
|
||||
if (!dfa->tables[YYTD_ID_ACCEPT2])
|
||||
goto out;
|
||||
if (state_count != dfa->tables[YYTD_ID_ACCEPT2]->td_lolen)
|
||||
goto out;
|
||||
}
|
||||
if (state_count != dfa->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)
|
||||
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)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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)
|
||||
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,7 +408,7 @@ 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, pos;
|
||||
unsigned int state = start;
|
||||
|
||||
if (state == 0)
|
||||
return 0;
|
||||
|
@ -344,23 +417,13 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
|
|||
if (dfa->tables[YYTD_ID_EC]) {
|
||||
/* Equivalence class table defined */
|
||||
u8 *equiv = EQUIV_TABLE(dfa);
|
||||
/* default is direct to next state */
|
||||
for (; len; len--) {
|
||||
pos = base_idx(base[state]) + equiv[(u8) *str++];
|
||||
if (check[pos] == state)
|
||||
state = next[pos];
|
||||
else
|
||||
state = def[state];
|
||||
}
|
||||
for (; len; len--)
|
||||
match_char(state, def, base, next, check,
|
||||
equiv[(u8) *str++]);
|
||||
} else {
|
||||
/* default is direct to next state */
|
||||
for (; len; len--) {
|
||||
pos = base_idx(base[state]) + (u8) *str++;
|
||||
if (check[pos] == state)
|
||||
state = next[pos];
|
||||
else
|
||||
state = def[state];
|
||||
}
|
||||
for (; len; len--)
|
||||
match_char(state, def, base, next, check, (u8) *str++);
|
||||
}
|
||||
|
||||
return state;
|
||||
|
@ -385,7 +448,7 @@ unsigned int aa_dfa_match(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, pos;
|
||||
unsigned int state = start;
|
||||
|
||||
if (state == 0)
|
||||
return 0;
|
||||
|
@ -395,22 +458,13 @@ 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) {
|
||||
pos = base_idx(base[state]) + equiv[(u8) *str++];
|
||||
if (check[pos] == state)
|
||||
state = next[pos];
|
||||
else
|
||||
state = def[state];
|
||||
}
|
||||
while (*str)
|
||||
match_char(state, def, base, next, check,
|
||||
equiv[(u8) *str++]);
|
||||
} else {
|
||||
/* default is direct to next state */
|
||||
while (*str) {
|
||||
pos = base_idx(base[state]) + (u8) *str++;
|
||||
if (check[pos] == state)
|
||||
state = next[pos];
|
||||
else
|
||||
state = def[state];
|
||||
}
|
||||
while (*str)
|
||||
match_char(state, def, base, next, check, (u8) *str++);
|
||||
}
|
||||
|
||||
return state;
|
||||
|
@ -433,27 +487,254 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
|
|||
u32 *base = BASE_TABLE(dfa);
|
||||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
unsigned int pos;
|
||||
|
||||
/* 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)
|
||||
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 */
|
||||
|
||||
pos = base_idx(base[state]) + equiv[(u8) c];
|
||||
if (check[pos] == state)
|
||||
state = next[pos];
|
||||
else
|
||||
state = def[state];
|
||||
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 */
|
||||
pos = base_idx(base[state]) + (u8) c;
|
||||
if (check[pos] == state)
|
||||
state = next[pos];
|
||||
else
|
||||
state = def[state];
|
||||
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_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 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_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_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;
|
||||
|
||||
/* 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 */
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
||||
#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 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;
|
||||
|
||||
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 */
|
||||
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"
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* AppArmor security module
|
||||
*
|
||||
* This file contains AppArmor network mediation
|
||||
*
|
||||
* Copyright (C) 1998-2008 Novell/SUSE
|
||||
* Copyright 2009-2017 Canonical Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2 of the
|
||||
* License.
|
||||
*/
|
||||
|
||||
#include "include/apparmor.h"
|
||||
#include "include/audit.h"
|
||||
#include "include/cred.h"
|
||||
#include "include/label.h"
|
||||
#include "include/net.h"
|
||||
#include "include/policy.h"
|
||||
|
||||
#include "net_names.h"
|
||||
|
||||
|
||||
struct aa_sfs_entry aa_sfs_entry_network[] = {
|
||||
AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const char * const net_mask_names[] = {
|
||||
"unknown",
|
||||
"send",
|
||||
"receive",
|
||||
"unknown",
|
||||
|
||||
"create",
|
||||
"shutdown",
|
||||
"connect",
|
||||
"unknown",
|
||||
|
||||
"setattr",
|
||||
"getattr",
|
||||
"setcred",
|
||||
"getcred",
|
||||
|
||||
"chmod",
|
||||
"chown",
|
||||
"chgrp",
|
||||
"lock",
|
||||
|
||||
"mmap",
|
||||
"mprot",
|
||||
"unknown",
|
||||
"unknown",
|
||||
|
||||
"accept",
|
||||
"bind",
|
||||
"listen",
|
||||
"unknown",
|
||||
|
||||
"setopt",
|
||||
"getopt",
|
||||
"unknown",
|
||||
"unknown",
|
||||
|
||||
"unknown",
|
||||
"unknown",
|
||||
"unknown",
|
||||
"unknown",
|
||||
};
|
||||
|
||||
|
||||
/* audit callback for net specific fields */
|
||||
void audit_net_cb(struct audit_buffer *ab, void *va)
|
||||
{
|
||||
struct common_audit_data *sa = va;
|
||||
|
||||
audit_log_format(ab, " family=");
|
||||
if (address_family_names[sa->u.net->family])
|
||||
audit_log_string(ab, address_family_names[sa->u.net->family]);
|
||||
else
|
||||
audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family);
|
||||
audit_log_format(ab, " sock_type=");
|
||||
if (sock_type_names[aad(sa)->net.type])
|
||||
audit_log_string(ab, sock_type_names[aad(sa)->net.type]);
|
||||
else
|
||||
audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type);
|
||||
audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol);
|
||||
|
||||
if (aad(sa)->request & NET_PERMS_MASK) {
|
||||
audit_log_format(ab, " requested_mask=");
|
||||
aa_audit_perm_mask(ab, aad(sa)->request, NULL, 0,
|
||||
net_mask_names, NET_PERMS_MASK);
|
||||
|
||||
if (aad(sa)->denied & NET_PERMS_MASK) {
|
||||
audit_log_format(ab, " denied_mask=");
|
||||
aa_audit_perm_mask(ab, aad(sa)->denied, NULL, 0,
|
||||
net_mask_names, NET_PERMS_MASK);
|
||||
}
|
||||
}
|
||||
if (aad(sa)->peer) {
|
||||
audit_log_format(ab, " peer=");
|
||||
aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
|
||||
FLAGS_NONE, GFP_ATOMIC);
|
||||
}
|
||||
}
|
||||
|
||||
/* Generic af perm */
|
||||
int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
|
||||
u32 request, u16 family, int type)
|
||||
{
|
||||
struct aa_perms perms = { };
|
||||
unsigned int state;
|
||||
__be16 buffer[2];
|
||||
|
||||
AA_BUG(family >= AF_MAX);
|
||||
AA_BUG(type < 0 || type >= SOCK_MAX);
|
||||
|
||||
if (profile_unconfined(profile))
|
||||
return 0;
|
||||
state = PROFILE_MEDIATES(profile, AA_CLASS_NET);
|
||||
if (!state)
|
||||
return 0;
|
||||
|
||||
buffer[0] = cpu_to_be16(family);
|
||||
buffer[1] = cpu_to_be16((u16) type);
|
||||
state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer,
|
||||
4);
|
||||
aa_compute_perms(profile->policy.dfa, state, &perms);
|
||||
aa_apply_modes_to_perms(profile, &perms);
|
||||
|
||||
return aa_check_perms(profile, &perms, request, sa, audit_net_cb);
|
||||
}
|
||||
|
||||
int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family,
|
||||
int type, int protocol)
|
||||
{
|
||||
struct aa_profile *profile;
|
||||
DEFINE_AUDIT_NET(sa, op, NULL, family, type, protocol);
|
||||
|
||||
return fn_for_each_confined(label, profile,
|
||||
aa_profile_af_perm(profile, &sa, request, family,
|
||||
type));
|
||||
}
|
||||
|
||||
static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request,
|
||||
struct sock *sk)
|
||||
{
|
||||
struct aa_profile *profile;
|
||||
DEFINE_AUDIT_SK(sa, op, sk);
|
||||
|
||||
AA_BUG(!label);
|
||||
AA_BUG(!sk);
|
||||
|
||||
if (unconfined(label))
|
||||
return 0;
|
||||
|
||||
return fn_for_each_confined(label, profile,
|
||||
aa_profile_af_sk_perm(profile, &sa, request, sk));
|
||||
}
|
||||
|
||||
int aa_sk_perm(const char *op, u32 request, struct sock *sk)
|
||||
{
|
||||
struct aa_label *label;
|
||||
int error;
|
||||
|
||||
AA_BUG(!sk);
|
||||
AA_BUG(in_interrupt());
|
||||
|
||||
/* TODO: switch to begin_current_label ???? */
|
||||
label = begin_current_label_crit_section();
|
||||
error = aa_label_sk_perm(label, op, request, sk);
|
||||
end_current_label_crit_section(label);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
|
||||
struct socket *sock)
|
||||
{
|
||||
AA_BUG(!label);
|
||||
AA_BUG(!sock);
|
||||
AA_BUG(!sock->sk);
|
||||
|
||||
return aa_label_sk_perm(label, op, request, sock->sk);
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -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"
|
||||
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/* 0x1 [^\000]*[^/\000]//& */ 0x1B, 0x5E, 0x78, 0x3D, 0x00, 0x00,
|
||||
0x00, 0x18, 0x00, 0x00, 0x04, 0xD8, 0x00, 0x00, 0x6E, 0x6F, 0x74,
|
||||
0x66, 0x6C, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
|
||||
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02,
|
||||
0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x08, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00,
|
||||
0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
|
||||
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00
|
|
@ -1,72 +1,23 @@
|
|||
/*
|
||||
* AppArmor security module
|
||||
*
|
||||
* This file contains AppArmor functions used to manipulate object security
|
||||
* contexts.
|
||||
* This file contains AppArmor task related definitions and mediation
|
||||
*
|
||||
* Copyright (C) 1998-2008 Novell/SUSE
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
* Copyright 2017 Canonical Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2 of the
|
||||
* License.
|
||||
*
|
||||
*
|
||||
* AppArmor sets confinement on every task, via the the aa_task_ctx and
|
||||
* the aa_task_ctx.label, both of which are required and are not allowed
|
||||
* to be NULL. The aa_task_ctx is not reference counted and is unique
|
||||
* to each cred (which is reference count). The label pointed to by
|
||||
* the task_ctx is reference counted.
|
||||
*
|
||||
* TODO
|
||||
* If a task uses change_hat it currently does not return to the old
|
||||
* cred or task context but instead creates a new one. Ideally the task
|
||||
* should return to the previous cred if it has not been modified.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "include/context.h"
|
||||
#include "include/policy.h"
|
||||
|
||||
/**
|
||||
* aa_alloc_task_context - allocate a new task_ctx
|
||||
* @flags: gfp flags for allocation
|
||||
*
|
||||
* Returns: allocated buffer or NULL on failure
|
||||
*/
|
||||
struct aa_task_ctx *aa_alloc_task_context(gfp_t flags)
|
||||
{
|
||||
return kzalloc(sizeof(struct aa_task_ctx), flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_free_task_context - free a task_ctx
|
||||
* @ctx: task_ctx to free (MAYBE NULL)
|
||||
*/
|
||||
void aa_free_task_context(struct aa_task_ctx *ctx)
|
||||
{
|
||||
if (ctx) {
|
||||
aa_put_label(ctx->label);
|
||||
aa_put_label(ctx->previous);
|
||||
aa_put_label(ctx->onexec);
|
||||
|
||||
kzfree(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_dup_task_context - duplicate a task context, incrementing reference counts
|
||||
* @new: a blank task context (NOT NULL)
|
||||
* @old: the task context to copy (NOT NULL)
|
||||
*/
|
||||
void aa_dup_task_context(struct aa_task_ctx *new, const struct aa_task_ctx *old)
|
||||
{
|
||||
*new = *old;
|
||||
aa_get_label(new->label);
|
||||
aa_get_label(new->previous);
|
||||
aa_get_label(new->onexec);
|
||||
}
|
||||
#include "include/cred.h"
|
||||
#include "include/task.h"
|
||||
|
||||
/**
|
||||
* aa_get_task_label - Get another task's label
|
||||
|
@ -93,11 +44,13 @@ struct aa_label *aa_get_task_label(struct task_struct *task)
|
|||
*/
|
||||
int aa_replace_current_label(struct aa_label *label)
|
||||
{
|
||||
struct aa_task_ctx *ctx = current_ctx();
|
||||
struct aa_label *old = aa_current_raw_label();
|
||||
struct aa_task_ctx *ctx = task_ctx(current);
|
||||
struct cred *new;
|
||||
|
||||
AA_BUG(!label);
|
||||
|
||||
if (ctx->label == label)
|
||||
if (old == label)
|
||||
return 0;
|
||||
|
||||
if (current_cred() != current_real_cred())
|
||||
|
@ -107,27 +60,34 @@ int aa_replace_current_label(struct aa_label *label)
|
|||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx = cred_ctx(new);
|
||||
if (unconfined(label) || (labels_ns(ctx->label) != labels_ns(label)))
|
||||
/* if switching to unconfined or a different label namespace
|
||||
if (ctx->nnp && label_is_stale(ctx->nnp)) {
|
||||
struct aa_label *tmp = ctx->nnp;
|
||||
|
||||
ctx->nnp = aa_get_newest_label(tmp);
|
||||
aa_put_label(tmp);
|
||||
}
|
||||
if (unconfined(label) || (labels_ns(old) != labels_ns(label)))
|
||||
/*
|
||||
* if switching to unconfined or a different label namespace
|
||||
* clear out context state
|
||||
*/
|
||||
aa_clear_task_ctx_trans(ctx);
|
||||
aa_clear_task_ctx_trans(task_ctx(current));
|
||||
|
||||
/*
|
||||
* be careful switching ctx->profile, when racing replacement it
|
||||
* is possible that ctx->profile->proxy->profile is the reference
|
||||
* keeping @profile valid, so make sure to get its reference before
|
||||
* dropping the reference on ctx->profile
|
||||
* be careful switching cred label, when racing replacement it
|
||||
* is possible that the cred labels's->proxy->label is the reference
|
||||
* keeping @label valid, so make sure to get its reference before
|
||||
* dropping the reference on the cred's label
|
||||
*/
|
||||
aa_get_label(label);
|
||||
aa_put_label(ctx->label);
|
||||
ctx->label = label;
|
||||
aa_put_label(cred_label(new));
|
||||
cred_label(new) = label;
|
||||
|
||||
commit_creds(new);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* aa_set_current_onexec - set the tasks change_profile to happen onexec
|
||||
* @label: system label to set at exec (MAYBE NULL to clear value)
|
||||
|
@ -136,18 +96,13 @@ int aa_replace_current_label(struct aa_label *label)
|
|||
*/
|
||||
int aa_set_current_onexec(struct aa_label *label, bool stack)
|
||||
{
|
||||
struct aa_task_ctx *ctx;
|
||||
struct cred *new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
struct aa_task_ctx *ctx = task_ctx(current);
|
||||
|
||||
ctx = cred_ctx(new);
|
||||
aa_get_label(label);
|
||||
aa_clear_task_ctx_trans(ctx);
|
||||
aa_put_label(ctx->onexec);
|
||||
ctx->onexec = label;
|
||||
ctx->token = stack;
|
||||
|
||||
commit_creds(new);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -163,25 +118,27 @@ int aa_set_current_onexec(struct aa_label *label, bool stack)
|
|||
*/
|
||||
int aa_set_current_hat(struct aa_label *label, u64 token)
|
||||
{
|
||||
struct aa_task_ctx *ctx;
|
||||
struct cred *new = prepare_creds();
|
||||
struct aa_task_ctx *ctx = task_ctx(current);
|
||||
struct cred *new;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
AA_BUG(!label);
|
||||
|
||||
ctx = cred_ctx(new);
|
||||
if (!ctx->previous) {
|
||||
/* transfer refcount */
|
||||
ctx->previous = ctx->label;
|
||||
ctx->previous = cred_label(new);
|
||||
ctx->token = token;
|
||||
} else if (ctx->token == token) {
|
||||
aa_put_label(ctx->label);
|
||||
aa_put_label(cred_label(new));
|
||||
} else {
|
||||
/* previous_profile && ctx->token != token */
|
||||
abort_creds(new);
|
||||
return -EACCES;
|
||||
}
|
||||
ctx->label = aa_get_newest_label(label);
|
||||
|
||||
cred_label(new) = aa_get_newest_label(label);
|
||||
/* clear exec on switching context */
|
||||
aa_put_label(ctx->onexec);
|
||||
ctx->onexec = NULL;
|
||||
|
@ -201,28 +158,26 @@ int aa_set_current_hat(struct aa_label *label, u64 token)
|
|||
*/
|
||||
int aa_restore_previous_label(u64 token)
|
||||
{
|
||||
struct aa_task_ctx *ctx;
|
||||
struct cred *new = prepare_creds();
|
||||
struct aa_task_ctx *ctx = task_ctx(current);
|
||||
struct cred *new;
|
||||
|
||||
if (ctx->token != token)
|
||||
return -EACCES;
|
||||
/* ignore restores when there is no saved label */
|
||||
if (!ctx->previous)
|
||||
return 0;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx = cred_ctx(new);
|
||||
if (ctx->token != token) {
|
||||
abort_creds(new);
|
||||
return -EACCES;
|
||||
}
|
||||
/* ignore restores when there is no saved label */
|
||||
if (!ctx->previous) {
|
||||
abort_creds(new);
|
||||
return 0;
|
||||
}
|
||||
|
||||
aa_put_label(ctx->label);
|
||||
ctx->label = aa_get_newest_label(ctx->previous);
|
||||
AA_BUG(!ctx->label);
|
||||
aa_put_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;
|
||||
}
|
Loading…
Reference in New Issue