Merge tag 'apparmor-pr-2017-09-22' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 23 Sep 2017 15:33:29 +0000 (05:33 -1000)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 23 Sep 2017 15:33:29 +0000 (05:33 -1000)
Pull apparmor updates from John Johansen:
 "This is the apparmor pull request, similar to SELinux and seccomp.

  It's the same series that I was sent to James' security tree + one
  regression fix that was found after the series was sent to James and
  would have been sent for v4.14-rc2.

  Features:
  - in preparation for secid mapping add support for absolute root view
    based labels
  - add base infastructure for socket mediation
  - add mount mediation
  - add signal mediation

  minor cleanups and changes:
  - be defensive, ensure unconfined profiles have dfas initialized
  - add more debug asserts to apparmorfs
  - enable policy unpacking to audit different reasons for failure
  - cleanup conditional check for label in label_print
  - Redundant condition: prev_ns. in [label.c:1498]

  Bug Fixes:
  - fix regression in apparmorfs DAC access permissions
  - fix build failure on sparc caused by undeclared signals
  - fix sparse report of incorrect type assignment when freeing label proxies
  - fix race condition in null profile creation
  - Fix an error code in aafs_create()
  - Fix logical error in verify_header()
  - Fix shadowed local variable in unpack_trans_table()"

* tag 'apparmor-pr-2017-09-22' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor:
  apparmor: fix apparmorfs DAC access permissions
  apparmor: fix build failure on sparc caused by undeclared signals
  apparmor: fix incorrect type assignment when freeing proxies
  apparmor: ensure unconfined profiles have dfas initialized
  apparmor: fix race condition in null profile creation
  apparmor: move new_null_profile to after profile lookup fns()
  apparmor: add base infastructure for socket mediation
  apparmor: add more debug asserts to apparmorfs
  apparmor: make policy_unpack able to audit different info messages
  apparmor: add support for absolute root view based labels
  apparmor: cleanup conditional check for label in label_print
  apparmor: add mount mediation
  apparmor: add the ability to mediate signals
  apparmor: Redundant condition: prev_ns. in [label.c:1498]
  apparmor: Fix an error code in aafs_create()
  apparmor: Fix logical error in verify_header()
  apparmor: Fix shadowed local variable in unpack_trans_table()

24 files changed:
security/apparmor/.gitignore
security/apparmor/Makefile
security/apparmor/apparmorfs.c
security/apparmor/domain.c
security/apparmor/file.c
security/apparmor/include/apparmor.h
security/apparmor/include/audit.h
security/apparmor/include/domain.h
security/apparmor/include/ipc.h
security/apparmor/include/label.h
security/apparmor/include/mount.h [new file with mode: 0644]
security/apparmor/include/net.h [new file with mode: 0644]
security/apparmor/include/perms.h
security/apparmor/include/policy.h
security/apparmor/include/sig_names.h [new file with mode: 0644]
security/apparmor/ipc.c
security/apparmor/label.c
security/apparmor/lib.c
security/apparmor/lsm.c
security/apparmor/mount.c [new file with mode: 0644]
security/apparmor/net.c [new file with mode: 0644]
security/apparmor/policy.c
security/apparmor/policy_ns.c
security/apparmor/policy_unpack.c

index 9cdec70..d5b291e 100644 (file)
@@ -1,5 +1,6 @@
 #
 # Generated include files
 #
+net_names.h
 capability_names.h
 rlim_names.h
index a16b195..dafdd38 100644 (file)
@@ -4,11 +4,44 @@ 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 secid.o file.o policy_ns.o label.o
+              resource.o secid.o file.o policy_ns.o label.o mount.o net.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_SFS_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 "};" >> $@ ;\
+       printf '%s' '\#define AA_SFS_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 +94,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 +102,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)
index 853c2ec..518d592 100644 (file)
@@ -32,6 +32,7 @@
 #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"
@@ -248,8 +249,10 @@ static struct dentry *aafs_create(const char *name, umode_t mode,
 
        inode_lock(dir);
        dentry = lookup_one_len(name, parent, strlen(name));
-       if (IS_ERR(dentry))
+       if (IS_ERR(dentry)) {
+               error = PTR_ERR(dentry);
                goto fail_lock;
+       }
 
        if (d_really_is_positive(dentry)) {
                error = -EEXIST;
@@ -1443,6 +1446,10 @@ void __aafs_profile_migrate_dents(struct aa_profile *old,
 {
        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])
@@ -1506,6 +1513,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
        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);
@@ -1731,6 +1741,7 @@ void __aafs_ns_rmdir(struct aa_ns *ns)
 
        if (!ns)
                return;
+       AA_BUG(!mutex_is_locked(&ns->lock));
 
        list_for_each_entry(child, &ns->base.profiles, base.list)
                __aafs_profile_rmdir(child);
@@ -1903,6 +1914,10 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
 {
        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)) {
                next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
@@ -1937,6 +1952,9 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
 static struct aa_profile *__first_profile(struct aa_ns *root,
                                          struct aa_ns *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,
@@ -1959,6 +1977,8 @@ static struct aa_profile *__next_profile(struct aa_profile *p)
        struct aa_profile *parent;
        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))
                return list_first_entry(&p->base.profiles, typeof(*p),
@@ -2127,6 +2147,11 @@ static struct aa_sfs_entry aa_sfs_entry_ptrace[] = {
        { }
 };
 
+static struct aa_sfs_entry aa_sfs_entry_signal[] = {
+       AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK),
+       { }
+};
+
 static struct aa_sfs_entry aa_sfs_entry_domain[] = {
        AA_SFS_FILE_BOOLEAN("change_hat",       1),
        AA_SFS_FILE_BOOLEAN("change_hatv",      1),
@@ -2151,9 +2176,14 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = {
        { }
 };
 
+static struct aa_sfs_entry aa_sfs_entry_mount[] = {
+       AA_SFS_FILE_STRING("mask", "mount umount pivot_root"),
+       { }
+};
+
 static struct aa_sfs_entry aa_sfs_entry_ns[] = {
        AA_SFS_FILE_BOOLEAN("profile",          1),
-       AA_SFS_FILE_BOOLEAN("pivot_root",       1),
+       AA_SFS_FILE_BOOLEAN("pivot_root",       0),
        { }
 };
 
@@ -2172,22 +2202,25 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
        AA_SFS_DIR("policy",                    aa_sfs_entry_policy),
        AA_SFS_DIR("domain",                    aa_sfs_entry_domain),
        AA_SFS_DIR("file",                      aa_sfs_entry_file),
+       AA_SFS_DIR("network",                   aa_sfs_entry_network),
+       AA_SFS_DIR("mount",                     aa_sfs_entry_mount),
        AA_SFS_DIR("namespaces",                aa_sfs_entry_ns),
        AA_SFS_FILE_U64("capability",           VFS_CAP_FLAGS_MASK),
        AA_SFS_DIR("rlimit",                    aa_sfs_entry_rlimit),
        AA_SFS_DIR("caps",                      aa_sfs_entry_caps),
        AA_SFS_DIR("ptrace",                    aa_sfs_entry_ptrace),
+       AA_SFS_DIR("signal",                    aa_sfs_entry_signal),
        AA_SFS_DIR("query",                     aa_sfs_entry_query),
        { }
 };
 
 static struct aa_sfs_entry aa_sfs_entry_apparmor[] = {
-       AA_SFS_FILE_FOPS(".access", 0640, &aa_sfs_access),
+       AA_SFS_FILE_FOPS(".access", 0666, &aa_sfs_access),
        AA_SFS_FILE_FOPS(".stacked", 0444, &seq_ns_stacked_fops),
        AA_SFS_FILE_FOPS(".ns_stacked", 0444, &seq_ns_nsstacked_fops),
-       AA_SFS_FILE_FOPS(".ns_level", 0666, &seq_ns_level_fops),
-       AA_SFS_FILE_FOPS(".ns_name", 0640, &seq_ns_name_fops),
-       AA_SFS_FILE_FOPS("profiles", 0440, &aa_sfs_profiles_fops),
+       AA_SFS_FILE_FOPS(".ns_level", 0444, &seq_ns_level_fops),
+       AA_SFS_FILE_FOPS(".ns_name", 0444, &seq_ns_name_fops),
+       AA_SFS_FILE_FOPS("profiles", 0444, &aa_sfs_profiles_fops),
        AA_SFS_DIR("features", aa_sfs_entry_features),
        { }
 };
index 17a601c..dd754b7 100644 (file)
@@ -374,8 +374,8 @@ static const char *next_name(int xtype, const char *name)
  *
  * Returns: refcounted label, or NULL on failure (MAYBE NULL)
  */
-static struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
-                                      const char **name)
+struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
+                               const char **name)
 {
        struct aa_label *label = NULL;
        u32 xtype = xindex & AA_X_TYPE_MASK;
index 3382518..db80221 100644 (file)
@@ -21,6 +21,7 @@
 #include "include/context.h"
 #include "include/file.h"
 #include "include/match.h"
+#include "include/net.h"
 #include "include/path.h"
 #include "include/policy.h"
 #include "include/label.h"
@@ -566,6 +567,32 @@ static int __file_path_perm(const char *op, struct aa_label *label,
        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;
+}
+
 /**
  * aa_file_perm - do permission revalidation check & audit for @file
  * @op: operation being checked
@@ -610,6 +637,9 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
                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();
 
index aaf893f..829082c 100644 (file)
@@ -27,7 +27,9 @@
 #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_LABEL
index c68839a..ff4316e 100644 (file)
@@ -71,6 +71,10 @@ enum audit_type {
 #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"
@@ -86,6 +90,7 @@ enum audit_type {
 #define OP_SHUTDOWN "socket_shutdown"
 
 #define OP_PTRACE "ptrace"
+#define OP_SIGNAL "signal"
 
 #define OP_EXEC "exec"
 
@@ -116,20 +121,36 @@ struct apparmor_audit_data {
                /* these entries require a custom callback fn */
                struct {
                        struct aa_label *peer;
-                       struct {
-                               const char *target;
-                               kuid_t ouid;
-                       } fs;
+                       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 {
+                                       int rlim;
+                                       unsigned long max;
+                               } rlim;
+                       };
                };
                struct {
-                       const char *name;
-                       long pos;
+                       struct aa_profile *profile;
                        const char *ns;
+                       long pos;
                } iface;
                struct {
-                       int rlim;
-                       unsigned long max;
-               } rlim;
+                       const char *src_name;
+                       const char *type;
+                       const char *trans;
+                       const char *data;
+                       unsigned long flags;
+               } mnt;
        };
 };
 
index 24c5976..ac9862f 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/binfmts.h>
 #include <linux/types.h>
 
+#include "label.h"
+
 #ifndef __AA_DOMAIN_H
 #define __AA_DOMAIN_H
 
@@ -29,6 +31,9 @@ struct aa_domain {
 #define AA_CHANGE_ONEXEC  4
 #define AA_CHANGE_STACK 8
 
+struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
+                               const char **name);
+
 int apparmor_bprm_set_creds(struct linux_binprm *bprm);
 
 void aa_free_domain_entries(struct aa_domain *domain);
