Browse Source

Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security

Pull security subsystem updates from James Morris:
 "Nothing major for this kernel, just maintenance updates"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (21 commits)
  apparmor: add the ability to report a sha1 hash of loaded policy
  apparmor: export set of capabilities supported by the apparmor module
  apparmor: add the profile introspection file to interface
  apparmor: add an optional profile attachment string for profiles
  apparmor: add interface files for profiles and namespaces
  apparmor: allow setting any profile into the unconfined state
  apparmor: make free_profile available outside of policy.c
  apparmor: rework namespace free path
  apparmor: update how unconfined is handled
  apparmor: change how profile replacement update is done
  apparmor: convert profile lists to RCU based locking
  apparmor: provide base for multiple profiles to be replaced at once
  apparmor: add a features/policy dir to interface
  apparmor: enable users to query whether apparmor is enabled
  apparmor: remove minimum size check for vmalloc()
  Smack: parse multiple rules per write to load2, up to PAGE_SIZE-1 bytes
  Smack: network label match fix
  security: smack: add a hash table to quicken smk_find_entry()
  security: smack: fix memleak in smk_write_rules_list()
  xattr: Constify ->name member of "struct xattr".
  ...
master
Linus Torvalds 9 years ago
parent
commit
11c7b03d42
  1. 2
      fs/ocfs2/xattr.h
  2. 8
      include/linux/security.h
  3. 2
      include/linux/xattr.h
  4. 2
      include/uapi/linux/reiserfs_xattr.h
  5. 12
      security/apparmor/Kconfig
  6. 7
      security/apparmor/Makefile
  7. 636
      security/apparmor/apparmorfs.c
  8. 5
      security/apparmor/capability.c
  9. 16
      security/apparmor/context.c
  10. 97
      security/apparmor/crypto.c
  11. 24
      security/apparmor/domain.c
  12. 6
      security/apparmor/include/apparmor.h
  13. 40
      security/apparmor/include/apparmorfs.h
  14. 1
      security/apparmor/include/audit.h
  15. 4
      security/apparmor/include/capability.h
  16. 15
      security/apparmor/include/context.h
  17. 36
      security/apparmor/include/crypto.h
  18. 218
      security/apparmor/include/policy.h
  19. 21
      security/apparmor/include/policy_unpack.h
  20. 5
      security/apparmor/lib.c
  21. 22
      security/apparmor/lsm.c
  22. 609
      security/apparmor/policy.c
  23. 135
      security/apparmor/policy_unpack.c
  24. 2
      security/apparmor/procattr.c
  25. 2
      security/capability.c
  26. 2
      security/integrity/evm/evm_main.c
  27. 8
      security/security.c
  28. 17
      security/selinux/hooks.c
  29. 13
      security/smack/smack.h
  30. 29
      security/smack/smack_access.c
  31. 51
      security/smack/smack_lsm.c
  32. 180
      security/smack/smackfs.c

2
fs/ocfs2/xattr.h

