Signed-off-by: John Johansen <john.johan...@canonical.com> --- security/apparmor/.gitignore | 1 + security/apparmor/Makefile | 42 +++- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/audit.h | 4 + security/apparmor/include/file.h | 2 + security/apparmor/include/net.h | 54 ++++++ security/apparmor/include/policy.h | 3 + security/apparmor/lsm.c | 381 +++++++++++++++++++++++++++++++++++++ security/apparmor/net.c | 169 ++++++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 46 +++++ 11 files changed, 702 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 c6fc5e4..efe7815 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 label.o + resource.o sid.o file.o label.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 d97c1bd..0fe98c4 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -766,6 +766,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("policy", aa_fs_entry_policy), AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 4bd6b8a..6c63573 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -125,6 +125,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/file.h b/security/apparmor/include/file.h index 0b0c1ab..836f892 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -22,6 +22,8 @@ struct aa_profile; struct path; +#define INODE_CXT(X) (X)->i_security + /* * We use MAY_EXEC, MAY_WRITE, MAY_READ, MAY_APPEND and the following flags * for profile permissions diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 0000000..377c750 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,54 @@ +/* + * 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 <net/sock.h> + +#include "apparmorfs.h" +#include "label.h" + +struct aa_sk_cxt { + struct aa_label *label; + struct aa_label *peer; +}; + +#define SK_CXT(X) (X)->sk_security +#define SOCK_CXT(X) SOCK_INODE(X)->i_security + +/* 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_label *label, 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 ea585c4..e6d81d3 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -28,6 +28,7 @@ #include "domain.h" #include "file.h" #include "label.h" +#include "net.h" #include "resource.h" extern const char *aa_hidden_ns_name; @@ -155,6 +156,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 * * @dents: dentries for the profiles file entries in apparmorfs @@ -195,6 +197,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; char *dirname; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index eb7bf2a..0cdd341 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" @@ -189,6 +190,16 @@ static int common_perm(int op, struct path *path, u32 mask, return error; } +static void apparmor_inode_free_security(struct inode *inode) +{ + struct aa_label *cxt = INODE_CXT(inode); + + if (cxt) { + INODE_CXT(inode) = NULL; + aa_put_label(cxt); + } +} + /** * common_perm_dir_dentry - common permission wrapper when path is dir, dentry * @op: operation being checked @@ -633,6 +644,351 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } +/** + * apparmor_sk_alloc_security - allocate and attach the sk_security field +??? local stream only ???? + */ +static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags) +{ + struct aa_sk_cxt *cxt; + + cxt = kzalloc(sizeof(*cxt), flags); + if (!cxt) + return -ENOMEM; + + SK_CXT(sk) = cxt; + + return 0; +} + +/** + * apparmor_sk_free_security - free the sk_security field + */ +static void apparmor_sk_free_security(struct sock *sk) +{ + struct aa_sk_cxt *cxt = SK_CXT(sk); + + SK_CXT(sk) = NULL; + aa_put_label(cxt->label); + aa_put_label(cxt->peer); + kfree(cxt); +} + +/** + * apparmor_clone_security - clone the sk_security field + */ +static void apparmor_sk_clone_security(const struct sock *sk, + struct sock *newsk) +{ +// ??? selinux + struct aa_sk_cxt *cxt = SK_CXT(sk); + struct aa_sk_cxt *new = SK_CXT(newsk); + + new->label = aa_get_label(cxt->label); + new->peer = aa_get_label(cxt->peer); +} + +#include <net/af_unix.h> +#define print_sk(SK) \ +do { \ + if ((SK)->sk_family == PF_UNIX) { \ + struct unix_sock *u = unix_sk(SK); \ + int len, addr_len; \ + char *buf; \ + if (!u->addr) { \ + addr_len = sizeof(sa_family_t); \ + } else { \ + addr_len = u->addr->len; \ + buf = (char *) &u->addr->name->sun_path; \ + } \ + len = addr_len - sizeof(sa_family_t); \ + printk("%s: %s: f %d, t %d, p %d", __FUNCTION__, \ + #SK , \ + (SK)->sk_family, (SK)->sk_type, (SK)->sk_protocol); \ + if (len <= 0) \ + printk(" <unamed>"); \ + else if (buf[0]) \ + printk(" %s", buf); \ + else \ + printk(" altns: %d %.*s", len, len, buf+1); \ + printk("\n"); \ + } else { \ + printk("%s: %s: family %d\n", __FUNCTION__, #SK , (SK)->sk_family); \ + } \ +} while (0) + +// sk->sk_socket is NULL when orphaned/being shutdown +// socket->sk set on graft, and sock_init_data if (socket exists) + +/** + * apparmor_unix_stream_connect - check perms before making unix domain conn + * + * only used for alt unix socket namespace ??? + */ +static int apparmor_unix_stream_connect(struct sock *sock, struct sock *other, + struct sock *newsk) +{ + struct aa_sk_cxt *sock_cxt = SK_CXT(sock); + struct aa_sk_cxt *other_cxt = SK_CXT(other); + struct aa_sk_cxt *new_cxt = SK_CXT(newsk); + + +#if 0 + if (!perms to connect sock to other) + + return error; +#endif + +// ??? label not updated after connection??? it would be good if the label +// was updated as the task labeling is updated + if (new_cxt->peer) { + //printk("%s: new_cxt->peer\n", __FUNCTION__); + aa_put_label(new_cxt->peer); + } + if (sock_cxt->peer) { + //printk("%s: sock_cxt->peer\n", __FUNCTION__); + aa_put_label(sock_cxt->peer); + } + + new_cxt->peer = aa_get_label(sock_cxt->label); + sock_cxt->peer = aa_get_label(other_cxt->label); + +// print_sk(sock); +// print_sk(other); +// print_sk(newsk); + + return 0; +} + +/** + * apparmor_unix_may_send - check perms before conn or sending unix dgrams + * + * Only used for alt unix socket namespace ???? + */ +static int apparmor_unix_may_send(struct socket *sock, struct socket *other) +{ + // ??? how do these play in with regular perm checks, conditional? + +// print_sk(sock->sk); +// print_sk(other->sk); + + return 0; +} + +/** + * apparmor_socket_create - check perms before create a new socket + */ +static int apparmor_socket_create(int family, int type, int protocol, int kern) +{ + struct aa_label *label; + + label = aa_current_label(); + if (kern || unconfined(label)) + return 0; + + return aa_net_perm(OP_CREATE, label, family, type, protocol, NULL); +} + +/** + * apparmor_socket_post_create - setup the per-socket security struct + * + * Note: socket likely does not have sk here + * ??? inode vs socket storage ??? + * sk labeling done in sock_graft + */ +static int apparmor_socket_post_create(struct socket *sock, int family, + int type, int protocol, int kern) +{ + if (!kern) { +/* set sock and sk label to NULL if kernel ????? */ + SOCK_CXT(sock) = aa_get_label(aa_current_label()); + + if (sock->sk) { + struct aa_sk_cxt *cxt = SK_CXT(sock->sk); + aa_put_label(cxt->label); + cxt->label = aa_get_label(aa_current_label()); + } + } + return 0; +} + +/** + * apparmor_socket_bind - check perms before bind addr to socket + */ +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); +} + +/** + * apparmor_socket_connect - check perms before connecting @sock to @address + */ +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); +} + +/** + * apparmor_socket_list - check perms before allowing listen + */ +static int apparmor_socket_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_LISTEN, sk); +} + +/** + * apparmor_socket_accept - check perms before accepting a new connection. + * + * Note: while @newsock is created and has some information, the accept + * has not been done. + */ +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_ACCEPT, sk); +} + +/** + * apparmor_socket_sendmsg - check perms before sending msg to another socket + */ +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); +} + +/** + * apparmor_socket_recvmsg - check perms before receiving a message + */ +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); +} + +/** + * apparmor_socket_getsockname - check perms before getting the local address + */ +static int apparmor_socket_getsockname(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKNAME, sk); +} + +/** + * apparmor_socket_getpeername - check perms before getting remote address + */ +static int apparmor_socket_getpeername(struct socket *sock) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETPEERNAME, sk); +} + +/** + * apparmor_getsockopt - check perms before getting socket options + */ +static int apparmor_socket_getsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_GETSOCKOPT, sk); +} + +/** + * apparmor_setsockopt - check perms before setting socket options + */ +static int apparmor_socket_setsockopt(struct socket *sock, int level, + int optname) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SETSOCKOPT, sk); +} + +/** + * apparmor_socket_shutdown - check perms before shutting down @sock conn + */ +static int apparmor_socket_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); +} + +/** + * apparmor_socket_getpeersec_stream - get security context of peer + * + * Note: for tcp only valid if using ipsec or cipso on lan + */ +static int apparmor_socket_getpeersec_stream(struct socket *sock, + char __user *optval, + int __user *optlen, unsigned len) +{ + char *name; + int slen, error = 0; + struct aa_sk_cxt *cxt = SK_CXT(sock->sk); + struct aa_label *label = aa_current_label(); + + if (!cxt->peer) + return -ENOPROTOOPT; + + slen = aa_label_asprint(&name, labels_ns(label), cxt->peer, true, + GFP_KERNEL); + /* don't include terminating \0 in slen, it breaks some apps */ + if (slen < 0) { + error = -ENOMEM; + goto out; + } else if (slen > len) { + error = -ERANGE; + goto out; + } + + if (copy_to_user(optval, name, slen)) + error = -EFAULT; + +out: + if (put_user(slen, optlen)) + error = -EFAULT; + kfree(name); + + return error; +} + +/** + * apparmor_sock_graft - set the sockets isec sid to the sock's sid ??? + * + * could set off of SOCK_CXT(parent) but need to track inode and we can + * just + * set sk security information off of current creating process label + */ +static void apparmor_sock_graft(struct sock *sk, struct socket *parent) +{ + struct aa_sk_cxt *cxt = SK_CXT(sk); + if (cxt->label) { + //printk("%s: cxt->label\n", __FUNCTION__); + aa_put_label(cxt->label); + } + + cxt->label = aa_get_label(__aa_current_label()); +} + + static struct security_operations apparmor_ops = { .name = "apparmor", @@ -641,6 +997,8 @@ static struct security_operations apparmor_ops = { .capget = apparmor_capget, .capable = apparmor_capable, + .inode_free_security = apparmor_inode_free_security, + .path_link = apparmor_path_link, .path_unlink = apparmor_path_unlink, .path_symlink = apparmor_path_symlink, @@ -665,6 +1023,29 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, + .sk_alloc_security = apparmor_sk_alloc_security, + .sk_free_security = apparmor_sk_free_security, + .sk_clone_security = apparmor_sk_clone_security, + + .unix_stream_connect = apparmor_unix_stream_connect, + .unix_may_send = apparmor_unix_may_send, + + .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, + .socket_getpeersec_stream = apparmor_socket_getpeersec_stream, + .sock_graft = apparmor_sock_graft, + .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..fc1b39a --- /dev/null +++ b/security/apparmor/net.c @@ -0,0 +1,169 @@ +/* + * 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/label.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); + + 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 + * @label: label 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_label *label, u16 family, int type, + int protocol, struct sock *sk) +{ + struct aa_profile *profile; + u16 family_mask; + int i, error = 0; + + 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; + + label_for_each_confined(i, label, profile) { + family_mask = profile->net.allow[family]; + error = (family_mask & (1 << type)) ? 0 : -EACCES; + error = audit_net(profile, op, family, type, protocol, sk, + error); + if (error) + break; + } + + return 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_label *label; + 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; + + label = __aa_current_label(); + if (!unconfined(label)) + error = aa_net_perm(op, label, 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 331bd02..3d8a17a 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -617,6 +617,7 @@ void aa_free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); + aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); kzfree(profile->dirname); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 13a14bc..67c1edf 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -194,6 +194,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)) { @@ -475,6 +488,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; @@ -573,6 +587,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.1.2 -- AppArmor mailing list AppArmor@lists.ubuntu.com Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor