From 92abc4c61088c561a0060219cc95da5eaf79a857 Mon Sep 17 00:00:00 2001 From: Austin Seipp Date: Wed, 7 May 2014 17:59:29 -0500 Subject: [PATCH] kernel: enable AppArmor by default AppArmor only requires a few patches to the 3.2 and 3.4 kernels in order to work properly (with the minor catch grsecurity -stable includes the 3.2 patches.) This adds them to the kernel builds by default, removes features.apparmor (since it's always true) and makes it the default MAC system. Signed-off-by: Austin Seipp --- nixos/modules/security/apparmor.nix | 7 - nixos/modules/security/grsecurity.nix | 4 +- ...bility-patch-for-v5-network-controll.patch | 553 ++++++++++ ...compatibility-patch-for-v5-interface.patch | 391 +++++++ ...fa-backward-compatibility-with-broke.patch | 69 ++ ...Armor-Add-profile-introspection-file.patch | 264 +++++ ...AUCE-AppArmor-basic-networking-rules.patch | 603 +++++++++++ ...armor-Add-the-ability-to-mediate-mou.patch | 957 ++++++++++++++++++ .../linux/kernel/common-config.nix | 4 + pkgs/os-specific/linux/kernel/linux-3.2.nix | 16 +- pkgs/os-specific/linux/kernel/linux-3.4.nix | 11 + pkgs/os-specific/linux/kernel/patches.nix | 25 - pkgs/top-level/all-packages.nix | 18 - 13 files changed, 2868 insertions(+), 54 deletions(-) create mode 100644 pkgs/os-specific/linux/kernel/apparmor-patches/3.2/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch create mode 100644 pkgs/os-specific/linux/kernel/apparmor-patches/3.2/0002-AppArmor-compatibility-patch-for-v5-interface.patch create mode 100644 pkgs/os-specific/linux/kernel/apparmor-patches/3.2/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch create mode 100644 pkgs/os-specific/linux/kernel/apparmor-patches/3.4/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch create mode 100644 pkgs/os-specific/linux/kernel/apparmor-patches/3.4/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch create mode 100644 pkgs/os-specific/linux/kernel/apparmor-patches/3.4/0003-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix index da7c93beee98..f29e7a5ad818 100644 --- a/nixos/modules/security/apparmor.nix +++ b/nixos/modules/security/apparmor.nix @@ -23,13 +23,6 @@ in }; config = mkIf cfg.enable { - assertions = - [ { assertion = config.boot.kernelPackages.kernel.features ? apparmor - && config.boot.kernelPackages.kernel.features.apparmor; - message = "Your selected kernel does not have AppArmor support"; - } - ]; - environment.systemPackages = [ pkgs.apparmor ]; systemd.services.apparmor = { wantedBy = [ "local-fs.target" ]; diff --git a/nixos/modules/security/grsecurity.nix b/nixos/modules/security/grsecurity.nix index e6db05e11f43..a0f63cdf3a9e 100644 --- a/nixos/modules/security/grsecurity.nix +++ b/nixos/modules/security/grsecurity.nix @@ -73,8 +73,6 @@ let restrictLinks = optionalString cfg.testing "GRKERNSEC_LINK n"; in '' - SECURITY_APPARMOR y - DEFAULT_SECURITY_APPARMOR y GRKERNSEC y ${grsecMainConfig} @@ -434,7 +432,7 @@ in chmod -R 0600 /etc/grsec ''; - # Enable apparmor support, gradm udev rules, and utilities + # Enable AppArmor, gradm udev rules, and utilities security.apparmor.enable = true; boot.kernelPackages = grsecPackage; services.udev.packages = [ pkgs.gradm ]; diff --git a/pkgs/os-specific/linux/kernel/apparmor-patches/3.2/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch b/pkgs/os-specific/linux/kernel/apparmor-patches/3.2/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch new file mode 100644 index 000000000000..b411f43298c9 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/apparmor-patches/3.2/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch @@ -0,0 +1,553 @@ +From 125fccb600288968aa3395883c0a394c47176fcd 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 3783202..7459547 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 741dd13..ee8043e 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.9.5 + diff --git a/pkgs/os-specific/linux/kernel/apparmor-patches/3.2/0002-AppArmor-compatibility-patch-for-v5-interface.patch b/pkgs/os-specific/linux/kernel/apparmor-patches/3.2/0002-AppArmor-compatibility-patch-for-v5-interface.patch new file mode 100644 index 000000000000..aa4b6b1109f5 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/apparmor-patches/3.2/0002-AppArmor-compatibility-patch-for-v5-interface.patch @@ -0,0 +1,391 @@ +From 004192fb5223c7b81a949e36a080a5da56132826 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 69ddb47..867995c 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 @@ static 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.9.5 + diff --git a/pkgs/os-specific/linux/kernel/apparmor-patches/3.2/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch b/pkgs/os-specific/linux/kernel/apparmor-patches/3.2/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch new file mode 100644 index 000000000000..7dd55781fdaa --- /dev/null +++ b/pkgs/os-specific/linux/kernel/apparmor-patches/3.2/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch @@ -0,0 +1,69 @@ +From e5d90918aa31f948ecec2f3c088567dbab30c90b 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 file changed, 17 insertions(+) + +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.9.5 + diff --git a/pkgs/os-specific/linux/kernel/apparmor-patches/3.4/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch b/pkgs/os-specific/linux/kernel/apparmor-patches/3.4/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch new file mode 100644 index 000000000000..88a50ca780ae --- /dev/null +++ b/pkgs/os-specific/linux/kernel/apparmor-patches/3.4/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch @@ -0,0 +1,264 @@ +From 8de755e4dfdbc40bfcaca848ae6b5aeaf0ede0e8 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Thu, 22 Jul 2010 02:32:02 -0700 +Subject: [PATCH 1/3] UBUNTU: SAUCE: AppArmor: Add profile introspection file + to interface + +Add the dynamic profiles file to the interace, to allow load policy +introspection. + +Signed-off-by: John Johansen +Acked-by: Kees Cook +Signed-off-by: Tim Gardner +--- + security/apparmor/apparmorfs.c | 227 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 227 insertions(+) + +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 16c15ec..89bdc62 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -182,6 +182,232 @@ const struct file_operations aa_fs_seq_file_ops = { + .release = single_release, + }; + ++/** ++ * __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, ++}; ++ + /** Base file system setup **/ + + static struct aa_fs_entry aa_fs_entry_file[] = { +@@ -210,6 +436,7 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { + AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), + AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), + AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), ++ AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), + AA_FS_DIR("features", aa_fs_entry_features), + { } + }; +-- +1.7.9.5 + diff --git a/pkgs/os-specific/linux/kernel/apparmor-patches/3.4/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch b/pkgs/os-specific/linux/kernel/apparmor-patches/3.4/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch new file mode 100644 index 000000000000..01316b9db78b --- /dev/null +++ b/pkgs/os-specific/linux/kernel/apparmor-patches/3.4/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch @@ -0,0 +1,603 @@ +From 423e2cb454d75d6185eecd0c1b5cf6ccc2d8482d Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Mon, 4 Oct 2010 15:03:36 -0700 +Subject: [PATCH 2/3] UBUNTU: SAUCE: AppArmor: basic networking rules + +Base support for network mediation. + +Signed-off-by: John Johansen +--- + security/apparmor/.gitignore | 2 +- + 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(+), 3 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 4d995ae..d5b291e 100644 +--- a/security/apparmor/.gitignore ++++ b/security/apparmor/.gitignore +@@ -1,6 +1,6 @@ + # + # Generated include files + # +-af_names.h ++net_names.h + capability_names.h + rlim_names.h +diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile +index 806bd19..19daa85 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 net_names.h + + + # Build a lower case string table of capability names +@@ -20,6 +20,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ + -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ + echo "};" >> $@ + ++# 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", ++# ++# 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 $< >>$@ -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 +@@ -56,6 +88,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/linux/capability.h \ + $(src)/Makefile +@@ -63,3 +96,8 @@ $(obj)/capability_names.h : $(srctree)/include/linux/capability.h \ + $(obj)/rlim_names.h : $(srctree)/include/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 89bdc62..c66315d 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -427,6 +427,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { + 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_FILE_U64("capability", VFS_CAP_FLAGS_MASK), + AA_FS_DIR("rlimit", aa_fs_entry_rlimit), + { } +diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h +index 3868b1e..c1ff09c 100644 +--- a/security/apparmor/include/audit.h ++++ b/security/apparmor/include/audit.h +@@ -126,6 +126,10 @@ struct apparmor_audit_data { + u32 denied; + uid_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..cb8a121 +--- /dev/null ++++ b/security/apparmor/include/net.h +@@ -0,0 +1,44 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor network mediation definitions. ++ * ++ * 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 ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ */ ++ ++#ifndef __AA_NET_H ++#define __AA_NET_H ++ ++#include ++ ++#include "apparmorfs.h" ++ ++/* 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 struct aa_fs_entry aa_fs_entry_network[]; ++ ++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 bda4569..eb13a73 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 profile_mode_names[]; +@@ -157,6 +158,7 @@ struct aa_policydb { + * @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 + * + * The AppArmor profile contains the basic confinement data. Each profile +@@ -194,6 +196,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; + }; + +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index ad05d39..3cde194 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" +@@ -622,6 +623,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", + +@@ -653,6 +752,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..084232b +--- /dev/null ++++ b/security/apparmor/net.c +@@ -0,0 +1,162 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor network mediation ++ * ++ * 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 ++ * 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 "net_names.h" ++ ++struct aa_fs_entry aa_fs_entry_network[] = { ++ AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), ++ { } ++}; ++ ++/* 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; ++ struct apparmor_audit_data aad = { }; ++ struct lsm_network_audit net = { }; ++ if (sk) { ++ COMMON_AUDIT_DATA_INIT(&sa, NET); ++ } else { ++ COMMON_AUDIT_DATA_INIT(&sa, 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 (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 f1f7506..b8100a7 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 deab7c7..8f8e9c1 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) ++{ ++ 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)) { +@@ -471,6 +484,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; +@@ -564,6 +578,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; ++ + if (unpack_nameX(e, AA_STRUCT, "policydb")) { + /* generic policy dfa - optional and may be NULL */ + profile->policy.dfa = unpack_dfa(e); +-- +1.7.9.5 + diff --git a/pkgs/os-specific/linux/kernel/apparmor-patches/3.4/0003-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch b/pkgs/os-specific/linux/kernel/apparmor-patches/3.4/0003-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch new file mode 100644 index 000000000000..48b34343e0b8 --- /dev/null +++ b/pkgs/os-specific/linux/kernel/apparmor-patches/3.4/0003-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch @@ -0,0 +1,957 @@ +From a94d5e11c0484af59e5feebf144cc48c186892ad 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 +--- + security/apparmor/Makefile | 2 +- + security/apparmor/apparmorfs.c | 13 + + 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, 767 insertions(+), 3 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 19daa85..63e0a4c 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 + + clean-files := capability_names.h rlim_names.h net_names.h + +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index c66315d..ff19009 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -424,10 +424,23 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { + { } + }; + ++static struct aa_fs_entry aa_fs_entry_mount[] = { ++ AA_FS_FILE_STRING("mask", "mount umount"), ++ { } ++}; ++ ++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[] = { + 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), + { } +diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c +index cc3520d..b9f5ee9 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", ++ + "create", + "post_create", + "bind", +diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c +index 6327685..dfdc47b 100644 +--- a/security/apparmor/domain.c ++++ b/security/apparmor/domain.c +@@ -242,7 +242,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 40aedd9..e243d96 100644 +--- a/security/apparmor/include/apparmor.h ++++ b/security/apparmor/include/apparmor.h +@@ -29,8 +29,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 c1ff09c..7b90900c 100644 +--- a/security/apparmor/include/audit.h ++++ b/security/apparmor/include/audit.h +@@ -73,6 +73,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..bc17a53 +--- /dev/null ++++ b/security/apparmor/include/mount.h +@@ -0,0 +1,54 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor file mediation function definitions. ++ * ++ * 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 ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ */ ++ ++#ifndef __AA_MOUNT_H ++#define __AA_MOUNT_H ++ ++#include ++#include ++ ++#include "domain.h" ++#include "policy.h" ++ ++/* 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 ++ ++#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) ++ ++int aa_remount(struct aa_profile *profile, struct path *path, ++ unsigned long flags, void *data); ++ ++int aa_bind_mount(struct aa_profile *profile, struct path *path, ++ const char *old_name, unsigned long flags); ++ ++ ++int aa_mount_change_type(struct aa_profile *profile, struct path *path, ++ unsigned long flags); ++ ++int aa_move_mount(struct aa_profile *profile, struct path *path, ++ const char *old_name); ++ ++int aa_new_mount(struct aa_profile *profile, const char *dev_name, ++ struct path *path, const char *type, unsigned long flags, ++ void *data); ++ ++int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); ++ ++int aa_pivotroot(struct aa_profile *profile, struct path *old_path, ++ struct path *new_path); ++ ++#endif /* __AA_MOUNT_H */ +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index 3cde194..4512cc6 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; +@@ -512,6 +513,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) ++{ ++ 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); ++ } ++ return error; ++} ++ ++static int apparmor_sb_umount(struct vfsmount *mnt, int flags) ++{ ++ struct aa_profile *profile; ++ int error = 0; ++ ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) ++ error = aa_umount(profile, mnt, flags); ++ ++ return error; ++} ++ ++static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) ++{ ++ struct aa_profile *profile; ++ int error = 0; ++ ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) ++ error = aa_pivotroot(profile, old_path, new_path); ++ ++ return error; ++} ++ + static int apparmor_getprocattr(struct task_struct *task, char *name, + char **value) + { +@@ -729,6 +784,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..63d8493 +--- /dev/null ++++ b/security/apparmor/mount.c +@@ -0,0 +1,620 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor mediation of files ++ * ++ * 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 ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ */ ++ ++#include ++#include ++#include ++ ++#include "include/apparmor.h" ++#include "include/audit.h" ++#include "include/context.h" ++#include "include/domain.h" ++#include "include/file.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"); ++} ++ ++/** ++ * audit_cb - call back for mount specific audit fields ++ * @ab: audit_buffer (NOT NULL) ++ * @va: audit struct to audit values of (NOT NULL) ++ */ ++static void audit_cb(struct audit_buffer *ab, void *va) ++{ ++ struct common_audit_data *sa = va; ++ ++ 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); ++ } ++} ++ ++/** ++ * 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; ++ ++ if (likely(!request)) ++ return 0; ++ audit_type = AUDIT_APPARMOR_AUDIT; ++ } else { ++ /* only report permissions that were denied */ ++ request = request & ~perms->allow; ++ ++ if (request & perms->kill) ++ audit_type = AUDIT_APPARMOR_KILL; ++ ++ /* 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; ++ ++ if (!request) ++ return COMPLAIN_MODE(profile) ? ++ complain_error(error) : error; ++ } ++ ++ COMMON_AUDIT_DATA_INIT(&sa, 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); ++} ++ ++/** ++ * 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 unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, ++ unsigned long flags) ++{ ++ unsigned int i; ++ ++ for (i = 0; i <= 31 ; ++i) { ++ if ((1 << i) & flags) ++ state = aa_dfa_next(dfa, state, i + 1); ++ } ++ ++ return state; ++} ++ ++/** ++ * compute_mnt_perms - compute mount permission associated with @state ++ * @dfa: dfa to match against (NOT NULL) ++ * @state: state match finished in ++ * ++ * Returns: mount permissions ++ */ ++static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, ++ unsigned int state) ++{ ++ 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" ++}; ++ ++/* ++ * Returns 0 on success else element that match failed in, this is the ++ * index into the mnt_info_table above ++ */ ++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) ++{ ++ 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; ++ } ++ ++ /* failed at end of flags match */ ++ return 4; ++} ++ ++/** ++ * 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; ++ ++ 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; ++ } ++ ++ return 0; ++} ++ ++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 aa_remount(struct aa_profile *profile, struct path *path, ++ unsigned long flags, void *data) ++{ ++ struct file_perms perms = { }; ++ const char *name, *info = NULL; ++ char *buffer = NULL; ++ int binary, error; ++ ++ binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; ++ ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; ++ ++ error = match_mnt(profile, name, NULL, NULL, flags, data, binary, ++ &perms, &info); ++ ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, ++ NULL, flags, data, AA_MAY_MOUNT, &perms, info, ++ error); ++ kfree(buffer); ++ ++ return error; ++} ++ ++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 (!dev_name || !*dev_name) ++ return -EINVAL; ++ ++ flags &= MS_REC | MS_BIND; ++ ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; ++ ++ error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); ++ if (error) ++ goto audit; ++ ++ error = aa_path_name(&old_path, path_flags(profile, &old_path), ++ &old_buffer, &old_name, &info); ++ path_put(&old_path); ++ if (error) ++ goto audit; ++ ++ error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, ++ &perms, &info); ++ ++audit: ++ 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 error; ++} ++ ++int aa_mount_change_type(struct aa_profile *profile, struct path *path, ++ unsigned long flags) ++{ ++ struct file_perms perms = { }; ++ char *buffer = NULL; ++ const char *name, *info = NULL; ++ int error; ++ ++ /* These are the flags allowed by do_change_type() */ ++ flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | ++ MS_UNBINDABLE); ++ ++ 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); ++ ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, ++ NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, ++ error); ++ kfree(buffer); ++ ++ return error; ++} ++ ++int aa_move_mount(struct aa_profile *profile, struct path *path, ++ const char *orig_name) ++{ ++ 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 (!orig_name || !*orig_name) ++ return -EINVAL; ++ ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; ++ ++ error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); ++ if (error) ++ goto audit; ++ ++ error = aa_path_name(&old_path, path_flags(profile, &old_path), ++ &old_buffer, &old_name, &info); ++ path_put(&old_path); ++ if (error) ++ goto audit; ++ ++ error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, ++ &perms, &info); ++ ++audit: ++ 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); ++ ++ return error; ++} ++ ++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 file_perms perms = { }; ++ char *buffer = NULL, *dev_buffer = NULL; ++ const char *name = NULL, *dev_name = NULL, *info = NULL; ++ int binary = 1; ++ int error; ++ ++ dev_name = orig_dev_name; ++ if (type) { ++ int requires_dev; ++ struct file_system_type *fstype = get_fs_type(type); ++ if (!fstype) ++ return -ENODEV; ++ ++ binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; ++ requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; ++ put_filesystem(fstype); ++ ++ if (requires_dev) { ++ struct path dev_path; ++ ++ if (!dev_name || !*dev_name) { ++ error = -ENOENT; ++ goto out; ++ } ++ ++ error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); ++ if (error) ++ goto audit; ++ ++ error = aa_path_name(&dev_path, ++ path_flags(profile, &dev_path), ++ &dev_buffer, &dev_name, &info); ++ path_put(&dev_path); ++ if (error) ++ goto audit; ++ } ++ } ++ ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; ++ ++ error = match_mnt(profile, name, dev_name, type, flags, data, binary, ++ &perms, &info); ++ ++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); ++ ++out: ++ return error; ++ ++} ++ ++int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) ++{ ++ struct file_perms perms = { }; ++ char *buffer = NULL; ++ const char *name, *info = NULL; ++ int error; ++ ++ struct path path = { mnt, mnt->mnt_root }; ++ error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; ++ ++ 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); ++ } ++ ++ if (AA_MAY_UMOUNT & ~perms.allow) ++ error = -EACCES; ++ ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, ++ NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); ++ kfree(buffer); ++ ++ return error; ++} ++ ++int aa_pivotroot(struct aa_profile *profile, struct path *old_path, ++ struct path *new_path) ++{ ++ 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; ++ ++ error = aa_path_name(old_path, path_flags(profile, old_path), ++ &old_buffer, &old_name, &info); ++ if (error) ++ goto audit; ++ ++ error = aa_path_name(new_path, path_flags(profile, new_path), ++ &new_buffer, &new_name, &info); ++ if (error) ++ goto audit; ++ ++ 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: ++ 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); ++ ++ return error; ++} +-- +1.7.9.5 + diff --git a/pkgs/os-specific/linux/kernel/common-config.nix b/pkgs/os-specific/linux/kernel/common-config.nix index db654fc0505c..7f668c96bc52 100644 --- a/pkgs/os-specific/linux/kernel/common-config.nix +++ b/pkgs/os-specific/linux/kernel/common-config.nix @@ -177,6 +177,10 @@ with stdenv.lib; USER_NS y # Support for user namespaces ''} + # AppArmor support + SECURITY_APPARMOR y + DEFAULT_SECURITY_APPARMOR y + # Misc. options. 8139TOO_8129 y 8139TOO_PIO n # PIO is slower diff --git a/pkgs/os-specific/linux/kernel/linux-3.2.nix b/pkgs/os-specific/linux/kernel/linux-3.2.nix index c0c32b051d67..4cb26a78abb1 100644 --- a/pkgs/os-specific/linux/kernel/linux-3.2.nix +++ b/pkgs/os-specific/linux/kernel/linux-3.2.nix @@ -9,5 +9,19 @@ import ./generic.nix (args // rec { sha256 = "1mszzixiv4k61m241dl2n5s8rca26l6hc40v23lha814nrahjkn1"; }; - features.iwlwifi = true; + # We don't provide these patches if grsecurity is enabled, because + # the grsec 3.2 -stable patchset already includes them. + kernelPatches = args.kernelPatches ++ ( + stdenv.lib.optional (!(args.features.grsecurity or false)) + [ { name = "0001-AppArmor-compatibility-patch-for-v5-network-controll"; + patch = ./apparmor-patches/3.2/0001-AppArmor-compatibility-patch-for-v5-network-controll.patch; + } + { name = "0002-AppArmor-compatibility-patch-for-v5-interface"; + patch = ./apparmor-patches/3.2/0002-AppArmor-compatibility-patch-for-v5-interface.patch; + } + { name = "0003-AppArmor-Allow-dfa-backward-compatibility-with-broke"; + patch = ./apparmor-patches/3.2/0003-AppArmor-Allow-dfa-backward-compatibility-with-broke.patch; + }]); + + features.iwlwifi = true; } // (args.argsOverride or {})) diff --git a/pkgs/os-specific/linux/kernel/linux-3.4.nix b/pkgs/os-specific/linux/kernel/linux-3.4.nix index 68fb2496a133..63d0d4904024 100644 --- a/pkgs/os-specific/linux/kernel/linux-3.4.nix +++ b/pkgs/os-specific/linux/kernel/linux-3.4.nix @@ -9,6 +9,17 @@ import ./generic.nix (args // rec { sha256 = "0kh4y1sbsjm3awplfsd0i59rz7wc1dj23mcs5rwwhc0p7i8w4r75"; }; + kernelPatches = args.kernelPatches ++ + [ { name = "0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file"; + patch = ./apparmor-patches/3.4/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch; + } + { name = "0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules"; + patch = ./apparmor-patches/3.4/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch; + } + { name = "0003-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou"; + patch = ./apparmor-patches/3.4/0003-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch; + }]; + features.iwlwifi = true; features.efiBootStub = true; features.needsCifsUtils = true; diff --git a/pkgs/os-specific/linux/kernel/patches.nix b/pkgs/os-specific/linux/kernel/patches.nix index de1f65e29053..c90f869bf4bc 100644 --- a/pkgs/os-specific/linux/kernel/patches.nix +++ b/pkgs/os-specific/linux/kernel/patches.nix @@ -26,37 +26,12 @@ let inherit sha256; }; features.grsecurity = true; - # The grsec kernel patchset includes AppArmor patches - features.apparmor = true; - }; - - makeAppArmorPatch = {apparmor, version}: - stdenv.mkDerivation { - name = "apparmor-${version}.patch"; - phases = ["installPhase"]; - installPhase = '' - cat ${apparmor}/kernel-patches/${version}/* > $out - ''; }; in rec { - apparmor_3_2 = rec { - version = "3.2"; - name = "apparmor-${version}"; - patch = makeAppArmorPatch { inherit apparmor version; }; - features.apparmor = true; - }; - - apparmor_3_4 = rec { - version = "3.4"; - name = "apparmor-${version}"; - patch = makeAppArmorPatch { inherit apparmor version; }; - features.apparmor = true; - }; - no_xsave = { name = "no-xsave"; patch = ./no-xsave.patch; diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 4a61d064bf2e..77af3087b3d6 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -7064,14 +7064,6 @@ let kernelPatches = []; }; - linux_3_2_apparmor = lowPrio (linux_3_2.override { - kernelPatches = [ kernelPatches.apparmor_3_2 ]; - extraConfig = '' - SECURITY_APPARMOR y - DEFAULT_SECURITY_APPARMOR y - ''; - }); - linux_3_2_xen = lowPrio (linux_3_2.override { extraConfig = '' XEN_DOM0 y @@ -7086,14 +7078,6 @@ let ]; }; - linux_3_4_apparmor = lowPrio (linux_3_4.override { - kernelPatches = [ kernelPatches.apparmor_3_4 ]; - extraConfig = '' - SECURITY_APPARMOR y - DEFAULT_SECURITY_APPARMOR y - ''; - }); - linux_3_6_rpi = makeOverridable (import ../os-specific/linux/kernel/linux-rpi-3.6.nix) { inherit fetchurl stdenv perl buildLinux; }; @@ -7233,10 +7217,8 @@ let # Build the kernel modules for the some of the kernels. linuxPackages_3_2 = recurseIntoAttrs (linuxPackagesFor pkgs.linux_3_2 linuxPackages_3_2); - linuxPackages_3_2_apparmor = linuxPackagesFor pkgs.linux_3_2_apparmor linuxPackages_3_2_apparmor; linuxPackages_3_2_xen = linuxPackagesFor pkgs.linux_3_2_xen linuxPackages_3_2_xen; linuxPackages_3_4 = recurseIntoAttrs (linuxPackagesFor pkgs.linux_3_4 linuxPackages_3_4); - linuxPackages_3_4_apparmor = linuxPackagesFor pkgs.linux_3_4_apparmor linuxPackages_3_4_apparmor; linuxPackages_3_6_rpi = linuxPackagesFor pkgs.linux_3_6_rpi linuxPackages_3_6_rpi; linuxPackages_3_10 = recurseIntoAttrs (linuxPackagesFor pkgs.linux_3_10 linuxPackages_3_10); linuxPackages_3_10_tuxonice = linuxPackagesFor pkgs.linux_3_10_tuxonice linuxPackages_3_10_tuxonice;