From dd51c84857630e77c139afe4d9bba65fc051dc3f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Jul 2013 21:05:43 -0700 Subject: apparmor: provide base for multiple profiles to be replaced at once previously profiles had to be loaded one at a time, which could result in cases where a replacement of a set would partially succeed, and then fail resulting in inconsistent policy. Allow multiple profiles to replaced "atomically" so that the replacement either succeeds or fails for the entire set of profiles. Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 114 ++++++++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 28 deletions(-) (limited to 'security/apparmor/policy_unpack.c') diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 6dac7d77cb4..080a26b11f0 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -333,8 +333,10 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) /* * The dfa is aligned with in the blob to 8 bytes * from the beginning of the stream. + * alignment adjust needed by dfa unpack */ - size_t sz = blob - (char *)e->start; + size_t sz = blob - (char *) e->start - + ((e->pos - e->start) & 7); size_t pad = ALIGN(sz, 8) - sz; int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | TO_ACCEPT2_FLAG(YYTD_DATA32); @@ -622,29 +624,41 @@ fail: /** * verify_head - unpack serialized stream header * @e: serialized data read head (NOT NULL) + * @required: whether the header is required or optional * @ns: Returns - namespace if one is specified else NULL (NOT NULL) * * Returns: error or 0 if header is good */ -static int verify_header(struct aa_ext *e, const char **ns) +static int verify_header(struct aa_ext *e, int required, const char **ns) { int error = -EPROTONOSUPPORT; + const char *name = NULL; + *ns = NULL; + /* get the interface version */ if (!unpack_u32(e, &e->version, "version")) { - audit_iface(NULL, NULL, "invalid profile format", e, error); - return error; - } + if (required) { + audit_iface(NULL, NULL, "invalid profile format", e, + error); + return error; + } - /* check that the interface version is currently supported */ - if (e->version != 5) { - audit_iface(NULL, NULL, "unsupported interface version", e, - error); - return error; + /* check that the interface version is currently supported */ + if (e->version != 5) { + audit_iface(NULL, NULL, "unsupported interface version", + e, error); + return error; + } } + /* read the namespace if present */ - if (!unpack_str(e, ns, "namespace")) - *ns = NULL; + if (unpack_str(e, &name, "namespace")) { + if (*ns && strcmp(*ns, name)) + audit_iface(NULL, NULL, "invalid ns change", e, error); + else if (!*ns) + *ns = name; + } return 0; } @@ -693,18 +707,40 @@ static int verify_profile(struct aa_profile *profile) return 0; } +void aa_load_ent_free(struct aa_load_ent *ent) +{ + if (ent) { + aa_put_profile(ent->rename); + aa_put_profile(ent->old); + aa_put_profile(ent->new); + kzfree(ent); + } +} + +struct aa_load_ent *aa_load_ent_alloc(void) +{ + struct aa_load_ent *ent = kzalloc(sizeof(*ent), GFP_KERNEL); + if (ent) + INIT_LIST_HEAD(&ent->list); + return ent; +} + /** - * aa_unpack - unpack packed binary profile 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) * @size: the size of the user data + * @lh: list to place unpacked profiles in a aa_repl_ws * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) * - * Unpack user data and return refcounted allocated profile or ERR_PTR + * Unpack user data and return refcounted allocated profile(s) stored in + * @lh in order of discovery, with the list chain stored in base.list + * or error * - * Returns: profile else error pointer if fails to unpack + * Returns: profile(s) on @lh else error pointer if fails to unpack */ -struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) +int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) { + struct aa_load_ent *tmp, *ent; struct aa_profile *profile = NULL; int error; struct aa_ext e = { @@ -713,20 +749,42 @@ struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) .pos = udata, }; - error = verify_header(&e, ns); - if (error) - return ERR_PTR(error); + *ns = NULL; + while (e.pos < e.end) { + error = verify_header(&e, e.pos == e.start, ns); + if (error) + goto fail; - profile = unpack_profile(&e); - if (IS_ERR(profile)) - return profile; + profile = unpack_profile(&e); + if (IS_ERR(profile)) { + error = PTR_ERR(profile); + goto fail; + } + + error = verify_profile(profile); + if (error) { + aa_put_profile(profile); + goto fail; + } + + ent = aa_load_ent_alloc(); + if (!ent) { + error = -ENOMEM; + aa_put_profile(profile); + goto fail; + } - error = verify_profile(profile); - if (error) { - aa_put_profile(profile); - profile = ERR_PTR(error); + ent->new = profile; + list_add_tail(&ent->list, lh); } - /* return refcount */ - return profile; + return 0; + +fail: + list_for_each_entry_safe(ent, tmp, lh, list) { + list_del_init(&ent->list); + aa_load_ent_free(ent); + } + + return error; } -- cgit v1.2.3-70-g09d2 From 8651e1d6572bc2c061073f05fabcd7175789259d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Jul 2013 21:11:43 -0700 Subject: apparmor: make free_profile available outside of policy.c Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 1 + security/apparmor/policy.c | 9 ++++----- security/apparmor/policy_unpack.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'security/apparmor/policy_unpack.c') diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 4eafdd88f44..8a68226ff7f 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -228,6 +228,7 @@ struct aa_namespace *aa_find_namespace(struct aa_namespace *root, 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); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index aee2e71827c..7a80b0c7e0c 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -307,7 +307,6 @@ fail_ns: return NULL; } -static void free_profile(struct aa_profile *profile); /** * free_namespace - free a profile namespace * @ns: the namespace to free (MAYBE NULL) @@ -324,7 +323,7 @@ static void free_namespace(struct aa_namespace *ns) aa_put_namespace(ns->parent); ns->unconfined->ns = NULL; - free_profile(ns->unconfined); + aa_free_profile(ns->unconfined); kzfree(ns); } @@ -568,7 +567,7 @@ void aa_free_replacedby_kref(struct kref *kref) } /** - * free_profile - free a profile + * aa_free_profile - free a profile * @profile: the profile to free (MAYBE NULL) * * Free a profile, its hats and null_profile. All references to the profile, @@ -577,7 +576,7 @@ void aa_free_replacedby_kref(struct kref *kref) * If the profile was referenced from a task context, free_profile() will * be called from an rcu callback routine, so we must not sleep here. */ -static void free_profile(struct aa_profile *profile) +void aa_free_profile(struct aa_profile *profile) { AA_DEBUG("%s(%p)\n", __func__, profile); @@ -612,7 +611,7 @@ static void aa_free_profile_rcu(struct rcu_head *head) if (p->flags & PFLAG_NS_COUNT) free_namespace(p->ns); else - free_profile(p); + aa_free_profile(p); } /** diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 080a26b11f0..ce15313896e 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -616,7 +616,7 @@ fail: else if (!name) name = "unknown"; audit_iface(profile, name, "failed to unpack profile", e, error); - aa_put_profile(profile); + aa_free_profile(profile); return ERR_PTR(error); } @@ -763,7 +763,7 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) error = verify_profile(profile); if (error) { - aa_put_profile(profile); + aa_free_profile(profile); goto fail; } -- cgit v1.2.3-70-g09d2 From 038165070aa55375d4bdd2f84b34a486feca63d6 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Jul 2013 21:12:43 -0700 Subject: apparmor: allow setting any profile into the unconfined state Allow emulating the default profile behavior from boot, by allowing loading of a profile in the unconfined state into a new NS. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/domain.c | 4 ++-- security/apparmor/include/policy.h | 6 +++--- security/apparmor/include/policy_unpack.h | 7 +++++++ security/apparmor/policy.c | 6 ++++-- security/apparmor/policy_unpack.c | 8 ++++++-- 5 files changed, 22 insertions(+), 9 deletions(-) (limited to 'security/apparmor/policy_unpack.c') diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index bc28f2670ee..26c607c971f 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -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; diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 8a68226ff7f..65662e3c75c 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -56,11 +56,11 @@ 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 */ @@ -199,7 +199,7 @@ struct aa_profile { struct aa_dfa *xmatch; int xmatch_len; enum audit_mode audit; - enum profile_mode mode; + long mode; long flags; u32 path_flags; int size; @@ -240,7 +240,7 @@ ssize_t aa_remove_profiles(char *name, size_t size); #define PROF_ADD 1 #define PROF_REPLACE 0 -#define unconfined(X) ((X)->flags & PFLAG_UNCONFINED) +#define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) /** diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index 0d7ad722b8f..c214fb88b1b 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -27,6 +27,13 @@ struct aa_load_ent { void aa_load_ent_free(struct aa_load_ent *ent); struct aa_load_ent *aa_load_ent_alloc(void); +#define PACKED_FLAG_HAT 1 + +#define PACKED_MODE_ENFORCE 0 +#define PACKED_MODE_COMPLAIN 1 +#define PACKED_MODE_KILL 2 +#define PACKED_MODE_UNCONFINED 3 + int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns); #endif /* __POLICY_INTERFACE_H */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 7a80b0c7e0c..2e4e2ecb25b 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -96,6 +96,7 @@ const char *const profile_mode_names[] = { "enforce", "complain", "kill", + "unconfined", }; /** @@ -290,8 +291,9 @@ static struct aa_namespace *alloc_namespace(const char *prefix, if (!ns->unconfined) goto fail_unconfined; - ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | - PFLAG_IMMUTABLE | PFLAG_NS_COUNT; + ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR | + PFLAG_IMMUTABLE | PFLAG_NS_COUNT; + ns->unconfined->mode = APPARMOR_UNCONFINED; /* ns and ns->unconfined share ns->unconfined refcount */ ns->unconfined->ns = ns; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index ce15313896e..cac0aa07578 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -511,12 +511,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) goto fail; if (!unpack_u32(e, &tmp, NULL)) goto fail; - if (tmp) + if (tmp & PACKED_FLAG_HAT) profile->flags |= PFLAG_HAT; if (!unpack_u32(e, &tmp, NULL)) goto fail; - if (tmp) + if (tmp == PACKED_MODE_COMPLAIN) profile->mode = APPARMOR_COMPLAIN; + else if (tmp == PACKED_MODE_KILL) + profile->mode = APPARMOR_KILL; + else if (tmp == PACKED_MODE_UNCONFINED) + profile->mode = APPARMOR_UNCONFINED; if (!unpack_u32(e, &tmp, NULL)) goto fail; if (tmp) -- cgit v1.2.3-70-g09d2 From 556d0be74b19cb6288e5eb2f3216eac247d87968 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Jul 2013 21:17:43 -0700 Subject: apparmor: add an optional profile attachment string for profiles Add the ability to take in and report a human readable profile attachment string for profiles so that attachment specifications can be easily inspected. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/apparmorfs.c | 34 ++++++++++++++++++++++++++++++++++ security/apparmor/include/apparmorfs.h | 1 + security/apparmor/include/policy.h | 2 ++ security/apparmor/policy_unpack.c | 3 +++ 4 files changed, 40 insertions(+) (limited to 'security/apparmor/policy_unpack.c') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0fdd08c6ea5..d6329aa7aa9 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -290,6 +290,34 @@ static const struct file_operations aa_fs_profmode_fops = { .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, "\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, +}; + /** fns to setup dynamic per profile/namespace files **/ void __aa_fs_profile_rmdir(struct aa_profile *profile) { @@ -385,6 +413,12 @@ int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) 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; + list_for_each_entry(child, &profile->base.profiles, base.list) { error = __aa_fs_profile_mkdir(child, prof_child_dir(profile)); if (error) diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 2494e112f2b..f91712cf1b3 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -81,6 +81,7 @@ enum aafs_prof_type { AAFS_PROF_PROFS, AAFS_PROF_NAME, AAFS_PROF_MODE, + AAFS_PROF_ATTACH, AAFS_PROF_SIZEOF, }; diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 5c72231d1c4..59b36372ae4 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -165,6 +165,7 @@ struct aa_replacedby { * @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 @@ -204,6 +205,7 @@ struct aa_profile { struct aa_replacedby *replacedby; const char *rename; + const char *attach; struct aa_dfa *xmatch; int xmatch_len; enum audit_mode audit; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index cac0aa07578..bdaef2e1b2a 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -492,6 +492,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) /* profile renaming is optional */ (void) unpack_str(e, &profile->rename, "rename"); + /* attachment string is optional */ + (void) unpack_str(e, &profile->attach, "attach"); + /* xmatch is optional and may be NULL */ profile->xmatch = unpack_dfa(e); if (IS_ERR(profile->xmatch)) { -- cgit v1.2.3-70-g09d2 From f8eb8a1324e81927b2c64823b2fc38386efd3fef Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 14 Aug 2013 11:27:36 -0700 Subject: 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 Acked-by: Seth Arnold --- security/apparmor/Kconfig | 12 +++++ security/apparmor/Makefile | 1 + security/apparmor/apparmorfs.c | 37 +++++++++++++ security/apparmor/crypto.c | 97 ++++++++++++++++++++++++++++++++++ security/apparmor/include/apparmorfs.h | 1 + security/apparmor/include/crypto.h | 36 +++++++++++++ security/apparmor/include/policy.h | 1 + security/apparmor/policy_unpack.c | 20 ++++--- 8 files changed, 199 insertions(+), 6 deletions(-) create mode 100644 security/apparmor/crypto.c create mode 100644 security/apparmor/include/crypto.h (limited to 'security/apparmor/policy_unpack.c') diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b2e32..d49c53960b6 100644 --- a/security/apparmor/Kconfig +++ b/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. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 0831e049072..d693df87481 100644 --- a/security/apparmor/Makefile +++ b/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 diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index d708a55d072..95c2b2689a0 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -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) diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c new file mode 100644 index 00000000000..d6222ba4e91 --- /dev/null +++ b/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 + +#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); diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index f91712cf1b3..414e56878dd 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -82,6 +82,7 @@ enum aafs_prof_type { AAFS_PROF_NAME, AAFS_PROF_MODE, AAFS_PROF_ATTACH, + AAFS_PROF_HASH, AAFS_PROF_SIZEOF, }; diff --git a/security/apparmor/include/crypto.h b/security/apparmor/include/crypto.h new file mode 100644 index 00000000000..dc418e5024d --- /dev/null +++ b/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 */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 59b36372ae4..f2d4b6348cb 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.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]; }; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index bdaef2e1b2a..a689f10930b 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -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); -- cgit v1.2.3-70-g09d2