X-Git-Url: http://git.pld-linux.org/?a=blobdiff_plain;f=kernel-apparmor.patch;h=cab89b93e18841fa854adb58e78685ce84e3aaf7;hb=refs%2Ftags%2Fauto%2Fth%2Fkernel-3.14-3.14.26-1;hp=1a5695e22419961a9095f2d075eea48136e1ac4d;hpb=9474138df4dd4ed162b1f9e80ee329f6dda92d2c;p=packages%2Fkernel.git diff --git a/kernel-apparmor.patch b/kernel-apparmor.patch index 1a5695e2..cab89b93 100644 --- a/kernel-apparmor.patch +++ b/kernel-apparmor.patch @@ -1,165 +1,154 @@ -diff --git a/include/linux/audit.h b/include/linux/audit.h -index 4fa2810..9f87073 100644 ---- a/include/linux/audit.h -+++ b/include/linux/audit.h -@@ -33,7 +33,7 @@ - * 1200 - 1299 messages internal to the audit daemon - * 1300 - 1399 audit event messages - * 1400 - 1499 SE Linux use -- * 1500 - 1599 kernel LSPP events -+ * 1500 - 1599 AppArmor use - * 1600 - 1699 kernel crypto events - * 1700 - 1799 kernel anomaly records - * 1800 - 1899 kernel integrity events -@@ -122,6 +122,14 @@ - #define AUDIT_MAC_UNLBL_STCADD 1416 /* NetLabel: add a static label */ - #define AUDIT_MAC_UNLBL_STCDEL 1417 /* NetLabel: del a static label */ +From d29d73fa5d7b5d016f9c17236fff2a741acea247 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Mon, 4 Oct 2010 15:03:36 -0700 +Subject: [PATCH 1/3] UBUNTU: SAUCE: AppArmor: basic networking rules + +Base support for network mediation. + +Signed-off-by: John Johansen + +Conflicts: + security/apparmor/Makefile + security/apparmor/policy.c +--- + security/apparmor/.gitignore | 1 + + security/apparmor/Makefile | 42 +++++++++- + security/apparmor/apparmorfs.c | 1 + + security/apparmor/include/audit.h | 4 + + security/apparmor/include/net.h | 44 ++++++++++ + security/apparmor/include/policy.h | 3 + + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ + security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ + security/apparmor/policy.c | 1 + + security/apparmor/policy_unpack.c | 46 +++++++++++ + 10 files changed, 414 insertions(+), 2 deletions(-) + create mode 100644 security/apparmor/include/net.h + create mode 100644 security/apparmor/net.c + +diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore +index 9cdec70..d5b291e 100644 +--- a/security/apparmor/.gitignore ++++ b/security/apparmor/.gitignore +@@ -1,5 +1,6 @@ + # + # Generated include files + # ++net_names.h + capability_names.h + rlim_names.h +diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile +index d693df8..5dbb72f 100644 +--- a/security/apparmor/Makefile ++++ b/security/apparmor/Makefile +@@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o -+#define AUDIT_APPARMOR_AUDIT 1501 /* AppArmor audited grants */ -+#define AUDIT_APPARMOR_ALLOWED 1502 /* Allowed Access for learning */ -+#define AUDIT_APPARMOR_DENIED 1503 -+#define AUDIT_APPARMOR_HINT 1504 /* Process Tracking information */ -+#define AUDIT_APPARMOR_STATUS 1505 /* Changes in config */ -+#define AUDIT_APPARMOR_ERROR 1506 /* Internal AppArmor Errors */ -+#define AUDIT_APPARMOR_KILL 1507 /* AppArmor killing processes */ -+ - #define AUDIT_FIRST_KERN_ANOM_MSG 1700 - #define AUDIT_LAST_KERN_ANOM_MSG 1799 - #define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */ -diff --git a/security/Kconfig b/security/Kconfig -index bb24477..f3db74c 100644 ---- a/security/Kconfig -+++ b/security/Kconfig -@@ -136,6 +136,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR - source security/selinux/Kconfig - source security/smack/Kconfig - source security/tomoyo/Kconfig -+source security/apparmor/Kconfig + 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 ++ resource.o sid.o file.o net.o + apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o - source security/integrity/ima/Kconfig +-clean-files := capability_names.h rlim_names.h ++clean-files := capability_names.h rlim_names.h net_names.h -diff --git a/security/Makefile b/security/Makefile -index fa77021..60aa7c5 100644 ---- a/security/Makefile -+++ b/security/Makefile -@@ -6,6 +6,7 @@ obj-$(CONFIG_KEYS) += keys/ - subdir-$(CONFIG_SECURITY_SELINUX) += selinux - subdir-$(CONFIG_SECURITY_SMACK) += smack - subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo -+subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor - # always enable default capabilities - obj-y += commoncap.o -@@ -17,6 +18,7 @@ obj-$(CONFIG_SECURITYFS) += inode.o - obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o - obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o - obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o -+obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o - obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o - obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o + # Build a lower case string table of capability names +@@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ + -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ + tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ -diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig -new file mode 100644 -index 0000000..0f7ba5e ---- /dev/null -+++ b/security/apparmor/Kconfig -@@ -0,0 +1,53 @@ -+config SECURITY_APPARMOR -+ bool "AppArmor support" -+ depends on SECURITY && SECURITY_NETWORK && NET && INET -+ select AUDIT -+ select SECURITY_PATH -+ select SECURITYFS -+ default n -+ help -+ This enables the AppArmor security module. -+ Required userspace tools (if they are not included in your -+ distribution) and further information may be found at -+ -+ -+ If you are unsure how to answer this question, answer N. -+ -+config SECURITY_APPARMOR_NETWORK -+ bool "AppArmor network support" -+ depends on SECURITY_APPARMOR -+ default n -+ help -+ This enables AppArmor to mediate applications network use. -+ This will enable the SECURITY_NETWORK hooks. -+ -+config SECURITY_APPARMOR_BOOTPARAM_VALUE -+ int "AppArmor boot parameter default value" -+ depends on SECURITY_APPARMOR -+ range 0 1 -+ default 1 -+ help -+ This option sets the default value for the kernel parameter -+ 'apparmor', which allows AppArmor to be enabled or disabled -+ at boot. If this option is set to 0 (zero), the AppArmor -+ kernel parameter will default to 0, disabling AppArmor at -+ bootup. If this option is set to 1 (one), the AppArmor -+ kernel parameter will default to 1, enabling AppArmor at -+ bootup. -+ -+ If you are unsure how to answer this question, answer 1. -+ -+config SECURITY_APPARMOR_DISABLE -+ bool "AppArmor runtime disable" -+ depends on SECURITY_APPARMOR -+ default n -+ help -+ This option enables writing to a apparmorfs node 'disable', which -+ allows AppArmor to be disabled at runtime prior to the policy load. -+ AppArmor will then remain disabled until the next boot. -+ This option is similar to the apparmor.enabled=0 boot parameter, -+ but is to support runtime disabling of AppArmor, e.g. from -+ /sbin/init, for portability across platforms where boot -+ parameters are difficult to employ. -+ -+ If you are unsure how to answer this question, answer N. -diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile -new file mode 100644 -index 0000000..6e186ce ---- /dev/null -+++ b/security/apparmor/Makefile -@@ -0,0 +1,24 @@ -+# Makefile for AppArmor Linux Security Module ++# Build a lower case string table of address family names ++# Transform lines from ++# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ ++# #define AF_INET 2 /* Internet IP Protocol */ ++# to ++# [1] = "local", ++# [2] = "inet", +# -+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_interface.o procattr.o lsm.o \ -+ resource.o sid.o file.o -+ -+apparmor-$(CONFIG_SECURITY_APPARMOR_NETWORK) += net.o -+ -+clean-files: capability_names.h af_names.h -+ -+quiet_cmd_make-caps = GEN $@ -+cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ; sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ -+ ++# and build the securityfs entries for the mapping. ++# Transforms lines from ++# #define AF_INET 2 /* Internet IP Protocol */ ++# to ++# #define AA_FS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ -+cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ; sed -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "s/^\#define[ \\t]\\+AF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ -+ -+$(obj)/capability.o : $(obj)/capability_names.h -+$(obj)/net.o : $(obj)/af_names.h -+$(obj)/capability_names.h : $(srctree)/include/linux/capability.h -+ $(call cmd,make-caps) -+$(obj)/af_names.h : $(srctree)/include/linux/socket.h ++cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ ++ sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ ++ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ ++ echo "};" >> $@ ;\ ++ echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ ++ sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ ++ $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ ++ ++# Build a lower case string table of sock type names ++# Transform lines from ++# SOCK_STREAM = 1, ++# to ++# [1] = "stream", ++quiet_cmd_make-sock = GEN $@ ++cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ ++ sed $^ >>$@ -r -n \ ++ -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ ++ echo "};" >> $@ + + # Build a lower case string table of rlimit names. + # Transforms lines from +@@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ + tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + + $(obj)/capability.o : $(obj)/capability_names.h ++$(obj)/net.o : $(obj)/net_names.h + $(obj)/resource.o : $(obj)/rlim_names.h + $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ + $(src)/Makefile +@@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ + $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ + $(src)/Makefile + $(call cmd,make-rlim) ++$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ ++ $(srctree)/include/linux/net.h \ ++ $(src)/Makefile + $(call cmd,make-af) ++ $(call cmd,make-sock) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 7db9954..18fc02c 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -806,6 +806,7 @@ 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_DIR("network", aa_fs_entry_network), + 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), +diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h +index 30e8d76..61abec5 100644 +--- a/security/apparmor/include/audit.h ++++ b/security/apparmor/include/audit.h +@@ -126,6 +126,10 @@ struct apparmor_audit_data { + u32 denied; + kuid_t ouid; + } fs; ++ struct { ++ int type, protocol; ++ struct sock *sk; ++ } net; + }; + }; + +diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 -index 0000000..02ba36f +index 0000000..cb8a121 --- /dev/null -+++ b/security/apparmor/apparmorfs.c -@@ -0,0 +1,395 @@ ++++ b/security/apparmor/include/net.h +@@ -0,0 +1,44 @@ +/* + * AppArmor security module + * -+ * This file contains AppArmor /proc//attr interface functions ++ * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. ++ * Copyright 2009-2012 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 @@ -167,400 +156,214 @@ index 0000000..02ba36f + * License. + */ + -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "include/apparmor.h" -+#include "include/audit.h" -+#include "include/context.h" -+#include "include/policy.h" -+#include "include/policy_interface.h" -+ -+static char *aa_simple_write_to_buffer(const char __user *userbuf, -+ size_t alloc_size, size_t copy_size, -+ loff_t *pos, const char *operation) -+{ -+ const struct cred *cred; -+ struct aa_profile *profile; -+ char *data; -+ -+ if (*pos != 0) { -+ /* only writes from pos 0, that is complete writes */ -+ data = ERR_PTR(-ESPIPE); -+ goto out; -+ } -+ -+ /* -+ * Don't allow confined processes to load/replace/remove profiles. -+ * No sane person would add rules allowing this to a profile -+ * but we enforce the restriction anyways. -+ */ -+ cred = aa_current_policy(&profile); -+ if (profile) { -+ struct aa_audit sa; -+ memset(&sa, 0, sizeof(sa)); -+ sa.operation = operation; -+ sa.gfp_mask = GFP_KERNEL; -+ sa.error = -EACCES; -+ data = ERR_PTR(aa_audit(AUDIT_APPARMOR_DENIED, profile, &sa, -+ NULL)); -+ goto out; -+ } -+ -+ data = vmalloc(alloc_size); -+ if (data == NULL) { -+ data = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ if (copy_from_user(data, userbuf, copy_size)) { -+ vfree(data); -+ data = ERR_PTR(-EFAULT); -+ goto out; -+ } ++#ifndef __AA_NET_H ++#define __AA_NET_H + -+out: -+ return data; -+} ++#include + -+static struct aa_profile *next_profile(struct aa_profile *profile) -+{ -+ struct aa_profile *next = profile; -+ struct aa_namespace *ns; ++#include "apparmorfs.h" + -+ if (!list_empty(&profile->base.profiles)) { -+ list_for_each_entry(next, &profile->base.profiles, base.list) -+ return next; -+ } ++/* struct aa_net - network confinement data ++ * @allowed: basic network families permissions ++ * @audit_network: which network permissions to force audit ++ * @quiet_network: which network permissions to quiet rejects ++ */ ++struct aa_net { ++ u16 allow[AF_MAX]; ++ u16 audit[AF_MAX]; ++ u16 quiet[AF_MAX]; ++}; + -+ while (profile->parent) { -+ next = profile->parent; -+ list_for_each_entry_continue(next, -+ &profile->parent->base.profiles, -+ base.list) -+ return next; -+ profile = profile->parent; -+ } ++extern struct aa_fs_entry aa_fs_entry_network[]; + -+ next = profile; -+ list_for_each_entry_continue(next, &profile->ns->base.profiles, -+ base.list) -+ return next; -+ -+ ns = profile->ns; -+ read_unlock(&ns->base.lock); -+ list_for_each_entry_continue(ns, &ns_list, base.list) { -+ read_lock(&ns->base.lock); -+ list_for_each_entry(profile, &ns->base.profiles, base.list) -+ return profile; -+ read_unlock(&ns->base.lock); -+ } -+ return NULL; -+} ++extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, ++ int type, int protocol, struct sock *sk); ++extern int aa_revalidate_sk(int op, struct sock *sk); + -+static void *p_start(struct seq_file *f, loff_t *pos) -+ __acquires(ns_list_lock) ++static inline void aa_free_net_rules(struct aa_net *new) +{ -+ struct aa_namespace *ns; -+ loff_t l = *pos; -+ -+ read_lock(&ns_list_lock); -+ if (!list_empty(&ns_list)) { -+ struct aa_profile *profile = NULL; -+ ns = list_first_entry(&ns_list, typeof(*ns), base.list); -+ read_lock(&ns->base.lock); -+ if (!list_empty(&ns->base.profiles)) { -+ profile = list_first_entry(&ns->base.profiles, -+ typeof(*profile), base.list); -+ for ( ; profile && l > 0; l--) -+ profile = next_profile(profile); -+ return profile; -+ } else -+ read_unlock(&ns->base.lock); -+ } -+ return NULL; ++ /* NOP */ +} + -+static void *p_next(struct seq_file *f, void *p, loff_t *pos) ++#endif /* __AA_NET_H */ +diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h +index c28b0f2..b524d88 100644 +--- a/security/apparmor/include/policy.h ++++ b/security/apparmor/include/policy.h +@@ -27,6 +27,7 @@ + #include "capability.h" + #include "domain.h" + #include "file.h" ++#include "net.h" + #include "resource.h" + + extern const char *const aa_profile_mode_names[]; +@@ -176,6 +177,7 @@ struct aa_replacedby { + * @policy: general match rules governing policy + * @file: The set of rules governing basic file access and domain transitions + * @caps: capabilities for the profile ++ * @net: network controls for the profile + * @rlimits: rlimits for the profile + * + * @dents: dentries for the profiles file entries in apparmorfs +@@ -217,6 +219,7 @@ struct aa_profile { + struct aa_policydb policy; + struct aa_file_rules file; + struct aa_caps caps; ++ struct aa_net net; + struct aa_rlimit rlimits; + + unsigned char *hash; +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index fb99e18..de55a7f 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -32,6 +32,7 @@ + #include "include/context.h" + #include "include/file.h" + #include "include/ipc.h" ++#include "include/net.h" + #include "include/path.h" + #include "include/policy.h" + #include "include/procattr.h" +@@ -615,6 +616,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, + return error; + } + ++static int apparmor_socket_create(int family, int type, int protocol, int kern) +{ -+ struct aa_profile *profile = (struct aa_profile *) p; ++ struct aa_profile *profile; ++ int error = 0; + -+ (*pos)++; -+ profile = next_profile(profile); ++ if (kern) ++ return 0; + -+ return profile; ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) ++ error = aa_net_perm(OP_CREATE, profile, family, type, protocol, ++ NULL); ++ return error; +} + -+static void p_stop(struct seq_file *f, void *p) -+ __releases(ns_list_lock) ++static int apparmor_socket_bind(struct socket *sock, ++ struct sockaddr *address, int addrlen) +{ -+ struct aa_profile *profile = (struct aa_profile *) p; -+ -+ if (profile) -+ read_unlock(&profile->ns->base.lock); -+ read_unlock(&ns_list_lock); -+} ++ struct sock *sk = sock->sk; + -+static void print_name(struct seq_file *f, struct aa_profile *profile) -+{ -+ if (profile->parent) { -+ print_name(f, profile->parent); -+ seq_printf(f, "//"); -+ } -+ seq_printf(f, "%s", profile->base.name); ++ return aa_revalidate_sk(OP_BIND, sk); +} + -+static int seq_show_profile(struct seq_file *f, void *p) ++static int apparmor_socket_connect(struct socket *sock, ++ struct sockaddr *address, int addrlen) +{ -+ struct aa_profile *profile = (struct aa_profile *)p; -+ -+ if (profile->ns != default_namespace) -+ seq_printf(f, ":%s:", profile->ns->base.name); -+ print_name(f, profile); -+ seq_printf(f, " (%s)\n", -+ PROFILE_COMPLAIN(profile) ? "complain" : "enforce"); ++ struct sock *sk = sock->sk; + -+ return 0; ++ return aa_revalidate_sk(OP_CONNECT, sk); +} + -+/* Used in apparmorfs.c */ -+static struct seq_operations apparmorfs_profiles_op = { -+ .start = p_start, -+ .next = p_next, -+ .stop = p_stop, -+ .show = seq_show_profile, -+}; -+ -+static int aa_profiles_open(struct inode *inode, struct file *file) ++static int apparmor_socket_listen(struct socket *sock, int backlog) +{ -+ return seq_open(file, &apparmorfs_profiles_op); -+} -+ ++ struct sock *sk = sock->sk; + -+static int aa_profiles_release(struct inode *inode, struct file *file) -+{ -+ return seq_release(inode, file); ++ return aa_revalidate_sk(OP_LISTEN, sk); +} + -+static struct file_operations apparmorfs_profiles_fops = { -+ .open = aa_profiles_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = aa_profiles_release, -+}; -+ -+/* apparmor/matching */ -+static ssize_t aa_matching_read(struct file *file, char __user *buf, -+ size_t size, loff_t *ppos) ++static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ -+ const char *matching = "pattern=aadfa audit perms=crwxamlk/ user::other"; ++ struct sock *sk = sock->sk; + -+ return simple_read_from_buffer(buf, size, ppos, matching, -+ strlen(matching)); ++ return aa_revalidate_sk(OP_ACCEPT, sk); +} + -+static struct file_operations apparmorfs_matching_fops = { -+ .read = aa_matching_read, -+}; -+ -+/* apparmor/features */ -+static ssize_t aa_features_read(struct file *file, char __user *buf, -+ size_t size, loff_t *ppos) ++static int apparmor_socket_sendmsg(struct socket *sock, ++ struct msghdr *msg, int size) +{ -+ const char *features = "file=3.1 capability=2.0 network=1.0 " -+ "change_hat=1.5 change_profile=1.1 " -+ "aanamespaces=1.1 rlimit=1.1"; ++ struct sock *sk = sock->sk; + -+ return simple_read_from_buffer(buf, size, ppos, features, -+ strlen(features)); ++ return aa_revalidate_sk(OP_SENDMSG, sk); +} + -+static struct file_operations apparmorfs_features_fops = { -+ .read = aa_features_read, -+}; -+ -+/* apparmor/.load */ -+static ssize_t aa_profile_load(struct file *f, const char __user *buf, -+ size_t size, loff_t *pos) ++static int apparmor_socket_recvmsg(struct socket *sock, ++ struct msghdr *msg, int size, int flags) +{ -+ char *data; -+ ssize_t error; -+ -+ data = aa_simple_write_to_buffer(buf, size, size, pos, "profile_load"); -+ -+ error = PTR_ERR(data); -+ if (!IS_ERR(data)) { -+ error = aa_interface_add_profiles(data, size); -+ vfree(data); -+ } ++ struct sock *sk = sock->sk; + -+ return error; ++ return aa_revalidate_sk(OP_RECVMSG, sk); +} + -+ -+static struct file_operations apparmorfs_profile_load = { -+ .write = aa_profile_load -+}; -+ -+/* apparmor/.replace */ -+static ssize_t aa_profile_replace(struct file *f, const char __user *buf, -+ size_t size, loff_t *pos) ++static int apparmor_socket_getsockname(struct socket *sock) +{ -+ char *data; -+ ssize_t error; -+ -+ data = aa_simple_write_to_buffer(buf, size, size, pos, -+ "profile_replace"); -+ error = PTR_ERR(data); -+ if (!IS_ERR(data)) { -+ error = aa_interface_replace_profiles(data, size); -+ vfree(data); -+ } ++ struct sock *sk = sock->sk; + -+ return error; ++ return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + -+ -+static struct file_operations apparmorfs_profile_replace = { -+ .write = aa_profile_replace -+}; -+ -+/* apparmor/.remove */ -+static ssize_t aa_profile_remove(struct file *f, const char __user *buf, -+ size_t size, loff_t *pos) ++static int apparmor_socket_getpeername(struct socket *sock) +{ -+ char *data; -+ ssize_t error; -+ -+ /* -+ * aa_remove_profile needs a null terminated string so 1 extra -+ * byte is allocated and the copied data is null terminated. -+ */ -+ data = aa_simple_write_to_buffer(buf, size + 1, size, pos, -+ "profile_remove"); -+ -+ error = PTR_ERR(data); -+ if (!IS_ERR(data)) { -+ data[size] = 0; -+ error = aa_interface_remove_profiles(data, size); -+ vfree(data); -+ } ++ struct sock *sk = sock->sk; + -+ return error; ++ return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + -+static struct file_operations apparmorfs_profile_remove = { -+ .write = aa_profile_remove -+}; -+ -+static struct dentry *apparmorfs_dentry; -+struct dentry *apparmorfs_null; -+struct vfsmount *apparmorfs_mnt; -+ -+static void aafs_remove(const char *name) ++static int apparmor_socket_getsockopt(struct socket *sock, int level, ++ int optname) +{ -+ struct dentry *dentry; ++ struct sock *sk = sock->sk; + -+ dentry = lookup_one_len(name, apparmorfs_dentry, strlen(name)); -+ if (!IS_ERR(dentry)) { -+ securityfs_remove(dentry); -+ dput(dentry); -+ } ++ return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + -+static int aafs_create(const char *name, int mask, struct file_operations *fops) ++static int apparmor_socket_setsockopt(struct socket *sock, int level, ++ int optname) +{ -+ struct dentry *dentry; -+ -+ dentry = securityfs_create_file(name, S_IFREG | mask, apparmorfs_dentry, -+ NULL, fops); -+ -+ return IS_ERR(dentry) ? PTR_ERR(dentry) : 0; -+} ++ struct sock *sk = sock->sk; + -+void destroy_apparmorfs(void) -+{ -+ if (apparmorfs_dentry) { -+ aafs_remove(".remove"); -+ aafs_remove(".replace"); -+ aafs_remove(".load"); -+ aafs_remove("matching"); -+ aafs_remove("features"); -+ aafs_remove("profiles"); -+ securityfs_remove(apparmorfs_dentry); -+ apparmorfs_dentry = NULL; -+ } ++ return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + -+int create_apparmorfs(void) ++static int apparmor_socket_shutdown(struct socket *sock, int how) +{ -+ int error; -+ -+ if (!apparmor_initialized) -+ return 0; -+ -+ if (apparmorfs_dentry) { -+ AA_ERROR("%s: AppArmor securityfs already exists\n", __func__); -+ return -EEXIST; -+ } -+ -+ apparmorfs_dentry = securityfs_create_dir("apparmor", NULL); -+ if (IS_ERR(apparmorfs_dentry)) { -+ error = PTR_ERR(apparmorfs_dentry); -+ apparmorfs_dentry = NULL; -+ goto error; -+ } -+ error = aafs_create("profiles", 0440, &apparmorfs_profiles_fops); -+ if (error) -+ goto error; -+ error = aafs_create("matching", 0444, &apparmorfs_matching_fops); -+ if (error) -+ goto error; -+ error = aafs_create("features", 0444, &apparmorfs_features_fops); -+ if (error) -+ goto error; -+ error = aafs_create(".load", 0640, &apparmorfs_profile_load); -+ if (error) -+ goto error; -+ error = aafs_create(".replace", 0640, &apparmorfs_profile_replace); -+ if (error) -+ goto error; -+ error = aafs_create(".remove", 0640, &apparmorfs_profile_remove); -+ if (error) -+ goto error; -+ -+ /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ -+ -+ /* Report that AppArmor fs is enabled */ -+ info_message("AppArmor Filesystem Enabled"); -+ return 0; ++ struct sock *sk = sock->sk; + -+error: -+ destroy_apparmorfs(); -+ AA_ERROR("Error creating AppArmor securityfs\n"); -+ apparmor_disable(); -+ return error; ++ return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + -+fs_initcall(create_apparmorfs); + static struct security_operations apparmor_ops = { + .name = "apparmor", + +@@ -647,6 +746,19 @@ static struct security_operations apparmor_ops = { + .getprocattr = apparmor_getprocattr, + .setprocattr = apparmor_setprocattr, + ++ .socket_create = apparmor_socket_create, ++ .socket_bind = apparmor_socket_bind, ++ .socket_connect = apparmor_socket_connect, ++ .socket_listen = apparmor_socket_listen, ++ .socket_accept = apparmor_socket_accept, ++ .socket_sendmsg = apparmor_socket_sendmsg, ++ .socket_recvmsg = apparmor_socket_recvmsg, ++ .socket_getsockname = apparmor_socket_getsockname, ++ .socket_getpeername = apparmor_socket_getpeername, ++ .socket_getsockopt = apparmor_socket_getsockopt, ++ .socket_setsockopt = apparmor_socket_setsockopt, ++ .socket_shutdown = apparmor_socket_shutdown, + -diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c + .cred_alloc_blank = apparmor_cred_alloc_blank, + .cred_free = apparmor_cred_free, + .cred_prepare = apparmor_cred_prepare, +diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 -index 0000000..834a4f5 +index 0000000..003dd18 --- /dev/null -+++ b/security/apparmor/audit.c -@@ -0,0 +1,151 @@ ++++ b/security/apparmor/net.c +@@ -0,0 +1,162 @@ +/* + * AppArmor security module + * -+ * This file contains AppArmor auditing functions ++ * This file contains AppArmor network mediation + * + * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. ++ * Copyright 2009-2012 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 @@ -568,284 +371,482 @@ index 0000000..834a4f5 + * License. + */ + -+#include -+#include -+ +#include "include/apparmor.h" +#include "include/audit.h" ++#include "include/context.h" ++#include "include/net.h" +#include "include/policy.h" + -+const char *audit_mode_names[] = { -+ "normal", -+ "quiet_denied", -+ "quiet" -+ "noquiet", -+ "all" -+}; ++#include "net_names.h" + -+static char* aa_audit_type[] = { -+ "APPARMOR_AUDIT", -+ "APPARMOR_ALLOWED", -+ "APPARMOR_DENIED", -+ "APPARMOR_HINT", -+ "APPARMOR_STATUS", -+ "APPARMOR_ERROR", -+ "APPARMOR_KILLED" ++struct aa_fs_entry aa_fs_entry_network[] = { ++ AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), ++ { } +}; + -+/* -+ * TODO: -+ * user auditing - netlink interface -+ * system control of whether user audit messages go to system log -+ */ -+static int aa_audit_base(int type, struct aa_profile *profile, -+ struct aa_audit *sa, struct audit_context *audit_cxt, -+ void(*cb)(struct audit_buffer *, void *)) ++/* audit callback for net specific fields */ ++static void audit_cb(struct audit_buffer *ab, void *va) +{ -+ struct audit_buffer *ab = NULL; -+ -+ if (profile && PROFILE_KILL(profile) && type == AUDIT_APPARMOR_DENIED) -+ type = AUDIT_APPARMOR_KILL; ++ struct common_audit_data *sa = va; + -+ ab = audit_log_start(audit_cxt, sa->gfp_mask, type); -+ -+ if (!ab) { -+ AA_ERROR("(%d) Unable to log event of type (%d)\n", -+ -ENOMEM, type); -+ /* don't fail operations in complain mode even if logging -+ * fails */ -+ return type == AUDIT_APPARMOR_ALLOWED ? 0 : -ENOMEM; ++ audit_log_format(ab, " family="); ++ if (address_family_names[sa->u.net->family]) { ++ audit_log_string(ab, address_family_names[sa->u.net->family]); ++ } else { ++ audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); + } -+ -+ if (g_apparmor_audit_header) -+ audit_log_format(ab, "type=%s ", -+ aa_audit_type[type - AUDIT_APPARMOR_AUDIT]); -+ -+ if (sa->operation) -+ audit_log_format(ab, "operation=\"%s\"", sa->operation); -+ -+ if (sa->info) { -+ audit_log_format(ab, " info=\"%s\"", sa->info); -+ if (sa->error) -+ audit_log_format(ab, " error=%d", sa->error); ++ audit_log_format(ab, " sock_type="); ++ if (sock_type_names[sa->aad->net.type]) { ++ audit_log_string(ab, sock_type_names[sa->aad->net.type]); ++ } else { ++ audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); + } ++ audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); ++} + -+ audit_log_format(ab, " pid=%d", sa->task ?sa->task->pid : current->pid); ++/** ++ * audit_net - audit network access ++ * @profile: profile being enforced (NOT NULL) ++ * @op: operation being checked ++ * @family: network family ++ * @type: network type ++ * @protocol: network protocol ++ * @sk: socket auditing is being applied to ++ * @error: error code for failure else 0 ++ * ++ * Returns: %0 or sa->error else other errorcode on failure ++ */ ++static int audit_net(struct aa_profile *profile, int op, u16 family, int type, ++ int protocol, struct sock *sk, int error) ++{ ++ int audit_type = AUDIT_APPARMOR_AUTO; ++ struct common_audit_data sa; ++ struct apparmor_audit_data aad = { }; ++ struct lsm_network_audit net = { }; ++ if (sk) { ++ sa.type = LSM_AUDIT_DATA_NET; ++ } else { ++ sa.type = LSM_AUDIT_DATA_NONE; ++ } ++ /* todo fill in socket addr info */ ++ sa.aad = &aad; ++ sa.u.net = &net; ++ sa.aad->op = op, ++ sa.u.net->family = family; ++ sa.u.net->sk = sk; ++ sa.aad->net.type = type; ++ sa.aad->net.protocol = protocol; ++ sa.aad->error = error; ++ ++ if (likely(!sa.aad->error)) { ++ u16 audit_mask = profile->net.audit[sa.u.net->family]; ++ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && ++ !(1 << sa.aad->net.type & audit_mask))) ++ return 0; ++ audit_type = AUDIT_APPARMOR_AUDIT; ++ } else { ++ u16 quiet_mask = profile->net.quiet[sa.u.net->family]; ++ u16 kill_mask = 0; ++ u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; + -+ if (profile) { -+ pid_t pid = sa->task ? sa->task->real_parent->pid : -+ current->real_parent->pid; -+ audit_log_format(ab, " parent=%d", pid); -+ audit_log_format(ab, " profile="); -+ audit_log_untrustedstring(ab, profile->fqname); ++ if (denied & kill_mask) ++ audit_type = AUDIT_APPARMOR_KILL; + -+ if (profile->ns != default_namespace) { -+ audit_log_format(ab, " namespace="); -+ audit_log_untrustedstring(ab, profile->ns->base.name); -+ } ++ if ((denied & quiet_mask) && ++ AUDIT_MODE(profile) != AUDIT_NOQUIET && ++ AUDIT_MODE(profile) != AUDIT_ALL) ++ return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + } + -+ if (cb) -+ cb(ab, sa); -+ -+ audit_log_end(ab); -+ -+ if (type == AUDIT_APPARMOR_KILL) -+ (void)send_sig_info(SIGKILL, NULL, -+ sa->task ? sa->task : current); -+ -+ return type == AUDIT_APPARMOR_ALLOWED ? 0 : sa->error; ++ return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); +} + +/** -+ * aa_audit - Log an audit event to the audit subsystem -+ * @type: audit type for the message -+ * @profile: profile to check against -+ * @sa: audit event ++ * aa_net_perm - very course network access check ++ * @op: operation being checked ++ * @profile: profile being enforced (NOT NULL) ++ * @family: network family ++ * @type: network type ++ * @protocol: network protocol ++ * ++ * Returns: %0 else error if permission denied + */ -+int aa_audit(int type, struct aa_profile *profile, struct aa_audit *sa, -+ void(*cb)(struct audit_buffer *, void *)) ++int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, ++ int protocol, struct sock *sk) +{ -+ struct audit_context *audit_cxt; -+ audit_cxt = g_apparmor_logsyscall ? current->audit_context : NULL; -+ -+ if (type == AUDIT_APPARMOR_AUTO) { -+ if (likely(!sa->error)) -+ type = AUDIT_APPARMOR_AUDIT; -+ else if (PROFILE_COMPLAIN(profile)) -+ type = AUDIT_APPARMOR_ALLOWED; -+ else -+ type = AUDIT_APPARMOR_DENIED; -+ } -+ if (PROFILE_AUDIT_MODE(profile) == AUDIT_QUIET || -+ (type == AUDIT_APPARMOR_DENIED && -+ PROFILE_AUDIT_MODE(profile) == AUDIT_QUIET)) -+ return sa->error; ++ u16 family_mask; ++ int error; + -+ return aa_audit_base(type, profile, sa, audit_cxt, cb); -+} ++ if ((family < 0) || (family >= AF_MAX)) ++ return -EINVAL; + -+/** -+ * aa_audit_syscallreject - Log a syscall rejection to the audit subsystem -+ * @profile: profile to check against -+ * @gfp: memory allocation flags -+ * @msg: string describing syscall being rejected -+ */ -+int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp, -+ const char *msg, -+ void(*cb)(struct audit_buffer *, void *)) -+{ -+ struct aa_audit sa; -+ memset(&sa, 0, sizeof(sa)); -+ sa.operation = "syscall"; -+ sa.info = msg; -+ sa.gfp_mask = gfp; -+ sa.error = -EACCES; -+ -+ return aa_audit_base(AUDIT_APPARMOR_DENIED, profile, &sa, -+ current->audit_context, NULL); -+} -diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c -new file mode 100644 -index 0000000..79097e8 ---- /dev/null -+++ b/security/apparmor/capability.c -@@ -0,0 +1,121 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor capability mediation functions -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ */ ++ if ((type < 0) || (type >= SOCK_MAX)) ++ return -EINVAL; + -+#include -+#include -+#include ++ /* unix domain and netlink sockets are handled by ipc */ ++ if (family == AF_UNIX || family == AF_NETLINK) ++ return 0; + -+#include "include/apparmor.h" -+#include "include/capability.h" -+#include "include/context.h" -+#include "include/policy.h" -+#include "include/audit.h" ++ family_mask = profile->net.allow[family]; + -+/* -+ * Table of capability names: we generate it from capabilities.h. -+ */ -+#include "capability_names.h" ++ error = (family_mask & (1 << type)) ? 0 : -EACCES; + -+struct audit_cache { -+ struct task_struct *task; -+ kernel_cap_t caps; -+}; ++ return audit_net(profile, op, family, type, protocol, sk, error); ++} + -+static DEFINE_PER_CPU(struct audit_cache, audit_cache); ++/** ++ * aa_revalidate_sk - Revalidate access to a sock ++ * @op: operation being checked ++ * @sk: sock being revalidated (NOT NULL) ++ * ++ * Returns: %0 else error if permission denied ++ */ ++int aa_revalidate_sk(int op, struct sock *sk) ++{ ++ struct aa_profile *profile; ++ int error = 0; + -+struct aa_audit_caps { -+ struct aa_audit base; ++ /* aa_revalidate_sk should not be called from interrupt context ++ * don't mediate these calls as they are not task related ++ */ ++ if (in_interrupt()) ++ return 0; + -+ int cap; -+}; -+ -+static void audit_cb(struct audit_buffer *ab, void *va) -+{ -+ struct aa_audit_caps *sa = va; ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) ++ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, ++ sk->sk_protocol, sk); + -+ audit_log_format(ab, " name="); -+ audit_log_untrustedstring(ab, capability_names[sa->cap]); ++ return error; +} -+ -+static int aa_audit_caps(struct aa_profile *profile, struct aa_audit_caps *sa) +diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c +index 705c287..e2afe29 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -603,6 +603,7 @@ void aa_free_profile(struct aa_profile *profile) + + aa_free_file_rules(&profile->file); + aa_free_cap_rules(&profile->caps); ++ aa_free_net_rules(&profile->net); + aa_free_rlimit_rules(&profile->rlimits); + + kzfree(profile->dirname); +diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c +index a689f10..1a35e6b 100644 +--- a/security/apparmor/policy_unpack.c ++++ b/security/apparmor/policy_unpack.c +@@ -193,6 +193,19 @@ fail: + return 0; + } + ++static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ -+ struct audit_cache *ent; -+ int type = AUDIT_APPARMOR_AUTO; -+ -+ if (likely(!sa->base.error)) { -+ /* test if auditing is being forced */ -+ if (likely((PROFILE_AUDIT_MODE(profile) != AUDIT_ALL) && -+ !cap_raised(profile->caps.audit, sa->cap))) -+ return 0; -+ } else if (PROFILE_KILL(profile) || -+ cap_raised(profile->caps.kill, sa->cap)) { -+ type = AUDIT_APPARMOR_KILL; -+ } else if (cap_raised(profile->caps.quiet, sa->cap) && -+ PROFILE_AUDIT_MODE(profile) != AUDIT_NOQUIET && -+ PROFILE_AUDIT_MODE(profile) != AUDIT_ALL) { -+ /* quiet auditing */ -+ return sa->base.error; -+ } -+ -+ /* Do simple duplicate message elimination */ -+ ent = &get_cpu_var(audit_cache); -+ if (sa->base.task == ent->task && cap_raised(ent->caps, sa->cap)) { -+ if (PROFILE_COMPLAIN(profile)) ++ if (unpack_nameX(e, AA_U16, name)) { ++ if (!inbounds(e, sizeof(u16))) + return 0; -+ return sa->base.error; -+ } else { -+ ent->task = sa->base.task; -+ cap_raise(ent->caps, sa->cap); ++ if (data) ++ *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); ++ e->pos += sizeof(u16); ++ return 1; + } -+ put_cpu_var(audit_cache); -+ -+ return aa_audit(type, profile, &sa->base, audit_cb); -+} -+ -+int aa_profile_capable(struct aa_profile *profile, int cap) -+{ -+ return cap_raised(profile->caps.allowed, cap) ? 0 : -EPERM; ++ return 0; +} + -+/** -+ * aa_capable - test permission to use capability -+ * @task: task doing capability test against -+ * @profile: profile confining @task -+ * @cap: capability to be tested -+ * @audit: whether an audit record should be generated -+ * -+ * Look up capability in profile capability set. -+ * Returns 0 on success, or else an error code. -+ */ -+int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, -+ int audit) -+{ -+ int error = aa_profile_capable(profile, cap); -+ struct aa_audit_caps sa; + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) + { + if (unpack_nameX(e, AA_U32, name)) { +@@ -476,6 +489,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) + { + struct aa_profile *profile = NULL; + const char *name = NULL; ++ size_t size = 0; + int i, error = -EPROTO; + kernel_cap_t tmpcap; + u32 tmp; +@@ -576,6 +590,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) + if (!unpack_rlimits(e, profile)) + goto fail; + ++ size = unpack_array(e, "net_allowed_af"); ++ if (size) { + -+ if (!audit) { -+ if (PROFILE_COMPLAIN(profile)) -+ return 0; -+ return error; ++ for (i = 0; i < size; i++) { ++ /* discard extraneous rules that this kernel will ++ * never request ++ */ ++ if (i >= AF_MAX) { ++ u16 tmp; ++ if (!unpack_u16(e, &tmp, NULL) || ++ !unpack_u16(e, &tmp, NULL) || ++ !unpack_u16(e, &tmp, NULL)) ++ goto fail; ++ continue; ++ } ++ if (!unpack_u16(e, &profile->net.allow[i], NULL)) ++ goto fail; ++ if (!unpack_u16(e, &profile->net.audit[i], NULL)) ++ goto fail; ++ if (!unpack_u16(e, &profile->net.quiet[i], NULL)) ++ goto fail; ++ } ++ if (!unpack_nameX(e, AA_ARRAYEND, NULL)) ++ goto fail; + } ++ /* ++ * allow unix domain and netlink sockets they are handled ++ * by IPC ++ */ ++ profile->net.allow[AF_UNIX] = 0xffff; ++ profile->net.allow[AF_NETLINK] = 0xffff; ++ + if (unpack_nameX(e, AA_STRUCT, "policydb")) { + /* generic policy dfa - optional and may be NULL */ + profile->policy.dfa = unpack_dfa(e); +-- +1.8.3.2 + +From b452a37e97af826ba6c7548230e07c95bd13d9c4 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Fri, 29 Jun 2012 17:34:00 -0700 +Subject: [PATCH 2/3] apparmor: Fix quieting of audit messages for network + mediation + +If a profile specified a quieting of network denials for a given rule by +either the quiet or deny rule qualifiers, the resultant quiet mask for +denied requests was applied incorrectly, resulting in two potential bugs. +1. The misapplied quiet mask would prevent denials from being correctly + tested against the kill mask/mode. Thus network access requests that + should have resulted in the application being killed did not. + +2. The actual quieting of the denied network request was not being applied. + This would result in network rejections always being logged even when + they had been specifically marked as quieted. + +Signed-off-by: John Johansen +--- + security/apparmor/net.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/security/apparmor/net.c b/security/apparmor/net.c +index 003dd18..6e6e5c9 100644 +--- a/security/apparmor/net.c ++++ b/security/apparmor/net.c +@@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; +- u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; ++ u16 denied = (1 << sa.aad->net.type); + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; +-- +1.8.3.2 + +From 0f113c1f052be315f5097d8b7294a620b0adda87 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 16 May 2012 10:58:05 -0700 +Subject: [PATCH 3/3] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount + +Add the ability for apparmor to do mediation of mount operations. Mount +rules require an updated apparmor_parser (2.8 series) for policy compilation. + +The basic form of the rules are. + + [audit] [deny] mount [conds]* [device] [ -> [conds] path], + [audit] [deny] remount [conds]* [path], + [audit] [deny] umount [conds]* [path], + [audit] [deny] pivotroot [oldroot=] + + remount is just a short cut for mount options=remount + + where [conds] can be + fstype= + options= + +Example mount commands + mount, # allow all mounts, but not umount or pivotroot + + mount fstype=procfs, # allow mounting procfs anywhere + + mount options=(bind, ro) /foo -> /bar, # readonly bind mount + + mount /dev/sda -> /mnt, + + mount /dev/sd** -> /mnt/**, + + mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ + + umount, + + umount /m*, + +See the apparmor userspace for full documentation + +Signed-off-by: John Johansen +Acked-by: Kees Cook + +Conflicts: + security/apparmor/Makefile + security/apparmor/apparmorfs.c +--- + security/apparmor/Makefile | 2 +- + security/apparmor/apparmorfs.c | 15 +- + security/apparmor/audit.c | 4 + + security/apparmor/domain.c | 2 +- + security/apparmor/include/apparmor.h | 3 +- + security/apparmor/include/audit.h | 11 + + security/apparmor/include/domain.h | 2 + + security/apparmor/include/mount.h | 54 +++ + security/apparmor/lsm.c | 59 ++++ + security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ + 10 files changed, 768 insertions(+), 4 deletions(-) + create mode 100644 security/apparmor/include/mount.h + create mode 100644 security/apparmor/mount.c + +diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile +index 5dbb72f..89b3445 100644 +--- a/security/apparmor/Makefile ++++ b/security/apparmor/Makefile +@@ -4,7 +4,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 net.o ++ resource.o sid.o file.o net.o mount.o + apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o + + clean-files := capability_names.h rlim_names.h net_names.h +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 18fc02c..e709030 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -799,7 +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_mount[] = { ++ AA_FS_FILE_STRING("mask", "mount umount"), ++ { } ++}; + -+ memset(&sa, 0, sizeof(sa)); -+ sa.base.operation = "capable"; -+ sa.base.task = task; -+ sa.base.gfp_mask = GFP_ATOMIC; -+ sa.base.error = error; -+ sa.cap = cap; ++static struct aa_fs_entry aa_fs_entry_namespaces[] = { ++ AA_FS_FILE_BOOLEAN("profile", 1), ++ AA_FS_FILE_BOOLEAN("pivot_root", 1), ++ { } + }; + + static struct aa_fs_entry aa_fs_entry_features[] = { +@@ -807,6 +818,8 @@ static struct aa_fs_entry aa_fs_entry_features[] = { + AA_FS_DIR("domain", aa_fs_entry_domain), + AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), ++ AA_FS_DIR("mount", aa_fs_entry_mount), ++ AA_FS_DIR("namespaces", aa_fs_entry_namespaces), + 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), +diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c +index 031d2d9..02d804c 100644 +--- a/security/apparmor/audit.c ++++ b/security/apparmor/audit.c +@@ -44,6 +44,10 @@ const char *const op_table[] = { + "file_mmap", + "file_mprotect", + ++ "pivotroot", ++ "mount", ++ "umount", + -+ return aa_audit_caps(profile, &sa); -+} -diff --git a/security/apparmor/context.c b/security/apparmor/context.c + "create", + "post_create", + "bind", +diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c +index 26c607c..23936c5 100644 +--- a/security/apparmor/domain.c ++++ b/security/apparmor/domain.c +@@ -238,7 +238,7 @@ static const char *next_name(int xtype, const char *name) + * + * Returns: refcounted profile, or NULL on failure (MAYBE NULL) + */ +-static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) ++struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) + { + struct aa_profile *new_profile = NULL; + struct aa_namespace *ns = profile->ns; +diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h +index 8fb1488..22b172c 100644 +--- a/security/apparmor/include/apparmor.h ++++ b/security/apparmor/include/apparmor.h +@@ -30,8 +30,9 @@ + #define AA_CLASS_NET 4 + #define AA_CLASS_RLIMITS 5 + #define AA_CLASS_DOMAIN 6 ++#define AA_CLASS_MOUNT 7 + +-#define AA_CLASS_LAST AA_CLASS_DOMAIN ++#define AA_CLASS_LAST AA_CLASS_MOUNT + + /* Control parameters settable through module/boot flags */ + extern enum audit_mode aa_g_audit; +diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h +index 61abec5..a9835c3 100644 +--- a/security/apparmor/include/audit.h ++++ b/security/apparmor/include/audit.h +@@ -72,6 +72,10 @@ enum aa_ops { + OP_FMMAP, + OP_FMPROT, + ++ OP_PIVOTROOT, ++ OP_MOUNT, ++ OP_UMOUNT, ++ + OP_CREATE, + OP_POST_CREATE, + OP_BIND, +@@ -121,6 +125,13 @@ struct apparmor_audit_data { + unsigned long max; + } rlim; + struct { ++ const char *src_name; ++ const char *type; ++ const char *trans; ++ const char *data; ++ unsigned long flags; ++ } mnt; ++ struct { + const char *target; + u32 request; + u32 denied; +diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h +index de04464..a3f70c5 100644 +--- a/security/apparmor/include/domain.h ++++ b/security/apparmor/include/domain.h +@@ -23,6 +23,8 @@ struct aa_domain { + char **table; + }; + ++struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); ++ + int apparmor_bprm_set_creds(struct linux_binprm *bprm); + int apparmor_bprm_secureexec(struct linux_binprm *bprm); + void apparmor_bprm_committing_creds(struct linux_binprm *bprm); +diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 -index 0000000..02e0b70 +index 0000000..bc17a53 --- /dev/null -+++ b/security/apparmor/context.c -@@ -0,0 +1,209 @@ ++++ b/security/apparmor/include/mount.h +@@ -0,0 +1,54 @@ +/* + * AppArmor security module + * -+ * This file contains AppArmor functions used to manipulate object security -+ * contexts. ++ * This file contains AppArmor file mediation function definitions. + * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. ++ * Copyright 2012 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 @@ -853,213 +854,144 @@ index 0000000..02e0b70 + * License. + */ + -+#include "include/context.h" -+#include "include/policy.h" ++#ifndef __AA_MOUNT_H ++#define __AA_MOUNT_H + ++#include ++#include + ++#include "domain.h" ++#include "policy.h" + -+struct aa_task_context *aa_alloc_task_context(gfp_t flags) -+{ -+ return kzalloc(sizeof(struct aa_task_context), flags); -+} ++/* mount perms */ ++#define AA_MAY_PIVOTROOT 0x01 ++#define AA_MAY_MOUNT 0x02 ++#define AA_MAY_UMOUNT 0x04 ++#define AA_AUDIT_DATA 0x40 ++#define AA_CONT_MATCH 0x40 + -+void aa_free_task_context(struct aa_task_context *cxt) -+{ -+ if (cxt) { -+ aa_put_profile(cxt->sys.profile); -+ aa_put_profile(cxt->sys.previous); -+ aa_put_profile(cxt->sys.onexec); ++#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + -+ memset(cxt, 0, sizeof(*cxt)); -+ kfree(cxt); -+ } -+} ++int aa_remount(struct aa_profile *profile, struct path *path, ++ unsigned long flags, void *data); + -+/* -+ * duplicate a task context, incrementing reference counts -+ */ -+struct aa_task_context *aa_dup_task_context(struct aa_task_context *old_cxt, -+ gfp_t gfp) -+{ -+ struct aa_task_context *cxt; ++int aa_bind_mount(struct aa_profile *profile, struct path *path, ++ const char *old_name, unsigned long flags); + -+ cxt = kmemdup(old_cxt, sizeof(*cxt), gfp); -+ if (!cxt) -+ return NULL; + -+ aa_get_profile(cxt->sys.profile); -+ aa_get_profile(cxt->sys.previous); -+ aa_get_profile(cxt->sys.onexec); ++int aa_mount_change_type(struct aa_profile *profile, struct path *path, ++ unsigned long flags); + -+ return cxt; -+} ++int aa_move_mount(struct aa_profile *profile, struct path *path, ++ const char *old_name); + -+/** -+ * aa_cred_policy - obtain cred's profiles -+ * @cred: cred to obtain profiles from -+ * @sys: return system profile -+ * does NOT increment reference count -+ */ -+void aa_cred_policy(const struct cred *cred, struct aa_profile **sys) -+{ -+ struct aa_task_context *cxt = cred->security; -+ BUG_ON(!cxt); -+ *sys = aa_filtered_profile(aa_profile_newest(cxt->sys.profile)); -+} ++int aa_new_mount(struct aa_profile *profile, const char *dev_name, ++ struct path *path, const char *type, unsigned long flags, ++ void *data); + -+/** -+ * aa_get_task_policy - get the cred with the task policy, and current profiles -+ * @task: task to get policy of -+ * @sys: return - pointer to system profile -+ * -+ * Only gets the cred ref count which has ref counts on the profiles returned -+ */ -+struct cred *aa_get_task_policy(const struct task_struct *task, -+ struct aa_profile **sys) -+{ -+ struct cred *cred = get_task_cred(task); -+ aa_cred_policy(cred, sys); -+ return cred; -+} ++int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); + -+void aa_put_task_policy(struct cred *cred) -+{ -+ put_cred(cred); -+} ++int aa_pivotroot(struct aa_profile *profile, struct path *old_path, ++ struct path *new_path); + -+static void replace_group(struct aa_task_cxt_group *cgrp, -+ struct aa_profile *profile) ++#endif /* __AA_MOUNT_H */ +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index de55a7f..e0dd95f 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -36,6 +36,7 @@ + #include "include/path.h" + #include "include/policy.h" + #include "include/procattr.h" ++#include "include/mount.h" + + /* Flag indicating whether initialization completed */ + int apparmor_initialized __initdata; +@@ -502,6 +503,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, + !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); + } + ++static int apparmor_sb_mount(char *dev_name, struct path *path, char *type, ++ unsigned long flags, void *data) +{ -+ if (cgrp->profile == profile) -+ return; -+ -+ if (!profile || (profile->flags & PFLAG_UNCONFINED) || -+ (cgrp->profile && cgrp->profile->ns != profile->ns)) { -+ aa_put_profile(cgrp->previous); -+ aa_put_profile(cgrp->onexec); -+ cgrp->previous = NULL; -+ cgrp->onexec = NULL; -+ cgrp->token = 0; ++ struct aa_profile *profile; ++ int error = 0; ++ ++ /* Discard magic */ ++ if ((flags & MS_MGC_MSK) == MS_MGC_VAL) ++ flags &= ~MS_MGC_MSK; ++ ++ flags &= ~AA_MS_IGNORE_MASK; ++ ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) { ++ if (flags & MS_REMOUNT) ++ error = aa_remount(profile, path, flags, data); ++ else if (flags & MS_BIND) ++ error = aa_bind_mount(profile, path, dev_name, flags); ++ else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | ++ MS_UNBINDABLE)) ++ error = aa_mount_change_type(profile, path, flags); ++ else if (flags & MS_MOVE) ++ error = aa_move_mount(profile, path, dev_name); ++ else ++ error = aa_new_mount(profile, dev_name, path, type, ++ flags, data); + } -+ aa_put_profile(cgrp->profile); -+ cgrp->profile = aa_get_profile(profile); ++ return error; +} + -+/** -+ * aa_replace_current_profiles - replace the current tasks profiles -+ * @sys: new system profile -+ * -+ * Returns: error on failure -+ */ -+int aa_replace_current_profiles(struct aa_profile *sys) ++static int apparmor_sb_umount(struct vfsmount *mnt, int flags) +{ -+ struct aa_task_context *cxt; -+ struct cred *new = prepare_creds(); -+ if (!new) -+ return -ENOMEM; ++ struct aa_profile *profile; ++ int error = 0; + -+ cxt = new->security; -+ replace_group(&cxt->sys, sys); ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) ++ error = aa_umount(profile, mnt, flags); + -+ commit_creds(new); -+ return 0; ++ return error; +} + -+int aa_set_current_onexec(struct aa_profile *sys) ++static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) +{ -+ struct aa_task_context *cxt; -+ struct cred *new = prepare_creds(); -+ if (!new) -+ return -ENOMEM; -+ -+ cxt = new->security; -+ aa_put_profile(cxt->sys.onexec); -+ cxt->sys.onexec = aa_get_profile(sys); -+ -+ commit_creds(new); -+ return 0; -+} ++ struct aa_profile *profile; ++ int error = 0; + -+/* -+ * Do the actual cred switching of a changehat -+ * profile must be valid -+ */ -+int aa_set_current_hat(struct aa_profile *profile, u64 token) -+{ -+ struct aa_task_context *cxt; -+ struct cred *new = prepare_creds(); -+ if (!new) -+ return -ENOMEM; -+ -+ cxt = new->security; -+ if (!cxt->sys.previous) { -+ cxt->sys.previous = cxt->sys.profile; -+ cxt->sys.token = token; -+ } else if (cxt->sys.token == token) { -+ aa_put_profile(cxt->sys.profile); -+ } else { -+ /* previous_profile && cxt->token != token */ -+ abort_creds(new); -+ return -EACCES; -+ } -+ cxt->sys.profile = aa_get_profile(profile); -+ /* clear exec on switching context */ -+ aa_put_profile(cxt->sys.onexec); -+ cxt->sys.onexec = NULL; ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) ++ error = aa_pivotroot(profile, old_path, new_path); + -+ commit_creds(new); -+ return 0; ++ return error; +} + -+/* -+ * Attempt to return out of a hat to the previous profile -+ */ -+int aa_restore_previous_profile(u64 token) -+{ -+ struct aa_task_context *cxt; -+ struct cred *new = prepare_creds(); -+ if (!new) -+ return -ENOMEM; -+ -+ cxt = new->security; -+ if (cxt->sys.token != token) { -+ abort_creds(new); -+ return -EACCES; -+ } -+ /* ignore restores when there is no saved profile */ -+ if (!cxt->sys.previous) { -+ abort_creds(new); -+ return 0; -+ } -+ -+ aa_put_profile(cxt->sys.profile); -+ cxt->sys.profile = aa_profile_newest(cxt->sys.previous); -+ if (unlikely(cxt->sys.profile != cxt->sys.previous)) { -+ aa_get_profile(cxt->sys.profile); -+ aa_put_profile(cxt->sys.previous); -+ } -+ /* clear exec && prev information when restoring to previous context */ -+ cxt->sys.previous = NULL; -+ cxt->sys.token = 0; -+ aa_put_profile(cxt->sys.onexec); -+ cxt->sys.onexec = NULL; -+ -+ commit_creds(new); -+ return 0; -+} -diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c + static int apparmor_getprocattr(struct task_struct *task, char *name, + char **value) + { +@@ -722,6 +777,10 @@ static struct security_operations apparmor_ops = { + .capget = apparmor_capget, + .capable = apparmor_capable, + ++ .sb_mount = apparmor_sb_mount, ++ .sb_umount = apparmor_sb_umount, ++ .sb_pivotroot = apparmor_sb_pivotroot, ++ + .path_link = apparmor_path_link, + .path_unlink = apparmor_path_unlink, + .path_symlink = apparmor_path_symlink, +diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 -index 0000000..34f337c +index 0000000..478aa4d --- /dev/null -+++ b/security/apparmor/domain.c -@@ -0,0 +1,699 @@ ++++ b/security/apparmor/mount.c +@@ -0,0 +1,620 @@ +/* + * AppArmor security module + * -+ * This file contains AppArmor policy attachment and domain transitions ++ * This file contains AppArmor mediation of files + * -+ * Copyright (C) 2002-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. ++ * Copyright (C) 1998-2008 Novell/SUSE ++ * Copyright 2009-2012 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 @@ -1067,6303 +999,612 @@ index 0000000..34f337c + * License. + */ + -+#include -+#include -+#include ++#include +#include -+#include -+#include -+#include ++#include + ++#include "include/apparmor.h" +#include "include/audit.h" -+#include "include/apparmorfs.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" -+#include "include/ipc.h" +#include "include/match.h" ++#include "include/mount.h" +#include "include/path.h" +#include "include/policy.h" + ++ ++static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) ++{ ++ if (flags & MS_RDONLY) ++ audit_log_format(ab, "ro"); ++ else ++ audit_log_format(ab, "rw"); ++ if (flags & MS_NOSUID) ++ audit_log_format(ab, ", nosuid"); ++ if (flags & MS_NODEV) ++ audit_log_format(ab, ", nodev"); ++ if (flags & MS_NOEXEC) ++ audit_log_format(ab, ", noexec"); ++ if (flags & MS_SYNCHRONOUS) ++ audit_log_format(ab, ", sync"); ++ if (flags & MS_REMOUNT) ++ audit_log_format(ab, ", remount"); ++ if (flags & MS_MANDLOCK) ++ audit_log_format(ab, ", mand"); ++ if (flags & MS_DIRSYNC) ++ audit_log_format(ab, ", dirsync"); ++ if (flags & MS_NOATIME) ++ audit_log_format(ab, ", noatime"); ++ if (flags & MS_NODIRATIME) ++ audit_log_format(ab, ", nodiratime"); ++ if (flags & MS_BIND) ++ audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); ++ if (flags & MS_MOVE) ++ audit_log_format(ab, ", move"); ++ if (flags & MS_SILENT) ++ audit_log_format(ab, ", silent"); ++ if (flags & MS_POSIXACL) ++ audit_log_format(ab, ", acl"); ++ if (flags & MS_UNBINDABLE) ++ audit_log_format(ab, flags & MS_REC ? ", runbindable" : ++ ", unbindable"); ++ if (flags & MS_PRIVATE) ++ audit_log_format(ab, flags & MS_REC ? ", rprivate" : ++ ", private"); ++ if (flags & MS_SLAVE) ++ audit_log_format(ab, flags & MS_REC ? ", rslave" : ++ ", slave"); ++ if (flags & MS_SHARED) ++ audit_log_format(ab, flags & MS_REC ? ", rshared" : ++ ", shared"); ++ if (flags & MS_RELATIME) ++ audit_log_format(ab, ", relatime"); ++ if (flags & MS_I_VERSION) ++ audit_log_format(ab, ", iversion"); ++ if (flags & MS_STRICTATIME) ++ audit_log_format(ab, ", strictatime"); ++ if (flags & MS_NOUSER) ++ audit_log_format(ab, ", nouser"); ++} ++ +/** -+ * aa_free_domain_entries - free entries in a domain table -+ * @domain: the domain table to free ++ * audit_cb - call back for mount specific audit fields ++ * @ab: audit_buffer (NOT NULL) ++ * @va: audit struct to audit values of (NOT NULL) + */ -+void aa_free_domain_entries(struct aa_domain *domain) ++static void audit_cb(struct audit_buffer *ab, void *va) +{ -+ int i; -+ -+ if (!domain->table) -+ return; ++ struct common_audit_data *sa = va; + -+ for (i = 0; i < domain->size; i++) -+ kfree(domain->table[i]); -+ kfree(domain->table); ++ if (sa->aad->mnt.type) { ++ audit_log_format(ab, " fstype="); ++ audit_log_untrustedstring(ab, sa->aad->mnt.type); ++ } ++ if (sa->aad->mnt.src_name) { ++ audit_log_format(ab, " srcname="); ++ audit_log_untrustedstring(ab, sa->aad->mnt.src_name); ++ } ++ if (sa->aad->mnt.trans) { ++ audit_log_format(ab, " trans="); ++ audit_log_untrustedstring(ab, sa->aad->mnt.trans); ++ } ++ if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { ++ audit_log_format(ab, " flags=\""); ++ audit_mnt_flags(ab, sa->aad->mnt.flags); ++ audit_log_format(ab, "\""); ++ } ++ if (sa->aad->mnt.data) { ++ audit_log_format(ab, " options="); ++ audit_log_untrustedstring(ab, sa->aad->mnt.data); ++ } +} + -+/* -+ * check if the task is ptraced and if so if the tracing task is allowed -+ * to trace the new domain -+ */ -+static int aa_may_change_ptraced_domain(struct task_struct *task, -+ struct aa_profile *to_profile) -+{ -+ struct task_struct *tracer; -+ struct cred *cred = NULL; -+ struct aa_profile *tracerp = NULL; -+ int error = 0; ++/** ++ * audit_mount - handle the auditing of mount operations ++ * @profile: the profile being enforced (NOT NULL) ++ * @gfp: allocation flags ++ * @op: operation being mediated (NOT NULL) ++ * @name: name of object being mediated (MAYBE NULL) ++ * @src_name: src_name of object being mediated (MAYBE_NULL) ++ * @type: type of filesystem (MAYBE_NULL) ++ * @trans: name of trans (MAYBE NULL) ++ * @flags: filesystem idependent mount flags ++ * @data: filesystem mount flags ++ * @request: permissions requested ++ * @perms: the permissions computed for the request (NOT NULL) ++ * @info: extra information message (MAYBE NULL) ++ * @error: 0 if operation allowed else failure error code ++ * ++ * Returns: %0 or error on failure ++ */ ++static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, ++ const char *name, const char *src_name, ++ const char *type, const char *trans, ++ unsigned long flags, const void *data, u32 request, ++ struct file_perms *perms, const char *info, int error) ++{ ++ int audit_type = AUDIT_APPARMOR_AUTO; ++ struct common_audit_data sa = { }; ++ struct apparmor_audit_data aad = { }; ++ ++ if (likely(!error)) { ++ u32 mask = perms->audit; ++ ++ if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) ++ mask = 0xffff; ++ ++ /* mask off perms that are not being force audited */ ++ request &= mask; + -+ rcu_read_lock(); -+ tracer = tracehook_tracer_task(task); -+ if (tracer) -+ cred = aa_get_task_policy(tracer, &tracerp); -+ rcu_read_unlock(); ++ if (likely(!request)) ++ return 0; ++ audit_type = AUDIT_APPARMOR_AUDIT; ++ } else { ++ /* only report permissions that were denied */ ++ request = request & ~perms->allow; + -+ if (!tracerp) -+ return error; ++ if (request & perms->kill) ++ audit_type = AUDIT_APPARMOR_KILL; + -+ error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH); -+ put_cred(cred); ++ /* quiet known rejects, assumes quiet and kill do not overlap */ ++ if ((request & perms->quiet) && ++ AUDIT_MODE(profile) != AUDIT_NOQUIET && ++ AUDIT_MODE(profile) != AUDIT_ALL) ++ request &= ~perms->quiet; + -+ return error; ++ if (!request) ++ return COMPLAIN_MODE(profile) ? ++ complain_error(error) : error; ++ } ++ ++ sa.type = LSM_AUDIT_DATA_NONE; ++ sa.aad = &aad; ++ sa.aad->op = op; ++ sa.aad->name = name; ++ sa.aad->mnt.src_name = src_name; ++ sa.aad->mnt.type = type; ++ sa.aad->mnt.trans = trans; ++ sa.aad->mnt.flags = flags; ++ if (data && (perms->audit & AA_AUDIT_DATA)) ++ sa.aad->mnt.data = data; ++ sa.aad->info = info; ++ sa.aad->error = error; ++ ++ return aa_audit(audit_type, profile, gfp, &sa, audit_cb); +} + +/** -+ * change_profile_perms ++ * match_mnt_flags - Do an ordered match on mount flags ++ * @dfa: dfa to match against ++ * @state: state to start in ++ * @flags: mount flags to match against ++ * ++ * Mount flags are encoded as an ordered match. This is done instead of ++ * checking against a simple bitmask, to allow for logical operations ++ * on the flags. ++ * ++ * Returns: next state after flags match + */ -+static struct file_perms change_profile_perms(struct aa_profile *profile, -+ struct aa_namespace *ns, -+ const char *name, -+ unsigned int *rstate) ++static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, ++ unsigned long flags) +{ -+ struct file_perms perms; -+ struct path_cond cond = { 0, 0 }; -+ unsigned int state; ++ unsigned int i; + -+ if (!profile) { -+ /* unconfined */ -+ perms.allowed = AA_MAY_CHANGE_PROFILE; -+ perms.xindex = perms.dindex = 0; -+ perms.audit = perms.quiet = perms.kill = 0; -+ *rstate = 0; -+ return perms; -+ } else if ((ns == profile->ns)) { -+ /* try matching against rules with out namespace prependend */ -+ perms = aa_str_perms(profile->file.dfa, DFA_START, name, &cond, -+ rstate); -+ if (COMBINED_PERM_MASK(perms) & AA_MAY_CHANGE_PROFILE) -+ return perms; ++ for (i = 0; i <= 31 ; ++i) { ++ if ((1 << i) & flags) ++ state = aa_dfa_next(dfa, state, i + 1); + } + -+ /* try matching with namespace name and then profile */ -+ if (!profile->file.dfa) -+ return nullperms; -+ -+ state = aa_dfa_match(profile->file.dfa, DFA_START, ns->base.name); -+ state = aa_dfa_null_transition(profile->file.dfa, state); -+ return aa_str_perms(profile->file.dfa, state, name, &cond, rstate); ++ return state; +} + -+/* -+ * TODO: fix parser to detect unconfined, inherit, -+ * check for next name in list of names that is double null terminated -+ * The names list is a set of strings that \0 seperated with a double -+ * \0 terminating the list -+ * names that belong to namespaces begin with a : -+ * and are followed by a name a \0 seperated name. If the name is -+ * unspecified it is 0 length. This double \0\0 does not count as -+ * the end of the list ++/** ++ * compute_mnt_perms - compute mount permission associated with @state ++ * @dfa: dfa to match against (NOT NULL) ++ * @state: state match finished in + * -+ * profile\0\0 # single profile -+ * profile\0profile\0\0 # 2 profiles in list -+ * :namespace\0profile\0\0 # profile & namespace -+ * :namespace\0\0\0 # namespace without profile -+ * :namespace\0\0profile\0\0 # namespace without profile followed by profile -+*/ -+static const char *next_name(int xtype, const char *name) ++ * Returns: mount permissions ++ */ ++static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, ++ unsigned int state) +{ -+/* TODO: fix parser and enable -+ if (xtype == AA_X_TABLE) { -+ name = name + strlen(name) + 1; -+ if (*name != 0) -+ return name; -+ } -+*/ -+ return NULL; ++ struct file_perms perms; ++ ++ perms.kill = 0; ++ perms.allow = dfa_user_allow(dfa, state); ++ perms.audit = dfa_user_audit(dfa, state); ++ perms.quiet = dfa_user_quiet(dfa, state); ++ perms.xindex = dfa_user_xindex(dfa, state); ++ ++ return perms; +} + ++static const char const *mnt_info_table[] = { ++ "match succeeded", ++ "failed mntpnt match", ++ "failed srcname match", ++ "failed type match", ++ "failed flags match", ++ "failed data match" ++}; ++ +/* -+ * get target profile for xindex ++ * Returns 0 on success else element that match failed in, this is the ++ * index into the mnt_info_table above + */ -+static struct aa_profile *x_to_profile(struct aa_namespace *ns, -+ struct aa_profile *profile, -+ const char *name, u16 xindex) -+ ++static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, ++ const char *mntpnt, const char *devname, ++ const char *type, unsigned long flags, ++ void *data, bool binary, struct file_perms *perms) +{ -+ struct aa_profile *new_profile = NULL; -+ u16 xtype = xindex & AA_X_TYPE_MASK; -+ int index = xindex & AA_X_INDEX_MASK; -+ -+ if (!profile) -+ profile = ns->unconfined; -+ -+ switch(xtype) { -+ case AA_X_NONE: -+ /* fail exec unless ix || ux fallback - handled by caller */ -+ return ERR_PTR(-EACCES); -+ case AA_X_NAME: -+ break; -+ case AA_X_TABLE: -+ if (index > profile->file.trans.size) { -+ AA_ERROR("Invalid named transition\n"); -+ return ERR_PTR(-EACCES); -+ } -+ name = profile->file.trans.table[index]; -+ break; ++ unsigned int state; ++ ++ state = aa_dfa_match(dfa, start, mntpnt); ++ state = aa_dfa_null_transition(dfa, state); ++ if (!state) ++ return 1; ++ ++ if (devname) ++ state = aa_dfa_match(dfa, state, devname); ++ state = aa_dfa_null_transition(dfa, state); ++ if (!state) ++ return 2; ++ ++ if (type) ++ state = aa_dfa_match(dfa, state, type); ++ state = aa_dfa_null_transition(dfa, state); ++ if (!state) ++ return 3; ++ ++ state = match_mnt_flags(dfa, state, flags); ++ if (!state) ++ return 4; ++ *perms = compute_mnt_perms(dfa, state); ++ if (perms->allow & AA_MAY_MOUNT) ++ return 0; ++ ++ /* only match data if not binary and the DFA flags data is expected */ ++ if (data && !binary && (perms->allow & AA_CONT_MATCH)) { ++ state = aa_dfa_null_transition(dfa, state); ++ if (!state) ++ return 4; ++ ++ state = aa_dfa_match(dfa, state, data); ++ if (!state) ++ return 5; ++ *perms = compute_mnt_perms(dfa, state); ++ if (perms->allow & AA_MAY_MOUNT) ++ return 0; + } + -+ for (; !new_profile && name; name = next_name(xtype, name)) { -+ struct aa_namespace *new_ns; -+ const char *xname = NULL; -+ -+ new_ns = NULL; -+ if (xindex & AA_X_CHILD) { -+ new_profile = aa_find_child(profile, name); -+ if (new_profile) -+ return new_profile; -+ continue; -+ } else if (*name == ':') { -+ /* switching namespace */ -+ const char *ns_name = name + 1; -+ name = xname = ns_name + strlen(ns_name) + 1; -+ if (!*xname) -+ /* no name so use profile name */ -+ xname = profile->fqname; -+ if (*ns_name == '@') { -+ /* TODO: variable support */ -+ ; -+ } -+ new_ns = aa_find_namespace(ns_name); -+ if (!new_ns) -+ continue; -+ } else if (*name == '@') { -+ /* TODO: variable support */ ++ /* failed at end of flags match */ ++ return 4; ++} + -+ } else { -+ xname = name; -+ } ++/** ++ * match_mnt - handle path matching for mount ++ * @profile: the confining profile ++ * @mntpnt: string for the mntpnt (NOT NULL) ++ * @devname: string for the devname/src_name (MAYBE NULL) ++ * @type: string for the dev type (MAYBE NULL) ++ * @flags: mount flags to match ++ * @data: fs mount data (MAYBE NULL) ++ * @binary: whether @data is binary ++ * @perms: Returns: permission found by the match ++ * @info: Returns: infomation string about the match for logging ++ * ++ * Returns: 0 on success else error ++ */ ++static int match_mnt(struct aa_profile *profile, const char *mntpnt, ++ const char *devname, const char *type, ++ unsigned long flags, void *data, bool binary, ++ struct file_perms *perms, const char **info) ++{ ++ int pos; ++ ++ if (!profile->policy.dfa) ++ return -EACCES; + -+ new_profile = aa_find_profile_by_fqname(new_ns ? new_ns : ns, -+ xname); -+ aa_put_namespace(new_ns); ++ pos = do_match_mnt(profile->policy.dfa, ++ profile->policy.start[AA_CLASS_MOUNT], ++ mntpnt, devname, type, flags, data, binary, perms); ++ if (pos) { ++ *info = mnt_info_table[pos]; ++ return -EACCES; + } + -+ if (!new_profile) -+ return ERR_PTR(-ENOENT); ++ return 0; ++} + -+ return new_profile; ++static int path_flags(struct aa_profile *profile, struct path *path) ++{ ++ return profile->path_flags | ++ S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; +} + -+int apparmor_bprm_set_creds(struct linux_binprm *bprm) ++int aa_remount(struct aa_profile *profile, struct path *path, ++ unsigned long flags, void *data) +{ -+ struct aa_task_context *cxt; -+ struct aa_profile *profile, *new_profile = NULL; -+ struct aa_namespace *ns; ++ struct file_perms perms = { }; ++ const char *name, *info = NULL; + char *buffer = NULL; -+ unsigned int state = DFA_START; -+ struct aa_audit_file sa; -+ struct path_cond cond = { bprm->file->f_path.dentry->d_inode->i_uid, -+ bprm->file->f_path.dentry->d_inode->i_mode }; -+ -+ sa.base.error = cap_bprm_set_creds(bprm); -+ if (sa.base.error) -+ return sa.base.error; ++ int binary, error; + -+ if (bprm->cred_prepared) -+ return 0; ++ binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; + -+ memset(&sa, 0, sizeof(sa)); -+ sa.base.operation = "exec"; -+ sa.base.gfp_mask = GFP_KERNEL; -+ sa.request = MAY_EXEC; -+ sa.cond = &cond; -+ -+ cxt = bprm->cred->security; -+ BUG_ON(!cxt); -+ -+ profile = aa_filtered_profile(aa_profile_newest(cxt->sys.profile)); -+ ns = cxt->sys.profile->ns; -+ -+ sa.base.error = aa_get_name(&bprm->file->f_path, 0, &buffer, -+ (char **) &sa.name); -+ if (sa.base.error) { -+ if (profile || profile->flags & PFLAG_IX_ON_NAME_ERROR) -+ sa.base.error = 0; -+ sa.base.info = "Exec failed name resolution"; -+ sa.name = bprm->filename; ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) + goto audit; -+ } + -+ if (!profile) { -+ /* unconfined task - attach profile if one matches */ -+ new_profile = aa_sys_find_attach(ns, sa.name); -+ if (!new_profile) -+ goto cleanup; -+ goto apply; -+ } else if (cxt->sys.onexec) { -+ /* -+ * onexec permissions are stored in a pair, rewalk the -+ * dfa to get start of the exec path match. -+ */ -+ sa.perms = change_profile_perms(profile, cxt->sys.onexec->ns, -+ sa.name, &state); -+ state = aa_dfa_null_transition(profile->file.dfa, state); -+ } -+ sa.perms = aa_str_perms(profile->file.dfa, state, sa.name, &cond, -+ NULL); -+ if (cxt->sys.onexec && sa.perms.allowed & AA_MAY_ONEXEC) { -+ new_profile = cxt->sys.onexec; -+ cxt->sys.onexec = NULL; -+ sa.base.info = "change_profile onexec"; -+ } else if (sa.perms.allowed & MAY_EXEC) { -+ new_profile = x_to_profile(ns, profile, sa.name, -+ sa.perms.xindex); -+ if (IS_ERR(new_profile)) { -+ if (sa.perms.xindex & AA_X_INHERIT) { -+ /* (p|c|n)ix - don't change profile */ -+ sa.base.info = "ix fallback"; -+ goto x_clear; -+ } else if (sa.perms.xindex & AA_X_UNCONFINED) { -+ new_profile = aa_get_profile(ns->unconfined); -+ sa.base.info = "ux fallback"; -+ } else { -+ sa.base.error = PTR_ERR(new_profile); -+ if (sa.base.error == -ENOENT) -+ sa.base.info = "profile not found"; -+ new_profile = NULL; -+ } -+ } -+ } else if (PROFILE_COMPLAIN(profile)) { -+ new_profile = aa_alloc_null_profile(profile, 0); -+ sa.base.error = -EACCES; -+ if (!new_profile) -+ sa.base.error = -ENOMEM; -+ sa.name2 = new_profile->fqname; -+ sa.perms.xindex |= AA_X_UNSAFE; -+ } else { -+ sa.base.error = -EACCES; -+ } ++ error = match_mnt(profile, name, NULL, NULL, flags, data, binary, ++ &perms, &info); + -+ if (!new_profile) -+ goto audit; ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, ++ NULL, flags, data, AA_MAY_MOUNT, &perms, info, ++ error); ++ kfree(buffer); + -+ if (profile == new_profile) { -+ aa_put_profile(new_profile); -+ goto audit; -+ } ++ return error; ++} + -+ if (bprm->unsafe & LSM_UNSAFE_SHARE) { -+ /* FIXME: currently don't mediate shared state */ -+ ; -+ } ++int aa_bind_mount(struct aa_profile *profile, struct path *path, ++ const char *dev_name, unsigned long flags) ++{ ++ struct file_perms perms = { }; ++ char *buffer = NULL, *old_buffer = NULL; ++ const char *name, *old_name = NULL, *info = NULL; ++ struct path old_path; ++ int error; + -+ if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { -+ sa.base.error = aa_may_change_ptraced_domain(current, -+ new_profile); -+ if (sa.base.error) -+ goto audit; -+ } ++ if (!dev_name || !*dev_name) ++ return -EINVAL; + -+ /* Determine if secure exec is needed. -+ * Can be at this point for the following reasons: -+ * 1. unconfined switching to confined -+ * 2. confined switching to different confinement -+ * 3. confined switching to unconfined -+ * -+ * Cases 2 and 3 are marked as requiring secure exec -+ * (unless policy specified "unsafe exec") -+ * -+ * bprm->unsafe is used to cache the AA_X_UNSAFE permission -+ * to avoid having to recompute in secureexec -+ */ -+ if (!(sa.perms.xindex & AA_X_UNSAFE)) -+ bprm->unsafe |= AA_SECURE_X_NEEDED; ++ flags &= MS_REC | MS_BIND; + -+apply: -+ sa.name2 = new_profile->fqname; -+ /* When switching namespace ensure its part of audit message */ -+ if (new_profile->ns != profile->ns) -+ sa.name3 = new_profile->ns->base.name; ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; + -+ /* when transitioning profiles clear unsafe personality bits */ -+ bprm->per_clear |= PER_CLEAR_ON_SETID; ++ error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); ++ if (error) ++ goto audit; + -+ aa_put_profile(cxt->sys.profile); -+ cxt->sys.profile = new_profile; ++ error = aa_path_name(&old_path, path_flags(profile, &old_path), ++ &old_buffer, &old_name, &info); ++ path_put(&old_path); ++ if (error) ++ goto audit; + -+x_clear: -+ aa_put_profile(cxt->sys.previous); -+ aa_put_profile(cxt->sys.onexec); -+ cxt->sys.previous = NULL; -+ cxt->sys.onexec = NULL; -+ cxt->sys.token = 0; ++ error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, ++ &perms, &info); + +audit: -+ sa.base.error = aa_audit_file(profile, &sa); -+ -+cleanup: ++ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, ++ NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, ++ info, error); + kfree(buffer); ++ kfree(old_buffer); + -+ return sa.base.error; ++ return error; +} + -+int apparmor_bprm_secureexec(struct linux_binprm *bprm) ++int aa_mount_change_type(struct aa_profile *profile, struct path *path, ++ unsigned long flags) +{ -+ int ret = cap_bprm_secureexec(bprm); ++ struct file_perms perms = { }; ++ char *buffer = NULL; ++ const char *name, *info = NULL; ++ int error; + -+ /* the decision to use secure exec is computed in set_creds -+ * and stored in bprm->unsafe. The AppArmor X_UNSAFE flag is -+ * indicates don't -+ */ -+ if (!ret && (bprm->unsafe & AA_SECURE_X_NEEDED)) -+ ret = 1; ++ /* These are the flags allowed by do_change_type() */ ++ flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | ++ MS_UNBINDABLE); + -+ return ret; -+} ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; + ++ error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, ++ &info); + -+static int aa_revalidate_perm(struct aa_profile *profile, struct file *file, -+ char *buffer, int size) -+{ -+ umode_t mode = file->f_path.dentry->d_inode->i_mode; -+ char *name; -+ int error; ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, ++ NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, ++ error); ++ kfree(buffer); + -+ error = aa_get_name_to_buffer(&file->f_path, S_ISDIR(mode), buffer, -+ size, &name); -+ return aa_file_common_perm(profile, "file_inherit", file, -+ aa_map_file_to_perms(file), name, -+ error); ++ return error; +} + -+static void revalidate_file(struct aa_profile *profile, struct file *file, -+ unsigned long i, char *buffer, int size, -+ struct cred *cred) ++int aa_move_mount(struct aa_profile *profile, struct path *path, ++ const char *orig_name) +{ -+ if (aa_revalidate_perm(profile, file, buffer, size)) { -+ struct file *devnull = NULL; -+ int fd = get_unused_fd(); -+ sys_close(i); -+ if (fd != i) { -+ if (fd >= 0) -+ put_unused_fd(fd); -+ return; -+ } -+ if (devnull) { -+ get_file(devnull); -+ } else if (apparmorfs_null) { -+ devnull = dentry_open(dget(apparmorfs_null), -+ mntget(apparmorfs_mnt), -+ O_RDWR, cred); -+ if (IS_ERR(devnull)) { -+ devnull = NULL; -+ put_unused_fd(fd); -+ return; -+ } -+ } else { -+ /* apparmorfs_null not setup */ -+ put_unused_fd(fd); -+ return; -+ } -+ fd_install(fd, devnull); -+ } -+} -+ -+/* -+ * derived from security/selinux/hooks.c: flush_unauthorized_files && -+ * fs/exec.c:flush_old_files -+ */ -+static int revalidate_files(struct aa_profile *profile, -+ struct files_struct *files, gfp_t gfp, -+ struct cred *cred) -+{ -+ struct file *file; -+ struct fdtable *fdt; -+ long j = -1; -+ char *buffer = kmalloc(g_apparmor_path_max, gfp); -+ if (!buffer) -+ return -ENOMEM; -+ -+ spin_lock(&files->file_lock); -+ for (;;) { -+ unsigned long set, i; -+ -+ j++; -+ i = j * __NFDBITS; -+ fdt = files_fdtable(files); -+ if (i >= fdt->max_fds) -+ break; -+ set = fdt->open_fds->fds_bits[j]; -+ if (!set) -+ continue; -+ spin_unlock(&files->file_lock); -+ for ( ; set ; i++,set >>= 1) { -+ if (set & 1) { -+ file = fget(i); -+ if (!file) -+ continue; -+ revalidate_file(profile, file, i, buffer, -+ g_apparmor_path_max, cred); -+ fput(file); -+ } -+ } -+ spin_lock(&files->file_lock); -+ } -+ spin_unlock(&files->file_lock); -+ kfree(buffer); -+ return 0; -+} -+ -+int apparmor_bprm_committing_creds(struct linux_binprm *bprm) -+{ -+ struct aa_profile *profile; -+ struct cred *cred = aa_get_task_policy(current, &profile); -+ struct aa_task_context *new_cxt = bprm->cred->security; ++ struct file_perms perms = { }; ++ char *buffer = NULL, *old_buffer = NULL; ++ const char *name, *old_name = NULL, *info = NULL; ++ struct path old_path; + int error; + -+ if ((new_cxt->sys.profile == profile) || -+ (new_cxt->sys.profile->flags & PFLAG_UNCONFINED)) { -+ put_cred(cred); -+ return 0; -+ } -+ put_cred(cred); -+ -+ error = revalidate_files(new_cxt->sys.profile, current->files, -+ GFP_KERNEL, bprm->cred); -+ if (error) -+ return error; -+ -+ current->pdeath_signal = 0; -+ -+ /* reset soft limits and set hard limits for the new profile */ -+ __aa_transition_rlimits(profile, new_cxt->sys.profile); -+ return 0; -+} -+ -+void apparmor_bprm_committed_creds(struct linux_binprm *bprm) -+{ -+ /* TODO: cleanup signals - ipc mediation */ -+ return; -+} -+ -+/** -+ * aa_change_hat - change hat to/from subprofile -+ * @hat_name: hat to change to -+ * @token: magic value to validate the hat change -+ * @permtest: true if this is just a permission test -+ * -+ * Change to new @hat_name, and store the @hat_magic in the current task -+ * context. If the new @hat_name is %NULL and the @token matches that -+ * stored in the current task context and is not 0, return to the top level -+ * profile. -+ * Returns %0 on success, error otherwise. -+ */ -+int aa_change_hat(const char *hat_name, u64 token, int permtest) -+{ -+ const struct cred *cred; -+ struct aa_task_context *cxt; -+ struct aa_profile *profile, *previous_profile, *hat = NULL; -+ struct aa_audit_file sa; -+ -+ memset(&sa, 0, sizeof(sa)); -+ sa.base.gfp_mask = GFP_KERNEL; -+ sa.base.operation = "change_hat"; -+ -+ cred = aa_current_policy(&profile); -+ cxt = cred->security; -+ previous_profile = cxt->sys.previous; -+ token = cxt->sys.token; -+ -+ if (!profile) { -+ sa.base.info = "unconfined"; -+ sa.base.error = -EPERM; -+ goto audit; -+ } -+ -+ if (hat_name) { -+ if (previous_profile) -+ sa.name = previous_profile->fqname; -+ else -+ sa.name = profile->fqname; -+ -+ sa.name2 = profile->ns->base.name; -+ -+ if (PROFILE_IS_HAT(profile)) -+ hat = aa_find_child(profile->parent, hat_name); -+ else -+ hat = aa_find_child(profile, hat_name); -+ if (!hat) { -+ sa.base.info = "hat not found"; -+ sa.base.error = -ENOENT; -+ if (permtest || !PROFILE_COMPLAIN(profile)) -+ goto audit; -+ hat = aa_alloc_null_profile(profile, 1); -+ if (!hat) { -+ sa.base.info = "failed null profile create"; -+ sa.base.error = -ENOMEM; -+ goto audit; -+ } -+ } else if (!PROFILE_IS_HAT(hat)) { -+ sa.base.info = "target not hat"; -+ sa.base.error = -EPERM; -+ goto audit; -+ } -+ -+ sa.base.error = aa_may_change_ptraced_domain(current, hat); -+ if (sa.base.error) { -+ sa.base.info = "ptraced"; -+ sa.base.error = -EPERM; -+ goto audit; -+ } -+ -+ if (!permtest) { -+ sa.base.error = aa_set_current_hat(hat, token); -+ if (sa.base.error == -EACCES) { -+ (void)send_sig_info(SIGKILL, NULL, current); -+ sa.base.error = aa_audit(AUDIT_APPARMOR_KILL, -+ profile, &sa.base, -+ file_audit_cb); -+ goto out; -+ } -+ } -+ } else if (previous_profile) -+ sa.base.error = aa_restore_previous_profile(token); -+ /* else -+ ignore restores when there is no saved profile -+ */ -+ -+audit: -+ if (!permtest) -+ sa.base.error = aa_audit_file(profile, &sa); -+ -+ -+out: -+ aa_put_profile(hat); -+ -+ return sa.base.error; -+} -+ -+/** -+ * aa_change_profile - perform a one-way profile transition -+ * @ns_name: name of the profile namespace to change to -+ * @fqname: name of profile to change to -+ * @onexec: whether this transition is to take place immediately or at exec -+ * @permtest: true if this is just a permission test -+ * -+ * Change to new profile @name. Unlike with hats, there is no way -+ * to change back. If @onexec then the transition is delayed until -+ * the next exec. -+ * -+ * Returns %0 on success, error otherwise. -+ */ -+int aa_change_profile(const char *ns_name, const char *fqname, int onexec, -+ int permtest) -+{ -+ const struct cred *cred; -+ struct aa_task_context *cxt; -+ struct aa_profile *profile, *target = NULL; -+ struct aa_namespace *ns = NULL; -+ struct aa_audit_file sa; -+ char *name = NULL; -+ -+ if (!name && !ns_name) ++ if (!orig_name || !*orig_name) + return -EINVAL; + -+ memset(&sa, 0, sizeof(sa)); -+ sa.base.gfp_mask = GFP_KERNEL; -+ if (onexec) -+ sa.base.operation = "change_onexec"; -+ else -+ sa.base.operation = "change_profile"; -+ -+ cred = aa_current_policy(&profile); -+ cxt = cred->security; -+ ns = aa_get_namespace(cxt->sys.profile->ns); -+ -+ if (ns_name) { -+ sa.name2 = ns_name; -+ aa_put_namespace(ns); -+ ns = aa_find_namespace(ns_name); -+ if (!ns) { -+ /* we don't create new namespace in complain mode */ -+ sa.base.info = "namespace not found"; -+ sa.base.error = -ENOENT; -+ goto audit; -+ } -+ } else -+ sa.name2 = ns->base.name; -+ -+ /* if the name was not specified, use the name of the current profile */ -+ if (!fqname) { -+ if (!profile) -+ fqname = ns->unconfined->fqname; -+ else -+ fqname = profile->fqname; -+ } -+ sa.name = fqname; -+ -+ sa.perms = change_profile_perms(profile, ns, fqname, NULL); -+ -+ if (!(sa.perms.allowed & AA_MAY_CHANGE_PROFILE)) { -+ sa.base.error = -EACCES; ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) + goto audit; -+ } + -+ target = aa_find_profile_by_fqname(ns, fqname); -+ if (!target) { -+ sa.base.info = "profile not found"; -+ sa.base.error = -ENOENT; -+ if (permtest || !PROFILE_COMPLAIN(profile)) -+ goto audit; -+ target = aa_alloc_null_profile(profile, 0); -+ } -+ -+ /* check if tracing task is allowed to trace target domain */ -+ sa.base.error = aa_may_change_ptraced_domain(current, target); -+ if (sa.base.error) { -+ sa.base.info = "ptrace prevents transition"; ++ error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); ++ if (error) + goto audit; -+ } + -+ if (permtest) ++ error = aa_path_name(&old_path, path_flags(profile, &old_path), ++ &old_buffer, &old_name, &info); ++ path_put(&old_path); ++ if (error) + goto audit; + -+ if (onexec) -+ sa.base.error = aa_set_current_onexec(target); -+ else -+ sa.base.error = aa_replace_current_profiles(target); ++ error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, ++ &perms, &info); + +audit: -+ if (!permtest) -+ sa.base.error = aa_audit_file(profile, &sa); -+ -+ aa_put_namespace(ns); -+ aa_put_profile(target); -+ -+ return sa.base.error; -+} -diff --git a/security/apparmor/file.c b/security/apparmor/file.c -new file mode 100644 -index 0000000..fdade01 ---- /dev/null -+++ b/security/apparmor/file.c -@@ -0,0 +1,425 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor mediation of files -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ */ -+ -+#include "include/apparmor.h" -+#include "include/audit.h" -+#include "include/file.h" -+#include "include/match.h" -+#include "include/path.h" -+#include "include/policy.h" -+ -+struct file_perms nullperms; -+ -+static void aa_audit_file_sub_mask(struct audit_buffer *ab, char *buffer, -+ u16 mask, u16 xindex) -+{ -+ -+ /* const char xchar[] = "PpCc";*/ -+ -+ char *m = buffer; -+ -+ if (mask & AA_EXEC_MMAP) -+ *m++ = 'm'; -+ if (mask & MAY_READ) -+ *m++ = 'r'; -+ if (mask & (MAY_WRITE | AA_MAY_CREATE)) -+ *m++ = 'w'; -+ else if (mask & MAY_APPEND) -+ *m++ = 'a'; -+ if (mask & AA_MAY_LINK) -+ *m++ = 'l'; -+ if (mask & AA_MAY_LOCK) -+ *m++ = 'k'; -+ if (mask & MAY_EXEC) { -+ *m++ = 'x'; -+ -+/* FIXME: only want more advanced auditing of x if in audit/hint mode -+ u16 index = xindex & AA_X_INDEX_MASK; -+ u16 xtype = xindex & AA_X_TYPE_MASK; -+ if (xtype > AA_X_NONE) -+ *m++ = xchar[(xindex >> 12) & 0x3]; -+ if (xindex & AA_X_INHERIT) { -+ *m++ = 'i'; -+ } else if (xindex & AA_X_UNCONFINED) { -+ if (xindex & AA_X_UNSAFE) -+ *m++ = 'u'; -+ else -+ *m++ = 'U'; -+ } -+ *m++ = 'x'; -+ / * at most 7 character including trailing \0 * / -+ if (xtype == AA_X_VARIABLE) { -+ m += sprintf(m, "->v%x", index); -+ } else if (xtype == AA_X_TABLE) { -+ m += sprintf(m, "->n%x", index); -+ } -+*/ -+ } -+ *m++ = '\0'; -+} -+ -+static void aa_audit_file_mask(struct audit_buffer *ab, const char *name, -+ u16 mask, int xindex, int owner) -+{ -+/* char str[18]; */ -+ char str[10]; ++ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, ++ NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, ++ info, error); ++ kfree(buffer); ++ kfree(old_buffer); + -+ aa_audit_file_sub_mask(ab, str, mask, xindex); -+ if (owner) -+ audit_log_format(ab, " %s=\"%s::\"", name, str); -+ else -+ audit_log_format(ab, " %s=\"::%s\"", name, str); ++ return error; +} + -+void file_audit_cb(struct audit_buffer *ab, void *va) ++int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, ++ struct path *path, const char *type, unsigned long flags, ++ void *data) +{ -+ struct aa_audit_file *sa = va; -+ u16 denied = sa->request & ~sa->perms.allowed; -+ uid_t fsuid; -+ -+ if (sa->base.task) -+ fsuid = task_uid(sa->base.task); -+ else -+ fsuid = current_fsuid(); ++ struct file_perms perms = { }; ++ char *buffer = NULL, *dev_buffer = NULL; ++ const char *name = NULL, *dev_name = NULL, *info = NULL; ++ int binary = 1; ++ int error; + -+ if (sa->request & AA_AUDIT_FILE_MASK) -+ aa_audit_file_mask(ab, "requested_mask", sa->request, -+ AA_X_NONE, fsuid == sa->cond->uid); ++ dev_name = orig_dev_name; ++ if (type) { ++ int requires_dev; ++ struct file_system_type *fstype = get_fs_type(type); ++ if (!fstype) ++ return -ENODEV; + -+ if (denied & AA_AUDIT_FILE_MASK) -+ aa_audit_file_mask(ab, "denied_mask", denied, sa->perms.xindex, -+ fsuid == sa->cond->uid); ++ binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; ++ requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; ++ put_filesystem(fstype); + -+ if (sa->request & AA_AUDIT_FILE_MASK) { -+ audit_log_format(ab, " fsuid=%d", fsuid); -+ audit_log_format(ab, " ouid=%d", sa->cond->uid); -+ } ++ if (requires_dev) { ++ struct path dev_path; + -+ if (sa->name) { -+ audit_log_format(ab, " name="); -+ audit_log_untrustedstring(ab, sa->name); -+ } ++ if (!dev_name || !*dev_name) { ++ error = -ENOENT; ++ goto out; ++ } + -+ if (sa->name2) { -+ audit_log_format(ab, " name2="); -+ audit_log_untrustedstring(ab, sa->name2); -+ } ++ error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); ++ if (error) ++ goto audit; + -+ if (sa->name3) { -+ audit_log_format(ab, " name3="); -+ audit_log_untrustedstring(ab, sa->name3); ++ error = aa_path_name(&dev_path, ++ path_flags(profile, &dev_path), ++ &dev_buffer, &dev_name, &info); ++ path_put(&dev_path); ++ if (error) ++ goto audit; ++ } + } -+} -+ -+int aa_audit_file(struct aa_profile *profile, struct aa_audit_file *sa) -+{ -+ int type = AUDIT_APPARMOR_AUTO; -+ -+ if (likely(!sa->base.error)) { -+ u16 mask = sa->perms.audit; + -+ if (unlikely(PROFILE_AUDIT_MODE(profile) == AUDIT_ALL)) -+ mask = 0xffff; -+ -+ /* mask off perms that are not being force audited */ -+ sa->request &= mask; ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; + -+ if (likely(!sa->request)) -+ return 0; -+ } else { -+ /* quiet auditing of specific known rejects */ -+ u16 mask = sa->perms.quiet; -+ u16 denied = sa->request & ~sa->perms.allowed; ++ error = match_mnt(profile, name, dev_name, type, flags, data, binary, ++ &perms, &info); + -+ if (denied & sa->perms.kill) -+ type = AUDIT_APPARMOR_KILL; ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, ++ type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, ++ error); ++ kfree(buffer); ++ kfree(dev_buffer); + -+ /* assumes quiet and kill do not overlap */ -+ if ((denied & mask) && -+ PROFILE_AUDIT_MODE(profile) != AUDIT_NOQUIET && -+ PROFILE_AUDIT_MODE(profile) != AUDIT_ALL) -+ sa->request &= ~mask; ++out: ++ return error; + -+ if (!sa->request) -+ return PROFILE_COMPLAIN(profile) ? 0 : sa->base.error; -+ } -+ return aa_audit(type, profile, (struct aa_audit *)sa, file_audit_cb); +} + -+/* FIXME: convert from dfa + state to permission entry */ -+struct file_perms aa_compute_perms(struct aa_dfa *dfa, unsigned int state, -+ struct path_cond *cond) ++int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) +{ -+ struct file_perms perms; ++ struct file_perms perms = { }; ++ char *buffer = NULL; ++ const char *name, *info = NULL; ++ int error; + -+ /* FIXME: change over to new dfa format */ -+ /* currently file perms are encoded in the dfa */ -+ perms.kill = 0; -+ perms.dindex = 0; ++ struct path path = { mnt, mnt->mnt_root }; ++ error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; + -+ if (current_fsuid() == cond->uid) { -+ perms.allowed = dfa_user_allow(dfa, state); -+ perms.audit = dfa_user_audit(dfa, state); -+ perms.quiet = dfa_user_quiet(dfa, state); -+ perms.xindex = dfa_user_xindex(dfa, state); -+ } else { -+ perms.allowed = dfa_other_allow(dfa, state); -+ perms.audit = dfa_other_audit(dfa, state); -+ perms.quiet = dfa_other_quiet(dfa, state); -+ perms.xindex = dfa_other_xindex(dfa, state); ++ if (!error && profile->policy.dfa) { ++ unsigned int state; ++ state = aa_dfa_match(profile->policy.dfa, ++ profile->policy.start[AA_CLASS_MOUNT], ++ name); ++ perms = compute_mnt_perms(profile->policy.dfa, state); + } -+ /* in the old mapping MAY_WRITE implies AA_MAY_CREATE */ -+ perms.allowed |= (perms.allowed & MAY_WRITE) << 6; -+ perms.audit |= (perms.audit & MAY_WRITE) << 6; -+ perms.quiet |= (perms.quiet & MAY_WRITE) << 6; -+ -+ /* in the old mapping AA_MAY_LOCK and link subset are overlayed -+ * and only determined by which part of a pair they are in -+ */ -+ if (perms.allowed & AA_MAY_LOCK) -+ perms.allowed |= AA_LINK_SUBSET; -+ -+ /* change_profile wasn't determined by ownership in old mapping */ -+ if (ACCEPT_TABLE2(dfa)[state] & 0x80000000) -+ perms.allowed |= AA_MAY_CHANGE_PROFILE; -+ -+ return perms; -+} -+ -+struct file_perms aa_str_perms(struct aa_dfa *dfa, unsigned int start, -+ const char *name, struct path_cond *cond, -+ unsigned int *rstate) -+{ -+ unsigned int state; -+ if (!dfa) -+ return nullperms; + -+ state = aa_dfa_match(dfa, start, name); ++ if (AA_MAY_UMOUNT & ~perms.allow) ++ error = -EACCES; + -+ if (rstate) -+ *rstate = state; -+ -+ /* TODO: convert to new dfa format */ -+ -+ return aa_compute_perms(dfa, state, cond); -+} -+ -+int aa_pathstr_perm(struct aa_profile *profile, const char *op, -+ const char *name, u16 request, struct path_cond *cond) -+{ -+ struct aa_audit_file sa; -+ -+ memset(&sa, 0, sizeof(sa)); -+ sa.base.operation = op; -+ sa.base.gfp_mask = GFP_KERNEL; -+ sa.request = request; -+ sa.name = name; -+ sa.cond = cond; -+ -+ sa.perms = aa_str_perms(profile->file.dfa, DFA_START, sa.name, cond, -+ NULL); -+ if (request & ~sa.perms.allowed) -+ sa.base.error = -EACCES; -+ return aa_audit_file(profile, &sa); -+} -+ -+int aa_path_perm(struct aa_profile *profile, const char *operation, -+ struct path *path, u16 request, struct path_cond *cond) -+{ -+ struct aa_audit_file sa; -+ char *buffer, *name; -+ -+ memset(&sa, 0, sizeof(sa)); -+ sa.base.operation = operation; -+ sa.base.gfp_mask = GFP_KERNEL; -+ sa.request = request; -+ sa.cond = cond; -+ -+ sa.base.error = aa_get_name(path, S_ISDIR(cond->mode), &buffer, -+ &name); -+ sa.name = name; -+ if (sa.base.error) { -+ sa.perms = nullperms; -+ if (sa.base.error == -ENOENT) -+ sa.base.info = "Failed name lookup - deleted entry"; -+ else if (sa.base.error == -ESTALE) -+ sa.base.info = "Failed name lookup - disconnected path"; -+ else if (sa.base.error == -ENAMETOOLONG) -+ sa.base.info = "Failed name lookup - name too long"; -+ else -+ sa.base.info = "Failed name lookup"; -+ } else { -+ sa.perms = aa_str_perms(profile->file.dfa, DFA_START, sa.name, -+ cond, NULL); -+ if (request & ~sa.perms.allowed) -+ sa.base.error = -EACCES; -+ } -+ sa.base.error = aa_audit_file(profile, &sa); ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, ++ NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); + kfree(buffer); + -+ return sa.base.error; ++ return error; +} + -+int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, -+ struct path *new_dir, struct dentry *new_dentry) ++int aa_pivotroot(struct aa_profile *profile, struct path *old_path, ++ struct path *new_path) +{ -+ struct path link = { new_dir->mnt, new_dentry }; -+ struct path target = { new_dir->mnt, old_dentry }; -+ struct path_cond cond = { old_dentry->d_inode->i_uid, -+ old_dentry->d_inode->i_mode }; -+ char *buffer = NULL, *buffer2 = NULL; -+ char *lname, *tname; -+ struct file_perms perms; -+ unsigned int state; -+ -+ struct aa_audit_file sa; -+ memset(&sa, 0, sizeof(sa)); -+ sa.base.operation = "link"; -+ sa.base.gfp_mask = GFP_KERNEL; -+ sa.request = AA_MAY_LINK; -+ sa.cond = &cond; -+ sa.perms = nullperms; -+ -+ sa.base.error = aa_get_name(&link, 0, &buffer, &lname); -+ sa.name = lname; -+ if (sa.base.error) -+ goto audit; -+ -+ sa.base.error = aa_get_name(&target, 0, &buffer2, &tname); -+ sa.name2 = tname; -+ if (sa.base.error) -+ goto audit; -+ -+ -+ sa.perms = aa_str_perms(profile->file.dfa, DFA_START, sa.name, &cond, -+ &state); -+ sa.perms.audit &= AA_MAY_LINK; -+ sa.perms.quiet &= AA_MAY_LINK; -+ sa.perms.kill &= AA_MAY_LINK; -+ -+ if (!(sa.perms.allowed & AA_MAY_LINK)) { -+ sa.base.error = -EACCES; -+ goto audit; -+ } ++ struct file_perms perms = { }; ++ struct aa_profile *target = NULL; ++ char *old_buffer = NULL, *new_buffer = NULL; ++ const char *old_name, *new_name = NULL, *info = NULL; ++ int error; + -+ /* test to see if target can be paired with link */ -+ state = aa_dfa_null_transition(profile->file.dfa, state); -+ perms = aa_str_perms(profile->file.dfa, state, sa.name2, &cond, NULL); -+ if (!(perms.allowed & AA_MAY_LINK)) { -+ sa.base.error = -EACCES; -+ sa.base.info = "target restricted"; ++ error = aa_path_name(old_path, path_flags(profile, old_path), ++ &old_buffer, &old_name, &info); ++ if (error) + goto audit; -+ } + -+ /* done if link subset test is not required */ -+ if (!(perms.allowed & AA_LINK_SUBSET)) ++ error = aa_path_name(new_path, path_flags(profile, new_path), ++ &new_buffer, &new_name, &info); ++ if (error) + goto audit; + -+ /* Do link perm subset test requiring allowed permission on link are a -+ * subset of the allowed permissions on target. -+ */ -+ perms = aa_str_perms(profile->file.dfa, DFA_START, sa.name2, &cond, -+ NULL); -+ -+ /* AA_MAY_LINK is not considered in the subset test */ -+ sa.request = sa.perms.allowed & ~AA_MAY_LINK; -+ sa.perms.allowed &= perms.allowed | AA_MAY_LINK; -+ -+ sa.request |= AA_AUDIT_FILE_MASK & (sa.perms.allowed & ~perms.allowed); -+ if (sa.request & ~sa.perms.allowed) -+ sa.base.error = -EACCES; -+ else if (sa.perms.allowed & MAY_EXEC) { -+ if (((sa.perms.xindex & ~AA_X_UNSAFE) != -+ (perms.xindex &~AA_X_UNSAFE)) || -+ ((sa.perms.xindex & AA_X_UNSAFE) && -+ !(perms.xindex & AA_X_UNSAFE))) { -+ sa.perms.allowed &= ~MAY_EXEC; -+ sa.request |= MAY_EXEC; -+ sa.base.error = -EACCES; -+ sa.base.info = "link not subset of target"; ++ if (profile->policy.dfa) { ++ unsigned int state; ++ state = aa_dfa_match(profile->policy.dfa, ++ profile->policy.start[AA_CLASS_MOUNT], ++ new_name); ++ state = aa_dfa_null_transition(profile->policy.dfa, state); ++ state = aa_dfa_match(profile->policy.dfa, state, old_name); ++ perms = compute_mnt_perms(profile->policy.dfa, state); ++ } ++ ++ if (AA_MAY_PIVOTROOT & perms.allow) { ++ if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { ++ target = x_table_lookup(profile, perms.xindex); ++ if (!target) ++ error = -ENOENT; ++ else ++ error = aa_replace_current_profile(target); + } -+ } ++ } else ++ error = -EACCES; + +audit: -+ sa.base.error = aa_audit_file(profile, &sa); -+ kfree(buffer); -+ kfree(buffer2); -+ -+ return sa.base.error; -+} -+ -+ -+static inline int aa_is_deleted_file(struct dentry *dentry) -+{ -+ if (d_unhashed(dentry) && dentry->d_inode->i_nlink == 0) -+ return 1; -+ return 0; -+} -+ -+int aa_file_common_perm(struct aa_profile *profile, const char *operation, -+ struct file *file, u16 request, const char *name, -+ int error) -+{ -+ struct path_cond cond = { .uid = file->f_path.dentry->d_inode->i_uid, -+ .mode = file->f_path.dentry->d_inode->i_mode }; -+ struct aa_audit_file sa; -+ -+ memset(&sa, 0, sizeof(sa)); -+ sa.base.operation = operation; -+ sa.base.gfp_mask = GFP_KERNEL; -+ sa.request = request; -+ sa.base.error = error; -+ sa.name = name; -+ sa.cond = &cond; -+ -+ if (sa.base.error) { -+ sa.perms = nullperms; -+ if (sa.base.error == -ENOENT && -+ aa_is_deleted_file(file->f_path.dentry)) { -+ /* Access to open files that are deleted are -+ * give a pass (implicit delegation -+ */ -+ sa.base.error = 0; -+ sa.perms.allowed = sa.request; -+ } else if (sa.base.error == -ENOENT) -+ sa.base.info = "Failed name lookup - deleted entry"; -+ else if (sa.base.error == -ESTALE) -+ sa.base.info = "Failed name lookup - disconnected path"; -+ else if (sa.base.error == -ENAMETOOLONG) -+ sa.base.info = "Failed name lookup - name too long"; -+ else -+ sa.base.info = "Failed name lookup"; -+ } else { -+ sa.perms = aa_str_perms(profile->file.dfa, DFA_START, sa.name, -+ &cond, NULL); -+ if (request & ~sa.perms.allowed) -+ sa.base.error = -EACCES; -+ } -+ sa.base.error = aa_audit_file(profile, &sa); -+ -+ return sa.base.error; -+} -+ -+int aa_file_perm(struct aa_profile *profile, const char *operation, -+ struct file *file, u16 request) -+{ -+ char *buffer, *name; -+ umode_t mode = file->f_path.dentry->d_inode->i_mode; -+ int error = aa_get_name(&file->f_path, S_ISDIR(mode), &buffer, &name); ++ error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, ++ old_name, NULL, target ? target->base.name : NULL, ++ 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); ++ aa_put_profile(target); ++ kfree(old_buffer); ++ kfree(new_buffer); + -+ error = aa_file_common_perm(profile, operation, file, request, name, -+ error); -+ kfree(buffer); + return error; +} -diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h -new file mode 100644 -index 0000000..fbbc961 ---- /dev/null -+++ b/security/apparmor/include/apparmor.h -@@ -0,0 +1,65 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor basic global and lib definitions -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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_H -+#define __APPARMOR_H -+ -+#include -+ -+/* Control parameters settable thru module/boot flags or -+ * via /sys/kernel/security/apparmor/control */ -+extern enum audit_mode g_apparmor_audit; -+extern int g_apparmor_audit_header; -+extern int g_apparmor_debug; -+extern int g_apparmor_lock_policy; -+extern int g_apparmor_logsyscall; -+extern unsigned int g_apparmor_path_max; -+ -+ -+/* -+ * DEBUG remains global (no per profile flag) since it is mostly used in sysctl -+ * which is not related to profile accesses. -+ */ -+ -+#define AA_DEBUG(fmt, args...) \ -+ do { \ -+ if (g_apparmor_debug && printk_ratelimit()) \ -+ printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ -+ } while (0) -+ -+#define AA_ERROR(fmt, args...) \ -+ do { \ -+ if (printk_ratelimit()) \ -+ printk(KERN_ERR "AppArmor: " fmt, ##args); \ -+ } while (0) -+ -+/* Flag indicating whether initialization completed */ -+extern int apparmor_initialized; -+void apparmor_disable(void); -+ -+/* fn's in lib */ -+void info_message(const char *str); -+char *aa_split_name_from_ns(char *args, char **ns_name); -+char *new_compound_name(const char *n1, const char *n2); -+int aa_strneq(const char *str, const char *sub, int len); -+char *strchrnul(const char *s, int c); -+const char *fqname_subname(const char *name); -+ -+static inline int mediated_filesystem(struct inode *inode) -+{ -+ return !(inode->i_sb->s_flags & MS_NOUSER); -+} -+ -+#endif /* __APPARMOR_H */ -+ -diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h -new file mode 100644 -index 0000000..1af7723 ---- /dev/null -+++ b/security/apparmor/include/apparmorfs.h -@@ -0,0 +1,24 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor filesystem definitions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_APPARMORFS_H -+#define __AA_APPARMORFS_H -+ -+extern struct dentry *apparmorfs_null; -+extern struct vfsmount *apparmorfs_mnt; -+ -+extern int create_apparmorfs(void); -+extern void destroy_apparmorfs(void); -+ -+#endif /* __AA_APPARMORFS_H */ -diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h -new file mode 100644 -index 0000000..2180dd7 ---- /dev/null -+++ b/security/apparmor/include/audit.h -@@ -0,0 +1,59 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor auditing function definitions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_AUDIT_H -+#define __AA_AUDIT_H -+ -+#include -+#include -+#include -+#include -+ -+struct aa_profile; -+ -+ -+extern const char *audit_mode_names[]; -+#define AUDIT_MAX_INDEX 5 -+ -+#define AUDIT_APPARMOR_AUTO 0 /* auto choose audit message type */ -+ -+enum audit_mode { -+ AUDIT_NORMAL, /* follow normal auditing of accesses */ -+ AUDIT_QUIET_DENIED, /* quiet all denied access messages */ -+ AUDIT_QUIET, /* quiet all messages */ -+ AUDIT_NOQUIET, /* do not quiet audit messages */ -+ AUDIT_ALL /* audit all accesses */ -+}; -+ -+/* -+ * aa_audit - AppArmor auditing structure -+ * Structure is populated by access control code and passed to aa_audit which -+ * provides for a single point of logging. -+ */ -+struct aa_audit { -+ struct task_struct *task; -+ gfp_t gfp_mask; -+ int error; -+ const char *operation; -+ const char *info; -+}; -+ -+int aa_audit(int type, struct aa_profile *profile, struct aa_audit *sa, -+ void(*cb)(struct audit_buffer *, void *)); -+ -+int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp, const char *, -+ void(*cb)(struct audit_buffer *, void *)); -+ -+ -+#endif /* __AA_AUDIT_H */ -diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h -new file mode 100644 -index 0000000..43bb7eb ---- /dev/null -+++ b/security/apparmor/include/capability.h -@@ -0,0 +1,45 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor capability mediation definitions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_CAPABILITY_H -+#define __AA_CAPABILITY_H -+ -+#include -+ -+struct aa_profile; -+ -+/* aa_caps - confinement data for capabilities -+ * @set_caps: capabilities that are being set -+ * @capabilities: capabilities mask -+ * @audit_caps: caps that are to be audited -+ * @quiet_caps: caps that should not be audited -+ */ -+struct aa_caps { -+ kernel_cap_t set; -+ kernel_cap_t allowed; -+ kernel_cap_t audit; -+ kernel_cap_t quiet; -+ kernel_cap_t kill; -+}; -+ -+int aa_profile_capable(struct aa_profile *profile, int cap); -+int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, -+ int audit); -+ -+static inline void aa_free_cap_rules(struct aa_caps *caps) -+{ -+ /* NOP */ -+} -+ -+#endif /* __AA_CAPBILITY_H */ -diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h -new file mode 100644 -index 0000000..202a66a ---- /dev/null -+++ b/security/apparmor/include/context.h -@@ -0,0 +1,153 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor contexts used to associate "labels" to objects. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_CONTEXT_H -+#define __AA_CONTEXT_H -+ -+#include -+#include -+#include -+ -+#include "policy.h" -+ -+ -+/* struct aa_file_cxt - the AppArmor context the file was opened in -+ * @profile: the profile the file was opened under -+ * @perms: the permission the file was opened with -+ */ -+struct aa_file_cxt { -+ struct aa_profile *profile; -+ u16 allowed; -+}; -+ -+static inline struct aa_file_cxt *aa_alloc_file_context(gfp_t gfp) -+{ -+ return kzalloc(sizeof(struct aa_file_cxt), gfp); -+} -+ -+static inline void aa_free_file_context(struct aa_file_cxt *cxt) -+{ -+ aa_put_profile(cxt->profile); -+ memset(cxt, 0, sizeof(struct aa_file_cxt)); -+ kfree(cxt); -+} -+ -+ -+ -+ -+ -+/* struct aa_task_cxt_group - a grouping label data for confined tasks -+ * @profile: the current profile -+ * @exec: profile to transition to on next exec -+ * @previous: profile the task may return to -+ * @token: magic value the task must know for returning to @previous_profile -+ * -+ * Contains the task's current profile (which could change due to -+ * change_hat). Plus the hat_magic needed during change_hat. -+ */ -+struct aa_task_cxt_group { -+ struct aa_profile *profile; -+ struct aa_profile *onexec; -+ struct aa_profile *previous; -+ u64 token; -+}; -+ -+/** -+ * struct aa_task_context - primary label for confined tasks -+ * @sys: the system labeling for the task -+ * -+ * A task is confined by the intersection of its system and user profiles -+ */ -+struct aa_task_context { -+ struct aa_task_cxt_group sys; -+}; -+ -+struct aa_task_context *aa_alloc_task_context(gfp_t flags); -+void aa_free_task_context(struct aa_task_context *cxt); -+struct aa_task_context *aa_dup_task_context(struct aa_task_context *old_cxt, -+ gfp_t gfp); -+void aa_cred_policy(const struct cred *cred, struct aa_profile **sys); -+struct cred *aa_get_task_policy(const struct task_struct *task, -+ struct aa_profile **sys); -+int aa_replace_current_profiles(struct aa_profile *sys); -+void aa_put_task_policy(struct cred *cred); -+int aa_set_current_onexec(struct aa_profile *sys); -+int aa_set_current_hat(struct aa_profile *profile, u64 token); -+int aa_restore_previous_profile(u64 cookie); -+ -+ -+static inline struct aa_task_context *__aa_task_cxt(struct task_struct *task) -+{ -+ return __task_cred(task)->security; -+} -+ -+/** -+ * __aa_task_is_confined - determine if @task has any confinement -+ * @task: task to check confinement of -+ * -+ * If @task != current needs to be in RCU safe critical section -+ */ -+static inline int __aa_task_is_confined(struct task_struct *task) -+{ -+ struct aa_task_context *cxt; -+ int rc = 1; -+ -+ cxt = __aa_task_cxt(task); -+ if (!cxt || (cxt->sys.profile->flags & PFLAG_UNCONFINED)) -+ rc = 0; -+ -+ return rc; -+} -+ -+static inline const struct cred *aa_current_policy(struct aa_profile **sys) -+{ -+ const struct cred *cred = current_cred(); -+ struct aa_task_context *cxt = cred->security; -+ BUG_ON(!cxt); -+ *sys = aa_filtered_profile(aa_profile_newest(cxt->sys.profile)); -+ -+ return cred; -+} -+ -+static inline const struct cred *aa_current_policy_wupd(struct aa_profile **sys) -+{ -+ const struct cred *cred = current_cred(); -+ struct aa_task_context *cxt = cred->security; -+ BUG_ON(!cxt); -+ -+ *sys = aa_profile_newest(cxt->sys.profile); -+ if (unlikely((cxt->sys.profile != *sys))) -+ aa_replace_current_profiles(*sys); -+ *sys = aa_filtered_profile(*sys); -+ -+ return cred; -+} -+ -+static inline struct aa_profile *aa_current_profile(void) -+{ -+ const struct cred *cred = current_cred(); -+ struct aa_task_context *cxt = cred->security; -+ BUG_ON(!cxt); -+ return aa_filtered_profile(aa_profile_newest(cxt->sys.profile)); -+} -+ -+static inline struct aa_profile *aa_current_profile_wupd(void) -+{ -+ struct aa_profile *p; -+ aa_current_policy_wupd(&p); -+ return p; -+} -+ -+ -+#endif /* __AA_CONTEXT_H */ -diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h -new file mode 100644 -index 0000000..a340e62 ---- /dev/null -+++ b/security/apparmor/include/domain.h -@@ -0,0 +1,37 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor security domain transition function definitions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ */ -+ -+#include -+#include -+ -+#ifndef __AA_DOMAIN_H -+#define __AA_DOMAIN_H -+ -+struct aa_domain { -+ int size; -+ char **table; -+}; -+ -+int apparmor_bprm_set_creds(struct linux_binprm *bprm); -+int apparmor_bprm_secureexec(struct linux_binprm *bprm); -+int apparmor_bprm_committing_creds(struct linux_binprm *bprm); -+void apparmor_bprm_committed_creds(struct linux_binprm *bprm); -+ -+void aa_free_domain_entries(struct aa_domain *domain); -+int aa_change_hat(const char *hat_name, u64 token, int permtest); -+int aa_change_profile(const char *ns_name, const char *name, int onexec, -+ int permtest); -+ -+ -+#endif /* __AA_DOMAIN_H */ -diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h -new file mode 100644 -index 0000000..e99e6fe ---- /dev/null -+++ b/security/apparmor/include/file.h -@@ -0,0 +1,227 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor file mediation function definitions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_FILE_H -+#define __AA_FILE_H -+ -+#include -+ -+#include "audit.h" -+#include "domain.h" -+#include "match.h" -+ -+struct aa_profile; -+ -+/* -+ * We use MAY_EXEC, MAY_WRITE, MAY_READ, MAY_APPEND and the following flags -+ * for profile permissions -+ */ -+#define AA_MAY_LINK 0x0010 -+#define AA_MAY_LOCK 0x0020 -+#define AA_EXEC_MMAP 0x0040 -+ -+#define AA_MAY_CREATE 0x0080 -+#define AA_LINK_SUBSET 0x0100 -+#define AA_MAY_DELEGATE 0x0200 -+#define AA_EXEC_DELEGATE 0x0400 /*exec allows delegate*/ -+ -+#define AA_MAY_CHANGEHAT 0x2000 /* ctrl auditing only */ -+#define AA_MAY_ONEXEC 0x4000 /* exec allows onexec */ -+#define AA_MAY_CHANGE_PROFILE 0x8000 -+ -+ -+#define AA_AUDIT_FILE_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND |\ -+ AA_MAY_LINK | AA_MAY_LOCK | AA_EXEC_MMAP | \ -+ AA_MAY_CREATE) -+ -+/* -+ * The xindex is broken into 3 parts -+ * - index - an index into either the exec name table or the variable table -+ * - exec type - which determines how the executable name and index are used -+ * - flags - which modify how the destination name is applied -+ */ -+#define AA_X_INDEX_MASK 0x03ff -+ -+#define AA_X_TYPE_MASK 0x0c00 -+#define AA_X_TYPE_SHIFT 10 -+#define AA_X_NONE 0x0000 -+#define AA_X_NAME 0x0400 /* use executable name px */ -+#define AA_X_TABLE 0x0800 /* use a specified name ->n# */ -+ -+#define AA_X_UNSAFE 0x1000 -+#define AA_X_CHILD 0x2000 /* make >AA_X_NONE apply to children */ -+#define AA_X_INHERIT 0x4000 -+#define AA_X_UNCONFINED 0x8000 -+ -+ -+/* AA_SECURE_X_NEEDED - is passed in the bprm->unsafe field */ -+#define AA_SECURE_X_NEEDED 0x8000 -+ -+/* need to conditionalize which ones are being set */ -+struct path_cond { -+ uid_t uid; -+ umode_t mode; -+}; -+ -+/* struct file_perms - file permission fo -+ * @allowed: mask of permissions that are allowed -+ * @audit: mask of permissions to force an audit message for -+ * @quiet: mask of permissions to quiet audit messages for -+ * @kill: mask of permissions that when matched will kill the task -+ * @xindex: exec transition index if @allowed contains MAY_EXEC -+ * @dindex: delegate table index if @allowed contain AA_MAY_DELEGATE -+ * -+ * The @audit and @queit mask should be mutually exclusive. -+ */ -+struct file_perms { -+ u16 allowed; -+ u16 audit; -+ u16 quiet; -+ u16 kill; -+ u16 xindex; -+ u16 dindex; -+}; -+ -+extern struct file_perms nullperms; -+ -+#define COMBINED_PERM_MASK(X) ((X).allowed | (X).audit | (X).quiet | (X).kill) -+ -+/* FIXME: split perms from dfa and match this to description -+ * also add delegation info. -+ */ -+static inline u16 dfa_map_xindex(u16 mask) -+{ -+ u16 old_index = (mask >> 10) & 0xf; -+ u16 index = 0; -+ -+//printk("mask x%x\n", mask); -+ if (mask & 0x100) -+ index |= AA_X_UNSAFE; -+ if (mask & 0x200) -+ index |= AA_X_INHERIT; -+ -+ if (old_index == 1) { -+ index |= AA_X_UNCONFINED; -+ } else if (old_index == 2) { -+ index |= AA_X_NAME; -+ } else if (old_index == 3) { -+ index |= AA_X_NAME | AA_X_CHILD; -+ } else { -+ index |= AA_X_TABLE; -+ index |= old_index - 4; -+ } -+ -+ return index; -+} -+ -+/* -+ * map old dfa inline permissions to new format -+ */ -+#define dfa_user_allow(dfa, state) ((ACCEPT_TABLE(dfa)[state]) & 0x7f) -+#define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f) -+#define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f) -+#define dfa_user_xindex(dfa, state) \ -+ (dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff)) -+ -+#define dfa_other_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) >> 14) & 0x7f) -+#define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f) -+#define dfa_other_quiet(dfa, state) ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f) -+#define dfa_other_xindex(dfa, state) \ -+ dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) -+ -+ -+struct aa_audit_file { -+ struct aa_audit base; -+ -+ const char *name; -+ const char *name2; -+ const char *name3; -+ struct file_perms perms; -+ u16 request; -+ struct path_cond *cond; -+}; -+ -+int aa_audit_file(struct aa_profile *profile, struct aa_audit_file *sa); -+void file_audit_cb(struct audit_buffer *ab, void *va); -+ -+/** -+ * struct aa_file_rules - components used for file rule permissions -+ * @dfa: dfa to match path names and conditionals against -+ * @perms: permission table indexed by the matched state accept entry of @dfa -+ * @trans: transition table for indexed by named x transitions -+ * -+ * File permission are determined by matching a path against @dfa and then -+ * then using the value of the accept entry for the matching state as -+ * an index into @perms. If a named exec transition is required it is -+ * looked up in the transition table. -+ */ -+struct aa_file_rules { -+ struct aa_dfa *dfa; -+ /* struct perms perms; */ -+ struct aa_domain trans; -+ /* TODO: add delegate table */ -+}; -+ -+struct file_perms aa_str_perms(struct aa_dfa *dfa, unsigned int start, -+ const char *name, struct path_cond *cond, -+ unsigned int *rstate); -+ -+int aa_pathstr_perm(struct aa_profile *profile, const char *op, -+ const char *name, u16 request, struct path_cond *cond); -+ -+int aa_path_perm(struct aa_profile *profile, const char *operation, -+ struct path *path, u16 request, struct path_cond *cond); -+ -+int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, -+ struct path *new_dir, struct dentry *new_dentry); -+ -+int aa_file_common_perm(struct aa_profile *profile, const char *operation, -+ struct file *file, u16 request, const char *name, -+ int error); -+ -+int aa_file_perm(struct aa_profile *profile, const char *operation, -+ struct file *file, u16 request); -+ -+ -+static inline void aa_free_file_rules(struct aa_file_rules *rules) -+{ -+ aa_match_free(rules->dfa); -+ aa_free_domain_entries(&rules->trans); -+} -+ -+#define ACC_FMODE(x) (("\000\004\002\006"[(x)&O_ACCMODE]) | (((x) << 1) & 0x40)) -+ -+/* from namei.c */ -+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) -+#define MAP_OPEN_FLAGS(x) ((((x) + 1) & O_ACCMODE) ? (x) + 1 : (x)) -+/* -+ * map file flags to AppArmor permissions -+ */ -+static inline u16 aa_map_file_to_perms(struct file *file) -+{ -+ int flags = MAP_OPEN_FLAGS(file->f_flags); -+ u16 perms = ACC_FMODE(file->f_mode); -+ -+ if ((flags & O_APPEND) && (perms & MAY_WRITE)) -+ perms = (perms & ~MAY_WRITE) | MAY_APPEND; -+ /* trunc implies write permission */ -+ if (flags & O_TRUNC) -+ perms |= MAY_WRITE; -+ if (flags & O_CREAT) -+ perms |= AA_MAY_CREATE; -+ -+ return perms; -+} -+ -+#endif /* __AA_FILE_H */ -diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h -new file mode 100644 -index 0000000..e80a95e ---- /dev/null -+++ b/security/apparmor/include/ipc.h -@@ -0,0 +1,28 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor ipc mediation function definitions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_IPC_H -+#define __AA_IPC_H -+ -+#include -+ -+struct aa_profile; -+ -+int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, -+ struct aa_profile *tracee, unsigned int mode); -+ -+int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, -+ unsigned int mode); -+ -+#endif /* __AA_IPC_H */ -diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h -new file mode 100644 -index 0000000..8a0f59c ---- /dev/null -+++ b/security/apparmor/include/match.h -@@ -0,0 +1,105 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor policy dfa matching engine definitions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_MATCH_H -+#define __AA_MATCH_H -+ -+#define DFA_NOMATCH 0 -+#define DFA_START 1 -+ -+#define DFA_VALID_PERM_MASK 0xffffffff -+#define DFA_VALID_PERM2_MASK 0xffffffff -+ -+ -+/** -+ * The format used for transition tables is based on the GNU flex table -+ * file format (--tables-file option; see Table File Format in the flex -+ * info pages and the flex sources for documentation). The magic number -+ * used in the header is 0x1B5E783D insted of 0xF13C57B1 though, because -+ * the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used -+ * slightly differently (see the apparmor-parser package). -+ */ -+ -+#define YYTH_MAGIC 0x1B5E783D -+ -+struct table_set_header { -+ u32 th_magic; /* YYTH_MAGIC */ -+ u32 th_hsize; -+ u32 th_ssize; -+ u16 th_flags; -+ char th_version[]; -+}; -+ -+#define YYTD_ID_ACCEPT 1 -+#define YYTD_ID_BASE 2 -+#define YYTD_ID_CHK 3 -+#define YYTD_ID_DEF 4 -+#define YYTD_ID_EC 5 -+#define YYTD_ID_META 6 -+#define YYTD_ID_ACCEPT2 7 -+#define YYTD_ID_NXT 8 -+ -+ -+#define YYTD_DATA8 1 -+#define YYTD_DATA16 2 -+#define YYTD_DATA32 4 -+ -+struct table_header { -+ u16 td_id; -+ u16 td_flags; -+ u32 td_hilen; -+ u32 td_lolen; -+ char td_data[]; -+}; -+ -+#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF - 1]->td_data)) -+#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE - 1]->td_data)) -+#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT - 1]->td_data)) -+#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK - 1]->td_data)) -+#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC - 1]->td_data)) -+#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT - 1]->td_data)) -+#define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2 - 1]->td_data)) -+ -+struct aa_dfa { -+ struct table_header *tables[YYTD_ID_NXT]; -+}; -+ -+#define byte_to_byte(X) (X) -+ -+#define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \ -+ do { \ -+ typeof(LEN) __i; \ -+ TYPE *__t = (TYPE *) TABLE; \ -+ TYPE *__b = (TYPE *) BLOB; \ -+ for (__i = 0; __i < LEN; __i++) { \ -+ __t[__i] = NTOHX(__b[__i]); \ -+ } \ -+ } while (0) -+ -+static inline size_t table_size(size_t len, size_t el_size) -+{ -+ return ALIGN(sizeof(struct table_header) + len * el_size, 8); -+} -+ -+struct aa_dfa *aa_match_alloc(void); -+void aa_match_free(struct aa_dfa *dfa); -+int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size); -+int verify_dfa(struct aa_dfa *dfa); -+unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, -+ const char *str, int len); -+unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, -+ const char *str); -+unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, unsigned int start); -+ -+#endif /* __AA_MATCH_H */ -diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h -new file mode 100644 -index 0000000..ece94b2 ---- /dev/null -+++ b/security/apparmor/include/net.h -@@ -0,0 +1,40 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor network mediation definitions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_NET_H -+#define __AA_NET_H -+ -+#include -+ -+/* struct aa_net - network confinement data -+ * @allowed: basic network families permissions -+ * @audit_network: which network permissions to force audit -+ * @quiet_network: which network permissions to quiet rejects -+ */ -+struct aa_net { -+ u16 allowed[AF_MAX]; -+ u16 audit[AF_MAX]; -+ u16 quiet[AF_MAX]; -+}; -+ -+extern int aa_net_perm(struct aa_profile *profile, char *operation, -+ int family, int type, int protocol); -+extern int aa_revalidate_sk(struct sock *sk, char *operation); -+ -+static inline void aa_free_net_rules(struct aa_net *new) -+{ -+ /* NOP */ -+} -+ -+#endif /* __AA_NET_H */ -diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h -new file mode 100644 -index 0000000..d238e42 ---- /dev/null -+++ b/security/apparmor/include/path.h -@@ -0,0 +1,24 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor basic path manipulation function definitions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_PATH_H -+#define __AA_PATH_H -+ -+int aa_get_name_to_buffer(struct path *path, int is_dir, char *buffer, int size, -+ char **name); -+int aa_get_name(struct path *path, int is_dir, char **buffer, char **name); -+int d_namespace_path(struct path *path, char *buf, int buflen, char **name); -+char *sysctl_pathname(struct ctl_table *table, char *buffer, int buflen); -+ -+#endif /* __AA_PATH_H */ -diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h -new file mode 100644 -index 0000000..98cd0d9 ---- /dev/null -+++ b/security/apparmor/include/policy.h -@@ -0,0 +1,301 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor policy definitions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_POLICY_H -+#define __AA_POLICY_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "apparmor.h" -+#include "audit.h" -+#include "capability.h" -+#include "domain.h" -+#include "file.h" -+#include "net.h" -+#include "resource.h" -+ -+extern const char *profile_mode_names[]; -+#define APPARMOR_NAMES_MAX_INDEX 3 -+ -+#define PROFILE_COMPLAIN(_profile) \ -+ ((g_profile_mode == APPARMOR_COMPLAIN) || ((_profile) && \ -+ (_profile)->mode == APPARMOR_COMPLAIN)) -+ -+#define PROFILE_KILL(_profile) \ -+ ((g_profile_mode == APPARMOR_KILL) || ((_profile) && \ -+ (_profile)->mode == APPARMOR_KILL)) -+ -+#define PROFILE_IS_HAT(_profile) \ -+ ((_profile) && (_profile)->flags & PFLAG_HAT) -+ -+ -+/* -+ * FIXME: currently need a clean way to replace and remove profiles as a -+ * set. It should be done at the namespace level. -+ * Either, with a set of profiles loaded at the namespace level or via -+ * a mark and remove marked interface. -+ */ -+enum profile_mode { -+ APPARMOR_ENFORCE, /* enforce access rules */ -+ APPARMOR_COMPLAIN, /* allow and log access violations */ -+ APPARMOR_KILL, /* kill task on access violation */ -+}; -+ -+enum profile_flags { -+ PFLAG_HAT = 1, /* profile is a hat */ -+ PFLAG_UNCONFINED = 2, /* profile is the 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 */ -+ PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ -+}; -+ -+#define AA_NEW_SID 0 -+ -+struct aa_profile; -+ -+/* struct aa_policy_common - common part of both namespaces and profiles -+ * @name: name of the object -+ * @count: reference count of the obj -+ * lock: lock for modifying the object -+ * @list: list object is on -+ * @profiles: head of the profiles list contained in the object -+ */ -+struct aa_policy_common { -+ char *name; -+ struct kref count; -+ rwlock_t lock; -+ struct list_head list; -+ struct list_head profiles; -+}; -+ -+/* struct aa_ns_acct - accounting of profiles in namespace -+ * @max_size: maximum space allowed for all profiles in namespace -+ * @max_count: maximum number of profiles that can be in this namespace -+ * @size: current size of profiles -+ * @count: current count of profiles (includes null profiles) -+ */ -+struct aa_ns_acct { -+ int max_size; -+ int max_count; -+ int size; -+ int count; -+}; -+ -+/* struct aa_namespace - namespace for a set of profiles -+ * @name: the name of the namespace -+ * @list: list the namespace is on -+ * @profiles: list of profile in the namespace -+ * @acct: accounting for the namespace -+ * @profile_count: count of profiles on @profiles list -+ * @size: accounting of how much memory is consumed by the contained profiles -+ * @unconfined: special unconfined profile for the namespace -+ * @count: reference count on the namespace -+ * @lock: lock for adding/removing profile to the namespace -+ * -+ * 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 -+ * aa_namespaces and profile names within a namespace are guarenteed to be -+ * unique. When profiles in seperate namespaces have the same name they -+ * are NOT considered to be equivalent. -+ * -+ * Namespace names must be unique and can not contain the characters :/\0 -+ * -+ * FIXME TODO: add vserver support so a vserer gets a default namespace -+ */ -+struct aa_namespace { -+ struct aa_policy_common base; -+ struct aa_ns_acct acct; -+ int is_stale; -+ struct aa_profile *unconfined; -+}; -+ -+ -+/* struct aa_profile - basic confinement data -+ * @base - base componets of the profile (name, refcount, lists, lock ...) -+ * @fqname - The fully qualified profile name, less the namespace name -+ * @ns: namespace the profile is in -+ * @parent: parent profile of this profile, if one exists -+ * @replacedby: is set profile that replaced this profile -+ * @xmatch: optional extended matching for unconfined executables names -+ * @xmatch_plen: xmatch prefix len, used to determine xmatch priority -+ * @sid: the unique security id number of this profile -+ * @audit: the auditing mode of the profile -+ * @mode: the enforcement mode of the profile -+ * @flags: flags controlling profile behavior -+ * @size: the memory consumed by this profiles rules -+ * @file: The set of rules governing basic file access and domain transitions -+ * @caps: capabilities for the profile -+ * @net: network controls for the profile -+ * @rlimits: rlimits for the profile -+ * -+ * The AppArmor profile contains the basic confinement data. Each profile -+ * has a name, and exist in a namespace. The @name and @exec_match are -+ * used to determine profile attachment against unconfined tasks. All other -+ * attachments are determined by in 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. -+ * -+ * Profiles have a hierachy where hats and children profiles keep -+ * a reference to their parent. -+ * -+ * Profile names can not begin with a : and can not contain the \0 -+ * character. If a profile name begins with / it will be considered when -+ * determining profile attachment on "unconfined" tasks. -+ */ -+struct aa_profile { -+ struct aa_policy_common base; -+ char *fqname; -+ -+ struct aa_namespace *ns; -+ struct aa_profile *parent; -+ struct aa_profile *replacedby; -+ -+ struct aa_dfa *xmatch; -+ int xmatch_len; -+ u32 sid; -+ enum audit_mode audit; -+ enum profile_mode mode; -+ u32 flags; -+ int size; -+ -+ struct aa_file_rules file; -+ struct aa_caps caps; -+ struct aa_net net; -+ struct aa_rlimit rlimits; -+}; -+ -+ -+extern struct list_head ns_list; -+extern rwlock_t ns_list_lock; -+ -+extern struct aa_namespace *default_namespace; -+extern enum profile_mode g_profile_mode; -+ -+ -+void aa_add_profile(struct aa_policy_common *common, -+ struct aa_profile *profile); -+ -+int alloc_default_namespace(void); -+void free_default_namespace(void); -+struct aa_namespace *alloc_aa_namespace(const char *name); -+void free_aa_namespace_kref(struct kref *kref); -+void free_aa_namespace(struct aa_namespace *ns); -+struct aa_namespace *__aa_find_namespace(struct list_head *head, -+ const char *name); -+ -+struct aa_namespace *aa_find_namespace(const char *name); -+struct aa_namespace *aa_prepare_namespace(const char *name); -+void aa_remove_namespace(struct aa_namespace *ns); -+struct aa_namespace *aa_prepare_namespace(const char *name); -+void aa_profile_list_release(struct list_head *head); -+void aa_profile_ns_list_release(void); -+void __aa_remove_namespace(struct aa_namespace *ns); -+ -+ -+static inline struct aa_policy_common *aa_get_common(struct aa_policy_common *c) -+{ -+ if (c) -+ kref_get(&c->count); -+ -+ return c; -+} -+ -+static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) -+{ -+ if (ns) -+ kref_get(&(ns->base.count)); -+ -+ return ns; -+} -+ -+static inline void aa_put_namespace(struct aa_namespace *ns) -+{ -+ if (ns) -+ kref_put(&ns->base.count, free_aa_namespace_kref); -+} -+ -+ -+ -+struct aa_profile *alloc_aa_profile(const char *name); -+struct aa_profile *aa_alloc_null_profile(struct aa_profile *parent, int hat); -+void free_aa_profile_kref(struct kref *kref); -+void free_aa_profile(struct aa_profile *profile); -+struct aa_profile *__aa_find_profile(struct list_head *head, const char *name); -+struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); -+struct aa_policy_common *__aa_find_parent_by_fqname(struct aa_namespace *ns, -+ const char *fqname); -+struct aa_profile *__aa_find_profile_by_fqname(struct aa_namespace *ns, -+ const char *fqname); -+struct aa_profile *aa_find_profile_by_fqname(struct aa_namespace *ns, -+ const char *name); -+struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name); -+struct aa_profile *aa_profile_newest(struct aa_profile *profile); -+struct aa_profile *aa_sys_find_attach(struct aa_namespace *ns, -+ const char *name); -+void __aa_add_profile(struct aa_policy_common *common, -+ struct aa_profile *profile); -+void __aa_remove_profile(struct aa_profile *profile, -+ struct aa_profile *replacement); -+void __aa_replace_profile(struct aa_profile *profile, -+ struct aa_profile *replacement); -+void __aa_profile_list_release(struct list_head *head); -+ -+static inline struct aa_profile *aa_filtered_profile(struct aa_profile *profile) -+{ -+ if (profile->flags & PFLAG_UNCONFINED) -+ return NULL; -+ return profile; -+} -+ -+/** -+ * aa_get_profile - increment refcount on profile @p -+ * @p: profile -+ */ -+static inline struct aa_profile *aa_get_profile(struct aa_profile *p) -+{ -+ if (p) -+ kref_get(&(p->base.count)); -+ -+ return p; -+} -+ -+/** -+ * aa_put_profile - decrement refcount on profile @p -+ * @p: profile -+ */ -+static inline void aa_put_profile(struct aa_profile *p) -+{ -+ if (p) -+ kref_put(&p->base.count, free_aa_profile_kref); -+} -+ -+static inline int PROFILE_AUDIT_MODE(struct aa_profile *profile) -+{ -+ if (g_apparmor_audit != AUDIT_NORMAL) -+ return g_apparmor_audit; -+ if (profile) -+ return profile->audit; -+ return AUDIT_NORMAL; -+} -+ -+#endif /* __AA_POLICY_H */ -+ -diff --git a/security/apparmor/include/policy_interface.h b/security/apparmor/include/policy_interface.h -new file mode 100644 -index 0000000..1440876 ---- /dev/null -+++ b/security/apparmor/include/policy_interface.h -@@ -0,0 +1,22 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor policy loading interface function definitions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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 __POLICY_INTERFACE_H -+#define __POLICY_INTERFACE_H -+ -+ssize_t aa_interface_add_profiles(void *data, size_t size); -+ssize_t aa_interface_replace_profiles(void *udata, size_t size); -+ssize_t aa_interface_remove_profiles(char *name, size_t size); -+ -+#endif /* __POLICY_INTERFACE_H */ -diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h -new file mode 100644 -index 0000000..52e46c5 ---- /dev/null -+++ b/security/apparmor/include/procattr.h -@@ -0,0 +1,26 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor /proc//attr/ interface function defintions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_PROCATTR_H -+#define __AA_PROCATTR_H -+ -+#define AA_DO_TEST 1 -+ -+int aa_getprocattr(struct aa_namespace *ns, struct aa_profile *profile, -+ char **string); -+int aa_setprocattr_changehat(char *args, int test); -+int aa_setprocattr_changeprofile(char *args, int onexec, int test); -+int aa_setprocattr_permipc(char *args); -+ -+#endif /* __AA_PROCATTR_H */ -diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h -new file mode 100644 -index 0000000..0662c91 ---- /dev/null -+++ b/security/apparmor/include/resource.h -@@ -0,0 +1,46 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor resource limits function defintions. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_RESOURCE_H -+#define __AA_RESOURCE_H -+ -+#include -+#include -+ -+struct aa_profile; -+ -+/* struct aa_rlimit - rlimits settings for the profile -+ * @mask: which hard limits to set -+ * @limits: rlimit values that override task limits -+ * -+ * AppArmor rlimits are used to set confined task rlimits. Only the -+ * limits specified in @mask will be controlled by apparmor. -+ */ -+struct aa_rlimit { -+ unsigned int mask; -+ struct rlimit limits[RLIM_NLIMITS]; -+}; -+ -+ -+int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, -+ struct rlimit *new_rlim); -+ -+void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new); -+ -+static inline void aa_free_rlimit_rules(struct aa_rlimit *rlims) -+{ -+ /* NOP */ -+} -+ -+#endif /* __AA_RESOURCE_H */ -diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/sid.h -new file mode 100644 -index 0000000..83e3590 ---- /dev/null -+++ b/security/apparmor/include/sid.h -@@ -0,0 +1,46 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor security identifier (sid) definitions -+ * -+ * Copyright 2009 Canonical Ltd. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation, version 2 of the -+ * License. -+ */ -+ -+#ifndef __AA_SID_H -+#define __AA_SID_H -+ -+#include -+ -+struct aa_profile; -+ -+#define AA_ALLOC_USR_SID 1 -+#define AA_ALLOC_SYS_SID 0 -+ -+u32 aa_alloc_sid(int is_usr); -+void aa_free_sid(u32 sid); -+int aa_add_sid_profile(u32 sid, struct aa_profile *profile); -+int aa_replace_sid_profile(u32 sid, struct aa_profile *profile); -+struct aa_profile *aa_get_sid_profile(u32 sid); -+ -+ -+static inline u32 aa_compound_sid(u32 sys, u32 usr) -+{ -+ return sys | usr; -+} -+ -+static inline u32 aa_usr_sid(u32 sid) -+{ -+ return sid & 0xffff0000; -+} -+ -+static inline u32 aa_sys_sid(u32 sid) -+{ -+ return sid & 0xffff; -+} -+ -+#endif /* __AA_SID_H */ -diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c -new file mode 100644 -index 0000000..381c164 ---- /dev/null -+++ b/security/apparmor/ipc.c -@@ -0,0 +1,106 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor ipc mediation -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ */ -+ -+#include -+#include -+ -+#include "include/audit.h" -+#include "include/capability.h" -+#include "include/context.h" -+#include "include/policy.h" -+ -+ -+struct aa_audit_ptrace { -+ struct aa_audit base; -+ -+ pid_t tracer, tracee; -+}; -+ -+/* call back to audit ptrace fields */ -+static void audit_cb(struct audit_buffer *ab, void *va) -+{ -+ struct aa_audit_ptrace *sa = va; -+ audit_log_format(ab, " tracer=%d tracee=%d", sa->tracer, sa->tracee); -+} -+ -+static int aa_audit_ptrace(struct aa_profile *profile, -+ struct aa_audit_ptrace *sa) -+{ -+ return aa_audit(AUDIT_APPARMOR_AUTO, profile, (struct aa_audit *)sa, -+ audit_cb); -+} -+ -+int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, -+ struct aa_profile *tracee, unsigned int mode) -+{ -+ /* TODO: currently only based on capability, not extended ptrace -+ * rules, -+ * Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH -+ */ -+ -+ if (!tracer || tracer == tracee) -+ return 0; -+ /* log this capability request */ -+ return aa_capable(tracer_task, tracer, CAP_SYS_PTRACE, 1); -+} -+ -+int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, -+ unsigned int mode) -+{ -+ /* -+ * tracer can ptrace tracee when -+ * - tracer is unconfined || -+ * - tracer & tracee are in the same namespace && -+ * - tracer is in complain mode -+ * - tracer has rules allowing it to trace tracee currently this is: -+ * - confined by the same profile || -+ * - tracer profile has CAP_SYS_PTRACE -+ */ -+ -+ struct aa_profile *tracer_p; -+ const struct cred *cred = aa_get_task_policy(tracer, &tracer_p); -+ int error = 0; -+ -+ if (tracer_p) { -+ struct aa_audit_ptrace sa; -+ memset(&sa, 0, sizeof(sa)); -+ sa.base.operation = "ptrace"; -+ sa.base.gfp_mask = GFP_ATOMIC; -+ sa.tracer = tracer->pid; -+ sa.tracee = tracee->pid; -+ /* FIXME: different namespace restriction can be lifted -+ * if, namespace are matched to AppArmor namespaces -+ */ -+ if (tracer->nsproxy != tracee->nsproxy) { -+ sa.base.info = "different namespaces"; -+ sa.base.error = -EPERM; -+ aa_audit(AUDIT_APPARMOR_DENIED, tracer_p, &sa.base, -+ audit_cb); -+ } else { -+ struct aa_profile *tracee_p; -+ struct cred *lcred = aa_get_task_policy(tracee, -+ &tracee_p); -+ -+ sa.base.error = aa_may_ptrace(tracer, tracer_p, -+ tracee_p, mode); -+ sa.base.error = aa_audit_ptrace(tracer_p, &sa); -+ -+ put_cred(lcred); -+ } -+ error = sa.base.error; -+ } -+ put_cred(cred); -+ -+ return error; -+} -diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c -new file mode 100644 -index 0000000..5dbd16d ---- /dev/null -+++ b/security/apparmor/lib.c -@@ -0,0 +1,100 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains basic common functions used in AppArmor -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ */ -+ -+#include -+#include -+ -+#include "include/audit.h" -+ -+void info_message(const char *str) -+{ -+ struct aa_audit sa; -+ memset(&sa, 0, sizeof(sa)); -+ sa.gfp_mask = GFP_KERNEL; -+ sa.info = str; -+ printk(KERN_INFO "AppArmor: %s\n", str); -+ if (audit_enabled) -+ aa_audit(AUDIT_APPARMOR_STATUS, NULL, &sa, NULL); -+} -+ -+char *strchrnul(const char *s, int c) -+{ -+ for (; *s != (char)c && *s != '\0'; ++s) -+ ; -+ return (char *)s; -+} -+ -+char *aa_split_name_from_ns(char *args, char **ns_name) -+{ -+ char *name = strstrip(args); -+ -+ *ns_name = NULL; -+ if (args[0] == ':') { -+ char *split = strstrip(strchr(&args[1], ':')); -+ -+ if (!split) -+ return NULL; -+ -+ *split = 0; -+ *ns_name = &args[1]; -+ name = strstrip(split + 1); -+ } -+ if (*name == 0) -+ name = NULL; -+ -+ return name; -+} -+ -+char *new_compound_name(const char *n1, const char *n2) -+{ -+ char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL); -+ if (name) -+ sprintf(name, "%s//%s", n1, n2); -+ return name; -+} -+ -+/** -+ * aa_strneq - compare null terminated @str to a non null terminated substring -+ * @str: a null terminated string -+ * @sub: a substring, not necessarily null terminated -+ * @len: length of @sub to compare -+ * -+ * The @str string must be full consumed for this to be considered a match -+ */ -+int aa_strneq(const char *str, const char *sub, int len) -+{ -+ int res = strncmp(str, sub, len); -+ if (res) -+ return 0; -+ if (str[len] == 0) -+ return 1; -+ return 0; -+} -+ -+const char *fqname_subname(const char *name) -+{ -+ char *split; -+ /* check for namespace which begins with a : and ends with : or \0 */ -+ name = strstrip((char *) name); -+ if (*name == ':') { -+ split = strchrnul(name + 1, ':'); -+ if (*split == '\0') -+ return NULL; -+ name = strstrip(split + 1); -+ } -+ for (split = strstr(name, "//"); split; split = strstr(name, "//")) { -+ name = split + 2; -+ } -+ return name; -+} -diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c -new file mode 100644 -index 0000000..5becf5e ---- /dev/null -+++ b/security/apparmor/lsm.c -@@ -0,0 +1,1063 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor LSM hooks. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "include/apparmor.h" -+#include "include/apparmorfs.h" -+#include "include/audit.h" -+#include "include/capability.h" -+#include "include/context.h" -+#include "include/file.h" -+#include "include/ipc.h" -+#include "include/net.h" -+#include "include/path.h" -+#include "include/policy.h" -+#include "include/procattr.h" -+ -+/* Flag indicating whether initialization completed */ -+int apparmor_initialized; -+ -+ -+/* -+ * LSM hook functions -+ */ -+ -+/* -+ * prepare new aa_task_context for modification by prepare_cred block -+ */ -+static int apparmor_cred_prepare(struct cred *new, const struct cred *old, -+ gfp_t gfp) -+{ -+ struct aa_task_context *cxt = aa_dup_task_context(old->security, gfp); -+ if (!cxt) -+ return -ENOMEM; -+ new->security = cxt; -+ return 0; -+} -+ -+/* -+ * free the associated aa_task_context and put its profiles -+ */ -+static void apparmor_cred_free(struct cred *cred) -+{ -+ struct aa_task_context *cxt = cred->security; -+ cred->security = NULL; -+ aa_free_task_context(cxt); -+} -+ -+ -+static int apparmor_ptrace_may_access(struct task_struct *child, -+ unsigned int mode) -+{ -+ return aa_ptrace(current, child, mode); -+} -+ -+ -+static int apparmor_ptrace_traceme(struct task_struct *parent) -+{ -+ return aa_ptrace(parent, current, PTRACE_MODE_ATTACH); -+} -+ -+/* Derived from security/commoncap.c:cap_capget */ -+static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, -+ kernel_cap_t *inheritable, kernel_cap_t *permitted) -+{ -+ struct aa_profile *profile; -+ const struct cred *cred; -+ -+ rcu_read_lock(); -+ cred = __task_cred(target); -+ aa_cred_policy(cred, &profile); -+ -+ *effective = cred->cap_effective; -+ *inheritable = cred->cap_inheritable; -+ *permitted = cred->cap_permitted; -+ -+ if (profile) { -+ *effective = cap_combine(*effective, profile->caps.set); -+ *effective = cap_intersect(*effective, profile->caps.allowed); -+ } -+ rcu_read_unlock(); -+ -+ return 0; -+} -+ -+static int apparmor_capable(struct task_struct *task, const struct cred *cred, -+ int cap, int audit) -+{ -+ struct aa_profile *profile; -+ /* cap_capable returns 0 on success, else -EPERM */ -+ int error = cap_capable(task, cred, cap, audit); -+ -+ aa_cred_policy(cred, &profile); -+ if (profile && (!error || cap_raised(profile->caps.set, cap))) -+ error = aa_capable(task, profile, cap, audit); -+ -+ return error; -+} -+ -+static int apparmor_sysctl(struct ctl_table *table, int op) -+{ -+ int error = 0; -+ struct aa_profile *profile = aa_current_profile_wupd(); -+ -+ if (profile) { -+ char *buffer, *name; -+ int mask; -+ -+ mask = 0; -+ if (op & 4) -+ mask |= MAY_READ; -+ if (op & 2) -+ mask |= MAY_WRITE; -+ -+ error = -ENOMEM; -+ buffer = (char *)__get_free_page(GFP_KERNEL); -+ if (!buffer) -+ goto out; -+ -+ /* -+ * TODO: convert this over to using a global or per -+ * namespace control instead of a hard coded /proc -+ */ -+ name = sysctl_pathname(table, buffer, PAGE_SIZE); -+ if (name && name - buffer >= 5) { -+ struct path_cond cond = { 0, S_IFREG }; -+ name -= 5; -+ memcpy(name, "/proc", 5); -+ error = aa_pathstr_perm(profile, "sysctl", name, mask, -+ &cond); -+ } -+ free_page((unsigned long)buffer); -+ } -+ -+out: -+ return error; -+} -+ -+static int common_perm(const char *op, struct path *path, u16 mask, -+ struct path_cond *cond) -+{ -+ struct aa_profile *profile; -+ int error = 0; -+ -+ profile = aa_current_profile(); -+ if (profile) -+ error = aa_path_perm(profile, op, path, mask, cond); -+ -+ return error; -+} -+ -+static int common_perm_dentry(const char *op, struct path *dir, -+ struct dentry *dentry, u16 mask, -+ struct path_cond *cond) -+{ -+ struct path path = { dir->mnt, dentry }; -+ -+ return common_perm(op, &path, mask, cond); -+} -+ -+static int common_perm_rm(const char *op, struct path *dir, -+ struct dentry *dentry, u16 mask) -+{ -+ struct inode *inode = dentry->d_inode; -+ struct path_cond cond = {}; -+ -+ if (!dir->mnt || !inode || !mediated_filesystem(inode)) -+ return 0; -+ -+ cond.uid = inode->i_uid; -+ cond.mode = inode->i_mode; -+ -+ return common_perm_dentry(op, dir, dentry, mask, &cond); -+} -+ -+static int common_perm_create(const char *op, struct path *dir, -+ struct dentry *dentry, u16 mask, umode_t mode) -+{ -+ struct path_cond cond = { current_fsuid(), mode }; -+ -+ if (!dir->mnt || !mediated_filesystem(dir->dentry->d_inode)) -+ return 0; -+ -+ return common_perm_dentry(op, dir, dentry, mask, &cond); -+} -+ -+static int apparmor_path_unlink(struct path *dir, struct dentry *dentry) -+{ -+ return common_perm_rm("unlink", dir, dentry, MAY_WRITE); -+} -+ -+static int apparmor_path_mkdir(struct path *dir, struct dentry *dentry, -+ int mode) -+{ -+ return common_perm_create("mkdir", dir, dentry, AA_MAY_CREATE, S_IFDIR); -+} -+ -+static int apparmor_path_rmdir(struct path *dir, struct dentry *dentry) -+{ -+ return common_perm_rm("rmdir", dir, dentry, MAY_WRITE); -+} -+ -+static int apparmor_path_mknod(struct path *dir, struct dentry *dentry, -+ int mode, unsigned int dev) -+{ -+ return common_perm_create("mknod", dir, dentry, AA_MAY_CREATE, mode); -+} -+ -+static int apparmor_path_truncate(struct path *path, loff_t length, -+ unsigned int time_attrs) -+{ -+ struct path_cond cond = { path->dentry->d_inode->i_uid, -+ path->dentry->d_inode->i_mode }; -+ -+ if (!path->mnt || !mediated_filesystem(path->dentry->d_inode)) -+ return 0; -+ return common_perm("truncate", path, MAY_WRITE, &cond); -+} -+ -+static int apparmor_path_symlink(struct path *dir, struct dentry *dentry, -+ const char *old_name) -+{ -+ return common_perm_create("symlink_create", dir, dentry, AA_MAY_CREATE, -+ S_IFLNK); -+} -+ -+static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir, -+ struct dentry *new_dentry) -+{ -+ struct aa_profile *profile; -+ int error = 0; -+ -+ if (!mediated_filesystem(old_dentry->d_inode)) -+ return 0; -+ -+ profile = aa_current_profile_wupd(); -+ if (profile) -+ error = aa_path_link(profile, old_dentry, new_dir, new_dentry); -+ return error; -+} -+ -+static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry, -+ struct path *new_dir, struct dentry *new_dentry) -+{ -+ struct aa_profile *profile; -+ int error = 0; -+ -+ if (!mediated_filesystem(old_dentry->d_inode)) -+ return 0; -+ -+ profile = aa_current_profile_wupd(); -+ if (profile) { -+ struct path old_path = { old_dir->mnt, old_dentry }; -+ struct path new_path = { new_dir->mnt, new_dentry }; -+ struct path_cond cond = { old_dentry->d_inode->i_uid, -+ old_dentry->d_inode->i_mode }; -+ -+ error = aa_path_perm(profile, "rename_src", &old_path, -+ MAY_READ | MAY_WRITE, &cond); -+ if (!error) -+ error = aa_path_perm(profile, "rename_dest", &new_path, -+ AA_MAY_CREATE | MAY_WRITE, &cond); -+ -+ } -+ return error; -+} -+ -+static int apparmor_dentry_open(struct file *file, const struct cred *cred) -+{ -+ struct aa_profile *profile; -+ int error = 0; -+ -+ /* If in exec permission is handled by bprm hooks */ -+ if (current->in_execve || -+ !mediated_filesystem(file->f_path.dentry->d_inode)) -+ return 0; -+ -+ aa_cred_policy(cred, &profile); -+ if (profile) { -+ struct aa_file_cxt *fcxt = file->f_security; -+ struct inode *inode = file->f_path.dentry->d_inode; -+ struct path_cond cond = { inode->i_uid, inode->i_mode }; -+ -+ error = aa_path_perm(profile, "open", &file->f_path, -+ aa_map_file_to_perms(file), &cond); -+ fcxt->profile = aa_get_profile(profile); -+ /* todo cache actual allowed permissions */ -+ fcxt->allowed = 0; -+ } -+ -+ return error; -+} -+ -+static int apparmor_file_alloc_security(struct file *file) -+{ -+ file->f_security = aa_alloc_file_context(GFP_KERNEL); -+ if (!file->f_security) -+ return -ENOMEM; -+ return 0; -+ -+} -+ -+static void apparmor_file_free_security(struct file *file) -+{ -+ struct aa_file_cxt *cxt = file->f_security; -+ -+ aa_free_file_context(cxt); -+} -+ -+static int apparmor_file_permission(struct file *file, int mask) -+{ -+ /* -+ * Most basic (rw) file access is revalidated at exec. -+ * The revalidation done here is for parent/child hat -+ * file accesses. -+ * -+ * Currently profile replacement does not cause revalidation -+ * or file revocation. -+ * -+ * TODO: cache profiles that have revalidated? -+ */ -+ struct aa_file_cxt *fcxt = file->f_security; -+ struct aa_profile *profile, *fprofile = fcxt->profile; -+ int error = 0; -+ -+ if (!fprofile || !file->f_path.mnt || -+ !mediated_filesystem(file->f_path.dentry->d_inode)) -+ return 0; -+ -+ profile = aa_current_profile(); -+ /* TODO: Enable at exec time revalidation of files -+ if (profile && (fprofile != profile) && -+ ((PROFILE_IS_HAT(profile) && (profile->parent == fprofile)) || -+ (PROFILE_IS_HAT(fprofile) && (fprofile->parent == profile)))) -+ error = aa_file_perm(profile, "file_perm", file, mask); -+ */ -+ if (profile && ((fprofile != profile) || (mask & ~fcxt->allowed))) -+ error = aa_file_perm(profile, "file_perm", file, mask); -+ -+ return error; -+} -+ -+static int common_file_perm(const char *op, struct file *file, u16 mask) -+{ -+ const struct aa_file_cxt *fcxt = file->f_security; -+ struct aa_profile *profile, *fprofile = fcxt->profile; -+ int error = 0; -+ -+ if (!fprofile || !file->f_path.mnt || -+ !mediated_filesystem(file->f_path.dentry->d_inode)) -+ return 0; -+ -+ profile = aa_current_profile_wupd(); -+ if (profile && ((fprofile != profile) || (mask & ~fcxt->allowed))) -+ error = aa_file_perm(profile, op, file, mask); -+ -+ return error; -+} -+ -+static int apparmor_file_lock(struct file *file, unsigned int cmd) -+{ -+ u16 mask = AA_MAY_LOCK; -+ -+ if (cmd == F_WRLCK) -+ mask |= MAY_WRITE; -+ -+ return common_file_perm("file_lock", file, mask); -+} -+ -+ -+/* -+ * AppArmor doesn't current use the fcntl hook. -+ * -+ * FIXME - these are not implemented yet - REMOVE file_fcntl hook -+ * NOTE: some of the file control commands are further mediated -+ * by other hooks -+ * F_SETOWN - security_file_set_fowner -+ * F_SETLK - security_file_lock -+ * F_SETLKW - security_file_lock -+ * O_APPEND - AppArmor mediates append as a subset of full write -+ * so changing from full write to appending write is -+ * dropping priviledge and not restricted. -+ -+ -+static int apparmor_file_fcntl(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ return 0; -+} -+*/ -+ -+static int common_mmap(struct file *file, const char *operation, -+ unsigned long prot, unsigned long flags) -+{ -+ struct dentry *dentry; -+ int mask = 0; -+ -+ if (!file || !file->f_security) -+ return 0; -+ -+ if (prot & PROT_READ) -+ mask |= MAY_READ; -+ /* Private mappings don't require write perms since they don't -+ * write back to the files */ -+ if ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE)) -+ mask |= MAY_WRITE; -+ if (prot & PROT_EXEC) -+ mask |= AA_EXEC_MMAP; -+ -+ dentry = file->f_path.dentry; -+ return common_file_perm(operation, file, mask); -+} -+ -+static int apparmor_file_mmap(struct file *file, unsigned long reqprot, -+ unsigned long prot, unsigned long flags, -+ unsigned long addr, unsigned long addr_only) -+{ -+ if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO)) { -+ struct aa_profile *profile = aa_current_profile_wupd(); -+ if (profile) -+ /* future control check here */ -+ return -EACCES; -+ else -+ return -EACCES; -+ } -+ -+ return common_mmap(file, "file_mmap", prot, flags); -+} -+ -+static int apparmor_file_mprotect(struct vm_area_struct *vma, -+ unsigned long reqprot, unsigned long prot) -+{ -+ return common_mmap(vma->vm_file, "file_mprotect", prot, -+ !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); -+} -+ -+static int apparmor_getprocattr(struct task_struct *task, char *name, -+ char **value) -+{ -+ int error = -ENOENT; -+ struct aa_namespace *ns; -+ struct aa_profile *profile, *onexec, *prev; -+ const struct cred *cred = aa_get_task_policy(task, &profile); -+ struct aa_task_context *cxt = cred->security; -+ ns = cxt->sys.profile->ns; -+ onexec = cxt->sys.onexec; -+ prev = cxt->sys.previous; -+ -+ /* task must be either querying itself, unconfined or can ptrace */ -+ if (current != task && profile && !capable(CAP_SYS_PTRACE)) { -+ error = -EPERM; -+ } else { -+ if (strcmp(name, "current") == 0) { -+ error = aa_getprocattr(ns, profile, value); -+ } else if (strcmp(name, "prev") == 0) { -+ if (prev) -+ error = aa_getprocattr(ns, prev, value); -+ } else if (strcmp(name, "exec") == 0) { -+ if (onexec) -+ error = aa_getprocattr(ns, onexec, value); -+ } else { -+ error = -EINVAL; -+ } -+ } -+ -+ put_cred(cred); -+ -+ return error; -+} -+ -+static int apparmor_setprocattr(struct task_struct *task, char *name, -+ void *value, size_t size) -+{ -+ char *command, *args; -+ int error; -+ -+ if (size == 0 || size >= PAGE_SIZE) -+ return -EINVAL; -+ -+ /* task can only write its own attributes */ -+ if (current != task) -+ return -EACCES; -+ -+ args = value; -+ args[size] = '\0'; -+ args = strstrip(args); -+ command = strsep(&args, " "); -+ if (!args) -+ return -EINVAL; -+ while (isspace(*args)) -+ args++; -+ if (!*args) -+ return -EINVAL; -+ -+ if (strcmp(name, "current") == 0) { -+ if (strcmp(command, "changehat") == 0) { -+ error = aa_setprocattr_changehat(args, !AA_DO_TEST); -+ } else if (strcmp(command, "permhat") == 0) { -+ error = aa_setprocattr_changehat(args, AA_DO_TEST); -+ } else if (strcmp(command, "changeprofile") == 0) { -+ error = aa_setprocattr_changeprofile(args, 0, -+ !AA_DO_TEST); -+ } else if (strcmp(command, "permprofile") == 0) { -+ error = aa_setprocattr_changeprofile(args, 0, -+ AA_DO_TEST); -+ } else if (strcmp(command, "permipc") == 0) { -+ error = aa_setprocattr_permipc(args); -+ } else { -+ struct aa_audit sa; -+ memset(&sa, 0, sizeof(sa)); -+ sa.operation = "setprocattr"; -+ sa.gfp_mask = GFP_KERNEL; -+ sa.info = name; -+ sa.error = -EINVAL; -+ return aa_audit(AUDIT_APPARMOR_DENIED, NULL, &sa, NULL); -+ } -+ } else if (strcmp(name, "exec") == 0) { -+ error = aa_setprocattr_changeprofile(strstrip(args), 1, -+ !AA_DO_TEST); -+ } else { -+ /* only support the "current" and "exec" process attributes */ -+ return -EINVAL; -+ } -+ if (!error) -+ error = size; -+ return error; -+} -+ -+static int apparmor_task_setrlimit(unsigned int resource, -+ struct rlimit *new_rlim) -+{ -+ struct aa_profile *profile = aa_current_profile_wupd(); -+ int error = 0; -+ -+ if (profile) { -+ error = aa_task_setrlimit(profile, resource, new_rlim); -+ } -+ -+ return error; -+} -+ -+#ifdef CONFIG_SECURITY_APPARMOR_NETWORK -+static int apparmor_socket_create(int family, int type, int protocol, int kern){ -+ struct aa_profile *profile; -+ int error = 0; -+ -+ if (kern) -+ return 0; -+ -+ profile = aa_current_profile(); -+ if (profile) -+ error = aa_net_perm(profile, "socket_create", family, -+ type, protocol); -+ return error; -+} -+ -+static int apparmor_socket_post_create(struct socket *sock, int family, -+ int type, int protocol, int kern) -+{ -+ struct sock *sk = sock->sk; -+ -+ if (kern) -+ return 0; -+ -+ return aa_revalidate_sk(sk, "socket_post_create"); -+} -+ -+static int apparmor_socket_bind(struct socket *sock, -+ struct sockaddr *address, int addrlen) -+{ -+ struct sock *sk = sock->sk; -+ -+ return aa_revalidate_sk(sk, "socket_bind"); -+} -+ -+static int apparmor_socket_connect(struct socket *sock, -+ struct sockaddr *address, int addrlen) -+{ -+ struct sock *sk = sock->sk; -+ -+ return aa_revalidate_sk(sk, "socket_connect"); -+} -+ -+static int apparmor_socket_listen(struct socket *sock, int backlog) -+{ -+ struct sock *sk = sock->sk; -+ -+ return aa_revalidate_sk(sk, "socket_listen"); -+} -+ -+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) -+{ -+ struct sock *sk = sock->sk; -+ -+ return aa_revalidate_sk(sk, "socket_accept"); -+} -+ -+static int apparmor_socket_sendmsg(struct socket *sock, -+ struct msghdr *msg, int size) -+{ -+ struct sock *sk = sock->sk; -+ -+ return aa_revalidate_sk(sk, "socket_sendmsg"); -+} -+ -+static int apparmor_socket_recvmsg(struct socket *sock, -+ struct msghdr *msg, int size, int flags) -+{ -+ struct sock *sk = sock->sk; -+ -+ return aa_revalidate_sk(sk, "socket_recvmsg"); -+} -+ -+static int apparmor_socket_getsockname(struct socket *sock) -+{ -+ struct sock *sk = sock->sk; -+ -+ return aa_revalidate_sk(sk, "socket_getsockname"); -+} -+ -+static int apparmor_socket_getpeername(struct socket *sock) -+{ -+ struct sock *sk = sock->sk; -+ -+ return aa_revalidate_sk(sk, "socket_getpeername"); -+} -+ -+static int apparmor_socket_getsockopt(struct socket *sock, int level, -+ int optname) -+{ -+ struct sock *sk = sock->sk; -+ -+ return aa_revalidate_sk(sk, "socket_getsockopt"); -+} -+ -+static int apparmor_socket_setsockopt(struct socket *sock, int level, -+ int optname) -+{ -+ struct sock *sk = sock->sk; -+ -+ return aa_revalidate_sk(sk, "socket_setsockopt"); -+} -+ -+static int apparmor_socket_shutdown(struct socket *sock, int how) -+{ -+ struct sock *sk = sock->sk; -+ -+ return aa_revalidate_sk(sk, "socket_shutdown"); -+} -+#endif -+ -+static struct security_operations apparmor_ops = { -+ .name = "apparmor", -+ -+ .ptrace_may_access = apparmor_ptrace_may_access, -+ .ptrace_traceme = apparmor_ptrace_traceme, -+ .capget = apparmor_capget, -+ .sysctl = apparmor_sysctl, -+ .capable = apparmor_capable, -+/* -+ .inode_create = apparmor_inode_create, -+ .inode_setattr = apparmor_inode_setattr, -+ .inode_setxattr = apparmor_inode_setxattr, -+ .inode_getxattr = apparmor_inode_getxattr, -+ .inode_listxattr = apparmor_inode_listxattr, -+ .inode_removexattr = apparmor_inode_removexattr, -+ .inode_permission = ??? use to mediate owner access to non-mediated fs -+*/ -+ -+ .path_link = apparmor_path_link, -+ .path_unlink = apparmor_path_unlink, -+ .path_symlink = apparmor_path_symlink, -+ .path_mkdir = apparmor_path_mkdir, -+ .path_rmdir = apparmor_path_rmdir, -+ .path_mknod = apparmor_path_mknod, -+ .path_rename = apparmor_path_rename, -+ .path_truncate = apparmor_path_truncate, -+ .dentry_open = apparmor_dentry_open, -+ -+ .file_permission = apparmor_file_permission, -+ .file_alloc_security = apparmor_file_alloc_security, -+ .file_free_security = apparmor_file_free_security, -+ .file_mmap = apparmor_file_mmap, -+ .file_mprotect = apparmor_file_mprotect, -+ .file_lock = apparmor_file_lock, -+ -+/* .file_fcntl = apparmor_file_fcntl, */ -+ -+ .getprocattr = apparmor_getprocattr, -+ .setprocattr = apparmor_setprocattr, -+ -+#ifdef CONFIG_SECURITY_APPARMOR_NETWORK -+ .socket_create = apparmor_socket_create, -+ .socket_post_create = apparmor_socket_post_create, -+ .socket_bind = apparmor_socket_bind, -+ .socket_connect = apparmor_socket_connect, -+ .socket_listen = apparmor_socket_listen, -+ .socket_accept = apparmor_socket_accept, -+ .socket_sendmsg = apparmor_socket_sendmsg, -+ .socket_recvmsg = apparmor_socket_recvmsg, -+ .socket_getsockname = apparmor_socket_getsockname, -+ .socket_getpeername = apparmor_socket_getpeername, -+ .socket_getsockopt = apparmor_socket_getsockopt, -+ .socket_setsockopt = apparmor_socket_setsockopt, -+ .socket_shutdown = apparmor_socket_shutdown, -+#endif -+ -+ .cred_free = apparmor_cred_free, -+ .cred_prepare = apparmor_cred_prepare, -+ -+ .bprm_set_creds = apparmor_bprm_set_creds, -+ // .bprm_committing_creds = apparmor_bprm_committing_creds, -+ .bprm_committed_creds = apparmor_bprm_committed_creds, -+ .bprm_secureexec = apparmor_bprm_secureexec, -+ -+ .task_setrlimit = apparmor_task_setrlimit, -+}; -+ -+ -+/* -+ * AppArmor sysfs module parameters -+ */ -+ -+static int param_set_aabool(const char *val, struct kernel_param *kp); -+static int param_get_aabool(char *buffer, struct kernel_param *kp); -+#define param_check_aabool(name, p) __param_check(name, p, int) -+ -+static int param_set_aauint(const char *val, struct kernel_param *kp); -+static int param_get_aauint(char *buffer, struct kernel_param *kp); -+#define param_check_aauint(name, p) __param_check(name, p, int) -+ -+static int param_set_aalockpolicy(const char *val, struct kernel_param *kp); -+static int param_get_aalockpolicy(char *buffer, struct kernel_param *kp); -+#define param_check_aalockpolicy(name, p) __param_check(name, p, int) -+ -+static int param_set_audit(const char *val, struct kernel_param *kp); -+static int param_get_audit(char *buffer, struct kernel_param *kp); -+#define param_check_audit(name, p) __param_check(name, p, int) -+ -+static int param_set_mode(const char *val, struct kernel_param *kp); -+static int param_get_mode(char *buffer, struct kernel_param *kp); -+#define param_check_mode(name, p) __param_check(name, p, int) -+ -+/* Flag values, also controllable via /sys/module/apparmor/parameters -+ * We define special types as we want to do additional mediation. -+ */ -+ -+/* AppArmor global enforcement switch - complain, enforce, kill */ -+enum profile_mode g_profile_mode = APPARMOR_ENFORCE; -+module_param_call(mode, param_set_mode, param_get_mode, -+ &g_profile_mode, S_IRUSR | S_IWUSR); -+ -+/* Debug mode */ -+int g_apparmor_debug; -+module_param_named(debug, g_apparmor_debug, aabool, S_IRUSR | S_IWUSR); -+ -+/* Audit mode */ -+enum audit_mode g_apparmor_audit; -+module_param_call(audit, param_set_audit, param_get_audit, -+ &g_apparmor_audit, S_IRUSR | S_IWUSR); -+ -+/* Determines if audit header is included in audited messages. This -+ * provides more context if the audit daemon is not running -+ */ -+int g_apparmor_audit_header; -+module_param_named(audit_header, g_apparmor_audit_header, aabool, -+ S_IRUSR | S_IWUSR); -+ -+/* lock out loading/removal of policy -+ * TODO: add in at boot loading of policy, which is the only way to -+ * load policy, if lock_policy is set -+ */ -+int g_apparmor_lock_policy; -+module_param_named(lock_policy, g_apparmor_lock_policy, aalockpolicy, -+ S_IRUSR | S_IWUSR); -+ -+/* Syscall logging mode */ -+int g_apparmor_logsyscall; -+module_param_named(logsyscall, g_apparmor_logsyscall, aabool, -+ S_IRUSR | S_IWUSR); -+ -+/* Maximum pathname length before accesses will start getting rejected */ -+unsigned int g_apparmor_path_max = 2 * PATH_MAX; -+module_param_named(path_max, g_apparmor_path_max, aauint, S_IRUSR | S_IWUSR); -+ -+/* Boot time disable flag */ -+#ifdef CONFIG_SECURITY_APPARMOR_DISABLE -+#define AA_ENABLED_PERMS 0600 -+#else -+#define AA_ENABLED_PERMS 0400 -+#endif -+static int param_set_aa_enabled(const char *val, struct kernel_param *kp); -+static unsigned int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; -+module_param_call(enabled, param_set_aa_enabled, param_get_aauint, -+ &apparmor_enabled, AA_ENABLED_PERMS); -+ -+static int __init apparmor_enabled_setup(char *str) -+{ -+ apparmor_enabled = simple_strtol(str, NULL, 0); -+ return 1; -+} -+__setup("apparmor=", apparmor_enabled_setup); -+ -+static int param_set_aalockpolicy(const char *val, struct kernel_param *kp) -+{ -+ if (__aa_task_is_confined(current)) -+ return -EPERM; -+ if (g_apparmor_lock_policy) -+ return -EACCES; -+ return param_set_bool(val, kp); -+} -+ -+static int param_get_aalockpolicy(char *buffer, struct kernel_param *kp) -+{ -+ if (__aa_task_is_confined(current)) -+ return -EPERM; -+ return param_get_bool(buffer, kp); -+} -+ -+static int param_set_aabool(const char *val, struct kernel_param *kp) -+{ -+ if (__aa_task_is_confined(current)) -+ return -EPERM; -+ return param_set_bool(val, kp); -+} -+ -+static int param_get_aabool(char *buffer, struct kernel_param *kp) -+{ -+ if (__aa_task_is_confined(current)) -+ return -EPERM; -+ return param_get_bool(buffer, kp); -+} -+ -+static int param_set_aauint(const char *val, struct kernel_param *kp) -+{ -+ if (__aa_task_is_confined(current)) -+ return -EPERM; -+ return param_set_uint(val, kp); -+} -+ -+static int param_get_aauint(char *buffer, struct kernel_param *kp) -+{ -+ if (__aa_task_is_confined(current)) -+ return -EPERM; -+ return param_get_uint(buffer, kp); -+} -+ -+/* allow run time disabling of apparmor */ -+static int param_set_aa_enabled(const char *val, struct kernel_param *kp) -+{ -+ unsigned long l; -+ -+ if (!apparmor_initialized) { -+ apparmor_enabled = 0; -+ return 0; -+ } -+ -+ if (__aa_task_is_confined(current)) -+ return -EPERM; -+ -+ if (!apparmor_enabled) -+ return -EINVAL; -+ -+ if (!val) -+ return -EINVAL; -+ -+ if (strict_strtoul(val, 0, &l) || l != 0) -+ return -EINVAL; -+ -+ apparmor_enabled = 0; -+ apparmor_disable(); -+ return 0; -+} -+ -+static int param_get_audit(char *buffer, struct kernel_param *kp) -+{ -+ if (__aa_task_is_confined(current)) -+ return -EPERM; -+ -+ if (!apparmor_enabled) -+ return -EINVAL; -+ -+ return sprintf(buffer, "%s", audit_mode_names[g_apparmor_audit]); -+} -+ -+static int param_set_audit(const char *val, struct kernel_param *kp) -+{ -+ int i; -+ if (__aa_task_is_confined(current)) -+ return -EPERM; -+ -+ if (!apparmor_enabled) -+ return -EINVAL; -+ -+ if (!val) -+ return -EINVAL; -+ -+ for (i = 0; i < AUDIT_MAX_INDEX; i++) { -+ if (strcmp(val, audit_mode_names[i]) == 0) { -+ g_apparmor_audit = i; -+ return 0; -+ } -+ } -+ -+ return -EINVAL; -+} -+ -+static int param_get_mode(char *buffer, struct kernel_param *kp) -+{ -+ if (__aa_task_is_confined(current)) -+ return -EPERM; -+ -+ if (!apparmor_enabled) -+ return -EINVAL; -+ -+ return sprintf(buffer, "%s", profile_mode_names[g_profile_mode]); -+} -+ -+static int param_set_mode(const char *val, struct kernel_param *kp) -+{ -+ int i; -+ if (__aa_task_is_confined(current)) -+ return -EPERM; -+ -+ if (!apparmor_enabled) -+ return -EINVAL; -+ -+ if (!val) -+ return -EINVAL; -+ -+ for (i = 0; i < APPARMOR_NAMES_MAX_INDEX; i++) { -+ if (strcmp(val, profile_mode_names[i]) == 0) { -+ g_profile_mode = i; -+ return 0; -+ } -+ } -+ -+ return -EINVAL; -+} -+ -+ -+/* -+ * AppArmor init functions -+ */ -+ -+static int set_init_cxt(void) -+{ -+ struct cred *cred = (struct cred *) current->real_cred; -+ struct aa_task_context *cxt; -+ -+ cxt = aa_alloc_task_context(GFP_KERNEL); -+ if (!cxt) -+ return -ENOMEM; -+ -+ cxt->sys.profile = aa_get_profile(default_namespace->unconfined); -+ cred->security = cxt; -+ -+ return 0; -+} -+ -+static int __init apparmor_init(void) -+{ -+ int error; -+ -+ if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) { -+ info_message("AppArmor disabled by boot time parameter\n"); -+ apparmor_enabled = 0; -+ return 0; -+ } -+ -+ /* -+ * Activated with fs_initcall -+ error = create_apparmorfs(); -+ if (error) { -+ AA_ERROR("Unable to activate AppArmor filesystem\n"); -+ goto createfs_out; -+ } -+ */ -+ -+ error = alloc_default_namespace(); -+ if (error) { -+ AA_ERROR("Unable to allocate default profile namespace\n"); -+ goto alloc_out; -+ } -+ -+ error = set_init_cxt(); -+ if (error) { -+ AA_ERROR("Failed to set context on init task\n"); -+ goto alloc_out; -+ } -+ -+ error = register_security(&apparmor_ops); -+ if (error) { -+ AA_ERROR("Unable to register AppArmor\n"); -+ goto register_security_out; -+ } -+ -+ /* Report that AppArmor successfully initialized */ -+ apparmor_initialized = 1; -+ if (g_profile_mode == APPARMOR_COMPLAIN) -+ info_message("AppArmor initialized: complain mode enabled"); -+ else if (g_profile_mode == APPARMOR_KILL) -+ info_message("AppArmor initialized: kill mode enabled"); -+ else -+ info_message("AppArmor initialized"); -+ -+ return error; -+ -+register_security_out: -+ free_default_namespace(); -+ -+alloc_out: -+ destroy_apparmorfs(); -+ -+/*createfs_out:*/ -+ apparmor_enabled = 0; -+ return error; -+ -+} -+ -+security_initcall(apparmor_init); -+ -+void apparmor_disable(void) -+{ -+ /* Remove and release all the profiles on the profile list. */ -+ aa_profile_ns_list_release(); -+ -+ /* FIXME: cleanup profiles references on files */ -+ free_default_namespace(); -+ -+ /* -+ * Delay for an rcu cycle to make sure that all active task -+ * context readers have finished, and all profiles have been -+ * freed by their rcu callbacks. -+ */ -+ synchronize_rcu(); -+ destroy_apparmorfs(); -+ apparmor_initialized = 0; -+ -+ info_message("AppArmor protection disabled"); -+} -+ -diff --git a/security/apparmor/match.c b/security/apparmor/match.c -new file mode 100644 -index 0000000..a22d106 ---- /dev/null -+++ b/security/apparmor/match.c -@@ -0,0 +1,293 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor dfa based regular expression matching engine -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ */ -+ -+#include -+#include -+#include -+ -+/* TODO: remove !!!! */ -+// #include -+ -+#include "include/apparmor.h" -+#include "include/match.h" -+#include "include/file.h" -+ -+static struct table_header *unpack_table(void *blob, size_t bsize) -+{ -+ struct table_header *table = NULL; -+ struct table_header th; -+ size_t tsize; -+ -+ if (bsize < sizeof(struct table_header)) -+ goto out; -+ -+ th.td_id = be16_to_cpu(*(u16 *) (blob)); -+ th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); -+ th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); -+ blob += sizeof(struct table_header); -+ -+ if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 || -+ th.td_flags == YYTD_DATA8)) -+ goto out; -+ -+ tsize = table_size(th.td_lolen, th.td_flags); -+ if (bsize < tsize) -+ goto out; -+ -+ table = kmalloc(tsize, GFP_KERNEL); -+ if (table) { -+ *table = th; -+ if (th.td_flags == YYTD_DATA8) -+ UNPACK_ARRAY(table->td_data, blob, th.td_lolen, -+ u8, byte_to_byte); -+ else if (th.td_flags == YYTD_DATA16) -+ UNPACK_ARRAY(table->td_data, blob, th.td_lolen, -+ u16, be16_to_cpu); -+ else -+ UNPACK_ARRAY(table->td_data, blob, th.td_lolen, -+ u32, be32_to_cpu); -+ } -+ -+out: -+ return table; -+} -+ -+int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size) -+{ -+ int hsize, i; -+ int error = -ENOMEM; -+ -+ /* get dfa table set header */ -+ if (size < sizeof(struct table_set_header)) -+ goto fail; -+ -+ if (ntohl(*(u32 *)blob) != YYTH_MAGIC) -+ goto fail; -+ -+ hsize = ntohl(*(u32 *)(blob + 4)); -+ if (size < hsize) -+ goto fail; -+ -+ blob += hsize; -+ size -= hsize; -+ -+ error = -EPROTO; -+ while (size > 0) { -+ struct table_header *table; -+ table = unpack_table(blob, size); -+ if (!table) -+ goto fail; -+ -+ switch (table->td_id) { -+ case YYTD_ID_ACCEPT: -+ case YYTD_ID_ACCEPT2: -+ case YYTD_ID_BASE: -+ dfa->tables[table->td_id - 1] = table; -+ if (table->td_flags != YYTD_DATA32) -+ goto fail; -+ break; -+ case YYTD_ID_DEF: -+ case YYTD_ID_NXT: -+ case YYTD_ID_CHK: -+ dfa->tables[table->td_id - 1] = table; -+ if (table->td_flags != YYTD_DATA16) -+ goto fail; -+ break; -+ case YYTD_ID_EC: -+ dfa->tables[table->td_id - 1] = table; -+ if (table->td_flags != YYTD_DATA8) -+ goto fail; -+ break; -+ default: -+ kfree(table); -+ goto fail; -+ } -+ -+ blob += table_size(table->td_lolen, table->td_flags); -+ size -= table_size(table->td_lolen, table->td_flags); -+ } -+ -+ return 0; -+ -+fail: -+ for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) { -+ kfree(dfa->tables[i]); -+ dfa->tables[i] = NULL; -+ } -+ return error; -+} -+ -+/** -+ * verify_dfa - verify that all the transitions and states in the dfa tables -+ * are in bounds. -+ * @dfa: dfa to test -+ * -+ * assumes dfa has gone through the verification done by unpacking -+ */ -+int verify_dfa(struct aa_dfa *dfa) -+{ -+ size_t i, state_count, trans_count; -+ int error = -EPROTO; -+ -+ /* check that required tables exist */ -+ if (!(dfa->tables[YYTD_ID_ACCEPT - 1] && -+ dfa->tables[YYTD_ID_ACCEPT2 - 1] && -+ dfa->tables[YYTD_ID_DEF - 1] && -+ dfa->tables[YYTD_ID_BASE - 1] && -+ dfa->tables[YYTD_ID_NXT - 1] && -+ dfa->tables[YYTD_ID_CHK - 1])) -+ goto out; -+ -+ /* accept.size == default.size == base.size */ -+ state_count = dfa->tables[YYTD_ID_BASE - 1]->td_lolen; -+ if (!(state_count == dfa->tables[YYTD_ID_DEF - 1]->td_lolen && -+ state_count == dfa->tables[YYTD_ID_ACCEPT - 1]->td_lolen && -+ state_count == dfa->tables[YYTD_ID_ACCEPT2 - 1]->td_lolen)) -+ goto out; -+ -+ /* next.size == chk.size */ -+ trans_count = dfa->tables[YYTD_ID_NXT - 1]->td_lolen; -+ if (trans_count != dfa->tables[YYTD_ID_CHK - 1]->td_lolen) -+ goto out; -+ -+ /* if equivalence classes then its table size must be 256 */ -+ if (dfa->tables[YYTD_ID_EC - 1] && -+ dfa->tables[YYTD_ID_EC - 1]->td_lolen != 256) -+ goto out; -+ -+ for (i = 0; i < state_count; i++) { -+ if (DEFAULT_TABLE(dfa)[i] >= state_count) -+ goto out; -+ if (BASE_TABLE(dfa)[i] >= trans_count + 256) -+ goto out; -+ } -+ -+ for (i = 0; i < trans_count ; i++) { -+ if (NEXT_TABLE(dfa)[i] >= state_count) -+ goto out; -+ if (CHECK_TABLE(dfa)[i] >= state_count) -+ goto out; -+ } -+ -+ /* verify accept permissions */ -+ for (i = 0; i < state_count; i++) { -+ int mode = ACCEPT_TABLE(dfa)[i]; -+ -+ if (mode & ~DFA_VALID_PERM_MASK) -+ goto out; -+ if (ACCEPT_TABLE2(dfa)[i] & ~DFA_VALID_PERM2_MASK) -+ goto out; -+ -+ } -+ -+ error = 0; -+out: -+ return error; -+} -+ -+struct aa_dfa *aa_match_alloc(void) -+{ -+ return kzalloc(sizeof(struct aa_dfa), GFP_KERNEL); -+} -+ -+void aa_match_free(struct aa_dfa *dfa) -+{ -+ if (dfa) { -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) -+ kfree(dfa->tables[i]); -+ } -+ kfree(dfa); -+} -+ -+/** -+ * aa_dfa_match_len - traverse @dfa to find state @str stops at -+ * @dfa: the dfa to match @str against -+ * @start: the state of the dfa to start matching in -+ * @str: the string of bytes to match against the dfa -+ * @len: length of the string of bytes to match -+ * -+ * aa_dfa_match_len will match @str against the dfa and return the state it -+ * finished matching in. The final state can be used to look up the accepting -+ * label, or as the start state of a continuing match. -+ * -+ * This function will happily match again the 0 byte and only finishes -+ * when @len input is consumed. -+ */ -+unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, -+ const char *str, int len) -+{ -+ u16 *def = DEFAULT_TABLE(dfa); -+ u32 *base = BASE_TABLE(dfa); -+ u16 *next = NEXT_TABLE(dfa); -+ u16 *check = CHECK_TABLE(dfa); -+ unsigned int state = start, pos; -+ -+ if (state == 0) -+ return 0; -+ -+ /* current state is , matching character *str */ -+ if (dfa->tables[YYTD_ID_EC - 1]) { -+ u8 *equiv = EQUIV_TABLE(dfa); -+ for (; len; len--) { -+ pos = base[state] + equiv[(u8)*str++]; -+ if (check[pos] == state) -+ state = next[pos]; -+ else -+ state = def[state]; -+ } -+ } else { -+ for (; len; len--) { -+ pos = base[state] + (u8)*str++; -+ if (check[pos] == state) -+ state = next[pos]; -+ else -+ state = def[state]; -+ } -+ } -+ return state; -+} -+ -+ -+/** -+ * aa_dfa_next_state - traverse @dfa to find state @str stops at -+ * @dfa: the dfa to match @str against -+ * @start: the state of the dfa to start matching in -+ * @str: the null terminated string of bytes to match against the dfa -+ * -+ * aa_dfa_next_state will match @str against the dfa and return the state it -+ * finished matching in. The final state can be used to look up the accepting -+ * label, or as the start state of a continuing match. -+ */ -+unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, -+ const char *str) -+{ -+ return aa_dfa_match_len(dfa, start, str, strlen(str)); -+} -+ -+/** -+ * aa_dfa_null_transition - step to next state after null character -+ * @dfa: the dfa to match against -+ * @start: the state of the dfa to start matching in -+ * -+ * aa_dfa_null_transition transitions to the next state after a null -+ * character which is not used in standard matching and is only -+ * used to seperate pairs. -+ */ -+unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, unsigned int start) -+{ -+ return aa_dfa_match_len(dfa, start, "", 1); -+} -+ -diff --git a/security/apparmor/net.c b/security/apparmor/net.c -new file mode 100644 -index 0000000..beb8715 ---- /dev/null -+++ b/security/apparmor/net.c -@@ -0,0 +1,146 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor network mediation -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ */ -+ -+#include "include/apparmor.h" -+#include "include/audit.h" -+#include "include/context.h" -+#include "include/net.h" -+#include "include/policy.h" -+ -+#include "af_names.h" -+ -+static const char *sock_type_names[] = { -+ "unknown(0)", -+ "stream", -+ "dgram", -+ "raw", -+ "rdm", -+ "seqpacket", -+ "dccp", -+ "unknown(7)", -+ "unknown(8)", -+ "unknown(9)", -+ "packet", -+}; -+ -+struct aa_audit_net { -+ struct aa_audit base; -+ -+ int family, type, protocol; -+ -+}; -+ -+static void audit_cb(struct audit_buffer *ab, void *va) -+{ -+ struct aa_audit_net *sa = va; -+ -+ if (sa->family || sa->type) { -+ if (address_family_names[sa->family]) -+ audit_log_format(ab, " family=\"%s\"", -+ address_family_names[sa->family]); -+ else -+ audit_log_format(ab, " family=\"unknown(%d)\"", -+ sa->family); -+ -+ if (sock_type_names[sa->type]) -+ audit_log_format(ab, " sock_type=\"%s\"", -+ sock_type_names[sa->type]); -+ else -+ audit_log_format(ab, " sock_type=\"unknown(%d)\"", -+ sa->type); -+ -+ audit_log_format(ab, " protocol=%d", sa->protocol); -+ } -+ -+} -+ -+static int aa_audit_net(struct aa_profile *profile, struct aa_audit_net *sa) -+{ -+ int type = AUDIT_APPARMOR_AUTO; -+ -+ if (likely(!sa->base.error)) { -+ u16 audit_mask = profile->net.audit[sa->family]; -+ if (likely((PROFILE_AUDIT_MODE(profile) != AUDIT_ALL) && -+ !(1 << sa->type & audit_mask))) -+ return 0; -+ } else { -+ u16 quiet_mask = profile->net.quiet[sa->family]; -+ u16 kill_mask = 0; -+ u16 denied = (1 << sa->type) & ~quiet_mask; -+ -+ if (denied & kill_mask) -+ type = AUDIT_APPARMOR_KILL; -+ -+ if ((denied & quiet_mask) && -+ PROFILE_AUDIT_MODE(profile) != AUDIT_NOQUIET && -+ PROFILE_AUDIT_MODE(profile) != AUDIT_ALL) -+ return PROFILE_COMPLAIN(profile) ? 0 : sa->base.error; -+ } -+ -+ return aa_audit(type, profile, (struct aa_audit *)sa, audit_cb); -+} -+ -+int aa_net_perm(struct aa_profile *profile, char *operation, -+ int family, int type, int protocol) -+{ -+ struct aa_audit_net sa; -+ u16 family_mask; -+ -+ if ((family < 0) || (family >= AF_MAX)) -+ return -EINVAL; -+ -+ if ((type < 0) || (type >= SOCK_MAX)) -+ return -EINVAL; -+ -+ /* unix domain and netlink sockets are handled by ipc */ -+ if (family == AF_UNIX || family == AF_NETLINK) -+ return 0; -+ -+ family_mask = profile->net.allowed[family]; -+ -+ sa.base.error = (family_mask & (1 << type)) ? 0 : -EACCES; -+ -+ memset(&sa, 0, sizeof(sa)); -+ sa.base.operation = operation; -+ sa.base.gfp_mask = GFP_KERNEL; -+ sa.family = family; -+ sa.type = type; -+ sa.protocol = protocol; -+ -+ return aa_audit_net(profile, &sa); -+} -+ -+int aa_revalidate_sk(struct sock *sk, char *operation) -+{ -+ struct aa_profile *profile; -+ struct cred *cred; -+ int error = 0; -+ -+ /* this is some debugging code to flush out the network hooks that -+ that are called in interrupt context */ -+ if (in_interrupt()) { -+ printk(KERN_WARNING "AppArmor Debug: Hook being called from interrupt context\n"); -+ dump_stack(); -+ return 0; -+ } -+ -+ cred = aa_get_task_policy(current, &profile); -+ if (profile) -+ error = aa_net_perm(profile, operation, -+ sk->sk_family, sk->sk_type, -+ sk->sk_protocol); -+ put_cred(cred); -+ -+ return error; -+} -diff --git a/security/apparmor/path.c b/security/apparmor/path.c -new file mode 100644 -index 0000000..21f3e67 ---- /dev/null -+++ b/security/apparmor/path.c -@@ -0,0 +1,155 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor function for pathnames -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "include/apparmor.h" -+#include "include/path.h" -+ -+int aa_get_name_to_buffer(struct path *path, int is_dir, char *buffer, int size, -+ char **name) -+{ -+ int error = d_namespace_path(path, buffer, size - is_dir, name); -+ -+ if (!error && is_dir && (*name)[1] != '\0') -+ /* -+ * Append "/" to the pathname. The root directory is a special -+ * case; it already ends in slash. -+ */ -+ strcpy(&buffer[size - 2], "/"); -+ -+ return error; -+} -+ -+/** -+ * aa_get_name - compute the pathname of a file -+ * @path: path the file -+ * @is_dir: set if the file is a directory -+ * @buffer: buffer that aa_get_name() allocated -+ * @name: the error code indicating whether aa_get_name failed -+ * -+ * Returns an error code if the there was a failure in obtaining the -+ * name. -+ * -+ * @name is apointer to the beginning of the pathname (which usually differs -+ * from the beginning of the buffer), or NULL. If there is an error @name -+ * may contain a partial or invalid name (in the case of a deleted file), that -+ * can be used for audit purposes, but it can not be used for mediation. -+ * -+ * We need @is_dir to indicate whether the file is a directory or not because -+ * the file may not yet exist, and so we cannot check the inode's file type. -+ */ -+int aa_get_name(struct path *path, int is_dir, char **buffer, char **name) -+{ -+ char *buf, *str = NULL; -+ int size = 256; -+ int error; -+ -+ *name = NULL; -+ *buffer = NULL; -+ for (;;) { -+ buf = kmalloc(size, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ error = aa_get_name_to_buffer(path, is_dir, buf, size, &str); -+ if (!error || (error == -ENOENT) || (error == -ESTALE)) -+ break; -+ -+ kfree(buf); -+ size <<= 1; -+ if (size > g_apparmor_path_max) -+ return -ENAMETOOLONG; -+ } -+ *buffer = buf; -+ *name = str; -+ -+ return error; -+} -+ -+int d_namespace_path(struct path *path, char *buf, int buflen, char **name) -+{ -+ struct path root, tmp, ns_root = { }; -+ char *res; -+ int error = 0; -+ -+ read_lock(¤t->fs->lock); -+ root = current->fs->root; -+ path_get(¤t->fs->root); -+ read_unlock(¤t->fs->lock); -+ spin_lock(&vfsmount_lock); -+ if (root.mnt && root.mnt->mnt_ns) -+ ns_root.mnt = mntget(root.mnt->mnt_ns->root); -+ if (ns_root.mnt) -+ ns_root.dentry = dget(ns_root.mnt->mnt_root); -+ spin_unlock(&vfsmount_lock); -+ spin_lock(&dcache_lock); -+ tmp = ns_root; -+ res = __d_path(path, &tmp, buf, buflen); -+ -+ *name = res; -+ /* handle error conditions - and still allow a partial path to -+ * be returned */ -+ if (IS_ERR(res)) { -+ error = PTR_ERR(res); -+ *name = buf; -+ } else if (!IS_ROOT(path->dentry) && d_unhashed(path->dentry)) { -+ error = -ENOENT; -+#if 0 -+ } else if (tmp.dentry != ns_root.dentry && tmp.mnt != ns_root.mnt) { -+ /* disconnected path don return pathname starting with '/' */ -+ error = -ESTALE; -+ if (*res == '/') -+ *name = res + 1; -+#endif -+ } -+ -+ spin_unlock(&dcache_lock); -+ path_put(&root); -+ path_put(&ns_root); -+ -+ return error; -+} -+ -+char *sysctl_pathname(struct ctl_table *table, char *buffer, int buflen) -+{ -+ if (buflen < 1) -+ return NULL; -+ buffer += --buflen; -+ *buffer = '\0'; -+ -+ while (table) { -+ int namelen = strlen(table->procname); -+ -+ if (buflen < namelen + 1) -+ return NULL; -+ buflen -= namelen + 1; -+ buffer -= namelen; -+ memcpy(buffer, table->procname, namelen); -+ *--buffer = '/'; -+ table = table->parent; -+ } -+ if (buflen < 4) -+ return NULL; -+ buffer -= 4; -+ memcpy(buffer, "/sys", 4); -+ -+ return buffer; -+} -diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c -new file mode 100644 -index 0000000..3ba2642 ---- /dev/null -+++ b/security/apparmor/policy.c -@@ -0,0 +1,727 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor policy manipulation functions -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ * -+ * -+ * AppArmor policy is based around profiles, which contain the rules a -+ * task is confined by. Every task in the sytem has a profile attached -+ * to it determined either by matching "unconfined" tasks against the -+ * visible set of profiles or by following a profiles attachment rules. -+ * -+ * Each profile exists in an AppArmor profile namespace which is a -+ * container of related profiles. Each namespace contains a special -+ * "unconfined" profile, which doesn't efforce any confinement on -+ * a task beyond DAC. -+ * -+ * Namespace and profile names can be written together in either -+ * of two syntaxes. -+ * :namespace:profile - used by kernel interfaces for easy detection -+ * namespace://profile - used by policy -+ * -+ * Profile names name not start with : or @ and may not contain \0 -+ * a // in a profile name indicates a compound name with the name before -+ * the // being the parent profile and the name after the child -+ * -+ * Reserved profile names -+ * unconfined - special automatically generated unconfined profile -+ * inherit - special name to indicate profile inheritance -+ * null-XXXX-YYYY - special automically generated learning profiles -+ * -+ * Namespace names may not start with / or @ and may not contain \0 or // -+ * it is recommend that they do not contain any '/' characters -+ * Reserved namespace namespace -+ * default - the default namespace setup by AppArmor -+ * user-XXXX - user defined profiles -+ */ -+ -+#include -+#include -+#include -+ -+#include "include/apparmor.h" -+#include "include/capability.h" -+#include "include/file.h" -+#include "include/ipc.h" -+#include "include/match.h" -+#include "include/policy.h" -+#include "include/resource.h" -+#include "include/sid.h" -+ -+/* list of profile namespaces and lock */ -+LIST_HEAD(ns_list); -+DEFINE_RWLOCK(ns_list_lock); -+ -+struct aa_namespace *default_namespace; -+ -+const char *profile_mode_names[] = { -+ "enforce", -+ "complain", -+ "kill", -+}; -+ -+#define AA_SYS_SID 0 -+#define AA_USR_SID 1 -+ -+ -+static int common_init(struct aa_policy_common *common, const char *name) -+{ -+ common->name = kstrdup(name, GFP_KERNEL); -+ if (!common->name) -+ return 0; -+ INIT_LIST_HEAD(&common->list); -+ INIT_LIST_HEAD(&common->profiles); -+ kref_init(&common->count); -+ rwlock_init(&common->lock); -+ -+ return 1; -+} -+ -+static void common_free(struct aa_policy_common *common) -+{ -+ /* still contains profiles -- invalid */ -+ if (!list_empty(&common->profiles)) { -+ AA_ERROR("%s: internal error, " -+ "policy '%s' still contains profiles\n", -+ __func__, common->name); -+ BUG(); -+ } -+ if (!list_empty(&common->list)) { -+ AA_ERROR("%s: internal error, policy '%s' still on list\n", -+ __func__, common->name); -+ BUG(); -+ } -+ -+ kfree(common->name); -+} -+ -+static struct aa_policy_common *__common_find(struct list_head *head, -+ const char *name) -+ -+{ -+ struct aa_policy_common *common; -+ -+ list_for_each_entry(common, head, list) { -+ if (!strcmp(common->name, name)) -+ return common; -+ } -+ return NULL; -+} -+ -+static struct aa_policy_common *__common_find_strn(struct list_head *head, -+ const char *str, int len) -+{ -+ struct aa_policy_common *common; -+ -+ list_for_each_entry(common, head, list) { -+ if (aa_strneq(common->name, str, len)) -+ return common; -+ } -+ -+ return NULL; -+} -+ -+/* -+ * Routines for AppArmor namespaces -+ */ -+ -+int alloc_default_namespace(void) -+{ -+ struct aa_namespace *ns; -+ ns = alloc_aa_namespace("default"); -+ if (!ns) -+ return -ENOMEM; -+ -+ default_namespace = aa_get_namespace(ns); -+ write_lock(&ns_list_lock); -+ list_add(&ns->base.list, &ns_list); -+ write_unlock(&ns_list_lock); -+ -+ return 0; -+} -+ -+void free_default_namespace(void) -+{ -+ write_lock(&ns_list_lock); -+ list_del_init(&default_namespace->base.list); -+ aa_put_namespace(default_namespace); -+ write_unlock(&ns_list_lock); -+ aa_put_namespace(default_namespace); -+ default_namespace = NULL; -+} -+ -+/** -+ * alloc_aa_namespace - allocate, initialize and return a new namespace -+ * @name: a preallocated name -+ * Returns NULL on failure. -+ */ -+struct aa_namespace *alloc_aa_namespace(const char *name) -+{ -+ struct aa_namespace *ns; -+ -+ ns = kzalloc(sizeof(*ns), GFP_KERNEL); -+ AA_DEBUG("%s(%p)\n", __func__, ns); -+ if (!ns) -+ return NULL; -+ -+ if (!common_init(&ns->base, name)) -+ goto fail_ns; -+ -+ /* null profile is not added to the profile list */ -+ ns->unconfined = alloc_aa_profile("unconfined"); -+ if (!ns->unconfined) -+ goto fail_unconfined; -+ -+ ns->unconfined->sid = aa_alloc_sid(AA_ALLOC_SYS_SID); -+ ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | -+ PFLAG_IMMUTABLE; -+ ns->unconfined->ns = aa_get_namespace(ns); -+ -+ return ns; -+ -+fail_unconfined: -+ if (ns->base.name) -+ kfree(ns->base.name); -+fail_ns: -+ kfree(ns); -+ return NULL; -+} -+ -+/** -+ * free_aa_namespace_kref - free aa_namespace by kref (see aa_put_namespace) -+ * @kr: kref callback for freeing of a namespace -+ */ -+void free_aa_namespace_kref(struct kref *kref) -+{ -+ free_aa_namespace(container_of(kref, struct aa_namespace, base.count)); -+} -+ -+/** -+ * free_aa_namespace - free a profile namespace -+ * @namespace: the namespace to free -+ * -+ * Free a namespace. All references to the namespace must have been put. -+ * If the namespace was referenced by a profile confining a task, -+ */ -+void free_aa_namespace(struct aa_namespace *ns) -+{ -+ if (!ns) -+ return; -+ -+ common_free(&ns->base); -+ -+ if (ns->unconfined && ns->unconfined->ns == ns) -+ ns->unconfined->ns = NULL; -+ -+ aa_put_profile(ns->unconfined); -+ memset(ns, 0, sizeof(*ns)); -+ kfree(ns); -+} -+ -+struct aa_namespace *__aa_find_namespace(struct list_head *head, -+ const char *name) -+ -+{ -+ return (struct aa_namespace *) __common_find(head, name); -+} -+ -+/** -+ * aa_find_namespace - look up a profile namespace on the namespace list -+ * @name: name of namespace to find -+ * -+ * Returns a pointer to the namespace on the list, or NULL if no namespace -+ * called @name exists. -+ */ -+struct aa_namespace *aa_find_namespace(const char *name) -+{ -+ struct aa_namespace *ns = NULL; -+ -+ read_lock(&ns_list_lock); -+ ns = aa_get_namespace(__aa_find_namespace(&ns_list, name)); -+ read_unlock(&ns_list_lock); -+ -+ return ns; -+} -+ -+static struct aa_namespace *__aa_find_namespace_by_strn(struct list_head *head, -+ const char *name, -+ int len) -+{ -+ return (struct aa_namespace *) __common_find_strn(head, name, len); -+} -+ -+struct aa_namespace *aa_find_namespace_by_strn(const char *name, int len) -+{ -+ struct aa_namespace *ns = NULL; -+ -+ read_lock(&ns_list_lock); -+ ns = aa_get_namespace(__aa_find_namespace_by_strn(&ns_list, name, len)); -+ read_unlock(&ns_list_lock); -+ -+ return ns; -+} -+ -+/** -+ * aa_prepare_namespace - find an existing or create a new namespace of @name -+ * @name: the namespace to find or add -+ */ -+struct aa_namespace *aa_prepare_namespace(const char *name) -+{ -+ struct aa_namespace *ns; -+ -+ write_lock(&ns_list_lock); -+ if (name) -+ ns = aa_get_namespace(__aa_find_namespace(&ns_list, name)); -+ else -+ ns = aa_get_namespace(default_namespace); -+ if (!ns) { -+ struct aa_namespace *new_ns; -+ write_unlock(&ns_list_lock); -+ new_ns = alloc_aa_namespace(name); -+ if (!new_ns) -+ return NULL; -+ write_lock(&ns_list_lock); -+ ns = __aa_find_namespace(&ns_list, name); -+ if (!ns) { -+ list_add(&new_ns->base.list, &ns_list); -+ ns = new_ns; -+ } else { -+ /* raced so free the new one */ -+ free_aa_namespace(new_ns); -+ aa_get_namespace(ns); -+ } -+ } -+ write_unlock(&ns_list_lock); -+ -+ return ns; -+} -+ -+/* -+ * requires profile->ns set first, takes profiles refcount -+ * TODO: add accounting -+ */ -+void __aa_add_profile(struct aa_policy_common *common, -+ struct aa_profile *profile) -+{ -+ list_add(&profile->base.list, &common->profiles); -+ if (!(profile->flags & PFLAG_NO_LIST_REF)) -+ aa_get_profile(profile); -+} -+ -+void __aa_remove_profile(struct aa_profile *profile, -+ struct aa_profile *replacement) -+{ -+ if (replacement) -+ profile->replacedby = aa_get_profile(replacement); -+ else -+ profile->replacedby = ERR_PTR(-EINVAL); -+ list_del_init(&profile->base.list); -+ if (!(profile->flags & PFLAG_NO_LIST_REF)) -+ aa_put_profile(profile); -+} -+ -+/* TODO: add accounting */ -+void __aa_replace_profile(struct aa_profile *profile, -+ struct aa_profile *replacement) -+{ -+ if (replacement) { -+ struct aa_policy_common *common; -+ -+ if (profile->parent) -+ common = &profile->parent->base; -+ else -+ common = &profile->ns->base; -+ -+ __aa_remove_profile(profile, replacement); -+ __aa_add_profile(common, replacement); -+ } else -+ __aa_remove_profile(profile, NULL); -+} -+ -+/** -+ * __aa_profile_list_release - remove all profiles on the list and put refs -+ * @head: list of profiles -+ */ -+void __aa_profile_list_release(struct list_head *head) -+{ -+ struct aa_profile *profile, *tmp; -+ list_for_each_entry_safe(profile, tmp, head, base.list) { -+ __aa_profile_list_release(&profile->base.profiles); -+ __aa_remove_profile(profile, NULL); -+ } -+} -+ -+void __aa_remove_namespace(struct aa_namespace *ns) -+{ -+ struct aa_profile *unconfined = ns->unconfined; -+ list_del_init(&ns->base.list); -+ -+ /* -+ * break the ns, unconfined profile cyclic reference and forward -+ * all new unconfined profiles requests to the default namespace -+ */ -+ ns->unconfined = aa_get_profile(default_namespace->unconfined); -+ __aa_profile_list_release(&ns->base.profiles); -+ aa_put_profile(unconfined); -+ aa_put_namespace(ns); -+} -+ -+/** -+ * aa_remove_namespace = Remove namespace from the list -+ * @ns: namespace to remove -+ */ -+void aa_remove_namespace(struct aa_namespace *ns) -+{ -+ write_lock(&ns_list_lock); -+ write_lock(&ns->base.lock); -+ __aa_remove_namespace(ns); -+ write_unlock(&ns->base.lock); -+ write_unlock(&ns_list_lock); -+} -+ -+/** -+ * aa_profilelist_release - remove all namespaces and all associated profiles -+ */ -+void aa_profile_ns_list_release(void) -+{ -+ struct aa_namespace *ns, *tmp; -+ -+ /* Remove and release all the profiles on namespace profile lists. */ -+ write_lock(&ns_list_lock); -+ list_for_each_entry_safe(ns, tmp, &ns_list, base.list) { -+ write_lock(&ns->base.lock); -+ __aa_remove_namespace(ns); -+ write_unlock(&ns->base.lock); -+ } -+ write_unlock(&ns_list_lock); -+} -+ -+/** -+ * alloc_aa_profile - allocate, initialize and return a new profile -+ * @fqname: name of the profile -+ * -+ * Returns NULL on failure. -+ */ -+struct aa_profile *alloc_aa_profile(const char *fqname) -+{ -+ struct aa_profile *profile; -+ -+ profile = kzalloc(sizeof(*profile), GFP_KERNEL); -+ if (!profile) -+ return NULL; -+ -+ if (!common_init(&profile->base, fqname)) { -+ kfree(profile); -+ return NULL; -+ } -+ -+ profile->fqname = profile->base.name; -+ profile->base.name = (char *) fqname_subname((const char *) profile->fqname); -+ return profile; -+} -+ -+/** -+ * aa_new_null_profile - create a new null-X learning profile -+ * @parent: profile that caused this profile to be created -+ * @hat: true if the null- learning profile is a hat -+ * -+ * Create a null- complain mode profile used in learning mode. The name of -+ * the profile is unique and follows the format of parent//null-sid. -+ * -+ * null profiles are added to the profile list but the list does not -+ * hold a count on them so that they are automatically released when -+ * not in use. -+ */ -+struct aa_profile *aa_alloc_null_profile(struct aa_profile *parent, int hat) -+{ -+ struct aa_profile *profile = NULL; -+ char *name; -+ u32 sid = aa_alloc_sid(AA_ALLOC_SYS_SID); -+ -+ name = kmalloc(strlen(parent->fqname) + 2 + 7 + 8, GFP_KERNEL); -+ if (!name) -+ goto fail; -+ sprintf(name, "%s//null-%x", parent->fqname, sid); -+ -+ profile = alloc_aa_profile(name); -+ kfree(name); -+ if (!profile) -+ goto fail; -+ -+ profile->sid = aa_alloc_sid(AA_ALLOC_SYS_SID); -+ profile->mode = APPARMOR_COMPLAIN; -+ profile->flags = PFLAG_NULL | PFLAG_NO_LIST_REF; -+ if (hat) -+ profile->flags |= PFLAG_HAT; -+ -+ profile->parent = aa_get_profile(parent); -+ profile->ns = aa_get_namespace(parent->ns); -+ -+ write_lock(&profile->ns->base.lock); -+ __aa_add_profile(&parent->base, profile); -+ write_unlock(&profile->ns->base.lock); -+ -+ return profile; -+ -+fail: -+ aa_free_sid(sid); -+ return NULL; -+} -+ -+/** -+ * free_aa_profile_kref - free aa_profile by kref (called by aa_put_profile) -+ * @kr: kref callback for freeing of a profile -+ */ -+void free_aa_profile_kref(struct kref *kref) -+{ -+ struct aa_profile *p = container_of(kref, struct aa_profile, -+ base.count); -+ -+ free_aa_profile(p); -+} -+ -+/** -+ * free_aa_profile - free a profile -+ * @profile: the profile to free -+ * -+ * Free a profile, its hats and null_profile. All references to the profile, -+ * its hats and null_profile must have been put. -+ * -+ * If the profile was referenced from a task context, free_aa_profile() will -+ * be called from an rcu callback routine, so we must not sleep here. -+ */ -+void free_aa_profile(struct aa_profile *profile) -+{ -+ AA_DEBUG("%s(%p)\n", __func__, profile); -+ -+ if (!profile) -+ return; -+ -+ /* -+ * profile can still be on the list if the list doesn't hold a -+ * reference. There is no race as NULL profiles can't be attached -+ */ -+ if (!list_empty(&profile->base.list)) { -+ if ((profile->flags & PFLAG_NULL) && profile->ns) { -+ write_lock(&profile->ns->base.lock); -+ list_del_init(&profile->base.list); -+ write_unlock(&profile->ns->base.lock); -+ } else { -+ AA_ERROR("%s: internal error, " -+ "profile '%s' still on ns list\n", -+ __func__, profile->base.name); -+ BUG(); -+ } -+ } -+ -+ /* profile->name is a substring of fqname */ -+ profile->base.name = NULL; -+ common_free(&profile->base); -+ -+ BUG_ON(!list_empty(&profile->base.profiles)); -+ -+ kfree(profile->fqname); -+ -+ aa_put_namespace(profile->ns); -+ aa_put_profile(profile->parent); -+ -+ aa_free_file_rules(&profile->file); -+ aa_free_cap_rules(&profile->caps); -+ aa_free_net_rules(&profile->net); -+ aa_free_rlimit_rules(&profile->rlimits); -+ -+ aa_free_sid(profile->sid); -+ aa_match_free(profile->xmatch); -+ -+ if (profile->replacedby && !PTR_ERR(profile->replacedby)) -+ aa_put_profile(profile->replacedby); -+ -+ memset(profile, 0, sizeof(profile)); -+ kfree(profile); -+} -+ -+ -+/* TODO: profile count accounting - setup in remove */ -+ -+ -+struct aa_profile *__aa_find_profile(struct list_head *head, const char *name) -+{ -+ return (struct aa_profile *) __common_find(head, name); -+} -+ -+struct aa_profile *__aa_find_profile_by_strn(struct list_head *head, -+ const char *name, int len) -+{ -+ return (struct aa_profile *) __common_find_strn(head, name, len); -+} -+ -+ -+/** -+ * aa_find_child - find a profile by @name in @parent -+ * @parent: profile to search -+ * @name: profile name to search for -+ * -+ * Returns a ref counted profile or NULL if not found -+ */ -+struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) -+{ -+ struct aa_profile *profile; -+ -+ read_lock(&parent->ns->base.lock); -+ profile = aa_get_profile(__aa_find_profile(&parent->base.profiles, -+ name)); -+ read_unlock(&parent->ns->base.lock); -+ -+ return profile; -+} -+ -+ -+struct aa_policy_common *__aa_find_parent_by_fqname(struct aa_namespace *ns, -+ const char *fqname) -+{ -+ struct aa_policy_common *common; -+ struct aa_profile *profile = NULL; -+ char *split; -+ -+ common = &ns->base; -+ -+ -+ for (split = strstr(fqname, "//"); split; ) { -+ profile = __aa_find_profile_by_strn(&common->profiles, fqname, -+ split - fqname); -+ if (!profile) -+ return NULL; -+ common = &profile->base; -+ fqname = split + 2; -+ split = strstr(fqname, "//"); -+ } -+ if (!profile) -+ return &ns->base; -+ return &profile->base; -+} -+ -+struct aa_profile *__aa_find_profile_by_fqname(struct aa_namespace *ns, -+ const char *fqname) -+{ -+ struct aa_policy_common *common; -+ struct aa_profile *profile = NULL; -+ char *split; -+ -+ common = &ns->base; -+ for (split = strstr(fqname, "//"); split; ) { -+ profile = __aa_find_profile_by_strn(&common->profiles, fqname, -+ split - fqname); -+ if (!profile) -+ return NULL; -+ -+ common = &profile->base; -+ fqname = split + 2; -+ split = strstr(fqname, "//"); -+ } -+ -+ profile = __aa_find_profile(&common->profiles, fqname); -+ -+ return profile; -+} -+ -+/** -+ * aa_find_profile_by_name - find a profile by its full or partial name -+ * @ns: the namespace to start from -+ * @fqname: name to do lookup on. Does not contain namespace prefix -+ */ -+struct aa_profile *aa_find_profile_by_fqname(struct aa_namespace *ns, -+ const char *fqname) -+{ -+ struct aa_profile *profile; -+ -+ read_lock(&ns->base.lock); -+ profile = aa_get_profile(__aa_find_profile_by_fqname(ns, fqname)); -+ read_unlock(&ns->base.lock); -+ return profile; -+} -+ -+ -+/* __aa_attach_match_ - find an attachment match -+ * @name - to match against -+ * @head - profile list to walk -+ * -+ * Do a linear search on the profiles in the list. There is a matching -+ * preference where an exact match is prefered over a name which uses -+ * expressions to match, and matching expressions with the greatest -+ * xmatch_len are prefered. -+ */ -+static struct aa_profile *__aa_attach_match(const char *name, -+ struct list_head *head) -+{ -+ int len = 0; -+ struct aa_profile *profile, *candidate = NULL; -+ -+ list_for_each_entry(profile, head, base.list) { -+ if (profile->flags & PFLAG_NULL) -+ continue; -+ if (profile->xmatch && profile->xmatch_len > len) { -+ unsigned int state = aa_dfa_match(profile->xmatch, -+ DFA_START, name); -+ /* any accepting state means a valid match */ -+ if (state > DFA_START) { -+ candidate = profile; -+ len = profile->xmatch_len; -+ } -+ } else if (!strcmp(profile->base.name, name)) -+ /* exact non-re match, no more searching required */ -+ return profile; -+ } -+ -+ return candidate; -+} -+ -+/** -+ * aa_sys_find_attach - do attachment search for sys unconfined processes -+ * @ns: the namespace to search -+ * name: the executable name to match against -+ */ -+struct aa_profile *aa_sys_find_attach(struct aa_namespace *ns, const char *name) -+{ -+ struct aa_profile *profile; -+ -+ read_lock(&ns->base.lock); -+ profile = aa_get_profile(__aa_attach_match(name, &ns->base.profiles)); -+ read_unlock(&ns->base.lock); -+ -+ return profile; -+} -+ -+/** -+ * aa_profile_newest - find the newest version of @profile -+ * @profile: the profile to check for newer versions of -+ * -+ * Find the newest version of @profile, if @profile is the newest version -+ * return @profile. If @profile has been removed return NULL. -+ * -+ * NOTE: the profile returned is not refcounted, The refcount on @profile -+ * must be held until the caller decides what to do with the returned newest -+ * version. -+ */ -+struct aa_profile *aa_profile_newest(struct aa_profile *profile) -+{ -+ if (unlikely(profile && profile->replacedby)) { -+ for (;profile->replacedby; profile = profile->replacedby) { -+ if (IS_ERR(profile->replacedby)) { -+ /* profile has been removed */ -+ profile = NULL; -+ break; -+ } -+ } -+ } -+ -+ return profile; -+} -+ -diff --git a/security/apparmor/policy_interface.c b/security/apparmor/policy_interface.c -new file mode 100644 -index 0000000..24277dc ---- /dev/null -+++ b/security/apparmor/policy_interface.c -@@ -0,0 +1,850 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor functions for unpacking policy loaded from -+ * userspace. -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ * -+ * AppArmor uses a serialized binary format for loading policy. -+ * The policy format is documented in Documentation/??? -+ * All policy is validated all before it is used. -+ */ -+ -+#include -+#include -+ -+#include "include/apparmor.h" -+#include "include/audit.h" -+#include "include/context.h" -+#include "include/match.h" -+#include "include/policy.h" -+#include "include/policy_interface.h" -+#include "include/sid.h" -+ -+/* FIXME: convert profiles to internal hieracy, accounting -+ * FIXME: have replacement routines set replaced_by profile instead of error -+ * FIXME: name mapping to hierarchy -+ */ -+ -+/* -+ * The AppArmor interface treats data as a type byte followed by the -+ * actual data. The interface has the notion of a a named entry -+ * which has a name (AA_NAME typecode followed by name string) followed by -+ * the entries typecode and data. Named types allow for optional -+ * elements and extensions to be added and tested for without breaking -+ * backwards compatability. -+ */ -+ -+enum aa_code { -+ AA_U8, -+ AA_U16, -+ AA_U32, -+ AA_U64, -+ AA_NAME, /* same as string except it is items name */ -+ AA_STRING, -+ AA_BLOB, -+ AA_STRUCT, -+ AA_STRUCTEND, -+ AA_LIST, -+ AA_LISTEND, -+ AA_ARRAY, -+ AA_ARRAYEND, -+}; -+ -+/* -+ * aa_ext is the read of the buffer containing the serialized profile. The -+ * data is copied into a kernel buffer in apparmorfs and then handed off to -+ * the unpack routines. -+ */ -+struct aa_ext { -+ void *start; -+ void *end; -+ void *pos; /* pointer to current position in the buffer */ -+ u32 version; -+ char *ns_name; -+}; -+ -+ -+struct aa_audit_iface { -+ struct aa_audit base; -+ -+ const char *name; -+ const char *name2; -+ const struct aa_ext *e; -+}; -+ -+static void aa_audit_init(struct aa_audit_iface *sa, const char *operation, -+ struct aa_ext *e) -+{ -+ memset(sa, 0, sizeof(*sa)); -+ sa->base.operation = operation; -+ sa->base.gfp_mask = GFP_KERNEL; -+ sa->e = e; -+} -+ -+static void audit_cb(struct audit_buffer *ab, void *va) -+{ -+ struct aa_audit_iface *sa = va; -+ -+ if (sa->name) -+ audit_log_format(ab, " name=%s", sa->name); -+ if (sa->name2) -+ audit_log_format(ab, " namespace=%s", sa->name2); -+ if (sa->base.error && sa->e) -+ audit_log_format(ab, " offset=%d", sa->e->pos - sa->e->start); -+} -+ -+static int aa_audit_iface(struct aa_audit_iface *sa) -+{ -+ struct aa_profile *profile; -+ struct cred *cred = aa_get_task_policy(current, &profile); -+ int error = aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa->base, -+ audit_cb); -+ put_cred(cred); -+ return error; -+} -+ -+static int aa_inbounds(struct aa_ext *e, size_t size) -+{ -+ return (size <= e->end - e->pos); -+} -+ -+/** -+ * aa_u16_chunck - test and do bounds checking for a u16 size based chunk -+ * @e: serialized data read head -+ * @chunk: start address for chunk of data -+ * -+ * return the size of chunk found with the read head at the end of -+ * the chunk. -+ */ -+static size_t aa_is_u16_chunk(struct aa_ext *e, char **chunk) -+{ -+ void *pos = e->pos; -+ size_t size = 0; -+ -+ if (!aa_inbounds(e, sizeof(u16))) -+ goto fail; -+ size = le16_to_cpu(get_unaligned((u16 *)e->pos)); -+ e->pos += sizeof(u16); -+ if (!aa_inbounds(e, size)) -+ goto fail; -+ *chunk = e->pos; -+ e->pos += size; -+ return size; -+ -+fail: -+ e->pos = pos; -+ return 0; -+} -+ -+static int aa_is_X(struct aa_ext *e, enum aa_code code) -+{ -+ if (!aa_inbounds(e, 1)) -+ return 0; -+ if (*(u8 *) e->pos != code) -+ return 0; -+ e->pos++; -+ return 1; -+} -+ -+/** -+ * aa_is_nameX - check is the next element is of type X with a name of @name -+ * @e: serialized data extent information -+ * @code: type code -+ * @name: name to match to the serialized element. -+ * -+ * check that the next serialized data element is of type X and has a tag -+ * name @name. If @name is specified then there must be a matching -+ * name element in the stream. If @name is NULL any name element will be -+ * skipped and only the typecode will be tested. -+ * returns 1 on success (both type code and name tests match) and the read -+ * head is advanced past the headers -+ * returns %0 if either match failes, the read head does not move -+ */ -+static int aa_is_nameX(struct aa_ext *e, enum aa_code code, const char *name) -+{ -+ void *pos = e->pos; -+ /* -+ * Check for presence of a tagname, and if present name size -+ * AA_NAME tag value is a u16. -+ */ -+ if (aa_is_X(e, AA_NAME)) { -+ char *tag = NULL; -+ size_t size = aa_is_u16_chunk(e, &tag); -+ /* if a name is specified it must match. otherwise skip tag */ -+ if (name && (!size || strcmp(name, tag))) -+ goto fail; -+ } else if (name) { -+ /* if a name is specified and there is no name tag fail */ -+ goto fail; -+ } -+ -+ /* now check if type code matches */ -+ if (aa_is_X(e, code)) -+ return 1; -+ -+fail: -+ e->pos = pos; -+ return 0; -+} -+ -+static int aa_is_u16(struct aa_ext *e, u16 *data, const char *name) -+{ -+ void *pos = e->pos; -+ if (aa_is_nameX(e, AA_U16, name)) { -+ if (!aa_inbounds(e, sizeof(u16))) -+ goto fail; -+ if (data) -+ *data = le16_to_cpu(get_unaligned((u16 *)e->pos)); -+ e->pos += sizeof(u16); -+ return 1; -+ } -+fail: -+ e->pos = pos; -+ return 0; -+} -+ -+static int aa_is_u32(struct aa_ext *e, u32 *data, const char *name) -+{ -+ void *pos = e->pos; -+ if (aa_is_nameX(e, AA_U32, name)) { -+ if (!aa_inbounds(e, sizeof(u32))) -+ goto fail; -+ if (data) -+ *data = le32_to_cpu(get_unaligned((u32 *)e->pos)); -+ e->pos += sizeof(u32); -+ return 1; -+ } -+fail: -+ e->pos = pos; -+ return 0; -+} -+ -+static int aa_is_u64(struct aa_ext *e, u64 *data, const char *name) -+{ -+ void *pos = e->pos; -+ if (aa_is_nameX(e, AA_U64, name)) { -+ if (!aa_inbounds(e, sizeof(u64))) -+ goto fail; -+ if (data) -+ *data = le64_to_cpu(get_unaligned((u64 *)e->pos)); -+ e->pos += sizeof(u64); -+ return 1; -+ } -+fail: -+ e->pos = pos; -+ return 0; -+} -+ -+static size_t aa_is_array(struct aa_ext *e, const char *name) -+{ -+ void *pos = e->pos; -+ if (aa_is_nameX(e, AA_ARRAY, name)) { -+ int size; -+ if (!aa_inbounds(e, sizeof(u16))) -+ goto fail; -+ size = (int) le16_to_cpu(get_unaligned((u16 *)e->pos)); -+ e->pos += sizeof(u16); -+ return size; -+ } -+fail: -+ e->pos = pos; -+ return 0; -+} -+ -+static size_t aa_is_blob(struct aa_ext *e, char **blob, const char *name) -+{ -+ void *pos = e->pos; -+ if (aa_is_nameX(e, AA_BLOB, name)) { -+ u32 size; -+ if (!aa_inbounds(e, sizeof(u32))) -+ goto fail; -+ size = le32_to_cpu(get_unaligned((u32 *)e->pos)); -+ e->pos += sizeof(u32); -+ if (aa_inbounds(e, (size_t) size)) { -+ *blob = e->pos; -+ e->pos += size; -+ return size; -+ } -+ } -+fail: -+ e->pos = pos; -+ return 0; -+} -+ -+static int aa_is_string(struct aa_ext *e, char **string, const char *name) -+{ -+ char *src_str; -+ size_t size = 0; -+ void *pos = e->pos; -+ *string = NULL; -+ if (aa_is_nameX(e, AA_STRING, name) && -+ (size = aa_is_u16_chunk(e, &src_str))) { -+ /* strings are null terminated, length is size - 1 */ -+ if (src_str[size - 1] != 0) -+ goto fail; -+ *string = src_str; -+ } -+ -+ return size; -+ -+fail: -+ e->pos = pos; -+ return 0; -+} -+ -+static int aa_is_dynstring(struct aa_ext *e, char **string, const char *name) -+{ -+ char *tmp; -+ void *pos = e->pos; -+ int res = aa_is_string(e, &tmp, name); -+ *string = NULL; -+ -+ if (!res) -+ return res; -+ -+ *string = kstrdup(tmp, GFP_KERNEL); -+ if (!*string) { -+ e->pos = pos; -+ return 0; -+ } -+ -+ return res; -+} -+ -+/** -+ * aa_unpack_dfa - unpack a file rule dfa -+ * @e: serialized data extent information -+ * -+ * returns dfa or ERR_PTR -+ */ -+static struct aa_dfa *aa_unpack_dfa(struct aa_ext *e) -+{ -+ char *blob = NULL; -+ size_t size, error = 0; -+ struct aa_dfa *dfa = NULL; -+ -+ size = aa_is_blob(e, &blob, "aadfa"); -+ if (size) { -+ dfa = aa_match_alloc(); -+ if (dfa) { -+ /* -+ * The dfa is aligned with in the blob to 8 bytes -+ * from the beginning of the stream. -+ */ -+ size_t sz = blob - (char *) e->start; -+ size_t pad = ALIGN(sz, 8) - sz; -+ error = unpack_dfa(dfa, blob + pad, size - pad); -+ if (!error) -+ error = verify_dfa(dfa); -+ } else { -+ error = -ENOMEM; -+ } -+ -+ if (error) { -+ aa_match_free(dfa); -+ dfa = ERR_PTR(error); -+ } -+ } -+ -+ return dfa; -+} -+ -+static int aa_unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) -+{ -+ void *pos = e->pos; -+ -+ /* exec table is optional */ -+ if (aa_is_nameX(e, AA_STRUCT, "xtable")) { -+ int i, size; -+ -+ size = aa_is_array(e, NULL); -+ /* currently 4 exec bits and entries 0-3 are reserved iupcx */ -+ if (size > 16 - 4) -+ goto fail; -+ profile->file.trans.table = kzalloc(sizeof(char *) * size, -+ GFP_KERNEL); -+ if (!profile->file.trans.table) -+ goto fail; -+ -+ for (i = 0; i < size; i++) { -+ char *tmp; -+ if (!aa_is_dynstring(e, &tmp, NULL)) -+ goto fail; -+ /* note: strings beginning with a : have an embedded -+ \0 seperating the profile ns name from the profile -+ name */ -+ profile->file.trans.table[i] = tmp; -+ } -+ if (!aa_is_nameX(e, AA_ARRAYEND, NULL)) -+ goto fail; -+ if (!aa_is_nameX(e, AA_STRUCTEND, NULL)) -+ goto fail; -+ profile->file.trans.size = size; -+ } -+ return 1; -+ -+fail: -+ e->pos = pos; -+ return 0; -+} -+ -+int aa_unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) -+{ -+ void *pos = e->pos; -+ -+ /* rlimits are optional */ -+ if (aa_is_nameX(e, AA_STRUCT, "rlimits")) { -+ int i, size; -+ u32 tmp = 0; -+ if (!aa_is_u32(e, &tmp, NULL)) -+ goto fail; -+ profile->rlimits.mask = tmp; -+ -+ size = aa_is_array(e, NULL); -+ if (size > RLIM_NLIMITS) -+ goto fail; -+ for (i = 0; i < size; i++) { -+ u64 tmp = 0; -+ if (!aa_is_u64(e, &tmp, NULL)) -+ goto fail; -+ profile->rlimits.limits[i].rlim_max = tmp; -+ } -+ if (!aa_is_nameX(e, AA_ARRAYEND, NULL)) -+ goto fail; -+ if (!aa_is_nameX(e, AA_STRUCTEND, NULL)) -+ goto fail; -+ } -+ return 1; -+ -+fail: -+ e->pos = pos; -+ return 0; -+} -+ -+/** -+ * aa_unpack_profile - unpack a serialized profile -+ * @e: serialized data extent information -+ * @sa: audit struct for the operation -+ */ -+static struct aa_profile *aa_unpack_profile(struct aa_ext *e, -+ struct aa_audit_iface *sa) -+{ -+ struct aa_profile *profile = NULL; -+ char *name; -+ size_t size = 0; -+ int i, error = -EPROTO; -+ u32 tmp; -+ -+ /* check that we have the right struct being passed */ -+ if (!aa_is_nameX(e, AA_STRUCT, "profile")) -+ goto fail; -+ if (!aa_is_string(e, &name, NULL)) -+ goto fail; -+ -+ profile = alloc_aa_profile(name); -+ if (!profile) -+ return ERR_PTR(-ENOMEM); -+ -+ /* xmatch is optional and may be NULL */ -+ profile->xmatch = aa_unpack_dfa(e); -+ if (IS_ERR(profile->xmatch)) { -+ error = PTR_ERR(profile->xmatch); -+ profile->xmatch = NULL; -+ goto fail; -+ } -+ /* xmatch_len is not optional is xmatch is set */ -+ if (profile->xmatch && !aa_is_u32(e, &tmp, NULL)) -+ goto fail; -+ profile->xmatch_len = tmp; -+ -+ /* per profile debug flags (complain, audit) */ -+ if (!aa_is_nameX(e, AA_STRUCT, "flags")) -+ goto fail; -+ if (!aa_is_u32(e, &tmp, NULL)) -+ goto fail; -+ if (tmp) -+ profile->flags |= PFLAG_HAT; -+ if (!aa_is_u32(e, &tmp, NULL)) -+ goto fail; -+ if (tmp) -+ profile->mode = APPARMOR_COMPLAIN; -+ if (!aa_is_u32(e, &tmp, NULL)) -+ goto fail; -+ if (tmp) -+ profile->audit = AUDIT_ALL; -+ -+ if (!aa_is_nameX(e, AA_STRUCTEND, NULL)) -+ goto fail; -+ -+ if (!aa_is_u32(e, &(profile->caps.allowed.cap[0]), NULL)) -+ goto fail; -+ if (!aa_is_u32(e, &(profile->caps.audit.cap[0]), NULL)) -+ goto fail; -+ if (!aa_is_u32(e, &(profile->caps.quiet.cap[0]), NULL)) -+ goto fail; -+ if (!aa_is_u32(e, &(profile->caps.set.cap[0]), NULL)) -+ goto fail; -+ -+ if (aa_is_nameX(e, AA_STRUCT, "caps64")) { -+ /* optional upper half of 64 bit caps */ -+ if (!aa_is_u32(e, &(profile->caps.allowed.cap[1]), NULL)) -+ goto fail; -+ if (!aa_is_u32(e, &(profile->caps.audit.cap[1]), NULL)) -+ goto fail; -+ if (!aa_is_u32(e, &(profile->caps.quiet.cap[1]), NULL)) -+ goto fail; -+ if (!aa_is_u32(e, &(profile->caps.set.cap[1]), NULL)) -+ goto fail; -+ if (!aa_is_nameX(e, AA_STRUCTEND, NULL)) -+ goto fail; -+ } -+ -+ if (!aa_unpack_rlimits(e, profile)) -+ goto fail; -+ -+ size = aa_is_array(e, "net_allowed_af"); -+ if (size) { -+ if (size > AF_MAX) -+ goto fail; -+ -+ for (i = 0; i < size; i++) { -+ if (!aa_is_u16(e, &profile->net.allowed[i], NULL)) -+ goto fail; -+ if (!aa_is_u16(e, &profile->net.audit[i], NULL)) -+ goto fail; -+ if (!aa_is_u16(e, &profile->net.quiet[i], NULL)) -+ goto fail; -+ } -+ if (!aa_is_nameX(e, AA_ARRAYEND, NULL)) -+ goto fail; -+ /* allow unix domain and netlink sockets they are handled -+ * by IPC -+ */ -+ } -+ profile->net.allowed[AF_UNIX] = 0xffff; -+ profile->net.allowed[AF_NETLINK] = 0xffff; -+ -+ /* get file rules */ -+ profile->file.dfa = aa_unpack_dfa(e); -+ if (IS_ERR(profile->file.dfa)) { -+ error = PTR_ERR(profile->file.dfa); -+ profile->file.dfa = NULL; -+ goto fail; -+ } -+ -+ if (!aa_unpack_trans_table(e, profile)) -+ goto fail; -+ -+ if (!aa_is_nameX(e, AA_STRUCTEND, NULL)) -+ goto fail; -+ -+ return profile; -+ -+fail: -+ sa->name = profile && profile->base.name ? profile->base.name : -+ "unknown"; -+ if (!sa->base.info) -+ sa->base.info = "failed to unpack profile"; -+ aa_audit_iface(sa); -+ -+ free_aa_profile(profile); -+ -+ return ERR_PTR(error); -+} -+ -+/** -+ * aa_verify_head - unpack serialized stream header -+ * @e: serialized data read head -+ * @operation: operation header is being verified for -+ * -+ * returns error or 0 if header is good -+ */ -+static int aa_verify_header(struct aa_ext *e, struct aa_audit_iface *sa) -+{ -+ /* get the interface version */ -+ if (!aa_is_u32(e, &e->version, "version")) { -+ sa->base.info = "invalid profile format"; -+ aa_audit_iface(sa); -+ return -EPROTONOSUPPORT; -+ } -+ -+ /* check that the interface version is currently supported */ -+ if (e->version != 5) { -+ sa->base.info = "unsupported interface version"; -+ aa_audit_iface(sa); -+ return -EPROTONOSUPPORT; -+ } -+ -+ /* read the namespace if present */ -+ if (!aa_is_string(e, &e->ns_name, "namespace")) -+ e->ns_name = NULL; -+ -+ return 0; -+} -+ -+ -+ -+/** -+ * aa_interface_add_profiles - Unpack and add new profile(s) to the profile list -+ * @data: serialized data stream -+ * @size: size of the serialized data stream -+ */ -+ssize_t aa_interface_add_profiles(void *data, size_t size) -+{ -+ struct aa_profile *profile; -+ struct aa_namespace *ns = NULL; -+ struct aa_policy_common *common; -+ struct aa_ext e = { -+ .start = data, -+ .end = data + size, -+ .pos = data, -+ .ns_name = NULL -+ }; -+ ssize_t error; -+ struct aa_audit_iface sa; -+ aa_audit_init(&sa, "profile_load", &e); -+ -+ error = aa_verify_header(&e, &sa); -+ if (error) -+ return error; -+ -+ profile = aa_unpack_profile(&e, &sa); -+ if (IS_ERR(profile)) -+ return PTR_ERR(profile); -+ -+ sa.name2 = e.ns_name; -+ ns = aa_prepare_namespace(e.ns_name); -+ if (IS_ERR(ns)) { -+ sa.base.info = "failed to prepare namespace"; -+ sa.base.error = PTR_ERR(ns); -+ goto fail; -+ } -+ /* profiles are currently loaded flat with fqnames */ -+ sa.name = profile->fqname; -+ -+ write_lock(&ns->base.lock); -+ -+ common = __aa_find_parent_by_fqname(ns, sa.name); -+ if (!common) { -+ sa.base.info = "parent does not exist"; -+ sa.base.error = -ENOENT; -+ goto fail2; -+ } -+ -+ if (common != &ns->base) -+ profile->parent = aa_get_profile((struct aa_profile *) common); -+ -+ if (__aa_find_profile(&common->profiles, profile->base.name)) { -+ /* A profile with this name exists already. */ -+ sa.base.info = "profile already exists"; -+ goto fail2; -+ } -+ profile->sid = aa_alloc_sid(AA_ALLOC_SYS_SID); -+ profile->ns = aa_get_namespace(ns); -+ -+ __aa_add_profile(common, profile); -+ write_unlock(&ns->base.lock); -+ -+ aa_audit_iface(&sa); -+ aa_put_namespace(ns); -+ kfree(e.ns_name); -+ return size; -+ -+fail2: -+ write_unlock(&ns->base.lock); -+ sa.base.error = -EEXIST; -+ -+fail: -+ error = aa_audit_iface(&sa); -+ aa_put_namespace(ns); -+ aa_put_profile(profile); -+ kfree(e.ns_name); -+ return error; -+} -+ -+/** -+ * aa_interface_replace_profiles - replace profile(s) on the profile list -+ * @udata: serialized data stream -+ * @size: size of the serialized data stream -+ * -+ * unpack and replace a profile on the profile list and uses of that profile -+ * by any aa_task_context. If the profile does not exist on the profile list -+ * it is added. Return %0 or error. -+ */ -+ssize_t aa_interface_replace_profiles(void *udata, size_t size) -+{ -+ struct aa_policy_common *common; -+ struct aa_profile *old_profile = NULL, *new_profile; -+ struct aa_namespace *ns; -+ struct aa_ext e = { -+ .start = udata, -+ .end = udata + size, -+ .pos = udata, -+ .ns_name = NULL -+ }; -+ ssize_t error; -+ struct aa_audit_iface sa; -+ aa_audit_init(&sa, "profile_replace", &e); -+ -+ if (g_apparmor_lock_policy) -+ return -EACCES; -+ -+ error = aa_verify_header(&e, &sa); -+ if (error) -+ return error; -+ -+ new_profile = aa_unpack_profile(&e, &sa); -+ if (IS_ERR(new_profile)) -+ return PTR_ERR(new_profile); -+ -+ sa.name2 = e.ns_name; -+ ns = aa_prepare_namespace(e.ns_name); -+ if (!ns) { -+ sa.base.info = "failed to prepare namespace"; -+ sa.base.error = -ENOMEM; -+ goto fail; -+ } -+ -+ sa.name = new_profile->fqname; -+ -+ write_lock(&ns->base.lock); -+ common = __aa_find_parent_by_fqname(ns, sa.name); -+ -+ if (!common) { -+ sa.base.info = "parent does not exist"; -+ sa.base.error = -ENOENT; -+ goto fail2; -+ } -+ -+ if (common != &ns->base) -+ new_profile->parent = aa_get_profile((struct aa_profile *) -+ common); -+ -+ old_profile = __aa_find_profile(&common->profiles, -+ new_profile->base.name); -+ aa_get_profile(old_profile); -+ if (old_profile && old_profile->flags & PFLAG_IMMUTABLE) { -+ sa.base.info = "cannot replace immutible profile"; -+ sa.base.error = -EPERM; -+ goto fail2; -+ } else if (old_profile) { -+ // __aa_profile_list_release(&old_profile->base.profiles); -+ /* TODO: remove for new interface -+ * move children profiles over to the new profile so -+ * that replacement behaves correctly -+ */ -+ // list_replace_init(&old_profile->base.profiles, -+ // &new_profile->base.profiles); -+ struct aa_profile *profile, *tmp; -+ list_for_each_entry_safe(profile, tmp, &old_profile->base.profiles, -+ base.list) { -+ aa_put_profile(profile->parent); -+ list_del(&profile->base.list); -+ profile->parent = aa_get_profile(new_profile); -+ list_add(&profile->base.list, -+ &new_profile->base.profiles); -+ } -+ __aa_replace_profile(old_profile, new_profile); -+ new_profile->sid = old_profile->sid; -+ } else { -+ __aa_add_profile(common, new_profile); -+ new_profile->sid = aa_alloc_sid(AA_ALLOC_SYS_SID); -+ } -+ -+ new_profile->ns = aa_get_namespace(ns); -+ -+ write_unlock(&ns->base.lock); -+ -+ if (!old_profile) -+ sa.base.operation = "profile_load"; -+ -+ aa_audit_iface(&sa); -+ aa_put_namespace(ns); -+ aa_put_profile(old_profile); -+ kfree(e.ns_name); -+ return size; -+ -+fail2: -+ write_unlock(&ns->base.lock); -+fail: -+ error = aa_audit_iface(&sa); -+ aa_put_namespace(ns); -+ aa_put_profile(old_profile); -+ aa_put_profile(new_profile); -+ kfree(e.ns_name); -+ return error; -+} -+ -+/** -+ * aa_interface_remove_profiles - remove profile(s) from the system -+ * @name: name of the profile to remove -+ * @size: size of the name -+ * -+ * remove a profile from the profile list and all aa_task_context references -+ * to said profile. -+ * NOTE: removing confinement does not restore rlimits to preconfinemnet values -+ */ -+ssize_t aa_interface_remove_profiles(char *name, size_t size) -+{ -+ struct aa_namespace *ns; -+ struct aa_profile *profile; -+ struct aa_audit_iface sa; -+ aa_audit_init(&sa, "profile_remove", NULL); -+ -+ if (g_apparmor_lock_policy) -+ return -EACCES; -+ -+ write_lock(&ns_list_lock); -+ if (name[0] == ':') { -+ char *ns_name; -+ name = aa_split_name_from_ns(name, &ns_name); -+ ns = __aa_find_namespace(&ns_list, ns_name); -+ } else { -+ ns = aa_get_namespace(default_namespace); -+ } -+ -+ if (!ns) { -+ sa.base.info = "failed: namespace does not exist"; -+ goto fail_ns_list_lock; -+ } -+ -+ sa.name2 = ns->base.name; -+ write_lock(&ns->base.lock); -+ if (!name) { -+ /* remove namespace */ -+ // __aa_remove_namespace(ns); -+ } else { -+ /* remove profile */ -+ profile = __aa_find_profile_by_fqname(ns, name); -+ if (!profile) { -+ sa.name = name; -+ sa.base.info = "failed: profile does not exist"; -+ goto fail_ns_lock; -+ } -+ sa.name = profile->fqname; -+ __aa_profile_list_release(&profile->base.profiles); -+ __aa_remove_profile(profile, profile->ns->unconfined); -+ } -+ write_unlock(&ns->base.lock); -+ write_unlock(&ns_list_lock); -+ -+ aa_audit_iface(&sa); -+ aa_put_namespace(ns); -+ return size; -+ -+fail_ns_lock: -+ write_unlock(&ns->base.lock); -+ -+fail_ns_list_lock: -+ write_unlock(&ns_list_lock); -+ aa_audit_iface(&sa); -+ return 0; //-ENOENT; -+} -diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c -new file mode 100644 -index 0000000..834cfab ---- /dev/null -+++ b/security/apparmor/procattr.c -@@ -0,0 +1,117 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor /proc//attr/ interface functions -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ */ -+ -+#include "include/apparmor.h" -+#include "include/policy.h" -+#include "include/domain.h" -+ -+/* FIXME show profile multiplexing */ -+int aa_getprocattr(struct aa_namespace *ns, struct aa_profile *profile, -+ char **string) -+{ -+ char *str; -+ int len = 0; -+ -+ if (profile) { -+ int mode_len, name_len, ns_len = 0; -+ const char *mode_str = profile_mode_names[profile->mode]; -+ char *s; -+ -+ mode_len = strlen(mode_str) + 3; /* _(mode_str)\n */ -+ name_len = strlen(profile->fqname); -+ if (ns != default_namespace) -+ ns_len = strlen(ns->base.name) + 3; -+ len = mode_len + ns_len + name_len + 1; -+ s = str = kmalloc(len + 1, GFP_ATOMIC); -+ if (!str) -+ return -ENOMEM; -+ -+ if (ns_len) { -+ sprintf(s, "%s://", ns->base.name); -+ s += ns_len; -+ } -+ memcpy(s, profile->fqname, name_len); -+ s += name_len; -+ sprintf(s, " (%s)\n", mode_str); -+ } else { -+ const char *unconfined_str = "unconfined\n"; -+ -+ len = strlen(unconfined_str); -+ if (ns != default_namespace) -+ len += strlen(ns->base.name) + 1; -+ -+ str = kmalloc(len + 1, GFP_ATOMIC); -+ if (!str) -+ return -ENOMEM; -+ -+ if (ns != default_namespace) -+ sprintf(str, "%s://%s", ns->base.name, unconfined_str); -+ else -+ memcpy(str, unconfined_str, len); -+ } -+ *string = str; -+ -+ return len; -+} -+ -+static char *split_token_from_name(const char *op, char *args, u64 *token) -+{ -+ char *name; -+ -+ *token = simple_strtoull(args, &name, 16); -+ if ((name == args) || *name != '^') { -+ AA_ERROR("%s: Invalid input '%s'", op, args); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ name++; /* skip ^ */ -+ if (!*name) -+ name = NULL; -+ return name; -+} -+ -+int aa_setprocattr_changehat(char *args, int test) -+{ -+ char *hat; -+ u64 token; -+ -+ hat = split_token_from_name("change_hat", args, &token); -+ if (IS_ERR(hat)) -+ return PTR_ERR(hat); -+ -+ if (!hat && !token) { -+ AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic"); -+ return -EINVAL; -+ } -+ -+ AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", -+ __func__, token, hat ? hat : NULL); -+ -+ return aa_change_hat(hat, token, test); -+} -+ -+int aa_setprocattr_changeprofile(char *args, int onexec, int test) -+{ -+ char *name, *ns_name; -+ -+ name = aa_split_name_from_ns(args, &ns_name); -+ return aa_change_profile(ns_name, name, onexec, test); -+} -+ -+ -+int aa_setprocattr_permipc(char *args) -+{ -+ /* TODO: add ipc permission querying */ -+ return -ENOTSUPP; -+} -diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c -new file mode 100644 -index 0000000..f00165b ---- /dev/null -+++ b/security/apparmor/resource.c -@@ -0,0 +1,104 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor resource mediation and attachment -+ * -+ * Copyright (C) 1998-2008 Novell/SUSE -+ * Copyright 2009 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. -+ */ -+ -+#include -+ -+#include "include/audit.h" -+#include "include/resource.h" -+#include "include/policy.h" -+ -+struct aa_audit_resource { -+ struct aa_audit base; -+ -+ int rlimit; -+}; -+ -+static void audit_cb(struct audit_buffer *ab, void *va) -+{ -+ struct aa_audit_resource *sa = va; -+ -+ if (sa->rlimit) -+ audit_log_format(ab, " rlimit=%d", sa->rlimit - 1); -+} -+ -+static int aa_audit_resource(struct aa_profile *profile, -+ struct aa_audit_resource *sa) -+{ -+ return aa_audit(AUDIT_APPARMOR_AUTO, profile, (struct aa_audit *)sa, -+ audit_cb); -+} -+ -+/** -+ * aa_task_setrlimit - test permission to set an rlimit -+ * @profile - profile confining the task -+ * @resource - the resource being set -+ * @new_rlim - the new resource limit -+ * -+ * Control raising the processes hard limit. -+ */ -+int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, -+ struct rlimit *new_rlim) -+{ -+ struct aa_audit_resource sa; -+ int error = 0; -+ -+ memset(&sa, 0, sizeof(sa)); -+ sa.base.operation = "setrlimit"; -+ sa.base.gfp_mask = GFP_KERNEL; -+ sa.rlimit = resource + 1; -+ -+ if (profile->rlimits.mask & (1 << resource) && -+ new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) { -+ sa.base.error = -EACCES; -+ -+ error = aa_audit_resource(profile, &sa); -+ } -+ -+ return error; -+} -+ -+void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new) -+{ -+ unsigned int mask = 0; -+ struct rlimit *rlim, *initrlim; -+ int i; -+ -+ /* for any rlimits the profile controlled reset the soft limit -+ * to the less of the tasks hard limit and the init tasks soft limit -+ */ -+ if (old && old->rlimits.mask) { -+ for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<=1) { -+ if (old->rlimits.mask & mask) { -+ rlim = current->signal->rlim + i; -+ initrlim = init_task.signal->rlim + i; -+ rlim->rlim_cur = min(rlim->rlim_max, -+ initrlim->rlim_cur); -+ } -+ } -+ } -+ -+ /* set any new hard limits as dictated by the new profile */ -+ if (!(new && new->rlimits.mask)) -+ return; -+ for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<=1) { -+ if (!(new->rlimits.mask & mask)) -+ continue; -+ -+ rlim = current->signal->rlim + i; -+ rlim->rlim_max = min(rlim->rlim_max, -+ new->rlimits.limits[i].rlim_max); -+ /* soft limit should not exceed hard limit */ -+ rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max); -+ } -+} -diff --git a/security/apparmor/sid.c b/security/apparmor/sid.c -new file mode 100644 -index 0000000..aa41a35 ---- /dev/null -+++ b/security/apparmor/sid.c -@@ -0,0 +1,113 @@ -+/* -+ * AppArmor security module -+ * -+ * This file contains AppArmor security identifier (sid) manipulation fns -+ * -+ * Copyright 2009 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. -+ * -+ * -+ * AppArmor allocates a unique sid for every profile loaded. If a profile -+ * is replaced it receive the sid of the profile it is replacing. Each sid -+ * is a u32 with the lower u16 being sids of system profiles and the -+ * upper u16 being user profile sids. -+ * -+ * The sid value of 0 is invalid for system sids and is used to indicate -+ * unconfined for user sids. -+ * -+ * A compound sid is a pair of user and system sids that is used to identify -+ * both profiles confining a task. -+ * -+ * Both system and user sids are globally unique with all users pulling -+ * from the same sid pool. User sid allocation is limited by the -+ * user controls, that can limit how many profiles are loaded by a user. -+ */ -+ -+#include -+#include -+#include -+ -+#include "include/sid.h" -+ -+/* global counter from which sids are allocated */ -+static u16 global_sys_sid; -+static u16 global_usr_sid; -+static DEFINE_SPINLOCK(sid_lock); -+ -+ -+/* TODO FIXME: add sid to profile mapping, and sid recycling */ -+ -+ -+/** -+ * aa_alloc_sid - allocate a new sid for a profile -+ * @is_usr: true if the new sid is a user based sid -+ */ -+u32 aa_alloc_sid(int is_usr) -+{ -+ u32 sid; -+ -+ /* -+ * TODO FIXME: sid recycling - part of profile mapping table -+ */ -+ spin_lock(&sid_lock); -+ if (is_usr) { -+ sid = (++global_usr_sid) << 16; -+ -+ } else { -+ sid = ++global_sys_sid; -+ } -+ spin_unlock(&sid_lock); -+ return sid; -+} -+ -+/** -+ * aa_free_sid - free a sid -+ * @sid: sid to free -+ */ -+void aa_free_sid(u32 sid) -+{ -+ ; /* NOP ATM */ -+} -+ -+/** -+ * aa_add_sid_profile - associate a profile to a sid for sid -> profile lookup -+ * @sid: sid of te profile -+ * @profile: profile to associate -+ * -+ * return 0 or error -+ */ -+int aa_add_sid_profile(u32 sid, struct aa_profile *profile) -+{ -+ /* NOP ATM */ -+ return 0; -+} -+ -+/** -+ * aa_replace_sid_profile - replace the profile associated with a sid -+ * @sid: sid to associate a new profile with -+ * @profile: profile to associate with side -+ * -+ * return 0 or error -+ */ -+int aa_replace_sid_profile(u32 sid, struct aa_profile *profile) -+{ -+ /* NOP ATM */ -+ return 0; -+} -+ -+/** -+ * aa_get_sid_profile - get the profile associated with the sid -+ * @sid: sid to lookup -+ * -+ * returns - the profile, or NULL for unconfined user. -+ * - if there is an error -ENOENT, -EINVAL -+ */ -+struct aa_profile *aa_get_sid_profile(u32 sid) -+{ -+ return ERR_PTR(-EINVAL); -+} -+ +-- +1.8.3.2 +