Commit 4125e8e9 authored by John Johansen's avatar John Johansen Committed by Seth Forshee

UBUNTU: SAUCE: (no-up) apparmor: sync of apparmor 3.6 (from Ubuntu 16.10)

This is a backport of apparmor 3.6 that is in 16.10 to support
lxc/lxd containers.

BugLink: http://bugs.launchpad.net/bugs/1611078Signed-off-by: default avatarJohn Johansen <john.johansen@canonical.com>
Acked-by: default avatarBrad Figg <brad.figg@canonical.com>
Signed-off-by: default avatarTim Gardner <tim.gardner@canonical.com>
parent bea2dd12
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/fs.h>
struct linux_binprm; struct linux_binprm;
struct cred; struct cred;
...@@ -1585,7 +1586,15 @@ static inline void security_audit_rule_free(void *lsmrule) ...@@ -1585,7 +1586,15 @@ static inline void security_audit_rule_free(void *lsmrule)
#endif /* CONFIG_AUDIT */ #endif /* CONFIG_AUDIT */
#ifdef CONFIG_SECURITYFS #ifdef CONFIG_SECURITYFS
extern int securityfs_pin_fs(void);
extern int __securityfs_setup_d_inode(struct inode *dir, struct dentry *dentry,
umode_t mode, void *data,
const struct file_operations *fops,
const struct inode_operations *iops);
extern struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
struct dentry *parent, void *data,
const struct file_operations *fops,
const struct inode_operations *iops);
extern struct dentry *securityfs_create_file(const char *name, umode_t mode, extern struct dentry *securityfs_create_file(const char *name, umode_t mode,
struct dentry *parent, void *data, struct dentry *parent, void *data,
const struct file_operations *fops); const struct file_operations *fops);
...@@ -1593,6 +1602,28 @@ extern struct dentry *securityfs_create_dir(const char *name, struct dentry *par ...@@ -1593,6 +1602,28 @@ extern struct dentry *securityfs_create_dir(const char *name, struct dentry *par
extern void securityfs_remove(struct dentry *dentry); extern void securityfs_remove(struct dentry *dentry);
#else /* CONFIG_SECURITYFS */ #else /* CONFIG_SECURITYFS */
static inline int securityfs_pin_fs(void)
{
return -ENODEV;
}
static inline int __securityfs_setup_d_inode(struct inode *dir,
struct dentry *dentry,
umode_t mode, void *data,
const struct file_operations *fops,
const struct inode_operations *iops)
{
return -ENODEV;
}
static inline struct dentry *securityfs_create_dentry(const char *name,
umode_t mode,
struct dentry *parent, void *data,
const struct file_operations *fops,
const struct inode_operations *iops)
{
return ERR_PTR(-ENODEV);
}
static inline struct dentry *securityfs_create_dir(const char *name, static inline struct dentry *securityfs_create_dir(const char *name,
struct dentry *parent) struct dentry *parent)
......
...@@ -66,7 +66,7 @@ config SECURITY_APPARMOR_UNCONFINED_INIT ...@@ -66,7 +66,7 @@ config SECURITY_APPARMOR_UNCONFINED_INIT
If you are unsure how to answer this question, answer Y. If you are unsure how to answer this question, answer Y.
config SECURITY_APPARMOR_HASH config SECURITY_APPARMOR_HASH
bool "enable introspection of sha1 hashes for loaded profiles" bool "Enable introspection of sha1 hashes for loaded profiles"
depends on SECURITY_APPARMOR depends on SECURITY_APPARMOR
select CRYPTO select CRYPTO
select CRYPTO_SHA1 select CRYPTO_SHA1
......
...@@ -5,7 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o ...@@ -5,7 +5,7 @@ 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 context.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
resource.o sid.o file.o label.o mount.o net.o af_unix.o \ resource.o sid.o file.o label.o mount.o net.o af_unix.o \
policy_ns.o policy_ns.o backport.o
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
clean-files := capability_names.h rlim_names.h net_names.h clean-files := capability_names.h rlim_names.h net_names.h
......
This diff is collapsed.
/*
* AppArmor security module
*
* This file contains AppArmor file mediation function definitions.
*
* Copyright 2014 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.
*
*
* This is a file of helper fns backported from newer kernels to support
* backporting of apparmor to older kernels. Fns prefixed with code they
* are copied of modified from
*/
#include "include/backport.h"
...@@ -29,6 +29,43 @@ unsigned int aa_hash_size(void) ...@@ -29,6 +29,43 @@ unsigned int aa_hash_size(void)
return apparmor_hash_size; return apparmor_hash_size;
} }
char *aa_calc_hash(void *data, size_t len)
{
struct {
struct shash_desc shash;
char ctx[crypto_shash_descsize(apparmor_tfm)];
} desc;
char *hash = NULL;
int error = -ENOMEM;
if (!apparmor_tfm)
return NULL;
hash = kzalloc(apparmor_hash_size, GFP_KERNEL);
if (!hash)
goto fail;
desc.shash.tfm = apparmor_tfm;
desc.shash.flags = 0;
error = crypto_shash_init(&desc.shash);
if (error)
goto fail;
error = crypto_shash_update(&desc.shash, (u8 *) data, len);
if (error)
goto fail;
error = crypto_shash_final(&desc.shash, hash);
if (error)
goto fail;
return hash;
fail:
kfree(hash);
return ERR_PTR(error);
}
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
size_t len) size_t len)
{ {
...@@ -39,6 +76,9 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, ...@@ -39,6 +76,9 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
int error = -ENOMEM; int error = -ENOMEM;
u32 le32_version = cpu_to_le32(version); u32 le32_version = cpu_to_le32(version);
if (!aa_g_hash_policy)
return 0;
if (!apparmor_tfm) if (!apparmor_tfm)
return 0; return 0;
......
...@@ -488,27 +488,41 @@ static struct aa_label *x_to_label(struct aa_profile *profile, ...@@ -488,27 +488,41 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
} }
static struct aa_label *profile_transition(struct aa_profile *profile, static struct aa_label *profile_transition(struct aa_profile *profile,
const char *name, const struct linux_binprm *bprm,
struct path_cond *cond, char *buffer, struct path_cond *cond,
bool *secure_exec) bool *secure_exec)
{ {
struct aa_label *new = NULL; struct aa_label *new = NULL;
const char *info = NULL; const char *info = NULL, *name = NULL, *target = NULL;
unsigned int state = profile->file.start; unsigned int state = profile->file.start;
struct aa_perms perms = {}; struct aa_perms perms = {};
const char *target = NULL;
int error = 0; int error = 0;
AA_BUG(!profile);
AA_BUG(!bprm);
AA_BUG(!buffer);
error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
&name, &info, profile->disconnected);
if (error) {
if (profile_unconfined(profile) ||
(profile->label.flags & FLAG_IX_ON_NAME_ERROR)) {
AA_DEBUG("name lookup ix on error");
error = 0;
new = aa_get_newest_label(&profile->label);
}
name = bprm->filename;
goto audit;
}
if (profile_unconfined(profile)) { if (profile_unconfined(profile)) {
new = find_attach(profile->ns, &profile->ns->base.profiles, new = find_attach(profile->ns, &profile->ns->base.profiles,
name); name);
if (new) { if (new) {
AA_DEBUG("unconfined attached to new label"); AA_DEBUG("unconfined attached to new label");
return new; return new;
} }
AA_DEBUG("unconfined exec no attachment"); AA_DEBUG("unconfined exec no attachment");
return aa_get_newest_label(&profile->label); return aa_get_newest_label(&profile->label);
} }
...@@ -565,14 +579,20 @@ static struct aa_label *profile_transition(struct aa_profile *profile, ...@@ -565,14 +579,20 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
} }
static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec, static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
bool stack, const char *xname, struct path_cond *cond, bool stack, const struct linux_binprm *bprm,
char *buffer, struct path_cond *cond,
bool *secure_exec) bool *secure_exec)
{ {
unsigned int state = profile->file.start; unsigned int state = profile->file.start;
struct aa_perms perms = {}; struct aa_perms perms = {};
const char *info = "change_profile onexec"; const char *xname = NULL, *info = "change_profile onexec";
int error = -EACCES; int error = -EACCES;
AA_BUG(!profile);
AA_BUG(!onexec);
AA_BUG(!bprm);
AA_BUG(!buffer);
if (profile_unconfined(profile)) { if (profile_unconfined(profile)) {
/* change_profile on exec already granted */ /* change_profile on exec already granted */
/* /*
...@@ -583,6 +603,18 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec, ...@@ -583,6 +603,18 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
return 0; return 0;
} }
error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
&xname, &info, profile->disconnected);
if (error) {
if (profile_unconfined(profile) ||
(profile->label.flags & FLAG_IX_ON_NAME_ERROR)) {
AA_DEBUG("name lookup ix on error");
error = 0;
}
xname = bprm->filename;
goto audit;
}
/* find exec permissions for name */ /* find exec permissions for name */
state = aa_str_perms(profile->file.dfa, state, xname, cond, &perms); state = aa_str_perms(profile->file.dfa, state, xname, cond, &perms);
if (!(perms.allow & AA_MAY_ONEXEC)) { if (!(perms.allow & AA_MAY_ONEXEC)) {
...@@ -618,46 +650,52 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec, ...@@ -618,46 +650,52 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
static struct aa_label *handle_onexec(struct aa_label *label, static struct aa_label *handle_onexec(struct aa_label *label,
struct aa_label *onexec, bool stack, struct aa_label *onexec, bool stack,
const char *xname, const struct linux_binprm *bprm,
struct path_cond *cond, char *buffer, struct path_cond *cond,
bool *unsafe) bool *unsafe)
{ {
struct aa_profile *profile; struct aa_profile *profile;
struct aa_label *new; struct aa_label *new;
int error; int error;
AA_BUG(!label);
AA_BUG(!onexec);
AA_BUG(!bprm);
AA_BUG(!buffer);
if (!stack) { if (!stack) {
error = fn_for_each_in_ns(label, profile, error = fn_for_each_in_ns(label, profile,
profile_onexec(profile, onexec, stack, profile_onexec(profile, onexec, stack,
xname, cond, unsafe)); bprm, buffer, cond, unsafe));
if (error) if (error)
return ERR_PTR(error); return ERR_PTR(error);
new = fn_label_build_in_ns(label, profile, GFP_ATOMIC, new = fn_label_build_in_ns(label, profile, GFP_ATOMIC,
aa_get_newest_label(onexec), aa_get_newest_label(onexec),
profile_transition(profile, xname, profile_transition(profile, bprm, buffer,
cond, unsafe)); cond, unsafe));
} else { } else {
/* TODO: determine how much we want to losen this */ /* TODO: determine how much we want to losen this */
error = fn_for_each_in_ns(label, profile, error = fn_for_each_in_ns(label, profile,
profile_onexec(profile, onexec, stack, xname, profile_onexec(profile, onexec, stack, bprm,
cond, unsafe)); buffer, cond, unsafe));
if (error) if (error)
return ERR_PTR(error); return ERR_PTR(error);
new = fn_label_build_in_ns(label, profile, GFP_ATOMIC, new = fn_label_build_in_ns(label, profile, GFP_ATOMIC,
aa_label_merge(&profile->label, aa_label_merge(&profile->label, onexec,
onexec, GFP_ATOMIC),
GFP_ATOMIC), profile_transition(profile, bprm, buffer,
profile_transition(profile, xname, cond, unsafe));
cond, unsafe));
} }
if (new) if (new)
return new; return new;
/* TODO: get rid of GLOBAL_ROOT_UID */
error = fn_for_each_in_ns(label, profile, error = fn_for_each_in_ns(label, profile,
aa_audit_file(profile, &nullperms, OP_CHANGE_ONEXEC, aa_audit_file(profile, &nullperms, OP_CHANGE_ONEXEC,
AA_MAY_ONEXEC, xname, NULL, onexec, AA_MAY_ONEXEC, bprm->filename, NULL,
GLOBAL_ROOT_UID, onexec, GLOBAL_ROOT_UID,
"failed to build target label", -ENOMEM)); "failed to build target label", -ENOMEM));
return ERR_PTR(error); return ERR_PTR(error);
} }
...@@ -676,7 +714,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) ...@@ -676,7 +714,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
struct aa_label *label, *new = NULL; struct aa_label *label, *new = NULL;
struct aa_profile *profile; struct aa_profile *profile;
char *buffer = NULL; char *buffer = NULL;
const char *xname = NULL;
const char *info = NULL; const char *info = NULL;
int error = 0; int error = 0;
bool unsafe = false; bool unsafe = false;
...@@ -692,28 +729,17 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) ...@@ -692,28 +729,17 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
AA_BUG(!ctx); AA_BUG(!ctx);
label = aa_get_newest_label(ctx->label); label = aa_get_newest_label(ctx->label);
profile = labels_profile(label);
/* buffer freed below, xname is pointer into buffer */ /* buffer freed below, name is pointer into buffer */
get_buffers(buffer); get_buffers(buffer);
error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
&xname, &info, profile->disconnected);
if (error) {
if (profile_unconfined(profile) ||
(profile->label.flags & FLAG_IX_ON_NAME_ERROR))
error = 0;
xname = bprm->filename;
goto audit;
}
/* Test for onexec first as onexec override other x transitions. */ /* Test for onexec first as onexec override other x transitions. */
if (ctx->onexec) if (ctx->onexec)
new = handle_onexec(label, ctx->onexec, ctx->token, xname, new = handle_onexec(label, ctx->onexec, ctx->token,
&cond, &unsafe); bprm, buffer, &cond, &unsafe);
else else
new = fn_label_build(label, profile, GFP_ATOMIC, new = fn_label_build(label, profile, GFP_ATOMIC,
profile_transition(profile, xname, &cond, profile_transition(profile, bprm, buffer,
&unsafe)); &cond, &unsafe));
AA_BUG(!new); AA_BUG(!new);
if (IS_ERR(new)) { if (IS_ERR(new)) {
...@@ -731,7 +757,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) ...@@ -731,7 +757,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
* subsets are allowed even when no_new_privs is set because this * subsets are allowed even when no_new_privs is set because this
* aways results in a further reduction of permissions. * aways results in a further reduction of permissions.
*/ */
if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS && if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) &&
!unconfined(label) && !aa_label_is_subset(new, label)) { !unconfined(label) && !aa_label_is_subset(new, label)) {
error = -EPERM; error = -EPERM;
info = "no new privs"; info = "no new privs";
...@@ -753,7 +779,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) ...@@ -753,7 +779,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
if (unsafe) { if (unsafe) {
if (DEBUG_ON) { if (DEBUG_ON) {
dbg_printk("scrubbing environment variables for %s " dbg_printk("scrubbing environment variables for %s "
"label=", xname); "label=", bprm->filename);
aa_label_printk(new, GFP_ATOMIC); aa_label_printk(new, GFP_ATOMIC);
dbg_printk("\n"); dbg_printk("\n");
} }
...@@ -764,7 +790,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) ...@@ -764,7 +790,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
/* when transitioning clear unsafe personality bits */ /* when transitioning clear unsafe personality bits */
if (DEBUG_ON) { if (DEBUG_ON) {
dbg_printk("apparmor: clearing unsafe personality " dbg_printk("apparmor: clearing unsafe personality "
"bits. %s label=", xname); "bits. %s label=", bprm->filename);
aa_label_printk(new, GFP_ATOMIC); aa_label_printk(new, GFP_ATOMIC);
dbg_printk("\n"); dbg_printk("\n");
} }
...@@ -786,7 +812,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) ...@@ -786,7 +812,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
audit: audit:
error = fn_for_each(label, profile, error = fn_for_each(label, profile,
aa_audit_file(profile, &nullperms, OP_EXEC, MAY_EXEC, aa_audit_file(profile, &nullperms, OP_EXEC, MAY_EXEC,
xname, NULL, new, bprm->filename, NULL, new,
file_inode(bprm->file)->i_uid, info, file_inode(bprm->file)->i_uid, info,
error)); error));
aa_put_label(new); aa_put_label(new);
......
...@@ -107,7 +107,8 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms, ...@@ -107,7 +107,8 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
{ {
int type = AUDIT_APPARMOR_AUTO; int type = AUDIT_APPARMOR_AUTO;
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, op);
sa.u.tsk = NULL;
aad(&sa)->request = request; aad(&sa)->request = request;
aad(&sa)->name = name; aad(&sa)->name = name;
aad(&sa)->fs.target = target; aad(&sa)->fs.target = target;
...@@ -164,18 +165,15 @@ static inline bool is_deleted(struct dentry *dentry) ...@@ -164,18 +165,15 @@ static inline bool is_deleted(struct dentry *dentry)
return 0; return 0;
} }
static int path_name(const char *op, struct aa_label *label, struct path *path, static int path_name(const char *op, struct aa_label *label,
int flags, char *buffer, const char**name, const struct path *path, int flags, char *buffer,
struct path_cond *cond, u32 request, bool delegate_deleted) const char**name, struct path_cond *cond, u32 request)
{ {
struct aa_profile *profile; struct aa_profile *profile;
const char *info = NULL; const char *info = NULL;
int error = aa_path_name(path, flags, buffer, name, &info, int error = aa_path_name(path, flags, buffer, name, &info,
labels_profile(label)->disconnected); labels_profile(label)->disconnected);
if (error) { if (error) {
if (error == -ENOENT && is_deleted(path->dentry) &&
delegate_deleted)
return 0;
fn_for_each_confined(label, profile, fn_for_each_confined(label, profile,
aa_audit_file(profile, &nullperms, op, request, *name, aa_audit_file(profile, &nullperms, op, request, *name,
NULL, NULL, cond->uid, info, error)); NULL, NULL, cond->uid, info, error));
...@@ -288,6 +286,7 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, ...@@ -288,6 +286,7 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name,
struct aa_perms *perms) struct aa_perms *perms)
{ {
int e = 0; int e = 0;
if (profile_unconfined(profile) || if (profile_unconfined(profile) ||
((flags & PATH_SOCK_COND) && !PROFILE_MEDIATES_AF(profile, AF_UNIX))) ((flags & PATH_SOCK_COND) && !PROFILE_MEDIATES_AF(profile, AF_UNIX)))
return 0; return 0;
...@@ -298,6 +297,27 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, ...@@ -298,6 +297,27 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name,
cond->uid, NULL, e); cond->uid, NULL, e);
} }
static int profile_path_perm(const char *op, struct aa_profile *profile,
const struct path *path, char *buffer, u32 request,
struct path_cond *cond, int flags,
struct aa_perms *perms)
{
const char *name;
int error;
if (profile_unconfined(profile))
return 0;
error = path_name(op, &profile->label, path,
flags | profile->path_flags, buffer, &name, cond,
request);
if (error)
return error;
return __aa_path_perm(op, profile, name, request, cond, flags,
perms);
}
/** /**
* aa_path_perm - do permissions check & audit for @path * aa_path_perm - do permissions check & audit for @path
* @op: operation being checked * @op: operation being checked
...@@ -309,26 +329,20 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, ...@@ -309,26 +329,20 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name,
* *
* Returns: %0 else error if access denied or other error * Returns: %0 else error if access denied or other error
*/ */
int aa_path_perm(const char *op, struct aa_label *label, struct path *path, int aa_path_perm(const char *op, struct aa_label *label,
int flags, u32 request, struct path_cond *cond) const struct path *path, int flags, u32 request,
struct path_cond *cond)
{ {
struct aa_perms perms = {}; struct aa_perms perms = {};
char *buffer = NULL;
const char *name;
struct aa_profile *profile; struct aa_profile *profile;
char *buffer = NULL;
int error; int error;
/* TODO: fix path lookup flags */ flags |= PATH_DELEGATE_DELETED | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0);
flags |= labels_profile(label)->path_flags |
(S_ISDIR(cond->mode) ? PATH_IS_DIR : 0);
get_buffers(buffer); get_buffers(buffer);
error = fn_for_each_confined(label, profile,
error = path_name(op, label, path, flags, buffer, &name, cond, profile_path_perm(op, profile, path, buffer, request,
request, true); cond, flags, &perms));
if (!error)
error = fn_for_each_confined(label, profile,
__aa_path_perm(op, profile, name, request, cond,
flags, &perms));
put_buffers(buffer); put_buffers(buffer);
return error; return error;
...@@ -354,15 +368,30 @@ static inline bool xindex_is_subset(u32 link, u32 target) ...@@ -354,15 +368,30 @@ static inline bool xindex_is_subset(u32 link, u32 target)
return 1; return 1;
} }
static int profile_path_link(struct aa_profile *profile, const char *lname, static int profile_path_link(struct aa_profile *profile,
const char *tname, struct path_cond *cond) const struct path *link, char *buffer,
const struct path *target, char *buffer2,
struct path_cond *cond)
{ {
const char *lname, *tname = NULL;
struct aa_perms lperms, perms; struct aa_perms lperms, perms;
const char *info = NULL; const char *info = NULL;
u32 request = AA_MAY_LINK; u32 request = AA_MAY_LINK;
unsigned int state; unsigned int state;
int e = -EACCES; int error;
error = path_name(OP_LINK, &profile->label, link, profile->path_flags,
buffer, &lname, cond, AA_MAY_LINK);
if (error)
goto audit;
/* buffer2 freed below, tname is pointer in buffer2 */
error = path_name(OP_LINK, &profile->label, target, profile->path_flags,
buffer2, &tname, cond, AA_MAY_LINK);
if (error)
goto audit;
error = -EACCES;
/* aa_str_perms - handles the case of the dfa being NULL */ /* aa_str_perms - handles the case of the dfa being NULL */
state = aa_str_perms(profile->file.dfa, profile->file.start, lname, state = aa_str_perms(profile->file.dfa, profile->file.start, lname,
cond, &lperms); cond, &lperms);
...@@ -413,11 +442,11 @@ static int profile_path_link(struct aa_profile *profile, const char *lname, ...@@ -413,11 +442,11 @@ static int profile_path_link(struct aa_profile *profile, const char *lname,
} }
done_tests: done_tests:
e = 0; error = 0;
audit: audit:
return aa_audit_file(profile, &lperms, OP_LINK, request, lname, tname, return aa_audit_file(profile, &lperms, OP_LINK, request, lname, tname,
NULL, cond->uid, info, e); NULL, cond->uid, info, error);
} }
/** /**
...@@ -439,7 +468,7 @@ static int profile_path_link(struct aa_profile *profile, const char *lname, ...@@ -439,7 +468,7 @@ static int profile_path_link(struct aa_profile *profile, const char *lname,
* Returns: %0 if allowed else error * Returns: %0 if allowed else error
*/ */
int aa_path_link(struct aa_label *label, struct dentry *old_dentry, int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
struct path *new_dir, struct dentry *new_dentry) const struct path *new_dir, struct dentry *new_dentry)
{ {
struct path link = { new_dir->mnt, new_dentry }; struct path link = { new_dir->mnt, new_dentry };
struct path target = { new_dir->mnt, old_dentry }; struct path target = { new_dir->mnt, old_dentry };
...@@ -448,31 +477,14 @@ int aa_path_link(struct aa_label *label, struct dentry *old_dentry, ...@@ -448,31 +477,14 @@ int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
d_backing_inode(old_dentry)->i_mode d_backing_inode(old_dentry)->i_mode
}; };
char *buffer = NULL, *buffer2 = NULL; char *buffer = NULL, *buffer2 = NULL;
const char *lname, *tname = NULL;
struct aa_profile *profile; struct aa_profile *profile;
int error; int error;
/* TODO: fix path lookup flags, auditing of failed path for profile */
profile = labels_profile(label);
/* buffer freed below, lname is pointer in buffer */ /* buffer freed below, lname is pointer in buffer */
get_buffers(buffer, buffer2); get_buffers(buffer, buffer2);
error = path_name(OP_LINK, label, &link,
labels_profile(label)->path_flags, buffer,
&lname, &cond, AA_MAY_LINK, false);
if (error)
goto out;
/* buffer2 freed below, tname is pointer in buffer2 */
error = path_name(OP_LINK, label, &target,
labels_profile(label)->path_flags, buffer2, &tname,
&cond, AA_MAY_LINK, false);
if (error)
goto out;
error = fn_for_each_confined(label, profile, error = fn_for_each_confined(label, profile,
profile_path_link(profile, lname, tname, &cond)); profile_path_link(profile, &link, buffer, &target,
buffer2, &cond));
out:
put_buffers(buffer, buffer2); put_buffers(buffer, buffer2);
return error; return error;
...@@ -509,7 +521,6 @@ static int __file_path_perm(const char *op, struct aa_label *label, ...@@ -509,7 +521,6 @@ static int __file_path_perm(const char *op, struct aa_label *label,
.uid = file_inode(file)->i_uid, .uid = file_inode(file)->i_uid,
.mode = file_inode(file)->i_mode .mode = file_inode(file)->i_mode
}; };
const char *name;
char *buffer; char *buffer;
int flags, error; int flags, error;
...@@ -518,27 +529,13 @@ static int __file_path_perm(const char *op, struct aa_label *label, ...@@ -518,27 +529,13 @@ static int __file_path_perm(const char *op, struct aa_label *label,
/* TODO: check for revocation on stale profiles */ /* TODO: check for revocation on stale profiles */
return 0; return 0;
/* TODO: fix path lookup flags */ flags = PATH_DELEGATE_DELETED | (S_ISDIR(cond.mode) ? PATH_IS_DIR : 0);
flags = PATH_DELEGATE_DELETED | labels_profile(label)->path_flags |
(S_ISDIR(cond.mode) ? PATH_IS_DIR : 0);
get_buffers(buffer); get_buffers(buffer);
error = path_name(op, label, &file->f_path, flags, buffer, &name, &cond,
request, true);
if (error) {
if (error == 1)
/* Access to open files that are deleted are
* given a pass (implicit delegation)
*/
/* TODO not needed when full perms cached */
error = 0;
goto out;
}
/* check every profile in task label not in current cache */ /* check every profile in task label not in current cache */
error = fn_for_each_not_in_set(flabel, label, profile, error = fn_for_each_not_in_set(flabel, label, profile,
__aa_path_perm(op, profile, name, request, &cond, 0, profile_path_perm(op, profile, &file->f_path, buffer,
&perms)); request, &cond, flags, &perms));
if (denied) { if (denied) {
/* check every profile in file label that was not tested /* check every profile in file label that was not tested
* in the initial check above. * in the initial check above.
...@@ -548,13 +545,13 @@ static int __file_path_perm(const char *op, struct aa_label *label, ...@@ -548,13 +545,13 @@ static int __file_path_perm(const char *op, struct aa_label *label,
/* TODO: don't audit here */ /* TODO: don't audit here */
last_error(error, last_error(error,
fn_for_each_not_in_set(label, flabel, profile, fn_for_each_not_in_set(label, flabel, profile,
__aa_path_perm(op, profile, name, request, profile_path_perm(op, profile, &file->f_path,
&cond, 0, &perms))); buffer, request, &cond, flags,
&perms)));
} }
if (!error) if (!error)
update_file_ctx(file_ctx(file), label, request); update_file_ctx(file_ctx(file), label, request);
out:
put_buffers(buffer); put_buffers(buffer);
return error; return error;
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <linux/types.h> #include <linux/types.h>
#include "backport.h"
/* /*
* Class of mediation types in the AppArmor policy db * Class of mediation types in the AppArmor policy db
*/ */
......
...@@ -70,6 +70,10 @@ enum aafs_ns_type { ...@@ -70,6 +70,10 @@ enum aafs_ns_type {
AAFS_NS_DIR, AAFS_NS_DIR,
AAFS_NS_PROFS, AAFS_NS_PROFS,
AAFS_NS_NS, AAFS_NS_NS,
AAFS_NS_RAW_DATA,
AAFS_NS_LOAD,
AAFS_NS_REPLACE,
AAFS_NS_REMOVE,
AAFS_NS_COUNT, AAFS_NS_COUNT,
AAFS_NS_MAX_COUNT, AAFS_NS_MAX_COUNT,
AAFS_NS_SIZE, AAFS_NS_SIZE,
...@@ -85,12 +89,19 @@ enum aafs_prof_type { ...@@ -85,12 +89,19 @@ enum aafs_prof_type {
AAFS_PROF_MODE, AAFS_PROF_MODE,
AAFS_PROF_ATTACH, AAFS_PROF_ATTACH,
AAFS_PROF_HASH, AAFS_PROF_HASH,
AAFS_PROF_RAW_DATA,
AAFS_PROF_RAW_HASH,
AAFS_PROF_RAW_ABI,
AAFS_PROF_SIZEOF, AAFS_PROF_SIZEOF,
}; };
#define ns_dir(X) ((X)->dents[AAFS_NS_DIR]) #define ns_dir(X) ((X)->dents[AAFS_NS_DIR])
#define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) #define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS])
#define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) #define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS])
#define ns_subdata_dir(X) ((X)->dents[AAFS_NS_RAW_DATA])
#define ns_subload(X) ((X)->dents[AAFS_NS_LOAD])
#define ns_subreplace(X) ((X)->dents[AAFS_NS_REPLACE])
#define ns_subremove(X) ((X)->dents[AAFS_NS_REMOVE])
#define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR])
#define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS])
...@@ -100,6 +111,7 @@ void __aa_fs_profile_migrate_dents(struct aa_profile *old, ...@@ -100,6 +111,7 @@ void __aa_fs_profile_migrate_dents(struct aa_profile *old,
struct aa_profile *new); struct aa_profile *new);
int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
void __aa_fs_ns_rmdir(struct aa_ns *ns); void __aa_fs_ns_rmdir(struct aa_ns *ns);
int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name); int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
struct dentry *dent);
#endif /* __AA_APPARMORFS_H */ #endif /* __AA_APPARMORFS_H */
/*
* AppArmor security module
*
* This file contains AppArmor file mediation function definitions.
*
* Copyright 2014 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.
*
*
* This is a file of helper macros, defines for backporting newer versions
* of apparmor to older kernels
*/
#ifndef __AA_BACKPORT_H
#define __AA_BACKPORT_H
#endif /* __AA_BACKPORT_H */
...@@ -18,9 +18,14 @@ ...@@ -18,9 +18,14 @@
#ifdef CONFIG_SECURITY_APPARMOR_HASH #ifdef CONFIG_SECURITY_APPARMOR_HASH
unsigned int aa_hash_size(void); unsigned int aa_hash_size(void);
char *aa_calc_hash(void *data, size_t len);
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
size_t len); size_t len);
#else #else
static inline char *aa_calc_hash(void *data, size_t len)
{
return NULL;
}
static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version, static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version,
void *start, size_t len) void *start, size_t len)
{ {
......
...@@ -191,14 +191,15 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, ...@@ -191,14 +191,15 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
const char *name, struct path_cond *cond, const char *name, struct path_cond *cond,
struct aa_perms *perms); struct aa_perms *perms);
int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, int __aa_path_perm(const char *op, struct aa_profile *profile,
u32 request, struct path_cond *cond, int flags, const char *name, u32 request, struct path_cond *cond,
struct aa_perms *perms); int flags, struct aa_perms *perms);
int aa_path_perm(const char *op, struct aa_label *label, struct path *path, int aa_path_perm(const char *op, struct aa_label *label,
int flags, u32 request, struct path_cond *cond); const struct path *path, int flags, u32 request,
struct path_cond *cond);
int aa_path_link(struct aa_label *label, struct dentry *old_dentry, int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
struct path *new_dir, struct dentry *new_dentry); const struct path *new_dir, struct dentry *new_dentry);
int aa_file_perm(const char *op, struct aa_label *label, struct file *file, int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
u32 request); u32 request);
......
...@@ -29,26 +29,26 @@ ...@@ -29,26 +29,26 @@
#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) #define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN)
int aa_remount(struct aa_label *label, struct path *path, unsigned long flags, int aa_remount(struct aa_label *label, const struct path *path,
void *data); unsigned long flags, void *data);
int aa_bind_mount(struct aa_label *label, struct path *path, int aa_bind_mount(struct aa_label *label, const struct path *path,
const char *old_name, unsigned long flags); const char *old_name, unsigned long flags);
int aa_mount_change_type(struct aa_label *label, struct path *path, int aa_mount_change_type(struct aa_label *label, const struct path *path,
unsigned long flags); unsigned long flags);
int aa_move_mount(struct aa_label *label, struct path *path, int aa_move_mount(struct aa_label *label, const struct path *path,
const char *old_name); const char *old_name);
int aa_new_mount(struct aa_label *label, const char *dev_name, int aa_new_mount(struct aa_label *label, const char *dev_name,
struct path *path, const char *type, unsigned long flags, const struct path *path, const char *type, unsigned long flags,
void *data); void *data);
int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags); int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags);
int aa_pivotroot(struct aa_label *label, struct path *old_path, int aa_pivotroot(struct aa_label *label, const struct path *old_path,
struct path *new_path); const struct path *new_path);
#endif /* __AA_MOUNT_H */ #endif /* __AA_MOUNT_H */
...@@ -27,7 +27,7 @@ enum path_flags { ...@@ -27,7 +27,7 @@ enum path_flags {
PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */
}; };
int aa_path_name(struct path *path, int flags, char *buffer, int aa_path_name(const struct path *path, int flags, char *buffer,
const char **name, const char **info, const char *disconnect); const char **name, const char **info, const char *disconnect);
#define MAX_PATH_BUFFERS 2 #define MAX_PATH_BUFFERS 2
......
...@@ -104,14 +104,24 @@ extern struct aa_perms allperms; ...@@ -104,14 +104,24 @@ extern struct aa_perms allperms;
}) })
/* TODO: update for labels pointing to labels instead of profiles /*
* Note: this only works for profiles from a single namespace * TODO: update for labels pointing to labels instead of profiles
*/ * TODO: optimize the walk, currently does subwalk of L2 for each P in L1
* gah this doesn't allow for label compound check!!!!
*/
#define xcheck_ns_profile_profile(P1, P2, FN, args...) \
({ \
int ____e = 0; \
if (P1->ns == P2->ns) \
____e = FN((P1), (P2), args); \
(____e); \
})
#define xcheck_profile_label(P, L, FN, args...) \ #define xcheck_ns_profile_label(P, L, FN, args...) \
({ \ ({ \
struct aa_profile *__p2; \ struct aa_profile *__p2; \
fn_for_each((L), __p2, FN((P), __p2, args)); \ fn_for_each((L), __p2, \
xcheck_ns_profile_profile((P), __p2, (FN), args)); \
}) })
#define xcheck_ns_labels(L1, L2, FN, args...) \ #define xcheck_ns_labels(L1, L2, FN, args...) \
...@@ -120,13 +130,9 @@ extern struct aa_perms allperms; ...@@ -120,13 +130,9 @@ extern struct aa_perms allperms;
fn_for_each((L1), __p1, FN(__p1, (L2), args)); \ fn_for_each((L1), __p1, FN(__p1, (L2), args)); \
}) })
/* todo: fix to handle multiple namespaces */
#define xcheck_labels(L1, L2, FN, args...) \
xcheck_ns_labels((L1), (L2), FN, args)
/* Do the cross check but applying FN at the profiles level */ /* Do the cross check but applying FN at the profiles level */
#define xcheck_labels_profiles(L1, L2, FN, args...) \ #define xcheck_labels_profiles(L1, L2, FN, args...) \
xcheck_ns_labels((L1), (L2), xcheck_profile_label, (FN), args) xcheck_ns_labels((L1), (L2), xcheck_ns_profile_label, (FN), args)
#define FINAL_CHECK true #define FINAL_CHECK true
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/rhashtable.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -78,6 +79,19 @@ struct aa_policydb { ...@@ -78,6 +79,19 @@ struct aa_policydb {
}; };
/* struct aa_data - generic data structure
* key: name for retrieving this data
* size: size of data in bytes
* data: binary data
* head: reserved for rhashtable
*/
struct aa_data {
char *key;
size_t size;
char *data;
struct rhash_head head;
};
/* struct aa_profile - basic confinement data /* struct aa_profile - basic confinement data
* @base - base components of the profile (name, refcount, lists, lock ...) * @base - base components of the profile (name, refcount, lists, lock ...)
* @label - label this profile is an extension of * @label - label this profile is an extension of
...@@ -97,9 +111,9 @@ struct aa_policydb { ...@@ -97,9 +111,9 @@ struct aa_policydb {
* @caps: capabilities for the profile * @caps: capabilities for the profile
* @net: network controls for the profile * @net: network controls for the profile
* @rlimits: rlimits for the profile * @rlimits: rlimits for the profile
*
* @dents: dentries for the profiles file entries in apparmorfs * @dents: dentries for the profiles file entries in apparmorfs
* @dirname: name of the profile dir in apparmorfs * @dirname: name of the profile dir in apparmorfs
* @data: hashtable for free-form policy aa_data
* *
* The AppArmor profile contains the basic confinement data. Each profile * The AppArmor profile contains the basic confinement data. Each profile
* has a name, and exists in a namespace. The @name and @exec_match are * has a name, and exists in a namespace. The @name and @exec_match are
...@@ -135,9 +149,11 @@ struct aa_profile { ...@@ -135,9 +149,11 @@ struct aa_profile {
struct aa_net net; struct aa_net net;
struct aa_rlimit rlimits; struct aa_rlimit rlimits;
struct aa_loaddata *rawdata;
unsigned char *hash; unsigned char *hash;
char *dirname; char *dirname;
struct dentry *dents[AAFS_PROF_SIZEOF]; struct dentry *dents[AAFS_PROF_SIZEOF];
struct rhashtable *data;
struct aa_label label; struct aa_label label;
}; };
...@@ -169,9 +185,10 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, ...@@ -169,9 +185,10 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
const char *fqname, size_t n); const char *fqname, size_t n);
struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name);
ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata, ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_label *label,
size_t size); u32 mask, struct aa_loaddata *udata);
ssize_t aa_remove_profiles(struct aa_label *label, char *name, size_t size); ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_label *label,
char *name, size_t size);
void __aa_profile_list_release(struct list_head *head); void __aa_profile_list_release(struct list_head *head);
#define PROF_ADD 1 #define PROF_ADD 1
...@@ -280,9 +297,9 @@ static inline int AUDIT_MODE(struct aa_profile *profile) ...@@ -280,9 +297,9 @@ static inline int AUDIT_MODE(struct aa_profile *profile)
return profile->audit; return profile->audit;
} }
bool policy_view_capable(void); bool policy_view_capable(struct aa_ns *ns);
bool policy_admin_capable(void); bool policy_admin_capable(struct aa_ns *ns);
bool aa_may_open_profiles(void); bool aa_may_open_profiles(void);
int aa_may_manage_policy(struct aa_label *label, u32 mask); int aa_may_manage_policy(struct aa_label *label, struct aa_ns *ns, u32 mask);
#endif /* __AA_POLICY_H */ #endif /* __AA_POLICY_H */
...@@ -88,6 +88,8 @@ void aa_free_ns_kref(struct kref *kref); ...@@ -88,6 +88,8 @@ void aa_free_ns_kref(struct kref *kref);
struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name); struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name);
struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n); struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n);
struct aa_ns *aa_create_ns(struct aa_ns *parent, const char *name,
struct dentry *dir);
struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name); struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name);
void __aa_remove_ns(struct aa_ns *ns); void __aa_remove_ns(struct aa_ns *ns);
...@@ -124,4 +126,25 @@ static inline void aa_put_ns(struct aa_ns *ns) ...@@ -124,4 +126,25 @@ static inline void aa_put_ns(struct aa_ns *ns)
aa_put_profile(ns->unconfined); aa_put_profile(ns->unconfined);
} }
/**
* __aa_findn_ns - find a namespace on a list by @name
* @head: list to search for namespace on (NOT NULL)
* @name: name of namespace to look for (NOT NULL)
* @n: length of @name
* Returns: unrefcounted namespace
*
* Requires: rcu_read_lock be held
*/
static inline struct aa_ns *__aa_findn_ns(struct list_head *head,
const char *name, size_t n)
{
return (struct aa_ns *)__policy_strn_find(head, name, n);
}
static inline struct aa_ns *__aa_find_ns(struct list_head *head,
const char *name)
{
return __aa_findn_ns(head, name, strlen(name));
}
#endif /* AA_NAMESPACE_H */ #endif /* AA_NAMESPACE_H */
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#define __POLICY_INTERFACE_H #define __POLICY_INTERFACE_H
#include <linux/list.h> #include <linux/list.h>
#include <linux/kref.h>
struct aa_load_ent { struct aa_load_ent {
struct list_head list; struct list_head list;
...@@ -35,6 +36,30 @@ struct aa_load_ent *aa_load_ent_alloc(void); ...@@ -35,6 +36,30 @@ struct aa_load_ent *aa_load_ent_alloc(void);
#define PACKED_MODE_KILL 2 #define PACKED_MODE_KILL 2
#define PACKED_MODE_UNCONFINED 3 #define PACKED_MODE_UNCONFINED 3
int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns); /* struct aa_loaddata - buffer of policy load data set */
struct aa_loaddata {
struct kref count;
size_t size;
int abi;
unsigned char *hash;
char data[];
};
int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns);
static inline struct aa_loaddata *
aa_get_loaddata(struct aa_loaddata *data)
{
if (data)
kref_get(&(data->count));
return data;
}
void aa_loaddata_kref(struct kref *kref);
static inline void aa_put_loaddata(struct aa_loaddata *data)
{
if (data)
kref_put(&data->count, aa_loaddata_kref);
}
#endif /* __POLICY_INTERFACE_H */ #endif /* __POLICY_INTERFACE_H */
...@@ -1061,8 +1061,11 @@ static struct aa_label *label_merge_insert(struct aa_label *new, ...@@ -1061,8 +1061,11 @@ static struct aa_label *label_merge_insert(struct aa_label *new,
AA_BUG(new->size < a->size + b->size); AA_BUG(new->size < a->size + b->size);
label_for_each_in_merge(i, a, b, next) { label_for_each_in_merge(i, a, b, next) {
AA_BUG(!next);
if (profile_is_stale(next)) { if (profile_is_stale(next)) {
new->vec[k] = aa_get_newest_profile(next); new->vec[k] = aa_get_newest_profile(next);
AA_BUG(!new->vec[k]->label.proxy);
AA_BUG(!new->vec[k]->label.proxy->label);
if (next->label.proxy != new->vec[k]->label.proxy) if (next->label.proxy != new->vec[k]->label.proxy)
invcount++; invcount++;
k++; k++;
...@@ -1991,10 +1994,8 @@ static struct aa_label *__label_update(struct aa_label *label) ...@@ -1991,10 +1994,8 @@ static struct aa_label *__label_update(struct aa_label *label)
{ {
struct aa_label *new, *tmp; struct aa_label *new, *tmp;
struct aa_labelset *ls; struct aa_labelset *ls;
struct aa_profile *p;
struct label_it i;
unsigned long flags; unsigned long flags;
int invcount = 0; int i, invcount = 0;
AA_BUG(!label); AA_BUG(!label);
AA_BUG(!mutex_is_locked(&labels_ns(label)->lock)); AA_BUG(!mutex_is_locked(&labels_ns(label)->lock));
...@@ -2008,9 +2009,13 @@ static struct aa_label *__label_update(struct aa_label *label) ...@@ -2008,9 +2009,13 @@ static struct aa_label *__label_update(struct aa_label *label)
*/ */
ls = labels_set(label); ls = labels_set(label);
write_lock_irqsave(&ls->lock, flags); write_lock_irqsave(&ls->lock, flags);
label_for_each(i, label, p) { for (i = 0; i < label->size; i++) {
new->vec[i.i] = aa_get_newest_profile(p); AA_BUG(!label->vec[i]);
if (new->vec[i.i]->label.proxy != p->label.proxy) new->vec[i] = aa_get_newest_profile(label->vec[i]);
AA_BUG(!new->vec[i]);
AA_BUG(!new->vec[i]->label.proxy);
AA_BUG(!new->vec[i]->label.proxy->label);
if (new->vec[i]->label.proxy != label->vec[i]->label.proxy)
invcount++; invcount++;
} }
......
This diff is collapsed.
...@@ -90,7 +90,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize) ...@@ -90,7 +90,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
table = kvzalloc(tsize); table = kvzalloc(tsize);
if (table) { if (table) {
*table = th; table->td_id = th.td_id;
table->td_flags = th.td_flags;
table->td_lolen = th.td_lolen;
if (th.td_flags == YYTD_DATA8) if (th.td_flags == YYTD_DATA8)
UNPACK_ARRAY(table->td_data, blob, th.td_lolen, UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
u8, byte_to_byte); u8, byte_to_byte);
......
This diff is collapsed.
...@@ -49,8 +49,8 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) ...@@ -49,8 +49,8 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen)
* of chroot) and specifically directed to connect paths to * of chroot) and specifically directed to connect paths to
* namespace root. * namespace root.
*/ */
static int disconnect(struct path *path, char *buf, char **name, int flags, static int disconnect(const struct path *path, char *buf, char **name,
const char *disconnected) int flags, const char *disconnected)
{ {
int error = 0; int error = 0;
...@@ -89,7 +89,7 @@ static int disconnect(struct path *path, char *buf, char **name, int flags, ...@@ -89,7 +89,7 @@ static int disconnect(struct path *path, char *buf, char **name, int flags,
* When no error the path name is returned in @name which points to * When no error the path name is returned in @name which points to
* to a position in @buf * to a position in @buf
*/ */
static int d_namespace_path(struct path *path, char *buf, char **name, static int d_namespace_path(const struct path *path, char *buf, char **name,
int flags, const char *disconnected) int flags, const char *disconnected)
{ {
char *res; char *res;
...@@ -162,7 +162,7 @@ static int d_namespace_path(struct path *path, char *buf, char **name, ...@@ -162,7 +162,7 @@ static int d_namespace_path(struct path *path, char *buf, char **name,
* allocated. * allocated.
*/ */
if (d_unlinked(path->dentry) && d_is_positive(path->dentry) && if (d_unlinked(path->dentry) && d_is_positive(path->dentry) &&
!(flags & PATH_MEDIATE_DELETED)) { !(flags & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))) {
error = -ENOENT; error = -ENOENT;
goto out; goto out;
} }
...@@ -198,8 +198,8 @@ static int d_namespace_path(struct path *path, char *buf, char **name, ...@@ -198,8 +198,8 @@ static int d_namespace_path(struct path *path, char *buf, char **name,
* *
* Returns: %0 else error code if could retrieve name * Returns: %0 else error code if could retrieve name
*/ */
int aa_path_name(struct path *path, int flags, char *buffer, const char **name, int aa_path_name(const struct path *path, int flags, char *buffer,
const char **info, const char *disconnected) const char **name, const char **info, const char *disconnected)
{ {
char *str = NULL; char *str = NULL;
int error = d_namespace_path(path, buffer, &str, flags, disconnected); int error = d_namespace_path(path, buffer, &str, flags, disconnected);
......
...@@ -87,10 +87,11 @@ ...@@ -87,10 +87,11 @@
#include "include/match.h" #include "include/match.h"
#include "include/path.h" #include "include/path.h"
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_ns.h"
#include "include/policy_unpack.h" #include "include/policy_unpack.h"
#include "include/resource.h" #include "include/resource.h"
int unprivileged_userns_apparmor_policy = 0; int unprivileged_userns_apparmor_policy = 1;
/* Note: mode names must be unique in the first character because of /* Note: mode names must be unique in the first character because of
* modechrs used to print modes on compound labels on some interfaces * modechrs used to print modes on compound labels on some interfaces
...@@ -102,6 +103,19 @@ const char *const aa_profile_mode_names[] = { ...@@ -102,6 +103,19 @@ const char *const aa_profile_mode_names[] = {
"unconfined", "unconfined",
}; };
/**
* aa_free_data - free a data blob
* @ptr: data to free
* @arg: unused
*/
static void aa_free_data(void *ptr, void *arg)
{
struct aa_data *data = ptr;
kzfree(data->data);
kzfree(data->key);
kzfree(data);
}
/** /**
* __add_profile - add a profiles to list and label tree * __add_profile - add a profiles to list and label tree
...@@ -199,6 +213,8 @@ void __aa_profile_list_release(struct list_head *head) ...@@ -199,6 +213,8 @@ void __aa_profile_list_release(struct list_head *head)
*/ */
void aa_free_profile(struct aa_profile *profile) void aa_free_profile(struct aa_profile *profile)
{ {
struct rhashtable *rht;
AA_DEBUG("%s(%p)\n", __func__, profile); AA_DEBUG("%s(%p)\n", __func__, profile);
if (!profile) if (!profile)
...@@ -220,7 +236,16 @@ void aa_free_profile(struct aa_profile *profile) ...@@ -220,7 +236,16 @@ void aa_free_profile(struct aa_profile *profile)
aa_put_dfa(profile->xmatch); aa_put_dfa(profile->xmatch);
aa_put_dfa(profile->policy.dfa); aa_put_dfa(profile->policy.dfa);
if (profile->data) {
rht = profile->data;
profile->data = NULL;
rhashtable_free_and_destroy(rht, aa_free_data, NULL);
kzfree(rht);
}
kzfree(profile->hash); kzfree(profile->hash);
aa_put_loaddata(profile->rawdata);
kzfree(profile); kzfree(profile);
} }
...@@ -611,49 +636,49 @@ static int audit_policy(struct aa_label *label, const char *op, ...@@ -611,49 +636,49 @@ static int audit_policy(struct aa_label *label, const char *op,
aad(&sa)->info = info; aad(&sa)->info = info;
aad(&sa)->error = error; aad(&sa)->error = error;
aad(&sa)->label = label; aad(&sa)->label = label;
aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, audit_cb); aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, audit_cb);
return error; return error;
} }
bool policy_view_capable(void) /**
* policy_view_capable - check if viewing policy in at @ns is allowed
* ns: namespace being viewed by current task (may be NULL)
* Returns: true if viewing policy is allowed
*
* If @ns is NULL then the namespace being viewed is assumed to be the
* tasks current namespace.
*/
bool policy_view_capable(struct aa_ns *ns)
{ {
struct user_namespace *user_ns = current_user_ns(); struct user_namespace *user_ns = current_user_ns();
struct aa_ns *ns = aa_get_current_ns(); struct aa_ns *view_ns = aa_get_current_ns();
bool root_in_user_ns = uid_eq(current_euid(), make_kuid(user_ns, 0)) ||
in_egroup_p(make_kgid(user_ns, 0));
bool response = false; bool response = false;
if (!ns)
ns = view_ns;
if (ns_capable(user_ns, CAP_MAC_ADMIN) && if (root_in_user_ns && aa_ns_visible(view_ns, ns, true) &&
(user_ns == &init_user_ns || (user_ns == &init_user_ns ||
(unprivileged_userns_apparmor_policy != 0 && (unprivileged_userns_apparmor_policy != 0 &&
user_ns->level == 1 && ns != root_ns))) user_ns->level == view_ns->level)))
response = true; response = true;
aa_put_ns(ns); aa_put_ns(view_ns);
return response; return response;
} }
bool policy_admin_capable(void) bool policy_admin_capable(struct aa_ns *ns)
{
return policy_view_capable() && !aa_g_lock_policy;
}
bool aa_may_open_profiles(void)
{ {
struct user_namespace *user_ns = current_user_ns(); struct user_namespace *user_ns = current_user_ns();
struct aa_ns *ns = aa_get_current_ns(); bool capable = ns_capable(user_ns, CAP_MAC_ADMIN);
bool root_in_user_ns = uid_eq(current_euid(), make_kuid(user_ns, 0)) ||
in_egroup_p(make_kgid(user_ns, 0));
bool response = false;
if (root_in_user_ns && AA_DEBUG("cap_mac_admin? %d\n", capable);
(user_ns == &init_user_ns || AA_DEBUG("policy locked? %d\n", aa_g_lock_policy);
(unprivileged_userns_apparmor_policy != 0 &&
user_ns->level == 1 && ns != root_ns)))
response = true;
aa_put_ns(ns);
return response; return policy_view_capable(ns) && capable && !aa_g_lock_policy;
} }
/** /**
...@@ -663,7 +688,7 @@ bool aa_may_open_profiles(void) ...@@ -663,7 +688,7 @@ bool aa_may_open_profiles(void)
* *
* Returns: 0 if the task is allowed to manipulate policy else error * Returns: 0 if the task is allowed to manipulate policy else error
*/ */
int aa_may_manage_policy(struct aa_label *label, u32 mask) int aa_may_manage_policy(struct aa_label *label, struct aa_ns *ns, u32 mask)
{ {
const char *op; const char *op;
...@@ -679,7 +704,7 @@ int aa_may_manage_policy(struct aa_label *label, u32 mask) ...@@ -679,7 +704,7 @@ int aa_may_manage_policy(struct aa_label *label, u32 mask)
return audit_policy(label, op, NULL, NULL, "policy_locked", return audit_policy(label, op, NULL, NULL, "policy_locked",
-EACCES); -EACCES);
if (!policy_admin_capable()) if (!policy_admin_capable(ns))
return audit_policy(label, op, NULL, NULL, "not policy admin", return audit_policy(label, op, NULL, NULL, "not policy admin",
-EACCES); -EACCES);
...@@ -825,10 +850,10 @@ static struct aa_profile *update_to_newest_parent(struct aa_profile *new) ...@@ -825,10 +850,10 @@ static struct aa_profile *update_to_newest_parent(struct aa_profile *new)
/** /**
* aa_replace_profiles - replace profile(s) on the profile list * aa_replace_profiles - replace profile(s) on the profile list
* @view: namespace load is viewed from
* @label: label that is attempting to load/replace policy * @label: label that is attempting to load/replace policy
* @mask: permission mask * @mask: permission mask
* @udata: serialized data stream (NOT NULL) * @udata: serialized data stream (NOT NULL)
* @size: size of the serialized data stream
* *
* unpack and replace a profile on the profile list and uses of that profile * 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 * by any aa_task_ctx. If the profile does not exist on the profile list
...@@ -836,10 +861,10 @@ static struct aa_profile *update_to_newest_parent(struct aa_profile *new) ...@@ -836,10 +861,10 @@ static struct aa_profile *update_to_newest_parent(struct aa_profile *new)
* *
* Returns: size of data consumed else error code on failure. * Returns: size of data consumed else error code on failure.
*/ */
ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata, ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_label *label,
size_t size) u32 mask, struct aa_loaddata *udata)
{ {
const char *ns_name, *name = NULL, *info = NULL; const char *ns_name, *info = NULL;
struct aa_ns *ns = NULL; struct aa_ns *ns = NULL;
struct aa_load_ent *ent, *tmp; struct aa_load_ent *ent, *tmp;
const char *op = mask & AA_MAY_REPLACE_POLICY ? OP_PROF_REPL : OP_PROF_LOAD; const char *op = mask & AA_MAY_REPLACE_POLICY ? OP_PROF_REPL : OP_PROF_LOAD;
...@@ -848,7 +873,7 @@ ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata, ...@@ -848,7 +873,7 @@ ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata,
LIST_HEAD(lh); LIST_HEAD(lh);
/* released below */ /* released below */
error = aa_unpack(udata, size, &lh, &ns_name); error = aa_unpack(udata, &lh, &ns_name);
if (error) if (error)
goto out; goto out;
...@@ -877,21 +902,22 @@ ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata, ...@@ -877,21 +902,22 @@ ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata,
count++; count++;
} }
if (ns_name) { if (ns_name) {
ns = aa_prepare_ns(labels_ns(label), ns_name); ns = aa_prepare_ns(view, ns_name);
if (!ns) { if (IS_ERR(ns)) {
info = "failed to prepare namespace"; info = "failed to prepare namespace";
error = -ENOMEM; error = PTR_ERR(ns);
ns = NULL;
goto fail; goto fail;
} }
} else } else
ns = aa_get_ns(labels_ns(label)); ns = aa_get_ns(view);
mutex_lock(&ns->lock); mutex_lock(&ns->lock);
/* setup parent and ns info */ /* setup parent and ns info */
list_for_each_entry(ent, &lh, list) { list_for_each_entry(ent, &lh, list) {
struct aa_policy *policy; struct aa_policy *policy;
name = ent->new->base.hname; ent->new->rawdata = aa_get_loaddata(udata);
error = __lookup_replace(ns, ent->new->base.hname, error = __lookup_replace(ns, ent->new->base.hname,
!(mask & AA_MAY_REPLACE_POLICY), !(mask & AA_MAY_REPLACE_POLICY),
&ent->old, &info); &ent->old, &info);
...@@ -920,7 +946,6 @@ ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata, ...@@ -920,7 +946,6 @@ ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata,
if (!p) { if (!p) {
error = -ENOENT; error = -ENOENT;
info = "parent does not exist"; info = "parent does not exist";
name = ent->new->base.hname;
goto fail_lock; goto fail_lock;
} }
rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
...@@ -985,13 +1010,26 @@ ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata, ...@@ -985,13 +1010,26 @@ ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata,
if (error) if (error)
return error; return error;
return size; return udata->size;
fail_lock: fail_lock:
mutex_unlock(&ns->lock); mutex_unlock(&ns->lock);
fail:
error = audit_policy(label, op, ns_name, name, info, error);
/* audit cause of failure */
op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
fail:
audit_policy(label, op, ns_name, ent->new->base.hname, info, error);
/* audit status that rest of profiles in the atomic set failed too */
info = "valid profile in failed atomic policy load";
list_for_each_entry(tmp, &lh, list) {
if (tmp == ent) {
info = "unchecked profile in failed atomic policy load";
/* skip entry that caused failure */
continue;
}
op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
audit_policy(label, op, ns_name, tmp->new->base.hname, info, error);
}
list_for_each_entry_safe(ent, tmp, &lh, list) { list_for_each_entry_safe(ent, tmp, &lh, list) {
list_del_init(&ent->list); list_del_init(&ent->list);
aa_load_ent_free(ent); aa_load_ent_free(ent);
...@@ -1002,6 +1040,7 @@ ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata, ...@@ -1002,6 +1040,7 @@ ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata,
/** /**
* aa_remove_profiles - remove profile(s) from the system * aa_remove_profiles - remove profile(s) from the system
* @view: namespace the remove is being done from
* @label: label attempting to remove policy * @label: label attempting to remove policy
* @fqname: name of the profile or namespace to remove (NOT NULL) * @fqname: name of the profile or namespace to remove (NOT NULL)
* @size: size of the name * @size: size of the name
...@@ -1013,7 +1052,8 @@ ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata, ...@@ -1013,7 +1052,8 @@ ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata,
* *
* Returns: size of data consume else error code if fails * Returns: size of data consume else error code if fails
*/ */
ssize_t aa_remove_profiles(struct aa_label *label, char *fqname, size_t size) ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_label *label,
char *fqname, size_t size)
{ {
struct aa_ns *root = NULL, *ns = NULL; struct aa_ns *root = NULL, *ns = NULL;
struct aa_profile *profile = NULL; struct aa_profile *profile = NULL;
...@@ -1027,7 +1067,7 @@ ssize_t aa_remove_profiles(struct aa_label *label, char *fqname, size_t size) ...@@ -1027,7 +1067,7 @@ ssize_t aa_remove_profiles(struct aa_label *label, char *fqname, size_t size)
goto fail; goto fail;
} }
root = labels_ns(label); root = view;
if (fqname[0] == ':') { if (fqname[0] == ':') {
name = aa_split_fqname(fqname, &ns_name); name = aa_split_fqname(fqname, &ns_name);
......
...@@ -147,21 +147,6 @@ void aa_free_ns(struct aa_ns *ns) ...@@ -147,21 +147,6 @@ void aa_free_ns(struct aa_ns *ns)
kzfree(ns); kzfree(ns);
} }
/**
* __aa_findn_ns - find a namespace on a list by @name
* @head: list to search for namespace on (NOT NULL)
* @name: name of namespace to look for (NOT NULL)
* @n: length of @name
* Returns: unrefcounted namespace
*
* Requires: rcu_read_lock be held
*/
static struct aa_ns *__aa_findn_ns(struct list_head *head, const char *name,
size_t n)
{
return (struct aa_ns *)__policy_strn_find(head, name, n);
}
/** /**
* aa_find_ns - look up a profile namespace on the namespace list * aa_find_ns - look up a profile namespace on the namespace list
* @root: namespace to search in (NOT NULL) * @root: namespace to search in (NOT NULL)
...@@ -199,43 +184,84 @@ struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name) ...@@ -199,43 +184,84 @@ struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name)
return aa_findn_ns(root, name, strlen(name)); return aa_findn_ns(root, name, strlen(name));
} }
static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name,
struct dentry *dir)
{
struct aa_ns *ns;
int error;
AA_BUG(!parent);
AA_BUG(!name);
AA_BUG(!mutex_is_locked(&parent->lock));
ns = alloc_ns(parent->base.hname, name);
if (!ns)
return NULL;
mutex_lock(&ns->lock);
error = __aa_fs_ns_mkdir(ns, ns_subns_dir(parent), name, dir);
if (error) {
AA_ERROR("Failed to create interface for ns %s\n",
ns->base.name);
mutex_unlock(&ns->lock);
aa_free_ns(ns);
return ERR_PTR(error);
} else {
ns->parent = aa_get_ns(parent);
ns->level = parent->level + 1;
list_add_rcu(&ns->base.list, &parent->sub_ns);
/* add list ref */
aa_get_ns(ns);
}
mutex_unlock(&ns->lock);
return ns;
}
/**
* aa_create_ns - create an ns, fail if it already exists
* @parent: the parent of the namespace being created
* @name: the name of the namespace
* @dir: if not null the dir to put the ns entries in
*
* Returns: the a refcounted ns that has been add or an ERR_PTR
*/
struct aa_ns *aa_create_ns(struct aa_ns *parent, const char *name,
struct dentry *dir)
{
struct aa_ns *ns;
mutex_lock(&parent->lock);
/* try and find the specified ns */
/* released by caller */
ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name));
if (!ns)
ns = __aa_create_ns(parent, name, dir);
else
ns = ERR_PTR(-EEXIST);
mutex_unlock(&parent->lock);
/* return ref */
return ns;
}
/** /**
* aa_prepare_ns - find an existing or create a new namespace of @name * aa_prepare_ns - find an existing or create a new namespace of @name
* @root: ns to treat as root * @parent: ns to treat as parent
* @name: the namespace to find or add (NOT NULL) * @name: the namespace to find or add (NOT NULL)
* *
* Returns: refcounted namespace or NULL if failed to create one * Returns: refcounted namespace or PTR_ERR if failed to create one
*/ */
struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name) struct aa_ns *aa_prepare_ns(struct aa_ns *parent, const char *name)
{ {
struct aa_ns *ns; struct aa_ns *ns;
mutex_lock(&root->lock); mutex_lock(&parent->lock);
/* try and find the specified ns and if it doesn't exist create it */ /* try and find the specified ns and if it doesn't exist create it */
/* released by caller */ /* released by caller */
ns = aa_get_ns(__aa_findn_ns(&root->sub_ns, name, strlen(name))); ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name));
if (!ns) { if (!ns)
ns = alloc_ns(root->base.hname, name); ns = __aa_create_ns(parent, name, NULL);
if (!ns) mutex_unlock(&parent->lock);
goto out;
mutex_lock(&ns->lock);
if (__aa_fs_ns_mkdir(ns, ns_subns_dir(root), name)) {
AA_ERROR("Failed to create interface for ns %s\n",
ns->base.name);
mutex_unlock(&ns->lock);
aa_free_ns(ns);
ns = NULL;
goto out;
}
ns->parent = aa_get_ns(root);
ns->level = root->level + 1;
list_add_rcu(&ns->base.list, &root->sub_ns);
/* add list ref */
aa_get_ns(ns);
mutex_unlock(&ns->lock);
}
out:
mutex_unlock(&root->lock);
/* return ref */ /* return ref */
return ns; return ns;
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/jhash.h>
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/audit.h" #include "include/audit.h"
...@@ -124,6 +125,15 @@ static int audit_iface(struct aa_profile *new, const char *ns_name, ...@@ -124,6 +125,15 @@ static int audit_iface(struct aa_profile *new, const char *ns_name,
return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb); return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb);
} }
void aa_loaddata_kref(struct kref *kref)
{
struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count);
if (d) {
kzfree(d->hash);
kvfree(d);
}
}
/* test if read will be in packed data bounds */ /* test if read will be in packed data bounds */
static bool inbounds(struct aa_ext *e, size_t size) static bool inbounds(struct aa_ext *e, size_t size)
{ {
...@@ -495,6 +505,30 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) ...@@ -495,6 +505,30 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
return 0; return 0;
} }
static void *kvmemdup(const void *src, size_t len)
{
void *p = kvmalloc(len);
if (p)
memcpy(p, src, len);
return p;
}
static u32 strhash(const void *data, u32 len, u32 seed)
{
const char * const *key = data;
return jhash(*key, strlen(*key), seed);
}
static int datacmp(struct rhashtable_compare_arg *arg, const void *obj)
{
const struct aa_data *data = obj;
const char * const *key = arg->key;
return strcmp(data->key, *key);
}
/** /**
* unpack_profile - unpack a serialized profile * unpack_profile - unpack a serialized profile
* @e: serialized data extent information (NOT NULL) * @e: serialized data extent information (NOT NULL)
...@@ -507,6 +541,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -507,6 +541,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
const char *tmpname, *tmpns = NULL, *name = NULL; const char *tmpname, *tmpns = NULL, *name = NULL;
const char *info = "failed to unpack profile"; const char *info = "failed to unpack profile";
size_t size = 0, ns_len; size_t size = 0, ns_len;
struct rhashtable_params params = { 0 };
char *key = NULL;
struct aa_data *data;
int i, error = -EPROTO; int i, error = -EPROTO;
kernel_cap_t tmpcap; kernel_cap_t tmpcap;
u32 tmp; u32 tmp;
...@@ -697,6 +734,45 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -697,6 +734,45 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (!unpack_trans_table(e, profile)) if (!unpack_trans_table(e, profile))
goto fail; goto fail;
if (unpack_nameX(e, AA_STRUCT, "data")) {
profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
if (!profile->data)
goto fail;
params.nelem_hint = 3;
params.key_len = sizeof(void *);
params.key_offset = offsetof(struct aa_data, key);
params.head_offset = offsetof(struct aa_data, head);
params.hashfn = strhash;
params.obj_cmpfn = datacmp;
if (rhashtable_init(profile->data, &params))
goto fail;
while (unpack_strdup(e, &key, NULL)) {
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
kzfree(key);
goto fail;
}
data->key = key;
data->size = unpack_blob(e, &data->data, NULL);
data->data = kvmemdup(data->data, data->size);
if (data->size && !data->data) {
kzfree(data->key);
kzfree(data);
goto fail;
}
rhashtable_insert_fast(profile->data, &data->head,
profile->data->p);
}
if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
}
if (!unpack_nameX(e, AA_STRUCTEND, NULL)) if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail; goto fail;
...@@ -827,7 +903,6 @@ struct aa_load_ent *aa_load_ent_alloc(void) ...@@ -827,7 +903,6 @@ struct aa_load_ent *aa_load_ent_alloc(void)
/** /**
* aa_unpack - unpack packed binary profile(s) data loaded from user space * aa_unpack - unpack packed binary profile(s) data loaded from user space
* @udata: user data copied to kmem (NOT NULL) * @udata: user data copied to kmem (NOT NULL)
* @size: the size of the user data
* @lh: list to place unpacked profiles in a aa_repl_ws * @lh: list to place unpacked profiles in a aa_repl_ws
* @ns: Returns namespace profile is in if specified else NULL (NOT NULL) * @ns: Returns namespace profile is in if specified else NULL (NOT NULL)
* *
...@@ -837,15 +912,15 @@ struct aa_load_ent *aa_load_ent_alloc(void) ...@@ -837,15 +912,15 @@ struct aa_load_ent *aa_load_ent_alloc(void)
* *
* Returns: profile(s) on @lh else error pointer if fails to unpack * Returns: profile(s) on @lh else error pointer if fails to unpack
*/ */
int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns)
{ {
struct aa_load_ent *tmp, *ent; struct aa_load_ent *tmp, *ent;
struct aa_profile *profile = NULL; struct aa_profile *profile = NULL;
int error; int error;
struct aa_ext e = { struct aa_ext e = {
.start = udata, .start = udata->data,
.end = udata + size, .end = udata->data + udata->size,
.pos = udata, .pos = udata->data,
}; };
*ns = NULL; *ns = NULL;
...@@ -855,7 +930,6 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) ...@@ -855,7 +930,6 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
error = verify_header(&e, e.pos == e.start, ns); error = verify_header(&e, e.pos == e.start, ns);
if (error) if (error)
goto fail; goto fail;
start = e.pos; start = e.pos;
profile = unpack_profile(&e, &ns_name); profile = unpack_profile(&e, &ns_name);
if (IS_ERR(profile)) { if (IS_ERR(profile)) {
...@@ -883,7 +957,15 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) ...@@ -883,7 +957,15 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
ent->ns_name = ns_name; ent->ns_name = ns_name;
list_add_tail(&ent->list, lh); list_add_tail(&ent->list, lh);
} }
udata->abi = e.version & K_ABI_MASK;
if (aa_g_hash_policy) {
udata->hash = aa_calc_hash(udata->data, udata->size);
if (IS_ERR(udata->hash)) {
error = PTR_ERR(udata->hash);
udata->hash = NULL;
goto fail;
}
}
return 0; return 0;
fail_profile: fail_profile:
......
...@@ -46,8 +46,42 @@ static struct file_system_type fs_type = { ...@@ -46,8 +46,42 @@ static struct file_system_type fs_type = {
.kill_sb = kill_litter_super, .kill_sb = kill_litter_super,
}; };
int securityfs_pin_fs(void)
{
return simple_pin_fs(&fs_type, &mount, &mount_count);
}
int __securityfs_setup_d_inode(struct inode *dir, struct dentry *dentry,
umode_t mode, void *data,
const struct file_operations *fops,
const struct inode_operations *iops)
{
bool is_dir = S_ISDIR(mode);
struct inode *inode = new_inode(dir->i_sb);
if (!inode)
return -ENOMEM;
inode->i_ino = get_next_ino();
inode->i_mode = mode;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
inode->i_private = data;
if (is_dir) {
inode->i_op = iops ? iops : &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
inc_nlink(inode);
inc_nlink(dir);
} else {
inode->i_fop = fops;
}
d_instantiate(dentry, inode);
dget(dentry);
return 0;
}
EXPORT_SYMBOL_GPL(__securityfs_setup_d_inode);
/** /**
* securityfs_create_file - create a file in the securityfs filesystem * securityfs_create_dentry - create a file/dir in the securityfs filesystem
* *
* @name: a pointer to a string containing the name of the file to create. * @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have * @mode: the permission that the file should have
...@@ -59,8 +93,10 @@ static struct file_system_type fs_type = { ...@@ -59,8 +93,10 @@ static struct file_system_type fs_type = {
* the open() call. * the open() call.
* @fops: a pointer to a struct file_operations that should be used for * @fops: a pointer to a struct file_operations that should be used for
* this file. * this file.
* @iops: a point to a struct of inode_operations that should be used for
* this file/dir
* *
* This is the basic "create a file" function for securityfs. It allows for a * This is the basic "create a xxx" function for securityfs. It allows for a
* wide range of flexibility in creating a file, or a directory (if you * wide range of flexibility in creating a file, or a directory (if you
* want to create a directory, the securityfs_create_dir() function is * want to create a directory, the securityfs_create_dir() function is
* recommended to be used instead). * recommended to be used instead).
...@@ -74,13 +110,14 @@ static struct file_system_type fs_type = { ...@@ -74,13 +110,14 @@ static struct file_system_type fs_type = {
* If securityfs is not enabled in the kernel, the value %-ENODEV is * If securityfs is not enabled in the kernel, the value %-ENODEV is
* returned. * returned.
*/ */
struct dentry *securityfs_create_file(const char *name, umode_t mode, struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
struct dentry *parent, void *data, struct dentry *parent, void *data,
const struct file_operations *fops) const struct file_operations *fops,
const struct inode_operations *iops)
{ {
struct dentry *dentry; struct dentry *dentry;
int is_dir = S_ISDIR(mode); int is_dir = S_ISDIR(mode);
struct inode *dir, *inode; struct inode *dir;
int error; int error;
if (!is_dir) { if (!is_dir) {
...@@ -109,26 +146,9 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode, ...@@ -109,26 +146,9 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode,
goto out1; goto out1;
} }
inode = new_inode(dir->i_sb); error = __securityfs_setup_d_inode(dir, dentry, mode, data, fops, iops);
if (!inode) { if (error)
error = -ENOMEM;
goto out1; goto out1;
}
inode->i_ino = get_next_ino();
inode->i_mode = mode;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
inode->i_private = data;
if (is_dir) {
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
inc_nlink(inode);
inc_nlink(dir);
} else {
inode->i_fop = fops;
}
d_instantiate(dentry, inode);
dget(dentry);
mutex_unlock(&dir->i_mutex); mutex_unlock(&dir->i_mutex);
return dentry; return dentry;
...@@ -140,6 +160,39 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode, ...@@ -140,6 +160,39 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode,
simple_release_fs(&mount, &mount_count); simple_release_fs(&mount, &mount_count);
return dentry; return dentry;
} }
EXPORT_SYMBOL_GPL(securityfs_create_dentry);
/**
* securityfs_create_file - create a file in the securityfs filesystem
*
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the securityfs filesystem.
* @data: a pointer to something that the caller will want to get to later
* on. The inode.i_private pointer will point to this value on
* the open() call.
* @fops: a pointer to a struct file_operations that should be used for
* this file.
*
* This function creates a file in securityfs with the given @name.
*
* This function returns a pointer to a dentry if it succeeds. This
* pointer must be passed to the securityfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here). If an error occurs, the function will return
* the error value (via ERR_PTR).
*
* If securityfs is not enabled in the kernel, the value %-ENODEV is
* returned.
*/
struct dentry *securityfs_create_file(const char *name, umode_t mode,
struct dentry *parent, void *data,
const struct file_operations *fops)
{
return securityfs_create_dentry(name, mode, parent, data, fops, NULL);
}
EXPORT_SYMBOL_GPL(securityfs_create_file); EXPORT_SYMBOL_GPL(securityfs_create_file);
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment