]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
UBUNTU: SAUCE: (no-up) apparmor: sync of apparmor3.5-beta1 snapshot
authorJohn Johansen <john.johansen@canonical.com>
Fri, 18 Mar 2016 13:09:27 +0000 (06:09 -0700)
committerTim Gardner <tim.gardner@canonical.com>
Wed, 6 Apr 2016 09:28:07 +0000 (10:28 +0100)
BugLink: http://bugs.launchpad.net/bugs/1379535
This is a sync and squash of the apparmor 3.5-beta1 snapshot. The
set of patches in this squash are available in
  git://kernel.ubuntu.com/jj/ubuntu-xenial.git
using the the tag
  apparmor-3.5-beta1-presuash-snapshot

This fixes multiple bugs and adds the policy namespace stacking features.
BugLink: http://bugs.launchpad.net/bugs/1379535
Signed-off-by: John Johansen <john.johansen@canonical.com>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
46 files changed:
security/apparmor/.gitignore
security/apparmor/Kconfig
security/apparmor/Makefile
security/apparmor/af_unix.c [new file with mode: 0644]
security/apparmor/apparmorfs.c
security/apparmor/audit.c
security/apparmor/capability.c
security/apparmor/context.c
security/apparmor/domain.c
security/apparmor/file.c
security/apparmor/include/af_unix.h [new file with mode: 0644]
security/apparmor/include/apparmor.h
security/apparmor/include/apparmorfs.h
security/apparmor/include/audit.h
security/apparmor/include/capability.h
security/apparmor/include/context.h
security/apparmor/include/domain.h
security/apparmor/include/file.h
security/apparmor/include/ipc.h
security/apparmor/include/label.h [new file with mode: 0644]
security/apparmor/include/lib.h [new file with mode: 0644]
security/apparmor/include/match.h
security/apparmor/include/mount.h [new file with mode: 0644]
security/apparmor/include/net.h [new file with mode: 0644]
security/apparmor/include/path.h
security/apparmor/include/perms.h [new file with mode: 0644]
security/apparmor/include/policy.h
security/apparmor/include/policy_ns.h [new file with mode: 0644]
security/apparmor/include/policy_unpack.h
security/apparmor/include/procattr.h
security/apparmor/include/resource.h
security/apparmor/include/sig_names.h [new file with mode: 0644]
security/apparmor/ipc.c
security/apparmor/label.c [new file with mode: 0644]
security/apparmor/lib.c
security/apparmor/lsm.c
security/apparmor/match.c
security/apparmor/mount.c [new file with mode: 0644]
security/apparmor/net.c [new file with mode: 0644]
security/apparmor/nulldfa.in [new file with mode: 0644]
security/apparmor/path.c
security/apparmor/policy.c
security/apparmor/policy_ns.c [new file with mode: 0644]
security/apparmor/policy_unpack.c
security/apparmor/procattr.c
security/apparmor/resource.c

index 9cdec70d72b8ed83c38ec3a271b9495b53975b2e..d5b291e94264f9784438ea38e3242ad7697cb510 100644 (file)
@@ -1,5 +1,6 @@
 #
 # Generated include files
 #
+net_names.h
 capability_names.h
 rlim_names.h
index 232469baa94f2f17e686f2cf702a56424eda8176..4cdad1488994603f4872535fd92e1b1005314a52 100644 (file)
@@ -30,14 +30,62 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE
 
          If you are unsure how to answer this question, answer 1.
 
+config SECURITY_APPARMOR_STATS
+       bool "enable debug statistics"
+       depends on SECURITY_APPARMOR
+       select APPARMOR_LABEL_STATS
+       default n
+       help
+         This enables keeping statistics on various internal structures
+         and functions in apparmor.
+
+         If you are unsure how to answer this question, answer N.
+
+config SECURITY_APPARMOR_UNCONFINED_INIT
+       bool "Set init to unconfined on boot"
+       depends on SECURITY_APPARMOR
+       default y
+       help
+         This option determines policy behavior during early boot by
+         placing the init process in the unconfined state, or the
+         'default' profile.
+
+         This option determines policy behavior during early boot by
+         placing the init process in the unconfined state, or the
+         'default' profile.
+
+         'Y' means init and its children are not confined, unless the
+         init process is re-execed after a policy load; loaded policy
+         will only apply to processes started after the load.
+
+         'N' means init and its children are confined in a profile
+         named 'default', which can be replaced later and thus
+         provide for confinement for processes started early at boot,
+         though not confined during early boot.
+
+         If you are unsure how to answer this question, answer Y.
+
 config SECURITY_APPARMOR_HASH
-       bool "SHA1 hash of loaded profiles"
+       bool "enable introspection of sha1 hashes for loaded profiles"
        depends on SECURITY_APPARMOR
        select CRYPTO
        select CRYPTO_SHA1
        default y
 
        help
-         This option selects whether sha1 hashing is done against loaded
-          profiles and exported for inspection to user space via the apparmor
-          filesystem.
+         This option selects whether introspection of loaded policy
+         is available to userspace via the apparmor filesystem.
+
+config SECURITY_APPARMOR_HASH_DEFAULT
+       bool "Enable policy hash introspection by default"
+       depends on SECURITY_APPARMOR_HASH
+       default y
+
+       help
+         This option selects whether sha1 hashing of loaded policy
+         is enabled by default. The generation of sha1 hashes for
+         loaded policy provide system administrators a quick way
+         to verify that policy in the kernel matches what is expected,
+         however it can slow down policy load on some devices. In
+         these cases policy hashing can be disabled by default and
+         enabled only if needed.
index d693df87481837fa8d2fb95137d54f3b155cc280..3a2d39530137acf891aed8e883348df9397f76b3 100644 (file)
@@ -4,11 +4,45 @@ 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 label.o mount.o net.o af_unix.o \
+              policy_ns.o
 apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.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 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 "/AF_ROUTE/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 -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \
+        '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 capability names
 # Transforms lines from
@@ -61,6 +95,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
@@ -68,3 +103,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/af_unix.c b/security/apparmor/af_unix.c
new file mode 100644 (file)
index 0000000..8178498
--- /dev/null
@@ -0,0 +1,643 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor af_unix fine grained mediation
+ *
+ * Copyright 2014 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 <net/tcp_states.h>
+
+#include "include/af_unix.h"
+#include "include/apparmor.h"
+#include "include/context.h"
+#include "include/file.h"
+#include "include/label.h"
+#include "include/path.h"
+#include "include/policy.h"
+
+static inline struct sock *aa_sock(struct unix_sock *u)
+{
+       return &u->sk;
+}
+
+static inline int unix_fs_perm(const char *op, u32 mask, struct aa_label *label,
+                              struct unix_sock *u, int flags)
+{
+       AA_BUG(!label);
+       AA_BUG(!u);
+       AA_BUG(!UNIX_FS(aa_sock(u)));
+
+       if (unconfined(label) || !LABEL_MEDIATES(label, AA_CLASS_FILE))
+               return 0;
+
+       mask &= NET_FS_PERMS;
+       if (!u->path.dentry) {
+               struct path_cond cond = { };
+               struct aa_perms perms = { };
+               struct aa_profile *profile;
+
+               /* socket path has been cleared because it is being shutdown
+                * can only fall back to original sun_path request
+                */
+               struct aa_sk_ctx *ctx = SK_CTX(&u->sk);
+               if (ctx->path.dentry)
+                       return aa_path_perm(op, label, &ctx->path, flags, mask,
+                                           &cond);
+               return fn_for_each_confined(label, profile,
+                       ((flags | profile->path_flags) & PATH_MEDIATE_DELETED) ?
+                               __aa_path_perm(op, profile,
+                                              u->addr->name->sun_path, mask,
+                                              &cond, flags, &perms) :
+                               aa_audit_file(profile, &nullperms, op, mask,
+                                             u->addr->name->sun_path, NULL,
+                                             NULL, cond.uid,
+                                             "Failed name lookup - "
+                                             "deleted entry", -EACCES));
+       } else {
+               /* the sunpath may not be valid for this ns so use the path */
+               struct path_cond cond = { u->path.dentry->d_inode->i_uid,
+                                         u->path.dentry->d_inode->i_mode
+               };
+
+               return aa_path_perm(op, label, &u->path, flags, mask, &cond);
+       }
+
+       return 0;
+}
+
+/* passing in state returned by PROFILE_MEDIATES_AF */
+static unsigned int match_to_prot(struct aa_profile *profile,
+                                 unsigned int state, int type, int protocol,
+                                 const char **info)
+{
+       u16 buffer[2];
+       buffer[0] = cpu_to_be16(type);
+       buffer[1] = cpu_to_be16(protocol);
+       state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer,
+                                4);
+       if (!state)
+               *info = "failed type and protocol match";
+       return state;
+}
+
+static unsigned int match_addr(struct aa_profile *profile, unsigned int state,
+                              struct sockaddr_un *addr, int addrlen)
+{
+       if (addr)
+               /* include leading \0 */
+               state = aa_dfa_match_len(profile->policy.dfa, state,
+                                        addr->sun_path,
+                                        unix_addr_len(addrlen));
+       else
+               /* anonymous end point */
+               state = aa_dfa_match_len(profile->policy.dfa, state, "\x01",
+                                        1);
+       /* todo change to out of band */
+       state = aa_dfa_null_transition(profile->policy.dfa, state);
+       return state;
+}
+
+static unsigned int match_to_local(struct aa_profile *profile,
+                                  unsigned int state, int type, int protocol,
+                                  struct sockaddr_un *addr, int addrlen,
+                                  const char **info)
+{
+       state = match_to_prot(profile, state, type, protocol, info);
+       if (state) {
+               state = match_addr(profile, state, addr, addrlen);
+               if (state) {
+                       /* todo: local label matching */
+                       state = aa_dfa_null_transition(profile->policy.dfa,
+                                                      state);
+                       if (!state)
+                               *info = "failed local label match";
+               } else
+                       *info = "failed local address match";
+       }
+
+       return state;
+}
+
+static unsigned int match_to_sk(struct aa_profile *profile,
+                               unsigned int state, struct unix_sock *u,
+                               const char **info)
+{
+       struct sockaddr_un *addr = NULL;
+       int addrlen = 0;
+
+       if (u->addr) {
+               addr = u->addr->name;
+               addrlen = u->addr->len;
+       }
+
+       return match_to_local(profile, state, u->sk.sk_type, u->sk.sk_protocol,
+                             addr, addrlen, info);
+}
+
+#define CMD_ADDR       1
+#define CMD_LISTEN     2
+#define CMD_OPT                4
+
+static inline unsigned int match_to_cmd(struct aa_profile *profile,
+                                       unsigned int state, struct unix_sock *u,
+                                       char cmd, const char **info)
+{
+       state = match_to_sk(profile, state, u, info);
+       if (state) {
+               state = aa_dfa_match_len(profile->policy.dfa, state, &cmd, 1);
+               if (!state)
+                       *info = "failed cmd selection match";
+       }
+
+       return state;
+}
+
+static inline unsigned int match_to_peer(struct aa_profile *profile,
+                                        unsigned int state,
+                                        struct unix_sock *u,
+                                        struct sockaddr_un *peer_addr,
+                                        int peer_addrlen,
+                                        const char **info)
+{
+       state = match_to_cmd(profile, state, u, CMD_ADDR, info);
+       if (state) {
+               state = match_addr(profile, state, peer_addr, peer_addrlen);
+               if (!state)
+                       *info = "failed peer address match";
+       }
+       return state;
+}
+
+static int do_perms(struct aa_profile *profile, unsigned int state, u32 request,
+                   struct common_audit_data *sa)
+{
+       struct aa_perms perms;
+
+       AA_BUG(!profile);
+
+       aa_compute_perms(profile->policy.dfa, state, &perms);
+       aa_apply_modes_to_perms(profile, &perms);
+       return aa_check_perms(profile, &perms, request, sa,
+                             audit_net_cb);
+}
+
+static int match_label(struct aa_profile *profile, struct aa_profile *peer,
+                             unsigned int state, u32 request,
+                             struct common_audit_data *sa)
+{
+       AA_BUG(!profile);
+       AA_BUG(!peer);
+
+       aad(sa)->peer = &peer->label;
+
+       if (state) {
+               state = aa_dfa_match(profile->policy.dfa, state, aa_peer_name(peer));
+               if (!state)
+                       aad(sa)->info = "failed peer label match";
+       }
+       return do_perms(profile, state, request, sa);
+}
+
+
+/* unix sock creation comes before we know if the socket will be an fs
+ * socket
+ * v6 - semantics are handled by mapping in profile load
+ * v7 - semantics require sock create for tasks creating an fs socket.
+ */
+static int profile_create_perm(struct aa_profile *profile, int family,
+                              int type, int protocol)
+{
+       unsigned int state;
+       DEFINE_AUDIT_NET(sa, OP_CREATE, NULL, family, type, protocol);
+
+       AA_BUG(!profile);
+       AA_BUG(profile_unconfined(profile));
+
+       if ((state = PROFILE_MEDIATES_AF(profile, AF_UNIX))) {
+               state = match_to_prot(profile, state, type, protocol,
+                                     &aad(&sa)->info);
+               return do_perms(profile, state, AA_MAY_CREATE, &sa);
+       }
+
+       return aa_profile_af_perm(profile, &sa, AA_MAY_CREATE, family, type);
+}
+
+int aa_unix_create_perm(struct aa_label *label, int family, int type,
+                       int protocol)
+{
+       struct aa_profile *profile;
+
+       if (unconfined(label))
+               return 0;
+
+       return fn_for_each_confined(label, profile,
+                       profile_create_perm(profile, family, type, protocol));
+}
+
+
+static inline int profile_sk_perm(struct aa_profile *profile, const char *op,
+                                 u32 request, struct sock *sk)
+{
+       unsigned int state;
+       DEFINE_AUDIT_SK(sa, op, sk);
+
+       AA_BUG(!profile);
+       AA_BUG(!sk);
+       AA_BUG(UNIX_FS(sk));
+       AA_BUG(profile_unconfined(profile));
+
+       state = PROFILE_MEDIATES_AF(profile, AF_UNIX);
+       if (state) {
+               state = match_to_sk(profile, state, unix_sk(sk),
+                                   &aad(&sa)->info);
+               return do_perms(profile, state, request, &sa);
+       }
+
+       return aa_profile_af_sk_perm(profile, &sa, request, sk);
+}
+
+int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request,
+                         struct sock *sk)
+{
+       struct aa_profile *profile;
+
+       return fn_for_each_confined(label, profile,
+                       profile_sk_perm(profile, op, request, sk));
+}
+
+static int unix_label_sock_perm(struct aa_label *label, const char *op, u32 request,
+                               struct socket *sock)
+{
+       if (unconfined(label))
+               return 0;
+       if (UNIX_FS(sock->sk))
+               return unix_fs_perm(op, request, label, unix_sk(sock->sk), 0);
+
+       return aa_unix_label_sk_perm(label, op, request, sock->sk);
+}
+
+/* revaliation, get/set attr */
+int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock)
+{
+       struct aa_label *label = aa_begin_current_label(DO_UPDATE);
+       int error = unix_label_sock_perm(label, op, request, sock);
+       aa_end_current_label(label);
+
+       return error;
+}
+
+static int profile_bind_perm(struct aa_profile *profile, struct sock *sk,
+                            struct sockaddr *addr, int addrlen)
+{
+       unsigned int state;
+       DEFINE_AUDIT_SK(sa, OP_BIND, sk);
+
+       AA_BUG(!profile);
+       AA_BUG(!sk);
+       AA_BUG(addr->sa_family != AF_UNIX);
+       AA_BUG(profile_unconfined(profile));
+       AA_BUG(unix_addr_fs(addr, addrlen));
+
+       state = PROFILE_MEDIATES_AF(profile, AF_UNIX);
+       if (state) {
+               /* bind for abstract socket */
+               aad(&sa)->net.addr = unix_addr(addr);
+               aad(&sa)->net.addrlen = addrlen;
+
+               state = match_to_local(profile, state,
+                                      sk->sk_type, sk->sk_protocol,
+                                      unix_addr(addr), addrlen,
+                                      &aad(&sa)->info);
+               return do_perms(profile, state, AA_MAY_BIND, &sa);
+       }
+
+       return aa_profile_af_sk_perm(profile, &sa, AA_MAY_BIND, sk);
+}
+
+int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address,
+                     int addrlen)
+{
+       struct aa_profile *profile;
+       struct aa_label *label = aa_begin_current_label(DO_UPDATE);
+       int error = 0;
+
+        /* fs bind is handled by mknod */
+       if (!(unconfined(label) || unix_addr_fs(address, addrlen)))
+               error = fn_for_each_confined(label, profile,
+                               profile_bind_perm(profile, sock->sk, address,
+                                                 addrlen));
+       aa_end_current_label(label);
+
+       return error;
+}
+
+int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address,
+                        int addrlen)
+{
+       /* unix connections are covered by the
+        * - unix_stream_connect (stream) and unix_may_send hooks (dgram)
+        * - fs connect is handled by open
+        */
+       return 0;
+}
+
+static int profile_listen_perm(struct aa_profile *profile, struct sock *sk,
+                              int backlog)
+{
+       unsigned int state;
+       DEFINE_AUDIT_SK(sa, OP_LISTEN, sk);
+
+       AA_BUG(!profile);
+       AA_BUG(!sk);
+       AA_BUG(UNIX_FS(sk));
+       AA_BUG(profile_unconfined(profile));
+
+       state = PROFILE_MEDIATES_AF(profile, AF_UNIX);
+       if (state) {
+               u16 b = cpu_to_be16(backlog);
+
+               state = match_to_cmd(profile, state, unix_sk(sk), CMD_LISTEN,
+                                    &aad(&sa)->info);
+               if (state) {
+                       state = aa_dfa_match_len(profile->policy.dfa, state,
+                                                (char *) &b, 2);
+                       if (!state)
+                               aad(&sa)->info = "failed listen backlog match";
+               }
+               return do_perms(profile, state, AA_MAY_LISTEN, &sa);
+       }
+
+       return aa_profile_af_sk_perm(profile, &sa, AA_MAY_LISTEN, sk);
+}
+
+int aa_unix_listen_perm(struct socket *sock, int backlog)
+{
+       struct aa_profile *profile;
+       struct aa_label *label = aa_begin_current_label(DO_UPDATE);
+       int error = 0;
+
+       if (!(unconfined(label) || UNIX_FS(sock->sk)))
+               error = fn_for_each_confined(label, profile,
+                               profile_listen_perm(profile, sock->sk,
+                                                   backlog));
+       aa_end_current_label(label);
+
+       return error;
+}
+
+
+static inline int profile_accept_perm(struct aa_profile *profile,
+                                     struct sock *sk,
+                                     struct sock *newsk)
+{
+       unsigned int state;
+       DEFINE_AUDIT_SK(sa, OP_ACCEPT, sk);
+
+       AA_BUG(!profile);
+       AA_BUG(!sk);
+       AA_BUG(UNIX_FS(sk));
+       AA_BUG(profile_unconfined(profile));
+
+       state = PROFILE_MEDIATES_AF(profile, AF_UNIX);
+       if (state) {
+               state = match_to_sk(profile, state, unix_sk(sk),
+                                   &aad(&sa)->info);
+               return do_perms(profile, state, AA_MAY_ACCEPT, &sa);
+       }
+
+       return aa_profile_af_sk_perm(profile, &sa, AA_MAY_ACCEPT, sk);
+}
+
+/* ability of sock to connect, not peer address binding */
+int aa_unix_accept_perm(struct socket *sock, struct socket *newsock)
+{
+       struct aa_profile *profile;
+       struct aa_label *label = aa_begin_current_label(DO_UPDATE);
+       int error = 0;
+
+       if (!(unconfined(label) || UNIX_FS(sock->sk)))
+               error = fn_for_each_confined(label, profile,
+                               profile_accept_perm(profile, sock->sk,
+                                                   newsock->sk));
+       aa_end_current_label(label);
+
+       return error;
+}
+
+
+/* dgram handled by unix_may_sendmsg, right to send on stream done at connect
+ * could do per msg unix_stream here
+ */
+/* sendmsg, recvmsg */
+int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock,
+                    struct msghdr *msg, int size)
+{
+       return 0;
+}
+
+
+static int profile_opt_perm(struct aa_profile *profile, const char *op, u32 request,
+                           struct sock *sk, int level, int optname)
+{
+       unsigned int state;
+       DEFINE_AUDIT_SK(sa, op, sk);
+
+       AA_BUG(!profile);
+       AA_BUG(!sk);
+       AA_BUG(UNIX_FS(sk));
+       AA_BUG(profile_unconfined(profile));
+
+       state = PROFILE_MEDIATES_AF(profile, AF_UNIX);
+       if (state) {
+               u16 b = cpu_to_be16(optname);
+
+               state = match_to_cmd(profile, state, unix_sk(sk), CMD_OPT,
+                                    &aad(&sa)->info);
+               if (state) {
+                       state = aa_dfa_match_len(profile->policy.dfa, state,
+                                                (char *) &b, 2);
+                       if (!state)
+                               aad(&sa)->info = "failed sockopt match";
+               }
+               return do_perms(profile, state, request, &sa);
+       }
+
+       return aa_profile_af_sk_perm(profile, &sa, request, sk);
+}
+
+int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level,
+                    int optname)
+{
+       struct aa_profile *profile;
+       struct aa_label *label = aa_begin_current_label(DO_UPDATE);
+       int error = 0;
+
+       if (!(unconfined(label) || UNIX_FS(sock->sk)))
+               error = fn_for_each_confined(label, profile,
+                               profile_opt_perm(profile, op, request,
+                                                sock->sk, level, optname));
+       aa_end_current_label(label);
+
+       return error;
+}
+
+/* null peer_label is allowed, in which case the peer_sk label is used */
+static int profile_peer_perm(struct aa_profile *profile, const char *op, u32 request,
+                            struct sock *sk, struct sock *peer_sk,
+                            struct aa_label *peer_label,
+                            struct common_audit_data *sa)
+{
+       unsigned int state;
+
+       AA_BUG(!profile);
+       AA_BUG(profile_unconfined(profile));
+       AA_BUG(!sk);
+       AA_BUG(!peer_sk);
+       AA_BUG(UNIX_FS(peer_sk));
+
+       state = PROFILE_MEDIATES_AF(profile, AF_UNIX);
+       if (state) {
+               struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk);
+               struct aa_profile *peerp;
+               struct sockaddr_un *addr = NULL;
+               int len = 0;
+               if (unix_sk(peer_sk)->addr) {
+                       addr = unix_sk(peer_sk)->addr->name;
+                       len = unix_sk(peer_sk)->addr->len;
+               }
+               state = match_to_peer(profile, state, unix_sk(sk),
+                                     addr, len, &aad(sa)->info);
+               if (!peer_label)
+                       peer_label = peer_ctx->label;
+               return fn_for_each(peer_label, peerp,
+                                  match_label(profile, peerp, state, request,
+                                              sa));
+       }
+
+       return aa_profile_af_sk_perm(profile, sa, request, sk);
+}
+
+/**
+ *
+ * Requires: lock held on both @sk and @peer_sk
+ */
+int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request,
+                     struct sock *sk, struct sock *peer_sk,
+                     struct aa_label *peer_label)
+{
+       struct unix_sock *peeru = unix_sk(peer_sk);
+       struct unix_sock *u = unix_sk(sk);
+
+       AA_BUG(!label);
+       AA_BUG(!sk);
+       AA_BUG(!peer_sk);
+
+       if (UNIX_FS(aa_sock(peeru)))
+               return unix_fs_perm(op, request, label, peeru, 0);
+       else if (UNIX_FS(aa_sock(u)))
+               return unix_fs_perm(op, request, label, u, 0);
+       else {
+               struct aa_profile *profile;
+               DEFINE_AUDIT_SK(sa, op, sk);
+               aad(&sa)->net.peer_sk = peer_sk;
+
+               /* TODO: ns!!! */
+               if (!net_eq(sock_net(sk), sock_net(peer_sk))) {
+                       ;
+               }
+
+               if (unconfined(label))
+                       return 0;
+
+               return fn_for_each_confined(label, profile,
+                               profile_peer_perm(profile, op, request, sk,
+                                                 peer_sk, peer_label, &sa));
+       }
+}
+
+
+/* from net/unix/af_unix.c */
+static void unix_state_double_lock(struct sock *sk1, struct sock *sk2)
+{
+       if (unlikely(sk1 == sk2) || !sk2) {
+               unix_state_lock(sk1);
+               return;
+       }
+       if (sk1 < sk2) {
+               unix_state_lock(sk1);
+               unix_state_lock_nested(sk2);
+       } else {
+               unix_state_lock(sk2);
+               unix_state_lock_nested(sk1);
+       }
+}
+
+static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2)
+{
+       if (unlikely(sk1 == sk2) || !sk2) {
+               unix_state_unlock(sk1);
+               return;
+       }
+       unix_state_unlock(sk1);
+       unix_state_unlock(sk2);
+}
+
+int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request,
+                     struct socket *sock)
+{
+       struct sock *peer_sk = NULL;
+       u32 sk_req = request & ~NET_PEER_MASK;
+       int error = 0;
+
+       AA_BUG(!label);
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       AA_BUG(sock->sk->sk_family != AF_UNIX);
+
+       /* TODO: update sock label with new task label */
+       unix_state_lock(sock->sk);
+       peer_sk = unix_peer(sock->sk);
+       if (peer_sk)
+               sock_hold(peer_sk);
+       if (!unix_connected(sock) && sk_req) {
+               error = unix_label_sock_perm(label, op, sk_req, sock);
+               if (!error) {
+                       // update label
+               }
+       }
+       unix_state_unlock(sock->sk);
+       if (!peer_sk)
+               return error;
+
+       unix_state_double_lock(sock->sk, peer_sk);
+       if (UNIX_FS(sock->sk)) {
+               error = unix_fs_perm(op, request, label, unix_sk(sock->sk),
+                                    PATH_SOCK_COND);
+       } else if (UNIX_FS(peer_sk)) {
+               error = unix_fs_perm(op, request, label, unix_sk(peer_sk),
+                                    PATH_SOCK_COND);
+       } else {
+               struct aa_sk_ctx *pctx = SK_CTX(peer_sk);
+               if (sk_req)
+                       error = aa_unix_label_sk_perm(label, op, sk_req,
+                                                     sock->sk);
+               last_error(error,
+                       xcheck(aa_unix_peer_perm(label, op,
+                                                MAY_READ | MAY_WRITE,
+                                                sock->sk, peer_sk, NULL),
+                              aa_unix_peer_perm(pctx->label, op,
+                                                MAY_READ | MAY_WRITE,
+                                                peer_sk, sock->sk, label)));
+       }
+
+       unix_state_double_unlock(sock->sk, peer_sk);
+       sock_put(peer_sk);
+
+       return error;
+}
index ad4fa49ad1db23a70dfa16751a9c7f8ec006e854..c3309329a8794b7930cd35027b0780578e3d4256 100644 (file)
 #include <linux/module.h>
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
+#include <linux/mount.h>
 #include <linux/namei.h>
 #include <linux/capability.h>
 #include <linux/rcupdate.h>
+#include <uapi/linux/major.h>
 
 #include "include/apparmor.h"
 #include "include/apparmorfs.h"
 #include "include/audit.h"
 #include "include/context.h"
 #include "include/crypto.h"
+#include "include/ipc.h"
+#include "include/policy_ns.h"
+#include "include/label.h"
 #include "include/policy.h"
 #include "include/resource.h"
+#include "include/lib.h"
 
 /**
  * aa_mangle_name - mangle a profile name to std profile layout form
@@ -37,7 +43,7 @@
  *
  * Returns: length of mangled name
  */
-static int mangle_name(char *name, char *target)
+static int mangle_name(const char *name, char *target)
 {
        char *t = target;
 
@@ -71,7 +77,6 @@ static int mangle_name(char *name, char *target)
 
 /**
  * aa_simple_write_to_buffer - common routine for getting policy from user
- * @op: operation doing the user buffer copy
  * @userbuf: user buffer to copy data from  (NOT NULL)
  * @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size)
  * @copy_size: size of data to copy from user buffer
@@ -80,7 +85,7 @@ static int mangle_name(char *name, char *target)
  * Returns: kernel buffer containing copy of user buffer data or an
  *          ERR_PTR on failure.
  */
-static char *aa_simple_write_to_buffer(int op, const char __user *userbuf,
+static char *aa_simple_write_to_buffer(const char __user *userbuf,
                                       size_t alloc_size, size_t copy_size,
                                       loff_t *pos)
 {
@@ -92,13 +97,6 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf,
                /* only writes from pos 0, that is complete writes */
                return ERR_PTR(-ESPIPE);
 
-       /*
-        * Don't allow profile load/replace/remove from profiles that don't
-        * have CAP_MAC_ADMIN
-        */
-       if (!aa_may_manage_policy(op))
-               return ERR_PTR(-EACCES);
-
        /* freed by caller to simple_write_to_buffer */
        data = kvmalloc(alloc_size);
        if (data == NULL)
@@ -112,25 +110,40 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf,
        return data;
 }
 
-
-/* .load file hook fn to load policy */
-static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
-                           loff_t *pos)
+static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
+                        loff_t *pos)
 {
-       char *data;
+       struct aa_label *label;
        ssize_t error;
+       char *data;
 
-       data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos);
+       label = aa_begin_current_label(DO_UPDATE);
 
+       /* high level check about policy management - fine grained in
+        * below after unpack
+        */
+       error = aa_may_manage_policy(label, mask);
+       if (error)
+               return error;
+
+       data = aa_simple_write_to_buffer(buf, size, size, pos);
        error = PTR_ERR(data);
        if (!IS_ERR(data)) {
-               error = aa_replace_profiles(data, size, PROF_ADD);
+               error = aa_replace_profiles(label, mask, data, size);
                kvfree(data);
        }
+       aa_end_current_label(label);
 
        return error;
 }
 
+/* .load file hook fn to load policy */
+static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
+                           loff_t *pos)
+{
+       return policy_update(AA_MAY_LOAD_POLICY, buf, size, pos);
+}
+
 static const struct file_operations aa_fs_profile_load = {
        .write = profile_load,
        .llseek = default_llseek,
@@ -140,17 +153,8 @@ static const struct file_operations aa_fs_profile_load = {
 static ssize_t profile_replace(struct file *f, const char __user *buf,
                               size_t size, loff_t *pos)
 {
-       char *data;
-       ssize_t error;
-
-       data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos);
-       error = PTR_ERR(data);
-       if (!IS_ERR(data)) {
-               error = aa_replace_profiles(data, size, PROF_REPLACE);
-               kvfree(data);
-       }
-
-       return error;
+       return policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY,
+                            buf, size, pos);
 }
 
 static const struct file_operations aa_fs_profile_replace = {
@@ -162,21 +166,31 @@ static const struct file_operations aa_fs_profile_replace = {
 static ssize_t profile_remove(struct file *f, const char __user *buf,
                              size_t size, loff_t *pos)
 {
-       char *data;
+       struct aa_label *label;
        ssize_t error;
+       char *data;
+
+       label = aa_begin_current_label(DO_UPDATE);
+       /* high level check about policy management - fine grained in
+        * below after unpack
+        */
+       error = aa_may_manage_policy(label, AA_MAY_REMOVE_POLICY);
+       if (error)
+               return error;
 
        /*
         * aa_remove_profile needs a null terminated string so 1 extra
         * byte is allocated and the copied data is null terminated.
         */
-       data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos);
+       data = aa_simple_write_to_buffer(buf, size + 1, size, pos);
 
        error = PTR_ERR(data);
        if (!IS_ERR(data)) {
                data[size] = 0;
-               error = aa_remove_profiles(data, size);
+               error = aa_remove_profiles(label, data, size);
                kvfree(data);
        }
+       aa_end_current_label(label);
 
        return error;
 }
@@ -186,6 +200,176 @@ static const struct file_operations aa_fs_profile_remove = {
        .llseek = default_llseek,
 };
 
+
+static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
+                            const char *match_str, size_t match_len)
+{
+       struct aa_perms tmp;
+       struct aa_dfa *dfa;
+       unsigned int state = 0;
+
+       if (profile_unconfined(profile))
+               return;
+       if (profile->file.dfa && *match_str == AA_CLASS_FILE) {
+               dfa = profile->file.dfa;
+               state = aa_dfa_match_len(dfa, profile->file.start,
+                                        match_str + 1, match_len - 1);
+               tmp = nullperms;
+               if (state) {
+                       struct path_cond cond = { };
+                       tmp = aa_compute_fperms(dfa, state, &cond);
+               }
+       } else if (profile->policy.dfa) {
+               if (!PROFILE_MEDIATES_SAFE(profile, *match_str))
+                       return; /* no change to current perms */
+               dfa = profile->policy.dfa;
+               state = aa_dfa_match_len(dfa, profile->policy.start[0],
+                                        match_str, match_len);
+               if (state)
+                       aa_compute_perms(dfa, state, &tmp);
+               else
+                       tmp = nullperms;
+       }
+       aa_apply_modes_to_perms(profile, &tmp);
+       aa_perms_accum_raw(perms, &tmp);
+}
+
+/**
+ * query_label - queries a label and writes permissions to buf
+ * @buf: the resulting permissions string is stored here (NOT NULL)
+ * @buf_len: size of buf
+ * @query: binary query string to match against the dfa
+ * @query_len: size of query
+ *
+ * The buffers pointed to by buf and query may overlap. The query buffer is
+ * parsed before buf is written to.
+ *
+ * The query should look like "LABEL_NAME\0DFA_STRING" where LABEL_NAME is
+ * the name of the label, in the current namespace, that is to be queried and
+ * DFA_STRING is a binary string to match against the label(s)'s DFA.
+ *
+ * LABEL_NAME must be NUL terminated. DFA_STRING may contain NUL characters
+ * but must *not* be NUL terminated.
+ *
+ * Returns: number of characters written to buf or -errno on failure
+ */
+static ssize_t query_label(char *buf, size_t buf_len,
+                          char *query, size_t query_len, bool ns_only)
+{
+       struct aa_profile *profile;
+       struct aa_label *label, *curr;
+       char *label_name, *match_str;
+       size_t label_name_len, match_len;
+       struct aa_perms perms;
+       struct label_it i;
+
+       if (!query_len)
+               return -EINVAL;
+
+       label_name = query;
+       label_name_len = strnlen(query, query_len);
+       if (!label_name_len || label_name_len == query_len)
+               return -EINVAL;
+
+       /**
+        * The extra byte is to account for the null byte between the
+        * profile name and dfa string. profile_name_len is greater
+        * than zero and less than query_len, so a byte can be safely
+        * added or subtracted.
+        */
+       match_str = label_name + label_name_len + 1;
+       match_len = query_len - label_name_len - 1;
+
+       curr = aa_begin_current_label(DO_UPDATE);
+       label = aa_label_parse(curr, label_name, GFP_KERNEL, false, false);
+       aa_end_current_label(curr);
+       if (IS_ERR(label))
+               return PTR_ERR(label);
+
+       perms = allperms;
+       if (ns_only) {
+               label_for_each_in_ns(i, labels_ns(label), label, profile) {
+                       profile_query_cb(profile, &perms, match_str, match_len);
+               }
+       } else {
+               label_for_each(i, label, profile) {
+                       profile_query_cb(profile, &perms, match_str, match_len);
+               }
+       }
+       aa_put_label(label);
+
+       return scnprintf(buf, buf_len,
+                     "allow 0x%08x\ndeny 0x%08x\naudit 0x%08x\nquiet 0x%08x\n",
+                     perms.allow, perms.deny, perms.audit, perms.quiet);
+}
+
+#define QUERY_CMD_LABEL                "label\0"
+#define QUERY_CMD_LABEL_LEN    6
+#define QUERY_CMD_PROFILE      "profile\0"
+#define QUERY_CMD_PROFILE_LEN  8
+#define QUERY_CMD_LABELALL     "labelall\0"
+#define QUERY_CMD_LABELALL_LEN 9
+
+/**
+ * aa_write_access - generic permissions query
+ * @file: pointer to open apparmorfs/access file
+ * @ubuf: user buffer containing the complete query string (NOT NULL)
+ * @count: size of ubuf
+ * @ppos: position in the file (MUST BE ZERO)
+ *
+ * Allows for one permission query per open(), write(), and read() sequence.
+ * The only query currently supported is a label-based query. For this query
+ * ubuf must begin with "label\0", followed by the profile query specific
+ * format described in the query_label() function documentation.
+ *
+ * Returns: number of bytes written or -errno on failure
+ */
+static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
+                              size_t count, loff_t *ppos)
+{
+       char *buf;
+       ssize_t len;
+
+       if (*ppos)
+               return -ESPIPE;
+
+       buf = simple_transaction_get(file, ubuf, count);
+       if (IS_ERR(buf))
+               return PTR_ERR(buf);
+
+       if (count > QUERY_CMD_PROFILE_LEN &&
+           !memcmp(buf, QUERY_CMD_PROFILE, QUERY_CMD_PROFILE_LEN)) {
+               len = query_label(buf, SIMPLE_TRANSACTION_LIMIT,
+                                 buf + QUERY_CMD_PROFILE_LEN,
+                                 count - QUERY_CMD_PROFILE_LEN, true);
+       } else if (count > QUERY_CMD_LABEL_LEN &&
+                  !memcmp(buf, QUERY_CMD_LABEL, QUERY_CMD_LABEL_LEN)) {
+               len = query_label(buf, SIMPLE_TRANSACTION_LIMIT,
+                                 buf + QUERY_CMD_LABEL_LEN,
+                                 count - QUERY_CMD_LABEL_LEN, true);
+       } else if (count > QUERY_CMD_LABELALL_LEN &&
+                  !memcmp(buf, QUERY_CMD_LABELALL, QUERY_CMD_LABELALL_LEN)) {
+               len = query_label(buf, SIMPLE_TRANSACTION_LIMIT,
+                                 buf + QUERY_CMD_LABELALL_LEN,
+                                 count - QUERY_CMD_LABELALL_LEN, false);
+       } else
+               len = -EINVAL;
+
+       if (len < 0)
+               return len;
+
+       simple_transaction_set(file, len);
+
+       return count;
+}
+
+static const struct file_operations aa_fs_access = {
+       .write          = aa_write_access,
+       .read           = simple_transaction_read,
+       .release        = simple_transaction_release,
+       .llseek         = generic_file_llseek,
+};
+
 static int aa_fs_seq_show(struct seq_file *seq, void *v)
 {
        struct aa_fs_entry *fs_file = seq->private;
@@ -227,12 +411,12 @@ const struct file_operations aa_fs_seq_file_ops = {
 static int aa_fs_seq_profile_open(struct inode *inode, struct file *file,
                                  int (*show)(struct seq_file *, void *))
 {
-       struct aa_replacedby *r = aa_get_replacedby(inode->i_private);
-       int error = single_open(file, show, r);
+       struct aa_proxy *proxy = aa_get_proxy(inode->i_private);
+       int error = single_open(file, show, proxy);
 
        if (error) {
                file->private_data = NULL;
-               aa_put_replacedby(r);
+               aa_put_proxy(proxy);
        }
 
        return error;
@@ -242,16 +426,17 @@ static int aa_fs_seq_profile_release(struct inode *inode, struct file *file)
 {
        struct seq_file *seq = (struct seq_file *) file->private_data;
        if (seq)
-               aa_put_replacedby(seq->private);
+               aa_put_proxy(seq->private);
        return single_release(inode, file);
 }
 
 static int aa_fs_seq_profname_show(struct seq_file *seq, void *v)
 {
-       struct aa_replacedby *r = seq->private;
-       struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
+       struct aa_proxy *proxy = seq->private;
+       struct aa_label *label = aa_get_label_rcu(&proxy->label);
+       struct aa_profile *profile = labels_profile(label);
        seq_printf(seq, "%s\n", profile->base.name);
-       aa_put_profile(profile);
+       aa_put_label(label);
 
        return 0;
 }
@@ -271,10 +456,11 @@ static const struct file_operations aa_fs_profname_fops = {
 
 static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v)
 {
-       struct aa_replacedby *r = seq->private;
-       struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
+       struct aa_proxy *proxy = seq->private;
+       struct aa_label *label = aa_get_label_rcu(&proxy->label);
+       struct aa_profile *profile = labels_profile(label);
        seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]);
-       aa_put_profile(profile);
+       aa_put_label(label);
 
        return 0;
 }
@@ -294,15 +480,16 @@ static const struct file_operations aa_fs_profmode_fops = {
 
 static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v)
 {
-       struct aa_replacedby *r = seq->private;
-       struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
+       struct aa_proxy *proxy = seq->private;
+       struct aa_label *label = aa_get_label_rcu(&proxy->label);
+       struct aa_profile *profile = labels_profile(label);
        if (profile->attach)
                seq_printf(seq, "%s\n", profile->attach);
        else if (profile->xmatch)
                seq_puts(seq, "<unknown>\n");
        else
                seq_printf(seq, "%s\n", profile->base.name);
-       aa_put_profile(profile);
+       aa_put_label(label);
 
        return 0;
 }
@@ -322,8 +509,9 @@ static const struct file_operations aa_fs_profattach_fops = {
 
 static int aa_fs_seq_hash_show(struct seq_file *seq, void *v)
 {
-       struct aa_replacedby *r = seq->private;
-       struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
+       struct aa_proxy *proxy = seq->private;
+       struct aa_label *label = aa_get_label_rcu(&proxy->label);
+       struct aa_profile *profile = labels_profile(label);
        unsigned int i, size = aa_hash_size();
 
        if (profile->hash) {
@@ -331,6 +519,7 @@ static int aa_fs_seq_hash_show(struct seq_file *seq, void *v)
                        seq_printf(seq, "%.2x", profile->hash[i]);
                seq_puts(seq, "\n");
        }
+       aa_put_label(label);
 
        return 0;
 }
@@ -349,6 +538,11 @@ static const struct file_operations aa_fs_seq_hash_fops = {
 };
 
 /** fns to setup dynamic per profile/namespace files **/
+
+/**
+ *
+ * Requires: @profile->ns->lock held
+ */
 void __aa_fs_profile_rmdir(struct aa_profile *profile)
 {
        struct aa_profile *child;
@@ -356,29 +550,40 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile)
 
        if (!profile)
                return;
+       AA_BUG(!mutex_is_locked(&profiles_ns(profile)->lock));
 
        list_for_each_entry(child, &profile->base.profiles, base.list)
                __aa_fs_profile_rmdir(child);
 
        for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) {
-               struct aa_replacedby *r;
+               struct aa_proxy *proxy;
                if (!profile->dents[i])
                        continue;
 
-               r = d_inode(profile->dents[i])->i_private;
+               proxy = d_inode(profile->dents[i])->i_private;
                securityfs_remove(profile->dents[i]);
-               aa_put_replacedby(r);
+               aa_put_proxy(proxy);
                profile->dents[i] = NULL;
        }
 }
 
+/**
+ *
+ * Requires: @old->ns->lock held
+ */
 void __aa_fs_profile_migrate_dents(struct aa_profile *old,
                                   struct aa_profile *new)
 {
        int i;
 
+       AA_BUG(!old);
+       AA_BUG(!new);
+       AA_BUG(!mutex_is_locked(&profiles_ns(old)->lock));
+
        for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
                new->dents[i] = old->dents[i];
+               if (new->dents[i])
+                       new->dents[i]->d_inode->i_mtime = CURRENT_TIME;
                old->dents[i] = NULL;
        }
 }
@@ -387,23 +592,29 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name,
                                          struct aa_profile *profile,
                                          const struct file_operations *fops)
 {
-       struct aa_replacedby *r = aa_get_replacedby(profile->replacedby);
+       struct aa_proxy *proxy = aa_get_proxy(profile->label.proxy);
        struct dentry *dent;
 
-       dent = securityfs_create_file(name, S_IFREG | 0444, dir, r, fops);
+       dent = securityfs_create_file(name, S_IFREG | 0444, dir, proxy, fops);
        if (IS_ERR(dent))
-               aa_put_replacedby(r);
+               aa_put_proxy(proxy);
 
        return dent;
 }
 
-/* requires lock be held */
+/**
+ *
+ * Requires: @profile->ns->lock held
+ */
 int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
 {
        struct aa_profile *child;
        struct dentry *dent = NULL, *dir;
        int error;
 
+       AA_BUG(!profile);
+       AA_BUG(!mutex_is_locked(&profiles_ns(profile)->lock));
+
        if (!parent) {
                struct aa_profile *p;
                p = aa_deref_parent(profile);
@@ -474,21 +685,26 @@ fail2:
        return error;
 }
 
-void __aa_fs_namespace_rmdir(struct aa_namespace *ns)
+/**
+ *
+ * Requires: @ns->lock held
+ */
+void __aa_fs_ns_rmdir(struct aa_ns *ns)
 {
-       struct aa_namespace *sub;
+       struct aa_ns *sub;
        struct aa_profile *child;
        int i;
 
        if (!ns)
                return;
+       AA_BUG(!mutex_is_locked(&ns->lock));
 
        list_for_each_entry(child, &ns->base.profiles, base.list)
                __aa_fs_profile_rmdir(child);
 
        list_for_each_entry(sub, &ns->sub_ns, base.list) {
                mutex_lock(&sub->lock);
-               __aa_fs_namespace_rmdir(sub);
+               __aa_fs_ns_rmdir(sub);
                mutex_unlock(&sub->lock);
        }
 
@@ -498,14 +714,21 @@ void __aa_fs_namespace_rmdir(struct aa_namespace *ns)
        }
 }
 
-int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
-                           const char *name)
+/**
+ *
+ * Requires: @ns->lock held
+ */
+int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name)
 {
-       struct aa_namespace *sub;
+       struct aa_ns *sub;
        struct aa_profile *child;
        struct dentry *dent, *dir;
        int error;
 
+       AA_BUG(!ns);
+       AA_BUG(!parent);
+       AA_BUG(!mutex_is_locked(&ns->lock));
+
        if (!name)
                name = ns->base.name;
 
@@ -532,7 +755,7 @@ int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
 
        list_for_each_entry(sub, &ns->sub_ns, base.list) {
                mutex_lock(&sub->lock);
-               error = __aa_fs_namespace_mkdir(sub, ns_subns_dir(ns), NULL);
+               error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL);
                mutex_unlock(&sub->lock);
                if (error)
                        goto fail2;
@@ -544,7 +767,7 @@ fail:
        error = PTR_ERR(dent);
 
 fail2:
-       __aa_fs_namespace_rmdir(ns);
+       __aa_fs_ns_rmdir(ns);
 
        return error;
 }
@@ -555,7 +778,7 @@ fail2:
 #define list_entry_is_head(pos, head, member) (&pos->member == (head))
 
 /**
- * __next_namespace - find the next namespace to list
+ * __next_ns - find the next namespace to list
  * @root: root namespace to stop search at (NOT NULL)
  * @ns: current ns position (NOT NULL)
  *
@@ -566,10 +789,13 @@ fail2:
  * Requires: ns->parent->lock to be held
  * NOTE: will not unlock root->lock
  */
-static struct aa_namespace *__next_namespace(struct aa_namespace *root,
-                                            struct aa_namespace *ns)
+static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
 {
-       struct aa_namespace *parent, *next;
+       struct aa_ns *parent, *next;
+
+       AA_BUG(!root);
+       AA_BUG(!ns);
+       AA_BUG(ns != root && !mutex_is_locked(&ns->parent->lock));
 
        /* is next namespace a child */
        if (!list_empty(&ns->sub_ns)) {
@@ -597,15 +823,17 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root,
 /**
  * __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)
+ * @ns: namespace to start in   (MAY BE NULL)
  *
  * Returns: unrefcounted profile or NULL if no profile
- * Requires: profile->ns.lock to be held
+ * Requires: ns.lock to be held
  */
-static struct aa_profile *__first_profile(struct aa_namespace *root,
-                                         struct aa_namespace *ns)
+static struct aa_profile *__first_profile(struct aa_ns *root, struct aa_ns *ns)
 {
-       for (; ns; ns = __next_namespace(root, ns)) {
+       AA_BUG(!root);
+       AA_BUG(ns && !mutex_is_locked(&ns->lock));
+
+       for (; ns; ns = __next_ns(root, ns)) {
                if (!list_empty(&ns->base.profiles))
                        return list_first_entry(&ns->base.profiles,
                                                struct aa_profile, base.list);
@@ -625,7 +853,9 @@ static struct aa_profile *__first_profile(struct aa_namespace *root,
 static struct aa_profile *__next_profile(struct aa_profile *p)
 {
        struct aa_profile *parent;
-       struct aa_namespace *ns = p->ns;
+       struct aa_ns *ns = p->ns;
+
+       AA_BUG(!mutex_is_locked(&profiles_ns(p)->lock));
 
        /* is next profile a child */
        if (!list_empty(&p->base.profiles))
@@ -659,7 +889,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p)
  *
  * Returns: next profile or NULL if there isn't one
  */
-static struct aa_profile *next_profile(struct aa_namespace *root,
+static struct aa_profile *next_profile(struct aa_ns *root,
                                       struct aa_profile *profile)
 {
        struct aa_profile *next = __next_profile(profile);
@@ -667,7 +897,7 @@ static struct aa_profile *next_profile(struct aa_namespace *root,
                return next;
 
        /* finished all profiles in namespace move to next namespace */
-       return __first_profile(root, __next_namespace(root, profile->ns));
+       return __first_profile(root, __next_ns(root, profile->ns));
 }
 
 /**
@@ -682,10 +912,9 @@ static struct aa_profile *next_profile(struct aa_namespace *root,
 static void *p_start(struct seq_file *f, loff_t *pos)
 {
        struct aa_profile *profile = NULL;
-       struct aa_namespace *root = aa_current_profile()->ns;
+       struct aa_ns *root = aa_get_current_ns();
        loff_t l = *pos;
-       f->private = aa_get_namespace(root);
-
+       f->private = root;
 
        /* find the first profile */
        mutex_lock(&root->lock);
@@ -711,7 +940,7 @@ static void *p_start(struct seq_file *f, loff_t *pos)
 static void *p_next(struct seq_file *f, void *p, loff_t *pos)
 {
        struct aa_profile *profile = p;
-       struct aa_namespace *ns = f->private;
+       struct aa_ns *ns = f->private;
        (*pos)++;
 
        return next_profile(ns, profile);
@@ -727,14 +956,14 @@ static void *p_next(struct seq_file *f, void *p, loff_t *pos)
 static void p_stop(struct seq_file *f, void *p)
 {
        struct aa_profile *profile = p;
-       struct aa_namespace *root = f->private, *ns;
+       struct aa_ns *root = f->private, *ns;
 
        if (profile) {
                for (ns = profile->ns; ns && ns != root; ns = ns->parent)
                        mutex_unlock(&ns->lock);
        }
        mutex_unlock(&root->lock);
-       aa_put_namespace(root);
+       aa_put_ns(root);
 }
 
 /**
@@ -747,12 +976,11 @@ static void p_stop(struct seq_file *f, void *p)
 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;
+       struct aa_ns *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,
-                  aa_profile_mode_names[profile->mode]);
+       aa_label_seq_xprint(f, root, &profile->label,
+                           FLAG_SHOW_MODE | FLAG_VIEW_SUBNS, GFP_KERNEL);
+       seq_printf(f, "\n");
 
        return 0;
 }
@@ -766,6 +994,9 @@ static const struct seq_operations aa_fs_profiles_op = {
 
 static int profiles_open(struct inode *inode, struct file *file)
 {
+       if (!policy_admin_capable())
+               return -EACCES;
+
        return seq_open(file, &aa_fs_profiles_op);
 }
 
@@ -789,34 +1020,76 @@ static struct aa_fs_entry aa_fs_entry_file[] = {
        { }
 };
 
+static struct aa_fs_entry aa_fs_entry_ptrace[] = {
+       AA_FS_FILE_STRING("mask", "read trace"),
+       { }
+};
+
+static struct aa_fs_entry aa_fs_entry_signal[] = {
+       AA_FS_FILE_STRING("mask", AA_FS_SIG_MASK),
+       { }
+};
+
 static struct aa_fs_entry aa_fs_entry_domain[] = {
        AA_FS_FILE_BOOLEAN("change_hat",        1),
        AA_FS_FILE_BOOLEAN("change_hatv",       1),
        AA_FS_FILE_BOOLEAN("change_onexec",     1),
        AA_FS_FILE_BOOLEAN("change_profile",    1),
+       AA_FS_FILE_BOOLEAN("stack",             1),
+       { }
+};
+
+static struct aa_fs_entry aa_fs_entry_versions[] = {
+       AA_FS_FILE_BOOLEAN("v5",        1),
+       AA_FS_FILE_BOOLEAN("v6",        1),
+       AA_FS_FILE_BOOLEAN("v7",        1),
        { }
 };
 
 static struct aa_fs_entry aa_fs_entry_policy[] = {
-       AA_FS_FILE_BOOLEAN("set_load",          1),
-       {}
+       AA_FS_DIR("versions",                   aa_fs_entry_versions),
+       AA_FS_FILE_BOOLEAN("set_load",          1),
+       { }
+};
+
+static struct aa_fs_entry aa_fs_entry_mount[] = {
+       AA_FS_FILE_STRING("mask", "mount umount"),
+       { }
+};
+
+static struct aa_fs_entry aa_fs_entry_ns[] = {
+       AA_FS_FILE_BOOLEAN("profile",           1),
+       AA_FS_FILE_BOOLEAN("pivot_root",        1),
+       { }
+};
+
+static struct aa_fs_entry aa_fs_entry_dbus[] = {
+       AA_FS_FILE_STRING("mask", "acquire send receive"),
+       { }
 };
 
 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_DIR("mount",                      aa_fs_entry_mount),
+       AA_FS_DIR("namespaces",                 aa_fs_entry_ns),
        AA_FS_FILE_U64("capability",            VFS_CAP_FLAGS_MASK),
        AA_FS_DIR("rlimit",                     aa_fs_entry_rlimit),
        AA_FS_DIR("caps",                       aa_fs_entry_caps),
+       AA_FS_DIR("ptrace",                     aa_fs_entry_ptrace),
+       AA_FS_DIR("signal",                     aa_fs_entry_signal),
+       AA_FS_DIR("dbus",                       aa_fs_entry_dbus),
        { }
 };
 
 static struct aa_fs_entry aa_fs_entry_apparmor[] = {
-       AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load),
-       AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace),
-       AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove),
-       AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops),
+       AA_FS_FILE_FOPS(".load", 0666, &aa_fs_profile_load),
+       AA_FS_FILE_FOPS(".replace", 0666, &aa_fs_profile_replace),
+       AA_FS_FILE_FOPS(".remove", 0666, &aa_fs_profile_remove),
+       AA_FS_FILE_FOPS(".access", 0666, &aa_fs_access),
+       AA_FS_FILE_FOPS("profiles", 0444, &aa_fs_profiles_fops),
        AA_FS_DIR("features", aa_fs_entry_features),
        { }
 };
@@ -925,6 +1198,51 @@ void __init aa_destroy_aafs(void)
        aafs_remove_dir(&aa_fs_entry);
 }
 
+
+#define NULL_FILE_NAME ".null"
+struct path aa_null;
+
+static int aa_mk_null_file(struct dentry *parent)
+{
+       struct vfsmount *mount = NULL;
+       struct dentry *dentry;
+       struct inode *inode;
+       int count = 0;
+       int error = simple_pin_fs(parent->d_sb->s_type, &mount, &count);
+       if (error)
+               return error;
+
+       mutex_lock(&parent->d_inode->i_mutex);
+       dentry = lookup_one_len(NULL_FILE_NAME, parent, strlen(NULL_FILE_NAME));
+       if (IS_ERR(dentry)) {
+               error = PTR_ERR(dentry);
+               goto out;
+       }
+       inode = new_inode(parent->d_inode->i_sb);
+       if (!inode) {
+               error = -ENOMEM;
+               goto out1;
+       }
+
+       inode->i_ino = get_next_ino();
+       inode->i_mode = S_IFCHR | S_IRUGO | S_IWUGO;
+       inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+       init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO,
+                          MKDEV(MEM_MAJOR, 3));
+       d_instantiate(dentry, inode);
+       aa_null.dentry = dget(dentry);
+       aa_null.mnt = mntget(mount);
+
+       error = 0;
+
+out1:
+       dput(dentry);
+out:
+       mutex_unlock(&parent->d_inode->i_mutex);
+       simple_release_fs(&mount, &count);
+       return error;
+}
+
 /**
  * aa_create_aafs - create the apparmor security filesystem
  *
@@ -949,12 +1267,20 @@ static int __init aa_create_aafs(void)
        if (error)
                goto error;
 
-       error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry.dentry,
-                                       "policy");
+       mutex_lock(&root_ns->lock);
+       error = __aa_fs_ns_mkdir(root_ns, aa_fs_entry.dentry, "policy");
+       mutex_unlock(&root_ns->lock);
+
+       if (error)
+               goto error;
+
+       error = aa_mk_null_file(aa_fs_entry.dentry);
        if (error)
                goto error;
 
-       /* TODO: add support for apparmorfs_null and apparmorfs_mnt */
+       if (!aa_g_unconfined_init) {
+               /* TODO: add default profile to apparmorfs */
+       }
 
        /* Report that AppArmor fs is enabled */
        aa_info_message("AppArmor Filesystem Enabled");
index 89c78658031f10bfc0527030ed970aebe3ff5471..ec2daa2c34252b347dc5667802e4d4a2d11108ba 100644 (file)
 #include "include/apparmor.h"
 #include "include/audit.h"
 #include "include/policy.h"
+#include "include/policy_ns.h"
 
-const char *const op_table[] = {
-       "null",
-
-       "sysctl",
-       "capable",
-
-       "unlink",
-       "mkdir",
-       "rmdir",
-       "mknod",
-       "truncate",
-       "link",
-       "symlink",
-       "rename_src",
-       "rename_dest",
-       "chmod",
-       "chown",
-       "getattr",
-       "open",
-
-       "file_perm",
-       "file_lock",
-       "file_mmap",
-       "file_mprotect",
-
-       "create",
-       "post_create",
-       "bind",
-       "connect",
-       "listen",
-       "accept",
-       "sendmsg",
-       "recvmsg",
-       "getsockname",
-       "getpeername",
-       "getsockopt",
-       "setsockopt",
-       "socket_shutdown",
-
-       "ptrace",
-
-       "exec",
-       "change_hat",
-       "change_profile",
-       "change_onexec",
-
-       "setprocattr",
-       "setrlimit",
-
-       "profile_replace",
-       "profile_load",
-       "profile_remove"
-};
 
 const char *const audit_mode_names[] = {
        "normal",
@@ -114,34 +62,42 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
 
        if (aa_g_audit_header) {
                audit_log_format(ab, "apparmor=");
-               audit_log_string(ab, aa_audit_type[sa->aad->type]);
+               audit_log_string(ab, aa_audit_type[aad(sa)->type]);
        }
 
-       if (sa->aad->op) {
+       if (aad(sa)->op) {
                audit_log_format(ab, " operation=");
-               audit_log_string(ab, op_table[sa->aad->op]);
+               audit_log_string(ab, aad(sa)->op);
        }
 
-       if (sa->aad->info) {
+       if (aad(sa)->info) {
                audit_log_format(ab, " info=");
-               audit_log_string(ab, sa->aad->info);
-               if (sa->aad->error)
-                       audit_log_format(ab, " error=%d", sa->aad->error);
+               audit_log_string(ab, aad(sa)->info);
+               if (aad(sa)->error)
+                       audit_log_format(ab, " error=%d", aad(sa)->error);
        }
 
-       if (sa->aad->profile) {
-               struct aa_profile *profile = sa->aad->profile;
-               if (profile->ns != root_ns) {
-                       audit_log_format(ab, " namespace=");
-                       audit_log_untrustedstring(ab, profile->ns->base.hname);
+       if (aad(sa)->label) {
+               struct aa_label *label = aad(sa)->label;
+               if (label_isprofile(label)) {
+                       struct aa_profile *profile = labels_profile(label);
+                       if (profile->ns != root_ns) {
+                               audit_log_format(ab, " namespace=");
+                               audit_log_untrustedstring(ab,
+                                                      profile->ns->base.hname);
+                       }
+                       audit_log_format(ab, " profile=");
+                       audit_log_untrustedstring(ab, profile->base.hname);
+               } else {
+                       audit_log_format(ab, " label=");
+                       aa_label_xaudit(ab, root_ns, label, FLAG_VIEW_SUBNS,
+                                       GFP_ATOMIC);
                }
-               audit_log_format(ab, " profile=");
-               audit_log_untrustedstring(ab, profile->base.hname);
        }
 
-       if (sa->aad->name) {
+       if (aad(sa)->name) {
                audit_log_format(ab, " name=");
-               audit_log_untrustedstring(ab, sa->aad->name);
+               audit_log_untrustedstring(ab, aad(sa)->name);
        }
 }
 
@@ -153,7 +109,12 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
 void aa_audit_msg(int type, struct common_audit_data *sa,
                  void (*cb) (struct audit_buffer *, void *))
 {
-       sa->aad->type = type;
+       /* TODO: redirect messages for profile to the correct ns
+        *       rejects from subns should goto the audit associated
+        *       with it, and audits from parent ns should got ns
+        *       associated with it
+        */
+       aad(sa)->type = type;
        common_lsm_audit(sa, audit_pre, cb);
 }
 
@@ -161,7 +122,6 @@ void aa_audit_msg(int type, struct common_audit_data *sa,
  * aa_audit - Log a profile based audit event to the audit subsystem
  * @type: audit type for the message
  * @profile: profile to check against (NOT NULL)
- * @gfp: allocation flags to use
  * @sa: audit event (NOT NULL)
  * @cb: optional callback fn for type specific fields (MAYBE NULL)
  *
@@ -169,14 +129,13 @@ void aa_audit_msg(int type, struct common_audit_data *sa,
  *
  * Returns: error on failure
  */
-int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
-            struct common_audit_data *sa,
+int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa,
             void (*cb) (struct audit_buffer *, void *))
 {
        BUG_ON(!profile);
 
        if (type == AUDIT_APPARMOR_AUTO) {
-               if (likely(!sa->aad->error)) {
+               if (likely(!aad(sa)->error)) {
                        if (AUDIT_MODE(profile) != AUDIT_ALL)
                                return 0;
                        type = AUDIT_APPARMOR_AUDIT;
@@ -188,22 +147,22 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
        if (AUDIT_MODE(profile) == AUDIT_QUIET ||
            (type == AUDIT_APPARMOR_DENIED &&
             AUDIT_MODE(profile) == AUDIT_QUIET))
-               return sa->aad->error;
+         return aad(sa)->error;
 
        if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED)
                type = AUDIT_APPARMOR_KILL;
 
-       if (!unconfined(profile))
-               sa->aad->profile = profile;
+       aad(sa)->label = &profile->label;
 
        aa_audit_msg(type, sa, cb);
 
-       if (sa->aad->type == AUDIT_APPARMOR_KILL)
+       if (aad(sa)->type == AUDIT_APPARMOR_KILL)
                (void)send_sig_info(SIGKILL, NULL,
-                                   sa->u.tsk ?  sa->u.tsk : current);
+                       sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ?
+                                   sa->u.tsk : current);
 
-       if (sa->aad->type == AUDIT_APPARMOR_ALLOWED)
-               return complain_error(sa->aad->error);
+       if (aad(sa)->type == AUDIT_APPARMOR_ALLOWED)
+         return complain_error(aad(sa)->error);
 
-       return sa->aad->error;
+       return aad(sa)->error;
 }
index 1101c6f64bb7cb36602957ef2bcbc1538ee8aa63..3c3b69b613734cd0b4ec7f03c43d6f8ec2f13476 100644 (file)
@@ -53,6 +53,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
 
 /**
  * audit_caps - audit a capability
+ * @sa: audit data
  * @profile: profile being tested for confinement (NOT NULL)
  * @cap: capability tested
  * @error: error code returned by test
@@ -62,17 +63,12 @@ static void audit_cb(struct audit_buffer *ab, void *va)
  *
  * Returns: 0 or sa->error on success,  error code on failure
  */
-static int audit_caps(struct aa_profile *profile, int cap, int error)
+static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
+                     int cap, int error)
 {
        struct audit_cache *ent;
        int type = AUDIT_APPARMOR_AUTO;
-       struct common_audit_data sa;
-       struct apparmor_audit_data aad = {0,};
-       sa.type = LSM_AUDIT_DATA_CAP;
-       sa.aad = &aad;
-       sa.u.cap = cap;
-       sa.aad->op = OP_CAPABLE;
-       sa.aad->error = error;
+       aad(sa)->error = error;
 
        if (likely(!error)) {
                /* test if auditing is being forced */
@@ -104,24 +100,44 @@ static int audit_caps(struct aa_profile *profile, int cap, int error)
        }
        put_cpu_var(audit_cache);
 
-       return aa_audit(type, profile, GFP_ATOMIC, &sa, audit_cb);
+       return aa_audit(type, profile, sa, audit_cb);
 }
 
 /**
  * profile_capable - test if profile allows use of capability @cap
  * @profile: profile being enforced    (NOT NULL, NOT unconfined)
  * @cap: capability to test if allowed
+ * @audit: whether an audit record should be generated
+ * @sa: audit data (MAY BE NULL indicating no auditing)
  *
  * Returns: 0 if allowed else -EPERM
  */
-static int profile_capable(struct aa_profile *profile, int cap)
+static int profile_capable(struct aa_profile *profile, int cap, int audit,
+                          struct common_audit_data *sa)
 {
-       return cap_raised(profile->caps.allow, cap) ? 0 : -EPERM;
+       int error;
+
+       if (cap_raised(profile->caps.allow, cap) &&
+           !cap_raised(profile->caps.denied, cap))
+               error = 0;
+       else
+               error = -EPERM;
+
+       if (audit == SECURITY_CAP_NOAUDIT) {
+               if (!COMPLAIN_MODE(profile))
+                      return error;
+              /* audit the cap request in complain mode but note that it
+               * should be optional.
+               */
+              aad(sa)->info = "optional: no audit";
+       }
+
+       return audit_caps(sa, profile, cap, error);
 }
 
 /**
  * aa_capable - test permission to use capability
- * @profile: profile being tested against (NOT NULL)
+ * @label: label being tested for capability (NOT NULL)
  * @cap: capability to be tested
  * @audit: whether an audit record should be generated
  *
@@ -129,15 +145,15 @@ static int profile_capable(struct aa_profile *profile, int cap)
  *
  * Returns: 0 on success, or else an error code.
  */
-int aa_capable(struct aa_profile *profile, int cap, int audit)
+int aa_capable(struct aa_label *label, int cap, int audit)
 {
-       int error = profile_capable(profile, cap);
+       struct aa_profile *profile;
+       int error = 0;
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE);
+       sa.u.cap = cap;
 
-       if (!audit) {
-               if (COMPLAIN_MODE(profile))
-                       return complain_error(error);
-               return error;
-       }
+       error = fn_for_each_confined(label, profile,
+                       profile_capable(profile, cap, audit, &sa));
 
-       return audit_caps(profile, cap, error);
+       return error;
 }
index 3064c6ced87cae69b8c2eb224e4d2ac5af9d9c79..825b11575dadc70b2b0a0fd37f75d9e0fb97e7ee 100644 (file)
  * License.
  *
  *
- * AppArmor sets confinement on every task, via the the aa_task_cxt and
- * the aa_task_cxt.profile, both of which are required and are not allowed
- * to be NULL.  The aa_task_cxt is not reference counted and is unique
- * to each cred (which is reference count).  The profile pointed to by
- * the task_cxt is reference counted.
+ * AppArmor sets confinement on every task, via the the aa_task_ctx and
+ * the aa_task_ctx.label, both of which are required and are not allowed
+ * to be NULL.  The aa_task_ctx is not reference counted and is unique
+ * to each cred (which is reference count).  The label pointed to by
+ * the task_ctx is reference counted.
  *
  * TODO
  * If a task uses change_hat it currently does not return to the old
 #include "include/policy.h"
 
 /**
- * aa_alloc_task_context - allocate a new task_cxt
+ * aa_alloc_task_context - allocate a new task_ctx
  * @flags: gfp flags for allocation
  *
  * Returns: allocated buffer or NULL on failure
  */
-struct aa_task_cxt *aa_alloc_task_context(gfp_t flags)
+struct aa_task_ctx *aa_alloc_task_context(gfp_t flags)
 {
-       return kzalloc(sizeof(struct aa_task_cxt), flags);
+       return kzalloc(sizeof(struct aa_task_ctx), flags);
 }
 
 /**
- * aa_free_task_context - free a task_cxt
- * @cxt: task_cxt to free (MAYBE NULL)
+ * aa_free_task_context - free a task_ctx
+ * @ctx: task_ctx to free (MAYBE NULL)
  */
-void aa_free_task_context(struct aa_task_cxt *cxt)
+void aa_free_task_context(struct aa_task_ctx *ctx)
 {
-       if (cxt) {
-               aa_put_profile(cxt->profile);
-               aa_put_profile(cxt->previous);
-               aa_put_profile(cxt->onexec);
+       if (ctx) {
+               aa_put_label(ctx->label);
+               aa_put_label(ctx->previous);
+               aa_put_label(ctx->onexec);
 
-               kzfree(cxt);
+               kzfree(ctx);
        }
 }
 
@@ -60,64 +60,63 @@ void aa_free_task_context(struct aa_task_cxt *cxt)
  * @new: a blank task context      (NOT NULL)
  * @old: the task context to copy  (NOT NULL)
  */
-void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old)
+void aa_dup_task_context(struct aa_task_ctx *new, const struct aa_task_ctx *old)
 {
        *new = *old;
-       aa_get_profile(new->profile);
-       aa_get_profile(new->previous);
-       aa_get_profile(new->onexec);
+       aa_get_label(new->label);
+       aa_get_label(new->previous);
+       aa_get_label(new->onexec);
 }
 
 /**
- * aa_get_task_profile - Get another task's profile
+ * aa_get_task_label - Get another task's label
  * @task: task to query  (NOT NULL)
  *
- * Returns: counted reference to @task's profile
+ * Returns: counted reference to @task's label
  */
-struct aa_profile *aa_get_task_profile(struct task_struct *task)
+struct aa_label *aa_get_task_label(struct task_struct *task)
 {
-       struct aa_profile *p;
+       struct aa_label *p;
 
        rcu_read_lock();
-       p = aa_get_profile(__aa_task_profile(task));
+       p = aa_get_newest_label(__aa_task_raw_label(task));
        rcu_read_unlock();
 
        return p;
 }
 
 /**
- * aa_replace_current_profile - replace the current tasks profiles
- * @profile: new profile  (NOT NULL)
+ * aa_replace_current_label - replace the current tasks label
+ * @label: new label  (NOT NULL)
  *
  * Returns: 0 or error on failure
  */
-int aa_replace_current_profile(struct aa_profile *profile)
+int aa_replace_current_label(struct aa_label *label)
 {
-       struct aa_task_cxt *cxt = current_cxt();
+       struct aa_task_ctx *ctx = current_ctx();
        struct cred *new;
-       BUG_ON(!profile);
+       BUG_ON(!label);
 
-       if (cxt->profile == profile)
+       if (ctx->label == label)
                return 0;
 
+       if (current_cred() != current_real_cred())
+               return -EBUSY;
+
        new  = prepare_creds();
        if (!new)
                return -ENOMEM;
 
-       cxt = cred_cxt(new);
-       if (unconfined(profile) || (cxt->profile->ns != profile->ns))
-               /* if switching to unconfined or a different profile namespace
+       ctx = cred_ctx(new);
+       if (unconfined(label) || (labels_ns(ctx->label) != labels_ns(label)))
+               /* if switching to unconfined or a different label namespace
                 * clear out context state
                 */
-               aa_clear_task_cxt_trans(cxt);
+               aa_clear_task_ctx_trans(ctx);
 
-       /* be careful switching cxt->profile, when racing replacement it
-        * is possible that cxt->profile->replacedby->profile is the reference
-        * keeping @profile valid, so make sure to get its reference before
-        * dropping the reference on cxt->profile */
-       aa_get_profile(profile);
-       aa_put_profile(cxt->profile);
-       cxt->profile = profile;
+       aa_get_label(label);
+       aa_put_label(ctx->label);
+       ctx->label = label;
 
        commit_creds(new);
        return 0;
@@ -125,21 +124,22 @@ int aa_replace_current_profile(struct aa_profile *profile)
 
 /**
  * aa_set_current_onexec - set the tasks change_profile to happen onexec
- * @profile: system profile to set at exec  (MAYBE NULL to clear value)
- *
+ * @label: system label to set at exec  (MAYBE NULL to clear value)
+ * @stack: whether stacking should be done
  * Returns: 0 or error on failure
  */
-int aa_set_current_onexec(struct aa_profile *profile)
+int aa_set_current_onexec(struct aa_label *label, bool stack)
 {
-       struct aa_task_cxt *cxt;
+       struct aa_task_ctx *ctx;
        struct cred *new = prepare_creds();
        if (!new)
                return -ENOMEM;
 
-       cxt = cred_cxt(new);
-       aa_get_profile(profile);
-       aa_put_profile(cxt->onexec);
-       cxt->onexec = profile;
+       ctx = cred_ctx(new);
+       aa_get_label(label);
+       aa_clear_task_ctx_trans(ctx);
+       ctx->onexec = label;
+       ctx->token = stack;
 
        commit_creds(new);
        return 0;
@@ -147,7 +147,7 @@ int aa_set_current_onexec(struct aa_profile *profile)
 
 /**
  * aa_set_current_hat - set the current tasks hat
- * @profile: profile to set as the current hat  (NOT NULL)
+ * @label: label to set as the current hat  (NOT NULL)
  * @token: token value that must be specified to change from the hat
  *
  * Do switch of tasks hat.  If the task is currently in a hat
@@ -155,67 +155,67 @@ int aa_set_current_onexec(struct aa_profile *profile)
  *
  * Returns: 0 or error on failure
  */
-int aa_set_current_hat(struct aa_profile *profile, u64 token)
+int aa_set_current_hat(struct aa_label *label, u64 token)
 {
-       struct aa_task_cxt *cxt;
+       struct aa_task_ctx *ctx;
        struct cred *new = prepare_creds();
        if (!new)
                return -ENOMEM;
-       BUG_ON(!profile);
+       BUG_ON(!label);
 
-       cxt = cred_cxt(new);
-       if (!cxt->previous) {
+       ctx = cred_ctx(new);
+       if (!ctx->previous) {
                /* transfer refcount */
-               cxt->previous = cxt->profile;
-               cxt->token = token;
-       } else if (cxt->token == token) {
-               aa_put_profile(cxt->profile);
+               ctx->previous = ctx->label;
+               ctx->token = token;
+       } else if (ctx->token == token) {
+               aa_put_label(ctx->label);
        } else {
-               /* previous_profile && cxt->token != token */
+               /* previous_profile && ctx->token != token */
                abort_creds(new);
                return -EACCES;
        }
-       cxt->profile = aa_get_newest_profile(profile);
+       ctx->label = aa_get_newest_label(label);
        /* clear exec on switching context */
-       aa_put_profile(cxt->onexec);
-       cxt->onexec = NULL;
+       aa_put_label(ctx->onexec);
+       ctx->onexec = NULL;
 
        commit_creds(new);
        return 0;
 }
 
 /**
- * aa_restore_previous_profile - exit from hat context restoring the profile
+ * aa_restore_previous_label - exit from hat context restoring previous label
  * @token: the token that must be matched to exit hat context
  *
- * Attempt to return out of a hat to the previous profile.  The token
+ * Attempt to return out of a hat to the previous label.  The token
  * must match the stored token value.
  *
  * Returns: 0 or error of failure
  */
-int aa_restore_previous_profile(u64 token)
+int aa_restore_previous_label(u64 token)
 {
-       struct aa_task_cxt *cxt;
+       struct aa_task_ctx *ctx;
        struct cred *new = prepare_creds();
        if (!new)
                return -ENOMEM;
 
-       cxt = cred_cxt(new);
-       if (cxt->token != token) {
+       ctx = cred_ctx(new);
+       if (ctx->token != token) {
                abort_creds(new);
                return -EACCES;
        }
-       /* ignore restores when there is no saved profile */
-       if (!cxt->previous) {
+       /* ignore restores when there is no saved label */
+       if (!ctx->previous) {
                abort_creds(new);
                return 0;
        }
 
-       aa_put_profile(cxt->profile);
-       cxt->profile = aa_get_newest_profile(cxt->previous);
-       BUG_ON(!cxt->profile);
+       aa_put_label(ctx->label);
+       ctx->label = aa_get_newest_label(ctx->previous);
+       BUG_ON(!ctx->label);
        /* clear exec && prev information when restoring to previous context */
-       aa_clear_task_cxt_trans(cxt);
+       aa_clear_task_ctx_trans(ctx);
 
        commit_creds(new);
        return 0;
index dc0027b28b049fa86b83543e2ce0a0640e591c89..0b1d361c47a59e7707ebf72b5f8c7425fac41468 100644 (file)
@@ -50,76 +50,259 @@ void aa_free_domain_entries(struct aa_domain *domain)
 
 /**
  * may_change_ptraced_domain - check if can change profile on ptraced task
- * @to_profile: profile to change to  (NOT NULL)
+ * @to_label: profile to change to  (NOT NULL)
+ * @info: message if there is an error
  *
  * Check if current is ptraced and if so if the tracing task is allowed
  * to trace the new domain
  *
  * Returns: %0 or error if change not allowed
  */
-static int may_change_ptraced_domain(struct aa_profile *to_profile)
+static int may_change_ptraced_domain(struct aa_label *to_label,
+                                    const char **info)
 {
        struct task_struct *tracer;
-       struct aa_profile *tracerp = NULL;
+       struct aa_label *tracerl = NULL;
        int error = 0;
 
        rcu_read_lock();
        tracer = ptrace_parent(current);
        if (tracer)
                /* released below */
-               tracerp = aa_get_task_profile(tracer);
+               tracerl = aa_get_task_label(tracer);
 
        /* not ptraced */
-       if (!tracer || unconfined(tracerp))
+       if (!tracer || unconfined(tracerl))
                goto out;
 
-       error = aa_may_ptrace(tracerp, to_profile, PTRACE_MODE_ATTACH);
+       error = aa_may_ptrace(tracerl, to_label, PTRACE_MODE_ATTACH);
 
 out:
        rcu_read_unlock();
-       aa_put_profile(tracerp);
+       aa_put_label(tracerl);
 
+       if (error)
+               *info = "ptrace prevents transition";
        return error;
 }
 
+/**** TODO: dedup to aa_label_match - needs perm and dfa, merging
+ * specifically this is an exact copy of aa_label_match except
+ * aa_compute_perms is replaced with aa_compute_fperms
+ * and policy.dfa with file.dfa
+ ****/
+/* match a profile and its associated ns component if needed
+ * Assumes visibility test has already been done.
+ * If a subns profile is not to be matched should be prescreened with
+ * visibility test.
+ */
+/* match a profile and its associated ns component if needed
+ * Assumes visibility test has already been done.
+ * If a subns profile is not to be matched should be prescreened with
+ * visibility test.
+ */
+static inline unsigned int match_component(struct aa_profile *profile,
+                                          struct aa_profile *tp,
+                                          bool stack, unsigned int state)
+{
+       const char *ns_name;
+
+       if (stack)
+               state = aa_dfa_match(profile->file.dfa, state, "&");
+       if (profile->ns == tp->ns)
+               return aa_dfa_match(profile->file.dfa, state, tp->base.hname);
+
+       /* try matching with namespace name and then profile */
+       ns_name = aa_ns_name(profile->ns, tp->ns, true);
+       state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
+       state = aa_dfa_match(profile->file.dfa, state, ns_name);
+       state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
+       return aa_dfa_match(profile->file.dfa, state, tp->base.hname);
+}
+
+/**
+ * label_component_match - find perms for full compound label
+ * @profile: profile to find perms for
+ * @label: label to check access permissions for
+ * @stack: whether this is a stacking request
+ * @start: state to start match in
+ * @subns: whether to do permission checks on components in a subns
+ * @request: permissions to request
+ * @perms: perms struct to set
+ *
+ * Returns: 0 on success else ERROR
+ *
+ * For the label A//&B//&C this does the perm match for A//&B//&C
+ * @perms should be preinitialized with allperms OR a previous permission
+ *        check to be stacked.
+ */
+static int label_compound_match(struct aa_profile *profile,
+                               struct aa_label *label, bool stack,
+                               unsigned int state, bool subns, u32 request,
+                               struct aa_perms *perms)
+{
+       struct aa_profile *tp;
+       struct label_it i;
+       struct path_cond cond = { };
+
+       /* find first subcomponent that is visible */
+       label_for_each(i, label, tp) {
+               if (!aa_ns_visible(profile->ns, tp->ns, subns))
+                       continue;
+               state = match_component(profile, tp, stack, state);
+               if (!state)
+                       goto fail;
+               goto next;
+       }
+
+       /* no component visible */
+       *perms = allperms;
+       return 0;
+
+next:
+       label_for_each_cont(i, label, tp) {
+               if (!aa_ns_visible(profile->ns, tp->ns, subns))
+                       continue;
+               state = aa_dfa_match(profile->file.dfa, state, "//&");
+               state = match_component(profile, tp, false, state);
+               if (!state)
+                       goto fail;
+       }
+       *perms = aa_compute_fperms(profile->file.dfa, state, &cond);
+       aa_apply_modes_to_perms(profile, perms);
+       if ((perms->allow & request) != request)
+               return -EACCES;
+
+       return 0;
+
+fail:
+       *perms = nullperms;
+       return -EACCES;
+}
+
+/**
+ * label_component_match - find perms for all subcomponents of a label
+ * @profile: profile to find perms for
+ * @label: label to check access permissions for
+ * @stack: whether this is a stacking request
+ * @start: state to start match in
+ * @subns: whether to do permission checks on components in a subns
+ * @request: permissions to request
+ * @perms: an initialized perms struct to add accumulation to
+ *
+ * Returns: 0 on success else ERROR
+ *
+ * For the label A//&B//&C this does the perm match for each of A and B and C
+ * @perms should be preinitialized with allperms OR a previous permission
+ *        check to be stacked.
+ */
+static int label_components_match(struct aa_profile *profile,
+                                 struct aa_label *label, bool stack,
+                                 unsigned int start, bool subns, u32 request,
+                                 struct aa_perms *perms)
+{
+       struct aa_profile *tp;
+       struct label_it i;
+       struct aa_perms tmp;
+       struct path_cond cond = { };
+       unsigned int state = 0;
+
+       /* find first subcomponent to test */
+       label_for_each(i, label, tp) {
+               if (!aa_ns_visible(profile->ns, tp->ns, subns))
+                       continue;
+               state = match_component(profile, tp, stack, start);
+               if (!state)
+                       goto fail;
+               goto next;
+       }
+
+       /* no subcomponents visible - no change in perms */
+       return 0;
+
+next:
+       tmp = aa_compute_fperms(profile->file.dfa, state, &cond);
+       aa_apply_modes_to_perms(profile, &tmp);
+       aa_perms_accum(perms, &tmp);
+       label_for_each_cont(i, label, tp) {
+               if (!aa_ns_visible(profile->ns, tp->ns, subns))
+                       continue;
+               state = match_component(profile, tp, stack, start);
+               if (!state)
+                       goto fail;
+               tmp = aa_compute_fperms(profile->file.dfa, state, &cond);
+               aa_apply_modes_to_perms(profile, &tmp);
+               aa_perms_accum(perms, &tmp);
+       }
+
+       if ((perms->allow & request) != request)
+               return -EACCES;
+
+       return 0;
+
+fail:
+       *perms = nullperms;
+       return -EACCES;
+}
+
+/**
+ * aa_label_match - do a multi-component label match
+ * @profile: profile to match against (NOT NULL)
+ * @label: label to match (NOT NULL)
+ * @stack: whether this is a stacking request
+ * @state: state to start in
+ * @subns: whether to match subns components
+ * @request: permission request
+ * @perms: Returns computed perms (NOT NULL)
+ *
+ * Returns: the state the match finished in, may be the none matching state
+ */
+static int label_match(struct aa_profile *profile, struct aa_label *label,
+                      bool stack, unsigned int state, bool subns, u32 request,
+                      struct aa_perms *perms)
+{
+       int error;
+
+       *perms = nullperms;
+       error = label_compound_match(profile, label, stack, state, subns,
+                                    request, perms);
+       if (!error)
+               return error;
+
+       *perms = allperms;
+       return label_components_match(profile, label, stack, state, subns,
+                                     request, perms);
+}
+
+/******* end TODO: dedup *****/
+
 /**
  * change_profile_perms - find permissions for change_profile
  * @profile: the current profile  (NOT NULL)
- * @ns: the namespace being switched to  (NOT NULL)
- * @name: the name of the profile to change to  (NOT NULL)
+ * @target: label to transition to (NOT NULL)
+ * @stack: whether this is a stacking request
  * @request: requested perms
  * @start: state to start matching in
  *
+ *
  * Returns: permission set
+ *
+ * currently only matches full label A//&B//&C or individual components A, B, C
+ * not arbitrary combinations. Eg. A//&B, C
  */
-static struct file_perms change_profile_perms(struct aa_profile *profile,
-                                             struct aa_namespace *ns,
-                                             const char *name, u32 request,
-                                             unsigned int start)
+static int change_profile_perms(struct aa_profile *profile,
+                               struct aa_label *target, bool stack,
+                               u32 request, unsigned int start,
+                               struct aa_perms *perms)
 {
-       struct file_perms perms;
-       struct path_cond cond = { };
-       unsigned int state;
-
-       if (unconfined(profile)) {
-               perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC;
-               perms.audit = perms.quiet = perms.kill = 0;
-               return perms;
-       } else if (!profile->file.dfa) {
-               return nullperms;
-       } else if ((ns == profile->ns)) {
-               /* try matching against rules with out namespace prepended */
-               aa_str_perms(profile->file.dfa, start, name, &cond, &perms);
-               if (COMBINED_PERM_MASK(perms) & request)
-                       return perms;
+       if (profile_unconfined(profile)) {
+               perms->allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC;
+               perms->audit = perms->quiet = perms->kill = 0;
+               return 0;
        }
 
-       /* try matching with namespace name and then profile */
-       state = aa_dfa_match(profile->file.dfa, start, ns->base.name);
-       state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
-       aa_str_perms(profile->file.dfa, state, name, &cond, &perms);
-
-       return perms;
+       /* TODO: add profile in ns screening */
+       return label_match(profile, target, stack, start, true, request, perms);
 }
 
 /**
@@ -143,7 +326,7 @@ static struct aa_profile *__attach_match(const char *name,
        struct aa_profile *profile, *candidate = NULL;
 
        list_for_each_entry_rcu(profile, head, base.list) {
-               if (profile->flags & PFLAG_NULL)
+               if (profile->label.flags & FLAG_NULL)
                        continue;
                if (profile->xmatch && profile->xmatch_len > len) {
                        unsigned int state = aa_dfa_match(profile->xmatch,
@@ -168,10 +351,10 @@ static struct aa_profile *__attach_match(const char *name,
  * @list: list to search  (NOT NULL)
  * @name: the executable name to match against  (NOT NULL)
  *
- * Returns: profile or NULL if no match found
+ * Returns: label or NULL if no match found
  */
-static struct aa_profile *find_attach(struct aa_namespace *ns,
-                                     struct list_head *list, const char *name)
+static struct aa_label *find_attach(struct aa_ns *ns, struct list_head *list,
+                                   const char *name)
 {
        struct aa_profile *profile;
 
@@ -179,49 +362,7 @@ static struct aa_profile *find_attach(struct aa_namespace *ns,
        profile = aa_get_profile(__attach_match(name, list));
        rcu_read_unlock();
 
-       return profile;
-}
-
-/**
- * separate_fqname - separate the namespace and profile names
- * @fqname: the fqname name to split  (NOT NULL)
- * @ns_name: the namespace name if it exists  (NOT NULL)
- *
- * This is the xtable equivalent routine of aa_split_fqname.  It finds the
- * split in an xtable fqname which contains an embedded \0 instead of a :
- * if a namespace is specified.  This is done so the xtable is constant and
- * isn't re-split on every lookup.
- *
- * Either the profile or namespace name may be optional but if the namespace
- * is specified the profile name termination must be present.  This results
- * in the following possible encodings:
- * profile_name\0
- * :ns_name\0profile_name\0
- * :ns_name\0\0
- *
- * NOTE: the xtable fqname is pre-validated at load time in unpack_trans_table
- *
- * Returns: profile name if it is specified else NULL
- */
-static const char *separate_fqname(const char *fqname, const char **ns_name)
-{
-       const char *name;
-
-       if (fqname[0] == ':') {
-               /* In this case there is guaranteed to be two \0 terminators
-                * in the string.  They are verified at load time by
-                * by unpack_trans_table
-                */
-               *ns_name = fqname + 1;          /* skip : */
-               name = *ns_name + strlen(*ns_name) + 1;
-               if (!*name)
-                       name = NULL;
-       } else {
-               *ns_name = NULL;
-               name = fqname;
-       }
-
-       return name;
+       return profile ? &profile->label : NULL;
 }
 
 static const char *next_name(int xtype, const char *name)
@@ -233,99 +374,291 @@ static const char *next_name(int xtype, const char *name)
  * x_table_lookup - lookup an x transition name via transition table
  * @profile: current profile (NOT NULL)
  * @xindex: index into x transition table
+ * @name: returns: name tested to find label (NOT NULL)
  *
- * Returns: refcounted profile, or NULL on failure (MAYBE NULL)
+ * Returns: refcounted label, or NULL on failure (MAYBE NULL)
  */
-static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
+struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
+                               const char **name)
 {
-       struct aa_profile *new_profile = NULL;
-       struct aa_namespace *ns = profile->ns;
+       struct aa_label *label = NULL;
        u32 xtype = xindex & AA_X_TYPE_MASK;
        int index = xindex & AA_X_INDEX_MASK;
-       const char *name;
 
-       /* index is guaranteed to be in range, validated at load time */
-       for (name = profile->file.trans.table[index]; !new_profile && name;
-            name = next_name(xtype, name)) {
-               struct aa_namespace *new_ns;
-               const char *xname = NULL;
+       AA_BUG(!name);
 
-               new_ns = NULL;
+       /* index is guaranteed to be in range, validated at load time */
+       /* TODO: move lookup parsing to unpack time so this is a straight
+        *       index into the resultant label
+        */
+       for (*name = profile->file.trans.table[index]; !label && *name;
+            *name = next_name(xtype, *name)) {
                if (xindex & AA_X_CHILD) {
+                       struct aa_profile *new_profile;
                        /* release by caller */
-                       new_profile = aa_find_child(profile, name);
-                       continue;
-               } else if (*name == ':') {
-                       /* switching namespace */
-                       const char *ns_name;
-                       xname = name = separate_fqname(name, &ns_name);
-                       if (!xname)
-                               /* no name so use profile name */
-                               xname = profile->base.hname;
-                       if (*ns_name == '@') {
-                               /* TODO: variable support */
-                               ;
-                       }
-                       /* released below */
-                       new_ns = aa_find_namespace(ns, ns_name);
-                       if (!new_ns)
-                               continue;
-               } else if (*name == '@') {
-                       /* TODO: variable support */
+                       new_profile = aa_find_child(profile, *name);
+                       if (new_profile)
+                               label = &new_profile->label;
                        continue;
-               } else {
-                       /* basic namespace lookup */
-                       xname = name;
                }
-
-               /* released by caller */
-               new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname);
-               aa_put_namespace(new_ns);
+               label = aa_label_parse(&profile->label, *name, GFP_ATOMIC,
+                                      true, false);
+               if (IS_ERR(label))
+                       label = NULL;
        }
 
        /* released by caller */
-       return new_profile;
+       return label;
 }
 
 /**
- * x_to_profile - get target profile for a given xindex
+ * x_to_label - get target label for a given xindex
  * @profile: current profile  (NOT NULL)
  * @name: name to lookup (NOT NULL)
  * @xindex: index into x transition table
+ * @lookupname: returns: name used in lookup if one was specified (NOT NULL)
  *
- * find profile for a transition index
+ * find label for a transition index
  *
- * Returns: refcounted profile or NULL if not found available
+ * Returns: refcounted label or NULL if not found available
  */
-static struct aa_profile *x_to_profile(struct aa_profile *profile,
-                                      const char *name, u32 xindex)
+static struct aa_label *x_to_label(struct aa_profile *profile,
+                                  const char *name, u32 xindex,
+                                  const char **lookupname,
+                                  const char **info)
 {
-       struct aa_profile *new_profile = NULL;
-       struct aa_namespace *ns = profile->ns;
+       struct aa_label *new = NULL;
+       struct aa_ns *ns = profile->ns;
        u32 xtype = xindex & AA_X_TYPE_MASK;
+       const char *stack = NULL;
 
        switch (xtype) {
        case AA_X_NONE:
                /* fail exec unless ix || ux fallback - handled by caller */
-               return NULL;
+               *lookupname = NULL;
+               break;
+       case AA_X_TABLE:
+               /* TODO: fix when perm mapping done at unload */
+               stack = profile->file.trans.table[xindex & AA_X_INDEX_MASK];
+               if (*stack != '&') {
+                       /* released by caller */
+                       new = x_table_lookup(profile, xindex, lookupname);
+                       stack = NULL;
+                       break;
+               }
+               /* fall through to X_NAME */
        case AA_X_NAME:
                if (xindex & AA_X_CHILD)
                        /* released by caller */
-                       new_profile = find_attach(ns, &profile->base.profiles,
-                                                 name);
+                       new = find_attach(ns, &profile->base.profiles,
+                                               name);
                else
                        /* released by caller */
-                       new_profile = find_attach(ns, &ns->base.profiles,
-                                                 name);
-               break;
-       case AA_X_TABLE:
-               /* released by caller */
-               new_profile = x_table_lookup(profile, xindex);
+                       new = find_attach(ns, &ns->base.profiles,
+                                               name);
+               *lookupname = name;
                break;
        }
 
+       if (!new) {
+               if (xindex & AA_X_INHERIT) {
+                       /* (p|c|n)ix - don't change profile but do
+                        * use the newest version
+                        */
+                       *info = "ix fallback";
+                       /* no profile && no error */
+                       new = aa_get_newest_label(&profile->label);
+               } else if (xindex & AA_X_UNCONFINED) {
+                       new = aa_get_newest_label(ns_unconfined(profile->ns));
+                       *info = "ux fallback";
+               }
+       }
+
+       if (new && stack) {
+               /* base the stack on post domain transition */
+               struct aa_label *base = new;
+               new = aa_label_parse(base, stack, GFP_ATOMIC, true, false);
+               if (IS_ERR(new))
+                       new = NULL;
+               aa_put_label(base);
+       }
+
        /* released by caller */
-       return new_profile;
+       return new;
+}
+
+static struct aa_label *profile_transition(struct aa_profile *profile,
+                                          const char *name,
+                                          struct path_cond *cond,
+                                          bool *secure_exec)
+{
+       struct aa_label *new = NULL;
+       const char *info = NULL;
+       unsigned int state = profile->file.start;
+       struct aa_perms perms = {};
+       const char *target = NULL;
+       int error = 0;
+       
+       if (profile_unconfined(profile)) {
+               new = find_attach(profile->ns, &profile->ns->base.profiles,
+                                 name);
+               if (new) {
+                       AA_DEBUG("unconfined attached to new label");
+
+                       return new;
+               }
+               AA_DEBUG("unconfined exec no attachment");
+
+               return aa_get_newest_label(&profile->label);
+       }
+
+       /* find exec permissions for name */
+       state = aa_str_perms(profile->file.dfa, state, name, cond, &perms);
+       if (perms.allow & MAY_EXEC) {
+               /* exec permission determine how to transition */
+               new = x_to_label(profile, name, perms.xindex, &target, &info);
+               if (new == &profile->label && info) {
+                       /* hack ix fallback - improve how this is detected */
+                       goto audit;
+               } else if (!new) {
+                       error = -EACCES;
+                       info = "profile transition not found";
+                       /* remove MAY_EXEC to audit as failure */
+                       perms.allow &= ~MAY_EXEC;
+               }
+       } else if (COMPLAIN_MODE(profile)) {
+               /* no exec permission - learning mode */
+               struct aa_profile *new_profile = aa_null_profile(profile, false,
+                                                             name, GFP_ATOMIC);
+               if (!new_profile) {
+                       error = -ENOMEM;
+                       info = "could not create null profile";
+               } else {
+                       error = -EACCES;
+                       new = &new_profile->label;
+               }
+               perms.xindex |= AA_X_UNSAFE;
+       } else
+               /* fail exec */
+               error = -EACCES;
+
+       if (!new)
+               goto audit;
+
+       if (!(perms.xindex & AA_X_UNSAFE)) {
+               if (DEBUG_ON) {
+                       dbg_printk("apparmor: scrubbing environment variables "
+                                  "for %s profile=", name);
+                       aa_label_printk(new, GFP_ATOMIC);
+                       dbg_printk("\n");
+               }
+               *secure_exec = true;
+       }
+
+audit:
+       aa_audit_file(profile, &perms, OP_EXEC, MAY_EXEC, name, target, new,
+                     cond->uid, info, error);
+       if (!new)
+               return ERR_PTR(error);
+
+       return new;
+}
+
+static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
+                         bool stack, const char *xname, struct path_cond *cond,
+                         bool *secure_exec)
+{
+       unsigned int state = profile->file.start;
+       struct aa_perms perms = {};
+       const char *info = "change_profile onexec";
+       int error = -EACCES;
+
+       if (profile_unconfined(profile)) {
+               /* change_profile on exec already granted */
+               /*
+                * NOTE: Domain transitions from unconfined are allowed
+                * even when no_new_privs is set because this aways results
+                * in a further reduction of permissions.
+                */
+               return 0;
+       }
+
+       /* find exec permissions for name */
+       state = aa_str_perms(profile->file.dfa, state, xname, cond, &perms);
+       if (!(perms.allow & AA_MAY_ONEXEC)) {
+               info = "no change_onexec valid for executable";
+               goto audit;
+       }
+       /* test if this exec can be paired with change_profile onexec.
+        * onexec permission is linked to exec with a standard pairing
+        * exec\0change_profile
+        */
+       state = aa_dfa_null_transition(profile->file.dfa, state);
+       error = change_profile_perms(profile, onexec, stack, AA_MAY_ONEXEC,
+                                    state, &perms);
+       if (error)
+               goto audit;
+
+       if (!(perms.xindex & AA_X_UNSAFE)) {
+               if (DEBUG_ON) {
+                       dbg_printk("appaarmor: scrubbing environment "
+                                  "variables for %s label=", xname);
+                       aa_label_printk(onexec, GFP_ATOMIC);
+                       dbg_printk("\n");
+               }
+               *secure_exec = true;
+       }
+
+audit:
+       return aa_audit_file(profile, &perms, OP_EXEC, AA_MAY_ONEXEC, xname,
+                            NULL, onexec, cond->uid, info, error);
+}
+
+/* ensure none ns domain transitions are correctly applied with onexec */
+
+static struct aa_label *handle_onexec(struct aa_label *label,
+                                     struct aa_label *onexec, bool stack,
+                                     const char *xname,
+                                     struct path_cond *cond,
+                                     bool *unsafe)
+{
+       struct aa_profile *profile;
+       struct aa_label *new;
+       int error;
+
+       if (!stack) {
+               error = fn_for_each_in_ns(label, profile,
+                               profile_onexec(profile, onexec, stack,
+                                              xname, cond, unsafe));
+               if (error)
+                       return ERR_PTR(error);
+               new = fn_label_build_in_ns(label, profile, GFP_ATOMIC,
+                                          aa_get_newest_label(onexec),
+                                          profile_transition(profile, xname,
+                                                             cond, unsafe));
+       } else {
+               /* TODO: determine how much we want to losen this */
+               error = fn_for_each_in_ns(label, profile,
+                               profile_onexec(profile, onexec, stack, xname,
+                                              cond, unsafe));
+               if (error)
+                       return ERR_PTR(error);
+               new = fn_label_build_in_ns(label, profile, GFP_ATOMIC,
+                                          aa_label_merge(label, onexec,
+                                                         GFP_ATOMIC),
+                                          profile_transition(profile, xname,
+                                                             cond, unsafe));
+       }
+
+       if (new)
+               return new;
+
+       error = fn_for_each_in_ns(label, profile,
+                       aa_audit_file(profile, &nullperms, OP_CHANGE_ONEXEC,
+                                     AA_MAY_ONEXEC, xname, NULL, onexec,
+                                     GLOBAL_ROOT_UID,
+                                     "failed to build target label", -ENOMEM));
+       return ERR_PTR(error);
 }
 
 /**
@@ -333,139 +666,76 @@ static struct aa_profile *x_to_profile(struct aa_profile *profile,
  * @bprm: binprm for the exec  (NOT NULL)
  *
  * Returns: %0 or error on failure
+ *
+ * TODO: once the other paths are done see if we can't refactor into a fn
  */
 int apparmor_bprm_set_creds(struct linux_binprm *bprm)
 {
-       struct aa_task_cxt *cxt;
-       struct aa_profile *profile, *new_profile = NULL;
-       struct aa_namespace *ns;
+       struct aa_task_ctx *ctx;
+       struct aa_label *label, *new = NULL;
+       struct aa_profile *profile;
        char *buffer = NULL;
-       unsigned int state;
-       struct file_perms perms = {};
+       const char *xname = NULL;
+       const char *info = NULL;
+       int error = 0;
+       bool unsafe = false;
        struct path_cond cond = {
                file_inode(bprm->file)->i_uid,
                file_inode(bprm->file)->i_mode
        };
-       const char *name = NULL, *target = NULL, *info = NULL;
-       int error = 0;
 
        if (bprm->cred_prepared)
                return 0;
 
-       cxt = cred_cxt(bprm->cred);
-       BUG_ON(!cxt);
+       ctx = cred_ctx(bprm->cred);
+       AA_BUG(!ctx);
 
-       profile = aa_get_newest_profile(cxt->profile);
-       /*
-        * get the namespace from the replacement profile as replacement
-        * can change the namespace
-        */
-       ns = profile->ns;
-       state = profile->file.start;
+       label = aa_get_newest_label(ctx->label);
+       profile = labels_profile(label);
 
-       /* buffer freed below, name is pointer into buffer */
-       error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer,
-                            &name, &info);
+       /* buffer freed below, xname is pointer into buffer */
+       get_buffers(buffer);
+       error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
+                            &xname, &info, profile->disconnected);
        if (error) {
-               if (unconfined(profile) ||
-                   (profile->flags & PFLAG_IX_ON_NAME_ERROR))
+               if (profile_unconfined(profile) ||
+                   (profile->label.flags & FLAG_IX_ON_NAME_ERROR))
                        error = 0;
-               name = bprm->filename;
+               xname = bprm->filename;
                goto audit;
        }
 
-       /* Test for onexec first as onexec directives override other
-        * x transitions.
-        */
-       if (unconfined(profile)) {
-               /* unconfined task */
-               if (cxt->onexec)
-                       /* change_profile on exec already been granted */
-                       new_profile = aa_get_profile(cxt->onexec);
-               else
-                       new_profile = find_attach(ns, &ns->base.profiles, name);
-               if (!new_profile)
-                       goto cleanup;
-               /*
-                * NOTE: Domain transitions from unconfined are allowed
-                * even when no_new_privs is set because this aways results
-                * in a further reduction of permissions.
-                */
-               goto apply;
-       }
-
-       /* find exec permissions for name */
-       state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms);
-       if (cxt->onexec) {
-               struct file_perms cp;
-               info = "change_profile onexec";
-               if (!(perms.allow & AA_MAY_ONEXEC))
-                       goto audit;
-
-               /* test if this exec can be paired with change_profile onexec.
-                * onexec permission is linked to exec with a standard pairing
-                * exec\0change_profile
-                */
-               state = aa_dfa_null_transition(profile->file.dfa, state);
-               cp = change_profile_perms(profile, cxt->onexec->ns,
-                                         cxt->onexec->base.name,
-                                         AA_MAY_ONEXEC, state);
-
-               if (!(cp.allow & AA_MAY_ONEXEC))
-                       goto audit;
-               new_profile = aa_get_newest_profile(cxt->onexec);
-               goto apply;
+       /* Test for onexec first as onexec override other x transitions. */
+       if (ctx->onexec)
+               new = handle_onexec(label, ctx->onexec, ctx->token, xname,
+                                   &cond, &unsafe);
+       else
+               new = fn_label_build(label, profile, GFP_ATOMIC,
+                               profile_transition(profile, xname, &cond,
+                                                  &unsafe));
+
+       AA_BUG(!new);
+       if (IS_ERR(new)) {
+               error = PTR_ERR(new);
+               goto done;
+       } else if (!new) {
+               error = -ENOMEM;
+               goto done;
        }
 
-       if (perms.allow & MAY_EXEC) {
-               /* exec permission determine how to transition */
-               new_profile = x_to_profile(profile, name, perms.xindex);
-               if (!new_profile) {
-                       if (perms.xindex & AA_X_INHERIT) {
-                               /* (p|c|n)ix - don't change profile but do
-                                * use the newest version, which was picked
-                                * up above when getting profile
-                                */
-                               info = "ix fallback";
-                               new_profile = aa_get_profile(profile);
-                               goto x_clear;
-                       } else if (perms.xindex & AA_X_UNCONFINED) {
-                               new_profile = aa_get_newest_profile(ns->unconfined);
-                               info = "ux fallback";
-                       } else {
-                               error = -ENOENT;
-                               info = "profile not found";
-                               /* remove MAY_EXEC to audit as failure */
-                               perms.allow &= ~MAY_EXEC;
-                       }
-               }
-       } else if (COMPLAIN_MODE(profile)) {
-               /* no exec permission - are we in learning mode */
-               new_profile = aa_new_null_profile(profile, 0);
-               if (!new_profile) {
-                       error = -ENOMEM;
-                       info = "could not create null profile";
-               } else {
-                       error = -EACCES;
-                       target = new_profile->base.hname;
-               }
-               perms.xindex |= AA_X_UNSAFE;
-       } else
-               /* fail exec */
-               error = -EACCES;
-
-       /*
-        * Policy has specified a domain transition, if no_new_privs then
-        * fail the exec.
+       /* Policy has specified a domain transitions. if no_new_privs and
+        * confined and not transitioning to the current domain fail.
+        *
+        * NOTE: Domain transitions from unconfined and to stritly stacked
+        * subsets are allowed even when no_new_privs is set because this
+        * aways results in a further reduction of permissions.
         */
-       if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) {
-               aa_put_profile(new_profile);
+       if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS &&
+           !unconfined(label) && !aa_label_is_subset(new, label)) {
                error = -EPERM;
-               goto cleanup;
-       }
-
-       if (!new_profile)
+               info = "no new privs";
                goto audit;
+       }
 
        if (bprm->unsafe & LSM_UNSAFE_SHARE) {
                /* FIXME: currently don't mediate shared state */
@@ -473,52 +743,53 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
        }
 
        if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
-               error = may_change_ptraced_domain(new_profile);
-               if (error) {
-                       aa_put_profile(new_profile);
+               /* TODO: test needs to be profile of label to new */
+               error = may_change_ptraced_domain(new, &info);
+               if (error)
                        goto audit;
-               }
        }
 
-       /* Determine if secure exec is needed.
-        * Can be at this point for the following reasons:
-        * 1. unconfined switching to confined
-        * 2. confined switching to different confinement
-        * 3. confined switching to unconfined
-        *
-        * Cases 2 and 3 are marked as requiring secure exec
-        * (unless policy specified "unsafe exec")
-        *
-        * bprm->unsafe is used to cache the AA_X_UNSAFE permission
-        * to avoid having to recompute in secureexec
-        */
-       if (!(perms.xindex & AA_X_UNSAFE)) {
-               AA_DEBUG("scrubbing environment variables for %s profile=%s\n",
-                        name, new_profile->base.hname);
+       if (unsafe) {
+               if (DEBUG_ON) {
+                       dbg_printk("scrubbing environment variables for %s "
+                                  "label=", xname);
+                       aa_label_printk(new, GFP_ATOMIC);
+                       dbg_printk("\n");
+               }
                bprm->unsafe |= AA_SECURE_X_NEEDED;
        }
-apply:
-       target = new_profile->base.hname;
-       /* when transitioning profiles clear unsafe personality bits */
-       bprm->per_clear |= PER_CLEAR_ON_SETID;
-
-x_clear:
-       aa_put_profile(cxt->profile);
-       /* transfer new profile reference will be released when cxt is freed */
-       cxt->profile = new_profile;
 
-       /* clear out all temporary/transitional state from the context */
-       aa_clear_task_cxt_trans(cxt);
+       if (label != new) {
+               /* when transitioning clear unsafe personality bits */
+               if (DEBUG_ON) {
+                       dbg_printk("apparmor: clearing unsafe personality "
+                                  "bits. %s label=", xname);
+                       aa_label_printk(new, GFP_ATOMIC);
+                       dbg_printk("\n");
+               }
+               bprm->per_clear |= PER_CLEAR_ON_SETID;
+       }
+       aa_put_label(ctx->label);
+       /* transfer reference, released when ctx is freed */
+       ctx->label = new;
 
-audit:
-       error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC,
-                             name, target, cond.uid, info, error);
+done:
+       /* clear out temporary/transitional state from the context */
+       aa_clear_task_ctx_trans(ctx);
 
-cleanup:
-       aa_put_profile(profile);
-       kfree(buffer);
+       aa_put_label(label);
+       put_buffers(buffer);
 
        return error;
+
+audit:
+       error = fn_for_each(label, profile,
+                       aa_audit_file(profile, &nullperms, OP_EXEC, MAY_EXEC,
+                                     xname, NULL, new,
+                                     file_inode(bprm->file)->i_uid, info,
+                                     error));
+       aa_put_label(new);
+       goto done;
 }
 
 /**
@@ -538,53 +809,146 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm)
        return 0;
 }
 
-/**
- * apparmor_bprm_committing_creds - do task cleanup on committing new creds
- * @bprm: binprm for the exec  (NOT NULL)
+/*
+ * Functions for self directed profile change
  */
-void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
-{
-       struct aa_profile *profile = __aa_current_profile();
-       struct aa_task_cxt *new_cxt = cred_cxt(bprm->cred);
-
-       /* bail out if unconfined or not changing profile */
-       if ((new_cxt->profile == profile) ||
-           (unconfined(new_cxt->profile)))
-               return;
-
-       current->pdeath_signal = 0;
 
-       /* reset soft limits and set hard limits for the new profile */
-       __aa_transition_rlimits(profile, new_cxt->profile);
-}
 
-/**
- * apparmor_bprm_commited_cred - do cleanup after new creds committed
- * @bprm: binprm for the exec  (NOT NULL)
+/* helper fn for change_hat
+ *
+ * Returns: label for hat transition OR ERR_PTR.  Does NOT return NULL
  */
-void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
+static struct aa_label *build_change_hat(struct aa_profile *profile,
+                                        const char *name, bool sibling)
 {
-       /* TODO: cleanup signals - ipc mediation */
-       return;
-}
+       struct aa_profile *root, *hat = NULL;
+       const char *info = NULL;
+       int error = 0;
 
-/*
- * Functions for self directed profile change
- */
+       if (sibling && PROFILE_IS_HAT(profile)) {
+               root = aa_get_profile_rcu(&profile->parent);
+       } else if (!sibling && !PROFILE_IS_HAT(profile)) {
+               root = aa_get_profile(profile);
+       } else {
+               info = "conflicting target types";
+               error = -EPERM;
+               goto audit;
+       }
 
-/**
- * new_compound_name - create an hname with @n2 appended to @n1
- * @n1: base of hname  (NOT NULL)
- * @n2: name to append (NOT NULL)
+       hat = aa_find_child(root, name);
+       if (!hat) {
+               error = -ENOENT;
+               if (COMPLAIN_MODE(profile)) {
+                       hat = aa_null_profile(profile, true, name, GFP_KERNEL);
+                       if (!hat) {
+                               info = "failed null profile create";
+                               error = -ENOMEM;
+                       }
+               }
+       }
+       aa_put_profile(root);
+
+audit:
+       aa_audit_file(profile, &nullperms, OP_CHANGE_HAT, AA_MAY_CHANGEHAT,
+                     name, hat ? hat->base.hname : NULL, hat ? &hat->label : NULL, GLOBAL_ROOT_UID,
+                     NULL, error);
+       if (!hat || (error && error != -ENOENT))
+               return ERR_PTR(error);
+       /* if hat && error - complain mode, already audited and we adjust for
+        * complain mode allow by returning hat->label
+        */
+       return &hat->label;
+}
+
+/* helper fn for changing into a hat
  *
- * Returns: new name or NULL on error
+ * Returns: label for hat transition or ERR_PTR. Does not return NULL
  */
-static char *new_compound_name(const char *n1, const char *n2)
+static struct aa_label *change_hat(struct aa_label *label, const char *hats[],
+                                  int count, bool permtest)
 {
-       char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL);
-       if (name)
-               sprintf(name, "%s//%s", n1, n2);
-       return name;
+       struct aa_profile *profile, *root, *hat = NULL;
+       struct aa_label *new;
+       struct label_it it;
+       bool sibling = false;
+       const char *name, *info = NULL;
+       int i, error;
+
+       AA_BUG(!label);
+       AA_BUG(!hats);
+       AA_BUG(count < 1);
+
+       if (PROFILE_IS_HAT(labels_profile(label)))
+               sibling = true;
+
+       /*find first matching hat */
+       for (i = 0; i < count && !hat; i++) {
+               name = hats[i];
+               label_for_each_in_ns(it, labels_ns(label), label, profile) {
+                       if (sibling && PROFILE_IS_HAT(profile)) {
+                               root = aa_get_profile_rcu(&profile->parent);
+                       } else if (!sibling && !PROFILE_IS_HAT(profile)) {
+                               root = aa_get_profile(profile);
+                       } else {        /* conflicting change type */
+                               info = "conflicting targets types";
+                               error = -EPERM;
+                               goto fail;
+                       }
+                       hat = aa_find_child(root, name);
+                       aa_put_profile(root);
+                       if (!hat) {
+                               if (!COMPLAIN_MODE(profile))
+                                       goto outer_continue;
+                               /* complain mode succeed as if hat */
+                       } else if (!PROFILE_IS_HAT(hat)) {
+                               info = "target not hat";
+                               error = -EPERM;
+                               aa_put_profile(hat);
+                               goto fail;
+                       }
+                       aa_put_profile(hat);
+               }
+               /* found a hat for all profiles in ns */
+               goto build;
+       outer_continue: ;
+       }
+       /* no hats that match, find appropriate error
+        *
+        * In complain mode audit of the failure is based off of the first
+        * hat supplied.  This is done due how userspace interacts with
+        * change_hat.
+        */
+       name = NULL;
+       label_for_each_in_ns(it, labels_ns(label), label, profile) {
+               if (!list_empty(&profile->base.profiles)) {
+                       info = "hat not found";
+                       error = -ENOENT;
+                       goto fail;
+               }
+       }
+       info = "no hats defined";
+       error = -ECHILD;
+
+fail:
+       fn_for_each_in_ns(label, profile,
+               /* no target as it has failed to be found or built */
+               /* TODO: get rid of GLOBAL_ROOT_UID */
+               aa_audit_file(profile, &nullperms, OP_CHANGE_HAT,
+                             AA_MAY_CHANGEHAT, name, NULL, NULL,
+                             GLOBAL_ROOT_UID, info, error));
+       return (ERR_PTR(error));
+
+build:
+       new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
+                                  build_change_hat(profile, name, sibling),
+                                  aa_get_label(&profile->label));
+       if (!new) {
+               info = "label build failed";
+               error = -ENOMEM;
+               goto fail;
+       } /* else if (IS_ERR) build_change_hat has logged error so return new */
+
+       return new;
 }
 
 /**
@@ -594,22 +958,24 @@ static char *new_compound_name(const char *n1, const char *n2)
  * @token: magic value to validate the hat change
  * @permtest: true if this is just a permission test
  *
+ * Returns %0 on success, error otherwise.
+ *
  * Change to the first profile specified in @hats that exists, and store
  * the @hat_magic in the current task context.  If the count == 0 and the
  * @token matches that stored in the current task context, return to the
  * top level profile.
  *
- * Returns %0 on success, error otherwise.
+ * change_hat only applies to profiles in the current ns, and each profile
+ * in the ns must make the same transition otherwise change_hat will fail.
  */
 int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
 {
        const struct cred *cred;
-       struct aa_task_cxt *cxt;
-       struct aa_profile *profile, *previous_profile, *hat = NULL;
-       char *name = NULL;
-       int i;
-       struct file_perms perms = {};
-       const char *target = NULL, *info = NULL;
+       struct aa_task_ctx *ctx;
+       struct aa_label *label, *previous, *new = NULL, *target = NULL;
+       struct aa_profile *profile;
+       struct aa_perms perms = {};
+       const char *info = NULL;
        int error = 0;
 
        /*
@@ -617,121 +983,102 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
         * There is no exception for unconfined as change_hat is not
         * available.
         */
-       if (task_no_new_privs(current))
+       if (task_no_new_privs(current)) {
+               /* not an apparmor denial per se, so don't log it */
+               AA_DEBUG("no_new_privs - chanage_hat denied");
                return -EPERM;
+       }
 
        /* released below */
        cred = get_current_cred();
-       cxt = cred_cxt(cred);
-       profile = aa_cred_profile(cred);
-       previous_profile = cxt->previous;
+       ctx = cred_ctx(cred);
+       label = aa_get_newest_cred_label(cred);
+       previous = aa_get_newest_label(ctx->previous);
 
-       if (unconfined(profile)) {
-               info = "unconfined";
+       if (unconfined(label)) {
+               info = "unconfined can not change_hat";
                error = -EPERM;
-               goto audit;
+               goto fail;
        }
 
        if (count) {
-               /* attempting to change into a new hat or switch to a sibling */
-               struct aa_profile *root;
-               if (PROFILE_IS_HAT(profile))
-                       root = aa_get_profile_rcu(&profile->parent);
-               else
-                       root = aa_get_profile(profile);
-
-               /* find first matching hat */
-               for (i = 0; i < count && !hat; i++)
-                       /* released below */
-                       hat = aa_find_child(root, hats[i]);
-               if (!hat) {
-                       if (!COMPLAIN_MODE(root) || permtest) {
-                               if (list_empty(&root->base.profiles))
-                                       error = -ECHILD;
-                               else
-                                       error = -ENOENT;
-                               aa_put_profile(root);
-                               goto out;
-                       }
+               new = change_hat(label, hats, count, permtest);
+               AA_BUG(!new);
+               if (IS_ERR(new)) {
+                       error = PTR_ERR(new);
+                       new = NULL;
+                       /* already audited */
+                       goto out;
+               }
 
-                       /*
-                        * In complain mode and failed to match any hats.
-                        * Audit the failure is based off of the first hat
-                        * supplied.  This is done due how userspace
-                        * interacts with change_hat.
-                        *
-                        * TODO: Add logging of all failed hats
-                        */
+               error = may_change_ptraced_domain(new, &info);
+               if (error)
+                       goto fail;
 
-                       /* freed below */
-                       name = new_compound_name(root->base.hname, hats[0]);
-                       aa_put_profile(root);
-                       target = name;
-                       /* released below */
-                       hat = aa_new_null_profile(profile, 1);
-                       if (!hat) {
-                               info = "failed null profile create";
-                               error = -ENOMEM;
-                               goto audit;
-                       }
-               } else {
-                       aa_put_profile(root);
-                       target = hat->base.hname;
-                       if (!PROFILE_IS_HAT(hat)) {
-                               info = "target not hat";
-                               error = -EPERM;
-                               goto audit;
-                       }
-               }
+               if (permtest)
+                       goto out;
 
-               error = may_change_ptraced_domain(hat);
+               target = new;
+               error = aa_set_current_hat(new, token);
+               if (error == -EACCES)
+                       /* kill task in case of brute force attacks */
+                       goto kill;
+       } else if (previous && !permtest) {
+               /* Return to saved label.  Kill task if restore fails
+                * to avoid brute force attacks
+                */
+               target = previous;
+               error = aa_restore_previous_label(token);
                if (error) {
-                       info = "ptraced";
-                       error = -EPERM;
-                       goto audit;
-               }
-
-               if (!permtest) {
-                       error = aa_set_current_hat(hat, token);
                        if (error == -EACCES)
-                               /* kill task in case of brute force attacks */
-                               perms.kill = AA_MAY_CHANGEHAT;
-                       else if (name && !error)
-                               /* reset error for learning of new hats */
-                               error = -ENOENT;
+                               goto kill;
+                       goto fail;
                }
-       } else if (previous_profile) {
-               /* Return to saved profile.  Kill task if restore fails
-                * to avoid brute force attacks
-                */
-               target = previous_profile->base.hname;
-               error = aa_restore_previous_profile(token);
-               perms.kill = AA_MAY_CHANGEHAT;
-       } else
-               /* ignore restores when there is no saved profile */
-               goto out;
-
-audit:
-       if (!permtest)
-               error = aa_audit_file(profile, &perms, GFP_KERNEL,
-                                     OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL,
-                                     target, GLOBAL_ROOT_UID, info, error);
+       } /* else ignore permtest && restores when there is no saved profile */
 
 out:
-       aa_put_profile(hat);
-       kfree(name);
+       aa_put_label(new);
+       aa_put_label(previous);
+       aa_put_label(label);
        put_cred(cred);
 
        return error;
+
+kill:
+       info = "failed token match";
+       perms.kill = AA_MAY_CHANGEHAT;
+
+fail:
+       fn_for_each_in_ns(label, profile,
+               aa_audit_file(profile, &perms, OP_CHANGE_HAT, AA_MAY_CHANGEHAT,
+                             NULL, NULL, target, GLOBAL_ROOT_UID, info, error));
+
+       goto out;
+}
+
+
+static int change_profile_perms_wrapper(const char *op, const char *name,
+                                       struct aa_profile *profile,
+                                       struct aa_label *target, bool stack,
+                                       u32 request, struct aa_perms *perms)
+{
+       int error = change_profile_perms(profile, target,
+                                        stack, request,
+                                        profile->file.start, perms);
+       if (error)
+               error = aa_audit_file(profile, perms, op, request, name,
+                                     NULL, target, GLOBAL_ROOT_UID, NULL,
+                                     error);
+
+       return error;
 }
 
 /**
  * aa_change_profile - perform a one-way profile transition
- * @ns_name: name of the profile namespace to change to (MAYBE NULL)
- * @hname: name of profile to change to (MAYBE NULL)
+ * @fqname: name of profile may include namespace (NOT NULL)
  * @onexec: whether this transition is to take place immediately or at exec
  * @permtest: true if this is just a permission test
- *
+ * @stack: true if this call is to stack on top of current domain
  * Change to new profile @name.  Unlike with hats, there is no way
  * to change back.  If @name isn't specified the current profile name is
  * used.
@@ -740,111 +1087,144 @@ out:
  *
  * Returns %0 on success, error otherwise.
  */
-int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
-                     bool permtest)
+int aa_change_profile(const char *fqname, bool onexec,
+                     bool permtest, bool stack)
 {
-       const struct cred *cred;
-       struct aa_profile *profile, *target = NULL;
-       struct aa_namespace *ns = NULL;
-       struct file_perms perms = {};
-       const char *name = NULL, *info = NULL;
-       int op, error = 0;
+       struct aa_label *label, *new = NULL, *target = NULL;
+       struct aa_profile *profile;
+       struct aa_perms perms = {};
+       const char *info = NULL;
+       const char *auditname = fqname;         /* retain leading & if stack */
+       int error = 0;
+       char *op;
        u32 request;
 
-       if (!hname && !ns_name)
+       if (!fqname || !*fqname) {
+               AA_DEBUG("no profile name");
                return -EINVAL;
+       }
 
        if (onexec) {
                request = AA_MAY_ONEXEC;
-               op = OP_CHANGE_ONEXEC;
+               if (stack)
+                       op = OP_STACK_ONEXEC;
+               else
+                       op = OP_CHANGE_ONEXEC;
        } else {
                request = AA_MAY_CHANGE_PROFILE;
-               op = OP_CHANGE_PROFILE;
+               if (stack)
+                       op = OP_STACK;
+               else
+                       op = OP_CHANGE_PROFILE;
        }
 
-       cred = get_current_cred();
-       profile = aa_cred_profile(cred);
+       label = aa_get_current_label();
 
-       /*
-        * Fail explicitly requested domain transitions if no_new_privs
-        * and not unconfined.
-        * Domain transitions from unconfined are allowed even when
-        * no_new_privs is set because this aways results in a reduction
-        * of permissions.
-        */
-       if (task_no_new_privs(current) && !unconfined(profile)) {
-               put_cred(cred);
-               return -EPERM;
+       if (*fqname == '&') {
+               stack = true;
+               /* don't have label_parse() do stacking */
+               fqname++;
        }
-
-       if (ns_name) {
+       target = aa_label_parse(label, fqname, GFP_KERNEL, true, false);
+       if (IS_ERR(target)) {
+               struct aa_profile *tprofile;
+
+               info = "label not found";
+               error = PTR_ERR(target);
+               target = NULL;
+               /* TODO: fixme using labels_profile is not right - do profile
+                  per complain profile ??? */
+               if (permtest || !COMPLAIN_MODE(labels_profile(label)))
+                       goto audit;
                /* released below */
-               ns = aa_find_namespace(profile->ns, ns_name);
-               if (!ns) {
-                       /* we don't create new namespace in complain mode */
-                       name = ns_name;
-                       info = "namespace not found";
-                       error = -ENOENT;
+               tprofile = aa_null_profile(labels_profile(label), false, fqname, GFP_KERNEL);
+               if (!tprofile) {
+                       info = "failed null profile create";
+                       error = -ENOMEM;
                        goto audit;
                }
-       } else
-               /* released below */
-               ns = aa_get_namespace(profile->ns);
-
-       /* if the name was not specified, use the name of the current profile */
-       if (!hname) {
-               if (unconfined(profile))
-                       hname = ns->unconfined->base.hname;
-               else
-                       hname = profile->base.hname;
+               target = &tprofile->label;
+               goto check;
        }
 
-       perms = change_profile_perms(profile, ns, hname, request,
-                                    profile->file.start);
-       if (!(perms.allow & request)) {
-               error = -EACCES;
+       /*
+        * Fail explicitly requested domain transitions when no_new_privs
+        * and not unconfined OR the transition results in a stack on
+        * the current label.
+        * Stacking domain transitions and transitions from unconfined are
+        * allowed even when no_new_privs is set because this aways results
+        * in a reduction of permissions.
+        */
+       if (task_no_new_privs(current) && !stack && !unconfined(label) &&
+           !aa_label_is_subset(target, label)) {
+               info = "no new privs";
+               error = -EPERM;
                goto audit;
        }
 
-       /* released below */
-       target = aa_lookup_profile(ns, hname);
-       if (!target) {
-               info = "profile not found";
-               error = -ENOENT;
-               if (permtest || !COMPLAIN_MODE(profile))
-                       goto audit;
-               /* released below */
-               target = aa_new_null_profile(profile, 0);
-               if (!target) {
-                       info = "failed null profile create";
-                       error = -ENOMEM;
-                       goto audit;
-               }
-       }
+       /* self directed transitions only apply to current policy ns */
+       /* TODO: currently requiring perms for stacking and straight change
+        *       stacking doesn't strictly need this. Determine how much
+        *       we want to loosen this restriction for stacking
+        */
+       /* if (!stack) { */
+       error = fn_for_each_in_ns(label, profile,
+                       change_profile_perms_wrapper(op, auditname,
+                                                    profile, target, stack,
+                                                    request, &perms));
+       if (error)
+               /* auditing done in change_profile_perms_wrapper */
+               goto out;
+
+       /* } */
 
+check:
        /* check if tracing task is allowed to trace target domain */
-       error = may_change_ptraced_domain(target);
-       if (error) {
-               info = "ptrace prevents transition";
+       error = may_change_ptraced_domain(target, &info);
+       if (error && !fn_for_each_in_ns(label, profile,
+                                       COMPLAIN_MODE(profile)))
                goto audit;
-       }
 
-       if (permtest)
+       /* TODO: add permission check to allow this
+       if (onexec && !current_is_single_threaded()) {
+               info = "not a single threaded task";
+               error = -EACCES;
                goto audit;
+       }
+       */
+       if (permtest)
+               goto out;
 
-       if (onexec)
-               error = aa_set_current_onexec(target);
-       else
-               error = aa_replace_current_profile(target);
+       if (!onexec) {
+               /* only transition profiles in the current ns */
+               if (stack)
+                       new = aa_label_merge(label, target, GFP_KERNEL);
+               else
+                       new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
+                                       aa_get_label(target),
+                                       aa_get_label(&profile->label));
+               if (IS_ERR_OR_NULL(new)) {
+                       info = "failed to build target label";
+                       error = PTR_ERR(new);
+                       new = NULL;
+                       perms.allow = 0;
+                       goto audit;
+               }
+               error = aa_replace_current_label(new);
+       } else
+               /* full transition will be built in exec path */
+               error = aa_set_current_onexec(target, stack);
 
 audit:
-       if (!permtest)
-               error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request,
-                                     name, hname, GLOBAL_ROOT_UID, info, error);
+       error = fn_for_each_in_ns(label, profile,
+                       aa_audit_file(profile, &perms, op, request, auditname,
+                                     NULL, new ? new : target,
+                                     GLOBAL_ROOT_UID, info, error));
 
-       aa_put_namespace(ns);
-       aa_put_profile(target);
-       put_cred(cred);
+out:
+       aa_put_label(new);
+       aa_put_label(target);
+       aa_put_label(label);
 
        return error;
 }
index 913f377a038a672f8cdc07c0aaa23721b47321b2..e04dd980805ba0944e8f4c650f128badbadba613 100644 (file)
  * License.
  */
 
+#include <linux/tty.h>
+#include <linux/fdtable.h>
+#include <linux/file.h>
+
+#include "include/af_unix.h"
 #include "include/apparmor.h"
 #include "include/audit.h"
+#include "include/context.h"
 #include "include/file.h"
 #include "include/match.h"
 #include "include/path.h"
 #include "include/policy.h"
+#include "include/label.h"
 
-struct file_perms nullperms;
+static u32 map_mask_to_chr_mask(u32 mask)
+{
+       u32 m = mask & PERMS_CHRS_MASK;
+       if (mask & AA_MAY_GETATTR)
+               m |= MAY_READ;
+       if (mask & (AA_MAY_SETATTR | AA_MAY_CHMOD | AA_MAY_CHOWN))
+               m |= MAY_WRITE;
 
+       return m;
+}
 
 /**
  * audit_file_mask - convert mask to permission string
@@ -31,29 +46,7 @@ static void audit_file_mask(struct audit_buffer *ab, u32 mask)
 {
        char str[10];
 
-       char *m = str;
-
-       if (mask & AA_EXEC_MMAP)
-               *m++ = 'm';
-       if (mask & (MAY_READ | AA_MAY_META_READ))
-               *m++ = 'r';
-       if (mask & (MAY_WRITE | AA_MAY_META_WRITE | AA_MAY_CHMOD |
-                   AA_MAY_CHOWN))
-               *m++ = 'w';
-       else if (mask & MAY_APPEND)
-               *m++ = 'a';
-       if (mask & AA_MAY_CREATE)
-               *m++ = 'c';
-       if (mask & AA_MAY_DELETE)
-               *m++ = 'd';
-       if (mask & AA_MAY_LINK)
-               *m++ = 'l';
-       if (mask & AA_MAY_LOCK)
-               *m++ = 'k';
-       if (mask & MAY_EXEC)
-               *m++ = 'x';
-       *m = '\0';
-
+       aa_perm_mask_to_str(str, aa_file_perm_chrs, map_mask_to_chr_mask(mask));
        audit_log_string(ab, str);
 }
 
@@ -67,24 +60,28 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
        struct common_audit_data *sa = va;
        kuid_t fsuid = current_fsuid();
 
-       if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) {
+       if (aad(sa)->request & AA_AUDIT_FILE_MASK) {
                audit_log_format(ab, " requested_mask=");
-               audit_file_mask(ab, sa->aad->fs.request);
+               audit_file_mask(ab, aad(sa)->request);
        }
-       if (sa->aad->fs.denied & AA_AUDIT_FILE_MASK) {
+       if (aad(sa)->denied & AA_AUDIT_FILE_MASK) {
                audit_log_format(ab, " denied_mask=");
-               audit_file_mask(ab, sa->aad->fs.denied);
+               audit_file_mask(ab, aad(sa)->denied);
        }
-       if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) {
+       if (aad(sa)->request & AA_AUDIT_FILE_MASK) {
                audit_log_format(ab, " fsuid=%d",
                                 from_kuid(&init_user_ns, fsuid));
                audit_log_format(ab, " ouid=%d",
-                                from_kuid(&init_user_ns, sa->aad->fs.ouid));
+                                from_kuid(&init_user_ns, aad(sa)->fs.ouid));
        }
 
-       if (sa->aad->fs.target) {
+       if (aad(sa)->peer) {
+               audit_log_format(ab, " target=");
+               aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
+                               FLAG_VIEW_SUBNS, GFP_ATOMIC);
+       } else if (aad(sa)->fs.target) {
                audit_log_format(ab, " target=");
-               audit_log_untrustedstring(ab, sa->aad->fs.target);
+               audit_log_untrustedstring(ab, aad(sa)->fs.target);
        }
 }
 
@@ -92,65 +89,100 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
  * aa_audit_file - handle the auditing of file operations
  * @profile: the profile being enforced  (NOT NULL)
  * @perms: the permissions computed for the request (NOT NULL)
- * @gfp: allocation flags
  * @op: operation being mediated
  * @request: permissions requested
  * @name: name of object being mediated (MAYBE NULL)
  * @target: name of target (MAYBE NULL)
+ * @tlabel: target label (MAY BE NULL)
  * @ouid: object uid
  * @info: extra information message (MAYBE NULL)
  * @error: 0 if operation allowed else failure error code
  *
  * Returns: %0 or error on failure
  */
-int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
-                 gfp_t gfp, int op, u32 request, const char *name,
-                 const char *target, kuid_t ouid, const char *info, int error)
+int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
+                 const char *op, u32 request, const char *name,
+                 const char *target, struct aa_label *tlabel,
+                 kuid_t ouid, const char *info, int error)
 {
        int type = AUDIT_APPARMOR_AUTO;
-       struct common_audit_data sa;
-       struct apparmor_audit_data aad = {0,};
-       sa.type = LSM_AUDIT_DATA_NONE;
-       sa.aad = &aad;
-       aad.op = op,
-       aad.fs.request = request;
-       aad.name = name;
-       aad.fs.target = target;
-       aad.fs.ouid = ouid;
-       aad.info = info;
-       aad.error = error;
-
-       if (likely(!sa.aad->error)) {
+
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
+       aad(&sa)->request = request;
+       aad(&sa)->name = name;
+       aad(&sa)->fs.target = target;
+       aad(&sa)->peer = tlabel;
+       aad(&sa)->fs.ouid = ouid;
+       aad(&sa)->info = info;
+       aad(&sa)->error = error;
+       sa.u.tsk = NULL;
+
+       if (likely(!aad(&sa)->error)) {
                u32 mask = perms->audit;
 
                if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
                        mask = 0xffff;
 
                /* mask off perms that are not being force audited */
-               sa.aad->fs.request &= mask;
+               aad(&sa)->request &= mask;
 
-               if (likely(!sa.aad->fs.request))
+               if (likely(!aad(&sa)->request))
                        return 0;
                type = AUDIT_APPARMOR_AUDIT;
        } else {
                /* only report permissions that were denied */
-               sa.aad->fs.request = sa.aad->fs.request & ~perms->allow;
+               aad(&sa)->request = aad(&sa)->request & ~perms->allow;
+               AA_BUG(!aad(&sa)->request);
 
-               if (sa.aad->fs.request & perms->kill)
+               if (aad(&sa)->request & perms->kill)
                        type = AUDIT_APPARMOR_KILL;
 
                /* quiet known rejects, assumes quiet and kill do not overlap */
-               if ((sa.aad->fs.request & perms->quiet) &&
+               if ((aad(&sa)->request & perms->quiet) &&
                    AUDIT_MODE(profile) != AUDIT_NOQUIET &&
                    AUDIT_MODE(profile) != AUDIT_ALL)
-                       sa.aad->fs.request &= ~perms->quiet;
+                       aad(&sa)->request &= ~perms->quiet;
 
-               if (!sa.aad->fs.request)
-                       return COMPLAIN_MODE(profile) ? 0 : sa.aad->error;
+               if (!aad(&sa)->request)
+                       return aad(&sa)->error;
        }
 
-       sa.aad->fs.denied = sa.aad->fs.request & ~perms->allow;
-       return aa_audit(type, profile, gfp, &sa, file_audit_cb);
+       aad(&sa)->denied = aad(&sa)->request & ~perms->allow;
+       return aa_audit(type, profile, &sa, file_audit_cb);
+}
+
+/**
+ * is_deleted - test if a file has been completely unlinked
+ * @dentry: dentry of file to test for deletion  (NOT NULL)
+ *
+ * Returns: %1 if deleted else %0
+ */
+static inline bool is_deleted(struct dentry *dentry)
+{
+       if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0)
+               return 1;
+       return 0;
+}
+
+static int path_name(const char *op, struct aa_label *label, struct path *path,
+                    int flags, char *buffer, const char**name,
+                    struct path_cond *cond, u32 request, bool delegate_deleted)
+{
+       struct aa_profile *profile;
+       const char *info = NULL;
+       int error = aa_path_name(path, flags, buffer, name, &info,
+                                labels_profile(label)->disconnected);
+       if (error) {
+               if (error == -ENOENT && is_deleted(path->dentry) &&
+                   delegate_deleted)
+                       return 0;
+               fn_for_each_confined(label, profile,
+                       aa_audit_file(profile, &nullperms, op, request, *name,
+                                     NULL, NULL, cond->uid, info, error));
+               return error;
+       }
+
+       return 0;
 }
 
 /**
@@ -163,10 +195,11 @@ static u32 map_old_perms(u32 old)
 {
        u32 new = old & 0xf;
        if (old & MAY_READ)
-               new |= AA_MAY_META_READ;
+               new |= AA_MAY_GETATTR | AA_MAY_OPEN;
        if (old & MAY_WRITE)
-               new |= AA_MAY_META_WRITE | AA_MAY_CREATE | AA_MAY_DELETE |
-                       AA_MAY_CHMOD | AA_MAY_CHOWN;
+               new |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE |
+                       AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN |
+                       AA_MAY_DELETE;
        if (old & 0x10)
                new |= AA_MAY_LINK;
        /* the old mapping lock and link_subset flags where overlaid
@@ -181,7 +214,7 @@ static u32 map_old_perms(u32 old)
 }
 
 /**
- * compute_perms - convert dfa compressed perms to internal perms
+ * aa_compute_fperms - convert dfa compressed perms to internal perms
  * @dfa: dfa to compute perms for   (NOT NULL)
  * @state: state in dfa
  * @cond:  conditions to consider  (NOT NULL)
@@ -191,17 +224,21 @@ static u32 map_old_perms(u32 old)
  *
  * Returns: computed permission set
  */
-static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state,
-                                      struct path_cond *cond)
+struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state,
+                                 struct path_cond *cond)
 {
-       struct file_perms perms;
+       struct aa_perms perms;
 
        /* FIXME: change over to new dfa format
         * currently file perms are encoded in the dfa, new format
         * splits the permissions from the dfa.  This mapping can be
         * done at profile load
         */
-       perms.kill = 0;
+       perms.deny = 0;
+       perms.kill = perms.stop = 0;
+       perms.complain = perms.cond = 0;
+       perms.hide = 0;
+       perms.prompt = 0;
 
        if (uid_eq(current_fsuid(), cond->uid)) {
                perms.allow = map_old_perms(dfa_user_allow(dfa, state));
@@ -214,7 +251,7 @@ static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state,
                perms.quiet = map_old_perms(dfa_other_quiet(dfa, state));
                perms.xindex = dfa_other_xindex(dfa, state);
        }
-       perms.allow |= AA_MAY_META_READ;
+       perms.allow |= AA_MAY_GETATTR;
 
        /* change_profile wasn't determined by ownership in old mapping */
        if (ACCEPT_TABLE(dfa)[state] & 0x80000000)
@@ -237,37 +274,34 @@ static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state,
  */
 unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
                          const char *name, struct path_cond *cond,
-                         struct file_perms *perms)
+                         struct aa_perms *perms)
 {
        unsigned int state;
-       if (!dfa) {
-               *perms = nullperms;
-               return DFA_NOMATCH;
-       }
-
        state = aa_dfa_match(dfa, start, name);
-       *perms = compute_perms(dfa, state, cond);
+       *perms = aa_compute_fperms(dfa, state, cond);
 
        return state;
 }
 
-/**
- * is_deleted - test if a file has been completely unlinked
- * @dentry: dentry of file to test for deletion  (NOT NULL)
- *
- * Returns: %1 if deleted else %0
- */
-static inline bool is_deleted(struct dentry *dentry)
+int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name,
+                  u32 request, struct path_cond *cond, int flags,
+                  struct aa_perms *perms)
 {
-       if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0)
-               return 1;
-       return 0;
+       int e = 0;
+       if (profile_unconfined(profile) ||
+           ((flags & PATH_SOCK_COND) && !PROFILE_MEDIATES_AF(profile, AF_UNIX)))
+               return 0;
+       aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms);
+       if (request & ~perms->allow)
+               e = -EACCES;
+       return aa_audit_file(profile, perms, op, request, name, NULL, NULL,
+                            cond->uid, NULL, e);
 }
 
 /**
  * aa_path_perm - do permissions check & audit for @path
  * @op: operation being checked
- * @profile: profile being enforced  (NOT NULL)
+ * @label: profile being enforced  (NOT NULL)
  * @path: path to check permissions of  (NOT NULL)
  * @flags: any additional path flags beyond what the profile specifies
  * @request: requested permissions
@@ -275,35 +309,28 @@ static inline bool is_deleted(struct dentry *dentry)
  *
  * Returns: %0 else error if access denied or other error
  */
-int aa_path_perm(int op, struct aa_profile *profile, struct path *path,
+int aa_path_perm(const char *op, struct aa_label *label, struct path *path,
                 int flags, u32 request, struct path_cond *cond)
 {
+       struct aa_perms perms = {};
        char *buffer = NULL;
-       struct file_perms perms = {};
-       const char *name, *info = NULL;
+       const char *name;
+       struct aa_profile *profile;
        int error;
 
-       flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0);
-       error = aa_path_name(path, flags, &buffer, &name, &info);
-       if (error) {
-               if (error == -ENOENT && is_deleted(path->dentry)) {
-                       /* Access to open files that are deleted are
-                        * give a pass (implicit delegation)
-                        */
-                       error = 0;
-                       info = NULL;
-                       perms.allow = request;
-               }
-       } else {
-               aa_str_perms(profile->file.dfa, profile->file.start, name, cond,
-                            &perms);
-               if (request & ~perms.allow)
-                       error = -EACCES;
-       }
-       error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name,
-                             NULL, cond->uid, info, error);
-       kfree(buffer);
+       /* TODO: fix path lookup flags */
+       flags |= labels_profile(label)->path_flags |
+               (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0);
+       get_buffers(buffer);
+
+       error = path_name(op, label, path, flags, buffer, &name, cond,
+                         request, true);
+       if (!error)
+               error = fn_for_each_confined(label, profile,
+                               __aa_path_perm(op, profile, name, request, cond,
+                                              flags, &perms));
 
+       put_buffers(buffer);
        return error;
 }
 
@@ -327,65 +354,25 @@ static inline bool xindex_is_subset(u32 link, u32 target)
        return 1;
 }
 
-/**
- * aa_path_link - Handle hard link permission check
- * @profile: the profile being enforced  (NOT NULL)
- * @old_dentry: the target dentry  (NOT NULL)
- * @new_dir: directory the new link will be created in  (NOT NULL)
- * @new_dentry: the link being created  (NOT NULL)
- *
- * Handle the permission test for a link & target pair.  Permission
- * is encoded as a pair where the link permission is determined
- * first, and if allowed, the target is tested.  The target test
- * is done from the point of the link match (not start of DFA)
- * making the target permission dependent on the link permission match.
- *
- * The subset test if required forces that permissions granted
- * on link are a subset of the permission granted to target.
- *
- * Returns: %0 if allowed else error
- */
-int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
-                struct path *new_dir, struct dentry *new_dentry)
+static int profile_path_link(struct aa_profile *profile, const char *lname,
+                            const char *tname, struct path_cond *cond)
 {
-       struct path link = { new_dir->mnt, new_dentry };
-       struct path target = { new_dir->mnt, old_dentry };
-       struct path_cond cond = {
-               d_backing_inode(old_dentry)->i_uid,
-               d_backing_inode(old_dentry)->i_mode
-       };
-       char *buffer = NULL, *buffer2 = NULL;
-       const char *lname, *tname = NULL, *info = NULL;
-       struct file_perms lperms, perms;
+       struct aa_perms lperms, perms;
+       const char *info = NULL;
        u32 request = AA_MAY_LINK;
        unsigned int state;
-       int error;
-
-       lperms = nullperms;
-
-       /* buffer freed below, lname is pointer in buffer */
-       error = aa_path_name(&link, profile->path_flags, &buffer, &lname,
-                            &info);
-       if (error)
-               goto audit;
+       int e = -EACCES;
 
-       /* buffer2 freed below, tname is pointer in buffer2 */
-       error = aa_path_name(&target, profile->path_flags, &buffer2, &tname,
-                            &info);
-       if (error)
-               goto audit;
-
-       error = -EACCES;
        /* aa_str_perms - handles the case of the dfa being NULL */
        state = aa_str_perms(profile->file.dfa, profile->file.start, lname,
-                            &cond, &lperms);
+                            cond, &lperms);
 
        if (!(lperms.allow & AA_MAY_LINK))
                goto audit;
 
        /* test to see if target can be paired with link */
        state = aa_dfa_null_transition(profile->file.dfa, state);
-       aa_str_perms(profile->file.dfa, state, tname, &cond, &perms);
+       aa_str_perms(profile->file.dfa, state, tname, cond, &perms);
 
        /* force audit/quiet masks for link are stored in the second entry
         * in the link pair.
@@ -396,6 +383,7 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
 
        if (!(perms.allow & AA_MAY_LINK)) {
                info = "target restricted";
+               lperms = perms;
                goto audit;
        }
 
@@ -403,10 +391,10 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
        if (!(perms.allow & AA_LINK_SUBSET))
                goto done_tests;
 
-       /* Do link perm subset test requiring allowed permission on link are a
-        * subset of the allowed permissions on target.
+       /* Do link perm subset test requiring allowed permission on link are
+        * subset of the allowed permissions on target.
         */
-       aa_str_perms(profile->file.dfa, profile->file.start, tname, &cond,
+       aa_str_perms(profile->file.dfa, profile->file.start, tname, cond,
                     &perms);
 
        /* AA_MAY_LINK is not considered in the subset test */
@@ -425,13 +413,175 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
        }
 
 done_tests:
-       error = 0;
+       e = 0;
 
 audit:
-       error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request,
-                             lname, tname, cond.uid, info, error);
-       kfree(buffer);
-       kfree(buffer2);
+       return aa_audit_file(profile, &lperms, OP_LINK, request, lname, tname,
+                            NULL, cond->uid, info, e);
+}
+
+/**
+ * aa_path_link - Handle hard link permission check
+ * @label: the label being enforced  (NOT NULL)
+ * @old_dentry: the target dentry  (NOT NULL)
+ * @new_dir: directory the new link will be created in  (NOT NULL)
+ * @new_dentry: the link being created  (NOT NULL)
+ *
+ * Handle the permission test for a link & target pair.  Permission
+ * is encoded as a pair where the link permission is determined
+ * first, and if allowed, the target is tested.  The target test
+ * is done from the point of the link match (not start of DFA)
+ * making the target permission dependent on the link permission match.
+ *
+ * The subset test if required forces that permissions granted
+ * on link are a subset of the permission granted to target.
+ *
+ * Returns: %0 if allowed else error
+ */
+int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
+                struct path *new_dir, struct dentry *new_dentry)
+{
+       struct path link = { new_dir->mnt, new_dentry };
+       struct path target = { new_dir->mnt, old_dentry };
+       struct path_cond cond = {
+               d_backing_inode(old_dentry)->i_uid,
+               d_backing_inode(old_dentry)->i_mode
+       };
+       char *buffer = NULL, *buffer2 = NULL;
+       const char *lname, *tname = NULL;
+       struct aa_profile *profile;
+       int error;
+
+       /* TODO: fix path lookup flags, auditing of failed path for profile */
+       profile = labels_profile(label);
+       /* buffer freed below, lname is pointer in buffer */
+       get_buffers(buffer, buffer2);
+       error = path_name(OP_LINK, label, &link,
+                         labels_profile(label)->path_flags, buffer,
+                         &lname, &cond, AA_MAY_LINK, false);
+       if (error)
+               goto out;
+
+       /* buffer2 freed below, tname is pointer in buffer2 */
+       error = path_name(OP_LINK, label, &target,
+                         labels_profile(label)->path_flags, buffer2, &tname,
+                         &cond, AA_MAY_LINK, false);
+       if (error)
+               goto out;
+
+       error = fn_for_each_confined(label, profile,
+                       profile_path_link(profile, lname, tname, &cond));
+
+out:
+       put_buffers(buffer, buffer2);
+
+       return error;
+}
+
+static void update_file_ctx(struct aa_file_ctx *fctx, struct aa_label *label,
+                           u32 request)
+{
+       struct aa_label *l, *old;
+
+       /* update caching of label on file_ctx */
+       spin_lock(&fctx->lock);
+       old = rcu_dereference_protected(fctx->label,
+                                       spin_is_locked(&fctx->lock));
+       l = aa_label_merge(old, label, GFP_ATOMIC);
+       if (l) {
+               if (l != old) {
+                       rcu_assign_pointer(fctx->label, l);
+                       aa_put_label(old);
+               } else
+                       aa_put_label(l);
+               fctx->allow |= request;
+       }
+       spin_unlock(&fctx->lock);
+}
+
+static int __file_path_perm(const char *op, struct aa_label *label,
+                           struct aa_label *flabel, struct file *file,
+                           u32 request, u32 denied)
+{
+       struct aa_profile *profile;
+       struct aa_perms perms = {};
+       struct path_cond cond = {
+               .uid = file_inode(file)->i_uid,
+               .mode = file_inode(file)->i_mode
+       };
+       const char *name;
+       char *buffer;
+       int flags, error;
+
+       /* revalidation due to label out of date. No revocation at this time */
+       if (!denied && aa_label_is_subset(flabel, label))
+               /* TODO: check for revocation on stale profiles */
+               return 0;
+
+       /* TODO: fix path lookup flags */
+       flags = PATH_DELEGATE_DELETED | labels_profile(label)->path_flags |
+               (S_ISDIR(cond.mode) ? PATH_IS_DIR : 0);
+       get_buffers(buffer);
+
+       error = path_name(op, label, &file->f_path, flags, buffer, &name, &cond,
+                         request, true);
+       if (error) {
+               if (error == 1)
+                       /* Access to open files that are deleted are
+                        * given a pass (implicit delegation)
+                        */
+                       /* TODO not needed when full perms cached */
+                       error = 0;
+               goto out;
+       }
+
+       /* check every profile in task label not in current cache */
+       error = fn_for_each_not_in_set(flabel, label, profile,
+                       __aa_path_perm(op, profile, name, request, &cond, 0,
+                                      &perms));
+       if (denied) {
+               /* check every profile in file label that was not tested
+                * in the initial check above.
+                */
+               /* TODO: cache full perms so this only happens because of
+                * conditionals */
+               /* TODO: don't audit here */
+               last_error(error,
+                       fn_for_each_not_in_set(label, flabel, profile,
+                               __aa_path_perm(op, profile, name, request,
+                                              &cond, 0, &perms)));
+       }
+       if (!error)
+               update_file_ctx(file_ctx(file), label, request);
+
+out:
+       put_buffers(buffer);
+
+       return error;
+}
+
+static int __file_sock_perm(const char *op, struct aa_label *label,
+                           struct aa_label *flabel, struct file *file,
+                           u32 request, u32 denied)
+{
+       struct socket *sock = (struct socket *) file->private_data;
+       int error;
+
+       AA_BUG(!sock);
+
+       /* revalidation due to label out of date. No revocation at this time */
+       if (!denied && aa_label_is_subset(flabel, label))
+               return 0;
+
+       /* TODO: improve to skip profiles cached in flabel */
+       error = aa_sock_file_perm(label, op, request, sock);
+       if (denied) {
+               /* TODO: improve to skip profiles checked above */
+               /* check every profile in file label to is cached */
+               last_error(error, aa_sock_file_perm(flabel, op, request, sock));
+       }
+       if (!error)
+               update_file_ctx(file_ctx(file), label, request);
 
        return error;
 }
@@ -439,20 +589,117 @@ audit:
 /**
  * aa_file_perm - do permission revalidation check & audit for @file
  * @op: operation being checked
- * @profile: profile being enforced   (NOT NULL)
+ * @label: label being enforced   (NOT NULL)
  * @file: file to revalidate access permissions on  (NOT NULL)
  * @request: requested permissions
  *
  * Returns: %0 if access allowed else error
  */
-int aa_file_perm(int op, struct aa_profile *profile, struct file *file,
+int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
                 u32 request)
 {
-       struct path_cond cond = {
-               .uid = file_inode(file)->i_uid,
-               .mode = file_inode(file)->i_mode
-       };
+       struct aa_file_ctx *fctx;
+       struct aa_label *flabel;
+       u32 denied;
+       int error = 0;
+
+       AA_BUG(!label);
+       AA_BUG(!file);
+
+       fctx = file_ctx(file);
+
+       rcu_read_lock();
+       flabel  = rcu_dereference(fctx->label);
+       AA_BUG(!flabel);
+
+       /* revalidate access, if task is unconfined, or the cached cred
+        * doesn't match or if the request is for more permissions than
+        * was granted.
+        *
+        * Note: the test for !unconfined(flabel) is to handle file
+        *       delegation from unconfined tasks
+        */
+       denied = request & ~fctx->allow;
+       if (unconfined(label) || unconfined(flabel) ||
+           (!denied && aa_label_is_subset(flabel, label)))
+               goto done;
+
+       /* TODO: label cross check */
+
+       if (file->f_path.mnt && path_mediated_fs(file->f_path.dentry)) {
+               error = __file_path_perm(op, label, flabel, file, request,
+                                        denied);
+
+       } else if (S_ISSOCK(file_inode(file)->i_mode)) {
+               error = __file_sock_perm(op, label, flabel, file, request,
+                                        denied);
+       }
+done:
+       rcu_read_unlock();
+
+       return error;
+}
 
-       return aa_path_perm(op, profile, &file->f_path, PATH_DELEGATE_DELETED,
-                           request, &cond);
+static void revalidate_tty(struct aa_label *label)
+{
+       struct tty_struct *tty;
+       int drop_tty = 0;
+
+       tty = get_current_tty();
+       if (!tty)
+               return;
+
+       spin_lock(&tty_files_lock);
+       if (!list_empty(&tty->tty_files)) {
+               struct tty_file_private *file_priv;
+               struct file *file;
+               /* TODO: Revalidate access to controlling tty. */
+               file_priv = list_first_entry(&tty->tty_files,
+                                            struct tty_file_private, list);
+               file = file_priv->file;
+
+               if (aa_file_perm(OP_INHERIT, label, file, MAY_READ | MAY_WRITE))
+                       drop_tty = 1;
+       }
+       spin_unlock(&tty_files_lock);
+       tty_kref_put(tty);
+
+       if (drop_tty)
+               no_tty();
+}
+
+static int match_file(const void *p, struct file *file, unsigned fd)
+{
+       struct aa_label *label = (struct aa_label *)p;
+       if (aa_file_perm(OP_INHERIT, label, file, aa_map_file_to_perms(file)))
+               return fd + 1;
+       return 0;
+}
+
+
+/* based on selinux's flush_unauthorized_files */
+void aa_inherit_files(const struct cred *cred, struct files_struct *files)
+{
+       struct aa_label *label = aa_get_newest_cred_label(cred);
+       struct file *devnull = NULL;
+       unsigned n;
+
+       revalidate_tty(label);
+
+       /* Revalidate access to inherited open files. */
+       n = iterate_fd(files, 0, match_file, label);
+       if (!n) /* none found? */
+               goto out;
+
+       devnull = dentry_open(&aa_null, O_RDWR, cred);
+       if (IS_ERR(devnull))
+               devnull = NULL;
+       /* replace all the matching ones with this */
+       do {
+               replace_fd(n - 1, devnull, 0);
+       } while ((n = iterate_fd(files, n, match_file, label)) != 0);
+       if (devnull)
+               fput(devnull);
+out:
+       aa_put_label(label);
 }
diff --git a/security/apparmor/include/af_unix.h b/security/apparmor/include/af_unix.h
new file mode 100644 (file)
index 0000000..d1b7f23
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor af_unix fine grained mediation
+ *
+ * Copyright 2014 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_AF_UNIX_H
+
+#include <net/af_unix.h>
+
+#include "label.h"
+//#include "include/net.h"
+
+#define unix_addr_len(L) ((L) - sizeof(sa_family_t))
+#define unix_abstract_name_len(L) (unix_addr_len(L) - 1)
+#define unix_abstract_len(U) (unix_abstract_name_len((U)->addr->len))
+#define addr_unix_abstract_name(B) ((B)[0] == 0)
+#define addr_unix_anonymous(U) (addr_unix_len(U) <= 0)
+#define addr_unix_abstract(U) (!addr_unix_anonymous(U) && addr_unix_abstract_name((U)->addr))
+//#define unix_addr_fs(U) (!unix_addr_anonymous(U) && !unix_addr_abstract_name((U)->addr))
+
+#define unix_addr(A) ((struct sockaddr_un *)(A))
+#define unix_addr_anon(A, L) ((A) && unix_addr_len(L) <= 0)
+#define unix_addr_fs(A, L) (!unix_addr_anon(A, L) && !addr_unix_abstract_name(unix_addr(A)->sun_path))
+
+#define UNIX_ANONYMOUS(U) (!unix_sk(U)->addr)
+/* from net/unix/af_unix.c */
+#define UNIX_ABSTRACT(U) (!UNIX_ANONYMOUS(U) &&                                \
+                         unix_sk(U)->addr->hash < UNIX_HASH_SIZE)
+#define UNIX_FS(U) (!UNIX_ANONYMOUS(U) && unix_sk(U)->addr->name->sun_path[0])
+#define unix_peer(sk) (unix_sk(sk)->peer)
+#define unix_connected(S) ((S)->state == SS_CONNECTED)
+
+static inline void print_unix_addr(struct sockaddr_un *A, int L)
+{
+       char *buf = (A) ? (char *) &(A)->sun_path : NULL;
+       int len = unix_addr_len(L);
+       if (!buf || len <= 0)
+               printk(" <anonymous>");
+       else if (buf[0])
+               printk(" %s", buf);
+       else
+               /* abstract name len includes leading \0 */
+               printk(" %d @%.*s", len - 1, len - 1, buf+1);
+};
+
+/*
+       printk("%s: %s: f %d, t %d, p %d", __FUNCTION__,                \
+              #SK ,                                                    \
+*/
+#define print_unix_sk(SK)                                              \
+do {                                                                   \
+       struct unix_sock *u = unix_sk(SK);                              \
+       printk("%s: f %d, t %d, p %d",  #SK ,                           \
+              (SK)->sk_family, (SK)->sk_type, (SK)->sk_protocol);      \
+       if (u->addr)                                                    \
+               print_unix_addr(u->addr->name, u->addr->len);           \
+       else                                                            \
+               print_unix_addr(NULL, sizeof(sa_family_t));             \
+       /* printk("\n");*/                                              \
+} while (0)
+
+#define print_sk(SK)                                                   \
+do {                                                                   \
+       if (!(SK)) {                                                    \
+               printk("%s: %s is null\n", __FUNCTION__, #SK);          \
+       } else if ((SK)->sk_family == PF_UNIX) {                        \
+               print_unix_sk(SK);                                      \
+               printk("\n");                                           \
+       } else {                                                        \
+               printk("%s: %s: family %d\n", __FUNCTION__, #SK ,       \
+                      (SK)->sk_family);                                \
+       }                                                               \
+} while (0)
+
+#define print_sock_addr(U) \
+do {                          \
+       printk("%s:\n", __FUNCTION__);                                  \
+       printk("    sock %s:", sock_ctx && sock_ctx->label ? aa_label_printk(sock_ctx->label, GFP_ATOMIC); : "<null>"); print_sk(sock); \
+       printk("    other %s:", other_ctx && other_ctx->label ? aa_label_printk(other_ctx->label, GFP_ATOMIC); : "<null>"); print_sk(other); \
+       printk("    new %s", new_ctx && new_ctx->label ? aa_label_printk(new_ctx->label, GFP_ATOMIC); : "<null>"); print_sk(newsk); \
+} while (0)
+
+
+
+
+int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request,
+                     struct sock *sk, struct sock *peer_sk,
+                     struct aa_label *peer_label);
+int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request,
+                         struct sock *sk);
+int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock);
+int aa_unix_create_perm(struct aa_label *label, int family, int type,
+                       int protocol);
+int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address,
+                     int addrlen);
+int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address,
+                        int addrlen);
+int aa_unix_listen_perm(struct socket *sock, int backlog);
+int aa_unix_accept_perm(struct socket *sock, struct socket *newsock);
+int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock,
+                    struct msghdr *msg, int size);
+int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level,
+                    int optname);
+int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request,
+                     struct socket *sock);
+
+#endif /* __AA_AF_UNIX_H */
index e4ea6266386662c2c88445743ff2e107b665230a..a498e3972f8689f2672e8bd643d9b4a6950f3a86 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * AppArmor security module
  *
- * This file contains AppArmor basic global and lib definitions
+ * This file contains AppArmor basic global
  *
  * Copyright (C) 1998-2008 Novell/SUSE
- * Copyright 2009-2010 Canonical Ltd.
+ * Copyright 2009-2016 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
 #ifndef __APPARMOR_H
 #define __APPARMOR_H
 
-#include <linux/slab.h>
-#include <linux/fs.h>
-
-#include "match.h"
+#include <linux/types.h>
 
 /*
  * Class of mediation types in the AppArmor policy db
 #define AA_CLASS_NET           4
 #define AA_CLASS_RLIMITS       5
 #define AA_CLASS_DOMAIN                6
+#define AA_CLASS_MOUNT         7
+#define AA_CLASS_PTRACE                9
+#define AA_CLASS_SIGNAL                10
+#define AA_CLASS_LABEL         16
 
-#define AA_CLASS_LAST          AA_CLASS_DOMAIN
+#define AA_CLASS_LAST          AA_CLASS_LABEL
 
 /* Control parameters settable through module/boot flags */
 extern enum audit_mode aa_g_audit;
 extern bool aa_g_audit_header;
 extern bool aa_g_debug;
+extern bool aa_g_hash_policy;
 extern bool aa_g_lock_policy;
 extern bool aa_g_logsyscall;
 extern bool aa_g_paranoid_load;
 extern unsigned int aa_g_path_max;
-
-/*
- * DEBUG remains global (no per profile flag) since it is mostly used in sysctl
- * which is not related to profile accesses.
- */
-
-#define AA_DEBUG(fmt, args...)                                         \
-       do {                                                            \
-               if (aa_g_debug && printk_ratelimit())                   \
-                       printk(KERN_DEBUG "AppArmor: " fmt, ##args);    \
-       } while (0)
-
-#define AA_ERROR(fmt, args...)                                         \
-       do {                                                            \
-               if (printk_ratelimit())                                 \
-                       printk(KERN_ERR "AppArmor: " fmt, ##args);      \
-       } while (0)
-
-/* Flag indicating whether initialization completed */
-extern int apparmor_initialized __initdata;
-
-/* fn's in lib */
-char *aa_split_fqname(char *args, char **ns_name);
-void aa_info_message(const char *str);
-void *__aa_kvmalloc(size_t size, gfp_t flags);
-
-static inline void *kvmalloc(size_t size)
-{
-       return __aa_kvmalloc(size, 0);
-}
-
-static inline void *kvzalloc(size_t size)
-{
-       return __aa_kvmalloc(size, __GFP_ZERO);
-}
-
-/* returns 0 if kref not incremented */
-static inline int kref_get_not0(struct kref *kref)
-{
-       return atomic_inc_not_zero(&kref->refcount);
-}
-
-/**
- * aa_strneq - compare null terminated @str to a non null terminated substring
- * @str: a null terminated string
- * @sub: a substring, not necessarily null terminated
- * @len: length of @sub to compare
- *
- * The @str string must be full consumed for this to be considered a match
- */
-static inline bool aa_strneq(const char *str, const char *sub, int len)
-{
-       return !strncmp(str, sub, len) && !str[len];
-}
-
-/**
- * aa_dfa_null_transition - step to next state after null character
- * @dfa: the dfa to match against
- * @start: the state of the dfa to start matching in
- *
- * aa_dfa_null_transition transitions to the next state after a null
- * character which is not used in standard matching and is only
- * used to separate pairs.
- */
-static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
-                                                 unsigned int start)
-{
-       /* the null transition only needs the string's null terminator byte */
-       return aa_dfa_next(dfa, start, 0);
-}
-
-static inline bool mediated_filesystem(struct dentry *dentry)
-{
-       return !(dentry->d_sb->s_flags & MS_NOUSER);
-}
+extern bool aa_g_unconfined_init;
 
 #endif /* __APPARMOR_H */
index 414e56878dd0c0462c0f85f0b0296448ad8ba27e..c6f84dc5c2b2bb13bc24dacf2ece8151e0a7a39d 100644 (file)
@@ -15,6 +15,8 @@
 #ifndef __AA_APPARMORFS_H
 #define __AA_APPARMORFS_H
 
+extern struct path aa_null;
+
 enum aa_fs_type {
        AA_FS_TYPE_BOOLEAN,
        AA_FS_TYPE_STRING,
@@ -62,7 +64,7 @@ extern const struct file_operations aa_fs_seq_file_ops;
 extern void __init aa_destroy_aafs(void);
 
 struct aa_profile;
-struct aa_namespace;
+struct aa_ns;
 
 enum aafs_ns_type {
        AAFS_NS_DIR,
@@ -97,8 +99,7 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile);
 void __aa_fs_profile_migrate_dents(struct aa_profile *old,
                                   struct aa_profile *new);
 int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
-void __aa_fs_namespace_rmdir(struct aa_namespace *ns);
-int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
-                           const char *name);
+void __aa_fs_ns_rmdir(struct aa_ns *ns);
+int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name);
 
 #endif /* __AA_APPARMORFS_H */
index ba3dfd17f23f2671b20512a63c06ba75928ed0fc..0360e863bbfe694f618d73819b0977bbfd662fb1 100644 (file)
@@ -22,8 +22,7 @@
 #include <linux/slab.h>
 
 #include "file.h"
-
-struct aa_profile;
+#include "label.h"
 
 extern const char *const audit_mode_names[];
 #define AUDIT_MAX_INDEX 5
@@ -46,97 +45,140 @@ enum audit_type {
        AUDIT_APPARMOR_AUTO
 };
 
-extern const char *const op_table[];
-enum aa_ops {
-       OP_NULL,
-
-       OP_SYSCTL,
-       OP_CAPABLE,
-
-       OP_UNLINK,
-       OP_MKDIR,
-       OP_RMDIR,
-       OP_MKNOD,
-       OP_TRUNC,
-       OP_LINK,
-       OP_SYMLINK,
-       OP_RENAME_SRC,
-       OP_RENAME_DEST,
-       OP_CHMOD,
-       OP_CHOWN,
-       OP_GETATTR,
-       OP_OPEN,
-
-       OP_FPERM,
-       OP_FLOCK,
-       OP_FMMAP,
-       OP_FMPROT,
-
-       OP_CREATE,
-       OP_POST_CREATE,
-       OP_BIND,
-       OP_CONNECT,
-       OP_LISTEN,
-       OP_ACCEPT,
-       OP_SENDMSG,
-       OP_RECVMSG,
-       OP_GETSOCKNAME,
-       OP_GETPEERNAME,
-       OP_GETSOCKOPT,
-       OP_SETSOCKOPT,
-       OP_SOCK_SHUTDOWN,
-
-       OP_PTRACE,
-
-       OP_EXEC,
-       OP_CHANGE_HAT,
-       OP_CHANGE_PROFILE,
-       OP_CHANGE_ONEXEC,
-
-       OP_SETPROCATTR,
-       OP_SETRLIMIT,
-
-       OP_PROF_REPL,
-       OP_PROF_LOAD,
-       OP_PROF_RM,
-};
+#define OP_NULL NULL
+
+#define OP_SYSCTL "sysctl"
+#define OP_CAPABLE "capable"
+
+#define OP_UNLINK "unlink"
+#define OP_MKDIR "mkdir"
+#define OP_RMDIR "rmdir"
+#define OP_MKNOD "mknod"
+#define OP_TRUNC "truncate"
+#define OP_LINK "link"
+#define OP_SYMLINK "symlink"
+#define OP_RENAME_SRC "rename_src"
+#define OP_RENAME_DEST "rename_dest"
+#define OP_CHMOD "chmod"
+#define OP_CHOWN "chown"
+#define OP_GETATTR "getattr"
+#define OP_OPEN "open"
+
+#define OP_FRECEIVE "file_receive"
+#define OP_FPERM "file_perm"
+#define OP_FLOCK "file_lock"
+#define OP_FMMAP "file_mmap"
+#define OP_FMPROT "file_mprotect"
+#define OP_INHERIT "file_inherit"
+
+#define OP_PIVOTROOT "pivotroot"
+#define OP_MOUNT "mount"
+#define OP_UMOUNT "umount"
+
+#define OP_CREATE "create"
+#define OP_POST_CREATE "post_create"
+#define OP_BIND "bind"
+#define OP_CONNECT "connect"
+#define OP_LISTEN "listen"
+#define OP_ACCEPT "accept"
+#define OP_SENDMSG "sendmsg"
+#define OP_RECVMSG "recvmsg"
+#define OP_GETSOCKNAME "getsockname"
+#define OP_GETPEERNAME "getpeername"
+#define OP_GETSOCKOPT "getsockopt"
+#define OP_SETSOCKOPT "setsockopt"
+#define OP_SHUTDOWN "socket_shutdown"
+
+#define OP_PTRACE "ptrace"
+#define OP_SIGNAL "signal"
+
+#define OP_EXEC "exec"
+
+#define OP_CHANGE_HAT "change_hat"
+#define OP_CHANGE_PROFILE "change_profile"
+#define OP_CHANGE_ONEXEC "change_onexec"
+#define OP_STACK "stack"
+#define OP_STACK_ONEXEC "stack_onexec"
+
+#define OP_SETPROCATTR "setprocattr"
+#define OP_SETRLIMIT "setrlimit"
+
+#define OP_PROF_REPL "profile_replace"
+#define OP_PROF_LOAD "profile_load"
+#define OP_PROF_RM "profile_remove"
 
 
 struct apparmor_audit_data {
        int error;
-       int op;
        int type;
-       void *profile;
+       const char *op;
+       struct aa_label *label;
        const char *name;
        const char *info;
+       u32 request;
+       u32 denied;
        union {
-               void *target;
+               /* these entries require a custom callback fn */
+               struct {
+                       struct aa_label *peer;
+                       union {
+                               struct {
+                                       kuid_t ouid;
+                                       const char *target;
+                               } fs;
+                               struct {
+                                       int type, protocol;
+                                       struct sock *peer_sk;
+                                       void *addr;
+                                       int addrlen;
+                               } net;
+                               int signal;
+                       };
+               };
                struct {
+                       struct aa_profile *profile;
+                       const char *ns;
                        long pos;
-                       void *target;
                } iface;
                struct {
                        int rlim;
                        unsigned long max;
                } rlim;
                struct {
-                       const char *target;
-                       u32 request;
-                       u32 denied;
-                       kuid_t ouid;
-               } fs;
+                       const char *src_name;
+                       const char *type;
+                       const char *trans;
+                       const char *data;
+                       unsigned long flags;
+               } mnt;
        };
 };
 
-/* define a short hand for apparmor_audit_data structure */
-#define aad apparmor_audit_data
+/* macros for dealing with  apparmor_audit_data structure */
+#define aad(SA) (SA)->apparmor_audit_data
+#define DEFINE_AUDIT_DATA(NAME, T, X)                                  \
+       /* TODO: cleanup audit init so we don't need _aad = {0,} */     \
+       struct apparmor_audit_data NAME ## _aad = { .op = (X), };       \
+       struct common_audit_data NAME =                                 \
+       {                                                               \
+       .type = (T),                                                    \
+       .u.tsk = NULL,                                                  \
+       };                                                              \
+       NAME.apparmor_audit_data = &(NAME ## _aad)
 
 void aa_audit_msg(int type, struct common_audit_data *sa,
                  void (*cb) (struct audit_buffer *, void *));
-int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
-            struct common_audit_data *sa,
+int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa,
             void (*cb) (struct audit_buffer *, void *));
 
+#define aa_audit_error(ERROR, SA, CB)                          \
+({                                                             \
+       aad((SA))->error = (ERROR);                             \
+       aa_audit_msg(AUDIT_APPARMOR_ERROR, (SA), (CB));         \
+       aad((SA))->error;                                       \
+})
+
+
 static inline int complain_error(int error)
 {
        if (error == -EPERM || error == -EACCES)
index fc3fa381d8506c5dc249e93809b15292c87ffb84..e01d97fa81437dec9c3c0208e9904bddcb877677 100644 (file)
 
 #include "apparmorfs.h"
 
-struct aa_profile;
+struct aa_label;
 
 /* aa_caps - confinement data for capabilities
  * @allowed: capabilities mask
  * @audit: caps that are to be audited
+ * @denied: caps that are explicitly denied
  * @quiet: caps that should not be audited
  * @kill: caps that when requested will result in the task being killed
  * @extended: caps that are subject finer grained mediation
@@ -31,6 +32,7 @@ struct aa_profile;
 struct aa_caps {
        kernel_cap_t allow;
        kernel_cap_t audit;
+       kernel_cap_t denied;
        kernel_cap_t quiet;
        kernel_cap_t kill;
        kernel_cap_t extended;
@@ -38,7 +40,7 @@ struct aa_caps {
 
 extern struct aa_fs_entry aa_fs_entry_caps[];
 
-int aa_capable(struct aa_profile *profile, int cap, int audit);
+int aa_capable(struct aa_label *label, int cap, int audit);
 
 static inline void aa_free_cap_rules(struct aa_caps *caps)
 {
index 6bf65798e5d145e985a65f674273db20472ece67..01b81b799b14ef8c41194a33a757366f018e6d5b 100644 (file)
 #include <linux/slab.h>
 #include <linux/sched.h>
 
-#include "policy.h"
+#include "label.h"
+#include "policy_ns.h"
 
-#define cred_cxt(X) (X)->security
-#define current_cxt() cred_cxt(current_cred())
-
-/* struct aa_file_cxt - the AppArmor context the file was opened in
- * @perms: the permission the file was opened with
- *
- * The file_cxt could currently be directly stored in file->f_security
- * as the profile reference is now stored in the f_cred.  However the
- * cxt struct will expand in the future so we keep the struct.
- */
-struct aa_file_cxt {
-       u16 allow;
-};
-
-/**
- * aa_alloc_file_context - allocate file_cxt
- * @gfp: gfp flags for allocation
- *
- * Returns: file_cxt or NULL on failure
- */
-static inline struct aa_file_cxt *aa_alloc_file_context(gfp_t gfp)
-{
-       return kzalloc(sizeof(struct aa_file_cxt), gfp);
-}
-
-/**
- * aa_free_file_context - free a file_cxt
- * @cxt: file_cxt to free  (MAYBE_NULL)
- */
-static inline void aa_free_file_context(struct aa_file_cxt *cxt)
-{
-       if (cxt)
-               kzfree(cxt);
-}
+#define cred_ctx(X) (X)->security
+#define current_ctx() cred_ctx(current_cred())
 
 /**
- * struct aa_task_cxt - primary label for confined tasks
- * @profile: the current profile   (NOT NULL)
- * @exec: profile to transition to on next exec  (MAYBE NULL)
- * @previous: profile the task may return to     (MAYBE NULL)
- * @token: magic value the task must know for returning to @previous_profile
+ * struct aa_task_ctx - primary label for confined tasks
+ * @label: the current label   (NOT NULL)
+ * @exec: label to transition to on next exec  (MAYBE NULL)
+ * @previous: label the task may return to     (MAYBE NULL)
+ * @token: magic value the task must know for returning to @previous
  *
- * Contains the task's current profile (which could change due to
+ * Contains the task's current label (which could change due to
  * change_hat).  Plus the hat_magic needed during change_hat.
  *
  * TODO: make so a task can be confined by a stack of contexts
  */
-struct aa_task_cxt {
-       struct aa_profile *profile;
-       struct aa_profile *onexec;
-       struct aa_profile *previous;
+struct aa_task_ctx {
+       struct aa_label *label;
+       struct aa_label *onexec;
+       struct aa_label *previous;
        u64 token;
 };
 
-struct aa_task_cxt *aa_alloc_task_context(gfp_t flags);
-void aa_free_task_context(struct aa_task_cxt *cxt);
-void aa_dup_task_context(struct aa_task_cxt *new,
-                        const struct aa_task_cxt *old);
-int aa_replace_current_profile(struct aa_profile *profile);
-int aa_set_current_onexec(struct aa_profile *profile);
-int aa_set_current_hat(struct aa_profile *profile, u64 token);
-int aa_restore_previous_profile(u64 cookie);
-struct aa_profile *aa_get_task_profile(struct task_struct *task);
+struct aa_task_ctx *aa_alloc_task_context(gfp_t flags);
+void aa_free_task_context(struct aa_task_ctx *ctx);
+void aa_dup_task_context(struct aa_task_ctx *new,
+                        const struct aa_task_ctx *old);
+int aa_replace_current_label(struct aa_label *label);
+int aa_set_current_onexec(struct aa_label *label, bool stack);
+int aa_set_current_hat(struct aa_label *label, u64 token);
+int aa_restore_previous_label(u64 cookie);
+struct aa_label *aa_get_task_label(struct task_struct *task);
 
 
 /**
- * aa_cred_profile - obtain cred's profiles
- * @cred: cred to obtain profiles from  (NOT NULL)
+ * aa_cred_raw_label - obtain cred's label
+ * @cred: cred to obtain label from  (NOT NULL)
  *
- * Returns: confining profile
+ * Returns: confining label
  *
  * does NOT increment reference count
  */
-static inline struct aa_profile *aa_cred_profile(const struct cred *cred)
+static inline struct aa_label *aa_cred_raw_label(const struct cred *cred)
 {
-       struct aa_task_cxt *cxt = cred_cxt(cred);
-       BUG_ON(!cxt || !cxt->profile);
-       return cxt->profile;
+       struct aa_task_ctx *ctx = cred_ctx(cred);
+       BUG_ON(!ctx || !ctx->label);
+       return ctx->label;
 }
 
 /**
- * __aa_task_profile - retrieve another task's profile
+ * aa_get_newest_cred_label - obtain the newest version of the label on a cred
+ * @cred: cred to obtain label from (NOT NULL)
+ *
+ * Returns: newest version of confining label
+ */
+static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred)
+{
+       return aa_get_newest_label(aa_cred_raw_label(cred));
+}
+
+/**
+ * __aa_task_raw_label - retrieve another task's label
  * @task: task to query  (NOT NULL)
  *
- * Returns: @task's profile without incrementing its ref count
+ * Returns: @task's label without incrementing its ref count
  *
  * If @task != current needs to be called in RCU safe critical section
  */
-static inline struct aa_profile *__aa_task_profile(struct task_struct *task)
+static inline struct aa_label *__aa_task_raw_label(struct task_struct *task)
 {
-       return aa_cred_profile(__task_cred(task));
+       return aa_cred_raw_label(__task_cred(task));
 }
 
 /**
@@ -122,57 +102,105 @@ static inline struct aa_profile *__aa_task_profile(struct task_struct *task)
  */
 static inline bool __aa_task_is_confined(struct task_struct *task)
 {
-       return !unconfined(__aa_task_profile(task));
+       return !unconfined(__aa_task_raw_label(task));
 }
 
 /**
- * __aa_current_profile - find the current tasks confining profile
+ * aa_current_raw_label - find the current tasks confining label
  *
- * Returns: up to date confining profile or the ns unconfined profile (NOT NULL)
+ * Returns: up to date confining label or the ns unconfined label (NOT NULL)
  *
  * This fn will not update the tasks cred to the most up to date version
- * of the profile so it is safe to call when inside of locks.
+ * of the label so it is safe to call when inside of locks.
  */
-static inline struct aa_profile *__aa_current_profile(void)
+static inline struct aa_label *aa_current_raw_label(void)
 {
-       return aa_cred_profile(current_cred());
+       return aa_cred_raw_label(current_cred());
 }
 
 /**
- * aa_current_profile - find the current tasks confining profile and do updates
+ * aa_get_current_label - get the newest version of the current tasks label
+ *
+ * Returns: newest version of confining label (NOT NULL)
  *
- * Returns: up to date confining profile or the ns unconfined profile (NOT NULL)
+ * This fn will not update the tasks cred, so it is safe inside of locks
  *
- * This fn will update the tasks cred structure if the profile has been
- * replaced.  Not safe to call inside locks
+ * The returned reference must be put with aa_put_label()
  */
-static inline struct aa_profile *aa_current_profile(void)
+static inline struct aa_label *aa_get_current_label(void)
 {
-       const struct aa_task_cxt *cxt = current_cxt();
-       struct aa_profile *profile;
-       BUG_ON(!cxt || !cxt->profile);
-
-       if (PROFILE_INVALID(cxt->profile)) {
-               profile = aa_get_newest_profile(cxt->profile);
-               aa_replace_current_profile(profile);
-               aa_put_profile(profile);
-               cxt = current_cxt();
+       struct aa_label *l = aa_current_raw_label();
+
+       if (label_is_stale(l))
+               return aa_get_newest_label(l);
+       return aa_get_label(l);
+}
+
+/**
+ * aa_end_current_label - put a reference found with aa_begin_current_label
+ * @label: label reference to put
+ *
+ * Should only be used with a reference obtained with aa_begin_current_label
+ * and never used in situations where the task cred may be updated
+ */
+static inline void aa_end_current_label(struct aa_label *label)
+{
+       if (label != aa_current_raw_label())
+               aa_put_label(label);
+}
+
+/**
+ * aa_begin_current_label - find the current tasks confining label and update it
+ * @update: whether the current label can be updated
+ *
+ * Returns: up to date confining label or the ns unconfined label (NOT NULL)
+ *
+ * If @update is true this fn will update the tasks cred structure if the
+ *   label has been replaced.  Not safe to call inside locks
+ * else
+ *   just return the up to date label
+ *
+ * The returned reference must be put with aa_end_current_label()
+ * This must NOT be used if the task cred could be updated within the
+ * critical section between aa_begin_current_label() .. aa_end_current_label()
+ */
+static inline struct aa_label *aa_begin_current_label(bool update)
+{
+       struct aa_label *label = aa_current_raw_label();
+
+       if (label_is_stale(label)) {
+               label = aa_get_newest_label(label);
+               if (update && aa_replace_current_label(label) == 0)
+                       /* task cred will keep the reference */
+                       aa_put_label(label);
        }
 
-       return cxt->profile;
+       return label;
+}
+
+#define NO_UPDATE false
+#define DO_UPDATE true
+
+static inline struct aa_ns *aa_get_current_ns(void)
+{
+       struct aa_label *label = aa_begin_current_label(NO_UPDATE);
+       struct aa_ns *ns = aa_get_ns(labels_ns(label));
+       aa_end_current_label(label);
+
+       return ns;
 }
 
 /**
- * aa_clear_task_cxt_trans - clear transition tracking info from the cxt
- * @cxt: task context to clear (NOT NULL)
+ * aa_clear_task_ctx_trans - clear transition tracking info from the ctx
+ * @ctx: task context to clear (NOT NULL)
  */
-static inline void aa_clear_task_cxt_trans(struct aa_task_cxt *cxt)
+static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx)
 {
-       aa_put_profile(cxt->previous);
-       aa_put_profile(cxt->onexec);
-       cxt->previous = NULL;
-       cxt->onexec = NULL;
-       cxt->token = 0;
+       aa_put_label(ctx->previous);
+       aa_put_label(ctx->onexec);
+       ctx->previous = NULL;
+       ctx->onexec = NULL;
+       ctx->token = 0;
 }
 
 #endif /* __AA_CONTEXT_H */
index de04464f0a3fdb0232d4343ef26afffa02613613..89cfa757c42efe9c201d47b350c9677a8ac43ea3 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/binfmts.h>
 #include <linux/types.h>
 
+#include "include/label.h"
+
 #ifndef __AA_DOMAIN_H
 #define __AA_DOMAIN_H
 
@@ -23,6 +25,9 @@ struct aa_domain {
        char **table;
 };
 
+struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
+                               const char **name);
+
 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);
@@ -30,7 +35,7 @@ void apparmor_bprm_committed_creds(struct linux_binprm *bprm);
 
 void aa_free_domain_entries(struct aa_domain *domain);
 int aa_change_hat(const char *hats[], int count, u64 token, bool permtest);
-int aa_change_profile(const char *ns_name, const char *name, bool onexec,
-                     bool permtest);
+int aa_change_profile(const char *fqname, bool onexec, bool permtest,
+                     bool stack);
 
 #endif /* __AA_DOMAIN_H */
index 2c922b86bd44f5ba7e4b519df43c663b21c34ad3..ff4b4b8fbdf0bc6ca28f11d5f3d48f260eeaf11c 100644 (file)
 #ifndef __AA_FILE_H
 #define __AA_FILE_H
 
+#include <linux/spinlock.h>
+
 #include "domain.h"
 #include "match.h"
+#include "label.h"
+#include "perms.h"
 
 struct aa_profile;
 struct path;
 
-/*
- * We use MAY_EXEC, MAY_WRITE, MAY_READ, MAY_APPEND and the following flags
- * for profile permissions
- */
-#define AA_MAY_CREATE                  0x0010
-#define AA_MAY_DELETE                  0x0020
-#define AA_MAY_META_WRITE              0x0040
-#define AA_MAY_META_READ               0x0080
-
-#define AA_MAY_CHMOD                   0x0100
-#define AA_MAY_CHOWN                   0x0200
-#define AA_MAY_LOCK                    0x0400
-#define AA_EXEC_MMAP                   0x0800
-
-#define AA_MAY_LINK                    0x1000
-#define AA_LINK_SUBSET                 AA_MAY_LOCK     /* overlaid */
-#define AA_MAY_ONEXEC                  0x40000000      /* exec allows onexec */
-#define AA_MAY_CHANGE_PROFILE          0x80000000
-#define AA_MAY_CHANGEHAT               0x80000000      /* ctrl auditing only */
+#define mask_mode_t(X) (X & (MAY_EXEC | MAY_WRITE | MAY_READ | MAY_APPEND))
 
 #define AA_AUDIT_FILE_MASK     (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND |\
                                 AA_MAY_CREATE | AA_MAY_DELETE |        \
-                                AA_MAY_META_READ | AA_MAY_META_WRITE | \
+                                AA_MAY_GETATTR | AA_MAY_SETATTR | \
                                 AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \
                                 AA_EXEC_MMAP | AA_MAY_LINK)
 
+#define file_ctx(X) ((struct aa_file_ctx *)(X)->f_security)
+
+/* struct aa_file_ctx - the AppArmor context the file was opened in
+ * @lock: lock to update the ctx
+ * @label: label currently cached on the ctx
+ * @perms: the permission the file was opened with
+ */
+struct aa_file_ctx {
+       spinlock_t lock;
+       struct aa_label __rcu *label;
+       u32 allow;
+};
+
+/**
+ * aa_alloc_file_ctx - allocate file_ctx
+ * @label: initial label of task creating the file
+ * @gfp: gfp flags for allocation
+ *
+ * Returns: file_ctx or NULL on failure
+ */
+static inline struct aa_file_ctx *aa_alloc_file_ctx(struct aa_label *label, gfp_t gfp)
+{
+       struct aa_file_ctx *ctx;
+
+       ctx = kzalloc(sizeof(struct aa_file_ctx), gfp);
+       if (ctx) {
+               spin_lock_init(&ctx->lock);
+               rcu_assign_pointer(ctx->label, aa_get_label(label));
+       }
+       return ctx;
+}
+
+/**
+ * aa_free_file_ctx - free a file_ctx
+ * @ctx: file_ctx to free  (MAYBE_NULL)
+ */
+static inline void aa_free_file_ctx(struct aa_file_ctx *ctx)
+{
+       if (ctx) {
+               aa_put_label(rcu_access_pointer(ctx->label));
+               kzfree(ctx);
+       }
+}
+
+static inline struct aa_label *aa_get_file_label(struct aa_file_ctx *ctx)
+{
+       return aa_get_label_rcu(&ctx->label);
+}
+
+#define inode_ctx(X) (X)->i_security
+
 /*
  * The xindex is broken into 3 parts
  * - index - an index into either the exec name table or the variable table
@@ -75,25 +112,6 @@ struct path_cond {
        umode_t mode;
 };
 
-/* struct file_perms - file permission
- * @allow: mask of permissions that are allowed
- * @audit: mask of permissions to force an audit message for
- * @quiet: mask of permissions to quiet audit messages for
- * @kill: mask of permissions that when matched will kill the task
- * @xindex: exec transition index if @allow contains MAY_EXEC
- *
- * The @audit and @queit mask should be mutually exclusive.
- */
-struct file_perms {
-       u32 allow;
-       u32 audit;
-       u32 quiet;
-       u32 kill;
-       u16 xindex;
-};
-
-extern struct file_perms nullperms;
-
 #define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill)
 
 /* FIXME: split perms from dfa and match this to description
@@ -144,9 +162,9 @@ static inline u16 dfa_map_xindex(u16 mask)
 #define dfa_other_xindex(dfa, state) \
        dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff)
 
-int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
-                 gfp_t gfp, int op, u32 request, const char *name,
-                 const char *target, kuid_t ouid, const char *info, int error);
+int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
+                 const char *op, u32 request, const char *name, const char *target, struct aa_label *tlabel,
+                 kuid_t ouid, const char *info, int error);
 
 /**
  * struct aa_file_rules - components used for file rule permissions
@@ -167,19 +185,26 @@ struct aa_file_rules {
        /* TODO: add delegate table */
 };
 
+struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state,
+                                   struct path_cond *cond);
 unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
                          const char *name, struct path_cond *cond,
-                         struct file_perms *perms);
+                         struct aa_perms *perms);
 
-int aa_path_perm(int op, struct aa_profile *profile, struct path *path,
+int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name,
+                  u32 request, struct path_cond *cond, int flags,
+                  struct aa_perms *perms);
+int aa_path_perm(const char *op, struct aa_label *label, struct path *path,
                 int flags, u32 request, struct path_cond *cond);
 
-int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
+int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
                 struct path *new_dir, struct dentry *new_dentry);
 
-int aa_file_perm(int op, struct aa_profile *profile, struct file *file,
+int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
                 u32 request);
 
+void aa_inherit_files(const struct cred *cred, struct files_struct *files);
+
 static inline void aa_free_file_rules(struct aa_file_rules *rules)
 {
        aa_put_dfa(rules->dfa);
index 288ca76e2fb116a6ccb1c366220ef1d918f7b67c..a786685b61db47f653bd6dd9eb34d4e8d8882a88 100644 (file)
@@ -4,7 +4,7 @@
  * This file contains AppArmor ipc mediation function definitions.
  *
  * Copyright (C) 1998-2008 Novell/SUSE
- * Copyright 2009-2010 Canonical Ltd.
+ * Copyright 2009-2013 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
 
 struct aa_profile;
 
-int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee,
-                 unsigned int mode);
+#define AA_PTRACE_TRACE                MAY_WRITE
+#define AA_PTRACE_READ         MAY_READ
+#define AA_MAY_BE_TRACED       AA_MAY_APPEND
+#define AA_MAY_BE_READ         AA_MAY_CREATE
+#define PTRACE_PERM_SHIFT      2
 
-int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,
-             unsigned int mode);
+#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \
+                            AA_MAY_BE_READ | AA_MAY_BE_TRACED)
+#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE)
+
+#define AA_FS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \
+       "segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \
+       "xcpu xfsz vtalrm prof winch io pwr sys emt lost"
+
+int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
+                 u32 request);
+int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig);
 
 #endif /* __AA_IPC_H */
diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h
new file mode 100644 (file)
index 0000000..829d6e4
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor label definitions
+ *
+ * Copyright 2013 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_LABEL_H
+#define __AA_LABEL_H
+
+#include <linux/atomic.h>
+#include <linux/audit.h>
+#include <linux/rbtree.h>
+#include <linux/rcupdate.h>
+
+#include "apparmor.h"
+#include "lib.h"
+
+struct aa_ns;
+
+#define LOCAL_VEC_ENTRIES 8
+#define DEFINE_VEC(T, V)                                               \
+       struct aa_ ## T *(_ ## V ## _localtmp)[LOCAL_VEC_ENTRIES];      \
+       struct aa_ ## T **(V)
+
+#define vec_setup(T, V, N, GFP)                                                \
+({                                                                     \
+       if ((N) <= LOCAL_VEC_ENTRIES) {                                 \
+               typeof(N) i;                                            \
+               (V) = (_ ## V ## _localtmp);                            \
+               for (i = 0; i < (N); i++)                               \
+                       (V)[i] = NULL;                                  \
+       } else                                                          \
+               (V) = kzalloc(sizeof(struct aa_ ## T *) * (N), (GFP));  \
+       (V) ? 0 : -ENOMEM;                                              \
+})
+
+#define vec_cleanup(T, V, N)                                           \
+do {                                                                   \
+       int i;                                                          \
+       for (i = 0; i < (N); i++) {                                     \
+               if (!IS_ERR_OR_NULL((V)[i]))                            \
+                       aa_put_ ## T ((V)[i]);                          \
+       }                                                               \
+       if ((V) != _ ## V ## _localtmp)                                 \
+               kfree(V);                                               \
+} while (0)
+
+#define vec_last(VEC, SIZE) ((VEC)[(SIZE) - 1])
+#define vec_ns(VEC, SIZE) (vec_last((VEC), (SIZE))->ns)
+#define vec_labelset(VEC, SIZE) (&vec_ns((VEC), (SIZE))->labels)
+#define cleanup_domain_vec(V, L) cleanup_label_vec((V), (L)->size)
+
+struct aa_profile;
+#define VEC_FLAG_TERMINATE 1
+int aa_vec_unique(struct aa_profile **vec, int n, int flags);
+struct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len,
+                                            gfp_t gfp);
+#define aa_sort_and_merge_vec(N, V) \
+       aa_sort_and_merge_profiles((N), (struct aa_profile **)(V))
+
+struct labelset_stats {
+       atomic_t sread;
+       atomic_t fread;
+       atomic_t msread;
+       atomic_t mfread;
+
+       atomic_t insert;
+       atomic_t existing;
+       atomic_t minsert;
+       atomic_t mexisting;
+
+       atomic_t stale;                 /* outstanding stale */
+};
+
+struct label_stats {
+       struct labelset_stats set_stats;
+
+       atomic_t allocated;
+       atomic_t failed;
+       atomic_t freed;
+
+       atomic_t printk_name_alloc;
+       atomic_t printk_name_fail;
+       atomic_t seq_print_name_alloc;
+       atomic_t seq_print_name_fail;
+       atomic_t audit_name_alloc;
+       atomic_t audit_name_fail;
+};
+
+
+#ifdef AA_LABEL_STATS
+#define labelstats_inc(X) atomic_inc(stats.(X))
+#define labelstats_dec(X) atomic_dec(stats.(X))
+#define labelsetstats_inc(LS, X)               \
+       do {                                    \
+               labelstats_inc(set_stats.##X);  \
+               atomic_inc((LS)->stats.(X));    \
+       } while (0)
+#define labelsetstats_dec(LS, X)               \
+       do {                                    \
+               labelstats_dec(set_stats.##X);  \
+               atomic_dec((LS)->stats.(X));    \
+       } while (0)
+#else
+#define labelstats_inc(X)
+#define labelstats_dec(X)
+#define labelsetstats_inc(LS, X)
+#define labelsetstats_dec(LS, X)
+#endif
+#define labelstats_init(X)
+
+/* struct aa_labelset - set of labels for a namespace
+ *
+ * Labels are reference counted; aa_labelset does not contribute to label
+ * reference counts. Once a label's last refcount is put it is removed from
+ * the set.
+ */
+struct aa_labelset {
+       rwlock_t lock;
+
+       struct rb_root root;
+
+       /* stats */
+#ifdef APPARMOR_LABEL_STATS
+       struct labelset_stats stats;
+#endif
+
+};
+
+#define __labelset_for_each(LS, N) \
+       for((N) = rb_first(&(LS)->root); (N); (N) = rb_next(N))
+
+void aa_labelset_destroy(struct aa_labelset *ls);
+void aa_labelset_init(struct aa_labelset *ls);
+
+
+enum label_flags {
+       FLAG_HAT = 1,                   /* profile is a hat */
+       FLAG_UNCONFINED = 2,            /* label unconfined only if all
+                                        * constituant profiles unconfined */
+       FLAG_NULL = 4,                  /* profile is null learning profile */
+       FLAG_IX_ON_NAME_ERROR = 8,      /* fallback to ix on name lookup fail */
+       FLAG_IMMUTIBLE = 0x10,          /* don't allow changes/replacement */
+       FLAG_USER_DEFINED = 0x20,       /* user based profile - lower privs */
+       FLAG_NO_LIST_REF = 0x40,        /* list doesn't keep profile ref */
+       FLAG_NS_COUNT = 0x80,           /* carries NS ref count */
+       FLAG_IN_TREE = 0x100,           /* label is in tree */
+       FLAG_PROFILE = 0x200,           /* label is a profile */
+       FLAG_EXPLICIT = 0x400,          /* explict static label */
+       FLAG_STALE = 0x800,             /* replaced/removed */
+       FLAG_RENAMED = 0x1000,          /* label has renaming in it */
+       FLAG_REVOKED = 0x2000,          /* label has revocation in it */
+
+       /* These flags must correspond with PATH_flags */
+       /* TODO: add new path flags */
+};
+
+struct aa_label;
+struct aa_proxy {
+       struct kref count;
+       struct aa_label __rcu *label;
+};
+
+struct label_it {
+       int i, j;
+};
+
+/* struct aa_label - lazy labeling struct
+ * @count: ref count of active users
+ * @node: rbtree position
+ * @rcu: rcu callback struct
+ * @proxy: is set to the label that replaced this label
+ * @hname: text representation of the label (MAYBE_NULL)
+ * @flags: stale and other flags - values may change under label set lock
+ * @sid: sid that references this label
+ * @size: number of entries in @ent[]
+ * @ent: set of profiles for label, actual size determined by @size
+ */
+struct aa_label {
+       struct kref count;
+       struct rb_node node;
+       struct rcu_head rcu;
+       struct aa_proxy *proxy;
+       __counted char *hname;
+       long flags;
+       u32 sid;
+       int size;
+       struct aa_profile *vec[];
+};
+
+#define last_error(E, FN)                              \
+do {                                                   \
+       int __subE = (FN);                              \
+       if (__subE)                                     \
+               (E) = __subE;                           \
+} while (0)
+
+#define label_isprofile(X) ((X)->flags & FLAG_PROFILE)
+#define label_unconfined(X) ((X)->flags & FLAG_UNCONFINED)
+#define unconfined(X) label_unconfined(X)
+#define label_is_stale(X) ((X)->flags & FLAG_STALE)
+#define __label_make_stale(X) do {        \
+       labelsetstats_inc(labels_set(X), stale); \
+       ((X)->flags |= FLAG_STALE);        \
+} while (0)
+#define labels_ns(X) (vec_ns(&((X)->vec[0]), (X)->size))
+#define labels_set(X) (&labels_ns(X)->labels)
+#define labels_profile(X) ((X)->vec[(X)->size - 1])
+
+
+int aa_label_next_confined(struct aa_label *l, int i);
+
+/* for each profile in a label */
+#define label_for_each_init(I) ((I).i = 0);
+#define label_for_each_next(I) (++((I).i))
+#define label_for_each_curr(I, L) ({ (L)->vec[(I).i] ; })
+#define label_for_each(I, L, P)                                                \
+       for ((I).i = 0; ((P) = (L)->vec[(I).i]); ++((I).i))
+
+/* assumes break/goto ended label_for_each */
+#define label_for_each_cont(I, L, P)                                   \
+  for (++((I).i); ((P) = (L)->vec[(I).i]);     ++((I).i))
+
+#define next_comb(I, L1, L2)                                           \
+do {                                                                   \
+       (I).j++;                                                        \
+       if ((I).j >= (L2)->size) {                                      \
+               (I).i++;                                                \
+               (I).j = 0;                                              \
+       }                                                               \
+} while (0)
+
+/* TODO: label_for_each_ns_comb */
+
+/* for each combination of P1 in L1, and P2 in L2 */
+#define label_for_each_comb(I, L1, L2, P1, P2)                         \
+for ((I).i = (I).j = 0;                                                        \
+     ((P1) = (L1)->vec[(I).i]) && ((P2) = (L2)->vec[(I).j]);           \
+     (I) = next_comb(I, L1, L2))
+
+#define fn_for_each_comb(L1, L2, P1, P2, FN)                           \
+({                                                                     \
+       struct label_it i;                                              \
+       int __E = 0;                                                    \
+       label_for_each_comb(i, (L1), (L2), (P1), (P2)) {                \
+               last_error(__E, (FN));                                  \
+       }                                                               \
+       __E;                                                            \
+})
+
+/* internal cross check */
+//fn_for_each_comb(L1, L2, P1, P2, xcheck(...));
+
+/* external cross check */
+// xcheck(fn_for_each_comb(L1, L2, ...),
+//        fn_for_each_comb(L2, L1, ...));
+
+/* for each profile that is enforcing confinement in a label */
+#define label_for_each_confined(I, L, P)                               \
+       for ((I).i = aa_label_next_confined((L), 0);                    \
+            ((P) = (L)->vec[(I).i]);                                   \
+            (I).i = aa_label_next_confined((L), (I).i + 1))
+
+#define label_for_each_in_merge(I, A, B, P)                            \
+       for ((I).i = (I).j = 0;                                         \
+            ((P) = aa_label_next_in_merge(&(I), (A), (B)));            \
+            )
+
+#define label_for_each_not_in_set(I, SET, SUB, P)                      \
+       for ((I).i = (I).j = 0;                                         \
+            ((P) = __aa_label_next_not_in_set(&(I), (SET), (SUB)));    \
+            )
+
+#define next_in_ns(i, NS, L)                                           \
+({                                                                     \
+       typeof(i) ___i = (i);                                           \
+       while ((L)->vec[___i] && (L)->vec[___i]->ns != (NS))            \
+               (___i)++;                                               \
+       (___i);                                                         \
+})
+
+#define label_for_each_in_ns(I, NS, L, P)                              \
+       for ((I).i = next_in_ns(0, (NS), (L));                          \
+            ((P) = (L)->vec[(I).i]);                                   \
+            (I).i = next_in_ns((I).i + 1, (NS), (L)))
+
+#define fn_for_each_in_ns(L, P, FN)                                    \
+({                                                                     \
+       struct label_it __i;                                            \
+       struct aa_ns *__ns = labels_ns(L);                              \
+       int __E = 0;                                                    \
+       label_for_each_in_ns(__i, __ns, (L), (P)) {                     \
+               last_error(__E, (FN));                                  \
+       }                                                               \
+       __E;                                                            \
+})
+
+
+#define fn_for_each_XXX(L, P, FN, ...)                                 \
+({                                                                     \
+       struct label_it i;                                              \
+       int __E = 0;                                                    \
+       label_for_each ## __VA_ARGS__ (i, (L), (P)) {                   \
+               last_error(__E, (FN));                                  \
+       }                                                               \
+       __E;                                                            \
+})
+
+#define fn_for_each(L, P, FN) fn_for_each_XXX(L, P, FN)
+#define fn_for_each_confined(L, P, FN) fn_for_each_XXX(L, P, FN, _confined)
+
+#define fn_for_each2_XXX(L1, L2, P, FN, ...)                           \
+({                                                                     \
+       struct label_it i;                                              \
+       int __E = 0;                                                    \
+       label_for_each ## __VA_ARGS__(i, (L1), (L2), (P)) {             \
+               last_error(__E, (FN));                                  \
+       }                                                               \
+       __E;                                                            \
+})
+
+#define fn_for_each_in_merge(L1, L2, P, FN)                            \
+       fn_for_each2_XXX((L1), (L2), P, FN, _in_merge)
+#define fn_for_each_not_in_set(L1, L2, P, FN)                          \
+       fn_for_each2_XXX((L1), (L2), P, FN, _not_in_set)
+
+#define LABEL_MEDIATES(L, C)                                           \
+({                                                                     \
+       struct aa_profile *profile;                                     \
+       struct label_it i;                                              \
+       int ret = 0;                                                    \
+       label_for_each(i, (L), profile) {                               \
+               if (PROFILE_MEDIATES(profile, (C))) {                   \
+                       ret = 1;                                        \
+                       break;                                          \
+               }                                                       \
+       }                                                               \
+       ret;                                                            \
+})
+
+
+void aa_labelset_destroy(struct aa_labelset *ls);
+void aa_labelset_init(struct aa_labelset *ls);
+void __aa_labelset_update_subtree(struct aa_ns *ns);
+
+void aa_label_free(struct aa_label *label);
+void aa_label_kref(struct kref *kref);
+bool aa_label_init(struct aa_label *label, int size);
+struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp);
+
+bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub);
+struct aa_profile *__aa_label_next_not_in_set(struct label_it *I,
+                                            struct aa_label *set,
+                                            struct aa_label *sub);
+bool aa_label_remove(struct aa_label *label);
+struct aa_label *aa_label_insert(struct aa_labelset *ls, struct aa_label *l);
+bool aa_label_replace(struct aa_label *old, struct aa_label *new);
+bool aa_label_make_newest(struct aa_labelset *ls, struct aa_label *old,
+                         struct aa_label *new);
+
+struct aa_label *aa_label_find(struct aa_label *l);
+
+struct aa_profile *aa_label_next_in_merge(struct label_it *I,
+                                         struct aa_label *a,
+                                         struct aa_label *b);
+struct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b);
+struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b,
+                               gfp_t gfp);
+
+
+bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp);
+
+#define FLAGS_NONE 0
+#define FLAG_SHOW_MODE 1
+#define FLAG_VIEW_SUBNS 2
+#define FLAG_HIDDEN_UNCONFINED 4
+int aa_profile_snxprint(char *str, size_t size, struct aa_ns *ns,
+                       struct aa_profile *profile, int flags);
+int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns,
+                     struct aa_label *label, int flags);
+int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label,
+                     int flags, gfp_t gfp);
+int aa_label_acntsxprint(char __counted **strp, struct aa_ns *ns,
+                        struct aa_label *label, int flags, gfp_t gfp);
+void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns,
+                    struct aa_label *label, int flags, gfp_t gfp);
+void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
+                        struct aa_label *label, int flags, gfp_t gfp);
+void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
+                     gfp_t gfp);
+void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp);
+void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp);
+void aa_label_printk(struct aa_label *label, gfp_t gfp);
+
+struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
+                               gfp_t gfp, bool create, bool force_stack);
+
+
+struct aa_perms;
+int aa_label_match(struct aa_profile *profile, struct aa_label *label,
+                  unsigned int state, bool subns, u32 request,
+                  struct aa_perms *perms);
+
+
+static inline struct aa_label *aa_get_label(struct aa_label *l)
+{
+       if (l)
+               kref_get(&(l->count));
+
+       return l;
+}
+
+static inline struct aa_label *aa_get_label_not0(struct aa_label *l)
+{
+       if (l && kref_get_not0(&l->count))
+               return l;
+
+       return NULL;
+}
+
+/**
+ * aa_get_label_rcu - increment refcount on a label that can be replaced
+ * @l: pointer to label that can be replaced (NOT NULL)
+ *
+ * Returns: pointer to a refcounted label.
+ *     else NULL if no label
+ */
+static inline struct aa_label *aa_get_label_rcu(struct aa_label __rcu **l)
+{
+       struct aa_label *c;
+
+       rcu_read_lock();
+       do {
+               c = rcu_dereference(*l);
+       } while (c && !kref_get_not0(&c->count));
+       rcu_read_unlock();
+
+       return c;
+}
+
+/**
+ * aa_get_newest_label - find the newest version of @l
+ * @l: the label to check for newer versions of
+ *
+ * Returns: refcounted newest version of @l taking into account
+ *          replacement, renames and removals
+ *          return @l.
+ */
+static inline struct aa_label *aa_get_newest_label(struct aa_label *l)
+{
+       if (!l)
+               return NULL;
+
+       if (label_is_stale(l)) {
+               struct aa_label *tmp;
+               AA_BUG(!l->proxy);
+               AA_BUG(!l->proxy->label);
+               /* BUG: only way this can happen is @l ref count and its
+                * replacement count have gone to 0 and are on their way
+                * to destruction. ie. we have a refcounting error
+                */
+               AA_BUG(!(tmp = aa_get_label_rcu(&l->proxy->label)));
+               return tmp;
+       }
+
+       return aa_get_label(l);
+}
+
+static inline void aa_put_label(struct aa_label *l)
+{
+       if (l)
+               kref_put(&l->count, aa_label_kref);
+}
+
+
+struct aa_proxy *aa_alloc_proxy(struct aa_label *l, gfp_t gfp);
+void aa_proxy_kref(struct kref *kref);
+
+static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *proxy)
+{
+       if (proxy)
+               kref_get(&(proxy->count));
+
+       return proxy;
+}
+
+static inline void aa_put_proxy(struct aa_proxy *proxy)
+{
+       if (proxy)
+               kref_put(&proxy->count, aa_proxy_kref);
+}
+
+void __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new);
+
+#endif /* __AA_LABEL_H */
diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h
new file mode 100644 (file)
index 0000000..954e22a
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor lib definitions
+ *
+ * 2016 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_LIB_H
+#define __AA_LIB_H
+
+#include <linux/slab.h>
+#include <linux/fs.h>
+
+#include "match.h"
+
+/* Provide our own test for whether a write lock is held for asserts
+ * this is because on none SMP systems write_can_lock will always
+ * resolve to true, which is what you want for code making decisions
+ * based on it, but wrong for asserts checking that the lock is held
+ */
+#ifdef CONFIG_SMP
+#define write_is_locked(X) !write_can_lock(X)
+#else
+#define write_is_locked(X) (1)
+#endif /* CONFIG_SMP */
+
+/*
+ * DEBUG remains global (no per profile flag) since it is mostly used in sysctl
+ * which is not related to profile accesses.
+ */
+
+#define DEBUG_ON (aa_g_debug && printk_ratelimit())
+#define dbg_printk(__fmt, __args...) printk(KERN_DEBUG __fmt, ##__args)
+#define AA_DEBUG(fmt, args...)                                         \
+       do {                                                            \
+               if (DEBUG_ON)                                           \
+                       dbg_printk("AppArmor: " fmt, ##args);           \
+       } while (0)
+
+#define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __FUNCTION__, #X)
+
+#define AA_BUG(X, args...) AA_BUG_FMT((X), "" args )
+#define AA_BUG_FMT(X, fmt, args...)                                    \
+       WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __FUNCTION__ , ##args )
+
+#define AA_ERROR(fmt, args...)                                         \
+       do {                                                            \
+               if (printk_ratelimit())                                 \
+                       printk(KERN_ERR "AppArmor: " fmt, ##args);      \
+       } while (0)
+
+/* Flag indicating whether initialization completed */
+extern int apparmor_initialized __initdata;
+
+/* fn's in lib */
+char *aa_split_fqname(char *args, char **ns_name);
+const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
+                            size_t *ns_len);
+void aa_info_message(const char *str);
+void *__aa_kvmalloc(size_t size, gfp_t flags);
+
+static inline void *kvmalloc(size_t size)
+{
+       return __aa_kvmalloc(size, 0);
+}
+
+static inline void *kvzalloc(size_t size)
+{
+       return __aa_kvmalloc(size, __GFP_ZERO);
+}
+
+/* returns 0 if kref not incremented */
+static inline int kref_get_not0(struct kref *kref)
+{
+       return atomic_inc_not_zero(&kref->refcount);
+}
+
+/**
+ * aa_strneq - compare null terminated @str to a non null terminated substring
+ * @str: a null terminated string
+ * @sub: a substring, not necessarily null terminated
+ * @len: length of @sub to compare
+ *
+ * The @str string must be full consumed for this to be considered a match
+ */
+static inline bool aa_strneq(const char *str, const char *sub, int len)
+{
+       return !strncmp(str, sub, len) && !str[len];
+}
+
+/**
+ * aa_dfa_null_transition - step to next state after null character
+ * @dfa: the dfa to match against
+ * @start: the state of the dfa to start matching in
+ *
+ * aa_dfa_null_transition transitions to the next state after a null
+ * character which is not used in standard matching and is only
+ * used to separate pairs.
+ */
+static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
+                                                 unsigned int start)
+{
+       /* the null transition only needs the string's null terminator byte */
+       return aa_dfa_next(dfa, start, 0);
+}
+
+static inline bool path_mediated_fs(struct dentry *dentry)
+{
+       return !(dentry->d_sb->s_flags & MS_NOUSER);
+}
+
+
+struct counted_str {
+       struct kref count;
+       char name[];
+};
+
+#define str_to_counted(str) \
+       ((struct counted_str *)(str - offsetof(struct counted_str,name)))
+
+#define __counted      /* atm just a notation */
+
+void aa_str_kref(struct kref *kref);
+char *aa_str_alloc(int size, gfp_t gfp);
+
+
+static inline __counted char *aa_get_str(__counted char *str)
+{
+       if (str)
+               kref_get(&(str_to_counted(str)->count));
+
+       return str;
+}
+
+static inline void aa_put_str(__counted char *str)
+{
+       if (str)
+               kref_put(&str_to_counted(str)->count, aa_str_kref);
+}
+
+const char *aa_imode_name(umode_t mode);
+
+
+/* struct aa_policy - common part of both namespaces and profiles
+ * @name: name of the object
+ * @hname - The hierarchical name, NOTE: is .name of struct counted_str
+ * @list: list policy object is on
+ * @profiles: head of the profiles list contained in the object
+ */
+struct aa_policy {
+       const char *name;
+       __counted char *hname;
+       struct list_head list;
+       struct list_head profiles;
+};
+
+#define aa_peer_name(peer) (peer)->base.hname
+
+/**
+ * basename - find the last component of an hname
+ * @name: hname to find the base profile name component of  (NOT NULL)
+ *
+ * Returns: the tail (base profile name) name component of an hname
+ */
+static inline const char *basename(const char *hname)
+{
+       char *split;
+       hname = strim((char *)hname);
+       for (split = strstr(hname, "//"); split; split = strstr(hname, "//"))
+               hname = split + 2;
+
+       return hname;
+}
+
+/**
+ * __policy_find - find a policy by @name on a policy list
+ * @head: list to search  (NOT NULL)
+ * @name: name to search for  (NOT NULL)
+ *
+ * Requires: rcu_read_lock be held
+ *
+ * Returns: unrefcounted policy that match @name or NULL if not found
+ */
+static inline struct aa_policy *__policy_find(struct list_head *head,
+                                             const char *name)
+{
+       struct aa_policy *policy;
+
+       list_for_each_entry_rcu(policy, head, list) {
+               if (!strcmp(policy->name, name))
+                       return policy;
+       }
+       return NULL;
+}
+
+/**
+ * __policy_strn_find - find a policy that's name matches @len chars of @str
+ * @head: list to search  (NOT NULL)
+ * @str: string to search for  (NOT NULL)
+ * @len: length of match required
+ *
+ * Requires: rcu_read_lock be held
+ *
+ * Returns: unrefcounted policy that match @str or NULL if not found
+ *
+ * if @len == strlen(@strlen) then this is equiv to __policy_find
+ * other wise it allows searching for policy by a partial match of name
+ */
+static inline struct aa_policy *__policy_strn_find(struct list_head *head,
+                                                  const char *str, int len)
+{
+       struct aa_policy *policy;
+
+       list_for_each_entry_rcu(policy, head, list) {
+               if (aa_strneq(policy->name, str, len))
+                       return policy;
+       }
+
+       return NULL;
+}
+
+bool aa_policy_init(struct aa_policy *policy, const char *prefix,
+                   const char *name, gfp_t gfp);
+void aa_policy_destroy(struct aa_policy *policy);
+
+
+/*
+ * fn_label_build - abstract out the build of a label transition
+ * @L: label the transition is being computed for
+ * @P: profile parameter derived from L by this macro, can be passed to FN
+ * @GFP: memory allocation type to use
+ * @FN: fn to call for each profile transition. @P is set to the profile
+ *
+ * Returns: new label on success
+ *          ERR_PTR if build @FN fails
+ *          NULL if label_build fails due to low memory conditions
+ *
+ * @FN must return a label or ERR_PTR on failure. NULL is not allowed
+ */
+#define fn_label_build(L, P, GFP, FN)                                  \
+({                                                                     \
+       __label__ __cleanup, __done;                                    \
+       struct aa_label *__new_;                                        \
+                                                                       \
+       if ((L)->size > 1) {                                            \
+               /* TODO: add cache of transitions already done */       \
+               struct label_it __i;                                    \
+               int __j, __k, __count;                                  \
+               DEFINE_VEC(label, __lvec);                              \
+               DEFINE_VEC(profile, __pvec);                            \
+               if (vec_setup(label, __lvec, (L)->size, (GFP))) {       \
+                       __new_ = NULL;                                  \
+                       goto __done;                                    \
+               }                                                       \
+               __j = 0;                                                \
+               label_for_each(__i, (L), (P)) {                         \
+                       __new_ = (FN);                                  \
+                       AA_BUG(!__new_);                                \
+                       if (IS_ERR(__new_))                             \
+                               goto __cleanup;                         \
+                       __lvec[__j++] = __new_;                         \
+               }                                                       \
+               for (__j = __count = 0; __j < (L)->size; __j++)         \
+                       __count += __lvec[__j]->size;                   \
+               if (!vec_setup(profile, __pvec, __count, (GFP))) {      \
+                       for (__j = __k = 0; __j < (L)->size; __j++) {   \
+                               label_for_each(__i, __lvec[__j], (P))   \
+                                       __pvec[__k++] = aa_get_profile(P); \
+                       }                                               \
+                       __count -= aa_vec_unique(__pvec, __count, 0);   \
+                       if (__count > 1) {                              \
+                               __new_ = aa_vec_find_or_create_label(__pvec,\
+                                                    __count, (GFP));   \
+                               /* only fails if out of Mem */          \
+                               if (!__new_)                            \
+                                       __new_ = NULL;                  \
+                       } else                                          \
+                               __new_ = aa_get_label(&__pvec[0]->label); \
+                       vec_cleanup(profile, __pvec, __count);          \
+               } else                                                  \
+                       __new_ = NULL;                                  \
+       __cleanup:                                                      \
+               vec_cleanup(label, __lvec, (L)->size);                  \
+       } else {                                                        \
+               (P) = labels_profile(L);                                \
+               __new_ = (FN);                                          \
+       }                                                               \
+__done:                                                                        \
+       if (!__new_)                                                    \
+               AA_DEBUG("label build failed\n");                       \
+       (__new_);                                                       \
+})
+
+
+#define __fn_build_in_ns(NS, P, NS_FN, OTHER_FN)                       \
+({                                                                     \
+       struct aa_label *__new;                                         \
+       if ((P)->ns != (NS))                                            \
+               __new = (OTHER_FN);                                     \
+       else                                                            \
+               __new = (NS_FN);                                        \
+       (__new);                                                        \
+})
+
+#define fn_label_build_in_ns(L, P, GFP, NS_FN, OTHER_FN)               \
+({                                                                     \
+       fn_label_build((L), (P), (GFP),                                 \
+               __fn_build_in_ns(labels_ns(L), (P), (NS_FN), (OTHER_FN))); \
+})
+
+#endif /* __AA_LIB_H */
index 001c43aa04065b99bdc3f6484b85213de3a2bf7e..d333b34832a4a969b0529e323f2ebe5837f6e784 100644 (file)
@@ -99,6 +99,8 @@ struct aa_dfa {
        struct table_header *tables[YYTD_ID_TSIZE];
 };
 
+extern struct aa_dfa *nulldfa;
+
 #define byte_to_byte(X) (X)
 
 #define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \
@@ -116,6 +118,9 @@ static inline size_t table_size(size_t len, size_t el_size)
        return ALIGN(sizeof(struct table_header) + len * el_size, 8);
 }
 
+int aa_setup_dfa_engine(void);
+void aa_teardown_dfa_engine(void);
+
 struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags);
 unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
                              const char *str, int len);
@@ -126,6 +131,21 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
 
 void aa_dfa_free_kref(struct kref *kref);
 
+/**
+ * aa_get_dfa - increment refcount on dfa @p
+ * @dfa: dfa  (MAYBE NULL)
+ *
+ * Returns: pointer to @dfa if @dfa is NULL will return NULL
+ * Requires: @dfa must be held with valid refcount when called
+ */
+static inline struct aa_dfa *aa_get_dfa(struct aa_dfa *dfa)
+{
+       if (dfa)
+               kref_get(&(dfa->count));
+
+       return dfa;
+}
+
 /**
  * aa_put_dfa - put a dfa refcount
  * @dfa: dfa to put refcount   (MAYBE NULL)
diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h
new file mode 100644 (file)
index 0000000..a12b5b4
--- /dev/null
@@ -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 <linux/fs.h>
+#include <linux/path.h>
+
+#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_MNT_CONT_MATCH      0x40
+
+#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN)
+
+int aa_remount(struct aa_label *label, struct path *path, unsigned long flags,
+              void *data);
+
+int aa_bind_mount(struct aa_label *label, struct path *path,
+                 const char *old_name, unsigned long flags);
+
+
+int aa_mount_change_type(struct aa_label *label, struct path *path,
+                        unsigned long flags);
+
+int aa_move_mount(struct aa_label *label, struct path *path,
+                 const char *old_name);
+
+int aa_new_mount(struct aa_label *label, const char *dev_name,
+                struct path *path, const char *type, unsigned long flags,
+                void *data);
+
+int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags);
+
+int aa_pivotroot(struct aa_label *label, struct path *old_path,
+                 struct path *new_path);
+
+#endif /* __AA_MOUNT_H */
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
new file mode 100644 (file)
index 0000000..b97e673
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor network mediation definitions.
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2014 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 <linux/path.h>
+
+#include "apparmorfs.h"
+#include "label.h"
+#include "perms.h"
+#include "policy.h"
+
+#define AA_MAY_SEND            AA_MAY_WRITE
+#define AA_MAY_RECEIVE         AA_MAY_READ
+
+#define AA_MAY_SHUTDOWN                AA_MAY_DELETE
+
+#define AA_MAY_CONNECT         AA_MAY_OPEN
+#define AA_MAY_ACCEPT          0x00100000
+
+#define AA_MAY_BIND            0x00200000
+#define AA_MAY_LISTEN          0x00400000
+
+#define AA_MAY_SETOPT          0x01000000
+#define AA_MAY_GETOPT          0x02000000
+
+#define NET_PERMS_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE |    \
+                       AA_MAY_SHUTDOWN | AA_MAY_BIND | AA_MAY_LISTEN |   \
+                       AA_MAY_CONNECT | AA_MAY_ACCEPT | AA_MAY_SETATTR | \
+                       AA_MAY_GETATTR | AA_MAY_SETOPT | AA_MAY_GETOPT)
+
+#define NET_FS_PERMS (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE |   \
+                     AA_MAY_SHUTDOWN | AA_MAY_CONNECT | AA_MAY_RENAME |\
+                     AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_CHMOD |  \
+                     AA_MAY_CHOWN | AA_MAY_CHGRP | AA_MAY_LOCK |       \
+                     AA_MAY_MPROT)
+
+#define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \
+                      AA_MAY_ACCEPT)
+struct aa_sk_ctx {
+       struct aa_label *label;
+       struct aa_label *peer;
+       struct path path;
+};
+
+#define SK_CTX(X) (X)->sk_security
+#define SOCK_ctx(X) SOCK_INODE(X)->i_security
+#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P)                                  \
+       struct lsm_network_audit NAME ## _net = { .sk = (SK),             \
+                                                 .family = (F)};         \
+       DEFINE_AUDIT_DATA(NAME,                                           \
+                         ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \
+                                                    LSM_AUDIT_DATA_NONE, \
+                         OP);                                            \
+       NAME.u.net = &(NAME ## _net);                                     \
+       aad(&NAME)->net.type = (T);                                       \
+       aad(&NAME)->net.protocol = (P)
+
+#define DEFINE_AUDIT_SK(NAME, OP, SK)                                  \
+       DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type,  \
+                        (SK)->sk_protocol)
+
+/* 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[];
+
+void audit_net_cb(struct audit_buffer *ab, void *va);
+int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
+                      u32 request, u16 family, int type);
+static inline int aa_profile_af_sk_perm(struct aa_profile *profile,
+                                       struct common_audit_data *sa,
+                                       u32 request,
+                                       struct sock *sk)
+{
+       return aa_profile_af_perm(profile, sa, request, sk->sk_family,
+                                 sk->sk_type);
+}
+
+int aa_sock_perm(const char *op, u32 request, struct socket *sock);
+int aa_sock_create_perm(struct aa_label *label, int family, int type,
+                       int protocol);
+int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address,
+                     int addrlen);
+int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address,
+                        int addrlen);
+int aa_sock_listen_perm(struct socket *sock, int backlog);
+int aa_sock_accept_perm(struct socket *sock, struct socket *newsock);
+int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock,
+                    struct msghdr *msg, int size);
+int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level,
+                    int optname);
+int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
+                     struct socket *sock);
+
+
+static inline void aa_free_net_rules(struct aa_net *new)
+{
+       /* NOP */
+}
+
+#endif /* __AA_NET_H */
index 286ac75dc88b62aa36db2501c52a0355e9679710..79c48d4691f7b631d1a74b18dadf68e4d7441372 100644 (file)
 
 enum path_flags {
        PATH_IS_DIR = 0x1,              /* path is a directory */
+       PATH_SOCK_COND = 0x2,
        PATH_CONNECT_PATH = 0x4,        /* connect disconnected paths to / */
        PATH_CHROOT_REL = 0x8,          /* do path lookup relative to chroot */
        PATH_CHROOT_NSCONNECT = 0x10,   /* connect paths that are at ns root */
 
        PATH_DELEGATE_DELETED = 0x08000, /* delegate deleted files */
-       PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */
+       PATH_MEDIATE_DELETED = 0x10000,  /* mediate deleted paths */
 };
 
-int aa_path_name(struct path *path, int flags, char **buffer,
-                const char **name, const char **info);
+int aa_path_name(struct path *path, int flags, char *buffer,
+                const char **name, const char **info, const char *disconnect);
+
+#define MAX_PATH_BUFFERS 2
+
+/* Per cpu buffers used during mediation */
+/* preallocated buffers to use during path lookups */
+struct aa_buffers {
+       char *buf[MAX_PATH_BUFFERS];
+};
+
+#include <linux/percpu.h>
+#include <linux/preempt.h>
+
+DECLARE_PER_CPU(struct aa_buffers, aa_buffers);
+
+#define COUNT_ARGS(X...) COUNT_ARGS_HELPER ( , ##X ,9,8,7,6,5,4,3,2,1,0)
+#define COUNT_ARGS_HELPER(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,n,X...) n
+#define CONCAT(X, Y) X ## Y
+#define CONCAT_AFTER(X, Y) CONCAT(X, Y)
+
+#define ASSIGN(FN, X, N) do { (X) = FN(N); } while (0)
+#define EVAL1(FN, X) ASSIGN(FN, X, 0) /*X = FN(0)*/
+#define EVAL2(FN, X, Y...) ASSIGN(FN, X, 1); /*X = FN(1);*/ EVAL1(FN, Y)
+#define EVAL(FN, X...) CONCAT_AFTER(EVAL, COUNT_ARGS(X))(FN, X)
+
+#define for_each_cpu_buffer(I) for ((I) = 0; (I) < MAX_PATH_BUFFERS; (I)++)
+
+#ifdef CONFIG_DEBUG_PREEMPT
+#define AA_BUG_PREEMPT_ENABLED(X) AA_BUG(preempt_count() <= 0, X)
+#else
+#define AA_BUG_PREEMPT_ENABLED(X) /* nop */
+#endif
+
+#define __get_buffer(N) ({                                     \
+       struct aa_buffers *__cpu_var; \
+       AA_BUG_PREEMPT_ENABLED("__get_buffer without preempt disabled");  \
+       __cpu_var = this_cpu_ptr(&aa_buffers);                  \
+        __cpu_var->buf[(N)]; })
+
+#define __get_buffers(X...)            \
+do {                                   \
+       EVAL(__get_buffer, X);          \
+} while (0)
+
+#define __put_buffers(X, Y...) (void)&(X)
+
+#define get_buffers(X...)      \
+do {                           \
+       preempt_disable();      \
+       __get_buffers(X);       \
+} while (0)
+
+#define put_buffers(X, Y...)   \
+do {                           \
+       __put_buffers(X, Y);    \
+       preempt_enable();       \
+} while (0)
 
 #endif /* __AA_PATH_H */
diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h
new file mode 100644 (file)
index 0000000..175fe13
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor basic permission sets definitions.
+ *
+ * Copyright 2013 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_PERM_H
+#define __AA_PERM_H
+
+#include <linux/fs.h>
+#include "label.h"
+
+#define AA_MAY_EXEC            MAY_EXEC
+#define AA_MAY_WRITE           MAY_WRITE
+#define AA_MAY_READ            MAY_READ
+#define AA_MAY_APPEND          MAY_APPEND
+
+#define AA_MAY_CREATE          0x0010
+#define AA_MAY_DELETE          0x0020
+#define AA_MAY_OPEN            0x0040
+#define AA_MAY_RENAME          0x0080          /* pair */
+
+#define AA_MAY_SETATTR         0x0100          /* meta write */
+#define AA_MAY_GETATTR         0x0200          /* meta read */
+#define AA_MAY_SETCRED         0x0400          /* security cred/attr */
+#define AA_MAY_GETCRED         0x0800
+
+#define AA_MAY_CHMOD           0x1000          /* pair */
+#define AA_MAY_CHOWN           0x2000          /* pair */
+#define AA_MAY_CHGRP           0x4000          /* pair */
+#define AA_MAY_LOCK            0x8000          /* LINK_SUBSET overlaid */
+
+#define AA_EXEC_MMAP           0x00010000
+#define AA_MAY_MPROT           0x00020000      /* extend conditions */
+#define AA_MAY_LINK            0x00040000      /* pair */
+#define AA_MAY_SNAPSHOT                0x00080000      /* pair */
+
+#define AA_MAY_DELEGATE
+#define AA_CONT_MATCH          0x08000000
+
+#define AA_MAY_STACK           0x10000000
+#define AA_MAY_ONEXEC          0x20000000 /* either stack or change_profile */
+#define AA_MAY_CHANGE_PROFILE  0x40000000
+#define AA_MAY_CHANGEHAT       0x80000000
+
+#define AA_LINK_SUBSET         AA_MAY_LOCK     /* overlaid */
+
+
+#define PERMS_CHRS_MASK (MAY_READ | MAY_WRITE | AA_MAY_CREATE |                \
+                        AA_MAY_DELETE | AA_MAY_LINK | AA_MAY_LOCK |    \
+                        AA_MAY_EXEC | AA_EXEC_MMAP | AA_MAY_APPEND)
+
+#define PERMS_NAMES_MASK (PERMS_CHRS_MASK | AA_MAY_OPEN | AA_MAY_RENAME |     \
+                         AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_SETCRED | \
+                         AA_MAY_GETCRED | AA_MAY_CHMOD | AA_MAY_CHOWN | \
+                         AA_MAY_CHGRP | AA_MAY_MPROT | AA_MAY_SNAPSHOT | \
+                         AA_MAY_STACK | AA_MAY_ONEXEC |                \
+                         AA_MAY_CHANGE_PROFILE | AA_MAY_CHANGEHAT)
+
+extern const char aa_file_perm_chrs[];
+extern const char *aa_file_perm_names[];
+
+
+struct aa_perms {
+       u32 allow;
+       u32 audit;      /* set only when allow is set */
+
+       u32 deny;       /* explicit deny, or conflict if allow also set */
+       u32 quiet;      /* set only when ~allow | deny */
+       u32 kill;       /* set only when ~allow | deny */
+       u32 stop;       /* set only when ~allow | deny */
+
+       u32 complain;   /* accumulates only used when ~allow & ~deny */
+       u32 cond;       /* set only when ~allow and ~deny */
+
+       u32 hide;       /* set only when  ~allow | deny */
+       u32 prompt;     /* accumulates only used when ~allow & ~deny */
+
+       /* Reserved:
+        * u32 subtree; / * set only when allow is set * /
+        */
+       u16 xindex;
+};
+
+#define ALL_PERMS_MASK 0xffffffff
+extern struct aa_perms nullperms;
+extern struct aa_perms allperms;
+
+
+#define xcheck(FN1, FN2)       \
+({                             \
+       int e, error = FN1;     \
+       e = FN2;                \
+       if (e)                  \
+               error = e;      \
+       error;                  \
+})
+
+
+/* TODO: update for labels pointing to labels instead of profiles
+*  Note: this only works for profiles from a single namespace
+*/
+
+#define xcheck_profile_label(P, L, FN, args...)                        \
+({                                                             \
+       struct aa_profile *__p2;                                \
+       fn_for_each((L), __p2, FN((P), __p2, args));            \
+})
+
+#define xcheck_ns_labels(L1, L2, FN, args...)                  \
+({                                                             \
+       struct aa_profile *__p1;                                \
+       fn_for_each((L1), __p1, FN(__p1, (L2), args));          \
+})
+
+/* todo: fix to handle multiple namespaces */
+#define xcheck_labels(L1, L2, FN, args...)                     \
+       xcheck_ns_labels((L1), (L2), FN, args)
+
+/* Do the cross check but applying FN at the profiles level */
+#define xcheck_labels_profiles(L1, L2, FN, args...)            \
+       xcheck_ns_labels((L1), (L2), xcheck_profile_label, (FN), args)
+
+
+#define FINAL_CHECK true
+
+void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask);
+void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask);
+void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
+                       u32 chrsmask, const char **names, u32 namesmask);
+void aa_apply_modes_to_perms(struct aa_profile *profile,
+                            struct aa_perms *perms);
+void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
+                     struct aa_perms *perms);
+void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend);
+void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend);
+void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label,
+                           int type, u32 request, struct aa_perms *perms);
+int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
+                         u32 request, int type, u32 *deny,
+                         struct common_audit_data *sa);
+int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
+                  u32 request, struct common_audit_data *sa,
+                  void (*cb) (struct audit_buffer *, void *));
+
+
+static inline int aa_xlabel_perm(struct aa_profile *profile,
+                                struct aa_profile *target,
+                                int type, u32 request, u32 reverse,
+                                u32 * deny, struct common_audit_data *sa)
+{
+  /* TODO: ??? 2nd aa_profile_label_perm needs to reverse perms */
+       return xcheck(aa_profile_label_perm(profile, target, request, type,
+                                           deny, sa),
+                     aa_profile_label_perm(target, profile, request /*??*/, type,
+                                           deny, sa));
+}
+
+
+#endif /* __AA_PERM_H */
index c28b0f20ab53ed69da7c1e6b8c60a8099370a4f8..e69373d1ea0cc885ea69b5c012e4bed4de190569 100644 (file)
 #include "capability.h"
 #include "domain.h"
 #include "file.h"
+#include "label.h"
+#include "net.h"
+#include "perms.h"
 #include "resource.h"
 
+struct aa_ns;
+
 extern const char *const aa_profile_mode_names[];
 #define APPARMOR_MODE_NAMES_MAX_INDEX 4
 
@@ -40,9 +45,9 @@ extern const char *const aa_profile_mode_names[];
 
 #define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL)
 
-#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
+#define PROFILE_IS_HAT(_profile) ((_profile)->label.flags & FLAG_HAT)
 
-#define PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID)
+#define profile_is_stale(_profile) (label_is_stale(&(_profile)->label))
 
 #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2)
 
@@ -59,86 +64,6 @@ enum profile_mode {
        APPARMOR_UNCONFINED,    /* profile set to unconfined */
 };
 
-enum profile_flags {
-       PFLAG_HAT = 1,                  /* profile is a hat */
-       PFLAG_NULL = 4,                 /* profile is null learning profile */
-       PFLAG_IX_ON_NAME_ERROR = 8,     /* fallback to ix on name lookup fail */
-       PFLAG_IMMUTABLE = 0x10,         /* don't allow changes/replacement */
-       PFLAG_USER_DEFINED = 0x20,      /* user based profile - lower privs */
-       PFLAG_NO_LIST_REF = 0x40,       /* list doesn't keep profile ref */
-       PFLAG_OLD_NULL_TRANS = 0x100,   /* use // as the null transition */
-       PFLAG_INVALID = 0x200,          /* profile replaced/removed */
-       PFLAG_NS_COUNT = 0x400,         /* carries NS ref count */
-
-       /* These flags must correspond with PATH_flags */
-       PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */
-};
-
-struct aa_profile;
-
-/* struct aa_policy - common part of both namespaces and profiles
- * @name: name of the object
- * @hname - The hierarchical name
- * @list: list policy object is on
- * @profiles: head of the profiles list contained in the object
- */
-struct aa_policy {
-       char *name;
-       char *hname;
-       struct list_head list;
-       struct list_head profiles;
-};
-
-/* struct aa_ns_acct - accounting of profiles in namespace
- * @max_size: maximum space allowed for all profiles in namespace
- * @max_count: maximum number of profiles that can be in this namespace
- * @size: current size of profiles
- * @count: current count of profiles (includes null profiles)
- */
-struct aa_ns_acct {
-       int max_size;
-       int max_count;
-       int size;
-       int count;
-};
-
-/* struct aa_namespace - namespace for a set of profiles
- * @base: common policy
- * @parent: parent of namespace
- * @lock: lock for modifying the object
- * @acct: accounting for the namespace
- * @unconfined: special unconfined profile for the namespace
- * @sub_ns: list of namespaces under the current namespace.
- * @uniq_null: uniq value used for null learning profiles
- * @uniq_id: a unique id count for the profiles in the namespace
- * @dents: dentries for the namespaces file entries in apparmorfs
- *
- * An aa_namespace defines the set profiles that are searched to determine
- * which profile to attach to a task.  Profiles can not be shared between
- * aa_namespaces and profile names within a namespace are guaranteed to be
- * unique.  When profiles in separate namespaces have the same name they
- * are NOT considered to be equivalent.
- *
- * Namespaces are hierarchical and only namespaces and profiles below the
- * current namespace are visible.
- *
- * Namespace names must be unique and can not contain the characters :/\0
- *
- * FIXME TODO: add vserver support of namespaces (can it all be done in
- *             userspace?)
- */
-struct aa_namespace {
-       struct aa_policy base;
-       struct aa_namespace *parent;
-       struct mutex lock;
-       struct aa_ns_acct acct;
-       struct aa_profile *unconfined;
-       struct list_head sub_ns;
-       atomic_t uniq_null;
-       long uniq_id;
-
-       struct dentry *dents[AAFS_NS_SIZEOF];
-};
 
 /* struct aa_policydb - match engine for a policy
  * dfa: dfa pattern match
@@ -151,31 +76,24 @@ struct aa_policydb {
 
 };
 
-struct aa_replacedby {
-       struct kref count;
-       struct aa_profile __rcu *profile;
-};
-
-
 /* struct aa_profile - basic confinement data
  * @base - base components of the profile (name, refcount, lists, lock ...)
- * @count: reference count of the obj
- * @rcu: rcu head used when removing from @list
+ * @label - label this profile is an extension of
  * @parent: parent of profile
  * @ns: namespace the profile is in
- * @replacedby: is set to the profile that replaced this profile
  * @rename: optional profile name that this profile renamed
  * @attach: human readable attachment string
  * @xmatch: optional extended matching for unconfined executables names
  * @xmatch_len: xmatch prefix len, used to determine xmatch priority
  * @audit: the auditing mode of the profile
  * @mode: the enforcement mode of the profile
- * @flags: flags controlling profile behavior
  * @path_flags: flags controlling path generation behavior
+ * @disconnected: what to prepend if attach_disconnected is specified
  * @size: the memory consumed by this profiles rules
  * @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
@@ -186,8 +104,6 @@ struct aa_replacedby {
  * used to determine profile attachment against unconfined tasks.  All other
  * attachments are determined by profile X transition rules.
  *
- * The @replacedby struct is write protected by the profile lock.
- *
  * Profiles have a hierarchy where hats and children profiles keep
  * a reference to their parent.
  *
@@ -197,12 +113,9 @@ struct aa_replacedby {
  */
 struct aa_profile {
        struct aa_policy base;
-       struct kref count;
-       struct rcu_head rcu;
        struct aa_profile __rcu *parent;
 
-       struct aa_namespace *ns;
-       struct aa_replacedby *replacedby;
+       struct aa_ns *ns;
        const char *rename;
 
        const char *attach;
@@ -210,57 +123,91 @@ struct aa_profile {
        int xmatch_len;
        enum audit_mode audit;
        long mode;
-       long flags;
        u32 path_flags;
+       const char *disconnected;
        int size;
 
        struct aa_policydb policy;
        struct aa_file_rules file;
        struct aa_caps caps;
+       struct aa_net net;
        struct aa_rlimit rlimits;
 
        unsigned char *hash;
        char *dirname;
        struct dentry *dents[AAFS_PROF_SIZEOF];
+       struct aa_label label;
 };
 
-extern struct aa_namespace *root_ns;
 extern enum profile_mode aa_g_profile_mode;
 
-void aa_add_profile(struct aa_policy *common, struct aa_profile *profile);
+#define AA_MAY_LOAD_POLICY     AA_MAY_APPEND
+#define AA_MAY_REPLACE_POLICY  AA_MAY_WRITE
+#define AA_MAY_REMOVE_POLICY   AA_MAY_DELETE
 
-bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view);
-const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child);
-int aa_alloc_root_ns(void);
-void aa_free_root_ns(void);
-void aa_free_namespace_kref(struct kref *kref);
+#define profiles_ns(P) ((P)->ns)
+#define name_is_shared(A, B) ((A)->hname && (A)->hname == (B)->hname)
+
+void aa_add_profile(struct aa_policy *common, struct aa_profile *profile);
 
-struct aa_namespace *aa_find_namespace(struct aa_namespace *root,
-                                      const char *name);
 
+struct aa_label *aa_setup_default_label(void);
 
-void aa_free_replacedby_kref(struct kref *kref);
-struct aa_profile *aa_alloc_profile(const char *name);
-struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat);
+struct aa_profile *aa_alloc_profile(const char *name, struct aa_proxy *proxy,
+                                   gfp_t gfp);
+struct aa_profile *aa_null_profile(struct aa_profile *parent, bool hat,
+                                  const char *base, gfp_t gfp);
 void aa_free_profile(struct aa_profile *profile);
 void aa_free_profile_kref(struct kref *kref);
 struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name);
-struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name);
-struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name);
-
-ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace);
-ssize_t aa_remove_profiles(char *name, size_t size);
+struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname,
+                                     size_t n);
+struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name);
+struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
+                                       const char *fqname, size_t n);
+struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name);
+
+ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata,
+                           size_t size);
+ssize_t aa_remove_profiles(struct aa_label *label, char *name, size_t size);
+void __aa_profile_list_release(struct list_head *head);
 
 #define PROF_ADD 1
 #define PROF_REPLACE 0
 
-#define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED)
+#define profile_unconfined(X) ((X)->mode == APPARMOR_UNCONFINED)
 
+/**
+ * aa_get_newest_profile - simple wrapper fn to wrap the label version
+ * @p: profile (NOT NULL)
+ *
+ * Returns refcount to newest version of the profile (maybe @p)
+ *
+ * Requires: @p must be held with a valid refcount
+ */
+static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p)
+{
+       return labels_profile(aa_get_newest_label(&p->label));
+}
 
-static inline struct aa_profile *aa_deref_parent(struct aa_profile *p)
+#define PROFILE_MEDIATES(P, T)  ((P)->policy.start[(T)])
+/* safe version of POLICY_MEDIATES for full range input */
+static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile,
+                                                unsigned char class)
 {
-       return rcu_dereference_protected(p->parent,
-                                        mutex_is_locked(&p->ns->lock));
+       if (profile->policy.dfa)
+               return aa_dfa_match_len(profile->policy.dfa,
+                                       profile->policy.start[0], &class, 1);
+       return 0;
+}
+
+static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile,
+                                              u16 AF) {
+       unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET);
+       u16 be_af = cpu_to_be16(AF);
+       if (!state)
+               return 0;
+       return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2);
 }
 
 /**
@@ -273,7 +220,7 @@ static inline struct aa_profile *aa_deref_parent(struct aa_profile *p)
 static inline struct aa_profile *aa_get_profile(struct aa_profile *p)
 {
        if (p)
-               kref_get(&(p->count));
+               kref_get(&(p->label.count));
 
        return p;
 }
@@ -287,7 +234,7 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *p)
  */
 static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p)
 {
-       if (p && kref_get_not0(&p->count))
+       if (p && kref_get_not0(&p->label.count))
                return p;
 
        return NULL;
@@ -307,31 +254,12 @@ static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p)
        rcu_read_lock();
        do {
                c = rcu_dereference(*p);
-       } while (c && !kref_get_not0(&c->count));
+       } while (c && !kref_get_not0(&c->label.count));
        rcu_read_unlock();
 
        return c;
 }
 
-/**
- * aa_get_newest_profile - find the newest version of @profile
- * @profile: the profile to check for newer versions of
- *
- * Returns: refcounted newest version of @profile taking into account
- *          replacement, renames and removals
- *          return @profile.
- */
-static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p)
-{
-       if (!p)
-               return NULL;
-
-       if (PROFILE_INVALID(p))
-               return aa_get_profile_rcu(&p->replacedby->profile);
-
-       return aa_get_profile(p);
-}
-
 /**
  * aa_put_profile - decrement refcount on profile @p
  * @p: profile  (MAYBE NULL)
@@ -339,60 +267,7 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p)
 static inline void aa_put_profile(struct aa_profile *p)
 {
        if (p)
-               kref_put(&p->count, aa_free_profile_kref);
-}
-
-static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p)
-{
-       if (p)
-               kref_get(&(p->count));
-
-       return p;
-}
-
-static inline void aa_put_replacedby(struct aa_replacedby *p)
-{
-       if (p)
-               kref_put(&p->count, aa_free_replacedby_kref);
-}
-
-/* requires profile list write lock held */
-static inline void __aa_update_replacedby(struct aa_profile *orig,
-                                         struct aa_profile *new)
-{
-       struct aa_profile *tmp;
-       tmp = rcu_dereference_protected(orig->replacedby->profile,
-                                       mutex_is_locked(&orig->ns->lock));
-       rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new));
-       orig->flags |= PFLAG_INVALID;
-       aa_put_profile(tmp);
-}
-
-/**
- * aa_get_namespace - increment references count on @ns
- * @ns: namespace to increment reference count of (MAYBE NULL)
- *
- * Returns: pointer to @ns, if @ns is NULL returns NULL
- * Requires: @ns must be held with valid refcount when called
- */
-static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns)
-{
-       if (ns)
-               aa_get_profile(ns->unconfined);
-
-       return ns;
-}
-
-/**
- * aa_put_namespace - decrement refcount on @ns
- * @ns: namespace to put reference of
- *
- * Decrement reference count of @ns and if no longer in use free it
- */
-static inline void aa_put_namespace(struct aa_namespace *ns)
-{
-       if (ns)
-               aa_put_profile(ns->unconfined);
+               kref_put(&p->label.count, aa_label_kref);
 }
 
 static inline int AUDIT_MODE(struct aa_profile *profile)
@@ -403,6 +278,7 @@ static inline int AUDIT_MODE(struct aa_profile *profile)
        return profile->audit;
 }
 
-bool aa_may_manage_policy(int op);
+bool policy_admin_capable(void);
+int aa_may_manage_policy(struct aa_label *label, u32 mask);
 
 #endif /* __AA_POLICY_H */
diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h
new file mode 100644 (file)
index 0000000..65b1e1e
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor policy definitions.
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2015 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_NAMESPACE_H
+#define __AA_NAMESPACE_H
+
+#include <linux/kref.h>
+
+#include "apparmor.h"
+#include "apparmorfs.h"
+#include "label.h"
+#include "policy.h"
+
+
+/* struct aa_ns_acct - accounting of profiles in namespace
+ * @max_size: maximum space allowed for all profiles in namespace
+ * @max_count: maximum number of profiles that can be in this namespace
+ * @size: current size of profiles
+ * @count: current count of profiles (includes null profiles)
+ */
+struct aa_ns_acct {
+       int max_size;
+       int max_count;
+       int size;
+       int count;
+};
+
+/* struct aa_ns - namespace for a set of profiles
+ * @base: common policy
+ * @parent: parent of namespace
+ * @lock: lock for modifying the object
+ * @acct: accounting for the namespace
+ * @unconfined: special unconfined profile for the namespace
+ * @sub_ns: list of namespaces under the current namespace.
+ * @uniq_null: uniq value used for null learning profiles
+ * @uniq_id: a unique id count for the profiles in the namespace
+ * @dents: dentries for the namespaces file entries in apparmorfs
+ *
+ * An aa_ns defines the set profiles that are searched to determine which
+ * profile to attach to a task.  Profiles can not be shared between aa_ns
+ * and profile names within a namespace are guaranteed to be unique.  When
+ * profiles in separate namespaces have the same name they are NOT considered
+ * to be equivalent.
+ *
+ * Namespaces are hierarchical and only namespaces and profiles below the
+ * current namespace are visible.
+ *
+ * Namespace names must be unique and can not contain the characters :/\0
+ */
+struct aa_ns {
+       struct aa_policy base;
+       struct aa_ns *parent;
+       struct mutex lock;
+       struct aa_ns_acct acct;
+       struct aa_profile *unconfined;
+       struct list_head sub_ns;
+       atomic_t uniq_null;
+       long uniq_id;
+       int level;
+       struct aa_labelset labels;
+
+       struct dentry *dents[AAFS_NS_SIZEOF];
+};
+
+extern struct aa_ns *root_ns;
+
+extern const char *aa_hidden_ns_name;
+
+#define ns_unconfined(NS) (&(NS)->unconfined->label)
+
+bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns);
+const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child, bool subns);
+void aa_free_ns(struct aa_ns *ns);
+int aa_alloc_root_ns(void);
+void aa_free_root_ns(void);
+void aa_free_ns_kref(struct kref *kref);
+
+struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name);
+struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n);
+struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name);
+void __aa_remove_ns(struct aa_ns *ns);
+
+static inline struct aa_profile *aa_deref_parent(struct aa_profile *p)
+{
+       return rcu_dereference_protected(p->parent,
+                                        mutex_is_locked(&p->ns->lock));
+}
+
+/**
+ * aa_get_ns - increment references count on @ns
+ * @ns: namespace to increment reference count of (MAYBE NULL)
+ *
+ * Returns: pointer to @ns, if @ns is NULL returns NULL
+ * Requires: @ns must be held with valid refcount when called
+ */
+static inline struct aa_ns *aa_get_ns(struct aa_ns *ns)
+{
+       if (ns)
+               aa_get_profile(ns->unconfined);
+
+       return ns;
+}
+
+/**
+ * aa_put_ns - decrement refcount on @ns
+ * @ns: ns to put reference of
+ *
+ * Decrement reference count of @ns and if no longer in use free it
+ */
+static inline void aa_put_ns(struct aa_ns *ns)
+{
+       if (ns)
+               aa_put_profile(ns->unconfined);
+}
+
+#endif /* AA_NAMESPACE_H */
index c214fb88b1bc8ada00577d675de1a149f6f30cf8..c2f7a96029b6dacc0dbaea8d45c9303ca9d735b7 100644 (file)
@@ -22,6 +22,7 @@ struct aa_load_ent {
        struct aa_profile *new;
        struct aa_profile *old;
        struct aa_profile *rename;
+       const char *ns_name;
 };
 
 void aa_load_ent_free(struct aa_load_ent *ent);
index 6bd5f33d9533ab166907b5dceb9eb9f8cc591549..8fb8549a22dc5b47838d4fc23141365524120393 100644 (file)
@@ -18,8 +18,7 @@
 #define AA_DO_TEST 1
 #define AA_ONEXEC  1
 
-int aa_getprocattr(struct aa_profile *profile, char **string);
+int aa_getprocattr(struct aa_label *label, char **string);
 int aa_setprocattr_changehat(char *args, size_t size, int test);
-int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test);
 
 #endif /* __AA_PROCATTR_H */
index d3f4cf027957bcf6edc5f2cb8aa039ec1970883f..69d3b8ad12834788ef2176dfe4a69e8ac3aee368 100644 (file)
@@ -37,10 +37,10 @@ struct aa_rlimit {
 extern struct aa_fs_entry aa_fs_entry_rlimit[];
 
 int aa_map_resource(int resource);
-int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *,
+int aa_task_setrlimit(struct aa_label *label, struct task_struct *,
                      unsigned int resource, struct rlimit *new_rlim);
 
-void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new);
+void __aa_transition_rlimits(struct aa_label *old, struct aa_label *new);
 
 static inline void aa_free_rlimit_rules(struct aa_rlimit *rlims)
 {
diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h
new file mode 100644 (file)
index 0000000..8a3dfc5
--- /dev/null
@@ -0,0 +1,95 @@
+#include <linux/signal.h>
+
+#define SIGUNKNOWN 0
+#define MAXMAPPED_SIG 35
+/* provide a mapping of arch signal to internal signal # for mediation
+ * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO
+ * map to the same entry those that may/or may not get a separate entry
+ */
+static const int sig_map[MAXMAPPED_SIG] = {
+       [0] = MAXMAPPED_SIG,    /* existance test */
+       [SIGHUP] = 1,
+       [SIGINT] = 2,
+       [SIGQUIT] = 3,
+       [SIGILL] = 4,
+       [SIGTRAP] = 5,          /* -, 5, - */
+       [SIGABRT] = 6,          /*  SIGIOT: -, 6, - */
+       [SIGBUS] = 7,           /* 10, 7, 10 */
+       [SIGFPE] = 8,
+       [SIGKILL] = 9,
+       [SIGUSR1] = 10,         /* 30, 10, 16 */
+       [SIGSEGV] = 11,
+       [SIGUSR2] = 12,         /* 31, 12, 17 */
+       [SIGPIPE] = 13,
+       [SIGALRM] = 14,
+       [SIGTERM] = 15,
+       [SIGSTKFLT] = 16,       /* -, 16, - */
+       [SIGCHLD] = 17,         /* 20, 17, 18.  SIGCHLD -, -, 18 */
+       [SIGCONT] = 18,         /* 19, 18, 25 */
+       [SIGSTOP] = 19,         /* 17, 19, 23 */
+       [SIGTSTP] = 20,         /* 18, 20, 24 */
+       [SIGTTIN] = 21,         /* 21, 21, 26 */
+       [SIGTTOU] = 22,         /* 22, 22, 27 */
+       [SIGURG] = 23,          /* 16, 23, 21 */
+       [SIGXCPU] = 24,         /* 24, 24, 30 */
+       [SIGXFSZ] = 25,         /* 25, 25, 31 */
+       [SIGVTALRM] = 26,       /* 26, 26, 28 */
+       [SIGPROF] = 27,         /* 27, 27, 29 */
+       [SIGWINCH] = 28,        /* 28, 28, 20 */
+       [SIGIO] = 29,           /* SIGPOLL: 23, 29, 22 */
+       [SIGPWR] = 30,          /* 29, 30, 19.  SIGINFO 29, -, - */
+#ifdef SIGSYS
+       [SIGSYS] = 31,          /* 12, 31, 12. often SIG LOST/UNUSED */
+#endif
+#ifdef SIGEMT
+       [SIGEMT] = 32,          /* 7, - , 7 */
+#endif
+#if defined(SIGLOST) && SIGPWR != SIGLOST              /* sparc */
+       [SIGLOST] = 33,         /* unused on Linux */
+#endif
+#if defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS
+       [SIGUNUSED] = 34,       /* -, 31, - */
+#endif
+};
+
+/* this table is ordered post sig_map[sig] mapping */
+static const char *const sig_names[MAXMAPPED_SIG + 1] = {
+       "unknown",
+       "hup",
+       "int",
+       "quit",
+       "ill",
+       "trap",
+       "abrt",
+       "bus",
+       "fpe",
+       "kill",
+       "usr1",
+       "segv",
+       "usr2",
+       "pipe",
+       "alrm",
+       "term",
+       "stkflt",
+       "chld",
+       "cont",
+       "stop",
+       "stp",
+       "ttin",
+       "ttou",
+       "urg",
+       "xcpu",
+       "xfsz",
+       "vtalrm",
+       "prof",
+       "winch",
+       "io",
+       "pwr",
+       "sys",
+       "emt",
+       "lost",
+       "unused",
+
+       "exists",       /* always last existance test mapped to MAXMAPPED_SIG */
+};
+
index 777ac1c47253ef4f88aa1bc97c0539a9e0b96e83..d6e373f0312d147f6480548dabe811a713a3c814 100644 (file)
@@ -4,7 +4,7 @@
  * This file contains AppArmor ipc mediation
  *
  * Copyright (C) 1998-2008 Novell/SUSE
- * Copyright 2009-2010 Canonical Ltd.
+ * Copyright 2009-2013 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
 #include "include/context.h"
 #include "include/policy.h"
 #include "include/ipc.h"
+#include "include/sig_names.h"
+
+/**
+ * audit_ptrace_mask - convert mask to permission string
+ * @buffer: buffer to write string to (NOT NULL)
+ * @mask: permission mask to convert
+ */
+static void audit_ptrace_mask(struct audit_buffer *ab, u32 mask)
+{
+       switch (mask) {
+       case MAY_READ:
+               audit_log_string(ab, "read");
+               break;
+       case MAY_WRITE:
+               audit_log_string(ab, "trace");
+               break;
+       case AA_MAY_BE_READ:
+               audit_log_string(ab, "readby");
+               break;
+       case AA_MAY_BE_TRACED:
+               audit_log_string(ab, "tracedby");
+               break;
+       }
+}
 
 /* call back to audit ptrace fields */
-static void audit_cb(struct audit_buffer *ab, void *va)
+static void audit_ptrace_cb(struct audit_buffer *ab, void *va)
 {
        struct common_audit_data *sa = va;
-       audit_log_format(ab, " target=");
-       audit_log_untrustedstring(ab, sa->aad->target);
+
+       if (aad(sa)->request & AA_PTRACE_PERM_MASK) {
+               audit_log_format(ab, " requested_mask=");
+               audit_ptrace_mask(ab, aad(sa)->request);
+
+               if (aad(sa)->denied & AA_PTRACE_PERM_MASK) {
+                       audit_log_format(ab, " denied_mask=");
+                       audit_ptrace_mask(ab, aad(sa)->denied);
+               }
+       }
+       audit_log_format(ab, " peer=");
+       aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
+                       FLAGS_NONE, GFP_ATOMIC);
 }
 
-/**
- * aa_audit_ptrace - do auditing for ptrace
- * @profile: profile being enforced  (NOT NULL)
- * @target: profile being traced (NOT NULL)
- * @error: error condition
- *
- * Returns: %0 or error code
- */
-static int aa_audit_ptrace(struct aa_profile *profile,
-                          struct aa_profile *target, int error)
+/* TODO: conditionals */
+static int profile_ptrace_perm(struct aa_profile *profile,
+                              struct aa_profile *peer, u32 request,
+                              struct common_audit_data *sa)
 {
-       struct common_audit_data sa;
-       struct apparmor_audit_data aad = {0,};
-       sa.type = LSM_AUDIT_DATA_NONE;
-       sa.aad = &aad;
-       aad.op = OP_PTRACE;
-       aad.target = target;
-       aad.error = error;
-
-       return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa,
-                       audit_cb);
+  struct aa_perms perms = { };
+
+       /* need because of peer in cross check */
+       if (profile_unconfined(profile) ||
+           !PROFILE_MEDIATES(profile, AA_CLASS_PTRACE))
+                return 0;
+
+       aad(sa)->peer = &peer->label;
+       aa_profile_match_label(profile, &peer->label, AA_CLASS_PTRACE, request,
+                              &perms);
+       aa_apply_modes_to_perms(profile, &perms);
+       return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb);
+}
+
+static int cross_ptrace_perm(struct aa_profile *tracer,
+                            struct aa_profile *tracee, u32 request,
+                            struct common_audit_data *sa)
+{
+       if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE))
+               return xcheck(profile_ptrace_perm(tracer, tracee, request, sa),
+                             profile_ptrace_perm(tracee, tracer,
+                                                 request << PTRACE_PERM_SHIFT,
+                                                 sa));
+       /* policy uses the old style capability check for ptrace */
+       if (profile_unconfined(tracer) || tracer == tracee)
+               return 0;
+
+       aad(sa)->label = &tracer->label;
+       aad(sa)->peer = &tracee->label;
+       aad(sa)->request = 0;
+       aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, 1);
+       return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb);
 }
 
 /**
  * aa_may_ptrace - test if tracer task can trace the tracee
- * @tracer: profile of the task doing the tracing  (NOT NULL)
- * @tracee: task to be traced
- * @mode: whether PTRACE_MODE_READ || PTRACE_MODE_ATTACH
+ * @tracer: label of the task doing the tracing  (NOT NULL)
+ * @tracee: task label to be traced
+ * @request: permission request
  *
  * Returns: %0 else error code if permission denied or error
  */
-int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee,
-                 unsigned int mode)
+int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
+                 u32 request)
 {
-       /* TODO: currently only based on capability, not extended ptrace
-        *       rules,
-        *       Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH
-        */
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE);
 
-       if (unconfined(tracer) || tracer == tracee)
-               return 0;
-       /* log this capability request */
-       return aa_capable(tracer, CAP_SYS_PTRACE, 1);
+       return xcheck_labels_profiles(tracer, tracee, cross_ptrace_perm,
+                                     request, &sa);
+}
+
+
+static inline int map_signal_num(int sig)
+{
+       if (sig > SIGRTMAX)
+               return SIGUNKNOWN;
+       else if (sig >= SIGRTMIN)
+               return sig - SIGRTMIN + 128;    /* rt sigs mapped to 128 */
+       else if (sig <= MAXMAPPED_SIG)
+               return sig_map[sig];
+       return SIGUNKNOWN;
 }
 
 /**
- * aa_ptrace - do ptrace permission check and auditing
- * @tracer: task doing the tracing (NOT NULL)
- * @tracee: task being traced (NOT NULL)
- * @mode: ptrace mode either PTRACE_MODE_READ || PTRACE_MODE_ATTACH
- *
- * Returns: %0 else error code if permission denied or error
+ * audit_file_mask - convert mask to permission string
+ * @buffer: buffer to write string to (NOT NULL)
+ * @mask: permission mask to convert
  */
-int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,
-             unsigned int mode)
+static void audit_signal_mask(struct audit_buffer *ab, u32 mask)
+{
+       if (mask & MAY_READ)
+               audit_log_string(ab, "receive");
+       if (mask & MAY_WRITE)
+               audit_log_string(ab, "send");
+}
+
+/**
+ * audit_cb - call back for signal specific audit fields
+ * @ab: audit_buffer  (NOT NULL)
+ * @va: audit struct to audit values of  (NOT NULL)
+ */
+static void audit_signal_cb(struct audit_buffer *ab, void *va)
+{
+       struct common_audit_data *sa = va;
+
+       if (aad(sa)->request & AA_SIGNAL_PERM_MASK) {
+               audit_log_format(ab, " requested_mask=");
+               audit_signal_mask(ab, aad(sa)->request);
+               if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) {
+                       audit_log_format(ab, " denied_mask=");
+                       audit_signal_mask(ab, aad(sa)->denied);
+               }
+       }
+       if (aad(sa)->signal <= MAXMAPPED_SIG)
+               audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]);
+       else
+               audit_log_format(ab, " signal=rtmin+%d",
+                                aad(sa)->signal - 128);
+       audit_log_format(ab, " peer=");
+       aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
+                       FLAGS_NONE, GFP_ATOMIC);
+}
+
+/* TODO: update to handle compound name&name2, conditionals */
+static void profile_match_signal(struct aa_profile *profile, const char *label,
+                                int signal, struct aa_perms *perms)
 {
-       /*
-        * tracer can ptrace tracee when
-        * - tracer is unconfined ||
-        *   - tracer is in complain mode
-        *   - tracer has rules allowing it to trace tracee currently this is:
-        *       - confined by the same profile ||
-        *       - tracer profile has CAP_SYS_PTRACE
-        */
+       unsigned int state;
+       /* TODO: secondary cache check <profile, profile, perm> */
+       state = aa_dfa_next(profile->policy.dfa,
+                           profile->policy.start[AA_CLASS_SIGNAL],
+                           signal);
+       state = aa_dfa_match(profile->policy.dfa, state, label);
+       aa_compute_perms(profile->policy.dfa, state, perms);
+}
 
-       struct aa_profile *tracer_p = aa_get_task_profile(tracer);
-       int error = 0;
+static int profile_signal_perm(struct aa_profile *profile,
+                              struct aa_profile *peer, u32 request,
+                              struct common_audit_data *sa)
+{
+       struct aa_perms perms;
 
-       if (!unconfined(tracer_p)) {
-               struct aa_profile *tracee_p = aa_get_task_profile(tracee);
+       if (profile_unconfined(profile) ||
+           !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
+               return 0;
 
-               error = aa_may_ptrace(tracer_p, tracee_p, mode);
-               error = aa_audit_ptrace(tracer_p, tracee_p, error);
+       aad(sa)->peer = &peer->label;
+       profile_match_signal(profile, aa_peer_name(peer), aad(sa)->signal,
+                            &perms);
+       aa_apply_modes_to_perms(profile, &perms);
+       return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
+}
 
-               aa_put_profile(tracee_p);
-       }
-       aa_put_profile(tracer_p);
+static int aa_signal_cross_perm(struct aa_profile *sender,
+                               struct aa_profile *target,
+                               struct common_audit_data *sa)
+{
+       return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa),
+                     profile_signal_perm(target, sender, MAY_READ, sa));
+}
 
-       return error;
+int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
+{
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL);
+       aad(&sa)->signal = map_signal_num(sig);
+       return xcheck_labels_profiles(sender, target, aa_signal_cross_perm,
+                                     &sa);
 }
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
new file mode 100644 (file)
index 0000000..21c9d6f
--- /dev/null
@@ -0,0 +1,2077 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor label definitions
+ *
+ * Copyright 2013 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 <linux/audit.h>
+#include <linux/seq_file.h>
+#include <linux/sort.h>
+
+#include "include/apparmor.h"
+#include "include/context.h"
+#include "include/label.h"
+#include "include/policy.h"
+#include "include/sid.h"
+
+
+/*
+ * the aa_label represents the set of profiles confining an object
+ *
+ * Labels maintain a reference count to the set of pointers they reference
+ * Labels are ref counted by
+ *   tasks and object via the security field/security context off the field
+ *   code - will take a ref count on a label if it needs the label
+ *          beyond what is possible with an rcu_read_lock.
+ *   profiles - each profile is a label
+ *   sids - a pinned sid will keep a refcount of the label it is
+ *          referencing
+ *   objects - inode, files, sockets, ...
+ *
+ * Labels are not ref counted by the label set, so they maybe removed and
+ * freed when no longer in use.
+ *
+ */
+
+#define PROXY_POISON 97
+#define LABEL_POISON 100
+
+static void free_proxy(struct aa_proxy *proxy)
+{
+       if (proxy) {
+               /* p->label will not updated any more as p is dead */
+               aa_put_label(rcu_dereference_protected(proxy->label, true));
+               memset(proxy, 0, sizeof(*proxy));
+               proxy->label = (struct aa_label *) PROXY_POISON;
+               kfree(proxy);
+       }
+}
+
+void aa_proxy_kref(struct kref *kref)
+{
+       struct aa_proxy *proxy = container_of(kref, struct aa_proxy, count);
+       free_proxy(proxy);
+}
+
+struct aa_proxy *aa_alloc_proxy(struct aa_label *label, gfp_t gfp)
+{
+       struct aa_proxy *new;
+
+       new = kzalloc(sizeof(struct aa_proxy), gfp);
+       if (new) {
+               kref_init(&new->count);
+               rcu_assign_pointer(new->label, aa_get_label(label));
+       }
+       return new;
+}
+
+/* requires profile list write lock held */
+void __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new)
+{
+       struct aa_label *tmp;
+
+       AA_BUG(!orig);
+       AA_BUG(!new);
+       AA_BUG(!write_is_locked(&labels_set(orig)->lock));
+
+       tmp = rcu_dereference_protected(orig->proxy->label,
+                                       &labels_ns(orig)->lock);
+       rcu_assign_pointer(orig->proxy->label, aa_get_label(new));
+       orig->flags |= FLAG_STALE;
+       aa_put_label(tmp);
+}
+
+static void __proxy_share(struct aa_label *old, struct aa_label *new)
+{
+       struct aa_proxy *proxy = new->proxy;
+       new->proxy = aa_get_proxy(old->proxy);
+       __aa_proxy_redirect(old, new);
+       aa_put_proxy(proxy);
+}
+
+
+/**
+ * ns_cmp - compare ns for label set ordering
+ * @a: ns to compare (NOT NULL)
+ * @b: ns to compare (NOT NULL)
+ *
+ * Returns: <0 if a < b
+ *          ==0 if a == b
+ *          >0  if a > b
+ */
+static int ns_cmp(struct aa_ns *a, struct aa_ns *b)
+{
+       int res;
+
+       AA_BUG(!a);
+       AA_BUG(!b);
+       AA_BUG(!a->base.name);
+       AA_BUG(!b->base.name);
+
+       if (a == b)
+               return 0;
+
+       res = a->level - b->level;
+       if (res)
+               return res;
+
+       return strcmp(a->base.name, b->base.name);
+}
+
+/**
+ * profile_cmp - profile comparision for set ordering
+ * @a: profile to compare (NOT NULL)
+ * @b: profile to compare (NOT NULL)
+ *
+ * Returns: <0  if a < b
+ *          ==0 if a == b
+ *          >0  if a > b
+ */
+static int profile_cmp(struct aa_profile *a, struct aa_profile *b)
+{
+       int res;
+
+       AA_BUG(!a);
+       AA_BUG(!b);
+       AA_BUG(!a->ns);
+       AA_BUG(!b->ns);
+       AA_BUG(!a->base.hname);
+       AA_BUG(!b->base.hname);
+
+       if (a == b || a->base.hname == b->base.hname)
+               return 0;
+       res = ns_cmp(a->ns, b->ns);
+       if (res)
+               return res;
+
+       return strcmp(a->base.hname, b->base.hname);
+}
+
+/**
+ * vec_cmp - label comparision for set ordering
+ * @a: label to compare (NOT NULL)
+ * @vec: vector of profiles to compare (NOT NULL)
+ * @n: length of @vec
+ *
+ * Returns: <0  if a < vec
+ *          ==0 if a == vec
+ *          >0  if a > vec
+ */
+static int vec_cmp(struct aa_profile **a, int an, struct aa_profile **b, int bn)
+{
+       int i;
+
+       AA_BUG(!a);
+       AA_BUG(!*a);
+       AA_BUG(!b);
+       AA_BUG(!*b);
+       AA_BUG(an <= 0);
+       AA_BUG(bn <= 0);
+
+       for (i = 0; i < an && i < bn; i++) {
+               int res = profile_cmp(a[i], b[i]);
+               if (res != 0)
+                       return res;
+       }
+
+       return an - bn;
+}
+
+static bool vec_is_stale(struct aa_profile **vec, int n)
+{
+       int i;
+
+       AA_BUG(!vec);
+
+       for (i = 0; i < n; i++) {
+               if (profile_is_stale(vec[i]))
+                       return true;
+       }
+
+       return false;
+}
+
+static bool vec_unconfined(struct aa_profile **vec, int n)
+{
+       int i;
+
+       AA_BUG(!vec);
+
+       for (i = 0; i < n; i++) {
+               if (!profile_unconfined(vec[i]))
+                       return false;
+       }
+
+       return true;
+}
+
+static int sort_cmp(const void *a, const void *b)
+{
+       return profile_cmp(*(struct aa_profile **)a, *(struct aa_profile **)b);
+}
+
+/* assumes vec is sorted
+ * Assumes @vec has null terminator at vec[n], and will null terminate
+ * vec[n - dups]
+*/
+static inline int unique(struct aa_profile **vec, int n)
+{
+       int i, pos, dups = 0;
+
+       AA_BUG(n < 1);
+       AA_BUG(!vec);
+
+       pos = 0;
+       for (i = 1; 1 < n; i++) {
+               int res = profile_cmp(vec[pos], vec[i]);
+               AA_BUG(res > 0, "vec not sorted");
+               if (res == 0) {
+                       /* drop duplicate */
+                       aa_put_profile(vec[i]);
+                       dups++;
+                       continue;
+               }
+               pos++;
+               if (dups)
+                       vec[pos] = vec[i];
+       }
+
+       AA_BUG(dups < 0);
+
+       return dups;
+}
+
+/**
+ * vec_unique - canonical sort and unique a list of profiles
+ * @n: number of refcounted profiles in the list (@n > 0)
+ * @vec: list of profiles to sort and merge
+ *
+ * Returns: the number of duplicates eliminated == references put
+ *
+ * If @flags & VEC_FLAG_TERMINATE @vec has null terminator at vec[n], and will
+ * null terminate vec[n - dups]
+ */
+int aa_vec_unique(struct aa_profile **vec, int n, int flags)
+{
+       int i, dups = 0;
+
+       AA_BUG(n < 1);
+       AA_BUG(!vec);
+
+       /* vecs are usually small and inorder, have a fallback for larger */
+       if (n > 8) {
+               sort(vec, n, sizeof(struct aa_profile *), sort_cmp, NULL);
+               dups = unique(vec, n);
+               goto out;
+       }
+
+       /* insertion sort + unique in one */
+       for (i = 1; i < n; i++) {
+               struct aa_profile *tmp = vec[i];
+               int pos, j;
+
+               for (pos = i - 1 - dups; pos >= 0; pos--) {
+                       int res = profile_cmp(vec[pos], tmp);
+                       if (res == 0) {
+                               /* drop duplicate entry */
+                               aa_put_profile(tmp);
+                               dups++;
+                               goto continue_outer;
+                       } else if (res < 0)
+                               break;
+               }
+               /* pos is at entry < tmp, or index -1. Set to insert pos */
+               pos++;
+
+               for (j = i - dups; j > pos; j--)
+                       vec[j] = vec[j - 1];
+               vec[pos] = tmp;
+       continue_outer: ;
+       }
+
+       AA_BUG(dups < 0);
+
+out:
+       if (flags & VEC_FLAG_TERMINATE)
+               vec[n - dups] = NULL;
+
+       return dups;
+}
+
+
+static void label_destroy(struct aa_label *label)
+{
+       AA_BUG(!label);
+
+       if (label_is_stale(label))
+               labelsetstats_dec(labels_set(label), stale);
+
+       if (!label_isprofile(label)) {
+               struct aa_profile *profile;
+               struct label_it i;
+
+               aa_put_str(label->hname);
+
+               label_for_each(i, label, profile) {
+                       aa_put_profile(profile);
+                       label->vec[i.i] = (struct aa_profile *) (LABEL_POISON + (long) i.i);
+               }
+       }
+
+       if (rcu_dereference_protected(label->proxy->label, true) == label)
+               rcu_assign_pointer(label->proxy->label, NULL);
+
+       aa_free_sid(label->sid);
+       aa_put_proxy(label->proxy);
+       label->proxy = (struct aa_proxy *) PROXY_POISON + 1;
+}
+
+void aa_label_free(struct aa_label *label)
+{
+       if (!label)
+               return;
+
+       label_destroy(label);
+       labelstats_inc(freed);
+       kfree(label);
+}
+
+static void label_free_switch(struct aa_label *label)
+{
+       if (label->flags & FLAG_NS_COUNT)
+               aa_free_ns(labels_ns(label));
+       else if (label_isprofile(label))
+               aa_free_profile(labels_profile(label));
+       else
+               aa_label_free(label);
+}
+
+static void label_free_rcu(struct rcu_head *head)
+{
+       struct aa_label *label = container_of(head, struct aa_label, rcu);
+
+       if (label->flags & FLAG_IN_TREE)
+               (void) aa_label_remove(label);
+       label_free_switch(label);
+}
+
+void aa_label_kref(struct kref *kref)
+{
+       struct aa_label *label = container_of(kref, struct aa_label, count);
+       struct aa_ns *ns = labels_ns(label);
+
+       if (!ns) {
+               /* never live, no rcu callback needed, just using the fn */
+               label_free_switch(label);
+               return;
+       }
+       /* TODO: update labels_profile macro so it works here */
+       AA_BUG(label_isprofile(label) && on_list_rcu(&label->vec[0]->base.profiles));
+       AA_BUG(label_isprofile(label) && on_list_rcu(&label->vec[0]->base.list));
+
+       /* TODO: if compound label and not stale add to reclaim cache */
+       call_rcu(&label->rcu, label_free_rcu);
+}
+
+bool aa_label_init(struct aa_label *label, int size)
+{
+       AA_BUG(!label);
+       AA_BUG(size < 1);
+
+       label->sid = aa_alloc_sid();
+       if (label->sid == AA_SID_INVALID)
+               return false;
+
+       label->size = size;                     /* doesn't include null */
+       label->vec[size] = NULL;                /* null terminate */
+       kref_init(&label->count);
+       RB_CLEAR_NODE(&label->node);
+
+       return true;
+}
+
+/**
+ * aa_label_alloc - allocate a label with a profile vector of @size length
+ * @size: size of profile vector in the label
+ * @proxy: proxy to use OR null if to allocate a new one
+ * @gfp: memory allocation type
+ *
+ * Returns: new label
+ *     else NULL if failed
+ */
+struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp)
+{
+       struct aa_label *new;
+
+       AA_BUG(size < 1);
+
+       /*  + 1 for null terminator entry on vec */
+       new = kzalloc(sizeof(*new) + sizeof(struct aa_profile *) * (size + 1),
+                       gfp);
+       AA_DEBUG("%s (%p)\n", __func__, new);
+       if (!new)
+               goto fail;
+
+       if (!aa_label_init(new, size))
+               goto fail;
+
+       if (!proxy) {
+               proxy = aa_alloc_proxy(new, gfp);
+               if (!proxy)
+                       goto fail;
+       } else
+               aa_get_proxy(proxy);
+       /* just set new's proxy, don't redirect proxy here if it was passed in*/
+       new->proxy = proxy;
+
+       labelstats_inc(allocated);
+
+       return new;
+
+fail:
+       kfree(new);
+       labelstats_inc(failed);
+
+       return NULL;
+}
+
+
+/**
+ * label_cmp - label comparision for set ordering
+ * @a: label to compare (NOT NULL)
+ * @b: label to compare (NOT NULL)
+ *
+ * Returns: <0  if a < b
+ *          ==0 if a == b
+ *          >0  if a > b
+ */
+static int label_cmp(struct aa_label *a, struct aa_label *b)
+{
+       AA_BUG(!b);
+
+       if (a == b)
+               return 0;
+
+       return vec_cmp(a->vec, a->size, b->vec, b->size);
+}
+
+/* helper fn for label_for_each_confined */
+int aa_label_next_confined(struct aa_label *label, int i)
+{
+       AA_BUG(!label);
+       AA_BUG(i < 0);
+
+       for (; i < label->size; i++) {
+               if (!profile_unconfined(label->vec[i]))
+                       return i;
+       }
+
+       return i;
+}
+
+/**
+ * aa_label_next_not_in_set - return the next profile of @sub not in @set
+ * @I: label iterator
+ * @set: label to test against
+ * @sub: label to if is subset of @set
+ *
+ * Returns: profile in @sub that is not in @set, with iterator set pos after
+ *     else NULL if @sub is a subset of @set
+ */
+struct aa_profile *__aa_label_next_not_in_set(struct label_it *I,
+                                             struct aa_label *set,
+                                             struct aa_label *sub)
+{
+       AA_BUG(!set);
+       AA_BUG(!I);
+       AA_BUG(I->i < 0);
+       AA_BUG(I->i > set->size);
+       AA_BUG(!sub);
+       AA_BUG(I->j < 0);
+       AA_BUG(I->j > sub->size);
+
+       while (I->j < sub->size && I->i < set->size) {
+               int res = profile_cmp(sub->vec[I->j], set->vec[I->i]);
+               if (res == 0) {
+                       (I->j)++;
+                       (I->i)++;
+               } else if (res > 0)
+                       (I->i)++;
+               else
+                       return sub->vec[(I->j)++];
+       }
+
+       if (I->j < sub->size)
+               return sub->vec[(I->j)++];
+
+       return NULL;
+}
+
+/**
+ * aa_label_is_subset - test if @sub is a subset of @set
+ * @set: label to test against
+ * @sub: label to test if is subset of @set
+ *
+ * Returns: true if @sub is subset of @set
+ *     else false
+ */
+bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub)
+{
+       struct label_it i = { };
+
+       AA_BUG(!set);
+       AA_BUG(!sub);
+
+       if (sub == set)
+               return true;
+
+       return __aa_label_next_not_in_set(&i, set, sub) == NULL;
+}
+
+
+
+/**
+ * __label_remove - remove @label from the label set
+ * @l: label to remove
+ * @new: label to redirect to
+ *
+ * Requires: labels_set(@label)->lock write_lock
+ * Returns:  true if the label was in the tree and removed
+ */
+static bool __label_remove(struct aa_label *label, struct aa_label *new)
+{
+       struct aa_labelset *ls = labels_set(label);
+       AA_BUG(!ls);
+       AA_BUG(!label);
+       AA_BUG(!write_is_locked(&ls->lock));
+
+       if (new)
+               __aa_proxy_redirect(label, new);
+
+       if (label_is_stale(label))
+               labelstats_dec(stale_intree);
+       else
+               __label_make_stale(label);
+
+       if (label->flags & FLAG_IN_TREE) {
+               labelsetstats_dec(ls, intree);
+               rb_erase(&label->node, &ls->root);
+               label->flags &= ~FLAG_IN_TREE;
+               return true;
+       }
+
+       return false;
+}
+
+/**
+ * __label_replace - replace @old with @new in label set
+ * @old: label to remove from label set
+ * @new: label to replace @old with
+ *
+ * Requires: labels_set(@old)->lock write_lock
+ *           valid ref count be held on @new
+ * Returns: true if @old was in set and replaced by @new
+ *
+ * Note: current implementation requires label set be order in such a way
+ *       that @new directly replaces @old position in the set (ie.
+ *       using pointer comparison of the label address would not work)
+ */
+static bool __label_replace(struct aa_label *old, struct aa_label *new)
+{
+       struct aa_labelset *ls = labels_set(old);
+       AA_BUG(!ls);
+       AA_BUG(!old);
+       AA_BUG(!new);
+       AA_BUG(!write_is_locked(&ls->lock));
+       AA_BUG(new->flags & FLAG_IN_TREE);
+
+       if (label_is_stale(old))
+               labelstats_dec(stale_intree);
+       else
+               __label_make_stale(old);
+
+       if (old->flags & FLAG_IN_TREE) {
+               rb_replace_node(&old->node, &new->node, &ls->root);
+               old->flags &= ~FLAG_IN_TREE;
+               new->flags |= FLAG_IN_TREE;
+               return true;
+       }
+
+       return false;
+}
+
+/**
+ * __label_insert - attempt to insert @l into a label set
+ * @ls: set of labels to insert @l into (NOT NULL)
+ * @label: new label to insert (NOT NULL)
+ * @replace: whether insertion should replace existing entry that is not stale
+ *
+ * Requires: @ls->lock
+ *           caller to hold a valid ref on l
+ *           if @replace is true l has a preallocated proxy associated
+ * Returns: @l if successful in inserting @l - with additional refcount
+ *          else ref counted equivalent label that is already in the set,
+            the else condition only happens if @replace is false
+ */
+static struct aa_label *__label_insert(struct aa_labelset *ls,
+                                      struct aa_label *label, bool replace)
+{
+       struct rb_node **new, *parent = NULL;
+
+       AA_BUG(!ls);
+       AA_BUG(!label);
+       AA_BUG(labels_set(label) != ls);
+       AA_BUG(!write_is_locked(&ls->lock));
+       AA_BUG(label->flags & FLAG_IN_TREE);
+
+       /* Figure out where to put new node */
+       new = &ls->root.rb_node;
+       while (*new) {
+               struct aa_label *this = rb_entry(*new, struct aa_label, node);
+               int result = label_cmp(label, this);
+
+               parent = *new;
+               if (result == 0) {
+                       labelsetstats_inc(ls, existing);
+                       /* !aa_get_label_not0 means queued for destruction,
+                        * so replace in place, however the label has
+                        * died before the replacement so do not share
+                        * the proxy
+                        */
+                       if (!replace && !label_is_stale(this)) {
+                               if (aa_get_label_not0(this))
+                                       return this;
+                       } else
+                               __proxy_share(this, label);
+                       AA_BUG(!__label_replace(this, label));
+                       return aa_get_label(label);
+               } else if (result < 0)
+                       new = &((*new)->rb_left);
+               else /* (result > 0) */
+                       new = &((*new)->rb_right);
+       }
+
+       /* Add new node and rebalance tree. */
+       rb_link_node(&label->node, parent, new);
+       rb_insert_color(&label->node, &ls->root);
+       label->flags |= FLAG_IN_TREE;
+       labelsetstats_inc(ls, insert);
+       labelsetstats_inc(ls, intree);
+
+       return aa_get_label(label);
+}
+
+/**
+ * __vec_find - find label that matches @vec in label set
+ * @vec: vec of profiles to find matching label for (NOT NULL)
+ * @n: length of @vec
+ *
+ * Requires: @vec_labelset(vec) lock held
+ *           caller to hold a valid ref on l
+ *
+ * Returns: ref counted @label if matching label is in tree
+ *          ref counted label that is equiv to @l in tree
+ *     else NULL if @vec equiv is not in tree
+ */
+static struct aa_label *__vec_find(struct aa_profile **vec, int n)
+{
+       struct rb_node *node;
+
+       AA_BUG(!vec);
+       AA_BUG(!*vec);
+       AA_BUG(n <= 0);
+
+       node = vec_labelset(vec, n)->root.rb_node;
+       while (node) {
+               struct aa_label *this = rb_entry(node, struct aa_label, node);
+               int result = vec_cmp(this->vec, this->size, vec, n);
+
+               if (result > 0)
+                       node = node->rb_left;
+               else if (result < 0)
+                       node = node->rb_right;
+               else
+                       return aa_get_label_not0(this);
+       }
+
+       return NULL;
+}
+
+/**
+ * __label_find - find label @label in label set
+ * @label: label to find (NOT NULL)
+ *
+ * Requires: labels_set(@label)->lock held
+ *           caller to hold a valid ref on l
+ *
+ * Returns: ref counted @label if @label is in tree OR
+ *          ref counted label that is equiv to @label in tree
+ *     else NULL if @label or equiv is not in tree
+ */
+static struct aa_label *__label_find(struct aa_label *label)
+{
+       AA_BUG(!label);
+
+       return __vec_find(label->vec, label->size);
+}
+
+
+/**
+ * aa_label_remove - remove a label from the labelset
+ * @label: label to remove
+ *
+ * Returns: true if @label was removed from the tree
+ *     else @label was not in tree so it could not be removed
+ */
+bool aa_label_remove(struct aa_label *label)
+{
+       struct aa_labelset *ls = labels_set(label);
+       unsigned long flags;
+       bool res;
+
+       AA_BUG(!ls);
+
+       write_lock_irqsave(&ls->lock, flags);
+       res = __label_remove(label, ns_unconfined(labels_ns(label)));
+       write_unlock_irqrestore(&ls->lock, flags);
+
+       return res;
+}
+
+/**
+ * aa_label_replace - replace a label @old with a new version @new
+ * @old: label to replace
+ * @new: label replacing @old
+ *
+ * Returns: true if @old was in tree and replaced
+ *     else @old was not in tree, and @new was not inserted
+ */
+bool aa_label_replace(struct aa_label *old, struct aa_label *new)
+{
+       unsigned long flags;
+       bool res;
+
+       if (name_is_shared(old, new) && labels_ns(old) == labels_ns(new)) {
+               write_lock_irqsave(&labels_set(old)->lock, flags);
+               if (old->proxy != new->proxy) {
+                       __proxy_share(old, new);
+               } else
+                       __aa_proxy_redirect(old, new);
+               res = __label_replace(old, new);
+               write_unlock_irqrestore(&labels_set(old)->lock, flags);
+       } else {
+               struct aa_label *l;
+               struct aa_labelset *ls = labels_set(old);
+               write_lock_irqsave(&ls->lock, flags);
+               res = __label_remove(old, new);
+               if (labels_ns(old) != labels_ns(new)) {
+                       write_unlock_irqrestore(&ls->lock, flags);
+                       ls = labels_set(new);
+                       write_lock_irqsave(&ls->lock, flags);
+               }
+               l = __label_insert(ls, new, true);
+               res = (l == new);
+               write_unlock_irqrestore(&ls->lock, flags);
+               aa_put_label(l);
+       }
+
+       return res;
+}
+
+/**
+ * vec_find - find label @l in label set
+ * @vec: array of profiles to find equiv label for (NOT NULL)
+ * @n: length of @vec
+ *
+ * Returns: refcounted label if @vec equiv is in tree
+ *     else NULL if @vec equiv is not in tree
+ */
+static struct aa_label *vec_find(struct aa_profile **vec, int n)
+{
+       struct aa_labelset *ls;
+       struct aa_label *label;
+       unsigned long flags;
+
+       AA_BUG(!vec);
+       AA_BUG(!*vec);
+       AA_BUG(n <= 0);
+
+       ls = vec_labelset(vec, n);
+       read_lock_irqsave(&ls->lock, flags);
+       label = __vec_find(vec, n);
+       labelstats_inc(sread);
+       read_unlock_irqrestore(&ls->lock, flags);
+
+       return label;
+}
+
+/* requires sort and merge done first */
+static struct aa_label *vec_create_and_insert_label(struct aa_profile **vec,
+                                                   int len, gfp_t gfp)
+{
+       struct aa_label *label = NULL;
+       struct aa_labelset *ls;
+       unsigned long flags;
+       struct aa_label *new;
+       int i;
+
+       AA_BUG(!vec);
+
+       if (len == 1)
+               return aa_get_label(&vec[0]->label);
+
+       ls = labels_set(&vec[len - 1]->label);
+
+       /* TODO: enable when read side is lockless
+        * check if label exists before taking locks
+        */
+       new = aa_label_alloc(len, NULL, gfp);
+       if (!new)
+               return NULL;
+
+       for (i = 0; i < len; i++) {
+               new->vec[i] = aa_get_profile(vec[i]);
+       }
+       write_lock_irqsave(&ls->lock, flags);
+       label = __label_insert(ls, new, false);
+       write_unlock_irqrestore(&ls->lock, flags);
+       aa_put_label(new);
+
+       return label;
+}
+
+struct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len,
+                                            gfp_t gfp)
+{
+       struct aa_label *label = vec_find(vec, len);
+       if (label)
+               return label;
+
+       return vec_create_and_insert_label(vec, len, gfp);
+}
+
+/**
+ * aa_label_find - find label @label in label set
+ * @label: label to find (NOT NULL)
+ *
+ * Requires: caller to hold a valid ref on l
+ *
+ * Returns: refcounted @label if @label is in tree
+ *          refcounted label that is equiv to @label in tree
+ *     else NULL if @label or equiv is not in tree
+ */
+struct aa_label *aa_label_find(struct aa_label *label)
+{
+       AA_BUG(!label);
+
+       return vec_find(label->vec, label->size);
+}
+
+
+/**
+ * aa_label_insert - insert label @label into @ls or return existing label
+ * @ls - labelset to insert @label into
+ * @label - label to insert
+ *
+ * Requires: caller to hold a valid ref on @label
+ *
+ * Returns: ref counted @label if successful in inserting @label
+ *     else ref counted equivalent label that is already in the set
+ */
+struct aa_label *aa_label_insert(struct aa_labelset *ls, struct aa_label *label)
+{
+       struct aa_label *l;
+       unsigned long flags;
+
+       AA_BUG(!ls);
+       AA_BUG(!label);
+
+       /* check if label exists before taking lock */
+       if (!label_is_stale(label)) {
+               read_lock_irqsave(&ls->lock, flags);
+               l = __label_find(label);
+               read_unlock_irqrestore(&ls->lock, flags);
+               labelstats_inc(fread);
+               if (l)
+                       return l;
+       }
+
+       write_lock_irqsave(&ls->lock, flags);
+       l = __label_insert(ls, label, false);
+       write_unlock_irqrestore(&ls->lock, flags);
+
+       return l;
+}
+
+
+/**
+ * aa_label_next_in_merge - find the next profile when merging @a and @b
+ * @I: label iterator
+ * @a: label to merge
+ * @b: label to merge
+ *
+ * Returns: next profile
+ *     else null if no more profiles
+ */
+struct aa_profile *aa_label_next_in_merge(struct label_it *I,
+                                         struct aa_label *a,
+                                         struct aa_label *b)
+{
+       AA_BUG(!a);
+       AA_BUG(!b);
+       AA_BUG(!I);
+       AA_BUG(I->i < 0);
+       AA_BUG(I->i > a->size);
+       AA_BUG(I->j < 0);
+       AA_BUG(I->j > b->size);
+
+       if (I->i < a->size) {
+               if (I->j < b->size) {
+                       int res = profile_cmp(a->vec[I->i], b->vec[I->j]);
+                       if (res > 0)
+                               return b->vec[(I->j)++];
+                       if (res == 0)
+                               (I->j)++;
+               }
+
+               return a->vec[(I->i)++];
+       }
+
+       if (I->j < b->size)
+               return b->vec[(I->j)++];
+
+       return NULL;
+}
+
+/**
+ * label_merge_cmp - cmp of @a merging with @b against @z for set ordering
+ * @a: label to merge then compare (NOT NULL)
+ * @b: label to merge then compare (NOT NULL)
+ * @z: label to compare merge against (NOT NULL)
+ *
+ * Assumes: using the most recent versions of @a, @b, and @z
+ *
+ * Returns: <0  if a < b
+ *          ==0 if a == b
+ *          >0  if a > b
+ */
+static int label_merge_cmp(struct aa_label *a, struct aa_label *b,
+                           struct aa_label *z)
+{
+       struct aa_profile *p = NULL;
+       struct label_it i = { };
+       int k;
+
+       AA_BUG(!a);
+       AA_BUG(!b);
+       AA_BUG(!z);
+
+       for (k = 0;
+            k < z->size && (p = aa_label_next_in_merge(&i, a, b));
+            k++) {
+               int res = profile_cmp(p, z->vec[k]);
+
+               if (res != 0)
+                       return res;
+       }
+
+       if (p)
+               return 1;
+       else if (k < z->size)
+               return -1;
+       return 0;
+}
+
+#if 0
+/**
+ * label_merge_len - find the length of the merge of @a and @b
+ * @a: label to merge (NOT NULL)
+ * @b: label to merge (NOT NULL)
+ *
+ * Assumes: using newest versions of labels @a and @b
+ *
+ * Returns: length of a label vector for merge of @a and @b
+ */
+static int label_merge_len(struct aa_label *a, struct aa_label *b)
+{
+       int len = a->size + b->size;
+       int i, j;
+
+       AA_BUG(!a);
+       AA_BUG(!b);
+
+       /* find entries in common and remove from count */
+       for (i = j = 0; i < a->size && j < b->size; ) {
+               int res = profile_cmp(a->vec[i], b->vec[j]);
+               if (res == 0) {
+                       len--;
+                       i++;
+                       j++;
+               } else if (res < 0)
+                       i++;
+               else
+                       j++;
+       }
+
+       return len;
+}
+#endif
+
+/**
+ * label_merge_insert - create a new label by merging @a and @b
+ * @new: preallocated label to merge into (NOT NULL)
+ * @a: label to merge with @b  (NOT NULL)
+ * @b: label to merge with @a  (NOT NULL)
+ *
+ * Requires: preallocated proxy
+ *
+ * Returns: ref counted label either @new if merge is unique
+ *          @a if @b is a subset of @a
+ *          @b if @a is a subset of @b
+ *
+ * NOTE: will not use @new if the merge results in @new == @a or @b
+ *
+ *       Must be used within labelset write lock to avoid racing with
+ *       setting labels stale.
+ */
+static struct aa_label *label_merge_insert(struct aa_label *new,
+                                          struct aa_label *a,
+                                          struct aa_label *b)
+{
+       struct aa_label *label;
+       struct aa_labelset *ls;
+       struct aa_profile *next;
+       struct label_it i;
+       unsigned long flags;
+       int k = 0, invcount = 0;
+       bool stale = false;
+
+       AA_BUG(!a);
+       AA_BUG(a->size < 0);
+       AA_BUG(!b);
+       AA_BUG(b->size < 0);
+       AA_BUG(!new);
+       AA_BUG(new->size < a->size + b->size);
+
+       label_for_each_in_merge(i, a, b, next) {
+               if (profile_is_stale(next)) {
+                       new->vec[k] = aa_get_newest_profile(next);
+                       if (next->label.proxy != new->vec[k]->label.proxy)
+                               invcount++;
+                       k++;
+                       stale = true;
+               } else
+                       new->vec[k++] = aa_get_profile(next);
+       }
+       /* set to actual size which is <= allocated len */
+       new->size = k;
+       new->vec[k] = NULL;
+
+       if (invcount) {
+               new->size -= aa_vec_unique(&new->vec[0], new->size,
+                                          VEC_FLAG_TERMINATE);
+       } else if (!stale) {
+               /* merge could be same as a || b, note: it is not possible
+                * for new->size == a->size == b->size unless a == b */
+               if (k == a->size)
+                       return aa_get_label(a);
+               else if (k == b->size)
+                       return aa_get_label(b);
+       }
+       if (vec_unconfined(new->vec, new->size))
+               new->flags |= FLAG_UNCONFINED;
+       ls = labels_set(new);
+       write_lock_irqsave(&ls->lock, flags);
+       label = __label_insert(labels_set(new), new, false);
+       write_unlock_irqrestore(&ls->lock, flags);
+
+       return label;
+}
+
+/**
+ * labelset_of_merge - find into which labelset a merged label should be inserted
+ * @a: label to merge and insert
+ * @b: label to merge and insert
+ *
+ * Returns: labelset that the merged label should be inserted into
+ */
+static struct aa_labelset *labelset_of_merge(struct aa_label *a, struct aa_label *b)
+{
+       struct aa_ns *nsa = labels_ns(a);
+       struct aa_ns *nsb = labels_ns(b);
+
+       if (ns_cmp(nsa, nsb) <= 0)
+               return &nsa->labels;
+       return &nsb->labels;
+}
+
+/**
+ * __label_find_merge - find label that is equiv to merge of @a and @b
+ * @ls: set of labels to search (NOT NULL)
+ * @a: label to merge with @b  (NOT NULL)
+ * @b: label to merge with @a  (NOT NULL)
+ *
+ * Requires: ls->lock read_lock held
+ *
+ * Returns: ref counted label that is equiv to merge of @a and @b
+ *     else NULL if merge of @a and @b is not in set
+ */
+static struct aa_label *__label_find_merge(struct aa_labelset *ls,
+                                          struct aa_label *a,
+                                          struct aa_label *b)
+{
+       struct rb_node *node;
+
+       AA_BUG(!ls);
+       AA_BUG(!a);
+       AA_BUG(!b);
+
+       if (a == b)
+               return __label_find(a);
+
+       node  = ls->root.rb_node;
+       while (node) {
+               struct aa_label *this = container_of(node, struct aa_label,
+                                                    node);
+               int result = label_merge_cmp(a, b, this);
+
+               if (result < 0)
+                       node = node->rb_left;
+               else if (result > 0)
+                       node = node->rb_right;
+               else
+                       return aa_get_label_not0(this);
+       }
+
+       return NULL;
+}
+
+
+/**
+ * aa_label_find_merge - find label that is equiv to merge of @a and @b
+ * @a: label to merge with @b  (NOT NULL)
+ * @b: label to merge with @a  (NOT NULL)
+ *
+ * Requires: labels be fully constructed with a valid ns
+ *
+ * Returns: ref counted label that is equiv to merge of @a and @b
+ *     else NULL if merge of @a and @b is not in set
+ */
+struct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b)
+{
+       struct aa_labelset *ls;
+       struct aa_label *label, *ar = NULL, *br = NULL;
+       unsigned long flags;
+
+       AA_BUG(!a);
+       AA_BUG(!b);
+
+       if (label_is_stale(a))
+               a = ar = aa_get_newest_label(a);
+       if (label_is_stale(b))
+               b = br = aa_get_newest_label(b);
+       ls = labelset_of_merge(a, b);
+       read_lock_irqsave(&ls->lock, flags);
+       label = __label_find_merge(ls, a, b);
+       read_unlock_irqrestore(&ls->lock, flags);
+       aa_put_label(ar);
+       aa_put_label(br);
+       labelsetstats_inc(ls, msread);
+
+       return label;
+}
+
+/**
+ * aa_label_merge - attempt to insert new merged label of @a and @b
+ * @ls: set of labels to insert label into (NOT NULL)
+ * @a: label to merge with @b  (NOT NULL)
+ * @b: label to merge with @a  (NOT NULL)
+ * @gfp: memory allocation type
+ *
+ * Requires: caller to hold valid refs on @a and @b
+ *           labels be fully constructed with a valid ns
+ *
+ * Returns: ref counted new label if successful in inserting merge of a & b
+ *     else ref counted equivalent label that is already in the set.
+ *     else NULL if could not create label (-ENOMEM)
+ */
+struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b,
+                               gfp_t gfp)
+{
+       struct aa_label *label = NULL;
+
+       AA_BUG(!a);
+       AA_BUG(!b);
+
+       if (a == b)
+               return aa_get_newest_label(a);
+
+       /* TODO: enable when read side is lockless
+        * check if label exists before taking locks
+       if (!label_is_stale(a) && !label_is_stale(b))
+               label = aa_label_find_merge(a, b);
+       */
+
+       if (!label) {
+               struct aa_label *new;
+
+               a = aa_get_newest_label(a);
+               b = aa_get_newest_label(b);
+
+               /* could use label_merge_len(a, b), but requires double
+                * comparison for small savings
+                */
+               new = aa_label_alloc(a->size + b->size, NULL, gfp);
+               if (!new)
+                       goto out;
+
+               label = label_merge_insert(new, a, b);
+               aa_put_label(new);
+       out:
+               aa_put_label(a);
+               aa_put_label(b);
+       }
+
+       return label;
+}
+
+static inline bool label_is_visible(struct aa_profile *profile,
+                                   struct aa_label *label)
+{
+       return aa_ns_visible(profile->ns, labels_ns(label), true);
+}
+
+/* match a profile and its associated ns component if needed
+ * Assumes visibility test has already been done.
+ * If a subns profile is not to be matched should be prescreened with
+ * visibility test.
+ */
+static inline unsigned int match_component(struct aa_profile *profile,
+                                          struct aa_profile *tp,
+                                          unsigned int state)
+{
+       const char *ns_name;
+
+       if (profile->ns == tp->ns)
+               return aa_dfa_match(profile->policy.dfa, state, tp->base.hname);
+
+       /* try matching with namespace name and then profile */
+       ns_name = aa_ns_name(profile->ns, tp->ns, true);
+       state = aa_dfa_match_len(profile->policy.dfa, state, ":", 1);
+       state = aa_dfa_match(profile->policy.dfa, state, ns_name);
+       state = aa_dfa_match_len(profile->policy.dfa, state, ":", 1);
+       return aa_dfa_match(profile->policy.dfa, state, tp->base.hname);
+}
+
+/**
+ * label_component_match - find perms for full compound label
+ * @profile: profile to find perms for
+ * @label: label to check access permissions for
+ * @start: state to start match in
+ * @subns: whether to do permission checks on components in a subns
+ * @request: permissions to request
+ * @perms: perms struct to set
+ *
+ * Returns: 0 on success else ERROR
+ *
+ * For the label A//&B//&C this does the perm match for A//&B//&C
+ * @perms should be preinitialized with allperms OR a previous permission
+ *        check to be stacked.
+ */
+static int label_compound_match(struct aa_profile *profile,
+                               struct aa_label *label,
+                               unsigned int state, bool subns, u32 request,
+                               struct aa_perms *perms)
+{
+       struct aa_profile *tp;
+       struct label_it i;
+
+       /* find first subcomponent that is visible */
+       label_for_each(i, label, tp) {
+               if (!aa_ns_visible(profile->ns, tp->ns, subns))
+                       continue;
+               state = match_component(profile, tp, state);
+               if (!state)
+                       goto fail;
+               goto next;
+       }
+
+       /* no component visible */
+       *perms = allperms;
+       return 0;
+
+next:
+       label_for_each_cont(i, label, tp) {
+               if (!aa_ns_visible(profile->ns, tp->ns, subns))
+                       continue;
+               state = aa_dfa_match(profile->policy.dfa, state, "//&");
+               state = match_component(profile, tp, state);
+               if (!state)
+                       goto fail;
+       }
+       aa_compute_perms(profile->policy.dfa, state, perms);
+       aa_apply_modes_to_perms(profile, perms);
+       if ((perms->allow & request) != request)
+               return -EACCES;
+
+       return 0;
+
+fail:
+       *perms = nullperms;
+       return state;
+}
+
+/**
+ * label_component_match - find perms for all subcomponents of a label
+ * @profile: profile to find perms for
+ * @label: label to check access permissions for
+ * @start: state to start match in
+ * @subns: whether to do permission checks on components in a subns
+ * @request: permissions to request
+ * @perms: an initialized perms struct to add accumulation to
+ *
+ * Returns: 0 on success else ERROR
+ *
+ * For the label A//&B//&C this does the perm match for each of A and B and C
+ * @perms should be preinitialized with allperms OR a previous permission
+ *        check to be stacked.
+ */
+static int label_components_match(struct aa_profile *profile,
+                                 struct aa_label *label, unsigned int start,
+                                 bool subns, u32 request,
+                                 struct aa_perms *perms)
+{
+       struct aa_profile *tp;
+       struct label_it i;
+       struct aa_perms tmp;
+       unsigned int state = 0;
+
+       /* find first subcomponent to test */
+       label_for_each(i, label, tp) {
+               if (!aa_ns_visible(profile->ns, tp->ns, subns))
+                       continue;
+               state = match_component(profile, tp, start);
+               if (!state)
+                       goto fail;
+               goto next;
+       }
+
+       /* no subcomponents visible - no change in perms */
+       return 0;
+
+next:
+       aa_compute_perms(profile->policy.dfa, state, &tmp);
+       aa_apply_modes_to_perms(profile, &tmp);
+       aa_perms_accum(perms, &tmp);
+       label_for_each_cont(i, label, tp) {
+               if (!aa_ns_visible(profile->ns, tp->ns, subns))
+                       continue;
+               state = match_component(profile, tp, start);
+               if (!state)
+                       goto fail;
+               aa_compute_perms(profile->policy.dfa, state, &tmp);
+               aa_apply_modes_to_perms(profile, &tmp);
+               aa_perms_accum(perms, &tmp);
+       }
+
+       if ((perms->allow & request) != request)
+               return -EACCES;
+
+       return 0;
+
+fail:
+       *perms = nullperms;
+       return -EACCES;
+}
+
+/**
+ * aa_label_match - do a multi-component label match
+ * @profile: profile to match against (NOT NULL)
+ * @label: label to match (NOT NULL)
+ * @state: state to start in
+ * @subns: whether to match subns components
+ * @request: permission request
+ * @perms: Returns computed perms (NOT NULL)
+ *
+ * Returns: the state the match finished in, may be the none matching state
+ */
+int aa_label_match(struct aa_profile *profile, struct aa_label *label,
+                  unsigned int state, bool subns, u32 request,
+                  struct aa_perms *perms)
+{
+       int error = label_compound_match(profile, label, state, subns, request,
+                                        perms);
+       if (!error)
+               return error;
+
+       *perms = allperms;
+       return label_components_match(profile, label, state, subns, request,
+                                     perms);
+}
+
+
+/**
+ * aa_update_label_name - update a label to have a stored name
+ * @ns: ns being viewed from (NOT NULL)
+ * @label: label to update (NOT NULL)
+ * @gfp: type of memory allocation
+ *
+ * Requires: labels_set(label) not locked in caller
+ *
+ * note: only updates the label name if it does not have a name already
+ *       and if it is in the labelset
+ */
+bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp)
+{
+       struct aa_labelset *ls;
+       unsigned long flags;
+       char __counted *name;
+       bool res = false;
+
+       AA_BUG(!ns);
+       AA_BUG(!label);
+
+       if (label->hname || labels_ns(label) != ns)
+               return res;
+
+       if (aa_label_acntsxprint(&name, ns, label, FLAGS_NONE, gfp) == -1)
+               return res;
+
+       ls = labels_set(label);
+       write_lock_irqsave(&ls->lock, flags);
+       if (!label->hname && label->flags & FLAG_IN_TREE) {
+               label->hname = name;
+               res = true;
+       } else
+               aa_put_str(name);
+       write_unlock_irqrestore(&ls->lock, flags);
+
+       return res;
+}
+
+/* cached label name is present and visible
+ * @label->hname only exists if label is namespace hierachical */
+static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label)
+{
+       if (label->hname && labels_ns(label) == ns)
+               return true;
+
+       return false;
+}
+
+/* helper macro for snprint routines */
+#define update_for_len(total, len, size, str)  \
+do {                                   \
+       AA_BUG(len < 0);                \
+       total += len;                   \
+       len = min(len, size);           \
+       size -= len;                    \
+       str += len;                     \
+} while (0)
+
+/**
+ * aa_profile_snxprint_profile - print a profile name to a buffer
+ * @str: buffer to write to. (MAY BE NULL if @size == 0)
+ * @size: size of buffer
+ * @ns: namespace profile is being viewed from
+ * @profile: profile to view (NOT NULL)
+ * @flags: whether to include the mode string
+ *
+ * Returns: size of name written or would be written if larger than
+ *          available buffer
+ *
+ * Note: will not print anything if the profile is not visible
+ */
+int aa_profile_snxprint(char *str, size_t size, struct aa_ns *ns,
+                       struct aa_profile *profile, int flags)
+{
+       const char *ns_name = "";
+
+       AA_BUG(!str && size != 0);
+       AA_BUG(!profile);
+
+       if (!ns)
+               ns = profiles_ns(profile);
+
+       if (ns != profile->ns) {
+               ns_name = aa_ns_name(ns, profile->ns, flags & FLAG_VIEW_SUBNS);
+               if (ns_name == aa_hidden_ns_name) {
+                       if (flags & FLAG_HIDDEN_UNCONFINED)
+                               return snprintf(str, size, "%s", "unconfined");
+                       return snprintf(str, size, "%s", ns_name);
+               }
+       }
+
+       if ((flags & FLAG_SHOW_MODE) && profile != profile->ns->unconfined) {
+               const char *modestr = aa_profile_mode_names[profile->mode];
+               if (strlen(ns_name))
+                       return snprintf(str, size, ":%s://%s (%s)", ns_name,
+                                       profile->base.hname, modestr);
+               return snprintf(str, size, "%s (%s)", profile->base.hname,
+                               modestr);
+       }
+
+       if (strlen(ns_name))
+               return snprintf(str, size, ":%s://%s", ns_name,
+                               profile->base.hname);
+       return snprintf(str, size, "%s", profile->base.hname);
+}
+
+static const char *label_modename(struct aa_ns *ns, struct aa_label *label,
+                                 int flags)
+{
+       struct aa_profile *profile;
+       struct label_it i;
+       const char *modestr = NULL;
+       int count = 0;
+
+       label_for_each(i, label, profile) {
+               if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
+                       const char *tmp_modestr;
+                       count++;
+                       tmp_modestr = aa_profile_mode_names[profile->mode];
+                       if (!modestr)
+                               modestr = tmp_modestr;
+                       else if (modestr != tmp_modestr)
+                               return "mixed";
+               }
+       }
+
+       if (count == 0)
+               return "-";
+
+       return modestr;
+}
+
+/* if any visible label is not unconfined the display_mode returns true */
+static inline bool display_mode(struct aa_ns *ns, struct aa_label *label,
+                               int flags)
+{
+       if ((flags & FLAG_SHOW_MODE)) {
+               struct aa_profile *profile;
+               struct label_it i;
+
+               label_for_each(i, label, profile) {
+                       if (aa_ns_visible(ns, profile->ns,
+                                         flags & FLAG_VIEW_SUBNS) &&
+                           profile != profile->ns->unconfined)
+                               return true;
+               }
+               /* only ns->unconfined in set of profiles in ns */
+               return false;
+       }
+
+       return false;
+}
+
+/**
+ * aa_label_snxprint - print a label name to a string buffer
+ * @str: buffer to write to. (MAY BE NULL if @size == 0)
+ * @size: size of buffer
+ * @ns: namespace profile is being viewed from
+ * @label: label to view (NOT NULL)
+ * @flags: whether to include the mode string
+ *
+ * Returns: size of name written or would be written if larger than
+ *          available buffer
+ *
+ * Note: labels do not have to be strictly hierarchical to the ns as
+ *       objects may be shared across different namespaces and thus
+ *       pickup labeling from each ns.  If a particular part of the
+ *       label is not visible it will just be excluded.  And if none
+ *       of the label is visible "---" will be used.
+ */
+int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns,
+                     struct aa_label *label, int flags)
+{
+       struct aa_profile *profile;
+       struct label_it i;
+       int count = 0, total = 0;
+       size_t len;
+
+       AA_BUG(!str && size != 0);
+       AA_BUG(!label);
+
+       if (!ns)
+               ns = labels_ns(label);
+
+       label_for_each(i, label, profile) {
+               if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
+                       if (count > 0) {
+                               len = snprintf(str, size, "//&");
+                               update_for_len(total, len, size, str);
+                       }
+                       len = aa_profile_snxprint(str, size, ns, profile,
+                                                 flags & FLAG_VIEW_SUBNS);
+                       update_for_len(total, len, size, str);
+                       count++;
+               }
+       }
+
+       if (count == 0) {
+               if (flags & FLAG_HIDDEN_UNCONFINED)
+                       return snprintf(str, size, "%s", "unconfined");
+               return snprintf(str, size, "%s", aa_hidden_ns_name);
+       }
+
+       /* count == 1 && ... is for backwards compat where the mode
+        * is not displayed for 'unconfined' in the current ns
+        */
+       if (display_mode(ns, label, flags)) {
+               len = snprintf(str, size, " (%s)",
+                              label_modename(ns, label, flags));
+               update_for_len(total, len, size, str);
+       }
+
+       return total;
+}
+#undef update_for_len
+
+/**
+ * aa_label_asxprint - allocate a string buffer and print label into it
+ * @strp: Returns - the allocated buffer with the label name. (NOT NULL)
+ * @ns: namespace profile is being viewed from
+ * @label: label to view (NOT NULL)
+ * @flags: flags controlling what label info is printed
+ * @gfp: kernel memory allocation type
+ *
+ * Returns: size of name written or would be written if larger than
+ *          available buffer
+ */
+int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label,
+                     int flags, gfp_t gfp)
+{
+       int size;
+
+       AA_BUG(!strp);
+       AA_BUG(!label);
+
+       size = aa_label_snxprint(NULL, 0, ns, label, flags);
+       if (size < 0)
+               return size;
+
+       *strp = kmalloc(size + 1, gfp);
+       if (!*strp)
+               return -ENOMEM;
+       return aa_label_snxprint(*strp, size + 1, ns, label, flags);
+}
+
+/**
+ * aa_label_acntsxprint - allocate a __counted string buffer and print label
+ * @strp: buffer to write to. (MAY BE NULL if @size == 0)
+ * @ns: namespace profile is being viewed from
+ * @label: label to view (NOT NULL)
+ * @flags: flags controlling what label info is printed
+ * @gfp: kernel memory allocation type
+ *
+ * Returns: size of name written or would be written if larger than
+ *          available buffer
+ */
+int aa_label_acntsxprint(char __counted **strp, struct aa_ns *ns,
+                        struct aa_label *label, int flags, gfp_t gfp)
+{
+       int size;
+
+       AA_BUG(!strp);
+       AA_BUG(!label);
+
+       size = aa_label_snxprint(NULL, 0, ns, label, flags);
+       if (size < 0)
+               return size;
+
+       *strp = aa_str_alloc(size + 1, gfp);
+       if (!*strp)
+               return -ENOMEM;
+       return aa_label_snxprint(*strp, size + 1, ns, label, flags);
+}
+
+
+void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns,
+                    struct aa_label *label, int flags, gfp_t gfp)
+{
+       const char *str;
+       char *name = NULL;
+       int len;
+
+       AA_BUG(!ab);
+       AA_BUG(!label);
+
+       if (!ns)
+               ns = labels_ns(label);
+
+       if (!use_label_hname(ns, label) || display_mode(ns, label, flags)) {
+               labelstats_inc(audit_name_alloc);
+               len  = aa_label_asxprint(&name, ns, label, flags, gfp);
+               if (len == -1) {
+                       labelstats_inc(audit_name_fail);
+                       AA_DEBUG("label print error");
+                       return;
+               }
+               str = name;
+       } else {
+               str = (char *) label->hname;
+               len = strlen(str);
+       }
+       if (audit_string_contains_control(str, len))
+               audit_log_n_hex(ab, str, len);
+       else
+               audit_log_n_string(ab, str, len);
+
+       kfree(name);
+}
+
+void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
+                        struct aa_label *label, int flags, gfp_t gfp)
+{
+       AA_BUG(!f);
+       AA_BUG(!label);
+
+       if (!ns)
+               ns = labels_ns(label);
+
+       if (!use_label_hname(ns, label)) {
+               char *str;
+               int len;
+
+               labelstats_inc(seq_print_name_alloc);
+               len = aa_label_asxprint(&str, ns, label, flags, gfp);
+               if (len == -1) {
+                       labelstats_inc(seq_print_name_fail);
+                       AA_DEBUG("label print error");
+                       return;
+               }
+               seq_printf(f, "%s", str);
+               kfree(str);
+       } else if (display_mode(ns, label, flags))
+               seq_printf(f, "%s (%s)", label->hname,
+                          label_modename(ns, label, flags));
+       else
+               seq_printf(f, "%s", label->hname);
+}
+
+void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
+                     gfp_t gfp)
+{
+       AA_BUG(!label);
+
+       if (!ns)
+               ns = labels_ns(label);
+
+       if (!use_label_hname(ns, label)) {
+               char *str;
+               int len;
+
+               labelstats_inc(printk_name_alloc);
+               len = aa_label_asxprint(&str, ns, label, flags, gfp);
+               if (len == -1) {
+                       labelstats_inc(printk_name_fail);
+                       AA_DEBUG("label print error");
+                       return;
+               }
+               printk("%s", str);
+               kfree(str);
+       } else if (display_mode(ns, label, flags))
+               printk("%s (%s)", label->hname,
+                      label_modename(ns, label, flags));
+       else
+               printk("%s", label->hname);
+}
+
+void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp)
+{
+       struct aa_ns *ns = aa_get_current_ns();
+       aa_label_xaudit(ab, ns, label, FLAG_VIEW_SUBNS, gfp);
+       aa_put_ns(ns);
+}
+
+void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp)
+{
+       struct aa_ns *ns = aa_get_current_ns();
+       aa_label_seq_xprint(f, ns, label, FLAG_VIEW_SUBNS, gfp);
+       aa_put_ns(ns);
+}
+
+void aa_label_printk(struct aa_label *label, gfp_t gfp)
+{
+       struct aa_ns *ns = aa_get_current_ns();
+       aa_label_xprintk(ns, label, FLAG_VIEW_SUBNS, gfp);
+       aa_put_ns(ns);
+}
+
+static int label_count_str_entries(const char *str)
+{
+       const char *split;
+       int count = 1;
+
+       AA_BUG(!str);
+
+       for (split = strstr(str, "//&"); split; split = strstr(str, "//&")) {
+               count++;
+               str = split + 3;
+       }
+
+       return count;
+}
+
+/**
+ * aa_label_parse - parse, validate and convert a text string to a label
+ * @base: base label to use for lookups (NOT NULL)
+ * @str: null terminated text string (NOT NULL)
+ * @gfp: allocation type
+ * @create: true if should create compound labels if they don't exist
+ * @force_stack: true if should stack even if no leading &
+ *
+ * Returns: the matching refcounted label if present
+ *     else ERRPTR
+ */
+struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
+                               gfp_t gfp, bool create, bool force_stack)
+{
+       DEFINE_VEC(profile, vec);
+       struct aa_label *label;
+       int i, len, stack = 0, error;
+       char *split;
+
+       AA_BUG(!base);
+       AA_BUG(!str);
+
+       str = skip_spaces(str);
+       len = label_count_str_entries(str);
+       if (*str == '&' || force_stack) {
+               /* stack on top of base */
+               stack = base->size;
+               len += stack;
+               if (*str == '&')
+                       str++;
+       }
+       error = vec_setup(profile, vec, len, gfp);
+       if (error)
+               return ERR_PTR(error);
+
+       for (i = 0; i < stack; i++)
+               vec[i] = aa_get_profile(base->vec[i]);
+
+       for (split = strstr(str, "//&"), i = stack; split && i < len; i++) {
+               vec[i] = aa_fqlookupn_profile(base, str, split - str);
+               if (!vec[i])
+                       goto fail;
+               str = split + 3;
+               split = strstr(str, "//&");
+       }
+       /* last element doesn't have a split so this should be the case but just to be safe */
+       if (i < len) {
+               vec[i] = aa_fqlookupn_profile(base, str, strlen(str));
+               if (!vec[i])
+                       goto fail;
+       }
+       if (len == 1)
+               /* no need to free vec as len < LOCAL_VEC_ENTRIES */
+               return &vec[0]->label;
+
+       len -= aa_vec_unique(vec, len, VEC_FLAG_TERMINATE);
+
+       if (create)
+               label = aa_vec_find_or_create_label(vec, len, gfp);
+       else
+               label = vec_find(vec, len);
+       if (!label)
+               goto fail;
+
+out:
+       /* use adjusted len from after vec_unique, not original */
+       vec_cleanup(profile, vec, len);
+       return label;
+
+fail:
+       label = ERR_PTR(-ENOENT);
+       goto out;
+}
+
+
+/**
+ * aa_labelset_destroy - remove all labels from the label set
+ * @ls: label set to cleanup (NOT NULL)
+ *
+ * Labels that are removed from the set may still exist beyond the set
+ * being destroyed depending on their reference counting
+ */
+void aa_labelset_destroy(struct aa_labelset *ls)
+{
+       struct rb_node *node;
+       unsigned long flags;
+
+       AA_BUG(!ls);
+
+       write_lock_irqsave(&ls->lock, flags);
+       for (node = rb_first(&ls->root); node; node = rb_first(&ls->root)) {
+               struct aa_label *this = rb_entry(node, struct aa_label, node);
+               if (labels_ns(this) != root_ns)
+                       __label_remove(this,
+                                      ns_unconfined(labels_ns(this)->parent));
+               else
+                       __label_remove(this, NULL);
+       }
+       write_unlock_irqrestore(&ls->lock, flags);
+}
+
+/*
+ * @ls: labelset to init (NOT NULL)
+ */
+void aa_labelset_init(struct aa_labelset *ls)
+{
+       AA_BUG(!ls);
+
+       rwlock_init(&ls->lock);
+       ls->root = RB_ROOT;
+       labelstats_init(&ls);
+}
+
+static struct aa_label *labelset_next_stale(struct aa_labelset *ls)
+{
+       struct aa_label *label;
+       struct rb_node *node;
+       unsigned long flags;
+
+       AA_BUG(!ls);
+
+       read_lock_irqsave(&ls->lock, flags);
+
+       __labelset_for_each(ls, node) {
+               label = rb_entry(node, struct aa_label, node);
+               if ((label_is_stale(label) || vec_is_stale(label->vec, label->size)) &&
+                   aa_get_label_not0(label))
+                       goto out;
+
+       }
+       label = NULL;
+
+out:
+       read_unlock_irqrestore(&ls->lock, flags);
+
+       return label;
+}
+
+/**
+ * __label_update - insert updated version of @label into labelset
+ * @label - the label to update/repace
+ *
+ * Returns: new label that is up to date
+ *     else NULL on failure
+ *
+ * Requires: @ns lock be held
+ *
+ * Note: worst case is the stale @label does not get updated and has
+ *       to be updated at a later time.
+ */
+static struct aa_label *__label_update(struct aa_label *label)
+{
+       struct aa_label *new, *tmp;
+       struct aa_labelset *ls;
+       struct aa_profile *p;
+       struct label_it i;
+       unsigned long flags;
+       int invcount = 0;
+
+       AA_BUG(!label);
+       AA_BUG(!mutex_is_locked(&labels_ns(label)->lock));
+
+       new = aa_label_alloc(label->size, label->proxy, GFP_KERNEL);
+       if (!new)
+               return NULL;
+
+       /* while holding the ns_lock will stop profile replacement, removal,
+        * and label updates, label merging and removal can be occuring
+        */
+       ls = labels_set(label);
+       write_lock_irqsave(&ls->lock, flags);
+       label_for_each(i, label, p) {
+               new->vec[i.i] = aa_get_newest_profile(p);
+               if (&new->vec[i.i]->label.proxy != &p->label.proxy)
+                       invcount++;
+       }
+
+       /* updated stale label by being removed/renamed from labelset */
+       if (invcount) {
+               new->size -= aa_vec_unique(&new->vec[0], new->size,
+                                          VEC_FLAG_TERMINATE);
+               if (labels_set(label) != labels_set(new)) {
+                       write_unlock_irqrestore(&ls->lock, flags);
+                       tmp = aa_label_insert(labels_set(new), new);
+                       write_lock_irqsave(&ls->lock, flags);
+                       goto remove;
+               }
+       } else
+               AA_BUG(labels_ns(label) != labels_ns(new));
+
+       tmp = __label_insert(labels_set(label), new, true);
+remove:
+       /* ensure label is removed, and redirected correctly */
+       __label_remove(label, tmp);
+       write_unlock_irqrestore(&ls->lock, flags);
+
+       aa_put_label(new);
+
+       return tmp;
+}
+
+/**
+ * __labelset_update - update labels in @ns
+ * @ns: namespace to update labels in  (NOT NULL)
+ *
+ * Requires: @ns lock be held
+ *
+ * Walk the labelset ensuring that all labels are up to date and valid
+ * Any label that has a stale component is marked stale and replaced and
+ * by an updated version.
+ *
+ * If failures happen due to memory pressures then stale labels will
+ * be left in place until the next pass.
+ */
+static void __labelset_update(struct aa_ns *ns)
+{
+       struct aa_label *label;
+
+       AA_BUG(!ns);
+       AA_BUG(!mutex_is_locked(&ns->lock));
+
+       do {
+               label = labelset_next_stale(&ns->labels);
+               if (label) {
+                       struct aa_label *l;
+                       l = __label_update(label);
+                       aa_put_label(l);
+                       aa_put_label(label);
+               }
+       } while (label);
+}
+
+/**
+ * __aa_labelset_udate_subtree - update all labels with a stale component
+ * @ns: ns to start update at (NOT NULL)
+ *
+ * Requires: @ns lock be held
+ *
+ * Invalidates labels based on @p in @ns and any children namespaces.
+*/
+void __aa_labelset_update_subtree(struct aa_ns *ns)
+{
+       struct aa_ns *child;
+
+       AA_BUG(!ns);
+       AA_BUG(!mutex_is_locked(&ns->lock));
+
+       __labelset_update(ns);
+
+       list_for_each_entry(child, &ns->sub_ns, base.list) {
+               mutex_lock(&child->lock);
+               __aa_labelset_update_subtree(child);
+               mutex_unlock(&child->lock);
+       }
+}
index c1827e068454cf992c510fd7f6bc9cbbda67a500..52d4efcad23740115f1a3dd28a929f8edc510746 100644 (file)
@@ -4,7 +4,7 @@
  * This file contains basic common functions used in AppArmor
  *
  * Copyright (C) 1998-2008 Novell/SUSE
- * Copyright 2009-2010 Canonical Ltd.
+ * Copyright 2009-2013 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
  * License.
  */
 
+#include <linux/ctype.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/vmalloc.h>
 
-#include "include/audit.h"
 #include "include/apparmor.h"
+#include "include/audit.h"
+#include "include/label.h"
+#include "include/lib.h"
+#include "include/perms.h"
+#include "include/policy.h"
 
+struct aa_perms nullperms;
+struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
+                            .quiet = ALL_PERMS_MASK,
+                            .hide = ALL_PERMS_MASK };
 
 /**
  * aa_split_fqname - split a fqname into a profile and namespace name
@@ -59,6 +68,57 @@ char *aa_split_fqname(char *fqname, char **ns_name)
        return name;
 }
 
+/**
+ * skipn_spaces - Removes leading whitespace from @str.
+ * @str: The string to be stripped.
+ *
+ * Returns a pointer to the first non-whitespace character in @str.
+ * if all whitespace will return NULL
+ */
+
+static const char *skipn_spaces(const char *str, size_t n)
+{
+       for (;n && isspace(*str); --n)
+               ++str;
+       if (n)
+               return (char *)str;
+       return NULL;
+}
+
+const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
+                            size_t *ns_len)
+{
+       const char *end = fqname + n;
+       const char *name = skipn_spaces(fqname, n);
+       if (!name)
+               return NULL;
+       *ns_name = NULL;
+       *ns_len = 0;
+       if (name[0] == ':') {
+               char *split = strnchr(&name[1], end - &name[1], ':');
+               *ns_name = skipn_spaces(&name[1], end - &name[1]);
+               if (!*ns_name)
+                       return NULL;
+               if (split) {
+                       *ns_len = split - *ns_name;
+                       if (*ns_len == 0)
+                               *ns_name = NULL;
+                       split++;
+                       if (end - split > 1 && strncmp(split, "//", 2) == 0)
+                               split += 2;
+                       name = skipn_spaces(split, end - split);
+               } else {
+                       /* a ns name without a following profile is allowed */
+                       name = NULL;
+                       *ns_len = end - *ns_name;
+               }
+       }
+       if (name && *name == 0)
+               name = NULL;
+
+       return name;
+}
+
 /**
  * aa_info_message - log a none profile related status message
  * @str: message to log
@@ -66,11 +126,8 @@ char *aa_split_fqname(char *fqname, char **ns_name)
 void aa_info_message(const char *str)
 {
        if (audit_enabled) {
-               struct common_audit_data sa;
-               struct apparmor_audit_data aad = {0,};
-               sa.type = LSM_AUDIT_DATA_NONE;
-               sa.aad = &aad;
-               aad.info = str;
+               DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
+               aad(&sa)->info = str;
                aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL);
        }
        printk(KERN_INFO "AppArmor: %s\n", str);
@@ -104,3 +161,405 @@ void *__aa_kvmalloc(size_t size, gfp_t flags)
        }
        return buffer;
 }
+
+
+__counted char *aa_str_alloc(int size, gfp_t gfp)
+{
+       struct counted_str *str;
+       str = kmalloc(sizeof(struct counted_str) + size, gfp);
+       if (!str)
+               return NULL;
+
+       kref_init(&str->count);
+       return str->name;
+}
+
+void aa_str_kref(struct kref *kref)
+{
+       kfree(container_of(kref, struct counted_str, count));
+}
+
+
+const char aa_file_perm_chrs[] = "xwracd         km l     ";
+const char *aa_file_perm_names[] = {
+       "exec",
+       "write",
+       "read",
+       "append",
+
+       "create",
+       "delete",
+       "open",
+       "rename",
+
+       "setattr",
+       "getattr",
+       "setcred",
+       "getcred",
+
+       "chmod",
+       "chown",
+       "chgrp",
+       "lock",
+
+       "mmap",
+       "mprot",
+       "link",
+       "snapshot",
+
+       "unknown",
+       "unknown",
+       "unknown",
+       "unknown",
+
+       "unknown",
+       "unknown",
+       "unknown",
+       "unknown",
+
+       "stack",
+       "change_onexec",
+       "change_profile",
+       "change_hat",
+};
+
+/**
+ * aa_perm_mask_to_str - convert a perm mask to its short string
+ * @str: character buffer to store string in (at least 10 characters)
+ * @mask: permission mask to convert
+ */
+void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask)
+{
+       unsigned int i, perm = 1;
+       for (i = 0; i < 32; perm <<= 1, i++) {
+               if (mask & perm)
+                       *str++ = chrs[i];
+       }
+       *str = '\0';
+}
+
+void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask)
+{
+       const char *fmt = "%s";
+       unsigned int i, perm = 1;
+       bool prev = false;
+       for (i = 0; i < 32; perm <<= 1, i++) {
+               if (mask & perm) {
+                       audit_log_format(ab, fmt, names[i]);
+                       if (!prev) {
+                               prev = true;
+                               fmt = " %s";
+                       }
+               }
+       }
+}
+
+void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
+                       u32 chrsmask, const char **names, u32 namesmask)
+{
+       char str[33];
+
+       audit_log_format(ab, "\"");
+       if ((mask & chrsmask) && chrs) {
+               aa_perm_mask_to_str(str, chrs, mask & chrsmask);
+               mask &= ~chrsmask;
+               audit_log_format(ab, "%s", str);
+               if (mask & namesmask)
+                       audit_log_format(ab, " ");
+       }
+       if ((mask & namesmask) && names)
+               aa_audit_perm_names(ab, names, mask & namesmask);
+       audit_log_format(ab, "\"");
+}
+
+/**
+ * aa_audit_perms_cb - generic callback fn for auditing perms
+ * @ab: audit buffer (NOT NULL)
+ * @va: audit struct to audit values of (NOT NULL)
+ */
+static void aa_audit_perms_cb(struct audit_buffer *ab, void *va)
+{
+       struct common_audit_data *sa = va;
+
+       if (aad(sa)->request) {
+               audit_log_format(ab, " requested_mask=");
+               aa_audit_perm_mask(ab, aad(sa)->request, aa_file_perm_chrs,
+                                  PERMS_CHRS_MASK, aa_file_perm_names,
+                                  PERMS_NAMES_MASK);
+       }
+       if (aad(sa)->denied) {
+               audit_log_format(ab, "denied_mask=");
+               aa_audit_perm_mask(ab, aad(sa)->denied, aa_file_perm_chrs,
+                                  PERMS_CHRS_MASK, aa_file_perm_names,
+                                  PERMS_NAMES_MASK);
+       }
+       audit_log_format(ab, " peer=");
+       aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
+                                     FLAGS_NONE, GFP_ATOMIC);
+}
+
+/**
+ * aa_apply_modes_to_perms - apply namespace and profile flags to perms
+ * @profile: that perms where computed from
+ * @perms: perms to apply mode modifiers to
+ *
+ * TODO: split into profile and ns based flags for when accumulating perms
+ */
+void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
+{
+       switch (AUDIT_MODE(profile)) {
+       case AUDIT_ALL:
+               perms->audit = ALL_PERMS_MASK;
+               /* fall through */
+       case AUDIT_NOQUIET:
+               perms->quiet = 0;
+               break;
+       case AUDIT_QUIET:
+               perms->audit = 0;
+               /* fall through */
+       case AUDIT_QUIET_DENIED:
+               perms->quiet = ALL_PERMS_MASK;
+               break;
+       }
+
+       if (KILL_MODE(profile))
+               perms->kill = ALL_PERMS_MASK;
+       else if (COMPLAIN_MODE(profile))
+               perms->complain = ALL_PERMS_MASK;
+/* TODO:
+       else if (PROMPT_MODE(profile))
+               perms->prompt = ALL_PERMS_MASK;
+*/
+}
+
+static u32 map_other(u32 x)
+{
+       return ((x & 0x3) << 8) |       /* SETATTR/GETATTR */
+               ((x & 0x1c) << 18) |    /* ACCEPT/BIND/LISTEN */
+               ((x & 0x60) << 19);     /* SETOPT/GETOPT */
+}
+
+void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
+                     struct aa_perms *perms)
+{
+       perms->deny = 0;
+       perms->kill = perms->stop = 0;
+       perms->complain = perms->cond = 0;
+       perms->hide = 0;
+       perms->prompt = 0;
+       perms->allow = dfa_user_allow(dfa, state);
+       perms->audit = dfa_user_audit(dfa, state);
+       perms->quiet = dfa_user_quiet(dfa, state);
+
+       /* for v5 perm mapping in the policydb, the other set is used
+        * to extend the general perm set
+        */
+       perms->allow |= map_other(dfa_other_allow(dfa, state));
+       perms->audit |= map_other(dfa_other_audit(dfa, state));
+       perms->quiet |= map_other(dfa_other_quiet(dfa, state));
+//     perms->xindex = dfa_user_xindex(dfa, state);
+}
+
+/**
+ * aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
+ * @accum - perms struct to accumulate into
+ * @addend - perms struct to add to @accum
+ */
+void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend)
+{
+       accum->deny |= addend->deny;
+       accum->allow &= addend->allow & ~addend->deny;
+       accum->audit |= addend->audit & addend->allow;
+       accum->quiet &= addend->quiet & ~addend->allow;
+       accum->kill |= addend->kill & ~addend->allow;
+       accum->stop |= addend->stop & ~addend->allow;
+       accum->complain |= addend->complain & ~addend->allow & ~addend->deny;
+       accum->cond |= addend->cond & ~addend->allow & ~addend->deny;
+       accum->hide &= addend->hide & ~addend->allow;
+       accum->prompt |= addend->prompt & ~addend->allow & ~addend->deny;
+}
+
+/**
+ * aa_perms_accum - accumulate perms, masking off overlapping perms
+ * @accum - perms struct to accumulate into
+ * @addend - perms struct to add to @accum
+ */
+void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend)
+{
+       accum->deny |= addend->deny;
+       accum->allow &= addend->allow & ~accum->deny;
+       accum->audit |= addend->audit & accum->allow;
+       accum->quiet &= addend->quiet & ~accum->allow;
+       accum->kill |= addend->kill & ~accum->allow;
+       accum->stop |= addend->stop & ~accum->allow;
+       accum->complain |= addend->complain & ~accum->allow & ~accum->deny;
+       accum->cond |= addend->cond & ~accum->allow & ~accum->deny;
+       accum->hide &= addend->hide & ~accum->allow;
+       accum->prompt |= addend->prompt & ~accum->allow & ~accum->deny;
+}
+
+void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label,
+                           int type, u32 request, struct aa_perms *perms)
+{
+       /* TODO: doesn't yet handle extended types */
+       unsigned int state;
+       state = aa_dfa_next(profile->policy.dfa,
+                           profile->policy.start[AA_CLASS_LABEL],
+                           type);
+       aa_label_match(profile, label, state, false, request, perms);
+}
+
+
+/* currently unused */
+int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
+                         u32 request, int type, u32 *deny,
+                         struct common_audit_data *sa)
+{
+       struct aa_perms perms;
+       aad(sa)->label = &profile->label;
+       aad(sa)->peer = &target->label;
+       aad(sa)->request = request;
+
+       aa_profile_match_label(profile, &target->label, type, request, &perms);
+       aa_apply_modes_to_perms(profile, &perms);
+       *deny |= request & perms.deny;
+       return aa_check_perms(profile, &perms, request, sa, aa_audit_perms_cb);
+}
+
+/**
+ * aa_check_perms - do audit mode selection based on perms set
+ * @profile: profile being checked
+ * @perms: perms computed for the request
+ * @request: requested perms
+ * @deny: Returns: explicit deny set
+ * @sa: initialized audit structure (MAY BE NULL if not auditing)
+ * @cb: callback fn for tpye specific fields (MAY BE NULL)
+ *
+ * Returns: 0 if permission else error code
+ *
+ * Note: profile audit modes need to be set before calling by setting the
+ *       perm masks appropriately.
+ *
+ *       If not auditing then complain mode is not enabled and the
+ *       error code will indicate whether there was an explicit deny
+ *      with a positive value.
+ */
+int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
+                  u32 request, struct common_audit_data *sa,
+                  void (*cb) (struct audit_buffer *, void *))
+{
+       int type, error;
+       bool stop = false;
+       u32 denied = request & (~perms->allow | perms->deny);
+       if (likely(!denied)) {
+               /* mask off perms that are not being force audited */
+               request &= perms->audit;
+               if (!request || !sa)
+                       return 0;
+
+               type = AUDIT_APPARMOR_AUDIT;
+               error = 0;
+       } else {
+               error = -EACCES;
+
+               if (denied & perms->kill)
+                       type = AUDIT_APPARMOR_KILL;
+               else if (denied == (denied & perms->complain))
+                       type = AUDIT_APPARMOR_ALLOWED;
+               else
+                       type = AUDIT_APPARMOR_DENIED;
+
+               if (denied & perms->stop)
+                       stop = true;
+               if (denied == (denied & perms->hide))
+                       error = -ENOENT;
+
+               denied &= ~perms->quiet;
+               if (!sa || !denied)
+                       return error;
+       }
+
+       if (sa) {
+               aad(sa)->label = &profile->label;
+               aad(sa)->request = request;
+               aad(sa)->denied = denied;
+               aad(sa)->error = error;
+               aa_audit_msg(type, sa, cb);
+       }
+
+       if (type == AUDIT_APPARMOR_ALLOWED)
+               error = 0;
+
+       return error;
+}
+
+const char *aa_imode_name(umode_t mode)
+{
+       switch(mode & S_IFMT) {
+       case S_IFSOCK:
+               return "sock";
+       case S_IFLNK:
+               return "link";
+       case S_IFREG:
+               return "reg";
+       case S_IFBLK:
+               return "blkdev";
+       case S_IFDIR:
+               return "dir";
+       case S_IFCHR:
+               return "chrdev";
+       case S_IFIFO:
+               return "fifo";
+       }
+       return "unknown";
+}
+
+/**
+ * aa_policy_init - initialize a policy structure
+ * @policy: policy to initialize  (NOT NULL)
+ * @prefix: prefix name if any is required.  (MAYBE NULL)
+ * @name: name of the policy, init will make a copy of it  (NOT NULL)
+ * @gfp: allocation mode
+ *
+ * Note: this fn creates a copy of strings passed in
+ *
+ * Returns: true if policy init successful
+ */
+bool aa_policy_init(struct aa_policy *policy, const char *prefix,
+                   const char *name, gfp_t gfp)
+{
+       char *hname;
+
+       /* freed by policy_free */
+       if (prefix) {
+               hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
+               if (hname)
+                       sprintf(hname, "%s//%s", prefix, name);
+       } else {
+               hname = aa_str_alloc(strlen(name) + 1, gfp);
+               if (hname)
+                       strcpy(hname, name);
+       }
+       if (!hname)
+               return 0;
+       policy->hname = hname;
+       /* base.name is a substring of fqname */
+       policy->name = (char *) basename(policy->hname);
+       INIT_LIST_HEAD(&policy->list);
+       INIT_LIST_HEAD(&policy->profiles);
+
+       return 1;
+}
+
+/**
+ * aa_policy_destroy - free the elements referenced by @policy
+ * @policy: policy that is to have its elements freed  (NOT NULL)
+ */
+void aa_policy_destroy(struct aa_policy *policy)
+{
+       AA_BUG(on_list_rcu(&policy->profiles));
+       AA_BUG(on_list_rcu(&policy->list));
+
+       /* don't free name as its a subset of hname */
+       aa_put_str(policy->hname);
+}
index dec607c17b6434d6b8e5416060a0202bc4cb52f4..1db90aeb668604525ff6a9ccb3c5ea718c2039fd 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/user_namespace.h>
 #include <net/sock.h>
 
+#include "include/af_unix.h"
 #include "include/apparmor.h"
 #include "include/apparmorfs.h"
 #include "include/audit.h"
 #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"
+#include "include/mount.h"
 
 /* Flag indicating whether initialization completed */
 int apparmor_initialized __initdata;
 
+DEFINE_PER_CPU(struct aa_buffers, aa_buffers);
+
+
 /*
  * LSM hook functions
  */
 
 /*
- * free the associated aa_task_cxt and put its profiles
+ * free the associated aa_task_ctx and put its labels
  */
 static void apparmor_cred_free(struct cred *cred)
 {
-       aa_free_task_context(cred_cxt(cred));
-       cred_cxt(cred) = NULL;
+       aa_free_task_context(cred_ctx(cred));
+       cred_ctx(cred) = NULL;
 }
 
 /*
@@ -58,27 +64,27 @@ static void apparmor_cred_free(struct cred *cred)
 static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
 {
        /* freed by apparmor_cred_free */
-       struct aa_task_cxt *cxt = aa_alloc_task_context(gfp);
-       if (!cxt)
+       struct aa_task_ctx *ctx = aa_alloc_task_context(gfp);
+       if (!ctx)
                return -ENOMEM;
 
-       cred_cxt(cred) = cxt;
+       cred_ctx(cred) = ctx;
        return 0;
 }
 
 /*
- * prepare new aa_task_cxt for modification by prepare_cred block
+ * prepare new aa_task_ctx for modification by prepare_cred block
  */
 static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
                                 gfp_t gfp)
 {
        /* freed by apparmor_cred_free */
-       struct aa_task_cxt *cxt = aa_alloc_task_context(gfp);
-       if (!cxt)
+       struct aa_task_ctx *ctx = aa_alloc_task_context(gfp);
+       if (!ctx)
                return -ENOMEM;
 
-       aa_dup_task_context(cxt, cred_cxt(old));
-       cred_cxt(new) = cxt;
+       aa_dup_task_context(ctx, cred_ctx(old));
+       cred_ctx(new) = ctx;
        return 0;
 }
 
@@ -87,43 +93,71 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
  */
 static void apparmor_cred_transfer(struct cred *new, const struct cred *old)
 {
-       const struct aa_task_cxt *old_cxt = cred_cxt(old);
-       struct aa_task_cxt *new_cxt = cred_cxt(new);
+       const struct aa_task_ctx *old_ctx = cred_ctx(old);
+       struct aa_task_ctx *new_ctx = cred_ctx(new);
 
-       aa_dup_task_context(new_cxt, old_cxt);
+       aa_dup_task_context(new_ctx, old_ctx);
 }
 
 static int apparmor_ptrace_access_check(struct task_struct *child,
                                        unsigned int mode)
 {
-       return aa_ptrace(current, child, mode);
+       struct aa_label *tracer, *tracee;
+       int error;
+       
+       tracer = aa_begin_current_label(DO_UPDATE);
+       tracee = aa_get_task_label(child);
+       error = aa_may_ptrace(tracer, tracee,
+                 mode == PTRACE_MODE_READ ? AA_PTRACE_READ : AA_PTRACE_TRACE);
+       aa_put_label(tracee);
+       aa_end_current_label(tracer);
+
+       return error;
 }
 
 static int apparmor_ptrace_traceme(struct task_struct *parent)
 {
-       return aa_ptrace(parent, current, PTRACE_MODE_ATTACH);
+       struct aa_label *tracer, *tracee;
+       int error;
+       
+       tracee = aa_begin_current_label(DO_UPDATE);
+       tracer = aa_get_task_label(parent);
+       error = aa_may_ptrace(tracer, tracee, AA_PTRACE_TRACE);
+       aa_put_label(tracer);
+       aa_end_current_label(tracee);
+
+       return error;
 }
 
 /* Derived from security/commoncap.c:cap_capget */
 static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
                           kernel_cap_t *inheritable, kernel_cap_t *permitted)
 {
-       struct aa_profile *profile;
+       struct aa_label *label;
        const struct cred *cred;
 
        rcu_read_lock();
        cred = __task_cred(target);
-       profile = aa_cred_profile(cred);
+       label = aa_get_newest_cred_label(cred);
 
        /*
         * cap_capget is stacked ahead of this and will
         * initialize effective and permitted.
         */
-       if (!unconfined(profile) && !COMPLAIN_MODE(profile)) {
-               *effective = cap_intersect(*effective, profile->caps.allow);
-               *permitted = cap_intersect(*permitted, profile->caps.allow);
+       if (!unconfined(label)) {
+               struct aa_profile *profile;
+               struct label_it i;
+               label_for_each_confined(i, label, profile) {
+                       if (COMPLAIN_MODE(profile))
+                               continue;
+                       *effective = cap_intersect(*effective,
+                                                  profile->caps.allow);
+                       *permitted = cap_intersect(*permitted,
+                                                  profile->caps.allow);
+               }
        }
        rcu_read_unlock();
+       aa_put_label(label);
 
        return 0;
 }
@@ -131,12 +165,14 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
 static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
                            int cap, int audit)
 {
-       struct aa_profile *profile;
+       struct aa_label *label;
        int error = 0;
 
-       profile = aa_cred_profile(cred);
-       if (!unconfined(profile))
-               error = aa_capable(profile, cap, audit);
+       label = aa_get_newest_cred_label(cred);
+       if (!unconfined(label))
+               error = aa_capable(label, cap, audit);
+       aa_put_label(label);
+
        return error;
 }
 
@@ -149,19 +185,39 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
  *
  * Returns: %0 else error code if error or permission denied
  */
-static int common_perm(int op, struct path *path, u32 mask,
+static int common_perm(const char *op, struct path *path, u32 mask,
                       struct path_cond *cond)
 {
-       struct aa_profile *profile;
+       struct aa_label *label;
        int error = 0;
 
-       profile = __aa_current_profile();
-       if (!unconfined(profile))
-               error = aa_path_perm(op, profile, path, 0, mask, cond);
+       label = aa_begin_current_label(NO_UPDATE);
+       if (!unconfined(label))
+               error = aa_path_perm(op, label, path, 0, mask, cond);
+       aa_end_current_label(label);
 
        return error;
 }
 
+static int common_perm_cond(const char *op, struct path *path, u32 mask)
+{
+       struct path_cond cond = { d_backing_inode(path->dentry)->i_uid,
+                                 d_backing_inode(path->dentry)->i_mode
+       };
+
+       return common_perm(op, path, mask, &cond);
+}
+
+static void apparmor_inode_free_security(struct inode *inode)
+{
+       struct aa_label *ctx = inode_ctx(inode);
+
+       if (ctx) {
+               inode_ctx(inode) = NULL;
+               aa_put_label(ctx);
+       }
+}
+
 /**
  * common_perm_dir_dentry - common permission wrapper when path is dir, dentry
  * @op: operation being checked
@@ -172,7 +228,7 @@ static int common_perm(int op, struct path *path, u32 mask,
  *
  * Returns: %0 else error code if error or permission denied
  */
-static int common_perm_dir_dentry(int op, struct path *dir,
+static int common_perm_dir_dentry(const char *op, struct path *dir,
                                  struct dentry *dentry, u32 mask,
                                  struct path_cond *cond)
 {
@@ -190,15 +246,12 @@ static int common_perm_dir_dentry(int op, struct path *dir,
  *
  * Returns: %0 else error code if error or permission denied
  */
-static int common_perm_mnt_dentry(int op, struct vfsmount *mnt,
+static int common_perm_mnt_dentry(const char *op, struct vfsmount *mnt,
                                  struct dentry *dentry, u32 mask)
 {
        struct path path = { mnt, dentry };
-       struct path_cond cond = { d_backing_inode(dentry)->i_uid,
-                                 d_backing_inode(dentry)->i_mode
-       };
 
-       return common_perm(op, &path, mask, &cond);
+       return common_perm_cond(op, &path, mask);
 }
 
 /**
@@ -210,13 +263,13 @@ static int common_perm_mnt_dentry(int op, struct vfsmount *mnt,
  *
  * Returns: %0 else error code if error or permission denied
  */
-static int common_perm_rm(int op, struct path *dir,
+static int common_perm_rm(const char *op, struct path *dir,
                          struct dentry *dentry, u32 mask)
 {
        struct inode *inode = d_backing_inode(dentry);
        struct path_cond cond = { };
 
-       if (!inode || !dir->mnt || !mediated_filesystem(dentry))
+       if (!inode || !dir->mnt || !path_mediated_fs(dentry))
                return 0;
 
        cond.uid = inode->i_uid;
@@ -235,12 +288,12 @@ static int common_perm_rm(int op, struct path *dir,
  *
  * Returns: %0 else error code if error or permission denied
  */
-static int common_perm_create(int op, struct path *dir, struct dentry *dentry,
+static int common_perm_create(const char *op, struct path *dir, struct dentry *dentry,
                              u32 mask, umode_t mode)
 {
        struct path_cond cond = { current_fsuid(), mode };
 
-       if (!dir->mnt || !mediated_filesystem(dir->dentry))
+       if (!dir->mnt || !path_mediated_fs(dir->dentry))
                return 0;
 
        return common_perm_dir_dentry(op, dir, dentry, mask, &cond);
@@ -271,15 +324,10 @@ static int apparmor_path_mknod(struct path *dir, struct dentry *dentry,
 
 static int apparmor_path_truncate(struct path *path)
 {
-       struct path_cond cond = { d_backing_inode(path->dentry)->i_uid,
-                                 d_backing_inode(path->dentry)->i_mode
-       };
-
-       if (!path->mnt || !mediated_filesystem(path->dentry))
+       if (!path->mnt || !path_mediated_fs(path->dentry))
                return 0;
 
-       return common_perm(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE,
-                          &cond);
+       return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_SETATTR);
 }
 
 static int apparmor_path_symlink(struct path *dir, struct dentry *dentry,
@@ -292,84 +340,84 @@ static int apparmor_path_symlink(struct path *dir, struct dentry *dentry,
 static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir,
                              struct dentry *new_dentry)
 {
-       struct aa_profile *profile;
+       struct aa_label *label;
        int error = 0;
 
-       if (!mediated_filesystem(old_dentry))
+       if (!path_mediated_fs(old_dentry))
                return 0;
 
-       profile = aa_current_profile();
-       if (!unconfined(profile))
-               error = aa_path_link(profile, old_dentry, new_dir, new_dentry);
+       label = aa_begin_current_label(DO_UPDATE);
+       if (!unconfined(label))
+               error = aa_path_link(label, old_dentry, new_dir, new_dentry);
+       aa_end_current_label(label);
+
        return error;
 }
 
 static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry,
                                struct path *new_dir, struct dentry *new_dentry)
 {
-       struct aa_profile *profile;
+       struct aa_label *label;
        int error = 0;
 
-       if (!mediated_filesystem(old_dentry))
+       if (!path_mediated_fs(old_dentry))
                return 0;
 
-       profile = aa_current_profile();
-       if (!unconfined(profile)) {
+       label = aa_begin_current_label(DO_UPDATE);
+       if (!unconfined(label)) {
                struct path old_path = { old_dir->mnt, old_dentry };
                struct path new_path = { new_dir->mnt, new_dentry };
                struct path_cond cond = { d_backing_inode(old_dentry)->i_uid,
                                          d_backing_inode(old_dentry)->i_mode
                };
 
-               error = aa_path_perm(OP_RENAME_SRC, profile, &old_path, 0,
-                                    MAY_READ | AA_MAY_META_READ | MAY_WRITE |
-                                    AA_MAY_META_WRITE | AA_MAY_DELETE,
+               error = aa_path_perm(OP_RENAME_SRC, label, &old_path, 0,
+                                    MAY_READ | AA_MAY_GETATTR | MAY_WRITE |
+                                    AA_MAY_SETATTR | AA_MAY_DELETE,
                                     &cond);
                if (!error)
-                       error = aa_path_perm(OP_RENAME_DEST, profile, &new_path,
-                                            0, MAY_WRITE | AA_MAY_META_WRITE |
+                       error = aa_path_perm(OP_RENAME_DEST, label, &new_path,
+                                            0, MAY_WRITE | AA_MAY_SETATTR |
                                             AA_MAY_CREATE, &cond);
 
        }
+       aa_end_current_label(label);
+
        return error;
 }
 
 static int apparmor_path_chmod(struct path *path, umode_t mode)
 {
-       if (!mediated_filesystem(path->dentry))
+       if (!path_mediated_fs(path->dentry))
                return 0;
 
-       return common_perm_mnt_dentry(OP_CHMOD, path->mnt, path->dentry, AA_MAY_CHMOD);
+       return common_perm_cond(OP_CHMOD, path, AA_MAY_CHMOD);
 }
 
 static int apparmor_path_chown(struct path *path, kuid_t uid, kgid_t gid)
 {
-       struct path_cond cond =  { d_backing_inode(path->dentry)->i_uid,
-                                  d_backing_inode(path->dentry)->i_mode
-       };
-
-       if (!mediated_filesystem(path->dentry))
+       if (!path_mediated_fs(path->dentry))
                return 0;
 
-       return common_perm(OP_CHOWN, path, AA_MAY_CHOWN, &cond);
+       return common_perm_cond(OP_CHOWN, path, AA_MAY_CHOWN);
 }
 
 static int apparmor_inode_getattr(const struct path *path)
 {
-       if (!mediated_filesystem(path->dentry))
+       if (!path_mediated_fs(path->dentry))
                return 0;
 
        return common_perm_mnt_dentry(OP_GETATTR, path->mnt, path->dentry,
-                                     AA_MAY_META_READ);
+                                     AA_MAY_GETATTR);
 }
 
 static int apparmor_file_open(struct file *file, const struct cred *cred)
 {
-       struct aa_file_cxt *fcxt = file->f_security;
-       struct aa_profile *profile;
+       struct aa_file_ctx *fctx = file_ctx(file);
+       struct aa_label *label;
        int error = 0;
 
-       if (!mediated_filesystem(file->f_path.dentry))
+       if (!path_mediated_fs(file->f_path.dentry))
                return 0;
 
        /* If in exec, permission is handled by bprm hooks.
@@ -378,69 +426,61 @@ static int apparmor_file_open(struct file *file, const struct cred *cred)
         * actually execute the image.
         */
        if (current->in_execve) {
-               fcxt->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP;
+               fctx->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP;
                return 0;
        }
 
-       profile = aa_cred_profile(cred);
-       if (!unconfined(profile)) {
+       label = aa_get_newest_cred_label(cred);
+       if (!unconfined(label)) {
                struct inode *inode = file_inode(file);
                struct path_cond cond = { inode->i_uid, inode->i_mode };
 
-               error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0,
+               error = aa_path_perm(OP_OPEN, label, &file->f_path, 0,
                                     aa_map_file_to_perms(file), &cond);
                /* todo cache full allowed permissions set and state */
-               fcxt->allow = aa_map_file_to_perms(file);
+               fctx->allow = aa_map_file_to_perms(file);
        }
+       aa_put_label(label);
 
        return error;
 }
 
 static int apparmor_file_alloc_security(struct file *file)
 {
+       int error = 0;
+
        /* freed by apparmor_file_free_security */
-       file->f_security = aa_alloc_file_context(GFP_KERNEL);
-       if (!file->f_security)
-               return -ENOMEM;
-       return 0;
+       struct aa_label *label = aa_begin_current_label(DO_UPDATE);
+       file->f_security = aa_alloc_file_ctx(label, GFP_KERNEL);
+       if (!file_ctx(file))
+               error = -ENOMEM;
+       aa_end_current_label(label);
 
+       return error;
 }
 
 static void apparmor_file_free_security(struct file *file)
 {
-       struct aa_file_cxt *cxt = file->f_security;
-
-       aa_free_file_context(cxt);
+       aa_free_file_ctx(file_ctx(file));
 }
 
-static int common_file_perm(int op, struct file *file, u32 mask)
+static int common_file_perm(const char *op, struct file *file, u32 mask)
 {
-       struct aa_file_cxt *fcxt = file->f_security;
-       struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred);
+       struct aa_label *label;
        int error = 0;
 
-       BUG_ON(!fprofile);
-
-       if (!file->f_path.mnt ||
-           !mediated_filesystem(file->f_path.dentry))
-               return 0;
-
-       profile = __aa_current_profile();
-
-       /* revalidate access, if task is unconfined, or the cached cred
-        * doesn't match or if the request is for more permissions than
-        * was granted.
-        *
-        * Note: the test for !unconfined(fprofile) is to handle file
-        *       delegation from unconfined tasks
-        */
-       if (!unconfined(profile) && !unconfined(fprofile) &&
-           ((fprofile != profile) || (mask & ~fcxt->allow)))
-               error = aa_file_perm(op, profile, file, mask);
+       label = aa_begin_current_label(NO_UPDATE);
+       error = aa_file_perm(op, label, file, mask);
+       aa_end_current_label(label);
 
        return error;
 }
 
+static int apparmor_file_receive(struct file *file)
+{
+       return common_file_perm(OP_FRECEIVE, file, aa_map_file_to_perms(file));
+}
+
 static int apparmor_file_permission(struct file *file, int mask)
 {
        return common_file_perm(OP_FPERM, file, mask);
@@ -456,12 +496,12 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd)
        return common_file_perm(OP_FLOCK, file, mask);
 }
 
-static int common_mmap(int op, struct file *file, unsigned long prot,
+static int common_mmap(const char *op, struct file *file, unsigned long prot,
                       unsigned long flags)
 {
        int mask = 0;
 
-       if (!file || !file->f_security)
+       if (!file || !file_ctx(file))
                return 0;
 
        if (prot & PROT_READ)
@@ -491,28 +531,86 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma,
                           !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
 }
 
+static int apparmor_sb_mount(const char *dev_name, struct path *path,
+                            const char *type, unsigned long flags, void *data)
+{
+       struct aa_label *label;
+       int error = 0;
+
+       /* Discard magic */
+       if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
+               flags &= ~MS_MGC_MSK;
+
+       flags &= ~AA_MS_IGNORE_MASK;
+
+       label = aa_begin_current_label(NO_UPDATE);
+       if (!unconfined(label)) {
+               if (flags & MS_REMOUNT)
+                       error = aa_remount(label, path, flags, data);
+               else if (flags & MS_BIND)
+                       error = aa_bind_mount(label, path, dev_name, flags);
+               else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE |
+                                 MS_UNBINDABLE))
+                       error = aa_mount_change_type(label, path, flags);
+               else if (flags & MS_MOVE)
+                       error = aa_move_mount(label, path, dev_name);
+               else
+                       error = aa_new_mount(label, dev_name, path, type,
+                                            flags, data);
+       }
+       aa_end_current_label(label);
+
+       return error;
+}
+
+static int apparmor_sb_umount(struct vfsmount *mnt, int flags)
+{
+       struct aa_label *label;
+       int error = 0;
+
+       label = aa_begin_current_label(NO_UPDATE);
+       if (!unconfined(label))
+               error = aa_umount(label, mnt, flags);
+       aa_end_current_label(label);
+
+       return error;
+}
+
+static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path)
+{
+       struct aa_label *label;
+       int error = 0;
+
+       label = aa_get_current_label();
+       if (!unconfined(label))
+               error = aa_pivotroot(label, old_path, new_path);
+       aa_put_label(label);
+
+       return error;
+}
+
 static int apparmor_getprocattr(struct task_struct *task, char *name,
                                char **value)
 {
        int error = -ENOENT;
        /* released below */
        const struct cred *cred = get_task_cred(task);
-       struct aa_task_cxt *cxt = cred_cxt(cred);
-       struct aa_profile *profile = NULL;
+       struct aa_task_ctx *ctx = cred_ctx(cred);
+       struct aa_label *label = NULL;
 
        if (strcmp(name, "current") == 0)
-               profile = aa_get_newest_profile(cxt->profile);
-       else if (strcmp(name, "prev") == 0  && cxt->previous)
-               profile = aa_get_newest_profile(cxt->previous);
-       else if (strcmp(name, "exec") == 0 && cxt->onexec)
-               profile = aa_get_newest_profile(cxt->onexec);
+               label = aa_get_newest_label(ctx->label);
+       else if (strcmp(name, "prev") == 0  && ctx->previous)
+               label = aa_get_newest_label(ctx->previous);
+       else if (strcmp(name, "exec") == 0 && ctx->onexec)
+               label = aa_get_newest_label(ctx->onexec);
        else
                error = -EINVAL;
 
-       if (profile)
-               error = aa_getprocattr(profile, value);
+       if (label)
+               error = aa_getprocattr(label, value);
 
-       aa_put_profile(profile);
+       aa_put_label(label);
        put_cred(cred);
 
        return error;
@@ -521,11 +619,10 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
 static int apparmor_setprocattr(struct task_struct *task, char *name,
                                void *value, size_t size)
 {
-       struct common_audit_data sa;
-       struct apparmor_audit_data aad = {0,};
        char *command, *args = value;
        size_t arg_size;
        int error;
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETPROCATTR);
 
        if (size == 0)
                return -EINVAL;
@@ -561,17 +658,23 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
                        error = aa_setprocattr_changehat(args, arg_size,
                                                         AA_DO_TEST);
                } else if (strcmp(command, "changeprofile") == 0) {
-                       error = aa_setprocattr_changeprofile(args, !AA_ONEXEC,
-                                                            !AA_DO_TEST);
+                       error = aa_change_profile(args, !AA_ONEXEC,
+                                                 !AA_DO_TEST, false);
                } else if (strcmp(command, "permprofile") == 0) {
-                       error = aa_setprocattr_changeprofile(args, !AA_ONEXEC,
-                                                            AA_DO_TEST);
+                       error = aa_change_profile(args, !AA_ONEXEC, AA_DO_TEST,
+                                                 false);
+               } else if (strcmp(command, "stack") == 0) {
+                       error = aa_change_profile(args, !AA_ONEXEC, !AA_DO_TEST,
+                                                 true);
                } else
                        goto fail;
        } else if (strcmp(name, "exec") == 0) {
                if (strcmp(command, "exec") == 0)
-                       error = aa_setprocattr_changeprofile(args, AA_ONEXEC,
-                                                            !AA_DO_TEST);
+                       error = aa_change_profile(args, AA_ONEXEC, !AA_DO_TEST,
+                                                 false);
+               else if (strcmp(command, "stack") == 0)
+                       error = aa_change_profile(args, AA_ONEXEC, !AA_DO_TEST,
+                                                 true);
                else
                        goto fail;
        } else
@@ -583,34 +686,500 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
        return error;
 
 fail:
-       sa.type = LSM_AUDIT_DATA_NONE;
-       sa.aad = &aad;
-       aad.profile = aa_current_profile();
-       aad.op = OP_SETPROCATTR;
-       aad.info = name;
-       aad.error = -EINVAL;
+       aad(&sa)->label = aa_begin_current_label(DO_UPDATE);
+       aad(&sa)->info = name;
+       aad(&sa)->error = -EINVAL;
        aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL);
+       aa_end_current_label(aad(&sa)->label);
        return -EINVAL;
 }
 
+/**
+ * apparmor_bprm_committing_creds - do task cleanup on committing new creds
+ * @bprm: binprm for the exec  (NOT NULL)
+ */
+void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
+{
+       struct aa_label *label = aa_current_raw_label();
+       struct aa_task_ctx *new_ctx = cred_ctx(bprm->cred);
+
+       /* bail out if unconfined or not changing profile */
+       if ((new_ctx->label->proxy == label->proxy) ||
+           (unconfined(new_ctx->label)))
+               return;
+
+       aa_inherit_files(bprm->cred, current->files);
+
+       current->pdeath_signal = 0;
+
+       /* reset soft limits and set hard limits for the new label */
+       __aa_transition_rlimits(label, new_ctx->label);
+}
+
+/**
+ * apparmor_bprm_commited_cred - do cleanup after new creds committed
+ * @bprm: binprm for the exec  (NOT NULL)
+ */
+void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
+{
+       /* TODO: cleanup signals - ipc mediation */
+       return;
+}
+
 static int apparmor_task_setrlimit(struct task_struct *task,
                unsigned int resource, struct rlimit *new_rlim)
 {
-       struct aa_profile *profile = __aa_current_profile();
+       struct aa_label *label = aa_begin_current_label(NO_UPDATE);
        int error = 0;
 
-       if (!unconfined(profile))
-               error = aa_task_setrlimit(profile, task, resource, new_rlim);
+       if (!unconfined(label))
+               error = aa_task_setrlimit(label, task, resource, new_rlim);
+       aa_end_current_label(label);
+
+       return error;
+}
+
+/**
+ * apparmor_sk_alloc_security - allocate and attach the sk_security field
+ */
+static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags)
+{
+       struct aa_sk_ctx *ctx;
+
+       ctx = kzalloc(sizeof(*ctx), flags);
+       if (!ctx)
+               return -ENOMEM;
+
+       SK_CTX(sk) = ctx;
+       //??? set local too current???
+
+       return 0;
+}
+
+/**
+ * apparmor_sk_free_security - free the sk_security field
+ */
+static void apparmor_sk_free_security(struct sock *sk)
+{
+       struct aa_sk_ctx *ctx = SK_CTX(sk);
+
+       SK_CTX(sk) = NULL;
+       aa_put_label(ctx->label);
+       aa_put_label(ctx->peer);
+       path_put(&ctx->path);
+       kfree(ctx);
+}
+
+/**
+ * apparmor_clone_security - clone the sk_security field
+ */
+static void apparmor_sk_clone_security(const struct sock *sk,
+                                      struct sock *newsk)
+{
+       struct aa_sk_ctx *ctx = SK_CTX(sk);
+       struct aa_sk_ctx *new = SK_CTX(newsk);
+
+       new->label = aa_get_label(ctx->label);
+       new->peer = aa_get_label(ctx->peer);
+       new->path = ctx->path;
+       path_get(&new->path);
+}
+
+static struct path *UNIX_FS_CONN_PATH(struct sock *sk, struct sock *newsk)
+{
+       if (sk->sk_family == PF_UNIX && UNIX_FS(sk))
+               return &unix_sk(sk)->path;
+       else if (newsk->sk_family == PF_UNIX && UNIX_FS(newsk))
+               return &unix_sk(newsk)->path;
+       return NULL;
+}
+
+/**
+ * apparmor_unix_stream_connect - check perms before making unix domain conn
+ *
+ * peer is locked when this hook is called
+ */
+static int apparmor_unix_stream_connect(struct sock *sk, struct sock *peer_sk,
+                                       struct sock *newsk)
+{
+       struct aa_sk_ctx *sk_ctx = SK_CTX(sk);
+       struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk);
+       struct aa_sk_ctx *new_ctx = SK_CTX(newsk);
+       struct aa_label *label;
+       struct path *path;
+       int error;
+
+       label = aa_begin_current_label(NO_UPDATE);
+       error = aa_unix_peer_perm(label, OP_CONNECT,
+                               (AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE),
+                                 sk, peer_sk, NULL);
+       if (!UNIX_FS(peer_sk)) {
+               last_error(error,
+                       aa_unix_peer_perm(peer_ctx->label, OP_CONNECT,
+                               (AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE),
+                               peer_sk, sk, label));
+       }
+       aa_end_current_label(label);
+
+       if (error)
+               return error;
+
+       /* label newsk if it wasn't labeled in post_create. Normally this
+        * would be done in sock_graft, but because we are directly looking
+        * at the peer_sk to obtain peer_labeling for unix socks this
+        * does not work
+        */
+       if (!new_ctx->label)
+               new_ctx->label = aa_get_label(peer_ctx->label);
+
+       /* Cross reference the peer labels for SO_PEERSEC */
+       if (new_ctx->peer)
+               aa_put_label(new_ctx->peer);
+
+       if (sk_ctx->peer)
+               aa_put_label(sk_ctx->peer);
+
+       new_ctx->peer = aa_get_label(sk_ctx->label);
+       sk_ctx->peer = aa_get_label(peer_ctx->label);
+
+       path = UNIX_FS_CONN_PATH(sk, peer_sk);
+       if (path) {
+               new_ctx->path = *path;
+               sk_ctx->path = *path;
+               path_get(path);
+               path_get(path);
+       }
+       return 0;
+}
+
+/**
+ * apparmor_unix_may_send - check perms before conn or sending unix dgrams
+ *
+ * other is locked when this hook is called
+ *
+ * dgram connect calls may_send, peer setup but path not copied?????
+ */
+static int apparmor_unix_may_send(struct socket *sock, struct socket *peer)
+{
+       struct aa_sk_ctx *peer_ctx = SK_CTX(peer->sk);
+       struct aa_label *label = aa_begin_current_label(NO_UPDATE);
+       int error;
+
+       error = xcheck(aa_unix_peer_perm(label, OP_SENDMSG, AA_MAY_SEND,
+                                        sock->sk, peer->sk, NULL),
+                      aa_unix_peer_perm(peer_ctx->label, OP_SENDMSG, AA_MAY_RECEIVE,
+                                        peer->sk, sock->sk, label));
+       aa_end_current_label(label);
+
+       return error;
+}
+
+/**
+ * apparmor_socket_create - check perms before creating a new socket
+ */
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
+{
+       struct aa_label *label;
+       int error = 0;
+
+       label = aa_begin_current_label(DO_UPDATE);
+       if (!(kern || unconfined(label)))
+               error = aa_sock_create_perm(label, family, type, protocol);
+       aa_end_current_label(label);
+
+       return error;
+}
+
+/**
+ * apparmor_socket_post_create - setup the per-socket security struct
+ *
+ * Note:
+ * -   kernel sockets currently labeled unconfined but we may want to
+ *     move to a special kernel label
+ * -   socket may not have sk here if created with sock_create_lite or
+ *     sock_alloc. These should be accept cases which will be handled in
+ *     sock_graft.
+ */
+static int apparmor_socket_post_create(struct socket *sock, int family,
+                                      int type, int protocol, int kern)
+{
+       struct aa_label *label;
+
+       if (kern) {
+               struct aa_ns *ns = aa_get_current_ns();
+               label = aa_get_label(ns_unconfined(ns));
+               aa_put_ns(ns);
+       } else
+               label = aa_get_current_label();
+
+       if (sock->sk) {
+               struct aa_sk_ctx *ctx = SK_CTX(sock->sk);
+               aa_put_label(ctx->label);
+               ctx->label = aa_get_label(label);
+       }
+       aa_put_label(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)
+{
+       return aa_sock_bind_perm(sock, address, addrlen);
+}
+
+/**
+ * apparmor_socket_connect - check perms before connecting @sock to @address
+ */
+static int apparmor_socket_connect(struct socket *sock,
+                                  struct sockaddr *address, int addrlen)
+{
+       return aa_sock_connect_perm(sock, address, addrlen);
+}
+
+/**
+ * apparmor_socket_list - check perms before allowing listen
+ */
+static int apparmor_socket_listen(struct socket *sock, int backlog)
+{
+       return aa_sock_listen_perm(sock, backlog);
+}
+
+/**
+ * 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)
+{
+       return aa_sock_accept_perm(sock, newsock);
+}
+
+/**
+ * apparmor_socket_sendmsg - check perms before sending msg to another socket
+ */
+static int apparmor_socket_sendmsg(struct socket *sock,
+                                  struct msghdr *msg, int size)
+{
+       int error = aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size);
+       if (!error) {
+               /* TODO: setup delegation on scm rights
+                  see smack for AF_INET, AF_INET6 */
+               ;
+       }
+
+       return error;
+}
+
+/**
+ * apparmor_socket_recvmsg - check perms before receiving a message
+ */
+static int apparmor_socket_recvmsg(struct socket *sock,
+                                  struct msghdr *msg, int size, int flags)
+{
+       return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size);
+}
+
+/**
+ * apparmor_socket_getsockname - check perms before getting the local address
+ */
+static int apparmor_socket_getsockname(struct socket *sock)
+{
+       return aa_sock_perm(OP_GETSOCKNAME, AA_MAY_GETATTR, sock);
+}
+
+/**
+ * apparmor_socket_getpeername - check perms before getting remote address
+ */
+static int apparmor_socket_getpeername(struct socket *sock)
+{
+       return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock);
+}
+
+/**
+ * apparmor_getsockopt - check perms before getting socket options
+ */
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
+                                     int optname)
+{
+       return aa_sock_opt_perm(OP_GETSOCKOPT, AA_MAY_GETOPT, sock,
+                               level, optname);
+}
+
+/**
+ * apparmor_setsockopt - check perms before setting socket options
+ */
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
+                                     int optname)
+{
+       return aa_sock_opt_perm(OP_SETSOCKOPT, AA_MAY_SETOPT, sock,
+                               level, optname);
+}
+
+/**
+ * apparmor_socket_shutdown - check perms before shutting down @sock conn
+ */
+static int apparmor_socket_shutdown(struct socket *sock, int how)
+{
+       return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock);
+}
+
+/**
+ * apparmor_socket_sock_recv_skb - check perms before associating skb to sk
+ *
+ * Note: can not sleep maybe called with locks held
+
+dont want protocol specific in __skb_recv_datagram()
+to deny an incoming connection  socket_sock_rcv_skb()
+
+ */
+static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+       /* TODO: */
+       return 0;
+}
+
+
+static struct aa_label *sk_peer_label(struct sock *sk)
+{
+       struct sock *peer_sk;
+       struct aa_sk_ctx *ctx = SK_CTX(sk);
+
+       if (ctx->peer)
+               return ctx->peer;
+
+       if (sk->sk_family != PF_UNIX)
+               return ERR_PTR(-ENOPROTOOPT);
+
+       /* check for sockpair peering which does not go through
+        * security_unix_stream_connect
+        */
+       peer_sk = unix_peer(sk);
+       if (peer_sk) {
+               ctx = SK_CTX(peer_sk);
+               if (ctx->label)
+                       return ctx->label;
+       }
+
+       return ERR_PTR(-ENOPROTOOPT);
+}
+
+/**
+ * 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_label *label = aa_begin_current_label(DO_UPDATE);
+       struct aa_label *peer = sk_peer_label(sock->sk);
+
+       if (IS_ERR(peer))
+               return PTR_ERR(peer);
+
+       slen = aa_label_asxprint(&name, labels_ns(label), peer,
+                                FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
+                                FLAG_HIDDEN_UNCONFINED, GFP_KERNEL);
+       /* don't include terminating \0 in slen, it breaks some apps */
+       if (slen < 0) {
+               error = -ENOMEM;
+       } else {
+               if (slen > len) {
+                       error = -ERANGE;
+               } else if (copy_to_user(optval, name, slen)) {
+                       error = -EFAULT;
+                       goto out;
+               }
+               if (put_user(slen, optlen))
+                       error = -EFAULT;
+       out:
+               kfree(name);
+
+       }
+       aa_end_current_label(label);
+
+       return error;
+}
+
+/**
+ * apparmor_socket_getpeersec_dgram - get security label of packet
+ * @sock: the peer socket
+ * @skb: packet data
+ * @secid: pointer to where to put the secid of the packet
+ *
+ * Sets the netlabel socket state on sk from parent
+ */
+static int apparmor_socket_getpeersec_dgram(struct socket *sock,
+                                           struct sk_buff *skb, u32 *secid)
+
+{
+       /* TODO: requires secid support, and netlabel */
+       return -ENOPROTOOPT;
+}
+
+/**
+ * apparmor_sock_graft - Initialize newly created socket
+ * @sk: child sock
+ * @parent: parent socket
+ *
+ * Note: could set off of SOCK_CTX(parent) but need to track inode and we can
+ *       just set sk security information off of current creating process label
+ *       Labeling of sk for accept case - probably should be sock based
+ *       instead of task, because of the case where an implicitly labeled
+ *       socket is shared by different tasks.
+ */
+static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
+{
+       struct aa_sk_ctx *ctx = SK_CTX(sk);
+       if (!ctx->label)
+               ctx->label = aa_get_current_label();
+}
+
+static int apparmor_task_kill(struct task_struct *target, struct siginfo *info,
+                             int sig, u32 secid)
+{
+       struct aa_label *cl, *tl;
+       int error;
+
+       if (secid)
+               /* TODO: after secid to label mapping is done.
+                *  Dealing with USB IO specific behavior
+                */
+               return 0;
+       cl = aa_begin_current_label(NO_UPDATE);
+       tl = aa_get_task_label(target);
+       error = aa_may_signal(cl, tl, sig);
+       aa_put_label(tl);
+       aa_end_current_label(cl);
 
        return error;
 }
 
+#ifndef LSM_HOOKS_NAME
+#define LSM_HOOKS_NAME(X) //.name =    (X),
+#endif
 static struct security_hook_list apparmor_hooks[] = {
+       LSM_HOOKS_NAME("apparmor")
+
        LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
        LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
        LSM_HOOK_INIT(capget, apparmor_capget),
        LSM_HOOK_INIT(capable, apparmor_capable),
 
+       LSM_HOOK_INIT(inode_free_security, apparmor_inode_free_security),
+
+       LSM_HOOK_INIT(sb_mount, apparmor_sb_mount),
+       LSM_HOOK_INIT(sb_umount, apparmor_sb_umount),
+       LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot),
+
        LSM_HOOK_INIT(path_link, apparmor_path_link),
        LSM_HOOK_INIT(path_unlink, apparmor_path_unlink),
        LSM_HOOK_INIT(path_symlink, apparmor_path_symlink),
@@ -624,16 +1193,43 @@ static struct security_hook_list apparmor_hooks[] = {
        LSM_HOOK_INIT(inode_getattr, apparmor_inode_getattr),
 
        LSM_HOOK_INIT(file_open, apparmor_file_open),
+       LSM_HOOK_INIT(file_receive, apparmor_file_receive),
        LSM_HOOK_INIT(file_permission, apparmor_file_permission),
        LSM_HOOK_INIT(file_alloc_security, apparmor_file_alloc_security),
        LSM_HOOK_INIT(file_free_security, apparmor_file_free_security),
        LSM_HOOK_INIT(mmap_file, apparmor_mmap_file),
+       LSM_HOOK_INIT(mmap_addr, cap_mmap_addr),
        LSM_HOOK_INIT(file_mprotect, apparmor_file_mprotect),
        LSM_HOOK_INIT(file_lock, apparmor_file_lock),
 
        LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
        LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
 
+       LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security),
+       LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security),
+       LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security),
+
+       LSM_HOOK_INIT(unix_stream_connect, apparmor_unix_stream_connect),
+       LSM_HOOK_INIT(unix_may_send, apparmor_unix_may_send),
+
+       LSM_HOOK_INIT(socket_create, apparmor_socket_create),
+       LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create),
+       LSM_HOOK_INIT(socket_bind, apparmor_socket_bind),
+       LSM_HOOK_INIT(socket_connect, apparmor_socket_connect),
+       LSM_HOOK_INIT(socket_listen, apparmor_socket_listen),
+       LSM_HOOK_INIT(socket_accept, apparmor_socket_accept),
+       LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg),
+       LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg),
+       LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname),
+       LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername),
+       LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt),
+       LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt),
+       LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown),
+       LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb),
+       LSM_HOOK_INIT(socket_getpeersec_stream, apparmor_socket_getpeersec_stream),
+       LSM_HOOK_INIT(socket_getpeersec_dgram, apparmor_socket_getpeersec_dgram),
+       LSM_HOOK_INIT(sock_graft, apparmor_sock_graft),
+
        LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank),
        LSM_HOOK_INIT(cred_free, apparmor_cred_free),
        LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare),
@@ -645,6 +1241,7 @@ static struct security_hook_list apparmor_hooks[] = {
        LSM_HOOK_INIT(bprm_secureexec, apparmor_bprm_secureexec),
 
        LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
+       LSM_HOOK_INIT(task_kill, apparmor_task_kill),
 };
 
 /*
@@ -690,23 +1287,26 @@ static int param_get_mode(char *buffer, struct kernel_param *kp);
 /* AppArmor global enforcement switch - complain, enforce, kill */
 enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE;
 module_param_call(mode, param_set_mode, param_get_mode,
-                 &aa_g_profile_mode, S_IRUSR | S_IWUSR);
+                 &aa_g_profile_mode, S_IRUGO | S_IWUSR);
+
+/* whether policy verification hashing is enabled */
+bool aa_g_hash_policy = CONFIG_SECURITY_APPARMOR_HASH_DEFAULT;
+module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUGO | S_IWUSR);
 
 /* Debug mode */
 bool aa_g_debug;
-module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR);
+module_param_named(debug, aa_g_debug, aabool, S_IRUGO | S_IWUSR);
 
 /* Audit mode */
 enum audit_mode aa_g_audit;
-module_param_call(audit, param_set_audit, param_get_audit,
-                 &aa_g_audit, S_IRUSR | S_IWUSR);
+module_param_call(audit, param_set_audit, param_get_audit, &aa_g_audit,
+                 S_IRUGO | S_IWUSR);
 
 /* Determines if audit header is included in audited messages.  This
  * provides more context if the audit daemon is not running
  */
 bool aa_g_audit_header = 1;
-module_param_named(audit_header, aa_g_audit_header, aabool,
-                  S_IRUSR | S_IWUSR);
+module_param_named(audit_header, aa_g_audit_header, aabool, S_IRUGO | S_IWUSR);
 
 /* lock out loading/removal of policy
  * TODO: add in at boot loading of policy, which is the only way to
@@ -714,27 +1314,33 @@ module_param_named(audit_header, aa_g_audit_header, aabool,
  */
 bool aa_g_lock_policy;
 module_param_named(lock_policy, aa_g_lock_policy, aalockpolicy,
-                  S_IRUSR | S_IWUSR);
+                  S_IRUGO | S_IWUSR);
 
 /* Syscall logging mode */
 bool aa_g_logsyscall;
-module_param_named(logsyscall, aa_g_logsyscall, aabool, S_IRUSR | S_IWUSR);
+module_param_named(logsyscall, aa_g_logsyscall, aabool, S_IRUGO | S_IWUSR);
 
 /* Maximum pathname length before accesses will start getting rejected */
 unsigned int aa_g_path_max = 2 * PATH_MAX;
-module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR);
+module_param_named(path_max, aa_g_path_max, aauint, S_IRUGO | S_IWUSR);
 
 /* Determines how paranoid loading of policy is and how much verification
  * on the loaded policy is done.
+ * DEPRECATED: read only as strict checking of load is always done now
+ * that none root users (user namespaces) can load policy.
  */
 bool aa_g_paranoid_load = 1;
-module_param_named(paranoid_load, aa_g_paranoid_load, aabool,
-                  S_IRUSR | S_IWUSR);
+module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO);
 
 /* Boot time disable flag */
 static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE;
 module_param_named(enabled, apparmor_enabled, bool, S_IRUGO);
 
+/* Boot time to set use of default or unconfined as initial profile */
+bool aa_g_unconfined_init = CONFIG_SECURITY_APPARMOR_UNCONFINED_INIT;
+module_param_named(unconfined, aa_g_unconfined_init, aabool, S_IRUGO);
+
+
 static int __init apparmor_enabled_setup(char *str)
 {
        unsigned long enabled;
@@ -749,7 +1355,7 @@ __setup("apparmor=", apparmor_enabled_setup);
 /* set global flag turning off the ability to load policy */
 static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
        if (aa_g_lock_policy)
                return -EACCES;
@@ -758,59 +1364,65 @@ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp
 
 static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
+       if (!apparmor_enabled)
+               return -EINVAL;
        return param_get_bool(buffer, kp);
 }
 
 static int param_set_aabool(const char *val, const struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
+       if (!apparmor_enabled)
+               return -EINVAL;
        return param_set_bool(val, kp);
 }
 
 static int param_get_aabool(char *buffer, const struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
+       if (!apparmor_enabled)
+               return -EINVAL;
        return param_get_bool(buffer, kp);
 }
 
 static int param_set_aauint(const char *val, const struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
+       if (!apparmor_enabled)
+               return -EINVAL;
        return param_set_uint(val, kp);
 }
 
 static int param_get_aauint(char *buffer, const struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
+       if (!apparmor_enabled)
+               return -EINVAL;
        return param_get_uint(buffer, kp);
 }
 
 static int param_get_audit(char *buffer, struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
-
        if (!apparmor_enabled)
                return -EINVAL;
-
        return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]);
 }
 
 static int param_set_audit(const char *val, struct kernel_param *kp)
 {
        int i;
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
-
        if (!apparmor_enabled)
                return -EINVAL;
-
        if (!val)
                return -EINVAL;
 
@@ -826,9 +1438,8 @@ static int param_set_audit(const char *val, struct kernel_param *kp)
 
 static int param_get_mode(char *buffer, struct kernel_param *kp)
 {
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
-
        if (!apparmor_enabled)
                return -EINVAL;
 
@@ -838,12 +1449,10 @@ static int param_get_mode(char *buffer, struct kernel_param *kp)
 static int param_set_mode(const char *val, struct kernel_param *kp)
 {
        int i;
-       if (!capable(CAP_MAC_ADMIN))
+       if (!policy_admin_capable())
                return -EPERM;
-
        if (!apparmor_enabled)
                return -EINVAL;
-
        if (!val)
                return -EINVAL;
 
@@ -862,21 +1471,63 @@ static int param_set_mode(const char *val, struct kernel_param *kp)
  */
 
 /**
- * set_init_cxt - set a task context and profile on the first task.
- *
- * TODO: allow setting an alternate profile than unconfined
+ * set_init_ctx - set a task context and profile on the first task.
  */
-static int __init set_init_cxt(void)
+static int __init set_init_ctx(void)
 {
        struct cred *cred = (struct cred *)current->real_cred;
-       struct aa_task_cxt *cxt;
+       struct aa_task_ctx *ctx;
 
-       cxt = aa_alloc_task_context(GFP_KERNEL);
-       if (!cxt)
+       ctx = aa_alloc_task_context(GFP_KERNEL);
+       if (!ctx)
                return -ENOMEM;
 
-       cxt->profile = aa_get_profile(root_ns->unconfined);
-       cred_cxt(cred) = cxt;
+       if (!aa_g_unconfined_init) {
+               ctx->label = aa_setup_default_label();
+               if (!ctx->label) {
+                       aa_free_task_context(ctx);
+                       return -ENOMEM;
+               }
+               /* fs setup of default is done in aa_create_aafs() */
+       } else
+               ctx->label = aa_get_label(ns_unconfined(root_ns));
+       cred_ctx(cred) = ctx;
+
+       return 0;
+}
+
+static void destroy_buffers(void)
+{
+       u32 i, j;
+
+       for_each_possible_cpu(i) {
+               for_each_cpu_buffer(j) {
+                       kfree(per_cpu(aa_buffers, i).buf[j]);
+                       per_cpu(aa_buffers, i).buf[j] = NULL;
+               }
+       }
+}
+
+static int __init alloc_buffers(void)
+{
+       u32 i, j;
+
+       for_each_possible_cpu(i) {
+               for_each_cpu_buffer(j) {
+                       char *buffer;
+                       if (cpu_to_node(i) > num_online_nodes())
+                               /* fallback to kmalloc for offline nodes */
+                               buffer = kmalloc(aa_g_path_max, GFP_KERNEL);
+                       else
+                               buffer = kmalloc_node(aa_g_path_max, GFP_KERNEL,
+                                                     cpu_to_node(i));
+                       if (!buffer) {
+                               destroy_buffers();
+                               return -ENOMEM;
+                       }
+                       per_cpu(aa_buffers, i).buf[j] = buffer;
+               }
+       }
 
        return 0;
 }
@@ -891,17 +1542,29 @@ static int __init apparmor_init(void)
                return 0;
        }
 
+       error = aa_setup_dfa_engine();
+       if (error) {
+               AA_ERROR("Unable to setup dfa engine\n");
+               goto alloc_out;
+       }
+
        error = aa_alloc_root_ns();
        if (error) {
                AA_ERROR("Unable to allocate default profile namespace\n");
                goto alloc_out;
        }
 
-       error = set_init_cxt();
+       error = alloc_buffers();
+       if (error) {
+               AA_ERROR("Unable to allocate work buffers\n");
+               goto buffers_out;
+       }
+
+       error = set_init_ctx();
        if (error) {
                AA_ERROR("Failed to set context on init task\n");
                aa_free_root_ns();
-               goto alloc_out;
+               goto buffers_out;
        }
        security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks));
 
@@ -916,8 +1579,12 @@ static int __init apparmor_init(void)
 
        return error;
 
+buffers_out:
+       destroy_buffers();
+
 alloc_out:
        aa_destroy_aafs();
+       aa_teardown_dfa_engine();
 
        apparmor_enabled = 0;
        return error;
index 727eb4200d5c922d8818a6f0a84ace38fd759306..4fe26416bd9a9161524ff9be81697cabf8938364 100644 (file)
 #include <linux/err.h>
 #include <linux/kref.h>
 
-#include "include/apparmor.h"
+#include "include/lib.h"
 #include "include/match.h"
 
 #define base_idx(X) ((X) & 0xffffff)
 
+static char nulldfa_src[] = {
+       #include "nulldfa.in"
+};
+struct aa_dfa *nulldfa;
+
+int aa_setup_dfa_engine(void)
+{
+       int error;
+
+       nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src),
+                               TO_ACCEPT1_FLAG(YYTD_DATA32) |
+                               TO_ACCEPT2_FLAG(YYTD_DATA32));
+       if (!IS_ERR(nulldfa))
+               return 0;
+
+       error = PTR_ERR(nulldfa);
+       nulldfa = NULL;
+
+       return error;
+}
+
+void aa_teardown_dfa_engine(void)
+{
+       aa_put_dfa(nulldfa);
+       nulldfa = NULL;
+}
+
 /**
  * unpack_table - unpack a dfa table (one of accept, default, base, next check)
  * @blob: data to unpack (NOT NULL)
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
new file mode 100644 (file)
index 0000000..b380e32
--- /dev/null
@@ -0,0 +1,731 @@
+/*
+ * 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 <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+
+#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 (aad(sa)->mnt.type) {
+               audit_log_format(ab, " fstype=");
+               audit_log_untrustedstring(ab, aad(sa)->mnt.type);
+       }
+       if (aad(sa)->mnt.src_name) {
+               audit_log_format(ab, " srcname=");
+               audit_log_untrustedstring(ab, aad(sa)->mnt.src_name);
+       }
+       if (aad(sa)->mnt.trans) {
+               audit_log_format(ab, " trans=");
+               audit_log_untrustedstring(ab, aad(sa)->mnt.trans);
+       }
+       if (aad(sa)->mnt.flags) {
+               audit_log_format(ab, " flags=\"");
+               audit_mnt_flags(ab, aad(sa)->mnt.flags);
+               audit_log_format(ab, "\"");
+       }
+       if (aad(sa)->mnt.data) {
+               audit_log_format(ab, " options=");
+               audit_log_untrustedstring(ab, aad(sa)->mnt.data);
+       }
+}
+
+/**
+ * audit_mount - handle the auditing of mount operations
+ * @profile: the profile being enforced  (NOT NULL)
+ * @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, const char *op, const char *name,
+                      const char *src_name, const char *type,
+                      const char *trans, unsigned long flags,
+                      const void *data, u32 request, struct aa_perms *perms,
+                      const char *info, int error)
+{
+       int audit_type = AUDIT_APPARMOR_AUTO;
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
+
+       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 error;
+       }
+
+       aad(&sa)->name = name;
+       aad(&sa)->mnt.src_name = src_name;
+       aad(&sa)->mnt.type = type;
+       aad(&sa)->mnt.trans = trans;
+       aad(&sa)->mnt.flags = flags;
+       if (data && (perms->audit & AA_AUDIT_DATA))
+               aad(&sa)->mnt.data = data;
+       aad(&sa)->info = info;
+       aad(&sa)->error = error;
+
+       return aa_audit(audit_type, profile, &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 aa_perms compute_mnt_perms(struct aa_dfa *dfa,
+                                          unsigned int state)
+{
+       struct aa_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 *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 aa_perms *perms)
+{
+       unsigned int state;
+
+       AA_BUG(!dfa);
+       AA_BUG(!perms);
+
+       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_MNT_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 aa_perms perms = { };
+       const char *info = NULL;
+       int pos, error = -EACCES;
+
+       AA_BUG(!profile);
+
+       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];
+               goto audit;
+       }
+       error = 0;
+
+audit:
+       return audit_mount(profile, OP_MOUNT, mntpnt, devname, type, NULL,
+                          flags, data, AA_MAY_MOUNT, &perms, info, error);
+}
+
+static int path_flags(struct aa_profile *profile, struct path *path)
+{
+       AA_BUG(!profile);
+       AA_BUG(!path);
+
+       return profile->path_flags |
+               (S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0);
+}
+
+int aa_remount(struct aa_label *label, struct path *path, unsigned long flags,
+              void *data)
+{
+       struct aa_profile *profile;
+       const char *name, *info = NULL;
+       char *buffer = NULL;
+       bool binary;
+       int error;
+
+       AA_BUG(!label);
+       AA_BUG(!path);
+
+       binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA;
+
+       get_buffers(buffer);
+       error = aa_path_name(path, path_flags(labels_profile(label), path),
+                            buffer, &name, &info,
+                            labels_profile(label)->disconnected);
+       if (error) {
+               error = audit_mount(labels_profile(label), OP_MOUNT, name, NULL,
+                                   NULL, NULL, flags, data, AA_MAY_MOUNT,
+                                   &nullperms, info, error);
+               goto out;
+       }
+
+       error = fn_for_each_confined(label, profile,
+                       match_mnt(profile, name, NULL, NULL, flags, data,
+                                 binary));
+
+out:
+       put_buffers(buffer);
+
+       return error;
+}
+
+int aa_bind_mount(struct aa_label *label, struct path *path,
+                 const char *dev_name, unsigned long flags)
+{
+       struct aa_profile *profile;
+       char *buffer = NULL, *old_buffer = NULL;
+       const char *name, *old_name = NULL, *info = NULL;
+       struct path old_path;
+       int error;
+
+       AA_BUG(!label);
+       AA_BUG(!path);
+
+       if (!dev_name || !*dev_name)
+               return -EINVAL;
+
+       flags &= MS_REC | MS_BIND;
+
+       error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
+       if (error)
+               return error;
+
+       get_buffers(buffer, old_buffer);
+       error = aa_path_name(path, path_flags(labels_profile(label), path), buffer, &name,
+                            &info, labels_profile(label)->disconnected);
+       if (error)
+               goto error;
+
+       error = aa_path_name(&old_path, path_flags(labels_profile(label),
+                                                  &old_path),
+                            old_buffer, &old_name, &info,
+                            labels_profile(label)->disconnected);
+       path_put(&old_path);
+       if (error)
+               goto error;
+
+       error = fn_for_each_confined(label, profile,
+                       match_mnt(profile, name, old_name, NULL, flags, NULL,
+                                 false));
+
+out:
+       put_buffers(buffer, old_buffer);
+
+       return error;
+
+error:
+       error = fn_for_each(label, profile,
+                       audit_mount(profile, OP_MOUNT, name, old_name, NULL,
+                                   NULL, flags, NULL, AA_MAY_MOUNT, &nullperms,
+                                   info, error));
+       goto out;
+}
+
+int aa_mount_change_type(struct aa_label *label, struct path *path,
+                        unsigned long flags)
+{
+       struct aa_profile *profile;
+       char *buffer = NULL;
+       const char *name, *info = NULL;
+       int error;
+
+       AA_BUG(!label);
+       AA_BUG(!path);
+
+       /* These are the flags allowed by do_change_type() */
+       flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
+                 MS_UNBINDABLE);
+
+       get_buffers(buffer);
+       error = aa_path_name(path, path_flags(labels_profile(label), path),
+                            buffer, &name, &info,
+                            labels_profile(label)->disconnected);
+       if (error) {
+               error = fn_for_each(label, profile,
+                               audit_mount(profile, OP_MOUNT, name, NULL,
+                                           NULL, NULL, flags, NULL,
+                                           AA_MAY_MOUNT, &nullperms, info,
+                                           error));
+               goto out;
+       }
+
+       error = fn_for_each_confined(label, profile,
+                       match_mnt(profile, name, NULL, NULL, flags, NULL,
+                                 false));
+
+out:
+       put_buffers(buffer);
+
+       return error;
+}
+
+int aa_move_mount(struct aa_label *label, struct path *path,
+                 const char *orig_name)
+{
+       struct aa_profile *profile;
+       char *buffer = NULL, *old_buffer = NULL;
+       const char *name, *old_name = NULL, *info = NULL;
+       struct path old_path;
+       int error;
+
+       AA_BUG(!label);
+       AA_BUG(!path);
+
+       if (!orig_name || !*orig_name)
+               return -EINVAL;
+
+       error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path);
+       if (error)
+               return error;
+
+       get_buffers(buffer, old_buffer);
+       error = aa_path_name(path, path_flags(labels_profile(label), path),
+                            buffer, &name, &info,
+                            labels_profile(label)->disconnected);
+       if (error)
+               goto error;
+
+       error = aa_path_name(&old_path, path_flags(labels_profile(label),
+                                                  &old_path),
+                            old_buffer, &old_name, &info,
+                            labels_profile(label)->disconnected);
+       path_put(&old_path);
+       if (error)
+               goto error;
+
+       error = fn_for_each_confined(label, profile,
+                       match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL,
+                                 false));
+
+out:
+       put_buffers(buffer, old_buffer);
+
+       return error;
+
+error:
+       error = fn_for_each(label, profile,
+                       audit_mount(profile, OP_MOUNT, name, old_name, NULL,
+                                   NULL, MS_MOVE, NULL, AA_MAY_MOUNT,
+                                   &nullperms, info, error));
+       goto out;
+}
+
+int aa_new_mount(struct aa_label *label, const char *orig_dev_name,
+                struct path *path, const char *type, unsigned long flags,
+                void *data)
+{
+       struct aa_profile *profile;
+       char *buffer = NULL, *dev_buffer = NULL;
+       const char *name = NULL, *dev_name = NULL, *info = NULL;
+       bool binary = true;
+       int error;
+       int requires_dev = 0;
+       struct path dev_path;
+
+       AA_BUG(!label);
+       AA_BUG(!path);
+
+       dev_name = orig_dev_name;
+       if (type) {
+               struct file_system_type *fstype;
+               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) {
+                       if (!dev_name || !*dev_name)
+                               return -ENOENT;
+
+                       error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path);
+                       if (error)
+                               return error;
+               }
+       }
+
+       get_buffers(buffer, dev_buffer);
+       if (type && requires_dev) {
+               error = aa_path_name(&dev_path,
+                                    path_flags(labels_profile(label),
+                                               &dev_path),
+                                    dev_buffer, &dev_name, &info,
+                                    labels_profile(label)->disconnected);
+               path_put(&dev_path);
+               if (error)
+                       goto error;
+       }
+
+       error = aa_path_name(path, path_flags(labels_profile(label), path),
+                            buffer, &name, &info,
+                            labels_profile(label)->disconnected);
+       if (error)
+               goto error;
+
+       error = fn_for_each_confined(label, profile,
+                       match_mnt(profile, name, dev_name, type, flags, data,
+                                 binary));
+
+cleanup:
+       put_buffers(buffer, dev_buffer);
+
+       return error;
+
+error:
+       error = fn_for_each(label, profile,
+                       audit_mount(labels_profile(label), OP_MOUNT, name,
+                                   dev_name, type, NULL, flags, data,
+                                   AA_MAY_MOUNT, &nullperms, info, error));
+       goto cleanup;
+}
+
+static int profile_umount(struct aa_profile *profile, const char *name)
+{
+       struct aa_perms perms = { };
+       const char *info = NULL;
+       unsigned int state;
+       int e = 0;
+
+       AA_BUG(!profile);
+       AA_BUG(!name);
+
+       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)
+               e = -EACCES;
+
+       return audit_mount(profile, OP_UMOUNT, name, NULL, NULL, NULL, 0, NULL,
+                          AA_MAY_UMOUNT, &perms, info, e);
+}
+
+int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags)
+{
+       struct aa_profile *profile;
+       char *buffer = NULL;
+       const char *name, *info = NULL;
+       int error;
+       struct path path = { mnt, mnt->mnt_root };
+
+       AA_BUG(!label);
+       AA_BUG(!mnt);
+
+       get_buffers(buffer);
+       error = aa_path_name(&path, path_flags(labels_profile(label), &path),
+                            buffer, &name, &info,
+                            labels_profile(label)->disconnected);
+       if (error) {
+               error = fn_for_each(label, profile,
+                               audit_mount(profile, OP_UMOUNT, name, NULL,
+                                           NULL, NULL, 0, NULL, AA_MAY_UMOUNT,
+                                           &nullperms, info, error));
+               goto out;
+       }
+
+       error = fn_for_each_confined(label, profile,
+                       profile_umount(profile, name));
+
+out:
+       put_buffers(buffer);
+
+       return error;
+}
+
+static int profile_pivotroot(struct aa_profile *profile, const char *new_name,
+                            const char *old_name, struct aa_label **trans)
+{
+       struct aa_label *target = NULL;
+       const char *trans_name = NULL;
+       struct aa_perms perms = { };
+       const char *info = NULL;
+       unsigned int state;
+       int error = -EACCES;
+
+       AA_BUG(!profile);
+       AA_BUG(!new_name);
+       AA_BUG(!old_name);
+       AA_BUG(!trans);
+
+       /* TODO: actual domain transition computation for multiple
+        *  profiles
+        */
+       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,
+                                               &trans_name);
+                       if (!target)
+                               error = -ENOENT;
+                       else
+                               *trans = target;
+               } else
+                       error = 0;
+       }
+
+       error = audit_mount(profile, OP_PIVOTROOT, new_name, old_name,
+                           NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT,
+                           &perms, info, error);
+       if (!*trans)
+               aa_put_label(target);
+
+       return error;
+}
+
+int aa_pivotroot(struct aa_label *label, struct path *old_path,
+                 struct path *new_path)
+{
+       struct aa_profile *profile;
+       struct aa_label *target = NULL;
+       char *old_buffer = NULL, *new_buffer = NULL;
+       const char *old_name, *new_name = NULL, *info = NULL;
+       int error;
+
+       AA_BUG(!label);
+       AA_BUG(!old_path);
+       AA_BUG(!new_path);
+
+       get_buffers(old_buffer, new_buffer);
+       error = aa_path_name(old_path, path_flags(labels_profile(label),
+                                                 old_path),
+                            old_buffer, &old_name, &info,
+                            labels_profile(label)->disconnected);
+       if (error)
+               goto error;
+       error = aa_path_name(new_path, path_flags(labels_profile(label),
+                                                 new_path),
+                            new_buffer, &new_name, &info,
+                            labels_profile(label)->disconnected);
+       if (error)
+               goto error;
+       error = fn_for_each(label, profile,
+                       profile_pivotroot(profile, new_name, old_name,
+                                         &target));
+out:
+       put_buffers(old_buffer, new_buffer);
+
+       if (target)
+               error = aa_replace_current_label(target);
+
+       return error;
+
+error:
+       error = fn_for_each(label, profile,
+                       audit_mount(profile, OP_PIVOTROOT, new_name, old_name,
+                                   NULL, NULL,
+                                   0, NULL, AA_MAY_PIVOTROOT, &nullperms, info,
+                                   error));
+       goto out;
+}
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
new file mode 100644 (file)
index 0000000..f9633ad
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor network mediation
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2014 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/af_unix.h"
+#include "include/apparmor.h"
+#include "include/audit.h"
+#include "include/context.h"
+#include "include/label.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),
+       AA_FS_FILE_BOOLEAN("af_unix",   1),
+       { }
+};
+
+static const char *net_mask_names[] = {
+       "unknown",
+       "send",
+       "receive",
+       "unknown",
+
+       "create",
+       "shutdown",
+       "connect",
+       "unknown",
+
+       "setattr",
+       "getattr",
+       "setcred",
+       "getcred",
+
+       "chmod",
+       "chown",
+       "chgrp",
+       "lock",
+
+       "mmap",
+       "mprot",
+       "unknown",
+       "unknown",
+
+       "accept",
+       "bind",
+       "listen",
+       "unknown",
+
+       "setopt",
+       "getopt",
+       "unknown",
+       "unknown",
+
+       "unknown",
+       "unknown",
+       "unknown",
+       "unknown",
+};
+
+static void audit_unix_addr(struct audit_buffer *ab, const char *str,
+                           struct sockaddr_un *addr, int addrlen)
+{
+       int len = unix_addr_len(addrlen);
+
+       if (!addr || len <= 0) {
+               audit_log_format(ab, " %s=none", str);
+       } else if (addr->sun_path[0]) {
+               audit_log_format(ab, " %s=", str);
+               audit_log_untrustedstring(ab, addr->sun_path);
+       } else {
+               audit_log_format(ab, " %s=\"@", str);
+               if (audit_string_contains_control(&addr->sun_path[1], len - 1))
+                       audit_log_n_hex(ab, &addr->sun_path[1], len - 1);
+               else
+                       audit_log_format(ab, "%.*s", len - 1,
+                                        &addr->sun_path[1]);
+               audit_log_format(ab, "\"");
+       }
+}
+
+static void audit_unix_sk_addr(struct audit_buffer *ab, const char *str,
+                              struct sock *sk)
+{
+       struct unix_sock *u = unix_sk(sk);
+       if (u && u->addr)
+               audit_unix_addr(ab, str, u->addr->name, u->addr->len);
+       else
+               audit_unix_addr(ab, str, NULL, 0);
+}
+
+/* audit callback for net specific fields */
+void audit_net_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[aad(sa)->net.type]) {
+               audit_log_string(ab, sock_type_names[aad(sa)->net.type]);
+       } else {
+               audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type);
+       }
+       audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol);
+
+       if (aad(sa)->request & NET_PERMS_MASK) {
+               audit_log_format(ab, " requested_mask=");
+               aa_audit_perm_mask(ab, aad(sa)->request, NULL, 0,
+                                  net_mask_names, NET_PERMS_MASK);
+
+               if (aad(sa)->denied & NET_PERMS_MASK) {
+                       audit_log_format(ab, " denied_mask=");
+                       aa_audit_perm_mask(ab, aad(sa)->denied, NULL, 0,
+                                          net_mask_names, NET_PERMS_MASK);
+               }
+       }
+       if (sa->u.net->family == AF_UNIX) {
+               if ((aad(sa)->request & ~NET_PEER_MASK) && aad(sa)->net.addr)
+                       audit_unix_addr(ab, "addr",
+                                       unix_addr(aad(sa)->net.addr),
+                                       aad(sa)->net.addrlen);
+               else
+                       audit_unix_sk_addr(ab, "addr", sa->u.net->sk);
+               if (aad(sa)->request & NET_PEER_MASK) {
+                       if (aad(sa)->net.addr)
+                               audit_unix_addr(ab, "peer_addr",
+                                               unix_addr(aad(sa)->net.addr),
+                                               aad(sa)->net.addrlen);
+                       else
+                               audit_unix_sk_addr(ab, "peer_addr",
+                                                  aad(sa)->net.peer_sk);
+               }
+       }
+       if (aad(sa)->peer) {
+               audit_log_format(ab, " peer=");
+               aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
+                               FLAGS_NONE, GFP_ATOMIC);
+       }
+}
+
+
+/* Generic af perm */
+int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
+                      u32 request, u16 family, int type)
+{
+       struct aa_perms perms = { };
+
+       AA_BUG(family >= AF_MAX);
+       AA_BUG(type < 0 && type >= SOCK_MAX);
+
+       if (profile_unconfined(profile))
+               return 0;
+
+       perms.allow = (profile->net.allow[family] & (1 << type)) ?
+               ALL_PERMS_MASK : 0;
+       perms.audit = (profile->net.audit[family] & (1 << type)) ?
+               ALL_PERMS_MASK : 0;
+       perms.quiet = (profile->net.quiet[family] & (1 << type)) ?
+               ALL_PERMS_MASK : 0;
+       aa_apply_modes_to_perms(profile, &perms);
+
+       return aa_check_perms(profile, &perms, request, sa, audit_net_cb);
+}
+
+static int aa_af_perm(struct aa_label *label, const char *op, u32 request,
+                     u16 family, int type, int protocol)
+{
+       struct aa_profile *profile;
+       DEFINE_AUDIT_NET(sa, op, NULL, family, type, protocol);
+
+       return fn_for_each_confined(label, profile,
+                       aa_profile_af_perm(profile, &sa, request, family, type));
+}
+
+static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request,
+                           struct sock *sk)
+{
+       struct aa_profile *profile;
+       DEFINE_AUDIT_SK(sa, op, sk);
+
+       AA_BUG(!label);
+       AA_BUG(!sk);
+
+       if (unconfined(label))
+               return 0;
+
+       return fn_for_each_confined(label, profile,
+                       aa_profile_af_sk_perm(profile, &sa, request, sk));
+}
+
+static int aa_sk_perm(const char *op, u32 request, struct sock *sk)
+{
+       struct aa_label *label;
+       int error;
+
+       AA_BUG(!sk);
+       AA_BUG(in_interrupt());
+
+       /* TODO: switch to begin_current_label ???? */
+       label = aa_begin_current_label(DO_UPDATE);
+       error = aa_label_sk_perm(label, op, request, sk);
+       aa_end_current_label(label);
+
+       return error;
+}
+
+#define af_select(FAMILY, FN, DEF_FN)          \
+({                                             \
+       int __e;                                \
+       switch ((FAMILY)) {                     \
+       case AF_UNIX:                           \
+               __e = aa_unix_ ## FN;           \
+               break;                          \
+       default:                                \
+               __e = DEF_FN;                   \
+       }                                       \
+       __e;                                    \
+})
+
+/* TODO: push into lsm.c ???? */
+
+/* revaliation, get/set attr, shutdown */
+int aa_sock_perm(const char *op, u32 request, struct socket *sock)
+{
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       AA_BUG(in_interrupt());
+
+       return af_select(sock->sk->sk_family,
+                        sock_perm(op, request, sock),
+                        aa_sk_perm(op, request, sock->sk));
+}
+
+int aa_sock_create_perm(struct aa_label *label, int family, int type,
+                       int protocol)
+{
+       AA_BUG(!label);
+       /* TODO: .... */
+       AA_BUG(in_interrupt());
+
+       return af_select(family,
+                        create_perm(label, family, type, protocol),
+                        aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family,
+                                   type, protocol));
+}
+
+int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address,
+                     int addrlen)
+{
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       AA_BUG(!address);
+       /* TODO: .... */
+       AA_BUG(in_interrupt());
+
+       return af_select(sock->sk->sk_family,
+                        bind_perm(sock, address, addrlen),
+                        aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk));
+}
+
+int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address,
+                        int addrlen)
+{
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       AA_BUG(!address);
+       /* TODO: .... */
+       AA_BUG(in_interrupt());
+
+       return af_select(sock->sk->sk_family,
+                        connect_perm(sock, address, addrlen),
+                        aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk));
+}
+
+int aa_sock_listen_perm(struct socket *sock, int backlog)
+{
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       /* TODO: .... */
+       AA_BUG(in_interrupt());
+
+       return af_select(sock->sk->sk_family,
+                        listen_perm(sock, backlog),
+                        aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk));
+}
+
+/* ability of sock to connect, not peer address binding */
+int aa_sock_accept_perm(struct socket *sock, struct socket *newsock)
+{
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       AA_BUG(!newsock);
+       /* TODO: .... */
+       AA_BUG(in_interrupt());
+
+       return af_select(sock->sk->sk_family,
+                        accept_perm(sock, newsock),
+                        aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk));
+}
+
+/* sendmsg, recvmsg */
+int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock,
+                    struct msghdr *msg, int size)
+{
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       AA_BUG(!msg);
+       /* TODO: .... */
+       AA_BUG(in_interrupt());
+
+       return af_select(sock->sk->sk_family,
+                        msg_perm(op, request, sock, msg, size),
+                        aa_sk_perm(op, request, sock->sk));
+}
+
+/* revaliation, get/set attr, opt */
+int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level,
+                    int optname)
+{
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       AA_BUG(in_interrupt());
+
+       return af_select(sock->sk->sk_family,
+                        opt_perm(op, request, sock, level, optname),
+                        aa_sk_perm(op, request, sock->sk));
+}
+
+int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
+                     struct socket *sock)
+{
+       AA_BUG(!label);
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+
+       return af_select(sock->sk->sk_family,
+                        file_perm(label, op, request, sock),
+                        aa_label_sk_perm(label, op, request, sock->sk));
+}
diff --git a/security/apparmor/nulldfa.in b/security/apparmor/nulldfa.in
new file mode 100644 (file)
index 0000000..3cb3802
--- /dev/null
@@ -0,0 +1 @@
+0x1B, 0x5E, 0x78, 0x3D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x04, 0x90, 0x00, 0x00, 0x6E, 0x6F, 0x74, 0x66, 0x6C, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
index 71e0e3a15b9dc3bbae6b73cd1d8134768f67d2c5..6cea2a6a15140758f65b79a0c38843095900b2fb 100644 (file)
@@ -25,7 +25,6 @@
 #include "include/path.h"
 #include "include/policy.h"
 
-
 /* modified from dcache.c */
 static int prepend(char **buffer, int buflen, const char *str, int namelen)
 {
@@ -39,13 +38,50 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen)
 
 #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT)
 
+/* If the path is not connected to the expected root,
+ * check if it is a sysctl and handle specially else remove any
+ * leading / that __d_path may have returned.
+ * Unless
+ *     specifically directed to connect the path,
+ * OR
+ *     if in a chroot and doing chroot relative paths and the path
+ *     resolves to the namespace root (would be connected outside
+ *     of chroot) and specifically directed to connect paths to
+ *     namespace root.
+ */
+static int disconnect(struct path *path, char *buf, char **name, int flags,
+                     const char *disconnected)
+{
+       int error = 0;
+
+       if (!(flags & PATH_CONNECT_PATH) &&
+           !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
+             our_mnt(path->mnt))) {
+               /* disconnected path, don't return pathname starting
+                * with '/'
+                */
+               error = -EACCES;
+               if (**name == '/')
+                       *name = *name + 1;
+       } else {
+               if (**name != '/')
+                       /* CONNECT_PATH with missing root */
+                       error = prepend(name, *name - buf, "/", 1);
+               if (!error && disconnected)
+                       error = prepend(name, *name - buf, disconnected,
+                                       strlen(disconnected));
+       }
+
+       return error;
+}
+
 /**
  * d_namespace_path - lookup a name associated with a given path
  * @path: path to lookup  (NOT NULL)
  * @buf:  buffer to store path to  (NOT NULL)
- * @buflen: length of @buf
  * @name: Returns - pointer for start of path name with in @buf (NOT NULL)
  * @flags: flags controlling path lookup
+ * @disconnected: string to prefix to disconnected paths
  *
  * Handle path name lookup.
  *
@@ -53,12 +89,14 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen)
  *          When no error the path name is returned in @name which points to
  *          to a position in @buf
  */
-static int d_namespace_path(struct path *path, char *buf, int buflen,
-                           char **name, int flags)
+static int d_namespace_path(struct path *path, char *buf, char **name,
+                           int flags, const char *disconnected)
 {
        char *res;
        int error = 0;
        int connected = 1;
+       int isdir = (flags & PATH_IS_DIR) ? 1 : 0;
+       int buflen = aa_g_path_max - isdir;
 
        if (path->mnt->mnt_flags & MNT_INTERNAL) {
                /* it's not mounted anywhere */
@@ -73,9 +111,12 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
                        /* TODO: convert over to using a per namespace
                         * control instead of hard coded /proc
                         */
-                       return prepend(name, *name - buf, "/proc", 5);
-               }
-               return 0;
+                       error = prepend(name, *name - buf, "/proc", 5);
+                       goto out;
+               } else
+                       error = disconnect(path, buf, name, flags,
+                                          disconnected);
+               goto out;
        }
 
        /* resolve paths relative to chroot?*/
@@ -94,8 +135,11 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
         * be returned.
         */
        if (!res || IS_ERR(res)) {
-               if (PTR_ERR(res) == -ENAMETOOLONG)
-                       return -ENAMETOOLONG;
+               if (PTR_ERR(res) == -ENAMETOOLONG) {
+                       error = -ENAMETOOLONG;
+                       *name = buf;
+                       goto out;
+               }
                connected = 0;
                res = dentry_path_raw(path->dentry, buf, buflen);
                if (IS_ERR(res)) {
@@ -120,78 +164,28 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
                        goto out;
        }
 
-       /* If the path is not connected to the expected root,
-        * check if it is a sysctl and handle specially else remove any
-        * leading / that __d_path may have returned.
-        * Unless
-        *     specifically directed to connect the path,
-        * OR
-        *     if in a chroot and doing chroot relative paths and the path
-        *     resolves to the namespace root (would be connected outside
-        *     of chroot) and specifically directed to connect paths to
-        *     namespace root.
-        */
-       if (!connected) {
-               if (!(flags & PATH_CONNECT_PATH) &&
-                          !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
-                            our_mnt(path->mnt))) {
-                       /* disconnected path, don't return pathname starting
-                        * with '/'
-                        */
-                       error = -EACCES;
-                       if (*res == '/')
-                               *name = res + 1;
-               }
-       }
+       if (!connected)
+               error = disconnect(path, buf, name, flags, disconnected);
 
 out:
-       return error;
-}
-
-/**
- * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended
- * @path: path to get name for  (NOT NULL)
- * @flags: flags controlling path lookup
- * @buffer: buffer to put name in  (NOT NULL)
- * @size: size of buffer
- * @name: Returns - contains position of path name in @buffer (NOT NULL)
- *
- * Returns: %0 else error on failure
- */
-static int get_name_to_buffer(struct path *path, int flags, char *buffer,
-                             int size, char **name, const char **info)
-{
-       int adjust = (flags & PATH_IS_DIR) ? 1 : 0;
-       int error = d_namespace_path(path, buffer, size - adjust, name, flags);
-
-       if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0')
-               /*
-                * Append "/" to the pathname.  The root directory is a special
-                * case; it already ends in slash.
-                */
-               strcpy(&buffer[size - 2], "/");
-
-       if (info && error) {
-               if (error == -ENOENT)
-                       *info = "Failed name lookup - deleted entry";
-               else if (error == -EACCES)
-                       *info = "Failed name lookup - disconnected path";
-               else if (error == -ENAMETOOLONG)
-                       *info = "Failed name lookup - name too long";
-               else
-                       *info = "Failed name lookup";
-       }
+       /*
+        * Append "/" to the pathname.  The root directory is a special
+        * case; it already ends in slash.
+        */
+       if (!error && isdir && ((*name)[1] != '\0' || (*name)[0] != '/'))
+               strcpy(&buf[aa_g_path_max - 2], "/");
 
        return error;
 }
 
 /**
- * aa_path_name - compute the pathname of a file
+ * aa_path_name - get the pathname to a buffer ensure dir / is appended
  * @path: path the file  (NOT NULL)
  * @flags: flags controlling path name generation
- * @buffer: buffer that aa_get_name() allocated  (NOT NULL)
+ * @buffer: buffer to put name in (NOT NULL)
  * @name: Returns - the generated path name if !error (NOT NULL)
  * @info: Returns - information on why the path lookup failed (MAYBE NULL)
+ * @disconnected: string to prepend to disconnected paths
  *
  * @name is a pointer to the beginning of the pathname (which usually differs
  * from the beginning of the buffer), or NULL.  If there is an error @name
@@ -204,33 +198,24 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer,
  *
  * Returns: %0 else error code if could retrieve name
  */
-int aa_path_name(struct path *path, int flags, char **buffer, const char **name,
-                const char **info)
+int aa_path_name(struct path *path, int flags, char *buffer, const char **name,
+                const char **info, const char *disconnected)
 {
-       char *buf, *str = NULL;
-       int size = 256;
-       int error;
-
-       *name = NULL;
-       *buffer = NULL;
-       for (;;) {
-               /* freed by caller */
-               buf = kmalloc(size, GFP_KERNEL);
-               if (!buf)
-                       return -ENOMEM;
-
-               error = get_name_to_buffer(path, flags, buf, size, &str, info);
-               if (error != -ENAMETOOLONG)
-                       break;
-
-               kfree(buf);
-               size <<= 1;
-               if (size > aa_g_path_max)
-                       return -ENAMETOOLONG;
-               *info = NULL;
+       char *str = NULL;
+       int error = d_namespace_path(path, buffer, &str, flags, disconnected);
+
+
+       if (info && error) {
+               if (error == -ENOENT)
+                       *info = "Failed name lookup - deleted entry";
+               else if (error == -EACCES)
+                       *info = "Failed name lookup - disconnected path";
+               else if (error == -ENAMETOOLONG)
+                       *info = "Failed name lookup - name too long";
+               else
+                       *info = "Failed name lookup";
        }
-       *buffer = buf;
-       *name = str;
 
+       *name = str;
        return error;
 }
index 705c2879d3a94a79e10d1190468483c2f9d7192a..b8f60ece8cbcaccc457d16ebe5d4f68b96dfe815 100644 (file)
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/string.h>
+#include <linux/user_namespace.h>
 
 #include "include/apparmor.h"
 #include "include/capability.h"
 #include "include/context.h"
 #include "include/file.h"
 #include "include/ipc.h"
+#include "include/label.h"
 #include "include/match.h"
 #include "include/path.h"
 #include "include/policy.h"
@@ -89,9 +91,9 @@
 #include "include/resource.h"
 
 
-/* root profile namespace */
-struct aa_namespace *root_ns;
-
+/* Note: mode names must be unique in the first character because of
+ *       modechrs used to print modes on compound labels on some interfaces
+ */
 const char *const aa_profile_mode_names[] = {
        "enforce",
        "complain",
@@ -99,322 +101,9 @@ const char *const aa_profile_mode_names[] = {
        "unconfined",
 };
 
-/**
- * hname_tail - find the last component of an hname
- * @name: hname to find the base profile name component of  (NOT NULL)
- *
- * Returns: the tail (base profile name) name component of an hname
- */
-static const char *hname_tail(const char *hname)
-{
-       char *split;
-       hname = strim((char *)hname);
-       for (split = strstr(hname, "//"); split; split = strstr(hname, "//"))
-               hname = split + 2;
-
-       return hname;
-}
-
-/**
- * policy_init - initialize a policy structure
- * @policy: policy to initialize  (NOT NULL)
- * @prefix: prefix name if any is required.  (MAYBE NULL)
- * @name: name of the policy, init will make a copy of it  (NOT NULL)
- *
- * Note: this fn creates a copy of strings passed in
- *
- * Returns: true if policy init successful
- */
-static bool policy_init(struct aa_policy *policy, const char *prefix,
-                       const char *name)
-{
-       /* freed by policy_free */
-       if (prefix) {
-               policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3,
-                                       GFP_KERNEL);
-               if (policy->hname)
-                       sprintf(policy->hname, "%s//%s", prefix, name);
-       } else
-               policy->hname = kstrdup(name, GFP_KERNEL);
-       if (!policy->hname)
-               return 0;
-       /* base.name is a substring of fqname */
-       policy->name = (char *)hname_tail(policy->hname);
-       INIT_LIST_HEAD(&policy->list);
-       INIT_LIST_HEAD(&policy->profiles);
-
-       return 1;
-}
-
-/**
- * policy_destroy - free the elements referenced by @policy
- * @policy: policy that is to have its elements freed  (NOT NULL)
- */
-static void policy_destroy(struct aa_policy *policy)
-{
-       /* still contains profiles -- invalid */
-       if (on_list_rcu(&policy->profiles)) {
-               AA_ERROR("%s: internal error, "
-                        "policy '%s' still contains profiles\n",
-                        __func__, policy->name);
-               BUG();
-       }
-       if (on_list_rcu(&policy->list)) {
-               AA_ERROR("%s: internal error, policy '%s' still on list\n",
-                        __func__, policy->name);
-               BUG();
-       }
-
-       /* don't free name as its a subset of hname */
-       kzfree(policy->hname);
-}
 
 /**
- * __policy_find - find a policy by @name on a policy list
- * @head: list to search  (NOT NULL)
- * @name: name to search for  (NOT NULL)
- *
- * Requires: rcu_read_lock be held
- *
- * Returns: unrefcounted policy that match @name or NULL if not found
- */
-static struct aa_policy *__policy_find(struct list_head *head, const char *name)
-{
-       struct aa_policy *policy;
-
-       list_for_each_entry_rcu(policy, head, list) {
-               if (!strcmp(policy->name, name))
-                       return policy;
-       }
-       return NULL;
-}
-
-/**
- * __policy_strn_find - find a policy that's name matches @len chars of @str
- * @head: list to search  (NOT NULL)
- * @str: string to search for  (NOT NULL)
- * @len: length of match required
- *
- * Requires: rcu_read_lock be held
- *
- * Returns: unrefcounted policy that match @str or NULL if not found
- *
- * if @len == strlen(@strlen) then this is equiv to __policy_find
- * other wise it allows searching for policy by a partial match of name
- */
-static struct aa_policy *__policy_strn_find(struct list_head *head,
-                                           const char *str, int len)
-{
-       struct aa_policy *policy;
-
-       list_for_each_entry_rcu(policy, head, list) {
-               if (aa_strneq(policy->name, str, len))
-                       return policy;
-       }
-
-       return NULL;
-}
-
-/*
- * Routines for AppArmor namespaces
- */
-
-static const char *hidden_ns_name = "---";
-/**
- * aa_ns_visible - test if @view is visible from @curr
- * @curr: namespace to treat as the parent (NOT NULL)
- * @view:  namespace to test if visible from @curr (NOT NULL)
- *
- * Returns: true if @view is visible from @curr else false
- */
-bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view)
-{
-       if (curr == view)
-               return true;
-
-       for ( ; view; view = view->parent) {
-               if (view->parent == curr)
-                       return true;
-       }
-       return false;
-}
-
-/**
- * aa_na_name - Find the ns name to display for @view from @curr
- * @curr - current namespace (NOT NULL)
- * @view - namespace attempting to view (NOT NULL)
- *
- * Returns: name of @view visible from @curr
- */
-const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view)
-{
-       /* if view == curr then the namespace name isn't displayed */
-       if (curr == view)
-               return "";
-
-       if (aa_ns_visible(curr, view)) {
-               /* at this point if a ns is visible it is in a view ns
-                * thus the curr ns.hname is a prefix of its name.
-                * Only output the virtualized portion of the name
-                * Add + 2 to skip over // separating curr hname prefix
-                * from the visible tail of the views hname
-                */
-               return view->base.hname + strlen(curr->base.hname) + 2;
-       } else
-               return hidden_ns_name;
-}
-
-/**
- * alloc_namespace - allocate, initialize and return a new namespace
- * @prefix: parent namespace name (MAYBE NULL)
- * @name: a preallocated name  (NOT NULL)
- *
- * Returns: refcounted namespace or NULL on failure.
- */
-static struct aa_namespace *alloc_namespace(const char *prefix,
-                                           const char *name)
-{
-       struct aa_namespace *ns;
-
-       ns = kzalloc(sizeof(*ns), GFP_KERNEL);
-       AA_DEBUG("%s(%p)\n", __func__, ns);
-       if (!ns)
-               return NULL;
-       if (!policy_init(&ns->base, prefix, name))
-               goto fail_ns;
-
-       INIT_LIST_HEAD(&ns->sub_ns);
-       mutex_init(&ns->lock);
-
-       /* released by free_namespace */
-       ns->unconfined = aa_alloc_profile("unconfined");
-       if (!ns->unconfined)
-               goto fail_unconfined;
-
-       ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR |
-               PFLAG_IMMUTABLE | PFLAG_NS_COUNT;
-       ns->unconfined->mode = APPARMOR_UNCONFINED;
-
-       /* ns and ns->unconfined share ns->unconfined refcount */
-       ns->unconfined->ns = ns;
-
-       atomic_set(&ns->uniq_null, 0);
-
-       return ns;
-
-fail_unconfined:
-       kzfree(ns->base.hname);
-fail_ns:
-       kzfree(ns);
-       return NULL;
-}
-
-/**
- * free_namespace - free a profile namespace
- * @ns: the namespace to free  (MAYBE NULL)
- *
- * Requires: All references to the namespace must have been put, if the
- *           namespace was referenced by a profile confining a task,
- */
-static void free_namespace(struct aa_namespace *ns)
-{
-       if (!ns)
-               return;
-
-       policy_destroy(&ns->base);
-       aa_put_namespace(ns->parent);
-
-       ns->unconfined->ns = NULL;
-       aa_free_profile(ns->unconfined);
-       kzfree(ns);
-}
-
-/**
- * __aa_find_namespace - find a namespace on a list by @name
- * @head: list to search for namespace on  (NOT NULL)
- * @name: name of namespace to look for  (NOT NULL)
- *
- * Returns: unrefcounted namespace
- *
- * Requires: rcu_read_lock be held
- */
-static struct aa_namespace *__aa_find_namespace(struct list_head *head,
-                                               const char *name)
-{
-       return (struct aa_namespace *)__policy_find(head, name);
-}
-
-/**
- * aa_find_namespace  -  look up a profile namespace on the namespace list
- * @root: namespace to search in  (NOT NULL)
- * @name: name of namespace to find  (NOT NULL)
- *
- * Returns: a refcounted namespace on the list, or NULL if no namespace
- *          called @name exists.
- *
- * refcount released by caller
- */
-struct aa_namespace *aa_find_namespace(struct aa_namespace *root,
-                                      const char *name)
-{
-       struct aa_namespace *ns = NULL;
-
-       rcu_read_lock();
-       ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name));
-       rcu_read_unlock();
-
-       return ns;
-}
-
-/**
- * aa_prepare_namespace - find an existing or create a new namespace of @name
- * @name: the namespace to find or add  (MAYBE NULL)
- *
- * Returns: refcounted namespace or NULL if failed to create one
- */
-static struct aa_namespace *aa_prepare_namespace(const char *name)
-{
-       struct aa_namespace *ns, *root;
-
-       root = aa_current_profile()->ns;
-
-       mutex_lock(&root->lock);
-
-       /* if name isn't specified the profile is loaded to the current ns */
-       if (!name) {
-               /* released by caller */
-               ns = aa_get_namespace(root);
-               goto out;
-       }
-
-       /* try and find the specified ns and if it doesn't exist create it */
-       /* released by caller */
-       ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name));
-       if (!ns) {
-               ns = alloc_namespace(root->base.hname, name);
-               if (!ns)
-                       goto out;
-               if (__aa_fs_namespace_mkdir(ns, ns_subns_dir(root), name)) {
-                       AA_ERROR("Failed to create interface for ns %s\n",
-                                ns->base.name);
-                       free_namespace(ns);
-                       ns = NULL;
-                       goto out;
-               }
-               ns->parent = aa_get_namespace(root);
-               list_add_rcu(&ns->base.list, &root->sub_ns);
-               /* add list ref */
-               aa_get_namespace(ns);
-       }
-out:
-       mutex_unlock(&root->lock);
-
-       /* return ref */
-       return ns;
-}
-
-/**
- * __list_add_profile - add a profile to a list
+ * __add_profile - add a profiles to list and label tree
  * @list: list to add it to  (NOT NULL)
  * @profile: the profile to add  (NOT NULL)
  *
@@ -422,12 +111,21 @@ out:
  *
  * Requires: namespace lock be held, or list not be shared
  */
-static void __list_add_profile(struct list_head *list,
-                              struct aa_profile *profile)
+static void __add_profile(struct list_head *list, struct aa_profile *profile)
 {
+       struct aa_label *l;
+
+       AA_BUG(!list);
+       AA_BUG(!profile);
+       AA_BUG(!profile->ns);
+       AA_BUG(!mutex_is_locked(&profile->ns->lock));
+
        list_add_rcu(&profile->base.list, list);
        /* get list reference */
        aa_get_profile(profile);
+       l = aa_label_insert(&profile->ns->labels, &profile->label);
+       AA_BUG(l != &profile->label);
+       aa_put_label(l);
 }
 
 /**
@@ -444,11 +142,15 @@ static void __list_add_profile(struct list_head *list,
  */
 static void __list_remove_profile(struct aa_profile *profile)
 {
+       AA_BUG(!profile);
+       AA_BUG(!profile->ns);
+       AA_BUG(!mutex_is_locked(&profile->ns->lock));
+
        list_del_rcu(&profile->base.list);
        aa_put_profile(profile);
 }
 
-static void __profile_list_release(struct list_head *head);
+void __aa_profile_list_release(struct list_head *head);
 
 /**
  * __remove_profile - remove old profile, and children
@@ -458,124 +160,31 @@ static void __profile_list_release(struct list_head *head);
  */
 static void __remove_profile(struct aa_profile *profile)
 {
+       AA_BUG(!profile);
+       AA_BUG(!profile->ns);
+       AA_BUG(!mutex_is_locked(&profile->ns->lock));
+
        /* release any children lists first */
-       __profile_list_release(&profile->base.profiles);
+       __aa_profile_list_release(&profile->base.profiles);
        /* released by free_profile */
-       __aa_update_replacedby(profile, profile->ns->unconfined);
+       aa_label_remove(&profile->label);
        __aa_fs_profile_rmdir(profile);
        __list_remove_profile(profile);
 }
 
 /**
- * __profile_list_release - remove all profiles on the list and put refs
+ * __aa_profile_list_release - remove all profiles on the list and put refs
  * @head: list of profiles  (NOT NULL)
  *
  * Requires: namespace lock be held
  */
-static void __profile_list_release(struct list_head *head)
+void __aa_profile_list_release(struct list_head *head)
 {
        struct aa_profile *profile, *tmp;
        list_for_each_entry_safe(profile, tmp, head, base.list)
                __remove_profile(profile);
 }
 
-static void __ns_list_release(struct list_head *head);
-
-/**
- * destroy_namespace - remove everything contained by @ns
- * @ns: namespace to have it contents removed  (NOT NULL)
- */
-static void destroy_namespace(struct aa_namespace *ns)
-{
-       if (!ns)
-               return;
-
-       mutex_lock(&ns->lock);
-       /* release all profiles in this namespace */
-       __profile_list_release(&ns->base.profiles);
-
-       /* release all sub namespaces */
-       __ns_list_release(&ns->sub_ns);
-
-       if (ns->parent)
-               __aa_update_replacedby(ns->unconfined, ns->parent->unconfined);
-       __aa_fs_namespace_rmdir(ns);
-       mutex_unlock(&ns->lock);
-}
-
-/**
- * __remove_namespace - remove a namespace and all its children
- * @ns: namespace to be removed  (NOT NULL)
- *
- * Requires: ns->parent->lock be held and ns removed from parent.
- */
-static void __remove_namespace(struct aa_namespace *ns)
-{
-       /* remove ns from namespace list */
-       list_del_rcu(&ns->base.list);
-       destroy_namespace(ns);
-       aa_put_namespace(ns);
-}
-
-/**
- * __ns_list_release - remove all profile namespaces on the list put refs
- * @head: list of profile namespaces  (NOT NULL)
- *
- * Requires: namespace lock be held
- */
-static void __ns_list_release(struct list_head *head)
-{
-       struct aa_namespace *ns, *tmp;
-       list_for_each_entry_safe(ns, tmp, head, base.list)
-               __remove_namespace(ns);
-
-}
-
-/**
- * aa_alloc_root_ns - allocate the root profile namespace
- *
- * Returns: %0 on success else error
- *
- */
-int __init aa_alloc_root_ns(void)
-{
-       /* released by aa_free_root_ns - used as list ref*/
-       root_ns = alloc_namespace(NULL, "root");
-       if (!root_ns)
-               return -ENOMEM;
-
-       return 0;
-}
-
- /**
-  * aa_free_root_ns - free the root profile namespace
-  */
-void __init aa_free_root_ns(void)
- {
-        struct aa_namespace *ns = root_ns;
-        root_ns = NULL;
-
-        destroy_namespace(ns);
-        aa_put_namespace(ns);
-}
-
-
-static void free_replacedby(struct aa_replacedby *r)
-{
-       if (r) {
-               /* r->profile will not be updated any more as r is dead */
-               aa_put_profile(rcu_dereference_protected(r->profile, true));
-               kzfree(r);
-       }
-}
-
-
-void aa_free_replacedby_kref(struct kref *kref)
-{
-       struct aa_replacedby *r = container_of(kref, struct aa_replacedby,
-                                              count);
-       free_replacedby(r);
-}
 
 /**
  * aa_free_profile - free a profile
@@ -595,89 +204,81 @@ void aa_free_profile(struct aa_profile *profile)
                return;
 
        /* free children profiles */
-       policy_destroy(&profile->base);
+       aa_policy_destroy(&profile->base);
        aa_put_profile(rcu_access_pointer(profile->parent));
 
-       aa_put_namespace(profile->ns);
+       aa_put_ns(profile->ns);
        kzfree(profile->rename);
 
        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);
        aa_put_dfa(profile->xmatch);
        aa_put_dfa(profile->policy.dfa);
-       aa_put_replacedby(profile->replacedby);
 
        kzfree(profile->hash);
        kzfree(profile);
 }
 
-/**
- * aa_free_profile_rcu - free aa_profile by rcu (called by aa_free_profile_kref)
- * @head: rcu_head callback for freeing of a profile  (NOT NULL)
- */
-static void aa_free_profile_rcu(struct rcu_head *head)
-{
-       struct aa_profile *p = container_of(head, struct aa_profile, rcu);
-       if (p->flags & PFLAG_NS_COUNT)
-               free_namespace(p->ns);
-       else
-               aa_free_profile(p);
-}
-
-/**
- * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile)
- * @kr: kref callback for freeing of a profile  (NOT NULL)
- */
-void aa_free_profile_kref(struct kref *kref)
-{
-       struct aa_profile *p = container_of(kref, struct aa_profile, count);
-       call_rcu(&p->rcu, aa_free_profile_rcu);
-}
-
 /**
  * aa_alloc_profile - allocate, initialize and return a new profile
  * @hname: name of the profile  (NOT NULL)
+ * @gfp: allocation type
  *
  * Returns: refcount profile or NULL on failure
  */
-struct aa_profile *aa_alloc_profile(const char *hname)
+struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
+                                   gfp_t gfp)
 {
        struct aa_profile *profile;
 
        /* freed by free_profile - usually through aa_put_profile */
-       profile = kzalloc(sizeof(*profile), GFP_KERNEL);
+       profile = kzalloc(sizeof(*profile) + sizeof (struct aa_profile *) * 2,
+                         gfp);
        if (!profile)
                return NULL;
 
-       profile->replacedby = kzalloc(sizeof(struct aa_replacedby), GFP_KERNEL);
-       if (!profile->replacedby)
+       if (!aa_policy_init(&profile->base, NULL, hname, gfp))
                goto fail;
-       kref_init(&profile->replacedby->count);
-
-       if (!policy_init(&profile->base, NULL, hname))
+       if (!aa_label_init(&profile->label, 1))
                goto fail;
-       kref_init(&profile->count);
+
+       /* update being set needed by fs interface */
+       if (!proxy) {
+               proxy = aa_alloc_proxy(&profile->label, gfp);
+               if (!proxy)
+                       goto fail;
+       } else
+               aa_get_proxy(proxy);
+       profile->label.proxy = proxy;
+
+       profile->label.hname = profile->base.hname;
+       profile->label.flags |= FLAG_PROFILE;
+       profile->label.vec[0] = profile;
 
        /* refcount released by caller */
        return profile;
 
 fail:
-       kzfree(profile->replacedby);
-       kzfree(profile);
+       aa_free_profile(profile);
 
        return NULL;
 }
 
 /**
- * aa_new_null_profile - create a new null-X learning profile
+ * aa_null_profile - create or find a null-X learning profile
  * @parent: profile that caused this profile to be created (NOT NULL)
  * @hat: true if the null- learning profile is a hat
+ * @base: name to base the null profile off of
+ * @gfp: type of allocation
  *
- * Create a null- complain mode profile used in learning mode.  The name of
- * the profile is unique and follows the format of parent//null-<uniq>.
+ * Find/Create a null- complain mode profile used in learning mode.  The
+ * name of the profile is unique and follows the format of parent//null-XXX.
+ * where XXX is based on the @name or if that fails or is not supplied
+ * a unique number
  *
  * null profiles are added to the profile list but the list does not
  * hold a count on them so that they are automatically released when
@@ -685,43 +286,86 @@ fail:
  *
  * Returns: new refcounted profile else NULL on failure
  */
-struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat)
+struct aa_profile *aa_null_profile(struct aa_profile *parent, bool hat,
+                                  const char *base, gfp_t gfp)
 {
-       struct aa_profile *profile = NULL;
+       struct aa_profile *profile;
        char *name;
-       int uniq = atomic_inc_return(&parent->ns->uniq_null);
 
-       /* freed below */
-       name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL);
+       AA_BUG(!parent);
+
+       if (base) {
+               name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base),
+                              gfp);
+               if (name) {
+                       sprintf(name, "%s//null-%s", parent->base.hname, base);
+                       goto name;
+               }
+               /* fall through to try shorter uniq */
+       }
+
+       name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp);
        if (!name)
-               goto fail;
-       sprintf(name, "%s//null-%x", parent->base.hname, uniq);
+               return NULL;
+       sprintf(name, "%s//null-%x", parent->base.hname,
+               atomic_inc_return(&parent->ns->uniq_null));
 
-       profile = aa_alloc_profile(name);
-       kfree(name);
+name:
+       /* lookup to see if this is a dup creation */
+       profile = aa_find_child(parent, basename(name));
+       if (profile)
+               goto out;
+
+       profile = aa_alloc_profile(name, NULL, gfp);
        if (!profile)
                goto fail;
 
        profile->mode = APPARMOR_COMPLAIN;
-       profile->flags = PFLAG_NULL;
+       profile->label.flags |= FLAG_NULL;
        if (hat)
-               profile->flags |= PFLAG_HAT;
+               profile->label.flags |= FLAG_HAT;
 
        /* released on free_profile */
        rcu_assign_pointer(profile->parent, aa_get_profile(parent));
-       profile->ns = aa_get_namespace(parent->ns);
+       profile->ns = aa_get_ns(parent->ns);
+       profile->file.dfa = aa_get_dfa(nulldfa);
+       profile->policy.dfa = aa_get_dfa(nulldfa);
 
        mutex_lock(&profile->ns->lock);
-       __list_add_profile(&parent->base.profiles, profile);
+       __add_profile(&parent->base.profiles, profile);
        mutex_unlock(&profile->ns->lock);
 
        /* refcount released by caller */
+out:
+       kfree(name);
        return profile;
 
 fail:
+       aa_free_profile(profile);
        return NULL;
 }
 
+/**
+ * aa_setup_default_label - create the initial default label
+ */
+struct aa_label *aa_setup_default_label(void)
+{
+       struct aa_profile *profile = aa_alloc_profile("default", NULL,
+                                                     GFP_KERNEL);
+       if (!profile)
+               return NULL;
+
+       /* the default profile pretends to be unconfined until it is replaced */
+       profile->label.flags |= FLAG_IX_ON_NAME_ERROR | FLAG_UNCONFINED;
+       profile->mode = APPARMOR_UNCONFINED;
+
+       profile->ns = aa_get_ns(root_ns);
+
+       __add_profile(&root_ns->base.profiles, profile);
+
+       return &profile->label;
+}
+
 /* TODO: profile accounting - setup in remove */
 
 /**
@@ -766,7 +410,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name)
        struct aa_profile *profile;
 
        rcu_read_lock();
-       profile = aa_get_profile(__find_child(&parent->base.profiles, name));
+       do {
+               profile = __find_child(&parent->base.profiles, name);
+       } while (profile && !aa_get_profile_not0(profile));
        rcu_read_unlock();
 
        /* refcount released by caller */
@@ -786,8 +432,7 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name)
  *
  * Returns: unrefcounted policy or NULL if not found
  */
-static struct aa_policy *__lookup_parent(struct aa_namespace *ns,
-                                        const char *hname)
+static struct aa_policy *__lookup_parent(struct aa_ns *ns, const char *hname)
 {
        struct aa_policy *policy;
        struct aa_profile *profile = NULL;
@@ -810,9 +455,10 @@ static struct aa_policy *__lookup_parent(struct aa_namespace *ns,
 }
 
 /**
- * __lookup_profile - lookup the profile matching @hname
+ * __lookupn_profile - lookup the profile matching @hname
  * @base: base list to start looking up profile name from  (NOT NULL)
  * @hname: hierarchical profile name  (NOT NULL)
+ * @n: length of @hname
  *
  * Requires: rcu_read_lock be held
  *
@@ -820,53 +466,95 @@ static struct aa_policy *__lookup_parent(struct aa_namespace *ns,
  *
  * Do a relative name lookup, recursing through profile tree.
  */
-static struct aa_profile *__lookup_profile(struct aa_policy *base,
-                                          const char *hname)
+static struct aa_profile *__lookupn_profile(struct aa_policy *base,
+                                           const char *hname, size_t n)
 {
        struct aa_profile *profile = NULL;
-       char *split;
+       const char *split;
 
-       for (split = strstr(hname, "//"); split;) {
+       for (split = strnstr(hname, "//", n); split;
+            split = strnstr(hname, "//", n)) {
                profile = __strn_find_child(&base->profiles, hname,
                                            split - hname);
                if (!profile)
                        return NULL;
 
                base = &profile->base;
+               n -= split + 2 - hname;
                hname = split + 2;
-               split = strstr(hname, "//");
        }
 
-       profile = __find_child(&base->profiles, hname);
+       if (n)
+               return __strn_find_child(&base->profiles, hname, n);
+       return NULL;
+}
 
-       return profile;
+static struct aa_profile *__lookup_profile(struct aa_policy *base,
+                                          const char *hname)
+{
+       return __lookupn_profile(base, hname, strlen(hname));
 }
 
 /**
  * aa_lookup_profile - find a profile by its full or partial name
  * @ns: the namespace to start from (NOT NULL)
  * @hname: name to do lookup on.  Does not contain namespace prefix (NOT NULL)
+ * @n: size of @hname
  *
  * Returns: refcounted profile or NULL if not found
  */
-struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname)
+struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname,
+                                     size_t n)
 {
        struct aa_profile *profile;
 
        rcu_read_lock();
        do {
-               profile = __lookup_profile(&ns->base, hname);
+               profile = __lookupn_profile(&ns->base, hname, n);
        } while (profile && !aa_get_profile_not0(profile));
        rcu_read_unlock();
 
        /* the unconfined profile is not in the regular profile list */
-       if (!profile && strcmp(hname, "unconfined") == 0)
+       if (!profile && strncmp(hname, "unconfined", n) == 0)
                profile = aa_get_newest_profile(ns->unconfined);
 
        /* refcount released by caller */
        return profile;
 }
 
+struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname)
+{
+       return aa_lookupn_profile(ns, hname, strlen(hname));
+}
+
+struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
+                                       const char *fqname, size_t n)
+{
+       struct aa_profile *profile;
+       struct aa_ns *ns;
+       const char *name, *ns_name;
+       size_t ns_len;
+
+       name = aa_splitn_fqname(fqname, n, &ns_name, &ns_len);
+       if (ns_name) {
+               ns = aa_findn_ns(labels_ns(base), ns_name, ns_len);
+               if (!ns)
+                       return NULL;
+       } else
+               ns = aa_get_ns(labels_ns(base));
+
+       if (name)
+               profile = aa_lookupn_profile(ns, name, n - (name - fqname));
+       else if (ns)
+               /* default profile for ns, currently unconfined */
+               profile = aa_get_newest_profile(ns->unconfined);
+       else
+               profile = NULL;
+       aa_put_ns(ns);
+
+       return profile;
+}
+
 /**
  * replacement_allowed - test to see if replacement is allowed
  * @profile: profile to test if it can be replaced  (MAYBE NULL)
@@ -879,7 +567,7 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace,
                               const char **info)
 {
        if (profile) {
-               if (profile->flags & PFLAG_IMMUTABLE) {
+               if (profile->label.flags & FLAG_IMMUTIBLE) {
                        *info = "cannot replace immutible profile";
                        return -EPERM;
                } else if (noreplace) {
@@ -890,58 +578,94 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace,
        return 0;
 }
 
+/* audit callback for net specific fields */
+static void audit_cb(struct audit_buffer *ab, void *va)
+{
+       struct common_audit_data *sa = va;
+
+       if (aad(sa)->iface.ns) {
+               audit_log_format(ab, " ns=");
+               audit_log_untrustedstring(ab, aad(sa)->iface.ns);
+       }
+}
+
 /**
- * aa_audit_policy - Do auditing of policy changes
+ * audit_policy - Do auditing of policy changes
+ * @label: label to check if it can manage policy
  * @op: policy operation being performed
- * @gfp: memory allocation flags
- * @name: name of profile being manipulated (NOT NULL)
+ * @profile: name of profile being manipulated (NOT NULL)
  * @info: any extra information to be audited (MAYBE NULL)
  * @error: error code
  *
  * Returns: the error to be returned after audit is done
  */
-static int audit_policy(int op, gfp_t gfp, const char *name, const char *info,
-                       int error)
+static int audit_policy(struct aa_label *label, const char *op,
+                       const char *ns_name, const char *name,
+                       const char *info, int error)
+{
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
+       //      aad(&sa)->op = op;
+       aad(&sa)->iface.ns = ns_name;
+       aad(&sa)->name = name;
+       aad(&sa)->info = info;
+       aad(&sa)->error = error;
+       aad(&sa)->label = label;
+       
+       aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, audit_cb);
+
+       return error;
+}
+
+bool policy_admin_capable(void)
 {
-       struct common_audit_data sa;
-       struct apparmor_audit_data aad = {0,};
-       sa.type = LSM_AUDIT_DATA_NONE;
-       sa.aad = &aad;
-       aad.op = op;
-       aad.name = name;
-       aad.info = info;
-       aad.error = error;
-
-       return aa_audit(AUDIT_APPARMOR_STATUS, __aa_current_profile(), gfp,
-                       &sa, NULL);
+       struct user_namespace *user_ns = current_user_ns();
+       struct aa_ns *ns = aa_get_current_ns();
+       bool response = false;
+
+       if (ns_capable(user_ns, CAP_MAC_ADMIN) &&
+           (user_ns == &init_user_ns ||
+            (user_ns->level == 1 && ns != root_ns)))
+               response = true;
+       aa_put_ns(ns);
+
+       return response;
 }
 
 /**
  * aa_may_manage_policy - can the current task manage policy
+ * @label: label to check if it can manage policy
  * @op: the policy manipulation operation being done
  *
- * Returns: true if the task is allowed to manipulate policy
+ * Returns: 0 if the task is allowed to manipulate policy else error
  */
-bool aa_may_manage_policy(int op)
+int aa_may_manage_policy(struct aa_label *label, u32 mask)
 {
+       const char *op;
+
+       if (mask & AA_MAY_REMOVE_POLICY)
+               op = OP_PROF_RM;
+       else if (mask & AA_MAY_REPLACE_POLICY)
+               op = OP_PROF_REPL;
+       else
+               op = OP_PROF_LOAD;
+
        /* check if loading policy is locked out */
-       if (aa_g_lock_policy) {
-               audit_policy(op, GFP_KERNEL, NULL, "policy_locked", -EACCES);
-               return 0;
-       }
+       if (aa_g_lock_policy)
+               return audit_policy(label, op, NULL, NULL, "policy_locked",
+                                   -EACCES);
 
-       if (!capable(CAP_MAC_ADMIN)) {
-               audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES);
-               return 0;
-       }
+       if (!policy_admin_capable())
+               return audit_policy(label, op, NULL, NULL, "not policy admin",
+                                   -EACCES);
 
-       return 1;
+       /* TODO: add fine grained mediation of policy loads */
+       return 0;
 }
 
 static struct aa_profile *__list_lookup_parent(struct list_head *lh,
                                               struct aa_profile *profile)
 {
-       const char *base = hname_tail(profile->base.hname);
+       const char *base = basename(profile->base.hname);
        long len = base - profile->base.hname;
        struct aa_load_ent *ent;
 
@@ -965,7 +689,6 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh,
  * __replace_profile - replace @old with @new on a list
  * @old: profile to be replaced  (NOT NULL)
  * @new: profile to replace @old with  (NOT NULL)
- * @share_replacedby: transfer @old->replacedby to @new
  *
  * Will duplicate and refcount elements that @new inherits from @old
  * and will inherit @old children.
@@ -974,8 +697,7 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh,
  *
  * Requires: namespace list lock be held, or list not be shared
  */
-static void __replace_profile(struct aa_profile *old, struct aa_profile *new,
-                             bool share_replacedby)
+static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
 {
        struct aa_profile *child, *tmp;
 
@@ -990,7 +712,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new,
                        p = __find_child(&new->base.profiles, child->base.name);
                        if (p) {
                                /* @p replaces @child  */
-                               __replace_profile(child, p, share_replacedby);
+                               __replace_profile(child, p);
                                continue;
                        }
 
@@ -1008,14 +730,8 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new,
                struct aa_profile *parent = aa_deref_parent(old);
                rcu_assign_pointer(new->parent, aa_get_profile(parent));
        }
-       __aa_update_replacedby(old, new);
-       if (share_replacedby) {
-               aa_put_replacedby(new->replacedby);
-               new->replacedby = aa_get_replacedby(old->replacedby);
-       } else if (!rcu_access_pointer(new->replacedby->profile))
-               /* aafs interface uses replacedby */
-               rcu_assign_pointer(new->replacedby->profile,
-                                  aa_get_profile(new));
+       aa_label_replace(&old->label, &new->label);
+       /* migrate dents must come after label replacement b/c update */
        __aa_fs_profile_migrate_dents(old, new);
 
        if (list_empty(&new->base.list)) {
@@ -1037,7 +753,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new,
  *
  * Returns: profile to replace (no ref) on success else ptr error
  */
-static int __lookup_replace(struct aa_namespace *ns, const char *hname,
+static int __lookup_replace(struct aa_ns *ns, const char *hname,
                            bool noreplace, struct aa_profile **p,
                            const char **info)
 {
@@ -1053,25 +769,57 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname,
        return 0;
 }
 
+static void share_name(struct aa_profile *old, struct aa_profile *new)
+{
+       aa_put_str(new->base.hname);
+       aa_get_str(old->base.hname);
+       new->base.hname = old->base.hname;
+       new->base.name = old->base.name;
+       new->label.hname = old->label.hname;
+}
+
+/* Update to newest version of parent after previous replacements
+ * Returns: unrefcount newest version of parent
+ */
+static struct aa_profile *update_to_newest_parent(struct aa_profile *new)
+{
+       struct aa_profile *parent, *newest;
+       parent = rcu_dereference_protected(new->parent,
+                                          mutex_is_locked(&new->ns->lock));
+       newest = aa_get_newest_profile(parent);
+
+       /* parent replaced in this atomic set? */
+       if (newest != parent) {
+               aa_put_profile(parent);
+               rcu_assign_pointer(new->parent, newest);
+       } else
+               aa_put_profile(newest);
+
+       return newest;
+}
+
 /**
  * aa_replace_profiles - replace profile(s) on the profile list
+ * @label: label that is attempting to load/replace policy
+ * @mask: permission mask
  * @udata: serialized data stream  (NOT NULL)
  * @size: size of the serialized data stream
- * @noreplace: true if only doing addition, no replacement allowed
  *
  * unpack and replace a profile on the profile list and uses of that profile
- * by any aa_task_cxt.  If the profile does not exist on the profile list
+ * by any aa_task_ctx.  If the profile does not exist on the profile list
  * it is added.
  *
  * Returns: size of data consumed else error code on failure.
  */
-ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
+ssize_t aa_replace_profiles(struct aa_label *label, u32 mask, void *udata,
+                           size_t size)
 {
        const char *ns_name, *name = NULL, *info = NULL;
-       struct aa_namespace *ns = NULL;
+       struct aa_ns *ns = NULL;
        struct aa_load_ent *ent, *tmp;
-       int op = OP_PROF_REPL;
-       ssize_t error;
+       const char *op = mask & AA_MAY_REPLACE_POLICY ? OP_PROF_REPL : OP_PROF_LOAD;
+       ssize_t count, error;
+
        LIST_HEAD(lh);
 
        /* released below */
@@ -1079,14 +827,39 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
        if (error)
                goto out;
 
-       /* released below */
-       ns = aa_prepare_namespace(ns_name);
-       if (!ns) {
-               info = "failed to prepare namespace";
-               error = -ENOMEM;
-               name = ns_name;
-               goto fail;
+       /* ensure that profiles are all for the same ns
+        * TODO: update locking to remove this constaint. All profiles in
+        *       the load set must succeed as a set or the load will
+        *       fail. Sort ent list and take ns locks in hierarchy order
+        */
+       count = 0;
+       list_for_each_entry(ent, &lh, list) {
+               if (ns_name) {
+                       if (ent->ns_name &&
+                           strcmp(ent->ns_name, ns_name) != 0) {
+                               info = "policy load has mixed namespaces";
+                               error = -EACCES;
+                               goto fail;
+                       }
+               } else if (ent->ns_name) {
+                       if (count) {
+                               info = "policy load has mixed namespaces";
+                               error = -EACCES;
+                               goto fail;
+                       }
+                       ns_name = ent->ns_name;
+               } else
+                       count++;
        }
+       if (ns_name) {
+               ns = aa_prepare_ns(labels_ns(label), ns_name);
+               if (!ns) {
+                       info = "failed to prepare namespace";
+                       error = -ENOMEM;
+                       goto fail;
+               }
+       } else
+               ns = aa_get_ns(labels_ns(label));
 
        mutex_lock(&ns->lock);
        /* setup parent and ns info */
@@ -1094,21 +867,22 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
                struct aa_policy *policy;
 
                name = ent->new->base.hname;
-               error = __lookup_replace(ns, ent->new->base.hname, noreplace,
+               error = __lookup_replace(ns, ent->new->base.hname,
+                                        !(mask & AA_MAY_REPLACE_POLICY),
                                         &ent->old, &info);
                if (error)
                        goto fail_lock;
 
                if (ent->new->rename) {
                        error = __lookup_replace(ns, ent->new->rename,
-                                                noreplace, &ent->rename,
-                                                &info);
+                                               !(mask & AA_MAY_REPLACE_POLICY),
+                                               &ent->rename, &info);
                        if (error)
                                goto fail_lock;
                }
 
                /* released when @new is freed */
-               ent->new->ns = aa_get_namespace(ns);
+               ent->new->ns = aa_get_ns(ns);
 
                if (ent->old || ent->rename)
                        continue;
@@ -1134,14 +908,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
 
        /* create new fs entries for introspection if needed */
        list_for_each_entry(ent, &lh, list) {
-               if (ent->old) {
-                       /* inherit old interface files */
-
-                       /* if (ent->rename)
-                               TODO: support rename */
-               /* } else if (ent->rename) {
-                       TODO: support rename */
-               } else {
+               if (!ent->old) {
                        struct dentry *parent;
                        if (rcu_access_pointer(ent->new->parent)) {
                                struct aa_profile *p;
@@ -1153,7 +920,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
                }
 
                if (error) {
-                       info = "failed to create ";
+                       info = "failed to create";
                        goto fail_lock;
                }
        }
@@ -1163,50 +930,33 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
                list_del_init(&ent->list);
                op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL;
 
-               audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error);
+               audit_policy(label, op, ns_name, ent->new->base.hname, NULL, error);
 
                if (ent->old) {
-                       __replace_profile(ent->old, ent->new, 1);
-                       if (ent->rename) {
-                               /* aafs interface uses replacedby */
-                               struct aa_replacedby *r = ent->new->replacedby;
-                               rcu_assign_pointer(r->profile,
-                                                  aa_get_profile(ent->new));
-                               __replace_profile(ent->rename, ent->new, 0);
-                       }
+                       share_name(ent->old, ent->new);
+                       __replace_profile(ent->old, ent->new);
+                       if (ent->rename)
+                               __replace_profile(ent->rename, ent->new);
                } else if (ent->rename) {
-                       /* aafs interface uses replacedby */
-                       rcu_assign_pointer(ent->new->replacedby->profile,
-                                          aa_get_profile(ent->new));
-                       __replace_profile(ent->rename, ent->new, 0);
-               } else if (ent->new->parent) {
-                       struct aa_profile *parent, *newest;
-                       parent = aa_deref_parent(ent->new);
-                       newest = aa_get_newest_profile(parent);
-
-                       /* parent replaced in this atomic set? */
-                       if (newest != parent) {
-                               aa_get_profile(newest);
-                               aa_put_profile(parent);
-                               rcu_assign_pointer(ent->new->parent, newest);
-                       } else
-                               aa_put_profile(newest);
-                       /* aafs interface uses replacedby */
-                       rcu_assign_pointer(ent->new->replacedby->profile,
-                                          aa_get_profile(ent->new));
-                       __list_add_profile(&parent->base.profiles, ent->new);
+                       /* TODO: case not actually supported yet */
+                       ;
                } else {
-                       /* aafs interface uses replacedby */
-                       rcu_assign_pointer(ent->new->replacedby->profile,
-                                          aa_get_profile(ent->new));
-                       __list_add_profile(&ns->base.profiles, ent->new);
+                       struct list_head *lh;
+                       if (rcu_access_pointer(ent->new->parent)) {
+                               struct aa_profile *parent;
+                               parent = update_to_newest_parent(ent->new);
+                               lh = &parent->base.profiles;
+                       } else
+                               lh = &ns->base.profiles;
+                       __add_profile(lh, ent->new);
                }
                aa_load_ent_free(ent);
        }
+       __aa_labelset_update_subtree(ns);
        mutex_unlock(&ns->lock);
 
 out:
-       aa_put_namespace(ns);
+       aa_put_ns(ns);
 
        if (error)
                return error;
@@ -1215,7 +965,7 @@ out:
 fail_lock:
        mutex_unlock(&ns->lock);
 fail:
-       error = audit_policy(op, GFP_KERNEL, name, info, error);
+       error = audit_policy(label, op, ns_name, name, info, error);
 
        list_for_each_entry_safe(ent, tmp, &lh, list) {
                list_del_init(&ent->list);
@@ -1227,6 +977,7 @@ fail:
 
 /**
  * aa_remove_profiles - remove profile(s) from the system
+ * @label: label attempting to remove policy
  * @fqname: name of the profile or namespace to remove  (NOT NULL)
  * @size: size of the name
  *
@@ -1237,11 +988,12 @@ fail:
  *
  * Returns: size of data consume else error code if fails
  */
-ssize_t aa_remove_profiles(char *fqname, size_t size)
+ssize_t aa_remove_profiles(struct aa_label *label, char *fqname, size_t size)
 {
-       struct aa_namespace *root, *ns = NULL;
+       struct aa_ns *root = NULL, *ns = NULL;
        struct aa_profile *profile = NULL;
        const char *name = fqname, *info = NULL;
+       char *ns_name = NULL;
        ssize_t error = 0;
 
        if (*fqname == 0) {
@@ -1250,13 +1002,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
                goto fail;
        }
 
-       root = aa_current_profile()->ns;
+       root = labels_ns(label);
 
        if (fqname[0] == ':') {
-               char *ns_name;
                name = aa_split_fqname(fqname, &ns_name);
                /* released below */
-               ns = aa_find_namespace(root, ns_name);
+               ns = aa_find_ns(root, ns_name);
                if (!ns) {
                        info = "namespace does not exist";
                        error = -ENOENT;
@@ -1264,12 +1015,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
                }
        } else
                /* released below */
-               ns = aa_get_namespace(root);
+               ns = aa_get_ns(root);
 
        if (!name) {
                /* remove namespace - can only happen if fqname[0] == ':' */
                mutex_lock(&ns->parent->lock);
-               __remove_namespace(ns);
+               __aa_remove_ns(ns);
                mutex_unlock(&ns->parent->lock);
        } else {
                /* remove profile */
@@ -1282,20 +1033,19 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
                }
                name = profile->base.hname;
                __remove_profile(profile);
+               __aa_labelset_update_subtree(ns);
                mutex_unlock(&ns->lock);
        }
 
        /* don't fail removal if audit fails */
-       (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error);
-       aa_put_namespace(ns);
+       (void) audit_policy(label, OP_PROF_RM, ns_name, name, info, error);
        aa_put_profile(profile);
        return size;
 
 fail_ns_lock:
        mutex_unlock(&ns->lock);
-       aa_put_namespace(ns);
 
 fail:
-       (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error);
+       (void) audit_policy(label, OP_PROF_RM, ns_name, name, info, error);
        return error;
 }
diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c
new file mode 100644 (file)
index 0000000..d06e664
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor policy manipulation functions
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2015 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.
+ *
+ * AppArmor policy namespaces, allow for different sets of policies
+ * to be loaded for tasks within the namespace.
+ */
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "include/apparmor.h"
+#include "include/context.h"
+#include "include/policy_ns.h"
+#include "include/label.h"
+#include "include/policy.h"
+
+/* root profile namespace */
+struct aa_ns *root_ns;
+const char *aa_hidden_ns_name = "---";
+
+/**
+ * aa_ns_visible - test if @view is visible from @curr
+ * @curr: namespace to treat as the parent (NOT NULL)
+ * @view: namespace to test if visible from @curr (NOT NULL)
+ * @subns: whether view of a subns is allowed
+ *
+ * Returns: true if @view is visible from @curr else false
+ */
+bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns)
+{
+       if (curr == view)
+               return true;
+
+       if (!subns)
+               return false;
+
+       for ( ; view; view = view->parent) {
+               if (view->parent == curr)
+                       return true;
+       }
+
+       return false;
+}
+
+/**
+ * aa_na_name - Find the ns name to display for @view from @curr
+ * @curr - current namespace (NOT NULL)
+ * @view - namespace attempting to view (NOT NULL)
+ * @subns - are subns visible
+ *
+ * Returns: name of @view visible from @curr
+ */
+const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view, bool subns)
+{
+       /* if view == curr then the namespace name isn't displayed */
+       if (curr == view)
+               return "";
+
+       if (aa_ns_visible(curr, view, subns)) {
+               /* at this point if a ns is visible it is in a view ns
+                * thus the curr ns.hname is a prefix of its name.
+                * Only output the virtualized portion of the name
+                * Add + 2 to skip over // separating curr hname prefix
+                * from the visible tail of the views hname
+                */
+               return view->base.hname + strlen(curr->base.hname) + 2;
+       } else
+               return aa_hidden_ns_name;
+}
+
+/**
+ * alloc_ns - allocate, initialize and return a new namespace
+ * @prefix: parent namespace name (MAYBE NULL)
+ * @name: a preallocated name  (NOT NULL)
+ *
+ * Returns: refcounted namespace or NULL on failure.
+ */
+static struct aa_ns *alloc_ns(const char *prefix, const char *name)
+{
+       struct aa_ns *ns;
+
+       ns = kzalloc(sizeof(*ns), GFP_KERNEL);
+       AA_DEBUG("%s(%p)\n", __func__, ns);
+       if (!ns)
+               return NULL;
+       if (!aa_policy_init(&ns->base, prefix, name, GFP_KERNEL))
+               goto fail_ns;
+
+       INIT_LIST_HEAD(&ns->sub_ns);
+       mutex_init(&ns->lock);
+
+       /* released by free_namespace */
+       ns->unconfined = aa_alloc_profile("unconfined", NULL, GFP_KERNEL);
+       if (!ns->unconfined)
+               goto fail_unconfined;
+
+       ns->unconfined->label.flags |= FLAG_IX_ON_NAME_ERROR |
+               FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED;
+       ns->unconfined->mode = APPARMOR_UNCONFINED;
+
+       /* ns and ns->unconfined share ns->unconfined refcount */
+       ns->unconfined->ns = ns;
+
+       atomic_set(&ns->uniq_null, 0);
+
+       aa_labelset_init(&ns->labels);
+
+       return ns;
+
+fail_unconfined:
+       kzfree(ns->base.hname);
+fail_ns:
+       kzfree(ns);
+       return NULL;
+}
+
+/**
+ * aa_free_ns - free a profile namespace
+ * @ns: the namespace to free  (MAYBE NULL)
+ *
+ * Requires: All references to the namespace must have been put, if the
+ *           namespace was referenced by a profile confining a task,
+ */
+void aa_free_ns(struct aa_ns *ns)
+{
+       if (!ns)
+               return;
+
+       aa_policy_destroy(&ns->base);
+       aa_labelset_destroy(&ns->labels);
+       aa_put_ns(ns->parent);
+
+       ns->unconfined->ns = NULL;
+       aa_free_profile(ns->unconfined);
+       kzfree(ns);
+}
+
+/**
+ * __aa_findn_ns - find a namespace on a list by @name
+ * @head: list to search for namespace on  (NOT NULL)
+ * @name: name of namespace to look for  (NOT NULL)
+ * @n: length of @name
+ * Returns: unrefcounted namespace
+ *
+ * Requires: rcu_read_lock be held
+ */
+static struct aa_ns *__aa_findn_ns(struct list_head *head, const char *name,
+                                  size_t n)
+{
+       return (struct aa_ns *)__policy_strn_find(head, name, n);
+}
+
+/**
+ * aa_find_ns  -  look up a profile namespace on the namespace list
+ * @root: namespace to search in  (NOT NULL)
+ * @name: name of namespace to find  (NOT NULL)
+ * @n: length of @name
+ *
+ * Returns: a refcounted namespace on the list, or NULL if no namespace
+ *          called @name exists.
+ *
+ * refcount released by caller
+ */
+struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n)
+{
+       struct aa_ns *ns = NULL;
+
+       rcu_read_lock();
+       ns = aa_get_ns(__aa_findn_ns(&root->sub_ns, name, n));
+       rcu_read_unlock();
+
+       return ns;
+}
+
+/**
+ * aa_find_ns  -  look up a profile namespace on the namespace list
+ * @root: namespace to search in  (NOT NULL)
+ * @name: name of namespace to find  (NOT NULL)
+ *
+ * Returns: a refcounted namespace on the list, or NULL if no namespace
+ *          called @name exists.
+ *
+ * refcount released by caller
+ */
+struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name)
+{
+       return aa_findn_ns(root, name, strlen(name));
+}
+
+/**
+ * aa_prepare_ns - find an existing or create a new namespace of @name
+ * @root: ns to treat as root
+ * @name: the namespace to find or add  (NOT NULL)
+ *
+ * Returns: refcounted namespace or NULL if failed to create one
+ */
+struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name)
+{
+       struct aa_ns *ns;
+
+       mutex_lock(&root->lock);
+       /* try and find the specified ns and if it doesn't exist create it */
+       /* released by caller */
+       ns = aa_get_ns(__aa_findn_ns(&root->sub_ns, name, strlen(name)));
+       if (!ns) {
+               ns = alloc_ns(root->base.hname, name);
+               if (!ns)
+                       goto out;
+               mutex_lock(&ns->lock);
+               if (__aa_fs_ns_mkdir(ns, ns_subns_dir(root), name)) {
+                       AA_ERROR("Failed to create interface for ns %s\n",
+                                ns->base.name);
+                       mutex_unlock(&ns->lock);
+                       aa_free_ns(ns);
+                       ns = NULL;
+                       goto out;
+               }
+               ns->parent = aa_get_ns(root);
+               ns->level = root->level + 1;
+               list_add_rcu(&ns->base.list, &root->sub_ns);
+               /* add list ref */
+               aa_get_ns(ns);
+               mutex_unlock(&ns->lock);
+       }
+out:
+       mutex_unlock(&root->lock);
+
+       /* return ref */
+       return ns;
+}
+
+static void __ns_list_release(struct list_head *head);
+
+/**
+ * destroy_namespace - remove everything contained by @ns
+ * @ns: namespace to have it contents removed  (NOT NULL)
+ */
+static void destroy_ns(struct aa_ns *ns)
+{
+       if (!ns)
+               return;
+
+       mutex_lock(&ns->lock);
+       /* release all profiles in this namespace */
+       __aa_profile_list_release(&ns->base.profiles);
+
+       /* release all sub namespaces */
+       __ns_list_release(&ns->sub_ns);
+
+       if (ns->parent)
+               __aa_proxy_redirect(ns_unconfined(ns),
+                                   ns_unconfined(ns->parent));
+       __aa_fs_ns_rmdir(ns);
+       mutex_unlock(&ns->lock);
+}
+
+/**
+ * __aa_remove_ns - remove a namespace and all its children
+ * @ns: namespace to be removed  (NOT NULL)
+ *
+ * Requires: ns->parent->lock be held and ns removed from parent.
+ */
+void __aa_remove_ns(struct aa_ns *ns)
+{
+       /* remove ns from namespace list */
+       list_del_rcu(&ns->base.list);
+       destroy_ns(ns);
+       aa_put_ns(ns);
+}
+
+/**
+ * __ns_list_release - remove all profile namespaces on the list put refs
+ * @head: list of profile namespaces  (NOT NULL)
+ *
+ * Requires: namespace lock be held
+ */
+static void __ns_list_release(struct list_head *head)
+{
+       struct aa_ns *ns, *tmp;
+       list_for_each_entry_safe(ns, tmp, head, base.list)
+               __aa_remove_ns(ns);
+
+}
+
+/**
+ * aa_alloc_root_ns - allocate the root profile namespace
+ *
+ * Returns: %0 on success else error
+ *
+ */
+int __init aa_alloc_root_ns(void)
+{
+       /* released by aa_free_root_ns - used as list ref*/
+       root_ns = alloc_ns(NULL, "root");
+       if (!root_ns)
+               return -ENOMEM;
+
+       return 0;
+}
+
+ /**
+  * aa_free_root_ns - free the root profile namespace
+  */
+void __init aa_free_root_ns(void)
+ {
+        struct aa_ns *ns = root_ns;
+        root_ns = NULL;
+
+        destroy_ns(ns);
+        aa_put_ns(ns);
+}
index a689f10930b5e825c4da751508e274d8715ef2b5..c62815f936a893d2725595b47e1d19d797f82af4 100644 (file)
 #include <asm/unaligned.h>
 #include <linux/ctype.h>
 #include <linux/errno.h>
+#include <linux/string.h>
 
 #include "include/apparmor.h"
 #include "include/audit.h"
 #include "include/context.h"
 #include "include/crypto.h"
 #include "include/match.h"
+#include "include/path.h"
 #include "include/policy.h"
 #include "include/policy_unpack.h"
 
+#define K_ABI_MASK 0x3ff
+#define FORCE_COMPLAIN_FLAG 0x800
+#define VERSION_CMP(OP, X, Y) (((X) & K_ABI_MASK) OP ((Y) & K_ABI_MASK))
+
+#define v5     5       /* base version */
+#define v6     6       /* per entry policydb mediation check */
+#define v7     7       /* full network masking */
+
 /*
  * The AppArmor interface treats data as a type byte followed by the
  * actual data.  The interface has the notion of a a named entry
@@ -70,18 +80,23 @@ struct aa_ext {
 static void audit_cb(struct audit_buffer *ab, void *va)
 {
        struct common_audit_data *sa = va;
-       if (sa->aad->iface.target) {
-               struct aa_profile *name = sa->aad->iface.target;
+
+       if (aad(sa)->iface.ns) {
+               audit_log_format(ab, " ns=");
+               audit_log_untrustedstring(ab, aad(sa)->iface.ns);
+       }
+       if (aad(sa)->name) {
                audit_log_format(ab, " name=");
-               audit_log_untrustedstring(ab, name->base.hname);
+               audit_log_untrustedstring(ab, aad(sa)->name);
        }
-       if (sa->aad->iface.pos)
-               audit_log_format(ab, " offset=%ld", sa->aad->iface.pos);
+       if (aad(sa)->iface.pos)
+               audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos);
 }
 
 /**
  * audit_iface - do audit message for policy unpacking/load/replace/remove
  * @new: profile if it has been allocated (MAYBE NULL)
+ * @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL)
  * @name: name of the profile being manipulated (MAYBE NULL)
  * @info: any extra info about the failure (MAYBE NULL)
  * @e: buffer position info
@@ -89,23 +104,24 @@ static void audit_cb(struct audit_buffer *ab, void *va)
  *
  * Returns: %0 or error
  */
-static int audit_iface(struct aa_profile *new, const char *name,
-                      const char *info, struct aa_ext *e, int error)
+static int audit_iface(struct aa_profile *new, const char *ns_name,
+                      const char *name, const char *info, struct aa_ext *e,
+                      int error)
 {
-       struct aa_profile *profile = __aa_current_profile();
-       struct common_audit_data sa;
-       struct apparmor_audit_data aad = {0,};
-       sa.type = LSM_AUDIT_DATA_NONE;
-       sa.aad = &aad;
+       struct aa_profile *profile = labels_profile(aa_current_raw_label());
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
        if (e)
-               aad.iface.pos = e->pos - e->start;
-       aad.iface.target = new;
-       aad.name = name;
-       aad.info = info;
-       aad.error = error;
-
-       return aa_audit(AUDIT_APPARMOR_STATUS, profile, GFP_KERNEL, &sa,
-                       audit_cb);
+               aad(&sa)->iface.pos = e->pos - e->start;
+
+       aad(&sa)->iface.ns = ns_name;
+       if (new)
+               aad(&sa)->name = new->base.hname;
+       else
+               aad(&sa)->name = name;
+       aad(&sa)->info = info;
+       aad(&sa)->error = error;
+
+       return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb);
 }
 
 /* test if read will be in packed data bounds */
@@ -193,6 +209,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)) {
@@ -340,12 +369,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
                        ((e->pos - e->start) & 7);
                size_t pad = ALIGN(sz, 8) - sz;
                int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) |
-                       TO_ACCEPT2_FLAG(YYTD_DATA32);
-
-
-               if (aa_g_paranoid_load)
-                       flags |= DFA_FLAG_VERIFY_STATES;
-
+                       TO_ACCEPT2_FLAG(YYTD_DATA32) | DFA_FLAG_VERIFY_STATES;
                dfa = aa_dfa_unpack(blob + pad, size - pad, flags);
 
                if (IS_ERR(dfa))
@@ -389,7 +413,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
                profile->file.trans.size = size;
                for (i = 0; i < size; i++) {
                        char *str;
-                       int c, j, size2 = unpack_strdup(e, &str, NULL);
+                       int c, j, pos, size2 = unpack_strdup(e, &str, NULL);
                        /* unpack_strdup verifies that the last character is
                         * null termination byte.
                         */
@@ -401,19 +425,24 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
                                goto fail;
 
                        /* count internal #  of internal \0 */
-                       for (c = j = 0; j < size2 - 2; j++) {
-                               if (!str[j])
+                       for (c = j = 0; j < size2 - 1; j++) {
+                               if (!str[j]) {
+                                       pos = j;
                                        c++;
+                               }
                        }
                        if (*str == ':') {
+                               /* first character after : must be valid */
+                               if (!str[1])
+                                       goto fail;
                                /* beginning with : requires an embedded \0,
                                 * verify that exactly 1 internal \0 exists
                                 * trailing \0 already verified by unpack_strdup
                                 */
-                               if (c != 1)
-                                       goto fail;
-                               /* first character after : must be valid */
-                               if (!str[1])
+                               if (c == 1)
+                                       /* convert \0 back to : for label_parse */
+                                       str[pos] = ':';
+                               else if (c > 1)
                                        goto fail;
                        } else if (c)
                                /* fail - all other cases with embedded \0 */
@@ -472,21 +501,35 @@ fail:
  *
  * NOTE: unpack profile sets audit struct if there is a failure
  */
-static struct aa_profile *unpack_profile(struct aa_ext *e)
+static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
 {
        struct aa_profile *profile = NULL;
-       const char *name = NULL;
+       const char *tmpname, *tmpns = NULL, *name = NULL;
+       const char *info = "failed to unpack profile";
+       size_t size = 0, ns_len;
        int i, error = -EPROTO;
        kernel_cap_t tmpcap;
        u32 tmp;
 
+       *ns_name = NULL;
+
        /* check that we have the right struct being passed */
        if (!unpack_nameX(e, AA_STRUCT, "profile"))
                goto fail;
        if (!unpack_str(e, &name, NULL))
                goto fail;
+       if (*name == '\0')
+               goto fail;
+
+       tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len);
+       if (tmpns) {
+         *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
+               if (!*ns_name)
+                       goto fail;
+               name = tmpname;
+       }
 
-       profile = aa_alloc_profile(name);
+       profile = aa_alloc_profile(name, NULL, GFP_KERNEL);
        if (!profile)
                return ERR_PTR(-ENOMEM);
 
@@ -510,16 +553,19 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
                profile->xmatch_len = tmp;
        }
 
+       /* disconnected attachment string is optional */
+       (void) unpack_str(e, &profile->disconnected, "disconnected");
+
        /* per profile debug flags (complain, audit) */
        if (!unpack_nameX(e, AA_STRUCT, "flags"))
                goto fail;
        if (!unpack_u32(e, &tmp, NULL))
                goto fail;
        if (tmp & PACKED_FLAG_HAT)
-               profile->flags |= PFLAG_HAT;
+               profile->label.flags |= FLAG_HAT;
        if (!unpack_u32(e, &tmp, NULL))
                goto fail;
-       if (tmp == PACKED_MODE_COMPLAIN)
+       if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG))
                profile->mode = APPARMOR_COMPLAIN;
        else if (tmp == PACKED_MODE_KILL)
                profile->mode = APPARMOR_KILL;
@@ -534,11 +580,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
                goto fail;
 
        /* path_flags is optional */
-       if (unpack_u32(e, &profile->path_flags, "path_flags"))
-               profile->path_flags |= profile->flags & PFLAG_MEDIATE_DELETED;
-       else
+       if (!unpack_u32(e, &profile->path_flags, "path_flags"))
                /* set a default value if path_flags field is not present */
-               profile->path_flags = PFLAG_MEDIATE_DELETED;
+               profile->path_flags = PATH_MEDIATE_DELETED;
 
        if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL))
                goto fail;
@@ -576,6 +620,37 @@ 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;
+       }
+       if (VERSION_CMP(<, e->version, v7)) {
+               /* old policy always allowed these too */
+               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);
@@ -596,7 +671,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
                }
                if (!unpack_nameX(e, AA_STRUCTEND, NULL))
                        goto fail;
-       }
+       } else
+               profile->policy.dfa = aa_get_dfa(nulldfa);
 
        /* get file rules */
        profile->file.dfa = unpack_dfa(e);
@@ -604,11 +680,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
                error = PTR_ERR(profile->file.dfa);
                profile->file.dfa = NULL;
                goto fail;
-       }
-
-       if (!unpack_u32(e, &profile->file.start, "dfa_start"))
-               /* default start state */
-               profile->file.start = DFA_START;
+       } else if (profile->file.dfa) {
+               if (!unpack_u32(e, &profile->file.start, "dfa_start"))
+                       /* default start state */
+                       profile->file.start = DFA_START;
+       } else if (profile->policy.dfa &&
+                  profile->policy.start[AA_CLASS_FILE]) {
+               profile->file.dfa = aa_get_dfa(profile->policy.dfa);
+               profile->file.start = profile->policy.start[AA_CLASS_FILE];
+       } else
+               profile->file.dfa = aa_get_dfa(nulldfa);
 
        if (!unpack_trans_table(e, profile))
                goto fail;
@@ -623,7 +704,7 @@ fail:
                name = NULL;
        else if (!name)
                name = "unknown";
-       audit_iface(profile, name, "failed to unpack profile", e, error);
+       audit_iface(profile, NULL, name, info, e, error);
        aa_free_profile(profile);
 
        return ERR_PTR(error);
@@ -646,24 +727,32 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
        /* get the interface version */
        if (!unpack_u32(e, &e->version, "version")) {
                if (required) {
-                       audit_iface(NULL, NULL, "invalid profile format", e,
-                                   error);
-                       return error;
-               }
-
-               /* check that the interface version is currently supported */
-               if (e->version != 5) {
-                       audit_iface(NULL, NULL, "unsupported interface version",
+                       audit_iface(NULL, NULL, NULL, "invalid profile format",
                                    e, error);
                        return error;
                }
        }
 
+       /* Check that the interface version is currently supported.
+        * if not specified use previous version
+        * Mask off everything that is not kernel abi version
+        */
+       if (VERSION_CMP(<, e->version, v5) && VERSION_CMP(>, e->version, v7)) {
+               audit_iface(NULL, NULL, NULL, "unsupported interface version",
+                           e, error);
+               return error;
+       }
 
        /* read the namespace if present */
        if (unpack_str(e, &name, "namespace")) {
+               if (*name == '\0') {
+                       audit_iface(NULL, NULL, NULL, "invalid namespace name",
+                                   e, error);
+                       return error;
+               }
                if (*ns && strcmp(*ns, name))
-                       audit_iface(NULL, NULL, "invalid ns change", e, error);
+                       audit_iface(NULL, NULL, NULL, "invalid ns change", e,
+                                   error);
                else if (!*ns)
                        *ns = name;
        }
@@ -676,7 +765,7 @@ static bool verify_xindex(int xindex, int table_size)
        int index, xtype;
        xtype = xindex & AA_X_TYPE_MASK;
        index = xindex & AA_X_INDEX_MASK;
-       if (xtype == AA_X_TABLE && index > table_size)
+       if (xtype == AA_X_TABLE && index >= table_size)
                return 0;
        return 1;
 }
@@ -702,14 +791,12 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size)
  */
 static int verify_profile(struct aa_profile *profile)
 {
-       if (aa_g_paranoid_load) {
-               if (profile->file.dfa &&
-                   !verify_dfa_xindex(profile->file.dfa,
-                                      profile->file.trans.size)) {
-                       audit_iface(profile, NULL, "Invalid named transition",
-                                   NULL, -EPROTO);
-                       return -EPROTO;
-               }
+       if (profile->file.dfa &&
+           !verify_dfa_xindex(profile->file.dfa,
+                              profile->file.trans.size)) {
+               audit_iface(profile, NULL, NULL,
+                           "Invalid named transition", NULL, -EPROTO);
+               return -EPROTO;
        }
 
        return 0;
@@ -721,6 +808,7 @@ void aa_load_ent_free(struct aa_load_ent *ent)
                aa_put_profile(ent->rename);
                aa_put_profile(ent->old);
                aa_put_profile(ent->new);
+               kfree(ent->ns_name);
                kzfree(ent);
        }
 }
@@ -759,13 +847,14 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
 
        *ns = NULL;
        while (e.pos < e.end) {
+               char *ns_name = NULL;
                void *start;
                error = verify_header(&e, e.pos == e.start, ns);
                if (error)
                        goto fail;
 
                start = e.pos;
-               profile = unpack_profile(&e);
+               profile = unpack_profile(&e, &ns_name);
                if (IS_ERR(profile)) {
                        error = PTR_ERR(profile);
                        goto fail;
@@ -775,8 +864,9 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
                if (error)
                        goto fail_profile;
 
-               error = aa_calc_profile_hash(profile, e.version, start,
-                                            e.pos - start);
+               if (aa_g_hash_policy)
+                       error = aa_calc_profile_hash(profile, e.version, start,
+                                                    e.pos - start);
                if (error)
                        goto fail_profile;
 
@@ -787,6 +877,7 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
                }
 
                ent->new = profile;
+               ent->ns_name = ns_name;
                list_add_tail(&ent->list, lh);
        }
 
index b125acc9aa26cc327572955fb7aa83dbf2c5d09d..65bf9e95b5c5b72d074902724d209c66fd26185e 100644 (file)
  *
  * Returns: size of string placed in @string else error code on failure
  */
-int aa_getprocattr(struct aa_profile *profile, char **string)
+int aa_getprocattr(struct aa_label *label, char **string)
 {
-       char *str;
-       int len = 0, mode_len = 0, ns_len = 0, name_len;
-       const char *mode_str = aa_profile_mode_names[profile->mode];
-       const char *ns_name = NULL;
-       struct aa_namespace *ns = profile->ns;
-       struct aa_namespace *current_ns = __aa_current_profile()->ns;
-       char *s;
-
-       if (!aa_ns_visible(current_ns, ns))
-               return -EACCES;
-
-       ns_name = aa_ns_name(current_ns, ns);
-       ns_len = strlen(ns_name);
+       struct aa_ns *ns = labels_ns(label);
+       struct aa_ns *current_ns = aa_get_current_ns();
+       int len;
 
-       /* if the visible ns_name is > 0 increase size for : :// seperator */
-       if (ns_len)
-               ns_len += 4;
+       if (!aa_ns_visible(current_ns, ns, true)) {
+               aa_put_ns(current_ns);
+               return -EACCES;
+       }
 
-       /* unconfined profiles don't have a mode string appended */
-       if (!unconfined(profile))
-               mode_len = strlen(mode_str) + 3;        /* + 3 for _() */
+       len = aa_label_snxprint(NULL, 0, current_ns, label,
+                               FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
+                               FLAG_HIDDEN_UNCONFINED);
+       AA_BUG(len < 0);
 
-       name_len = strlen(profile->base.hname);
-       len = mode_len + ns_len + name_len + 1;     /* + 1 for \n */
-       s = str = kmalloc(len + 1, GFP_KERNEL);     /* + 1 \0 */
-       if (!str)
+       *string = kmalloc(len + 2, GFP_KERNEL);
+       if (!*string) {
+               aa_put_ns(current_ns);
                return -ENOMEM;
+       }
 
-       if (ns_len) {
-               /* skip over prefix current_ns->base.hname and separating // */
-               sprintf(s, ":%s://", ns_name);
-               s += ns_len;
+       len = aa_label_snxprint(*string, len + 2, current_ns, label,
+                               FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
+                               FLAG_HIDDEN_UNCONFINED);
+       if (len < 0) {
+               aa_put_ns(current_ns);
+               return len;
        }
-       if (unconfined(profile))
-               /* mode string not being appended */
-               sprintf(s, "%s\n", profile->base.hname);
-       else
-               sprintf(s, "%s (%s)\n", profile->base.hname, mode_str);
-       *string = str;
-
-       /* NOTE: len does not include \0 of string, not saved as part of file */
-       return len;
+
+       (*string)[len] = '\n';
+       (*string)[len + 1] = 0;
+
+       aa_put_ns(current_ns);
+       return len + 1;
 }
 
 /**
@@ -87,13 +78,13 @@ int aa_getprocattr(struct aa_profile *profile, char **string)
  *
  * Returns: start position of name after token else NULL on failure
  */
-static char *split_token_from_name(int op, char *args, u64 * token)
+static char *split_token_from_name(const char *op, char *args, u64 * token)
 {
        char *name;
 
        *token = simple_strtoull(args, &name, 16);
        if ((name == args) || *name != '^') {
-               AA_ERROR("%s: Invalid input '%s'", op_table[op], args);
+               AA_ERROR("%s: Invalid input '%s'", op, args);
                return ERR_PTR(-EINVAL);
        }
 
@@ -138,28 +129,13 @@ int aa_setprocattr_changehat(char *args, size_t size, int test)
                for (count = 0; (hat < end) && count < 16; ++count) {
                        char *next = hat + strlen(hat) + 1;
                        hats[count] = hat;
+                       AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d hat '%s'\n"
+                                , __func__, current->pid, token, count, hat);
                        hat = next;
                }
-       }
-
-       AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n",
-                __func__, token, hat ? hat : NULL);
+       } else
+               AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d Hat '%s'\n",
+                        __func__, current->pid, token, count, "<NULL>");
 
        return aa_change_hat(hats, count, token, test);
 }
-
-/**
- * aa_setprocattr_changeprofile - handle procattr interface to changeprofile
- * @fqname: args received from writting to /proc/<pid>/attr/current (NOT NULL)
- * @onexec: true if change_profile should be delayed until exec
- * @test: true if this is a test of change_profile permissions
- *
- * Returns: %0 or error code if change_profile fails
- */
-int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test)
-{
-       char *name, *ns_name;
-
-       name = aa_split_fqname(fqname, &ns_name);
-       return aa_change_profile(ns_name, name, onexec, test);
-}
index 748bf0ca6c9f74572c601ebdaf033c6da224a20e..3888f2b308b9199db27f70f532c46bb7972ac358 100644 (file)
@@ -35,7 +35,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
        struct common_audit_data *sa = va;
 
        audit_log_format(ab, " rlimit=%s value=%lu",
-                        rlim_names[sa->aad->rlim.rlim], sa->aad->rlim.max);
+                        rlim_names[aad(sa)->rlim.rlim], aad(sa)->rlim.max);
 }
 
 /**
@@ -50,17 +50,11 @@ static void audit_cb(struct audit_buffer *ab, void *va)
 static int audit_resource(struct aa_profile *profile, unsigned int resource,
                          unsigned long value, int error)
 {
-       struct common_audit_data sa;
-       struct apparmor_audit_data aad = {0,};
-
-       sa.type = LSM_AUDIT_DATA_NONE;
-       sa.aad = &aad;
-       aad.op = OP_SETRLIMIT,
-       aad.rlim.rlim = resource;
-       aad.rlim.max = value;
-       aad.error = error;
-       return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_KERNEL, &sa,
-                       audit_cb);
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETRLIMIT);
+       aad(&sa)->rlim.rlim = resource;
+       aad(&sa)->rlim.max = value;
+       aad(&sa)->error = error;
+       return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb);
 }
 
 /**
@@ -77,9 +71,20 @@ int aa_map_resource(int resource)
        return rlim_map[resource];
 }
 
+static int profile_setrlimit(struct aa_profile *profile, unsigned int resource,
+                            struct rlimit *new_rlim)
+{
+       int e = 0;
+
+       if (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max >
+           profile->rlimits.limits[resource].rlim_max)
+               e = -EACCES;
+       return audit_resource(profile, resource, new_rlim->rlim_max, e);
+}
+
 /**
  * aa_task_setrlimit - test permission to set an rlimit
- * @profile - profile confining the task  (NOT NULL)
+ * @label - label confining the task  (NOT NULL)
  * @task - task the resource is being set on
  * @resource - the resource being set
  * @new_rlim - the new resource limit  (NOT NULL)
@@ -88,67 +93,84 @@ int aa_map_resource(int resource)
  *
  * Returns: 0 or error code if setting resource failed
  */
-int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task,
+int aa_task_setrlimit(struct aa_label *label, struct task_struct *task,
                      unsigned int resource, struct rlimit *new_rlim)
 {
-       struct aa_profile *task_profile;
+       struct aa_profile *profile;
+       struct aa_label *peer;
        int error = 0;
 
        rcu_read_lock();
-       task_profile = aa_get_profile(aa_cred_profile(__task_cred(task)));
+       peer = aa_get_newest_cred_label(__task_cred(task));
        rcu_read_unlock();
 
        /* TODO: extend resource control to handle other (non current)
         * profiles.  AppArmor rules currently have the implicit assumption
         * that the task is setting the resource of a task confined with
-        * the same profile.
+        * the same profile or that the task setting the resource of another
+        * task has CAP_SYS_RESOURCE.
         */
-       if (profile != task_profile ||
-           (profile->rlimits.mask & (1 << resource) &&
-            new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max))
-               error = -EACCES;
 
-       aa_put_profile(task_profile);
-
-       return audit_resource(profile, resource, new_rlim->rlim_max, error);
+       if (label != peer &&
+           !aa_capable(label, CAP_SYS_RESOURCE, SECURITY_CAP_NOAUDIT))
+               error = fn_for_each(label, profile,
+                               audit_resource(profile, resource,
+                                              new_rlim->rlim_max, EACCES));
+       else
+               error = fn_for_each_confined(label, profile,
+                               profile_setrlimit(profile, resource, new_rlim));
+       aa_put_label(peer);
+
+       return error;
 }
 
 /**
  * __aa_transition_rlimits - apply new profile rlimits
- * @old: old profile on task  (NOT NULL)
- * @new: new profile with rlimits to apply  (NOT NULL)
+ * @old_l: old label on task  (NOT NULL)
+ * @new_l: new label with rlimits to apply  (NOT NULL)
  */
-void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new)
+void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l)
 {
        unsigned int mask = 0;
        struct rlimit *rlim, *initrlim;
-       int i;
+       struct aa_profile *old, *new;
+       struct label_it i;
+
+       old = labels_profile(old_l);
+       new = labels_profile(new_l);
 
-       /* for any rlimits the profile controlled reset the soft limit
-        * to the less of the tasks hard limit and the init tasks soft limit
+       /* for any rlimits the profile controlled, reset the soft limit
+        * to the lesser of the tasks hard limit and the init tasks soft limit
         */
-       if (old->rlimits.mask) {
-               for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) {
-                       if (old->rlimits.mask & mask) {
-                               rlim = current->signal->rlim + i;
-                               initrlim = init_task.signal->rlim + i;
-                               rlim->rlim_cur = min(rlim->rlim_max,
-                                                    initrlim->rlim_cur);
+       label_for_each_confined(i, old_l, old) {
+               if (old->rlimits.mask) {
+                       int j;
+                       for (j = 0, mask = 1; j < RLIM_NLIMITS; j++,
+                                    mask <<= 1) {
+                               if (old->rlimits.mask & mask) {
+                                       rlim = current->signal->rlim + j;
+                                       initrlim = init_task.signal->rlim + j;
+                                       rlim->rlim_cur = min(rlim->rlim_max,
+                                                           initrlim->rlim_cur);
+                               }
                        }
                }
        }
 
        /* set any new hard limits as dictated by the new profile */
-       if (!new->rlimits.mask)
-               return;
-       for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) {
-               if (!(new->rlimits.mask & mask))
+       label_for_each_confined(i, new_l, new) {
+               int j;
+               if (!new->rlimits.mask)
                        continue;
-
-               rlim = current->signal->rlim + i;
-               rlim->rlim_max = min(rlim->rlim_max,
-                                    new->rlimits.limits[i].rlim_max);
-               /* soft limit should not exceed hard limit */
-               rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max);
+               for (j = 0, mask = 1; j < RLIM_NLIMITS; j++, mask <<= 1) {
+                       if (!(new->rlimits.mask & mask))
+                               continue;
+
+                       rlim = current->signal->rlim + j;
+                       rlim->rlim_max = min(rlim->rlim_max,
+                                            new->rlimits.limits[j].rlim_max);
+                       /* soft limit should not exceed hard limit */
+                       rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max);
+               }
        }
 }