Watch
1
Fork
You've already forked kernel_a52xq
0
mirror of https://github.com/a52xq-dev/kernel.git synced 2026-10-18 21:58:11 +07:00

+ 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:
Linus Torvalds 2018-04-13 15:38:53 -07:00
commit 80a17a5f50
33 changed files with 2119 additions and 525 deletions

View file

@ -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

View file

@ -1,5 +1,6 @@
#
# Generated include files
#
net_names.h
capability_names.h
rlim_names.h

View file

@ -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)

View file

@ -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");

View file

@ -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"

View file

@ -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 @@ build:
int aa_change_hat(const char *hats[], int count, u64 token, int flags)
{
const struct cred *cred;
struct aa_task_ctx *ctx;
struct aa_task_ctx *ctx = task_ctx(current);
struct aa_label *label, *previous, *new = NULL, *target = NULL;
struct aa_profile *profile;
struct aa_perms perms = {};
const char *info = NULL;
int error = 0;
/*
* Fail explicitly requested domain transitions if no_new_privs.
* There is no exception for unconfined as change_hat is not
* available.
*/
if (task_no_new_privs(current)) {
/* not an apparmor denial per se, so don't log it */
AA_DEBUG("no_new_privs - change_hat denied");
return -EPERM;
}
/* released below */
cred = get_current_cred();
ctx = cred_ctx(cred);
label = aa_get_newest_cred_label(cred);
previous = aa_get_newest_label(ctx->previous);
/*
* Detect no new privs being set, and store the label it
* occurred under. Ideally this would happen when nnp
* is set but there isn't a good way to do that yet.
*
* Testing for unconfined must be done before the subset test
*/
if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp)
ctx->nnp = aa_get_label(label);
if (unconfined(label)) {
info = "unconfined can not change_hat";
error = -EPERM;
@ -1093,6 +1208,18 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
if (error)
goto fail;
/*
* no new privs prevents domain transitions that would
* reduce restrictions.
*/
if (task_no_new_privs(current) && !unconfined(label) &&
!aa_label_is_subset(new, ctx->nnp)) {
/* not an apparmor denial per se, so don't log it */
AA_DEBUG("no_new_privs - change_hat denied");
error = -EPERM;
goto out;
}
if (flags & AA_CHANGE_TEST)
goto out;
@ -1102,6 +1229,18 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
/* kill task in case of brute force attacks */
goto kill;
} else if (previous && !(flags & AA_CHANGE_TEST)) {
/*
* no new privs prevents domain transitions that would
* reduce restrictions.
*/
if (task_no_new_privs(current) && !unconfined(label) &&
!aa_label_is_subset(previous, ctx->nnp)) {
/* not an apparmor denial per se, so don't log it */
AA_DEBUG("no_new_privs - change_hat denied");
error = -EPERM;
goto out;
}
/* Return to saved label. Kill task if restore fails
* to avoid brute force attacks
*/
@ -1144,21 +1283,6 @@ static int change_profile_perms_wrapper(const char *op, const char *name,
const char *info = NULL;
int error = 0;
/*
* Fail explicitly requested domain transitions when no_new_privs
* and not unconfined OR the transition results in a stack on
* the current label.
* Stacking domain transitions and transitions from unconfined are
* allowed even when no_new_privs is set because this aways results
* in a reduction of permissions.
*/
if (task_no_new_privs(current) && !stack &&
!profile_unconfined(profile) &&
!aa_label_is_subset(target, &profile->label)) {
info = "no new privs";
error = -EPERM;
}
if (!error)
error = change_profile_perms(profile, target, stack, request,
profile->file.start, perms);
@ -1192,10 +1316,23 @@ int aa_change_profile(const char *fqname, int flags)
const char *info = NULL;
const char *auditname = fqname; /* retain leading & if stack */
bool stack = flags & AA_CHANGE_STACK;
struct aa_task_ctx *ctx = task_ctx(current);
int error = 0;
char *op;
u32 request;
label = aa_get_current_label();
/*
* Detect no new privs being set, and store the label it
* occurred under. Ideally this would happen when nnp
* is set but there isn't a good way to do that yet.
*
* Testing for unconfined must be done before the subset test
*/
if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp)
ctx->nnp = aa_get_label(label);
if (!fqname || !*fqname) {
AA_DEBUG("no profile name");
return -EINVAL;
@ -1283,14 +1420,28 @@ check:
if (flags & AA_CHANGE_TEST)
goto out;
/* stacking is always a subset, so only check the nonstack case */
if (!stack) {
new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
aa_get_label(target),
aa_get_label(&profile->label));
/*
* no new privs prevents domain transitions that would
* reduce restrictions.
*/
if (task_no_new_privs(current) && !unconfined(label) &&
!aa_label_is_subset(new, ctx->nnp)) {
/* not an apparmor denial per se, so don't log it */
AA_DEBUG("no_new_privs - change_hat denied");
error = -EPERM;
goto out;
}
}
if (!(flags & AA_CHANGE_ONEXEC)) {
/* only transition profiles in the current ns */
if (stack)
new = aa_label_merge(label, target, GFP_KERNEL);
else
new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
aa_get_label(target),
aa_get_label(&profile->label));
if (IS_ERR_OR_NULL(new)) {
info = "failed to build target label";
error = PTR_ERR(new);
@ -1299,9 +1450,15 @@ check:
goto audit;
}
error = aa_replace_current_label(new);
} else
} else {
if (new) {
aa_put_label(new);
new = NULL;
}
/* full transition will be built in exec path */
error = aa_set_current_onexec(target, stack);
}
audit:
error = fn_for_each_in_ns(label, profile,

View file

@ -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();

View file

@ -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

View file

@ -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;

View file

@ -0,0 +1,176 @@
/*
* AppArmor security module
*
* This file contains AppArmor contexts used to associate "labels" to objects.
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2 of the
* License.
*/
#ifndef __AA_CONTEXT_H
#define __AA_CONTEXT_H
#include <linux/cred.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include "label.h"
#include "policy_ns.h"
#include "task.h"
#define cred_label(X) ((X)->security)
/**
* aa_cred_raw_label - obtain cred's label
* @cred: cred to obtain label from (NOT NULL)
*
* Returns: confining label
*
* does NOT increment reference count
*/
static inline struct aa_label *aa_cred_raw_label(const struct cred *cred)
{
struct aa_label *label = cred_label(cred);
AA_BUG(!label);
return label;
}
/**
* aa_get_newest_cred_label - obtain the newest label on a cred
* @cred: cred to obtain label from (NOT NULL)
*
* Returns: newest version of confining label
*/
static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred)
{
return aa_get_newest_label(aa_cred_raw_label(cred));
}
/**
* __aa_task_raw_label - retrieve another task's label
* @task: task to query (NOT NULL)
*
* Returns: @task's label without incrementing its ref count
*
* If @task != current needs to be called in RCU safe critical section
*/
static inline struct aa_label *__aa_task_raw_label(struct task_struct *task)
{
return aa_cred_raw_label(__task_cred(task));
}
/**
* aa_current_raw_label - find the current tasks confining label
*
* Returns: up to date confining label or the ns unconfined label (NOT NULL)
*
* This fn will not update the tasks cred to the most up to date version
* of the label so it is safe to call when inside of locks.
*/
static inline struct aa_label *aa_current_raw_label(void)
{
return aa_cred_raw_label(current_cred());
}
/**
* aa_get_current_label - get the newest version of the current tasks label
*
* Returns: newest version of confining label (NOT NULL)
*
* This fn will not update the tasks cred, so it is safe inside of locks
*
* The returned reference must be put with aa_put_label()
*/
static inline struct aa_label *aa_get_current_label(void)
{
struct aa_label *l = aa_current_raw_label();
if (label_is_stale(l))
return aa_get_newest_label(l);
return aa_get_label(l);
}
#define __end_current_label_crit_section(X) end_current_label_crit_section(X)
/**
* end_label_crit_section - put a reference found with begin_current_label..
* @label: label reference to put
*
* Should only be used with a reference obtained with
* begin_current_label_crit_section and never used in situations where the
* task cred may be updated
*/
static inline void end_current_label_crit_section(struct aa_label *label)
{
if (label != aa_current_raw_label())
aa_put_label(label);
}
/**
* __begin_current_label_crit_section - current's confining label
*
* Returns: up to date confining label or the ns unconfined label (NOT NULL)
*
* safe to call inside locks
*
* The returned reference must be put with __end_current_label_crit_section()
* This must NOT be used if the task cred could be updated within the
* critical section between __begin_current_label_crit_section() ..
* __end_current_label_crit_section()
*/
static inline struct aa_label *__begin_current_label_crit_section(void)
{
struct aa_label *label = aa_current_raw_label();
if (label_is_stale(label))
label = aa_get_newest_label(label);
return label;
}
/**
* begin_current_label_crit_section - current's confining label and update it
*
* Returns: up to date confining label or the ns unconfined label (NOT NULL)
*
* Not safe to call inside locks
*
* The returned reference must be put with end_current_label_crit_section()
* This must NOT be used if the task cred could be updated within the
* critical section between begin_current_label_crit_section() ..
* end_current_label_crit_section()
*/
static inline struct aa_label *begin_current_label_crit_section(void)
{
struct aa_label *label = aa_current_raw_label();
if (label_is_stale(label)) {
label = aa_get_newest_label(label);
if (aa_replace_current_label(label) == 0)
/* task cred will keep the reference */
aa_put_label(label);
}
return label;
}
static inline struct aa_ns *aa_get_current_ns(void)
{
struct aa_label *label;
struct aa_ns *ns;
label = __begin_current_label_crit_section();
ns = aa_get_ns(labels_ns(label));
__end_current_label_crit_section(label);
return ns;
}
#endif /* __AA_CONTEXT_H */