@ -32,7 +32,7 @@ enum ocfs2_xattr_type {
struct ocfs2_security_xattr_info {
int enable;
char *name;
const char *name;
void *value;
size_t value_len;
};

8
include/linux/security.h

@ -1492,7 +1492,7 @@ struct security_operations {
int (*inode_alloc_security) (struct inode *inode);
void (*inode_free_security) (struct inode *inode);
int (*inode_init_security) (struct inode *inode, struct inode *dir,
const struct qstr *qstr, char **name,
const struct qstr *qstr, const char **name,
void **value, size_t *len);
int (*inode_create) (struct inode *dir,
struct dentry *dentry, umode_t mode);
@ -1770,7 +1770,7 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
initxattrs initxattrs, void *fs_data);
int security_old_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, char **name,
const struct qstr *qstr, const char **name,
void **value, size_t *len);
int security_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode);
int security_inode_link(struct dentry *old_dentry, struct inode *dir,
@ -2094,8 +2094,8 @@ static inline int security_inode_init_security(struct inode *inode,
static inline int security_old_inode_init_security(struct inode *inode,
struct inode *dir,
const struct qstr *qstr,
char **name, void **value,
size_t *len)
const char **name,
void **value, size_t *len)
{
return -EOPNOTSUPP;
}

2
include/linux/xattr.h

@ -31,7 +31,7 @@ struct xattr_handler {
};
struct xattr {
char *name;
const char *name;
void *value;
size_t value_len;
};

2
include/uapi/linux/reiserfs_xattr.h

@ -16,7 +16,7 @@ struct reiserfs_xattr_header {
};
struct reiserfs_security_handle {
char *name;
const char *name;
void *value;
size_t length;
};

12
security/apparmor/Kconfig

@ -29,3 +29,15 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE
boot.
If you are unsure how to answer this question, answer 1.
config SECURITY_APPARMOR_HASH
bool "SHA1 hash of loaded profiles"
depends on SECURITY_APPARMOR
depends on CRYPTO
select CRYPTO_SHA1
default y
help
This option selects whether sha1 hashing is done against loaded
profiles and exported for inspection to user space via the apparmor
filesystem.

7
security/apparmor/Makefile

@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.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 \
resource.o sid.o file.o
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
clean-files := capability_names.h rlim_names.h
@ -18,7 +19,11 @@ quiet_cmd_make-caps = GEN $@
cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\
sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \
-e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\
echo "};" >> $@
echo "};" >> $@ ;\
echo -n '\#define AA_FS_CAPS_MASK "' >> $@ ;\
sed $< -r -n -e '/CAP_FS_MASK/d' \
-e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \
tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
# Build a lower case string table of rlimit names.

636
security/apparmor/apparmorfs.c

@ -12,6 +12,7 @@
* License.
*/
#include <linux/ctype.h>
#include <linux/security.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
@ -19,14 +20,55 @@
#include <linux/uaccess.h>
#include <linux/namei.h>
#include <linux/capability.h>
#include <linux/rcupdate.h>
#include "include/apparmor.h"
#include "include/apparmorfs.h"
#include "include/audit.h"
#include "include/context.h"
#include "include/crypto.h"
#include "include/policy.h"
#include "include/resource.h"
/**
* aa_mangle_name - mangle a profile name to std profile layout form
* @name: profile name to mangle (NOT NULL)
* @target: buffer to store mangled name, same length as @name (MAYBE NULL)
*
* Returns: length of mangled name
*/
static int mangle_name(char *name, char *target)
{
char *t = target;
while (*name == '/' || *name == '.')
name++;
if (target) {
for (; *name; name++) {
if (*name == '/')
*(t)++ = '.';
else if (isspace(*name))
*(t)++ = '_';
else if (isalnum(*name) || strchr("._-", *name))
*(t)++ = *name;
}
*t = 0;
} else {
int len = 0;
for (; *name; name++) {
if (isalnum(*name) || isspace(*name) ||
strchr("/._-", *name))
len++;
}
return len;
}
return t - target;
}
/**
* aa_simple_write_to_buffer - common routine for getting policy from user
* @op: operation doing the user buffer copy
@ -182,8 +224,567 @@ const struct file_operations aa_fs_seq_file_ops = {
.release = single_release,
};
/** Base file system setup **/
static int aa_fs_seq_profile_open(struct inode *inode, struct file *file,
int (*show)(struct seq_file *, void *))
{
struct aa_replacedby *r = aa_get_replacedby(inode->i_private);
int error = single_open(file, show, r);
if (error) {
file->private_data = NULL;
aa_put_replacedby(r);
}
return error;
}
static int aa_fs_seq_profile_release(struct inode *inode, struct file *file)
{
struct seq_file *seq = (struct seq_file *) file->private_data;
if (seq)
aa_put_replacedby(seq->private);
return single_release(inode, file);
}
static int aa_fs_seq_profname_show(struct seq_file *seq, void *v)
{
struct aa_replacedby *r = seq->private;
struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
seq_printf(seq, "%s\n", profile->base.name);
aa_put_profile(profile);
return 0;
}
static int aa_fs_seq_profname_open(struct inode *inode, struct file *file)
{
return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profname_show);
}
static const struct file_operations aa_fs_profname_fops = {
.owner = THIS_MODULE,
.open = aa_fs_seq_profname_open,
.read = seq_read,
.llseek = seq_lseek,
.release = aa_fs_seq_profile_release,
};
static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v)
{
struct aa_replacedby *r = seq->private;
struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]);
aa_put_profile(profile);
return 0;
}
static int aa_fs_seq_profmode_open(struct inode *inode, struct file *file)
{
return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profmode_show);
}
static const struct file_operations aa_fs_profmode_fops = {
.owner = THIS_MODULE,
.open = aa_fs_seq_profmode_open,
.read = seq_read,
.llseek = seq_lseek,
.release = aa_fs_seq_profile_release,
};
static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v)
{
struct aa_replacedby *r = seq->private;
struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
if (profile->attach)
seq_printf(seq, "%s\n", profile->attach);
else if (profile->xmatch)
seq_puts(seq, "<unknown>\n");
else
seq_printf(seq, "%s\n", profile->base.name);
aa_put_profile(profile);
return 0;
}
static int aa_fs_seq_profattach_open(struct inode *inode, struct file *file)
{
return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profattach_show);
}
static const struct file_operations aa_fs_profattach_fops = {
.owner = THIS_MODULE,
.open = aa_fs_seq_profattach_open,
.read = seq_read,
.llseek = seq_lseek,
.release = aa_fs_seq_profile_release,
};
static int aa_fs_seq_hash_show(struct seq_file *seq, void *v)
{
struct aa_replacedby *r = seq->private;
struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
unsigned int i, size = aa_hash_size();
if (profile->hash) {
for (i = 0; i < size; i++)
seq_printf(seq, "%.2x", profile->hash[i]);
seq_puts(seq, "\n");
}
return 0;
}
static int aa_fs_seq_hash_open(struct inode *inode, struct file *file)
{
return single_open(file, aa_fs_seq_hash_show, inode->i_private);
}
static const struct file_operations aa_fs_seq_hash_fops = {
.owner = THIS_MODULE,
.open = aa_fs_seq_hash_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/** fns to setup dynamic per profile/namespace files **/
void __aa_fs_profile_rmdir(struct aa_profile *profile)
{
struct aa_profile *child;
int i;
if (!profile)
return;
list_for_each_entry(child, &profile->base.profiles, base.list)
__aa_fs_profile_rmdir(child);
for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) {
struct aa_replacedby *r;
if (!profile->dents[i])
continue;
r = profile->dents[i]->d_inode->i_private;
securityfs_remove(profile->dents[i]);
aa_put_replacedby(r);
profile->dents[i] = NULL;
}
}
void __aa_fs_profile_migrate_dents(struct aa_profile *old,
struct aa_profile *new)
{
int i;
for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
new->dents[i] = old->dents[i];
old->dents[i] = NULL;
}
}
static struct dentry *create_profile_file(struct dentry *dir, const char *name,
struct aa_profile *profile,
const struct file_operations *fops)
{
struct aa_replacedby *r = aa_get_replacedby(profile->replacedby);
struct dentry *dent;
dent = securityfs_create_file(name, S_IFREG | 0444, dir, r, fops);
if (IS_ERR(dent))
aa_put_replacedby(r);
return dent;
}
/* requires lock be held */
int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
{
struct aa_profile *child;
struct dentry *dent = NULL, *dir;
int error;
if (!parent) {
struct aa_profile *p;
p = aa_deref_parent(profile);
dent = prof_dir(p);
/* adding to parent that previously didn't have children */
dent = securityfs_create_dir("profiles", dent);
if (IS_ERR(dent))
goto fail;
prof_child_dir(p) = parent = dent;
}
if (!profile->dirname) {
int len, id_len;
len = mangle_name(profile->base.name, NULL);
id_len = snprintf(NULL, 0, ".%ld", profile->ns->uniq_id);
profile->dirname = kmalloc(len + id_len + 1, GFP_KERNEL);
if (!profile->dirname)
goto fail;
mangle_name(profile->base.name, profile->dirname);
sprintf(profile->dirname + len, ".%ld", profile->ns->uniq_id++);
}
dent = securityfs_create_dir(profile->dirname, parent);
if (IS_ERR(dent))
goto fail;
prof_dir(profile) = dir = dent;
dent = create_profile_file(dir, "name", profile, &aa_fs_profname_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_NAME] = dent;
dent = create_profile_file(dir, "mode", profile, &aa_fs_profmode_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_MODE] = dent;
dent = create_profile_file(dir, "attach", profile,
&aa_fs_profattach_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_ATTACH] = dent;
if (profile->hash) {
dent = create_profile_file(dir, "sha1", profile,
&aa_fs_seq_hash_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_HASH] = dent;
}
list_for_each_entry(child, &profile->base.profiles, base.list) {
error = __aa_fs_profile_mkdir(child, prof_child_dir(profile));
if (error)
goto fail2;
}
return 0;
fail:
error = PTR_ERR(dent);
fail2:
__aa_fs_profile_rmdir(profile);
return error;
}
void __aa_fs_namespace_rmdir(struct aa_namespace *ns)
{
struct aa_namespace *sub;
struct aa_profile *child;
int i;
if (!ns)
return;
list_for_each_entry(child, &ns->base.profiles, base.list)
__aa_fs_profile_rmdir(child);
list_for_each_entry(sub, &ns->sub_ns, base.list) {
mutex_lock(&sub->lock);
__aa_fs_namespace_rmdir(sub);
mutex_unlock(&sub->lock);
}
for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) {
securityfs_remove(ns->dents[i]);
ns->dents[i] = NULL;
}
}
int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
const char *name)
{
struct aa_namespace *sub;
struct aa_profile *child;
struct dentry *dent, *dir;
int error;
if (!name)
name = ns->base.name;
dent = securityfs_create_dir(name, parent);
if (IS_ERR(dent))
goto fail;
ns_dir(ns) = dir = dent;
dent = securityfs_create_dir("profiles", dir);
if (IS_ERR(dent))
goto fail;
ns_subprofs_dir(ns) = dent;
dent = securityfs_create_dir("namespaces", dir);
if (IS_ERR(dent))
goto fail;
ns_subns_dir(ns) = dent;
list_for_each_entry(child, &ns->base.profiles, base.list) {
error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns));
if (error)
goto fail2;
}
list_for_each_entry(sub, &ns->sub_ns, base.list) {
mutex_lock(&sub->lock);
error = __aa_fs_namespace_mkdir(sub, ns_subns_dir(ns), NULL);
mutex_unlock(&sub->lock);
if (error)
goto fail2;
}
return 0;
fail:
error = PTR_ERR(dent);
fail2:
__aa_fs_namespace_rmdir(ns);
return error;
}
#define list_entry_next(pos, member) \
list_entry(pos->member.next, typeof(*pos), member)
#define list_entry_is_head(pos, head, member) (&pos->member == (head))
/**
* __next_namespace - find the next namespace to list
* @root: root namespace to stop search at (NOT NULL)
* @ns: current ns position (NOT NULL)
*
* Find the next namespace from @ns under @root and handle all locking needed
* while switching current namespace.
*
* Returns: next namespace or NULL if at last namespace under @root
* Requires: ns->parent->lock to be held
* NOTE: will not unlock root->lock
*/
static struct aa_namespace *__next_namespace(struct aa_namespace *root,
struct aa_namespace *ns)
{
struct aa_namespace *parent, *next;
/* is next namespace a child */
if (!list_empty(&ns->sub_ns)) {
next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
mutex_lock(&next->lock);
return next;
}
/* check if the next ns is a sibling, parent, gp, .. */
parent = ns->parent;
while (parent) {
mutex_unlock(&ns->lock);
next = list_entry_next(ns, base.list);
if (!list_entry_is_head(next, &parent->sub_ns, base.list)) {
mutex_lock(&next->lock);
return next;
}
if (parent == root)
return NULL;
ns = parent;
parent = parent->parent;
}
return NULL;
}
/**
* __first_profile - find the first profile in a namespace
* @root: namespace that is root of profiles being displayed (NOT NULL)
* @ns: namespace to start in (NOT NULL)
*
* Returns: unrefcounted profile or NULL if no profile
* Requires: profile->ns.lock to be held
*/
static struct aa_profile *__first_profile(struct aa_namespace *root,
struct aa_namespace *ns)
{
for (; ns; ns = __next_namespace(root, ns)) {
if (!list_empty(&ns->base.profiles))
return list_first_entry(&ns->base.profiles,
struct aa_profile, base.list);
}
return NULL;
}
/**
* __next_profile - step to the next profile in a profile tree
* @profile: current profile in tree (NOT NULL)
*
* Perform a depth first traversal on the profile tree in a namespace
*
* Returns: next profile or NULL if done
* Requires: profile->ns.lock to be held
*/
static struct aa_profile *__next_profile(struct aa_profile *p)
{
struct aa_profile *parent;
struct aa_namespace *ns = p->ns;
/* is next profile a child */
if (!list_empty(&p->base.profiles))
return list_first_entry(&p->base.profiles, typeof(*p),
base.list);
/* is next profile a sibling, parent sibling, gp, sibling, .. */
parent = rcu_dereference_protected(p->parent,
mutex_is_locked(&p->ns->lock));
while (parent) {
p = list_entry_next(p, base.list);
if (!list_entry_is_head(p, &parent->base.profiles, base.list))
return p;
p = parent;
parent = rcu_dereference_protected(parent->parent,
mutex_is_locked(&parent->ns->lock));
}
/* is next another profile in the namespace */
p = list_entry_next(p, base.list);
if (!list_entry_is_head(p, &ns->base.profiles, base.list))
return p;
return NULL;
}
/**
* next_profile - step to the next profile in where ever it may be
* @root: root namespace (NOT NULL)
* @profile: current profile (NOT NULL)
*
* Returns: next profile or NULL if there isn't one
*/
static struct aa_profile *next_profile(struct aa_namespace *root,
struct aa_profile *profile)
{
struct aa_profile *next = __next_profile(profile);
if (next)
return next;
/* finished all profiles in namespace move to next namespace */
return __first_profile(root, __next_namespace(root, profile->ns));
}
/**
* p_start - start a depth first traversal of profile tree
* @f: seq_file to fill
* @pos: current position
*
* Returns: first profile under current namespace or NULL if none found
*
* acquires first ns->lock
*/
static void *p_start(struct seq_file *f, loff_t *pos)
{
struct aa_profile *profile = NULL;
struct aa_namespace *root = aa_current_profile()->ns;
loff_t l = *pos;
f->private = aa_get_namespace(root);
/* find the first profile */
mutex_lock(&root->lock);
profile = __first_profile(root, root);
/* skip to position */
for (; profile && l > 0; l--)
profile = next_profile(root, profile);
return profile;
}
/**
* p_next - read the next profile entry
* @f: seq_file to fill
* @p: profile previously returned
* @pos: current position
*
* Returns: next profile after @p or NULL if none
*
* may acquire/release locks in namespace tree as necessary
*/
static void *p_next(struct seq_file *f, void *p, loff_t *pos)
{
struct aa_profile *profile = p;
struct aa_namespace *ns = f->private;
(*pos)++;
return next_profile(ns, profile);
}
/**
* p_stop - stop depth first traversal
* @f: seq_file we are filling
* @p: the last profile writen
*
* Release all locking done by p_start/p_next on namespace tree
*/
static void p_stop(struct seq_file *f, void *p)
{
struct aa_profile *profile = p;
struct aa_namespace *root = f->private, *ns;
if (profile) {
for (ns = profile->ns; ns && ns != root; ns = ns->parent)
mutex_unlock(&ns->lock);
}
mutex_unlock(&root->lock);
aa_put_namespace(root);
}
/**
* seq_show_profile - show a profile entry
* @f: seq_file to file
* @p: current position (profile) (NOT NULL)
*
* Returns: error on failure
*/
static int seq_show_profile(struct seq_file *f, void *p)
{
struct aa_profile *profile = (struct aa_profile *)p;
struct aa_namespace *root = f->private;
if (profile->ns != root)
seq_printf(f, ":%s://", aa_ns_name(root, profile->ns));
seq_printf(f, "%s (%s)\n", profile->base.hname,
aa_profile_mode_names[profile->mode]);
return 0;
}
static const struct seq_operations aa_fs_profiles_op = {
.start = p_start,
.next = p_next,
.stop = p_stop,
.show = seq_show_profile,
};
static int profiles_open(struct inode *inode, struct file *file)
{
return seq_open(file, &aa_fs_profiles_op);
}
static int profiles_release(struct inode *inode, struct file *file)
{
return seq_release(inode, file);
}
static const struct file_operations aa_fs_profiles_fops = {
.open = profiles_open,
.read = seq_read,
.llseek = seq_lseek,
.release = profiles_release,
};
/** Base file system setup **/
static struct aa_fs_entry aa_fs_entry_file[] = {
AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \
"link lock"),
@ -198,11 +799,18 @@ static struct aa_fs_entry aa_fs_entry_domain[] = {
{ }
};
static struct aa_fs_entry aa_fs_entry_policy[] = {
AA_FS_FILE_BOOLEAN("set_load", 1),
{}
};
static struct aa_fs_entry aa_fs_entry_features[] = {
AA_FS_DIR("policy", aa_fs_entry_policy),
AA_FS_DIR("domain", aa_fs_entry_domain),
AA_FS_DIR("file", aa_fs_entry_file),
AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
AA_FS_DIR("rlimit", aa_fs_entry_rlimit),
AA_FS_DIR("caps", aa_fs_entry_caps),
{ }
};
@ -210,6 +818,7 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = {
AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load),
AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace),
AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove),
AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops),
AA_FS_DIR("features", aa_fs_entry_features),
{ }
};
@ -240,6 +849,7 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file,
return error;
}
static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir);
/**
* aafs_create_dir - recursively create a directory entry in the securityfs
* @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL)
@ -250,17 +860,16 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file,
static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
struct dentry *parent)
{
int error;
struct aa_fs_entry *fs_file;
struct dentry *dir;
int error;
fs_dir->dentry = securityfs_create_dir(fs_dir->name, parent);
if (IS_ERR(fs_dir->dentry)) {
error = PTR_ERR(fs_dir->dentry);
fs_dir->dentry = NULL;
goto failed;
}
dir = securityfs_create_dir(fs_dir->name, parent);
if (IS_ERR(dir))
return PTR_ERR(dir);
fs_dir->dentry = dir;
for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) {
for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
if (fs_file->v_type == AA_FS_TYPE_DIR)
error = aafs_create_dir(fs_file, fs_dir->dentry);
else
@ -272,6 +881,8 @@ static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
return 0;
failed:
aafs_remove_dir(fs_dir);
return error;
}
@ -296,7 +907,7 @@ static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir)
{
struct aa_fs_entry *fs_file;
for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) {
for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
if (fs_file->v_type == AA_FS_TYPE_DIR)
aafs_remove_dir(fs_file);
else
@ -340,6 +951,11 @@ static int __init aa_create_aafs(void)
if (error)
goto error;
error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry.dentry,
"policy");
if (error)
goto error;
/* TODO: add support for apparmorfs_null and apparmorfs_mnt */
/* Report that AppArmor fs is enabled */

