forked from Minki/linux
apparmor: add the ability to report a sha1 hash of loaded policy
Provide userspace the ability to introspect a sha1 hash value for each profile currently loaded. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
This commit is contained in:
parent
84f1f78742
commit
f8eb8a1324
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "include/apparmorfs.h"
|
||||
#include "include/audit.h"
|
||||
#include "include/context.h"
|
||||
#include "include/crypto.h"
|
||||
#include "include/policy.h"
|
||||
#include "include/resource.h"
|
||||
|
||||
@ -319,6 +320,34 @@ static const struct file_operations aa_fs_profattach_fops = {
|
||||
.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)
|
||||
{
|
||||
@ -420,6 +449,14 @@ int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
|
||||
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)
|
||||
|
97
security/apparmor/crypto.c
Normal file
97
security/apparmor/crypto.c
Normal file
@ -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);
|
@ -82,6 +82,7 @@ enum aafs_prof_type {
|
||||
AAFS_PROF_NAME,
|
||||
AAFS_PROF_MODE,
|
||||
AAFS_PROF_ATTACH,
|
||||
AAFS_PROF_HASH,
|
||||
AAFS_PROF_SIZEOF,
|
||||
};
|
||||
|
||||
|
36
security/apparmor/include/crypto.h
Normal file
36
security/apparmor/include/crypto.h
Normal file
@ -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 */
|
@ -219,6 +219,7 @@ struct aa_profile {
|
||||
struct aa_caps caps;
|
||||
struct aa_rlimit rlimits;
|
||||
|
||||
unsigned char *hash;
|
||||
char *dirname;
|
||||
struct dentry *dents[AAFS_PROF_SIZEOF];
|
||||
};
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "include/apparmor.h"
|
||||
#include "include/audit.h"
|
||||
#include "include/context.h"
|
||||
#include "include/crypto.h"
|
||||
#include "include/match.h"
|
||||
#include "include/policy.h"
|
||||
#include "include/policy_unpack.h"
|
||||
@ -758,10 +759,12 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
|
||||
|
||||
*ns = NULL;
|
||||
while (e.pos < e.end) {
|
||||
void *start;
|
||||
error = verify_header(&e, e.pos == e.start, ns);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
start = e.pos;
|
||||
profile = unpack_profile(&e);
|
||||
if (IS_ERR(profile)) {
|
||||
error = PTR_ERR(profile);
|
||||
@ -769,16 +772,18 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
|
||||
}
|
||||
|
||||
error = verify_profile(profile);
|
||||
if (error) {
|
||||
aa_free_profile(profile);
|
||||
goto fail;
|
||||
}
|
||||
if (error)
|
||||
goto fail_profile;
|
||||
|
||||
error = aa_calc_profile_hash(profile, e.version, start,
|
||||
e.pos - start);
|
||||
if (error)
|
||||
goto fail_profile;
|
||||
|
||||
ent = aa_load_ent_alloc();
|
||||
if (!ent) {
|
||||
error = -ENOMEM;
|
||||
aa_put_profile(profile);
|
||||
goto fail;
|
||||
goto fail_profile;
|
||||
}
|
||||
|
||||
ent->new = profile;
|
||||
@ -787,6 +792,9 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
|
||||
|
||||
return 0;
|
||||
|
||||
fail_profile:
|
||||
aa_put_profile(profile);
|
||||
|
||||
fail:
|
||||
list_for_each_entry_safe(ent, tmp, lh, list) {
|
||||
list_del_init(&ent->list);
|
||||
|
Loading…
Reference in New Issue
Block a user