View file

@ -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,

View file

@ -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 */

View file

@ -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 */

View file

@ -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,

View file

@ -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);
}
/**

View file

@ -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);

View file

@ -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",

View file

@ -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 */

View file

@ -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));
}

View file

@ -16,7 +16,7 @@
#include <linux/sort.h>
#include "include/apparmor.h"
#include "include/context.h"
#include "include/cred.h"
#include "include/label.h"
#include "include/policy.h"
#include "include/secid.h"
@ -1808,14 +1808,17 @@ void aa_label_printk(struct aa_label *label, gfp_t gfp)
aa_put_ns(ns);
}
static int label_count_str_entries(const char *str)
static int label_count_strn_entries(const char *str, size_t n)
{
const char *end = str + n;
const char *split;
int count = 1;
AA_BUG(!str);
for (split = strstr(str, "//&"); split; split = strstr(str, "//&")) {
for (split = aa_label_strn_split(str, end - str);
split;
split = aa_label_strn_split(str, end - str)) {
count++;
str = split + 3;
}
@ -1843,9 +1846,10 @@ static struct aa_profile *fqlookupn_profile(struct aa_label *base,
}
/**
* aa_label_parse - parse, validate and convert a text string to a label
* aa_label_strn_parse - parse, validate and convert a text string to a label
* @base: base label to use for lookups (NOT NULL)
* @str: null terminated text string (NOT NULL)
* @n: length of str to parse, will stop at \0 if encountered before n
* @gfp: allocation type
* @create: true if should create compound labels if they don't exist
* @force_stack: true if should stack even if no leading &
@ -1853,19 +1857,24 @@ static struct aa_profile *fqlookupn_profile(struct aa_label *base,
* Returns: the matching refcounted label if present
* else ERRPTR
*/
struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
gfp_t gfp, bool create, bool force_stack)
struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str,
size_t n, gfp_t gfp, bool create,
bool force_stack)
{
DEFINE_VEC(profile, vec);
struct aa_label *label, *currbase = base;
int i, len, stack = 0, error;
char *split;
const char *end = str + n;
const char *split;
AA_BUG(!base);
AA_BUG(!str);
str = skip_spaces(str);
len = label_count_str_entries(str);
str = skipn_spaces(str, n);
if (str == NULL || (*str == '=' && base != &root_ns->unconfined->label))
return ERR_PTR(-EINVAL);
len = label_count_strn_entries(str, end - str);
if (*str == '&' || force_stack) {
/* stack on top of base */
stack = base->size;
@ -1873,8 +1882,6 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
if (*str == '&')
str++;
}
if (*str == '=')
base = &root_ns->unconfined->label;
error = vec_setup(profile, vec, len, gfp);
if (error)
@ -1883,7 +1890,8 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
for (i = 0; i < stack; i++)
vec[i] = aa_get_profile(base->vec[i]);
for (split = strstr(str, "//&"), i = stack; split && i < len; i++) {
for (split = aa_label_strn_split(str, end - str), i = stack;
split && i < len; i++) {
vec[i] = fqlookupn_profile(base, currbase, str, split - str);
if (!vec[i])
goto fail;
@ -1894,11 +1902,11 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
if (vec[i]->ns != labels_ns(currbase))
currbase = &vec[i]->label;
str = split + 3;
split = strstr(str, "//&");
split = aa_label_strn_split(str, end - str);
}
/* last element doesn't have a split */
if (i < len) {
vec[i] = fqlookupn_profile(base, currbase, str, strlen(str));
vec[i] = fqlookupn_profile(base, currbase, str, end - str);
if (!vec[i])
goto fail;
}
@ -1930,6 +1938,12 @@ fail:
goto out;
}
struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
gfp_t gfp, bool create, bool force_stack)
{
return aa_label_strn_parse(base, str, strlen(str), gfp, create,
force_stack);
}
/**
* aa_labelset_destroy - remove all labels from the label set

Some files were not shown because too many files have changed in this diff Show more