index 656fdb8..5ffc218 100644 (file)
@@ -27,8 +27,14 @@ struct aa_profile;
 
 #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_SFS_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 */
index 9a283b7..af22dcb 100644 (file)
@@ -310,6 +310,7 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp);
 #define FLAG_SHOW_MODE 1
 #define FLAG_VIEW_SUBNS 2
 #define FLAG_HIDDEN_UNCONFINED 4
+#define FLAG_ABS_ROOT 8
 int aa_label_snxprint(char *str, size_t size, struct aa_ns *view,
                      struct aa_label *label, int flags);
 int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label,
diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h
new file mode 100644 (file)
index 0000000..25d6067
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor file mediation function definitions.
+ *
+ * Copyright 2017 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, const struct path *path,
+              unsigned long flags, void *data);
+
+int aa_bind_mount(struct aa_label *label, const struct path *path,
+                 const char *old_name, unsigned long flags);
+
+
+int aa_mount_change_type(struct aa_label *label, const struct path *path,
+                        unsigned long flags);
+
+int aa_move_mount(struct aa_label *label, const struct path *path,
+                 const char *old_name);
+
+int aa_new_mount(struct aa_label *label, const char *dev_name,
+                const 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, const struct path *old_path,
+                const 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..140c8ef
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor network mediation definitions.
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2017 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
+ * @allow: basic network families permissions
+ * @audit: which network permissions to force audit
+ * @quiet: which network permissions to quiet rejects
+ */
+struct aa_net {
+       u16 allow[AF_MAX];
+       u16 audit[AF_MAX];
+       u16 quiet[AF_MAX];
+};
+
+
+extern struct aa_sfs_entry aa_sfs_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);
+int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family,
+              int type, int protocol);
+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_sk_perm(const char *op, u32 request, struct sock *sk);
+
+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 2b27bb7..af04d5a 100644 (file)
@@ -135,9 +135,10 @@ extern struct aa_perms allperms;
 
 
 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_names(struct audit_buffer *ab, const char * const *names,
+                        u32 mask);
 void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
-                       u32 chrsmask, const char **names, u32 namesmask);
+                       u32 chrsmask, const char * const *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,
index 17fe41a..4364088 100644 (file)
@@ -30,6 +30,7 @@
 #include "file.h"
 #include "lib.h"
 #include "label.h"
+#include "net.h"
 #include "perms.h"
 #include "resource.h"
 
@@ -111,6 +112,7 @@ struct aa_data {
  * @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
@@ -148,6 +150,7 @@ struct aa_profile {
        struct aa_policydb policy;
        struct aa_file_rules file;
        struct aa_caps caps;
+       struct aa_net net;
        struct aa_rlimit rlimits;
 
        struct aa_loaddata *rawdata;
@@ -220,6 +223,16 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile,
        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);
+}
+
 /**
  * aa_get_profile - increment refcount on profile @p
  * @p: profile  (MAYBE NULL)
diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h
new file mode 100644 (file)
index 0000000..92e62fe
--- /dev/null
@@ -0,0 +1,98 @@
+#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,    /* existence 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,
+#ifdef SIGSTKFLT
+       [SIGSTKFLT] = 16,       /* -, 16, - */
+#endif
+       [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(SIGUNUSED) && \
+    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 existence test mapped to MAXMAPPED_SIG */
+};
+
index 11e66b5..66fb9ed 100644 (file)
@@ -20,6 +20,7 @@
 #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
@@ -121,3 +122,101 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
 }
 
 
+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;
+}
+
+/**
+ * audit_file_mask - convert mask to permission string
+ * @buffer: buffer to write string to (NOT NULL)
+ * @mask: permission mask to convert
+ */
+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)
+{
+       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);
+}
+
+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 (profile_unconfined(profile) ||
+           !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
+               return 0;
+
+       aad(sa)->peer = &peer->label;
+       profile_match_signal(profile, peer->base.hname, aad(sa)->signal,
+                            &perms);
+       aa_apply_modes_to_perms(profile, &perms);
+       return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
+}
+
+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));
+}
+
+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);
+}
index e052eab..c5b99b9 100644 (file)
@@ -49,7 +49,7 @@ static void free_proxy(struct aa_proxy *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;
+               RCU_INIT_POINTER(proxy->label, (struct aa_label *)PROXY_POISON);
                kfree(proxy);
        }
 }
@@ -1450,9 +1450,11 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp)
  * 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)
+static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label,
+                                  int flags)
 {
-       if (label->hname && labels_ns(label) == ns)
+       if (label->hname && (!ns || labels_ns(label) == ns) &&
+           !(flags & ~FLAG_SHOW_MODE))
                return true;
 
        return false;
@@ -1495,7 +1497,7 @@ static int aa_profile_snxprint(char *str, size_t size, struct aa_ns *view,
                view = profiles_ns(profile);
 
        if (view != profile->ns &&
-           (!prev_ns || (prev_ns && *prev_ns != profile->ns))) {
+           (!prev_ns || (*prev_ns != profile->ns))) {
                if (prev_ns)
                        *prev_ns = profile->ns;
                ns_name = aa_ns_name(view, profile->ns,
@@ -1605,8 +1607,13 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns,
        AA_BUG(!str && size != 0);
        AA_BUG(!label);
 
-       if (!ns)
+       if (flags & FLAG_ABS_ROOT) {
+               ns = root_ns;
+               len = snprintf(str, size, "=");
+               update_for_len(total, len, size, str);
+       } else if (!ns) {
                ns = labels_ns(label);
+       }
 
        label_for_each(i, label, profile) {
                if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
@@ -1710,10 +1717,8 @@ void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns,
        AA_BUG(!ab);
        AA_BUG(!label);
 
-       if (!ns)
-               ns = labels_ns(label);
-
-       if (!use_label_hname(ns, label) || display_mode(ns, label, flags)) {
+       if (!use_label_hname(ns, label, flags) ||
+           display_mode(ns, label, flags)) {
                len  = aa_label_asxprint(&name, ns, label, flags, gfp);
                if (len == -1) {
                        AA_DEBUG("label print error");
@@ -1738,10 +1743,7 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
        AA_BUG(!f);
        AA_BUG(!label);
 
-       if (!ns)
-               ns = labels_ns(label);
-
-       if (!use_label_hname(ns, label)) {
+       if (!use_label_hname(ns, label, flags)) {
                char *str;
                int len;
 
@@ -1764,10 +1766,7 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
 {
        AA_BUG(!label);
 
-       if (!ns)
-               ns = labels_ns(label);
-
-       if (!use_label_hname(ns, label)) {
+       if (!use_label_hname(ns, label, flags)) {
                char *str;
                int len;
 
@@ -1874,6 +1873,9 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
                if (*str == '&')
                        str++;
        }
+       if (*str == '=')
+               base = &root_ns->unconfined->label;
+
        error = vec_setup(profile, vec, len, gfp);
        if (error)
                return ERR_PTR(error);
index 08ca26b..8818621 100644 (file)
@@ -211,7 +211,8 @@ void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask)
        *str = '\0';
 }
 
-void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask)
+void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
+                        u32 mask)
 {
        const char *fmt = "%s";
        unsigned int i, perm = 1;
@@ -229,7 +230,7 @@ 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)
+                       u32 chrsmask, const char * const *names, u32 namesmask)
 {
        char str[33];
 
index 7a82c0f..72b915d 100644 (file)
 #include "include/context.h"
 #include "include/file.h"
 #include "include/ipc.h"
+#include "include/net.h"
 #include "include/path.h"
 #include "include/label.h"
 #include "include/policy.h"
 #include "include/policy_ns.h"
 #include "include/procattr.h"
+#include "include/mount.h"
 
 /* Flag indicating whether initialization completed */
 int apparmor_initialized;
@@ -511,6 +513,65 @@ 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, const 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 = __begin_current_label_crit_section();
+       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);
+       }
+       __end_current_label_crit_section(label);
+
+       return error;
+}
+
+static int apparmor_sb_umount(struct vfsmount *mnt, int flags)
+{
+       struct aa_label *label;
+       int error = 0;
+
+       label = __begin_current_label_crit_section();
+       if (!unconfined(label))
+               error = aa_umount(label, mnt, flags);
+       __end_current_label_crit_section(label);
+
+       return error;
+}
+
+static int apparmor_sb_pivotroot(const struct path *old_path,
+                                const 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)
 {
@@ -656,12 +717,398 @@ static int apparmor_task_setrlimit(struct task_struct *task,
        return error;
 }
 
+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 = __begin_current_label_crit_section();
+       tl = aa_get_task_label(target);
+       error = aa_may_signal(cl, tl, sig);
+       aa_put_label(tl);
+       __end_current_label_crit_section(cl);
+
+       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;
+
+       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 int aa_sock_create_perm(struct aa_label *label, int family, int type,
+                              int protocol)
+{
+       AA_BUG(!label);
+       AA_BUG(in_interrupt());
+
+       return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type,
+                         protocol);
+}
+
+
+/**
+ * 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 = begin_current_label_crit_section();
+       if (!(kern || unconfined(label)))
+               error = aa_sock_create_perm(label, family, type, protocol);
+       end_current_label_crit_section(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)
+{
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       AA_BUG(!address);
+       AA_BUG(in_interrupt());
+
+       return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk);
+}
+
+/**
+ * apparmor_socket_connect - check perms before connecting @sock to @address
+ */
+static int apparmor_socket_connect(struct socket *sock,
+                                  struct sockaddr *address, int addrlen)
+{
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       AA_BUG(!address);
+       AA_BUG(in_interrupt());
+
+       return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk);
+}
+
+/**
+ * apparmor_socket_list - check perms before allowing listen
+ */
+static int apparmor_socket_listen(struct socket *sock, int backlog)
+{
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       AA_BUG(in_interrupt());
+
+       return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk);
+}
+
+/**
+ * apparmor_socket_accept - check perms before accepting a new connection.
+ *
+ * Note: while @newsock is created and has some information, the accept
+ *       has not been done.
+ */
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
+{
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       AA_BUG(!newsock);
+       AA_BUG(in_interrupt());
+
+       return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk);
+}
+
+static 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);
+       AA_BUG(in_interrupt());
+
+       return aa_sk_perm(op, request, sock->sk);
+}
+
+/**
+ * apparmor_socket_sendmsg - check perms before sending msg to another socket
+ */
+static int apparmor_socket_sendmsg(struct socket *sock,
+                                  struct msghdr *msg, int size)
+{
+       return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size);
+}
+
+/**
+ * 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);
+}
+
+/* revaliation, get/set attr, shutdown */
+static int aa_sock_perm(const char *op, u32 request, struct socket *sock)
+{
+       AA_BUG(!sock);
+       AA_BUG(!sock->sk);
+       AA_BUG(in_interrupt());
+
+       return aa_sk_perm(op, request, sock->sk);
+}
+
+/**
+ * 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);
+}
+
+/* revaliation, get/set attr, opt */
+static 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 aa_sk_perm(op, request, sock->sk);
+}
+
+/**
+ * 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 may be 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)
+{
+       return 0;
+}
+
+
+static struct aa_label *sk_peer_label(struct sock *sk)
+{
+       struct aa_sk_ctx *ctx = SK_CTX(sk);
+
+       if (ctx->peer)
+               return ctx->peer;
+
+       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 int len)
+{
+       char *name;
+       int slen, error = 0;
+       struct aa_label *label;
+       struct aa_label *peer;
+
+       label = begin_current_label_crit_section();
+       peer = sk_peer_label(sock->sk);
+       if (IS_ERR(peer)) {
+               error = PTR_ERR(peer);
+               goto done;
+       }
+       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);
+
+       }
+
+done:
+       end_current_label_crit_section(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 */
+       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 struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
        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(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),
