From 341b7e61dab923b09a4dc31e85562fcd437953e6 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 27 Aug 2011 12:56:53 -0700 Subject: [PATCH] Add compatibility patches for linux kernel 3.1 --- ...bility-patch-for-v5-network-controll.patch | 553 ++++++++++++++++++ ...compatibility-patch-for-v5-interface.patch | 391 +++++++++++++ ...fa-backward-compatibility-with-broke.patch | 69 +++ 3 files changed, 1013 insertions(+) create mode 100644 kernel-patches/3.1/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch create mode 100644 kernel-patches/3.1/0002-AppArmor-compatibility-patch-for-v5-interface.patch create mode 100644 kernel-patches/3.1/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch diff --git a/kernel-patches/3.1/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch b/kernel-patches/3.1/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch new file mode 100644 index 000000000..00c87127f --- /dev/null +++ b/kernel-patches/3.1/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch @@ -0,0 +1,553 @@ +From dc13dec93dbd04bfa7a9ba67df1b8ed3431d8d48 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 10 Aug 2011 22:02:39 -0700 +Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll + +Add compatibility for v5 network rules. + +Signed-off-by: John Johansen +--- + include/linux/lsm_audit.h | 4 + + security/apparmor/Makefile | 19 ++++- + security/apparmor/include/net.h | 40 +++++++++ + security/apparmor/include/policy.h | 3 + + security/apparmor/lsm.c | 112 +++++++++++++++++++++++ + security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++ + security/apparmor/policy.c | 1 + + security/apparmor/policy_unpack.c | 48 ++++++++++- + 8 files changed, 394 insertions(+), 3 deletions(-) + create mode 100644 security/apparmor/include/net.h + create mode 100644 security/apparmor/net.c + +diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h +index 88e78de..c63979a 100644 +--- a/include/linux/lsm_audit.h ++++ b/include/linux/lsm_audit.h +@@ -124,6 +124,10 @@ struct common_audit_data { + u32 denied; + uid_t ouid; + } fs; ++ struct { ++ int type, protocol; ++ struct sock *sk; ++ } net; + }; + } apparmor_audit_data; + #endif +diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile +index 2dafe50..7cefef9 100644 +--- a/security/apparmor/Makefile ++++ b/security/apparmor/Makefile +@@ -4,9 +4,9 @@ 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 ++ resource.o sid.o file.o net.o + +-clean-files := capability_names.h rlim_names.h ++clean-files := capability_names.h rlim_names.h af_names.h + + + # Build a lower case string table of capability names +@@ -44,9 +44,24 @@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\ + sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\ + echo "};" >> $@ + ++# Build a lower case string table of address family names. ++# Transform lines from ++# #define AF_INET 2 /* Internet IP Protocol */ ++# to ++# [2] = "inet", ++quiet_cmd_make-af = GEN $@ ++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 "};" >> $@ ++ ++ + $(obj)/capability.o : $(obj)/capability_names.h + $(obj)/resource.o : $(obj)/rlim_names.h ++$(obj)/net.o : $(obj)/af_names.h + $(obj)/capability_names.h : $(srctree)/include/linux/capability.h + $(call cmd,make-caps) + $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h + $(call cmd,make-rlim) ++$(obj)/af_names.h : $(srctree)/include/linux/socket.h ++ $(call cmd,make-af) +\ No newline at end of file +diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h +new file mode 100644 +index 0000000..3c7d599 +--- /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-2010 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 allow[AF_MAX]; ++ u16 audit[AF_MAX]; ++ u16 quiet[AF_MAX]; ++}; ++ ++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 inline void aa_free_net_rules(struct aa_net *new) ++{ ++ /* NOP */ ++} ++ ++#endif /* __AA_NET_H */ +diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h +index aeda5cf..6776929 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 *profile_mode_names[]; +@@ -145,6 +146,7 @@ struct aa_namespace { + * @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 +@@ -181,6 +183,7 @@ struct aa_profile { + + struct aa_file_rules file; + struct aa_caps caps; ++ struct aa_net net; + struct aa_rlimit rlimits; + }; + +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index 3d2fd14..aa293ae 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" +@@ -621,6 +622,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; ++ int error = 0; ++ ++ if (kern) ++ return 0; ++ ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) ++ error = aa_net_perm(OP_CREATE, profile, family, type, protocol, ++ NULL); ++ return error; ++} ++ ++static int apparmor_socket_bind(struct socket *sock, ++ struct sockaddr *address, int addrlen) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_BIND, sk); ++} ++ ++static int apparmor_socket_connect(struct socket *sock, ++ struct sockaddr *address, int addrlen) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_CONNECT, sk); ++} ++ ++static int apparmor_socket_listen(struct socket *sock, int backlog) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_LISTEN, sk); ++} ++ ++static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_ACCEPT, sk); ++} ++ ++static int apparmor_socket_sendmsg(struct socket *sock, ++ struct msghdr *msg, int size) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_SENDMSG, sk); ++} ++ ++static int apparmor_socket_recvmsg(struct socket *sock, ++ struct msghdr *msg, int size, int flags) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_RECVMSG, sk); ++} ++ ++static int apparmor_socket_getsockname(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_GETSOCKNAME, sk); ++} ++ ++static int apparmor_socket_getpeername(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_GETPEERNAME, sk); ++} ++ ++static int apparmor_socket_getsockopt(struct socket *sock, int level, ++ int optname) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_GETSOCKOPT, sk); ++} ++ ++static int apparmor_socket_setsockopt(struct socket *sock, int level, ++ int optname) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_SETSOCKOPT, sk); ++} ++ ++static int apparmor_socket_shutdown(struct socket *sock, int how) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); ++} ++ + static struct security_operations apparmor_ops = { + .name = "apparmor", + +@@ -652,6 +751,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, ++ + .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..1765901 +--- /dev/null ++++ b/security/apparmor/net.c +@@ -0,0 +1,170 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor network mediation ++ * ++ * Copyright (C) 1998-2008 Novell/SUSE ++ * Copyright 2009-2010 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", ++}; ++ ++/* audit callback for net specific fields */ ++static void audit_cb(struct audit_buffer *ab, void *va) ++{ ++ struct common_audit_data *sa = va; ++ ++ 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); ++ } ++ ++ 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_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; ++ if (sk) { ++ COMMON_AUDIT_DATA_INIT(&sa, NET); ++ } else { ++ COMMON_AUDIT_DATA_INIT(&sa, NONE); ++ } ++ /* todo fill in socket addr info */ ++ ++ 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 (denied & kill_mask) ++ audit_type = AUDIT_APPARMOR_KILL; ++ ++ if ((denied & quiet_mask) && ++ AUDIT_MODE(profile) != AUDIT_NOQUIET && ++ AUDIT_MODE(profile) != AUDIT_ALL) ++ return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; ++ } ++ ++ return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); ++} ++ ++/** ++ * 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_net_perm(int op, struct aa_profile *profile, u16 family, int type, ++ int protocol, struct sock *sk) ++{ ++ u16 family_mask; ++ int error; ++ ++ 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.allow[family]; ++ ++ error = (family_mask & (1 << type)) ? 0 : -EACCES; ++ ++ return audit_net(profile, op, family, type, protocol, sk, error); ++} ++ ++/** ++ * 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; ++ ++ /* 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; ++ ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) ++ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, ++ sk->sk_protocol, sk); ++ ++ return error; ++} +diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c +index 4f0eade..4d5ce13 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -745,6 +745,7 @@ static void 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); + + aa_free_sid(profile->sid); +diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c +index d6d9a57..f4874c4 100644 +--- a/security/apparmor/policy_unpack.c ++++ b/security/apparmor/policy_unpack.c +@@ -190,6 +190,19 @@ fail: + return 0; + } + ++static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) ++{ ++ if (unpack_nameX(e, AA_U16, name)) { ++ if (!inbounds(e, sizeof(u16))) ++ return 0; ++ if (data) ++ *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); ++ e->pos += sizeof(u16); ++ return 1; ++ } ++ return 0; ++} ++ + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) + { + if (unpack_nameX(e, AA_U32, name)) { +@@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) + { + struct aa_profile *profile = NULL; + const char *name = NULL; +- int error = -EPROTO; ++ size_t size = 0; ++ int i, error = -EPROTO; + kernel_cap_t tmpcap; + u32 tmp; + +@@ -559,6 +573,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) { ++ ++ 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; ++ + /* get file rules */ + profile->file.dfa = unpack_dfa(e); + if (IS_ERR(profile->file.dfa)) { +-- +1.7.5.4 + diff --git a/kernel-patches/3.1/0002-AppArmor-compatibility-patch-for-v5-interface.patch b/kernel-patches/3.1/0002-AppArmor-compatibility-patch-for-v5-interface.patch new file mode 100644 index 000000000..10d4640c9 --- /dev/null +++ b/kernel-patches/3.1/0002-AppArmor-compatibility-patch-for-v5-interface.patch @@ -0,0 +1,391 @@ +From a2515f25ad5a7833ddc5a032d34eee6a5ddee3a2 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 10 Aug 2011 22:02:40 -0700 +Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface + +Signed-off-by: John Johansen +--- + security/apparmor/Kconfig | 9 + + security/apparmor/Makefile | 1 + + security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++ + security/apparmor/apparmorfs.c | 18 ++- + security/apparmor/include/apparmorfs.h | 6 + + 5 files changed, 319 insertions(+), 2 deletions(-) + create mode 100644 security/apparmor/apparmorfs-24.c + +diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig +index 9b9013b..51ebf96 100644 +--- a/security/apparmor/Kconfig ++++ b/security/apparmor/Kconfig +@@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE + boot. + + If you are unsure how to answer this question, answer 1. ++ ++config SECURITY_APPARMOR_COMPAT_24 ++ bool "Enable AppArmor 2.4 compatability" ++ depends on SECURITY_APPARMOR ++ default y ++ help ++ This option enables compatability with AppArmor 2.4. It is ++ recommended if compatability with older versions of AppArmor ++ is desired. +diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile +index 7cefef9..0bb604b 100644 +--- a/security/apparmor/Makefile ++++ b/security/apparmor/Makefile +@@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o + apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ + path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ + resource.o sid.o file.o net.o ++apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o + + clean-files := capability_names.h rlim_names.h af_names.h + +diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c +new file mode 100644 +index 0000000..dc8c744 +--- /dev/null ++++ b/security/apparmor/apparmorfs-24.c +@@ -0,0 +1,287 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions ++ * ++ * Copyright (C) 1998-2008 Novell/SUSE ++ * Copyright 2009-2010 Canonical Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * ++ * This file contain functions providing an interface for <= AppArmor 2.4 ++ * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24 ++ * being set (see Makefile). ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "include/apparmor.h" ++#include "include/audit.h" ++#include "include/context.h" ++#include "include/policy.h" ++ ++ ++/* apparmor/matching */ ++static ssize_t aa_matching_read(struct file *file, char __user *buf, ++ size_t size, loff_t *ppos) ++{ ++ const char matching[] = "pattern=aadfa audit perms=crwxamlk/ " ++ "user::other"; ++ ++ return simple_read_from_buffer(buf, size, ppos, matching, ++ sizeof(matching) - 1); ++} ++ ++const struct file_operations aa_fs_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) ++{ ++ 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"; ++ ++ return simple_read_from_buffer(buf, size, ppos, features, ++ sizeof(features) - 1); ++} ++ ++const struct file_operations aa_fs_features_fops = { ++ .read = aa_features_read, ++}; ++ ++/** ++ * __next_namespace - find the next namespace to list ++ * @root: root namespace to stop search at (NOT NULL) ++ * @ns: current ns position (NOT NULL) ++ * ++ * Find the next namespace from @ns under @root and handle all locking needed ++ * while switching current namespace. ++ * ++ * Returns: next namespace or NULL if at last namespace under @root ++ * NOTE: will not unlock root->lock ++ */ ++static struct aa_namespace *__next_namespace(struct aa_namespace *root, ++ struct aa_namespace *ns) ++{ ++ struct aa_namespace *parent; ++ ++ /* is next namespace a child */ ++ if (!list_empty(&ns->sub_ns)) { ++ struct aa_namespace *next; ++ next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); ++ read_lock(&next->lock); ++ return next; ++ } ++ ++ /* check if the next ns is a sibling, parent, gp, .. */ ++ parent = ns->parent; ++ while (parent) { ++ read_unlock(&ns->lock); ++ list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { ++ read_lock(&ns->lock); ++ return ns; ++ } ++ if (parent == root) ++ return NULL; ++ ns = parent; ++ parent = parent->parent; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * __first_profile - find the first profile in a namespace ++ * @root: namespace that is root of profiles being displayed (NOT NULL) ++ * @ns: namespace to start in (NOT NULL) ++ * ++ * Returns: unrefcounted profile or NULL if no profile ++ */ ++static struct aa_profile *__first_profile(struct aa_namespace *root, ++ struct aa_namespace *ns) ++{ ++ for ( ; ns; ns = __next_namespace(root, ns)) { ++ if (!list_empty(&ns->base.profiles)) ++ return list_first_entry(&ns->base.profiles, ++ struct aa_profile, base.list); ++ } ++ return NULL; ++} ++ ++/** ++ * __next_profile - step to the next profile in a profile tree ++ * @profile: current profile in tree (NOT NULL) ++ * ++ * Perform a depth first taversal on the profile tree in a namespace ++ * ++ * Returns: next profile or NULL if done ++ * Requires: profile->ns.lock to be held ++ */ ++static struct aa_profile *__next_profile(struct aa_profile *p) ++{ ++ struct aa_profile *parent; ++ struct aa_namespace *ns = p->ns; ++ ++ /* is next profile a child */ ++ if (!list_empty(&p->base.profiles)) ++ return list_first_entry(&p->base.profiles, typeof(*p), ++ base.list); ++ ++ /* is next profile a sibling, parent sibling, gp, subling, .. */ ++ parent = p->parent; ++ while (parent) { ++ list_for_each_entry_continue(p, &parent->base.profiles, ++ base.list) ++ return p; ++ p = parent; ++ parent = parent->parent; ++ } ++ ++ /* is next another profile in the namespace */ ++ list_for_each_entry_continue(p, &ns->base.profiles, base.list) ++ return p; ++ ++ return NULL; ++} ++ ++/** ++ * next_profile - step to the next profile in where ever it may be ++ * @root: root namespace (NOT NULL) ++ * @profile: current profile (NOT NULL) ++ * ++ * Returns: next profile or NULL if there isn't one ++ */ ++static struct aa_profile *next_profile(struct aa_namespace *root, ++ struct aa_profile *profile) ++{ ++ struct aa_profile *next = __next_profile(profile); ++ if (next) ++ return next; ++ ++ /* finished all profiles in namespace move to next namespace */ ++ return __first_profile(root, __next_namespace(root, profile->ns)); ++} ++ ++/** ++ * p_start - start a depth first traversal of profile tree ++ * @f: seq_file to fill ++ * @pos: current position ++ * ++ * Returns: first profile under current namespace or NULL if none found ++ * ++ * acquires first ns->lock ++ */ ++static void *p_start(struct seq_file *f, loff_t *pos) ++ __acquires(root->lock) ++{ ++ struct aa_profile *profile = NULL; ++ struct aa_namespace *root = aa_current_profile()->ns; ++ loff_t l = *pos; ++ f->private = aa_get_namespace(root); ++ ++ ++ /* find the first profile */ ++ read_lock(&root->lock); ++ profile = __first_profile(root, root); ++ ++ /* skip to position */ ++ for (; profile && l > 0; l--) ++ profile = next_profile(root, profile); ++ ++ return profile; ++} ++ ++/** ++ * p_next - read the next profile entry ++ * @f: seq_file to fill ++ * @p: profile previously returned ++ * @pos: current position ++ * ++ * Returns: next profile after @p or NULL if none ++ * ++ * may acquire/release locks in namespace tree as necessary ++ */ ++static void *p_next(struct seq_file *f, void *p, loff_t *pos) ++{ ++ struct aa_profile *profile = p; ++ struct aa_namespace *root = f->private; ++ (*pos)++; ++ ++ return next_profile(root, profile); ++} ++ ++/** ++ * p_stop - stop depth first traversal ++ * @f: seq_file we are filling ++ * @p: the last profile writen ++ * ++ * Release all locking done by p_start/p_next on namespace tree ++ */ ++static void p_stop(struct seq_file *f, void *p) ++ __releases(root->lock) ++{ ++ struct aa_profile *profile = p; ++ struct aa_namespace *root = f->private, *ns; ++ ++ if (profile) { ++ for (ns = profile->ns; ns && ns != root; ns = ns->parent) ++ read_unlock(&ns->lock); ++ } ++ read_unlock(&root->lock); ++ aa_put_namespace(root); ++} ++ ++/** ++ * seq_show_profile - show a profile entry ++ * @f: seq_file to file ++ * @p: current position (profile) (NOT NULL) ++ * ++ * Returns: error on failure ++ */ ++static int seq_show_profile(struct seq_file *f, void *p) ++{ ++ struct aa_profile *profile = (struct aa_profile *)p; ++ struct aa_namespace *root = f->private; ++ ++ if (profile->ns != root) ++ seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); ++ seq_printf(f, "%s (%s)\n", profile->base.hname, ++ COMPLAIN_MODE(profile) ? "complain" : "enforce"); ++ ++ return 0; ++} ++ ++static const struct seq_operations aa_fs_profiles_op = { ++ .start = p_start, ++ .next = p_next, ++ .stop = p_stop, ++ .show = seq_show_profile, ++}; ++ ++static int profiles_open(struct inode *inode, struct file *file) ++{ ++ return seq_open(file, &aa_fs_profiles_op); ++} ++ ++static int profiles_release(struct inode *inode, struct file *file) ++{ ++ return seq_release(inode, file); ++} ++ ++const struct file_operations aa_fs_profiles_fops = { ++ .open = profiles_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = profiles_release, ++}; +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 0848292..28c52ac 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void) + aafs_remove(".remove"); + aafs_remove(".replace"); + aafs_remove(".load"); +- ++#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 ++ aafs_remove("profiles"); ++ aafs_remove("matching"); ++ aafs_remove("features"); ++#endif + securityfs_remove(aa_fs_dentry); + aa_fs_dentry = NULL; + } +@@ -218,7 +222,17 @@ int __init aa_create_aafs(void) + aa_fs_dentry = NULL; + goto error; + } +- ++#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 ++ error = aafs_create("matching", 0444, &aa_fs_matching_fops); ++ if (error) ++ goto error; ++ error = aafs_create("features", 0444, &aa_fs_features_fops); ++ if (error) ++ goto error; ++#endif ++ error = aafs_create("profiles", 0440, &aa_fs_profiles_fops); ++ if (error) ++ goto error; + error = aafs_create(".load", 0640, &aa_fs_profile_load); + if (error) + goto error; +diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h +index cb1e93a..14f955c 100644 +--- a/security/apparmor/include/apparmorfs.h ++++ b/security/apparmor/include/apparmorfs.h +@@ -17,4 +17,10 @@ + + extern void __init aa_destroy_aafs(void); + ++#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 ++extern const struct file_operations aa_fs_matching_fops; ++extern const struct file_operations aa_fs_features_fops; ++extern const struct file_operations aa_fs_profiles_fops; ++#endif ++ + #endif /* __AA_APPARMORFS_H */ +-- +1.7.5.4 + diff --git a/kernel-patches/3.1/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch b/kernel-patches/3.1/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch new file mode 100644 index 000000000..be325854d --- /dev/null +++ b/kernel-patches/3.1/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch @@ -0,0 +1,69 @@ +From 7a10d093f9779f42cb8d6affcb6a4436d3ebd6d3 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 10 Aug 2011 22:02:41 -0700 +Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken + userspace + +The apparmor_parser when compiling policy could generate invalid dfas +that did not have sufficient padding to avoid invalid references, when +used by the kernel. The kernels check to verify the next/check table +size was broken meaning invalid dfas were being created by userspace +and not caught. + +To remain compatible with old tools that are not fixed, pad the loaded +dfas next/check table. The dfa's themselves are valid except for the +high padding for potentially invalid transitions (high bounds error), +which have a maximimum is 256 entries. So just allocate an extra null filled +256 entries for the next/check tables. This will guarentee all bounds +are good and invalid transitions go to the null (0) state. + +Signed-off-by: John Johansen +--- + security/apparmor/match.c | 17 +++++++++++++++++ + 1 files changed, 17 insertions(+), 0 deletions(-) + +diff --git a/security/apparmor/match.c b/security/apparmor/match.c +index 94de6b4..081491e 100644 +--- a/security/apparmor/match.c ++++ b/security/apparmor/match.c +@@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize) + if (bsize < tsize) + goto out; + ++ /* Pad table allocation for next/check by 256 entries to remain ++ * backwards compatible with old (buggy) tools and remain safe without ++ * run time checks ++ */ ++ if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK) ++ tsize += 256 * th.td_flags; ++ + table = kvmalloc(tsize); + if (table) { ++ /* ensure the pad is clear, else there will be errors */ ++ memset(table, 0, tsize); + *table = th; + if (th.td_flags == YYTD_DATA8) + UNPACK_ARRAY(table->td_data, blob, th.td_lolen, +@@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) + goto out; + + if (flags & DFA_FLAG_VERIFY_STATES) { ++ int warning = 0; + for (i = 0; i < state_count; i++) { + if (DEFAULT_TABLE(dfa)[i] >= state_count) + goto out; + /* TODO: do check that DEF state recursion terminates */ + if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { ++ if (warning) ++ continue; ++ printk(KERN_WARNING "AppArmor DFA next/check " ++ "upper bounds error fixed, upgrade " ++ "user space tools \n"); ++ warning = 1; ++ } else if (BASE_TABLE(dfa)[i] >= trans_count) { + printk(KERN_ERR "AppArmor DFA next/check upper " + "bounds error\n"); + goto out; +-- +1.7.5.4 +