--- /dev/null
+From: John Johansen <jjohansen@suse.de>
+Subject: AppArmor: Simplified network controls for AppArmor
+
+Simple network control determining which network families a confined
+application has access to.
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ security/apparmor/Makefile | 7 +
+ security/apparmor/apparmor.h | 9 ++
+ security/apparmor/lsm.c | 129 ++++++++++++++++++++++++++++++++++-
+ security/apparmor/main.c | 107 ++++++++++++++++++++++++++++-
+ security/apparmor/module_interface.c | 26 ++++++-
+ 5 files changed, 271 insertions(+), 7 deletions(-)
+
+--- a/security/apparmor/Makefile
++++ b/security/apparmor/Makefile
+@@ -8,6 +8,11 @@ apparmor-y := main.o list.o procattr.o l
+ quiet_cmd_make-caps = GEN $@
+ cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@
+
+-$(obj)/main.o : $(obj)/capability_names.h
++quiet_cmd_make-af = GEN $@
++cmd_make-af = sed -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "s/^\#define[ \\t]\\+AF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@
++
++$(obj)/main.o : $(obj)/capability_names.h $(obj)/af_names.h
+ $(obj)/capability_names.h : $(srctree)/include/linux/capability.h
+ $(call cmd,make-caps)
++$(obj)/af_names.h : $(srctree)/include/linux/socket.h
++ $(call cmd,make-af)
+--- a/security/apparmor/apparmor.h
++++ b/security/apparmor/apparmor.h
+@@ -16,6 +16,8 @@
+ #include <linux/fs.h>
+ #include <linux/binfmts.h>
+ #include <linux/rcupdate.h>
++#include <linux/socket.h>
++#include <net/sock.h>
+
+ /*
+ * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags
+@@ -212,6 +214,9 @@ struct aa_profile {
+ struct list_head task_contexts;
+ spinlock_t lock;
+ unsigned long int_flags;
++ u16 network_families[AF_MAX];
++ u16 audit_network[AF_MAX];
++ u16 quiet_network[AF_MAX];
+ };
+
+ extern struct list_head profile_ns_list;
+@@ -258,6 +263,7 @@ struct aa_audit {
+ int request_mask, denied_mask, audit_mask;
+ struct iattr *iattr;
+ pid_t task, parent;
++ int family, type, protocol;
+ int error_code;
+ };
+
+@@ -319,6 +325,9 @@ extern void aa_change_task_context(struc
+ struct aa_profile *previous_profile);
+ extern int aa_may_ptrace(struct aa_task_context *cxt,
+ struct aa_profile *tracee);
++extern int aa_net_perm(struct aa_profile *profile, char *operation,
++ int family, int type, int protocol);
++extern int aa_revalidate_sk(struct sock *sk, char *operation);
+
+ /* lsm.c */
+ extern int apparmor_initialized;
+--- a/security/apparmor/lsm.c
++++ b/security/apparmor/lsm.c
+@@ -18,6 +18,7 @@
+ #include <linux/ctype.h>
+ #include <linux/sysctl.h>
+ #include <linux/audit.h>
++#include <net/sock.h>
+
+ #include "apparmor.h"
+ #include "inline.h"
+@@ -680,6 +681,117 @@ static void apparmor_task_free_security(
+ aa_release(task);
+ }
+
++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_get_profile(current);
++ if (profile)
++ error = aa_net_perm(profile, "socket_create", family,
++ type, protocol);
++ aa_put_profile(profile);
++
++ return error;
++}
++
++static int apparmor_socket_post_create(struct socket *sock, int family,
++ int type, int protocol, int kern)
++{
++ struct sock *sk = sock->sk;
++
++ if (kern)
++ return 0;
++
++ return aa_revalidate_sk(sk, "socket_post_create");
++}
++
++static int apparmor_socket_bind(struct socket *sock,
++ struct sockaddr *address, int addrlen)
++{
++ struct sock *sk = sock->sk;
++
++ return aa_revalidate_sk(sk, "socket_bind");
++}
++
++static int apparmor_socket_connect(struct socket *sock,
++ struct sockaddr *address, int addrlen)
++{
++ struct sock *sk = sock->sk;
++
++ return aa_revalidate_sk(sk, "socket_connect");
++}
++
++static int apparmor_socket_listen(struct socket *sock, int backlog)
++{
++ struct sock *sk = sock->sk;
++
++ return aa_revalidate_sk(sk, "socket_listen");
++}
++
++static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
++{
++ struct sock *sk = sock->sk;
++
++ return aa_revalidate_sk(sk, "socket_accept");
++}
++
++static int apparmor_socket_sendmsg(struct socket *sock,
++ struct msghdr *msg, int size)
++{
++ struct sock *sk = sock->sk;
++
++ return aa_revalidate_sk(sk, "socket_sendmsg");
++}
++
++static int apparmor_socket_recvmsg(struct socket *sock,
++ struct msghdr *msg, int size, int flags)
++{
++ struct sock *sk = sock->sk;
++
++ return aa_revalidate_sk(sk, "socket_recvmsg");
++}
++
++static int apparmor_socket_getsockname(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++
++ return aa_revalidate_sk(sk, "socket_getsockname");
++}
++
++static int apparmor_socket_getpeername(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++
++ return aa_revalidate_sk(sk, "socket_getpeername");
++}
++
++static int apparmor_socket_getsockopt(struct socket *sock, int level,
++ int optname)
++{
++ struct sock *sk = sock->sk;
++
++ return aa_revalidate_sk(sk, "socket_getsockopt");
++}
++
++static int apparmor_socket_setsockopt(struct socket *sock, int level,
++ int optname)
++{
++ struct sock *sk = sock->sk;
++
++ return aa_revalidate_sk(sk, "socket_setsockopt");
++}
++
++static int apparmor_socket_shutdown(struct socket *sock, int how)
++{
++ struct sock *sk = sock->sk;
++
++ return aa_revalidate_sk(sk, "socket_shutdown");
++}
++
+ static int apparmor_getprocattr(struct task_struct *task, char *name,
+ char **value)
+ {
+@@ -780,9 +892,6 @@ struct security_operations apparmor_ops
+ .capable = apparmor_capable,
+ .syslog = cap_syslog,
+
+- .netlink_send = cap_netlink_send,
+- .netlink_recv = cap_netlink_recv,
+-
+ .bprm_apply_creds = cap_bprm_apply_creds,
+ .bprm_set_security = apparmor_bprm_set_security,
+ .bprm_secureexec = apparmor_bprm_secureexec,
+@@ -820,6 +929,20 @@ struct security_operations apparmor_ops
+
+ .getprocattr = apparmor_getprocattr,
+ .setprocattr = apparmor_setprocattr,
++
++ .socket_create = apparmor_socket_create,
++ .socket_post_create = apparmor_socket_post_create,
++ .socket_bind = apparmor_socket_bind,
++ .socket_connect = apparmor_socket_connect,
++ .socket_listen = apparmor_socket_listen,
++ .socket_accept = apparmor_socket_accept,
++ .socket_sendmsg = apparmor_socket_sendmsg,
++ .socket_recvmsg = apparmor_socket_recvmsg,
++ .socket_getsockname = apparmor_socket_getsockname,
++ .socket_getpeername = apparmor_socket_getpeername,
++ .socket_getsockopt = apparmor_socket_getsockopt,
++ .socket_setsockopt = apparmor_socket_setsockopt,
++ .socket_shutdown = apparmor_socket_shutdown,
+ };
+
+ void info_message(const char *str)
+--- a/security/apparmor/main.c
++++ b/security/apparmor/main.c
+@@ -14,6 +14,9 @@
+ #include <linux/audit.h>
+ #include <linux/mount.h>
+ #include <linux/ptrace.h>
++#include <linux/socket.h>
++#include <linux/net.h>
++#include <net/sock.h>
+
+ #include "apparmor.h"
+
+@@ -116,6 +119,24 @@ static void aa_audit_file_mask(struct au
+ audit_log_format(ab, " %s=\"%s::%s\"", name, user, other);
+ }
+
++static const char *address_families[] = {
++#include "af_names.h"
++};
++
++static const char *sock_types[] = {
++ "unknown(0)",
++ "stream",
++ "dgram",
++ "raw",
++ "rdm",
++ "seqpacket",
++ "dccp",
++ "unknown(7)",
++ "unknown(8)",
++ "unknown(9)",
++ "packet",
++};
++
+ /**
+ * aa_audit - Log an audit event to the audit subsystem
+ * @profile: profile to check against
+@@ -187,7 +208,25 @@ static int aa_audit_base(struct aa_profi
+ audit_log_untrustedstring(ab, sa->name2);
+ }
+
+- audit_log_format(ab, " pid=%d", current->pid);
++ if (sa->family || sa->type) {
++ if (address_families[sa->family])
++ audit_log_format(ab, " family=\"%s\"",
++ address_families[sa->family]);
++ else
++ audit_log_format(ab, " family=\"unknown(%d)\"",
++ sa->family);
++
++ if (sock_types[sa->type])
++ audit_log_format(ab, " sock_type=\"%s\"",
++ sock_types[sa->type]);
++ else
++ audit_log_format(ab, " sock_type=\"unknown(%d)\"",
++ sa->type);
++
++ audit_log_format(ab, " protocol=%d", sa->protocol);
++ }
++
++ audit_log_format(ab, " pid=%d", current->pid);
+
+ if (profile) {
+ audit_log_format(ab, " profile=");
+@@ -767,6 +806,72 @@ int aa_link(struct aa_profile *profile,
+
+ return error;
+ }
++
++int aa_net_perm(struct aa_profile *profile, char *operation,
++ int family, int type, int protocol)
++{
++ struct aa_audit sa;
++ int error = 0;
++ u16 family_mask, audit_mask, quiet_mask;
++
++ if ((family < 0) || (family >= AF_MAX))
++ return -EINVAL;
++
++ if ((type < 0) || (type >= SOCK_MAX))
++ return -EINVAL;
++
++ /* unix domain and netlink sockets are handled by ipc */
++ if (family == AF_UNIX || family == AF_NETLINK)
++ return 0;
++
++ family_mask = profile->network_families[family];
++ audit_mask = profile->audit_network[family];
++ quiet_mask = profile->quiet_network[family];
++
++ error = (family_mask & (1 << type)) ? 0 : -EACCES;
++
++ memset(&sa, 0, sizeof(sa));
++ sa.operation = operation;
++ sa.gfp_mask = GFP_KERNEL;
++ sa.family = family;
++ sa.type = type;
++ sa.protocol = protocol;
++ sa.error_code = error;
++
++ if (likely(!error)) {
++ if (!PROFILE_AUDIT(profile) && !(family_mask & audit_mask))
++ return 0;
++ } else if (!((1 << type) & ~quiet_mask)) {
++ return error;
++ }
++
++ error = aa_audit(profile, &sa);
++
++ return error;
++}
++
++int aa_revalidate_sk(struct sock *sk, char *operation)
++{
++ struct aa_profile *profile;
++ int error = 0;
++
++ /* this is some debugging code to flush out the network hooks that
++ that are called in interrupt context */
++ if (in_interrupt()) {
++ printk("AppArmor Debug: Hook being called from interrupt context\n");
++ dump_stack();
++ return 0;
++ }
++
++ profile = aa_get_profile(current);
++ if (profile)
++ error = aa_net_perm(profile, operation,
++ sk->sk_family, sk->sk_type,
++ sk->sk_protocol);
++ aa_put_profile(profile);
++
++ return error;
++}
+
+ /*******************************
+ * Global task related functions
+--- a/security/apparmor/module_interface.c
++++ b/security/apparmor/module_interface.c
+@@ -321,8 +321,8 @@ static struct aa_profile *aa_unpack_prof
+ struct aa_audit *sa)
+ {
+ struct aa_profile *profile = NULL;
+-
+- int error = -EPROTO;
++ size_t size = 0;
++ int i, error = -EPROTO;
+
+ profile = alloc_aa_profile();
+ if (!profile)
+@@ -355,6 +355,28 @@ static struct aa_profile *aa_unpack_prof
+ if (!aa_is_u32(e, &(profile->set_caps), NULL))
+ goto fail;
+
++ size = aa_is_array(e, "net_allowed_af");
++ if (size) {
++ if (size > AF_MAX)
++ goto fail;
++
++ for (i = 0; i < size; i++) {
++ if (!aa_is_u16(e, &profile->network_families[i], NULL))
++ goto fail;
++ if (!aa_is_u16(e, &profile->audit_network[i], NULL))
++ goto fail;
++ if (!aa_is_u16(e, &profile->quiet_network[i], NULL))
++ goto fail;
++ }
++ if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
++ goto fail;
++ /* allow unix domain and netlink sockets they are handled
++ * by IPC
++ */
++ }
++ profile->network_families[AF_UNIX] = 0xffff;
++ profile->network_families[AF_NETLINK] = 0xffff;
++
+ /* get file rules */
+ profile->file_rules = aa_unpack_dfa(e);
+ if (IS_ERR(profile->file_rules)) {