@@ -686,6 +1133,30 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
        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(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),
@@ -696,6 +1167,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(bprm_committed_creds, apparmor_bprm_committed_creds),
 
        LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
+       LSM_HOOK_INIT(task_kill, apparmor_task_kill),
 };
 
 /*
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
new file mode 100644 (file)
index 0000000..82a64b5
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor mediation of files
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2017 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 * const mnt_info_table[] = {
+       "match succeeded",
+       "failed mntpnt match",
+       "failed srcname match",
+       "failed type match",
+       "failed flags match",
+       "failed data match"
+};
+
+/*
+ * Returns 0 on success else element that match failed in, this is the
+ * index into the mnt_info_table above
+ */
+static int do_match_mnt(struct aa_dfa *dfa, unsigned int start,
+                       const char *mntpnt, const char *devname,
+                       const char *type, unsigned long flags,
+                       void *data, bool binary, struct 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;
+}
+
+
+static int path_flags(struct aa_profile *profile, const 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);
+}
+
+/**
+ * match_mnt_path_str - handle path matching for mount
+ * @profile: the confining profile
+ * @mntpath: for the mntpnt (NOT NULL)
+ * @buffer: buffer to be used to lookup mntpath
+ * @devnme: string for the devname/src_name (MAY BE NULL OR ERRPTR)
+ * @type: string for the dev type (MAYBE NULL)
+ * @flags: mount flags to match
+ * @data: fs mount data (MAYBE NULL)
+ * @binary: whether @data is binary
+ * @devinfo: error str if (IS_ERR(@devname))
+ *
+ * Returns: 0 on success else error
+ */
+static int match_mnt_path_str(struct aa_profile *profile,
+                             const struct path *mntpath, char *buffer,
+                             const char *devname, const char *type,
+                             unsigned long flags, void *data, bool binary,
+                             const char *devinfo)
+{
+       struct aa_perms perms = { };
+       const char *mntpnt = NULL, *info = NULL;
+       int pos, error;
+
+       AA_BUG(!profile);
+       AA_BUG(!mntpath);
+       AA_BUG(!buffer);
+
+       error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer,
+                            &mntpnt, &info, profile->disconnected);
+       if (error)
+               goto audit;
+       if (IS_ERR(devname)) {
+               error = PTR_ERR(devname);
+               devname = NULL;
+               info = devinfo;
+               goto audit;
+       }
+
+       error = -EACCES;
+       pos = do_match_mnt(profile->policy.dfa,
+                          profile->policy.start[AA_CLASS_MOUNT],
+                          mntpnt, devname, type, flags, data, binary, &perms);
+       if (pos) {
+               info = mnt_info_table[pos];
+               goto audit;
+       }
+       error = 0;
+
+audit:
+       return audit_mount(profile, OP_MOUNT, mntpnt, devname, type, NULL,
+                          flags, data, AA_MAY_MOUNT, &perms, info, error);
+}
+
+/**
+ * match_mnt - handle path matching for mount
+ * @profile: the confining profile
+ * @mntpath: for the mntpnt (NOT NULL)
+ * @buffer: buffer to be used to lookup mntpath
+ * @devpath: path devname/src_name (MAYBE NULL)
+ * @devbuffer: buffer to be used to lookup devname/src_name
+ * @type: string for the dev type (MAYBE NULL)
+ * @flags: mount flags to match
+ * @data: fs mount data (MAYBE NULL)
+ * @binary: whether @data is binary
+ *
+ * Returns: 0 on success else error
+ */
+static int match_mnt(struct aa_profile *profile, const struct path *path,
+                    char *buffer, struct path *devpath, char *devbuffer,
+                    const char *type, unsigned long flags, void *data,
+                    bool binary)
+{
+       const char *devname = NULL, *info = NULL;
+       int error = -EACCES;
+
+       AA_BUG(!profile);
+       AA_BUG(devpath && !devbuffer);
+
+       if (devpath) {
+               error = aa_path_name(devpath, path_flags(profile, devpath),
+                                    devbuffer, &devname, &info,
+                                    profile->disconnected);
+               if (error)
+                       devname = ERR_PTR(error);
+       }
+
+       return match_mnt_path_str(profile, path, buffer, devname, type, flags,
+                                 data, binary, info);
+}
+
+int aa_remount(struct aa_label *label, const struct path *path,
+              unsigned long flags, void *data)
+{
+       struct aa_profile *profile;
+       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 = fn_for_each_confined(label, profile,
+                       match_mnt(profile, path, buffer, NULL, NULL, NULL,
+                                 flags, data, binary));
+       put_buffers(buffer);
+
+       return error;
+}
+
+int aa_bind_mount(struct aa_label *label, const struct path *path,
+                 const char *dev_name, unsigned long flags)
+{
+       struct aa_profile *profile;
+       char *buffer = NULL, *old_buffer = 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 = fn_for_each_confined(label, profile,
+                       match_mnt(profile, path, buffer, &old_path, old_buffer,
+                                 NULL, flags, NULL, false));
+       put_buffers(buffer, old_buffer);
+       path_put(&old_path);
+
+       return error;
+}
+
+int aa_mount_change_type(struct aa_label *label, const struct path *path,
+                        unsigned long flags)
+{
+       struct aa_profile *profile;
+       char *buffer = 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 = fn_for_each_confined(label, profile,
+                       match_mnt(profile, path, buffer, NULL, NULL, NULL,
+                                 flags, NULL, false));
+       put_buffers(buffer);
+
+       return error;
+}
+
+int aa_move_mount(struct aa_label *label, const struct path *path,
+                 const char *orig_name)
+{
+       struct aa_profile *profile;
+       char *buffer = NULL, *old_buffer = 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 = fn_for_each_confined(label, profile,
+                       match_mnt(profile, path, buffer, &old_path, old_buffer,
+                                 NULL, MS_MOVE, NULL, false));
+       put_buffers(buffer, old_buffer);
+       path_put(&old_path);
+
+       return error;
+}
+
+int aa_new_mount(struct aa_label *label, const char *dev_name,
+                const struct path *path, const char *type, unsigned long flags,
+                void *data)
+{
+       struct aa_profile *profile;
+       char *buffer = NULL, *dev_buffer = NULL;
+       bool binary = true;
+       int error;
+       int requires_dev = 0;
+       struct path tmp_path, *dev_path = NULL;
+
+       AA_BUG(!label);
+       AA_BUG(!path);
+
+       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, &tmp_path);
+                       if (error)
+                               return error;
+                       dev_path = &tmp_path;
+               }
+       }
+
+       get_buffers(buffer, dev_buffer);
+       if (dev_path) {
+               error = fn_for_each_confined(label, profile,
+                       match_mnt(profile, path, buffer, dev_path, dev_buffer,
+                                 type, flags, data, binary));
+       } else {
+               error = fn_for_each_confined(label, profile,
+                       match_mnt_path_str(profile, path, buffer, dev_name,
+                                          type, flags, data, binary, NULL));
+       }
+       put_buffers(buffer, dev_buffer);
+       if (dev_path)
+               path_put(dev_path);
+
+       return error;
+}
+
+static int profile_umount(struct aa_profile *profile, struct path *path,
+                         char *buffer)
+{
+       struct aa_perms perms = { };
+       const char *name = NULL, *info = NULL;
+       unsigned int state;
+       int error;
+
+       AA_BUG(!profile);
+       AA_BUG(!path);
+
+       error = aa_path_name(path, path_flags(profile, path), buffer, &name,
+                            &info, profile->disconnected);
+       if (error)
+               goto audit;
+
+       state = aa_dfa_match(profile->policy.dfa,
+                            profile->policy.start[AA_CLASS_MOUNT],
+                            name);
+       perms = compute_mnt_perms(profile->policy.dfa, state);
+       if (AA_MAY_UMOUNT & ~perms.allow)
+               error = -EACCES;
+
+audit:
+       return audit_mount(profile, OP_UMOUNT, name, NULL, NULL, NULL, 0, NULL,
+                          AA_MAY_UMOUNT, &perms, info, error);
+}
+
+int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags)
+{
+       struct aa_profile *profile;
+       char *buffer = NULL;
+       int error;
+       struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
+
+       AA_BUG(!label);
+       AA_BUG(!mnt);
+
+       get_buffers(buffer);
+       error = fn_for_each_confined(label, profile,
+                       profile_umount(profile, &path, buffer));
+       put_buffers(buffer);
+
+       return error;
+}
+
+/* helper fn for transition on pivotroot
+ *
+ * Returns: label for transition or ERR_PTR. Does not return NULL
+ */
+static struct aa_label *build_pivotroot(struct aa_profile *profile,
+                                       const struct path *new_path,
+                                       char *new_buffer,
+                                       const struct path *old_path,
+                                       char *old_buffer)
+{
+       const char *old_name, *new_name = NULL, *info = NULL;
+       const char *trans_name = NULL;
+       struct aa_perms perms = { };
+       unsigned int state;
+       int error;
+
+       AA_BUG(!profile);
+       AA_BUG(!new_path);
+       AA_BUG(!old_path);
+
+       if (profile_unconfined(profile))
+               return aa_get_newest_label(&profile->label);
+
+       error = aa_path_name(old_path, path_flags(profile, old_path),
+                            old_buffer, &old_name, &info,
+                            profile->disconnected);
+       if (error)
+               goto audit;
+       error = aa_path_name(new_path, path_flags(profile, new_path),
+                            new_buffer, &new_name, &info,
+                            profile->disconnected);
+       if (error)
+               goto audit;
+
+       error = -EACCES;
+       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)
+               error = 0;
+
+audit:
+       error = audit_mount(profile, OP_PIVOTROOT, new_name, old_name,
+                           NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT,
+                           &perms, info, error);
+       if (error)
+               return ERR_PTR(error);
+
+       return aa_get_newest_label(&profile->label);
+}
+
+int aa_pivotroot(struct aa_label *label, const struct path *old_path,
+                const struct path *new_path)
+{
+       struct aa_profile *profile;
+       struct aa_label *target = NULL;
+       char *old_buffer = NULL, *new_buffer = NULL, *info = NULL;
+       int error;
+
+       AA_BUG(!label);
+       AA_BUG(!old_path);
+       AA_BUG(!new_path);
+
+       get_buffers(old_buffer, new_buffer);
+       target = fn_label_build(label, profile, GFP_ATOMIC,
+                       build_pivotroot(profile, new_path, new_buffer,
+                                       old_path, old_buffer));
+       if (!target) {
+               info = "label build failed";
+               error = -ENOMEM;
+               goto fail;
+       } else if (!IS_ERR(target)) {
+               error = aa_replace_current_label(target);
+               if (error) {
+                       /* TODO: audit target */
+                       aa_put_label(target);
+                       goto out;
+               }
+       } else
+               /* already audited error */
+               error = PTR_ERR(target);
+out:
+       put_buffers(old_buffer, new_buffer);
+
+       return error;
+
+fail:
+       /* TODO: add back in auditing of new_name and old_name */
+       error = fn_for_each(label, profile,
+                       audit_mount(profile, OP_PIVOTROOT, NULL /*new_name */,
+                                   NULL /* 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..33d5443
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor network mediation
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2017 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#include "include/apparmor.h"
+#include "include/audit.h"
+#include "include/context.h"
+#include "include/label.h"
+#include "include/net.h"
+#include "include/policy.h"
+
+#include "net_names.h"
+
+
+struct aa_sfs_entry aa_sfs_entry_network[] = {
+       AA_SFS_FILE_STRING("af_mask",   AA_SFS_AF_MASK),
+       { }
+};
+
+static const char * const 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",
+};
+
+
+/* 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 (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);
+}
+
+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));
+}
+
+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 = begin_current_label_crit_section();
+       error = aa_label_sk_perm(label, op, request, sk);
+       end_current_label_crit_section(label);
+
+       return error;
+}
+
+
+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 aa_label_sk_perm(label, op, request, sock->sk);
+}
index 244ea4a..4243b0c 100644 (file)
@@ -289,85 +289,6 @@ fail:
        return NULL;
 }
 
-/**
- * aa_new_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
- *
- * 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
- * not in use.
- *
- * Returns: new refcounted profile else NULL on failure
- */
-struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
-                                      const char *base, gfp_t gfp)
-{
-       struct aa_profile *profile;
-       char *name;
-
-       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)
-               return NULL;
-       sprintf(name, "%s//null-%x", parent->base.hname,
-               atomic_inc_return(&parent->ns->uniq_null));
-
-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->label.flags |= FLAG_NULL;
-       if (hat)
-               profile->label.flags |= FLAG_HAT;
-       profile->path_flags = parent->path_flags;
-
-       /* released on free_profile */
-       rcu_assign_pointer(profile->parent, aa_get_profile(parent));
-       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);
-       __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;
-}
-
 /* TODO: profile accounting - setup in remove */
 
 /**
@@ -558,6 +479,93 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
        return profile;
 }
 
+/**
+ * aa_new_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
+ *
+ * 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
+ * not in use.
+ *
+ * Returns: new refcounted profile else NULL on failure
+ */
+struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
+                                      const char *base, gfp_t gfp)
+{
+       struct aa_profile *p, *profile;
+       const char *bname;
+       char *name;
+
+       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)
+               return NULL;
+       sprintf(name, "%s//null-%x", parent->base.hname,
+               atomic_inc_return(&parent->ns->uniq_null));
+
+name:
+       /* lookup to see if this is a dup creation */
+       bname = basename(name);
+       profile = aa_find_child(parent, bname);
+       if (profile)
+               goto out;
+
+       profile = aa_alloc_profile(name, NULL, gfp);
+       if (!profile)
+               goto fail;
+
+       profile->mode = APPARMOR_COMPLAIN;
+       profile->label.flags |= FLAG_NULL;
+       if (hat)
+               profile->label.flags |= FLAG_HAT;
+       profile->path_flags = parent->path_flags;
+
+       /* released on free_profile */
+       rcu_assign_pointer(profile->parent, aa_get_profile(parent));
+       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);
+       p = __find_child(&parent->base.profiles, bname);
+       if (p) {
+               aa_free_profile(profile);
+               profile = aa_get_profile(p);
+       } else {
+               __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;
+}
+
 /**
  * replacement_allowed - test to see if replacement is allowed
  * @profile: profile to test if it can be replaced  (MAYBE NULL)
index 351d3ba..62a3589 100644 (file)
@@ -112,6 +112,8 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name)
        ns->unconfined->label.flags |= FLAG_IX_ON_NAME_ERROR |
                FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED;
        ns->unconfined->mode = APPARMOR_UNCONFINED;
+       ns->unconfined->file.dfa = aa_get_dfa(nulldfa);
+       ns->unconfined->policy.dfa = aa_get_dfa(nulldfa);
 
        /* ns and ns->unconfined share ns->unconfined refcount */
        ns->unconfined->ns = ns;
index c600f4d..5a2aec3 100644 (file)
@@ -85,9 +85,9 @@ static void audit_cb(struct audit_buffer *ab, void *va)
                audit_log_format(ab, " ns=");
                audit_log_untrustedstring(ab, aad(sa)->iface.ns);
        }
-       if (aad(sa)->iface.name) {
+       if (aad(sa)->name) {
                audit_log_format(ab, " name=");
-               audit_log_untrustedstring(ab, aad(sa)->iface.name);
+               audit_log_untrustedstring(ab, aad(sa)->name);
        }
        if (aad(sa)->iface.pos)
                audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos);
@@ -114,9 +114,9 @@ static int audit_iface(struct aa_profile *new, const char *ns_name,
                aad(&sa)->iface.pos = e->pos - e->start;
        aad(&sa)->iface.ns = ns_name;
        if (new)
-               aad(&sa)->iface.name = new->base.hname;
+               aad(&sa)->name = new->base.hname;
        else
-               aad(&sa)->iface.name = name;
+               aad(&sa)->name = name;
        aad(&sa)->info = info;
        aad(&sa)->error = error;
 
@@ -275,6 +275,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((__le16 *) 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)) {
@@ -448,7 +461,7 @@ fail:
  */
 static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
 {
-       void *pos = e->pos;
+       void *saved_pos = e->pos;
 
        /* exec table is optional */
        if (unpack_nameX(e, AA_STRUCT, "xtable")) {
@@ -511,7 +524,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
 
 fail:
        aa_free_domain_entries(&profile->file.trans);
-       e->pos = pos;
+       e->pos = saved_pos;
        return 0;
 }
 
@@ -583,7 +596,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
 {
        struct aa_profile *profile = NULL;
        const char *tmpname, *tmpns = NULL, *name = NULL;
-       size_t ns_len;
+       const char *info = "failed to unpack profile";
+       size_t size = 0, ns_len;
        struct rhashtable_params params = { 0 };
        char *key = NULL;
        struct aa_data *data;
@@ -604,8 +618,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len);
        if (tmpns) {
                *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
-               if (!*ns_name)
+               if (!*ns_name) {
+                       info = "out of memory";
                        goto fail;
+               }
                name = tmpname;
        }
 
@@ -624,12 +640,15 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        if (IS_ERR(profile->xmatch)) {
                error = PTR_ERR(profile->xmatch);
                profile->xmatch = NULL;
+               info = "bad xmatch";
                goto fail;
        }
        /* xmatch_len is not optional if xmatch is set */
        if (profile->xmatch) {
-               if (!unpack_u32(e, &tmp, NULL))
+               if (!unpack_u32(e, &tmp, NULL)) {
+                       info = "missing xmatch len";
                        goto fail;
+               }
                profile->xmatch_len = tmp;
        }
 
@@ -637,8 +656,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        (void) unpack_str(e, &profile->disconnected, "disconnected");
 
        /* per profile debug flags (complain, audit) */
-       if (!unpack_nameX(e, AA_STRUCT, "flags"))
+       if (!unpack_nameX(e, AA_STRUCT, "flags")) {
+               info = "profile missing flags";
                goto fail;
+       }
+       info = "failed to unpack profile flags";
        if (!unpack_u32(e, &tmp, NULL))
                goto fail;
        if (tmp & PACKED_FLAG_HAT)
@@ -667,6 +689,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                /* set a default value if path_flags field is not present */
                profile->path_flags = PATH_MEDIATE_DELETED;
 
+       info = "failed to unpack profile capabilities";
        if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL))
                goto fail;
        if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL))