5
security/apparmor/capability.c

@ -27,6 +27,11 @@
*/
#include "capability_names.h"
struct aa_fs_entry aa_fs_entry_caps[] = {
AA_FS_FILE_STRING("mask", AA_FS_CAPS_MASK),
{ }
};
struct audit_cache {
struct aa_profile *profile;
kernel_cap_t caps;

16
security/apparmor/context.c

@ -112,9 +112,9 @@ int aa_replace_current_profile(struct aa_profile *profile)
aa_clear_task_cxt_trans(cxt);
/* be careful switching cxt->profile, when racing replacement it
* is possible that cxt->profile->replacedby is the reference keeping
* @profile valid, so make sure to get its reference before dropping
* the reference on cxt->profile */
* is possible that cxt->profile->replacedby->profile is the reference
* keeping @profile valid, so make sure to get its reference before
* dropping the reference on cxt->profile */
aa_get_profile(profile);
aa_put_profile(cxt->profile);
cxt->profile = profile;
@ -175,7 +175,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token)
abort_creds(new);
return -EACCES;
}
cxt->profile = aa_get_profile(aa_newest_version(profile));
cxt->profile = aa_get_newest_profile(profile);
/* clear exec on switching context */
aa_put_profile(cxt->onexec);
cxt->onexec = NULL;
@ -212,14 +212,8 @@ int aa_restore_previous_profile(u64 token)
}
aa_put_profile(cxt->profile);
cxt->profile = aa_newest_version(cxt->previous);
cxt->profile = aa_get_newest_profile(cxt->previous);
BUG_ON(!cxt->profile);
if (unlikely(cxt->profile != cxt->previous)) {
aa_get_profile(cxt->profile);
aa_put_profile(cxt->previous);
}
/* ref has been transfered so avoid putting ref in clear_task_cxt */
cxt->previous = NULL;
/* clear exec && prev information when restoring to previous context */
aa_clear_task_cxt_trans(cxt);

