From c3701be9fab07d254fbdbbb05f3adee7d8968978 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Sun, 5 Apr 2015 18:14:55 +0200 Subject: [PATCH] add apparmor patches from apparmor package --- ...Armor-Add-profile-introspection-file.patch | 285 ++++++ ...AUCE-AppArmor-basic-networking-rules.patch | 602 +++++++++++ ...eting-of-audit-messages-for-network-.patch | 38 + ...armor-Add-the-ability-to-mediate-mou.patch | 956 ++++++++++++++++++ Makefile | 6 + config-3.10.0.diff | 17 +- 6 files changed, 1896 insertions(+), 8 deletions(-) create mode 100644 0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch create mode 100644 0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch create mode 100644 0003-apparmor-Fix-quieting-of-audit-messages-for-network-.patch create mode 100644 0004-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch diff --git a/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch b/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch new file mode 100644 index 0000000..7a28126 --- /dev/null +++ b/0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch @@ -0,0 +1,285 @@ +From 1b5e29dcf1c18938e62e23f24e9a19c01b861561 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Thu, 22 Jul 2010 02:32:02 -0700 +Subject: [PATCH 1/4] 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 +--- + security/apparmor/Kconfig | 9 ++ + security/apparmor/apparmorfs.c | 231 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 240 insertions(+) + +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/apparmorfs.c b/security/apparmor/apparmorfs.c +index 16c15ec..42b7c9f 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -182,6 +182,234 @@ const struct file_operations aa_fs_seq_file_ops = { + .release = single_release, + }; + ++#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 ++/** ++ * __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, ++}; ++#endif /* CONFIG_SECURITY_APPARMOR_COMPAT_24 */ ++ + /** Base file system setup **/ + + static struct aa_fs_entry aa_fs_entry_file[] = { +@@ -210,6 +438,9 @@ 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), ++#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 ++ AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), ++#endif + AA_FS_DIR("features", aa_fs_entry_features), + { } + }; +-- +1.8.3.2 + diff --git a/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch b/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch new file mode 100644 index 0000000..e19d585 --- /dev/null +++ b/0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch @@ -0,0 +1,602 @@ +From e83b058f391e96a2a640fb2e2812d50ef67e0f43 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Mon, 4 Oct 2010 15:03:36 -0700 +Subject: [PATCH 2/4] UBUNTU: SAUCE: AppArmor: basic networking rules + +Base support for network mediation. + +Signed-off-by: John Johansen +--- + security/apparmor/.gitignore | 1 + + security/apparmor/Makefile | 42 +++++++++- + security/apparmor/apparmorfs.c | 1 + + security/apparmor/include/audit.h | 4 + + security/apparmor/include/net.h | 44 ++++++++++ + security/apparmor/include/policy.h | 3 + + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ + security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ + security/apparmor/policy.c | 1 + + security/apparmor/policy_unpack.c | 46 +++++++++++ + 10 files changed, 414 insertions(+), 2 deletions(-) + create mode 100644 security/apparmor/include/net.h + create mode 100644 security/apparmor/net.c + +diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore +index 9cdec70..d5b291e 100644 +--- a/security/apparmor/.gitignore ++++ b/security/apparmor/.gitignore +@@ -1,5 +1,6 @@ + # + # Generated include files + # ++net_names.h + capability_names.h + rlim_names.h +diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile +index 5706b74..e270692 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/uapi/linux/capability.h \ + $(src)/Makefile +@@ -63,3 +96,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ + $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ + $(src)/Makefile + $(call cmd,make-rlim) ++$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ ++ $(srctree)/include/linux/net.h \ ++ $(src)/Makefile ++ $(call cmd,make-af) ++ $(call cmd,make-sock) +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 42b7c9f..114fb23 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -429,6 +429,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 69d8cae..4af6523 100644 +--- a/security/apparmor/include/audit.h ++++ b/security/apparmor/include/audit.h +@@ -127,6 +127,10 @@ struct apparmor_audit_data { + u32 denied; + kuid_t ouid; + } fs; ++ struct { ++ int type, protocol; ++ struct sock *sk; ++ } net; + }; + }; + +diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h +new file mode 100644 +index 0000000..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 b21830e..1bce440 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" +@@ -614,6 +615,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", + +@@ -646,6 +745,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..003dd18 +--- /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) { ++ sa.type = LSM_AUDIT_DATA_NET; ++ } else { ++ sa.type = LSM_AUDIT_DATA_NONE; ++ } ++ /* todo fill in socket addr info */ ++ sa.aad = &aad; ++ sa.u.net = &net; ++ sa.aad->op = op, ++ sa.u.net->family = family; ++ sa.u.net->sk = sk; ++ sa.aad->net.type = type; ++ sa.aad->net.protocol = protocol; ++ sa.aad->error = error; ++ ++ if (likely(!sa.aad->error)) { ++ u16 audit_mask = profile->net.audit[sa.u.net->family]; ++ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && ++ !(1 << sa.aad->net.type & audit_mask))) ++ return 0; ++ audit_type = AUDIT_APPARMOR_AUDIT; ++ } else { ++ u16 quiet_mask = profile->net.quiet[sa.u.net->family]; ++ u16 kill_mask = 0; ++ u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; ++ ++ if (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 8132003..56e5304 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -747,6 +747,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 329b1fd..1b90dfa 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.8.3.2 + diff --git a/0003-apparmor-Fix-quieting-of-audit-messages-for-network-.patch b/0003-apparmor-Fix-quieting-of-audit-messages-for-network-.patch new file mode 100644 index 0000000..42f6fc3 --- /dev/null +++ b/0003-apparmor-Fix-quieting-of-audit-messages-for-network-.patch @@ -0,0 +1,38 @@ +From ee0073a1e7b0ec172273a6211a3b117d024e5949 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Fri, 29 Jun 2012 17:34:00 -0700 +Subject: [PATCH 3/4] apparmor: Fix quieting of audit messages for network + mediation + +If a profile specified a quieting of network denials for a given rule by +either the quiet or deny rule qualifiers, the resultant quiet mask for +denied requests was applied incorrectly, resulting in two potential bugs. +1. The misapplied quiet mask would prevent denials from being correctly + tested against the kill mask/mode. Thus network access requests that + should have resulted in the application being killed did not. + +2. The actual quieting of the denied network request was not being applied. + This would result in network rejections always being logged even when + they had been specifically marked as quieted. + +Signed-off-by: John Johansen +--- + security/apparmor/net.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/security/apparmor/net.c b/security/apparmor/net.c +index 003dd18..6e6e5c9 100644 +--- a/security/apparmor/net.c ++++ b/security/apparmor/net.c +@@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; +- u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; ++ u16 denied = (1 << sa.aad->net.type); + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; +-- +1.8.3.2 + diff --git a/0004-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch b/0004-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch new file mode 100644 index 0000000..3c508ab --- /dev/null +++ b/0004-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch @@ -0,0 +1,956 @@ +From 7a445944525ad3b2a3f292ddf0d491ae6ed947c1 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 16 May 2012 10:58:05 -0700 +Subject: [PATCH 4/4] 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 e270692..9b44e1a 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 114fb23..ee77ec9 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -426,10 +426,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 3ae28db..e267963 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 859abda..3fee1fe 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 4af6523..ada004d 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, +@@ -122,6 +126,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 1bce440..6750673 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; +@@ -504,6 +505,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) + { +@@ -721,6 +776,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..478aa4d +--- /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; ++ } ++ ++ sa.type = LSM_AUDIT_DATA_NONE; ++ sa.aad = &aad; ++ sa.aad->op = op; ++ sa.aad->name = name; ++ sa.aad->mnt.src_name = src_name; ++ sa.aad->mnt.type = type; ++ sa.aad->mnt.trans = trans; ++ sa.aad->mnt.flags = flags; ++ if (data && (perms->audit & AA_AUDIT_DATA)) ++ sa.aad->mnt.data = data; ++ sa.aad->info = info; ++ sa.aad->error = error; ++ ++ return aa_audit(audit_type, profile, gfp, &sa, audit_cb); ++} ++ ++/** ++ * 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.8.3.2 + diff --git a/Makefile b/Makefile index d53e5ed..a412553 100644 --- a/Makefile +++ b/Makefile @@ -210,6 +210,12 @@ ${KERNEL_SRC}/README: ${KERNEL_SRC}.org/README cd ${KERNEL_SRC}; patch -p1 <../add-empty-ndo_poll_controller-to-veth.patch cd ${KERNEL_SRC}; patch -p1 <../override_for_missing_acs_capabilities.patch cd ${KERNEL_SRC}; patch -p1 <../vhost-net-extend-device-allocation-to-vmalloc.patch + # patches from apparmor package + cd ${KERNEL_SRC}; patch -p1 <../0001-UBUNTU-SAUCE-AppArmor-Add-profile-introspection-file.patch + cd ${KERNEL_SRC}; patch -p1 <../0002-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch + cd ${KERNEL_SRC}; patch -p1 <../0003-apparmor-Fix-quieting-of-audit-messages-for-network-.patch + cd ${KERNEL_SRC}; patch -p1 <../0004-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch + # apparmor update from upstream cd ${KERNEL_SRC}; patch -p1 <../apparmor-01-add-kvzalloc-to-handle-zeroing-for-kvmalloc.patch cd ${KERNEL_SRC}; patch -p1 <../apparmor-02-fix-fully-qualified-name-parsing.patch cd ${KERNEL_SRC}; patch -p1 <../apparmor-03-no-need-to-delay-vfree.patch diff --git a/config-3.10.0.diff b/config-3.10.0.diff index a89dff1..2b83eb3 100644 --- a/config-3.10.0.diff +++ b/config-3.10.0.diff @@ -1,5 +1,5 @@ ---- rh-kernel-src/kernel-3.10.0-x86_64.config 2015-03-14 14:15:00.347227546 +0100 -+++ config-3.10.0 2015-04-04 19:17:16.553879288 +0200 +--- rh-kernel-src/kernel-3.10.0-x86_64.config 2015-04-05 18:12:54.643110125 +0200 ++++ linux-2.6-3.10.0/.config 2015-04-05 18:13:09.244970726 +0200 @@ -1,7 +1,6 @@ -# x86_64 # @@ -176,7 +176,7 @@ CONFIG_DLM=m CONFIG_DLM_DEBUG=y -@@ -5017,29 +5016,29 @@ +@@ -5017,29 +5016,30 @@ CONFIG_KEYS=y CONFIG_PERSISTENT_KEYRINGS=y CONFIG_BIG_KEYS=y @@ -210,10 +210,11 @@ -# CONFIG_SECURITY_APPARMOR is not set +CONFIG_SECURITY_APPARMOR=y +CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=1 ++CONFIG_SECURITY_APPARMOR_COMPAT_24=y # CONFIG_SECURITY_YAMA is not set CONFIG_INTEGRITY=y CONFIG_INTEGRITY_SIGNATURE=y -@@ -5050,11 +5049,10 @@ +@@ -5050,11 +5050,10 @@ CONFIG_IMA_LSM_RULES=y CONFIG_IMA_APPRAISE=y CONFIG_IMA_TRUSTED_KEYRING=y @@ -228,7 +229,7 @@ CONFIG_XOR_BLOCKS=m CONFIG_ASYNC_CORE=m CONFIG_ASYNC_MEMCPY=m -@@ -5066,7 +5064,6 @@ +@@ -5066,7 +5065,6 @@ # # Crypto core or helper # @@ -236,7 +237,7 @@ CONFIG_CRYPTO_ALGAPI=y CONFIG_CRYPTO_ALGAPI2=y CONFIG_CRYPTO_AEAD=y -@@ -5126,7 +5123,7 @@ +@@ -5126,7 +5124,7 @@ CONFIG_CRYPTO_CRC32C_INTEL=m CONFIG_CRYPTO_CRC32=m CONFIG_CRYPTO_CRC32_PCLMUL=m @@ -245,7 +246,7 @@ CONFIG_CRYPTO_CRCT10DIF_PCLMUL=m CONFIG_CRYPTO_GHASH=m CONFIG_CRYPTO_MD4=m -@@ -5252,7 +5249,7 @@ +@@ -5252,7 +5250,7 @@ CONFIG_CMPXCHG_LOCKREF=y CONFIG_CRC_CCITT=m CONFIG_CRC16=y @@ -254,7 +255,7 @@ CONFIG_CRC_ITU_T=m CONFIG_CRC32=y # CONFIG_CRC32_SELFTEST is not set -@@ -5301,6 +5298,7 @@ +@@ -5301,6 +5299,7 @@ CONFIG_DQL=y CONFIG_NLATTR=y CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y -- 2.39.2