@@ -676,6 +699,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        if (!unpack_u32(e, &tmpcap.cap[0], NULL))
                goto fail;
 
+       info = "failed to unpack upper profile capabilities";
        if (unpack_nameX(e, AA_STRUCT, "caps64")) {
                /* optional upper half of 64 bit caps */
                if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL))
@@ -690,6 +714,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                        goto fail;
        }
 
+       info = "failed to unpack extended profile capabilities";
        if (unpack_nameX(e, AA_STRUCT, "capsx")) {
                /* optional extended caps mediation mask */
                if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL))
@@ -700,11 +725,46 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                        goto fail;
        }
 
-       if (!unpack_rlimits(e, profile))
+       if (!unpack_rlimits(e, profile)) {
+               info = "failed to unpack profile rlimits";
                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_LT(e->version, v7)) {
+               /* pre v7 policy always allowed these */
+               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 */
+               info = "failed to unpack policydb";
                profile->policy.dfa = unpack_dfa(e);
                if (IS_ERR(profile->policy.dfa)) {
                        error = PTR_ERR(profile->policy.dfa);
@@ -734,6 +794,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        if (IS_ERR(profile->file.dfa)) {
                error = PTR_ERR(profile->file.dfa);
                profile->file.dfa = NULL;
+               info = "failed to unpack profile file rules";
                goto fail;
        } else if (profile->file.dfa) {
                if (!unpack_u32(e, &profile->file.start, "dfa_start"))
@@ -746,10 +807,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        } else
                profile->file.dfa = aa_get_dfa(nulldfa);
 
-       if (!unpack_trans_table(e, profile))
+       if (!unpack_trans_table(e, profile)) {
+               info = "failed to unpack profile transition table";
                goto fail;
+       }
 
        if (unpack_nameX(e, AA_STRUCT, "data")) {
+               info = "out of memory";
                profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
                if (!profile->data)
                        goto fail;
@@ -761,8 +825,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                params.hashfn = strhash;
                params.obj_cmpfn = datacmp;
 
-               if (rhashtable_init(profile->data, &params))
+               if (rhashtable_init(profile->data, &params)) {
+                       info = "failed to init key, value hash table";
                        goto fail;
+               }
 
                while (unpack_strdup(e, &key, NULL)) {
                        data = kzalloc(sizeof(*data), GFP_KERNEL);
@@ -784,12 +850,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                                               profile->data->p);
                }
 
-               if (!unpack_nameX(e, AA_STRUCTEND, NULL))
+               if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
+                       info = "failed to unpack end of key, value data table";
                        goto fail;
+               }
        }
 
-       if (!unpack_nameX(e, AA_STRUCTEND, NULL))
+       if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
+               info = "failed to unpack end of profile";
                goto fail;
+       }
 
        return profile;
 
@@ -798,8 +868,7 @@ fail:
                name = NULL;
        else if (!name)
                name = "unknown";
-       audit_iface(profile, NULL, name, "failed to unpack profile", e,
-                   error);
+       audit_iface(profile, NULL, name, info, e, error);
        aa_free_profile(profile);
 
        return ERR_PTR(error);
@@ -832,7 +901,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
         * if not specified use previous version
         * Mask off everything that is not kernel abi version
         */
-       if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) {
+       if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v7)) {
                audit_iface(NULL, NULL, NULL, "unsupported interface version",
                            e, error);
                return error;