97
security/apparmor/crypto.c

@ -0,0 +1,97 @@
/*
* AppArmor security module
*
* This file contains AppArmor policy loading interface function definitions.
*
* Copyright 2013 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.
*
* Fns to provide a checksum of policy that has been loaded this can be
* compared to userspace policy compiles to check loaded policy is what
* it should be.
*/
#include <linux/crypto.h>
#include "include/apparmor.h"
#include "include/crypto.h"
static unsigned int apparmor_hash_size;
static struct crypto_hash *apparmor_tfm;
unsigned int aa_hash_size(void)
{
return apparmor_hash_size;
}
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
size_t len)
{
struct scatterlist sg[2];
struct hash_desc desc = {
.tfm = apparmor_tfm,
.flags = 0
};
int error = -ENOMEM;
u32 le32_version = cpu_to_le32(version);
if (!apparmor_tfm)
return 0;
sg_init_table(sg, 2);
sg_set_buf(&sg[0], &le32_version, 4);
sg_set_buf(&sg[1], (u8 *) start, len);
profile->hash = kzalloc(apparmor_hash_size, GFP_KERNEL);
if (!profile->hash)
goto fail;
error = crypto_hash_init(&desc);
if (error)
goto fail;
error = crypto_hash_update(&desc, &sg[0], 4);
if (error)
goto fail;
error = crypto_hash_update(&desc, &sg[1], len);
if (error)
goto fail;
error = crypto_hash_final(&desc, profile->hash);
if (error)
goto fail;
return 0;
fail:
kfree(profile->hash);
profile->hash = NULL;
return error;
}
static int __init init_profile_hash(void)
{
struct crypto_hash *tfm;
if (!apparmor_initialized)
return 0;
tfm = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm)) {
int error = PTR_ERR(tfm);
AA_ERROR("failed to setup profile sha1 hashing: %d\n", error);
return error;
}
apparmor_tfm = tfm;
apparmor_hash_size = crypto_hash_digestsize(apparmor_tfm);
aa_info_message("AppArmor sha1 policy hashing enabled");
return 0;
}
late_initcall(init_profile_hash);

24
security/apparmor/domain.c

@ -144,7 +144,7 @@ static struct aa_profile *__attach_match(const char *name,
int len = 0;
struct aa_profile *profile, *candidate = NULL;
list_for_each_entry(profile, head, base.list) {
list_for_each_entry_rcu(profile, head, base.list) {
if (profile->flags & PFLAG_NULL)
continue;
if (profile->xmatch && profile->xmatch_len > len) {
@ -177,9 +177,9 @@ static struct aa_profile *find_attach(struct aa_namespace *ns,
{
struct aa_profile *profile;
read_lock(&ns->lock);
rcu_read_lock();
profile = aa_get_profile(__attach_match(name, list));
read_unlock(&ns->lock);
rcu_read_unlock();
return profile;
}
@ -359,7 +359,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
cxt = cred_cxt(bprm->cred);
BUG_ON(!cxt);
profile = aa_get_profile(aa_newest_version(cxt->profile));
profile = aa_get_newest_profile(cxt->profile);
/*
* get the namespace from the replacement profile as replacement
* can change the namespace
@ -371,8 +371,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer,
&name, &info);
if (error) {
if (profile->flags &
(PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED))
if (unconfined(profile) ||
(profile->flags & PFLAG_IX_ON_NAME_ERROR))
error = 0;
name = bprm->filename;
goto audit;
@ -417,7 +417,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
if (!(cp.allow & AA_MAY_ONEXEC))
goto audit;
new_profile = aa_get_profile(aa_newest_version(cxt->onexec));
new_profile = aa_get_newest_profile(cxt->onexec);
goto apply;
}
@ -434,7 +434,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
new_profile = aa_get_profile(profile);
goto x_clear;
} else if (perms.xindex & AA_X_UNCONFINED) {
new_profile = aa_get_profile(ns->unconfined);
new_profile = aa_get_newest_profile(ns->unconfined);
info = "ux fallback";
} else {
error = -ENOENT;
@ -641,7 +641,10 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
if (count) {
/* attempting to change into a new hat or switch to a sibling */
struct aa_profile *root;
root = PROFILE_IS_HAT(profile) ? profile->parent : profile;
if (PROFILE_IS_HAT(profile))
root = aa_get_profile_rcu(&profile->parent);
else
root = aa_get_profile(profile);
/* find first matching hat */
for (i = 0; i < count && !hat; i++)
@ -653,6 +656,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
error = -ECHILD;
else
error = -ENOENT;
aa_put_profile(root);
goto out;
}
@ -667,6 +671,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
/* freed below */
name = new_compound_name(root->base.hname, hats[0]);
aa_put_profile(root);
target = name;
/* released below */
hat = aa_new_null_profile(profile, 1);
@ -676,6 +681,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
goto audit;
}
} else {
aa_put_profile(root);
target = hat->base.hname;
if (!PROFILE_IS_HAT(hat)) {
info = "target not hat";

6
security/apparmor/include/apparmor.h

@ -78,6 +78,12 @@ static inline void *kvzalloc(size_t size)
return __aa_kvmalloc(size, __GFP_ZERO);
}
/* returns 0 if kref not incremented */
static inline int kref_get_not0(struct kref *kref)
{
return atomic_inc_not_zero(&kref->refcount);
}
/**
* aa_strneq - compare null terminated @str to a non null terminated substring
* @str: a null terminated string

40
security/apparmor/include/apparmorfs.h

@ -61,4 +61,44 @@ extern const struct file_operations aa_fs_seq_file_ops;
extern void __init aa_destroy_aafs(void);
struct aa_profile;
struct aa_namespace;
enum aafs_ns_type {
AAFS_NS_DIR,
AAFS_NS_PROFS,
AAFS_NS_NS,
AAFS_NS_COUNT,
AAFS_NS_MAX_COUNT,
AAFS_NS_SIZE,
AAFS_NS_MAX_SIZE,
AAFS_NS_OWNER,
AAFS_NS_SIZEOF,
};
enum aafs_prof_type {
AAFS_PROF_DIR,
AAFS_PROF_PROFS,
AAFS_PROF_NAME,
AAFS_PROF_MODE,
AAFS_PROF_ATTACH,
AAFS_PROF_HASH,
AAFS_PROF_SIZEOF,
};
#define ns_dir(X) ((X)->dents[AAFS_NS_DIR])
#define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS])
#define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS])
#define prof_dir(X) ((X)->dents[AAFS_PROF_DIR])
#define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS])
void __aa_fs_profile_rmdir(struct aa_profile *profile);
void __aa_fs_profile_migrate_dents(struct aa_profile *old,
struct aa_profile *new);
int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
void __aa_fs_namespace_rmdir(struct aa_namespace *ns);
int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
const char *name);
#endif /* __AA_APPARMORFS_H */

1
security/apparmor/include/audit.h

@ -27,7 +27,6 @@ struct aa_profile;
extern const char *const audit_mode_names[];
#define AUDIT_MAX_INDEX 5
enum audit_mode {
AUDIT_NORMAL, /* follow normal auditing of accesses */
AUDIT_QUIET_DENIED, /* quiet all denied access messages */

4
security/apparmor/include/capability.h

@ -17,6 +17,8 @@
#include <linux/sched.h>
#include "apparmorfs.h"
struct aa_profile;
/* aa_caps - confinement data for capabilities
@ -34,6 +36,8 @@ struct aa_caps {
kernel_cap_t extended;
};
extern struct aa_fs_entry aa_fs_entry_caps[];
int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap,
int audit);

15
security/apparmor/include/context.h

@ -98,7 +98,7 @@ static inline struct aa_profile *aa_cred_profile(const struct cred *cred)
{
struct aa_task_cxt *cxt = cred_cxt(cred);
BUG_ON(!cxt || !cxt->profile);
return aa_newest_version(cxt->profile);
return cxt->profile;
}
/**
@ -152,15 +152,14 @@ static inline struct aa_profile *aa_current_profile(void)
struct aa_profile *profile;
BUG_ON(!cxt || !cxt->profile);
profile = aa_newest_version(cxt->profile);
/*
* Whether or not replacement succeeds, use newest profile so
* there is no need to update it after replacement.
*/
if (unlikely((cxt->profile != profile)))
if (PROFILE_INVALID(cxt->profile)) {
profile = aa_get_newest_profile(cxt->profile);
aa_replace_current_profile(profile);
aa_put_profile(profile);
cxt = current_cxt();
}
return profile;
return cxt->profile;
}
/**

36
security/apparmor/include/crypto.h

@ -0,0 +1,36 @@
/*
* AppArmor security module
*
* This file contains AppArmor policy loading interface function definitions.
*
* Copyright 2013 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 __APPARMOR_CRYPTO_H
#define __APPARMOR_CRYPTO_H
#include "policy.h"
#ifdef CONFIG_SECURITY_APPARMOR_HASH
unsigned int aa_hash_size(void);
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
size_t len);
#else
static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version,
void *start, size_t len)
{
return 0;
}
static inline unsigned int aa_hash_size(void)
{
return 0;
}
#endif
#endif /* __APPARMOR_CRYPTO_H */

218
security/apparmor/include/policy.h

@ -29,8 +29,8 @@
#include "file.h"
#include "resource.h"
extern const char *const profile_mode_names[];
#define APPARMOR_NAMES_MAX_INDEX 3
extern const char *const aa_profile_mode_names[];
#define APPARMOR_MODE_NAMES_MAX_INDEX 4
#define PROFILE_MODE(_profile, _mode) \
((aa_g_profile_mode == (_mode)) || \
@ -42,6 +42,10 @@ extern const char *const profile_mode_names[];
#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
#define PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID)
#define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2)
/*
* FIXME: currently need a clean way to replace and remove profiles as a
* set. It should be done at the namespace level.
@ -52,17 +56,19 @@ enum profile_mode {
APPARMOR_ENFORCE, /* enforce access rules */
APPARMOR_COMPLAIN, /* allow and log access violations */
APPARMOR_KILL, /* kill task on access violation */
APPARMOR_UNCONFINED, /* profile set to unconfined */
};
enum profile_flags {
PFLAG_HAT = 1, /* profile is a hat */
PFLAG_UNCONFINED = 2, /* profile is an unconfined profile */
PFLAG_NULL = 4, /* profile is null learning profile */
PFLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */
PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */
PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */
PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */
PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */
PFLAG_INVALID = 0x200, /* profile replaced/removed */
PFLAG_NS_COUNT = 0x400, /* carries NS ref count */
/* These flags must correspond with PATH_flags */
PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */
@ -73,14 +79,12 @@ struct aa_profile;
/* struct aa_policy - common part of both namespaces and profiles
* @name: name of the object
* @hname - The hierarchical name
* @count: reference count of the obj
* @list: list policy object is on
* @profiles: head of the profiles list contained in the object
*/
struct aa_policy {
char *name;
char *hname;
struct kref count;
struct list_head list;
struct list_head profiles;
};
@ -106,6 +110,8 @@ struct aa_ns_acct {
* @unconfined: special unconfined profile for the namespace
* @sub_ns: list of namespaces under the current namespace.
* @uniq_null: uniq value used for null learning profiles
* @uniq_id: a unique id count for the profiles in the namespace
* @dents: dentries for the namespaces file entries in apparmorfs
*
* An aa_namespace defines the set profiles that are searched to determine
* which profile to attach to a task. Profiles can not be shared between
@ -124,11 +130,14 @@ struct aa_ns_acct {
struct aa_namespace {
struct aa_policy base;
struct aa_namespace *parent;
rwlock_t lock;
struct mutex lock;
struct aa_ns_acct acct;
struct aa_profile *unconfined;
struct list_head sub_ns;
atomic_t uniq_null;
long uniq_id;
struct dentry *dents[AAFS_NS_SIZEOF];
};
/* struct aa_policydb - match engine for a policy
@ -142,12 +151,21 @@ struct aa_policydb {
};
struct aa_replacedby {
struct kref count;
struct aa_profile __rcu *profile;
};
/* struct aa_profile - basic confinement data
* @base - base components of the profile (name, refcount, lists, lock ...)
* @count: reference count of the obj
* @rcu: rcu head used when removing from @list
* @parent: parent of profile
* @ns: namespace the profile is in
* @replacedby: is set to the profile that replaced this profile
* @rename: optional profile name that this profile renamed
* @attach: human readable attachment string
* @xmatch: optional extended matching for unconfined executables names
* @xmatch_len: xmatch prefix len, used to determine xmatch priority
* @audit: the auditing mode of the profile
@ -160,13 +178,15 @@ struct aa_policydb {
* @caps: capabilities for the profile
* @rlimits: rlimits for the profile
*
* @dents: dentries for the profiles file entries in apparmorfs
* @dirname: name of the profile dir in apparmorfs
*
* The AppArmor profile contains the basic confinement data. Each profile
* has a name, and exists in a namespace. The @name and @exec_match are
* used to determine profile attachment against unconfined tasks. All other
* attachments are determined by profile X transition rules.
*
* The @replacedby field is write protected by the profile lock. Reads
* are assumed to be atomic, and are done without locking.
* The @replacedby struct is write protected by the profile lock.
*
* Profiles have a hierarchy where hats and children profiles keep
* a reference to their parent.
@ -177,17 +197,20 @@ struct aa_policydb {
*/
struct aa_profile {
struct aa_policy base;
struct aa_profile *parent;
struct kref count;
struct rcu_head rcu;
struct aa_profile __rcu *parent;
struct aa_namespace *ns;
struct aa_profile *replacedby;
struct aa_replacedby *replacedby;
const char *rename;
const char *attach;
struct aa_dfa *xmatch;
int xmatch_len;
enum audit_mode audit;
enum profile_mode mode;
u32 flags;
long mode;
long flags;
u32 path_flags;
int size;
@ -195,6 +218,10 @@ struct aa_profile {
struct aa_file_rules file;
struct aa_caps caps;
struct aa_rlimit rlimits;
unsigned char *hash;
char *dirname;
struct dentry *dents[AAFS_PROF_SIZEOF];
};
extern struct aa_namespace *root_ns;
@ -211,43 +238,11 @@ void aa_free_namespace_kref(struct kref *kref);
struct aa_namespace *aa_find_namespace(struct aa_namespace *root,
const char *name);
static inline struct aa_policy *aa_get_common(struct aa_policy *c)
{
if (c)
kref_get(&c->count);
return c;
}
/**
* aa_get_namespace - increment references count on @ns
* @ns: namespace to increment reference count of (MAYBE NULL)
*
* Returns: pointer to @ns, if @ns is NULL returns NULL
* Requires: @ns must be held with valid refcount when called
*/
static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns)
{
if (ns)
kref_get(&(ns->base.count));
return ns;
}
/**
* aa_put_namespace - decrement refcount on @ns
* @ns: namespace to put reference of
*
* Decrement reference count of @ns and if no longer in use free it
*/
static inline void aa_put_namespace(struct aa_namespace *ns)
{
if (ns)
kref_put(&ns->base.count, aa_free_namespace_kref);
}
void aa_free_replacedby_kref(struct kref *kref);
struct aa_profile *aa_alloc_profile(const char *name);
struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat);
void aa_free_profile(struct aa_profile *profile);
void aa_free_profile_kref(struct kref *kref);
struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name);
struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name);
@ -259,25 +254,13 @@ ssize_t aa_remove_profiles(char *name, size_t size);