Merge tag 'keys-next-20140725' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowe...
authorJames Morris <james.l.morris@oracle.com>
Sat, 26 Jul 2014 00:38:59 +0000 (10:38 +1000)
committerJames Morris <james.l.morris@oracle.com>
Sat, 26 Jul 2014 00:38:59 +0000 (10:38 +1000)
40 files changed:
MAINTAINERS
arch/Kconfig
arch/arm/include/uapi/asm/unistd.h
arch/arm/kernel/calls.S
arch/mips/include/uapi/asm/unistd.h
arch/mips/kernel/scall32-o32.S
arch/mips/kernel/scall64-64.S
arch/mips/kernel/scall64-n32.S
arch/mips/kernel/scall64-o32.S
arch/x86/syscalls/syscall_32.tbl
arch/x86/syscalls/syscall_64.tbl
fs/exec.c
fs/proc/array.c
include/linux/capability.h
include/linux/sched.h
include/linux/seccomp.h
include/linux/syscalls.h
include/uapi/asm-generic/unistd.h
include/uapi/linux/seccomp.h
kernel/audit.c
kernel/capability.c
kernel/fork.c
kernel/seccomp.c
kernel/sys.c
kernel/sys_ni.c
scripts/selinux/genheaders/Makefile
scripts/selinux/mdp/Makefile
security/apparmor/domain.c
security/commoncap.c
security/selinux/hooks.c
security/selinux/include/netif.h
security/selinux/include/netnode.h
security/selinux/include/netport.h
security/selinux/include/security.h
security/selinux/netif.c
security/selinux/netnode.c
security/selinux/netport.c
security/selinux/ss/conditional.c
security/selinux/ss/policydb.c
security/selinux/ss/services.c

index e31c87474739329ec0b2458ad26a40f7251f3874..55762cba851644205f18e535499ee88efbcab0a8 100644 (file)
@@ -7953,6 +7953,16 @@ S:       Maintained
 F:     drivers/mmc/host/sdhci.*
 F:     drivers/mmc/host/sdhci-pltfm.[ch]
 
+SECURE COMPUTING
+M:     Kees Cook <keescook@chromium.org>
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git seccomp
+S:     Supported
+F:     kernel/seccomp.c
+F:     include/uapi/linux/seccomp.h
+F:     include/linux/seccomp.h
+K:     \bsecure_computing
+K:     \bTIF_SECCOMP\b
+
 SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF)
 M:     Anton Vorontsov <anton@enomsg.org>
 L:     linuxppc-dev@lists.ozlabs.org
index 97ff872c7accf99b3779314718fe31fd60ee1d42..0eae9df35b884190f7f988702c64158b9a66cda0 100644 (file)
@@ -321,6 +321,7 @@ config HAVE_ARCH_SECCOMP_FILTER
          - secure_computing is called from a ptrace_event()-safe context
          - secure_computing return value is checked and a return value of -1
            results in the system call being skipped immediately.
+         - seccomp syscall wired up
 
 config SECCOMP_FILTER
        def_bool y
index ba94446c72d9127633de59545a3691390ecdfc5d..e21b4a069701345c3663add69307ca161e41399c 100644 (file)
 #define __NR_sched_setattr             (__NR_SYSCALL_BASE+380)
 #define __NR_sched_getattr             (__NR_SYSCALL_BASE+381)
 #define __NR_renameat2                 (__NR_SYSCALL_BASE+382)
+#define __NR_seccomp                   (__NR_SYSCALL_BASE+383)
 
 /*
  * This may need to be greater than __NR_last_syscall+1 in order to
index 8f51bdcdacbbf6675933f38fb595adbdc825f4c2..bea85f97f3633b48eb307a154910b563f40187c6 100644 (file)
 /* 380 */      CALL(sys_sched_setattr)
                CALL(sys_sched_getattr)
                CALL(sys_renameat2)
+               CALL(sys_seccomp)
 #ifndef syscalls_counted
 .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
 #define syscalls_counted
index 5805414777e0c66abd878a3b798c56809e0e57c5..9bc13eaf9d6776e8e9e2dd2dbde8401784dfb45c 100644 (file)
 #define __NR_sched_setattr             (__NR_Linux + 349)
 #define __NR_sched_getattr             (__NR_Linux + 350)
 #define __NR_renameat2                 (__NR_Linux + 351)
+#define __NR_seccomp                   (__NR_Linux + 352)
 
 /*
  * Offset of the last Linux o32 flavoured syscall
  */
-#define __NR_Linux_syscalls            351
+#define __NR_Linux_syscalls            352
 
 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
 
 #define __NR_O32_Linux                 4000
-#define __NR_O32_Linux_syscalls                351
+#define __NR_O32_Linux_syscalls                352
 
 #if _MIPS_SIM == _MIPS_SIM_ABI64
 
 #define __NR_sched_setattr             (__NR_Linux + 309)
 #define __NR_sched_getattr             (__NR_Linux + 310)
 #define __NR_renameat2                 (__NR_Linux + 311)
+#define __NR_seccomp                   (__NR_Linux + 312)
 
 /*
  * Offset of the last Linux 64-bit flavoured syscall
  */
-#define __NR_Linux_syscalls            311
+#define __NR_Linux_syscalls            312
 
 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */
 
 #define __NR_64_Linux                  5000
-#define __NR_64_Linux_syscalls         311
+#define __NR_64_Linux_syscalls         312
 
 #if _MIPS_SIM == _MIPS_SIM_NABI32
 
 #define __NR_sched_setattr             (__NR_Linux + 313)
 #define __NR_sched_getattr             (__NR_Linux + 314)
 #define __NR_renameat2                 (__NR_Linux + 315)
+#define __NR_seccomp                   (__NR_Linux + 316)
 
 /*
  * Offset of the last N32 flavoured syscall
  */
-#define __NR_Linux_syscalls            315
+#define __NR_Linux_syscalls            316
 
 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */
 
 #define __NR_N32_Linux                 6000
-#define __NR_N32_Linux_syscalls                315
+#define __NR_N32_Linux_syscalls                316
 
 #endif /* _UAPI_ASM_UNISTD_H */
index 3245474f19d5cdec5563a3841ecdcd3760f98e17..ab02d14f1b5cd65e3689f58c4ee79facccfbc92b 100644 (file)
@@ -578,3 +578,4 @@ EXPORT(sys_call_table)
        PTR     sys_sched_setattr
        PTR     sys_sched_getattr               /* 4350 */
        PTR     sys_renameat2
+       PTR     sys_seccomp
index be2fedd4ae33193937010b376e62c7d9e327022d..010dccf128ec25bd192cc6195d3ccc5b72ee305b 100644 (file)
@@ -431,4 +431,5 @@ EXPORT(sys_call_table)
        PTR     sys_sched_setattr
        PTR     sys_sched_getattr               /* 5310 */
        PTR     sys_renameat2
+       PTR     sys_seccomp
        .size   sys_call_table,.-sys_call_table
index c1dbcda4b816844cc64821aa8d64a79d5c9a38f3..c3b3b6525df567cbb6276e24cfd905d7a1cc256b 100644 (file)
@@ -424,4 +424,5 @@ EXPORT(sysn32_call_table)
        PTR     sys_sched_setattr
        PTR     sys_sched_getattr
        PTR     sys_renameat2                   /* 6315 */
+       PTR     sys_seccomp
        .size   sysn32_call_table,.-sysn32_call_table
index f1343ccd7ed7e58d14563c4413ede6517ff60853..bb1550b1f5012bba69c470ba7e57dd1f9bea03e0 100644 (file)
@@ -557,4 +557,5 @@ EXPORT(sys32_call_table)
        PTR     sys_sched_setattr
        PTR     sys_sched_getattr               /* 4350 */
        PTR     sys_renameat2
+       PTR     sys_seccomp
        .size   sys32_call_table,.-sys32_call_table
index d6b867921612f69f8800fbf309652ade49170a89..7527eac241228ea9fe436cd763da67c0f12ae448 100644 (file)
 351    i386    sched_setattr           sys_sched_setattr
 352    i386    sched_getattr           sys_sched_getattr
 353    i386    renameat2               sys_renameat2
+354    i386    seccomp                 sys_seccomp
index ec255a1646d2da7629c79cd2e0014d341c3b6259..16272a6c12b740c85efaf762382d7b925b69e373 100644 (file)
 314    common  sched_setattr           sys_sched_setattr
 315    common  sched_getattr           sys_sched_getattr
 316    common  renameat2               sys_renameat2
+317    common  seccomp                 sys_seccomp
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
index a3d33fe592d6d95619506b6f4aac33621284d3f8..ab1f1200ce5d8b45534e0a3e0da47045116dec4e 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1216,7 +1216,7 @@ EXPORT_SYMBOL(install_exec_creds);
 /*
  * determine how safe it is to execute the proposed program
  * - the caller must hold ->cred_guard_mutex to protect against
- *   PTRACE_ATTACH
+ *   PTRACE_ATTACH or seccomp thread-sync
  */
 static void check_unsafe_exec(struct linux_binprm *bprm)
 {
@@ -1234,7 +1234,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
         * This isn't strictly necessary, but it makes it harder for LSMs to
         * mess up.
         */
-       if (current->no_new_privs)
+       if (task_no_new_privs(current))
                bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS;
 
        t = p;
@@ -1272,7 +1272,7 @@ int prepare_binprm(struct linux_binprm *bprm)
        bprm->cred->egid = current_egid();
 
        if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) &&
-           !current->no_new_privs &&
+           !task_no_new_privs(current) &&
            kuid_has_mapping(bprm->cred->user_ns, inode->i_uid) &&
            kgid_has_mapping(bprm->cred->user_ns, inode->i_gid)) {
                /* Set-uid? */
index 64db2bceac59e7634976235bcddeada98e592b1c..3e1290b0492e27b5e5f625262c1b63af1de0bec1 100644 (file)
@@ -297,15 +297,11 @@ static void render_cap_t(struct seq_file *m, const char *header,
        seq_puts(m, header);
        CAP_FOR_EACH_U32(__capi) {
                seq_printf(m, "%08x",
-                          a->cap[(_KERNEL_CAPABILITY_U32S-1) - __capi]);
+                          a->cap[CAP_LAST_U32 - __capi]);
        }
        seq_putc(m, '\n');
 }
 
-/* Remove non-existent capabilities */
-#define NORM_CAPS(v) (v.cap[CAP_TO_INDEX(CAP_LAST_CAP)] &= \
-                               CAP_TO_MASK(CAP_LAST_CAP + 1) - 1)
-
 static inline void task_cap(struct seq_file *m, struct task_struct *p)
 {
        const struct cred *cred;
@@ -319,11 +315,6 @@ static inline void task_cap(struct seq_file *m, struct task_struct *p)
        cap_bset        = cred->cap_bset;
        rcu_read_unlock();
 
-       NORM_CAPS(cap_inheritable);
-       NORM_CAPS(cap_permitted);
-       NORM_CAPS(cap_effective);
-       NORM_CAPS(cap_bset);
-
        render_cap_t(m, "CapInh:\t", &cap_inheritable);
        render_cap_t(m, "CapPrm:\t", &cap_permitted);
        render_cap_t(m, "CapEff:\t", &cap_effective);
index 84b13ad67c1cc4acdce9aff94be070f13fcce5a1..aa93e5ef594c15eb5a733c005dd7c5590e56b00e 100644 (file)
@@ -78,8 +78,11 @@ extern const kernel_cap_t __cap_init_eff_set;
 # error Fix up hand-coded capability macro initializers
 #else /* HAND-CODED capability initializers */
 
+#define CAP_LAST_U32                   ((_KERNEL_CAPABILITY_U32S) - 1)
+#define CAP_LAST_U32_VALID_MASK                (CAP_TO_MASK(CAP_LAST_CAP + 1) -1)
+
 # define CAP_EMPTY_SET    ((kernel_cap_t){{ 0, 0 }})
-# define CAP_FULL_SET     ((kernel_cap_t){{ ~0, ~0 }})
+# define CAP_FULL_SET     ((kernel_cap_t){{ ~0, CAP_LAST_U32_VALID_MASK }})
 # define CAP_FS_SET       ((kernel_cap_t){{ CAP_FS_MASK_B0 \
                                    | CAP_TO_MASK(CAP_LINUX_IMMUTABLE), \
                                    CAP_FS_MASK_B1 } })
index 306f4f0c987a006f43f520413f7de3a780f98a23..0fd19055bb6450119467837f18924c2c848eafb1 100644 (file)
@@ -1307,13 +1307,12 @@ struct task_struct {
                                 * execve */
        unsigned in_iowait:1;
 
-       /* task may not gain privileges */
-       unsigned no_new_privs:1;
-
        /* Revert to default priority/policy when forking */
        unsigned sched_reset_on_fork:1;
        unsigned sched_contributes_to_load:1;
 
+       unsigned long atomic_flags; /* Flags needing atomic access. */
+
        pid_t pid;
        pid_t tgid;
 
@@ -1967,6 +1966,19 @@ static inline void memalloc_noio_restore(unsigned int flags)
        current->flags = (current->flags & ~PF_MEMALLOC_NOIO) | flags;
 }
 
+/* Per-process atomic flags. */
+#define PFA_NO_NEW_PRIVS 0x00000001    /* May not gain new privileges. */
+
+static inline bool task_no_new_privs(struct task_struct *p)
+{
+       return test_bit(PFA_NO_NEW_PRIVS, &p->atomic_flags);
+}
+
+static inline void task_set_no_new_privs(struct task_struct *p)
+{
+       set_bit(PFA_NO_NEW_PRIVS, &p->atomic_flags);
+}
+
 /*
  * task->jobctl flags
  */
index 4054b0994071cada2d95bdbe0964a3b22cfe2590..5d586a45a319f20cc1c91412428e81beb0517e60 100644 (file)
@@ -3,6 +3,8 @@
 
 #include <uapi/linux/seccomp.h>
 
+#define SECCOMP_FILTER_FLAG_MASK       (SECCOMP_FILTER_FLAG_TSYNC)
+
 #ifdef CONFIG_SECCOMP
 
 #include <linux/thread_info.h>
@@ -14,11 +16,11 @@ struct seccomp_filter;
  *
  * @mode:  indicates one of the valid values above for controlled
  *         system calls available to a process.
- * @filter: The metadata and ruleset for determining what system calls
- *          are allowed for a task.
+ * @filter: must always point to a valid seccomp-filter or NULL as it is
+ *          accessed without locking during system call entry.
  *
  *          @filter must only be accessed from the context of current as there
- *          is no locking.
+ *          is no read locking.
  */
 struct seccomp {
        int mode;
index b0881a0ed322a98253b5a14503002f1bd50a772f..1713977ee26f8115c2800bd2e5ddae1473a6d057 100644 (file)
@@ -866,4 +866,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid,
 asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type,
                         unsigned long idx1, unsigned long idx2);
 asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags);
+asmlinkage long sys_seccomp(unsigned int op, unsigned int flags,
+                           const char __user *uargs);
 #endif
index 3336406080874bf2bd06cb1f0563aea6d8e56650..65acbf0e2867f1f1d3e316af86c70a89a89a7955 100644 (file)
@@ -699,9 +699,11 @@ __SYSCALL(__NR_sched_setattr, sys_sched_setattr)
 __SYSCALL(__NR_sched_getattr, sys_sched_getattr)
 #define __NR_renameat2 276
 __SYSCALL(__NR_renameat2, sys_renameat2)
+#define __NR_seccomp 277
+__SYSCALL(__NR_seccomp, sys_seccomp)
 
 #undef __NR_syscalls
-#define __NR_syscalls 277
+#define __NR_syscalls 278
 
 /*
  * All syscalls below here should go away really,
index ac2dc9f7297367c0529ed70e7ed02a8be8f4c27f..0f238a43ff1e7e5ebd8bd746a055fa910c10b51d 100644 (file)
 #define SECCOMP_MODE_STRICT    1 /* uses hard-coded filter. */
 #define SECCOMP_MODE_FILTER    2 /* uses user-supplied filter. */
 
+/* Valid operations for seccomp syscall. */
+#define SECCOMP_SET_MODE_STRICT        0
+#define SECCOMP_SET_MODE_FILTER        1
+
+/* Valid flags for SECCOMP_SET_MODE_FILTER */
+#define SECCOMP_FILTER_FLAG_TSYNC      1
+
 /*
  * All BPF programs must return a 32-bit value.
  * The bottom 16-bits are for optional return data.
index 3ef2e0e797e8e576cc6c922d65ef2dc6e40e2d86..ba2ff5a5c600daa49670502c2e2f8e9c72fe69e6 100644 (file)
@@ -1677,7 +1677,7 @@ void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap)
        audit_log_format(ab, " %s=", prefix);
        CAP_FOR_EACH_U32(i) {
                audit_log_format(ab, "%08x",
-                                cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]);
+                                cap->cap[CAP_LAST_U32 - i]);
        }
 }
 
index a5cf13c018ceca356dd02c37bd460abf9d95eddf..989f5bfc57dcfde3046b2756ddd2aac59da1ec73 100644 (file)
@@ -258,6 +258,10 @@ SYSCALL_DEFINE2(capset, cap_user_header_t, header, const cap_user_data_t, data)
                i++;
        }
 
+       effective.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
+       permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
+       inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
+
        new = prepare_creds();
        if (!new)
                return -ENOMEM;
index 6a13c46cd87dbe72bc830bf109256fa458f22ad1..ed4bc339c9dcba0b4d3cb03a6ddef585a64a5a30 100644 (file)
@@ -315,6 +315,15 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
                goto free_ti;
 
        tsk->stack = ti;
+#ifdef CONFIG_SECCOMP
+       /*
+        * We must handle setting up seccomp filters once we're under
+        * the sighand lock in case orig has changed between now and
+        * then. Until then, filter must be NULL to avoid messing up
+        * the usage counts on the error path calling free_task.
+        */
+       tsk->seccomp.filter = NULL;
+#endif
 
        setup_thread_stack(tsk, orig);
        clear_user_return_notifier(tsk);
@@ -1081,6 +1090,39 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
        return 0;
 }
 
+static void copy_seccomp(struct task_struct *p)
+{
+#ifdef CONFIG_SECCOMP
+       /*
+        * Must be called with sighand->lock held, which is common to
+        * all threads in the group. Holding cred_guard_mutex is not
+        * needed because this new task is not yet running and cannot
+        * be racing exec.
+        */
+       BUG_ON(!spin_is_locked(&current->sighand->siglock));
+
+       /* Ref-count the new filter user, and assign it. */
+       get_seccomp_filter(current);
+       p->seccomp = current->seccomp;
+
+       /*
+        * Explicitly enable no_new_privs here in case it got set
+        * between the task_struct being duplicated and holding the
+        * sighand lock. The seccomp state and nnp must be in sync.
+        */
+       if (task_no_new_privs(current))
+               task_set_no_new_privs(p);
+
+       /*
+        * If the parent gained a seccomp mode after copying thread
+        * flags and between before we held the sighand lock, we have
+        * to manually enable the seccomp thread flag here.
+        */
+       if (p->seccomp.mode != SECCOMP_MODE_DISABLED)
+               set_tsk_thread_flag(p, TIF_SECCOMP);
+#endif
+}
+
 SYSCALL_DEFINE1(set_tid_address, int __user *, tidptr)
 {
        current->clear_child_tid = tidptr;
@@ -1196,7 +1238,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
                goto fork_out;
 
        ftrace_graph_init_task(p);
-       get_seccomp_filter(p);
 
        rt_mutex_init_task(p);
 
@@ -1436,6 +1477,12 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 
        spin_lock(&current->sighand->siglock);
 
+       /*
+        * Copy seccomp details explicitly here, in case they were changed
+        * before holding sighand lock.
+        */
+       copy_seccomp(p);
+
        /*
         * Process group and session signals need to be delivered to just the
         * parent before the fork or both the parent and the child after the
index 301bbc24739c9ff8fd58fe95149ad242da8e50b7..74f4601791715257acdb1299a67d5345f5a8eb9e 100644 (file)
 #include <linux/compat.h>
 #include <linux/sched.h>
 #include <linux/seccomp.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
 
 /* #define SECCOMP_DEBUG 1 */
 
 #ifdef CONFIG_SECCOMP_FILTER
 #include <asm/syscall.h>
 #include <linux/filter.h>
+#include <linux/pid.h>
 #include <linux/ptrace.h>
 #include <linux/security.h>
-#include <linux/slab.h>
 #include <linux/tracehook.h>
 #include <linux/uaccess.h>
 
@@ -172,21 +174,24 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
  */
 static u32 seccomp_run_filters(int syscall)
 {
-       struct seccomp_filter *f;
+       struct seccomp_filter *f = ACCESS_ONCE(current->seccomp.filter);
        struct seccomp_data sd;
        u32 ret = SECCOMP_RET_ALLOW;
 
        /* Ensure unexpected behavior doesn't result in failing open. */
-       if (WARN_ON(current->seccomp.filter == NULL))
+       if (unlikely(WARN_ON(f == NULL)))
                return SECCOMP_RET_KILL;
 
+       /* Make sure cross-thread synced filter points somewhere sane. */
+       smp_read_barrier_depends();
+
        populate_seccomp_data(&sd);
 
        /*
         * All filters in the list are evaluated and the lowest BPF return
         * value always takes priority (ignoring the DATA).
         */
-       for (f = current->seccomp.filter; f; f = f->prev) {
+       for (; f; f = f->prev) {
                u32 cur_ret = SK_RUN_FILTER(f->prog, (void *)&sd);
 
                if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION))
@@ -194,29 +199,159 @@ static u32 seccomp_run_filters(int syscall)
        }
        return ret;
 }
+#endif /* CONFIG_SECCOMP_FILTER */
+
+static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode)
+{
+       BUG_ON(!spin_is_locked(&current->sighand->siglock));
+
+       if (current->seccomp.mode && current->seccomp.mode != seccomp_mode)
+               return false;
+
+       return true;
+}
+
+static inline void seccomp_assign_mode(struct task_struct *task,
+                                      unsigned long seccomp_mode)
+{
+       BUG_ON(!spin_is_locked(&task->sighand->siglock));
+
+       task->seccomp.mode = seccomp_mode;
+       /*
+        * Make sure TIF_SECCOMP cannot be set before the mode (and
+        * filter) is set.
+        */
+       smp_mb__before_atomic();
+       set_tsk_thread_flag(task, TIF_SECCOMP);
+}
+
+#ifdef CONFIG_SECCOMP_FILTER
+/* Returns 1 if the parent is an ancestor of the child. */
+static int is_ancestor(struct seccomp_filter *parent,
+                      struct seccomp_filter *child)
+{
+       /* NULL is the root ancestor. */
+       if (parent == NULL)
+               return 1;
+       for (; child; child = child->prev)
+               if (child == parent)
+                       return 1;
+       return 0;
+}
 
 /**
- * seccomp_attach_filter: Attaches a seccomp filter to current.
+ * seccomp_can_sync_threads: checks if all threads can be synchronized
+ *
+ * Expects sighand and cred_guard_mutex locks to be held.
+ *
+ * Returns 0 on success, -ve on error, or the pid of a thread which was
+ * either not in the correct seccomp mode or it did not have an ancestral
+ * seccomp filter.
+ */
+static inline pid_t seccomp_can_sync_threads(void)
+{
+       struct task_struct *thread, *caller;
+
+       BUG_ON(!mutex_is_locked(&current->signal->cred_guard_mutex));
+       BUG_ON(!spin_is_locked(&current->sighand->siglock));
+
+       /* Validate all threads being eligible for synchronization. */
+       caller = current;
+       for_each_thread(caller, thread) {
+               pid_t failed;
+
+               /* Skip current, since it is initiating the sync. */
+               if (thread == caller)
+                       continue;
+
+               if (thread->seccomp.mode == SECCOMP_MODE_DISABLED ||
+                   (thread->seccomp.mode == SECCOMP_MODE_FILTER &&
+                    is_ancestor(thread->seccomp.filter,
+                                caller->seccomp.filter)))
+                       continue;
+
+               /* Return the first thread that cannot be synchronized. */
+               failed = task_pid_vnr(thread);
+               /* If the pid cannot be resolved, then return -ESRCH */
+               if (unlikely(WARN_ON(failed == 0)))
+                       failed = -ESRCH;
+               return failed;
+       }
+
+       return 0;
+}
+
+/**
+ * seccomp_sync_threads: sets all threads to use current's filter
+ *
+ * Expects sighand and cred_guard_mutex locks to be held, and for
+ * seccomp_can_sync_threads() to have returned success already
+ * without dropping the locks.
+ *
+ */
+static inline void seccomp_sync_threads(void)
+{
+       struct task_struct *thread, *caller;
+
+       BUG_ON(!mutex_is_locked(&current->signal->cred_guard_mutex));
+       BUG_ON(!spin_is_locked(&current->sighand->siglock));
+
+       /* Synchronize all threads. */
+       caller = current;
+       for_each_thread(caller, thread) {
+               /* Skip current, since it needs no changes. */
+               if (thread == caller)
+                       continue;
+
+               /* Get a task reference for the new leaf node. */
+               get_seccomp_filter(caller);
+               /*
+                * Drop the task reference to the shared ancestor since
+                * current's path will hold a reference.  (This also
+                * allows a put before the assignment.)
+                */
+               put_seccomp_filter(thread);
+               smp_store_release(&thread->seccomp.filter,
+                                 caller->seccomp.filter);
+               /*
+                * Opt the other thread into seccomp if needed.
+                * As threads are considered to be trust-realm
+                * equivalent (see ptrace_may_access), it is safe to
+                * allow one thread to transition the other.
+                */
+               if (thread->seccomp.mode == SECCOMP_MODE_DISABLED) {
+                       /*
+                        * Don't let an unprivileged task work around
+                        * the no_new_privs restriction by creating
+                        * a thread that sets it up, enters seccomp,
+                        * then dies.
+                        */
+                       if (task_no_new_privs(caller))
+                               task_set_no_new_privs(thread);
+
+                       seccomp_assign_mode(thread, SECCOMP_MODE_FILTER);
+               }
+       }
+}
+
+/**
+ * seccomp_prepare_filter: Prepares a seccomp filter for use.
  * @fprog: BPF program to install
  *
- * Returns 0 on success or an errno on failure.
+ * Returns filter on success or an ERR_PTR on failure.
  */
-static long seccomp_attach_filter(struct sock_fprog *fprog)
+static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
 {
        struct seccomp_filter *filter;
-       unsigned long fp_size = fprog->len * sizeof(struct sock_filter);
-       unsigned long total_insns = fprog->len;
+       unsigned long fp_size;
        struct sock_filter *fp;
        int new_len;
        long ret;
 
        if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)
-               return -EINVAL;
-
-       for (filter = current->seccomp.filter; filter; filter = filter->prev)
-               total_insns += filter->prog->len + 4;  /* include a 4 instr penalty */
-       if (total_insns > MAX_INSNS_PER_PATH)
-               return -ENOMEM;
+               return ERR_PTR(-EINVAL);
+       BUG_ON(INT_MAX / fprog->len < sizeof(struct sock_filter));
+       fp_size = fprog->len * sizeof(struct sock_filter);
 
        /*
         * Installing a seccomp filter requires that the task has
@@ -224,14 +359,14 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
         * This avoids scenarios where unprivileged tasks can affect the
         * behavior of privileged children.
         */
-       if (!current->no_new_privs &&
+       if (!task_no_new_privs(current) &&
            security_capable_noaudit(current_cred(), current_user_ns(),
                                     CAP_SYS_ADMIN) != 0)
-               return -EACCES;
+               return ERR_PTR(-EACCES);
 
        fp = kzalloc(fp_size, GFP_KERNEL|__GFP_NOWARN);
        if (!fp)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        /* Copy the instructions from fprog. */
        ret = -EFAULT;
@@ -275,13 +410,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
 
        sk_filter_select_runtime(filter->prog);
 
-       /*
-        * If there is an existing filter, make it the prev and don't drop its
-        * task reference.
-        */
-       filter->prev = current->seccomp.filter;
-       current->seccomp.filter = filter;
-       return 0;
+       return filter;
 
 free_filter_prog:
        kfree(filter->prog);
@@ -289,19 +418,20 @@ free_filter:
        kfree(filter);
 free_prog:
        kfree(fp);
-       return ret;
+       return ERR_PTR(ret);
 }
 
 /**
- * seccomp_attach_user_filter - attaches a user-supplied sock_fprog
+ * seccomp_prepare_user_filter - prepares a user-supplied sock_fprog
  * @user_filter: pointer to the user data containing a sock_fprog.
  *
  * Returns 0 on success and non-zero otherwise.
  */
-static long seccomp_attach_user_filter(char __user *user_filter)
+static struct seccomp_filter *
+seccomp_prepare_user_filter(const char __user *user_filter)
 {
        struct sock_fprog fprog;
-       long ret = -EFAULT;
+       struct seccomp_filter *filter = ERR_PTR(-EFAULT);
 
 #ifdef CONFIG_COMPAT
        if (is_compat_task()) {
@@ -314,9 +444,56 @@ static long seccomp_attach_user_filter(char __user *user_filter)
 #endif
        if (copy_from_user(&fprog, user_filter, sizeof(fprog)))
                goto out;
-       ret = seccomp_attach_filter(&fprog);
+       filter = seccomp_prepare_filter(&fprog);
 out:
-       return ret;
+       return filter;
+}
+
+/**
+ * seccomp_attach_filter: validate and attach filter
+ * @flags:  flags to change filter behavior
+ * @filter: seccomp filter to add to the current process
+ *
+ * Caller must be holding current->sighand->siglock lock.
+ *
+ * Returns 0 on success, -ve on error.
+ */
+static long seccomp_attach_filter(unsigned int flags,
+                                 struct seccomp_filter *filter)
+{
+       unsigned long total_insns;
+       struct seccomp_filter *walker;
+
+       BUG_ON(!spin_is_locked(&current->sighand->siglock));
+
+       /* Validate resulting filter length. */
+       total_insns = filter->prog->len;
+       for (walker = current->seccomp.filter; walker; walker = walker->prev)
+               total_insns += walker->prog->len + 4;  /* 4 instr penalty */
+       if (total_insns > MAX_INSNS_PER_PATH)
+               return -ENOMEM;
+
+       /* If thread sync has been requested, check that it is possible. */
+       if (flags & SECCOMP_FILTER_FLAG_TSYNC) {
+               int ret;
+
+               ret = seccomp_can_sync_threads();
+               if (ret)
+                       return ret;
+       }
+
+       /*
+        * If there is an existing filter, make it the prev and don't drop its
+        * task reference.
+        */
+       filter->prev = current->seccomp.filter;
+       current->seccomp.filter = filter;
+
+       /* Now that the new filter is in place, synchronize to all threads. */
+       if (flags & SECCOMP_FILTER_FLAG_TSYNC)
+               seccomp_sync_threads();
+
+       return 0;
 }
 
 /* get_seccomp_filter - increments the reference count of the filter on @tsk */
@@ -329,6 +506,14 @@ void get_seccomp_filter(struct task_struct *tsk)
        atomic_inc(&orig->usage);
 }
 
+static inline void seccomp_filter_free(struct seccomp_filter *filter)
+{
+       if (filter) {
+               sk_filter_free(filter->prog);
+               kfree(filter);
+       }
+}
+
 /* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
 void put_seccomp_filter(struct task_struct *tsk)
 {
@@ -337,8 +522,7 @@ void put_seccomp_filter(struct task_struct *tsk)
        while (orig && atomic_dec_and_test(&orig->usage)) {
                struct seccomp_filter *freeme = orig;
                orig = orig->prev;
-               sk_filter_free(freeme->prog);
-               kfree(freeme);
+               seccomp_filter_free(freeme);
        }
 }
 
@@ -382,12 +566,17 @@ static int mode1_syscalls_32[] = {
 
 int __secure_computing(int this_syscall)
 {
-       int mode = current->seccomp.mode;
        int exit_sig = 0;
        int *syscall;
        u32 ret;
 
-       switch (mode) {
+       /*
+        * Make sure that any changes to mode from another thread have
+        * been seen after TIF_SECCOMP was seen.
+        */
+       rmb();
+
+       switch (current->seccomp.mode) {
        case SECCOMP_MODE_STRICT:
                syscall = mode1_syscalls;
 #ifdef CONFIG_COMPAT
@@ -473,47 +662,152 @@ long prctl_get_seccomp(void)
 }
 
 /**
- * prctl_set_seccomp: configures current->seccomp.mode
- * @seccomp_mode: requested mode to use
- * @filter: optional struct sock_fprog for use with SECCOMP_MODE_FILTER
+ * seccomp_set_mode_strict: internal function for setting strict seccomp
  *
- * This function may be called repeatedly with a @seccomp_mode of
- * SECCOMP_MODE_FILTER to install additional filters.  Every filter
- * successfully installed will be evaluated (in reverse order) for each system
- * call the task makes.
+ * Once current->seccomp.mode is non-zero, it may not be changed.
+ *
+ * Returns 0 on success or -EINVAL on failure.
+ */
+static long seccomp_set_mode_strict(void)
+{
+       const unsigned long seccomp_mode = SECCOMP_MODE_STRICT;
+       long ret = -EINVAL;
+
+       spin_lock_irq(&current->sighand->siglock);
+
+       if (!seccomp_may_assign_mode(seccomp_mode))
+               goto out;
+
+#ifdef TIF_NOTSC
+       disable_TSC();
+#endif
+       seccomp_assign_mode(current, seccomp_mode);
+       ret = 0;
+
+out:
+       spin_unlock_irq(&current->sighand->siglock);
+
+       return ret;
+}
+
+#ifdef CONFIG_SECCOMP_FILTER
+/**
+ * seccomp_set_mode_filter: internal function for setting seccomp filter
+ * @flags:  flags to change filter behavior
+ * @filter: struct sock_fprog containing filter
+ *
+ * This function may be called repeatedly to install additional filters.
+ * Every filter successfully installed will be evaluated (in reverse order)
+ * for each system call the task makes.
  *
  * Once current->seccomp.mode is non-zero, it may not be changed.
  *
  * Returns 0 on success or -EINVAL on failure.
  */
-long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)
+static long seccomp_set_mode_filter(unsigned int flags,
+                                   const char __user *filter)
 {
+       const unsigned long seccomp_mode = SECCOMP_MODE_FILTER;
+       struct seccomp_filter *prepared = NULL;
        long ret = -EINVAL;
 
-       if (current->seccomp.mode &&
-           current->seccomp.mode != seccomp_mode)
+       /* Validate flags. */
+       if (flags & ~SECCOMP_FILTER_FLAG_MASK)
+               return -EINVAL;
+
+       /* Prepare the new filter before holding any locks. */
+       prepared = seccomp_prepare_user_filter(filter);
+       if (IS_ERR(prepared))
+               return PTR_ERR(prepared);
+
+       /*
+        * Make sure we cannot change seccomp or nnp state via TSYNC
+        * while another thread is in the middle of calling exec.
+        */
+       if (flags & SECCOMP_FILTER_FLAG_TSYNC &&
+           mutex_lock_killable(&current->signal->cred_guard_mutex))
+               goto out_free;
+
+       spin_lock_irq(&current->sighand->siglock);
+
+       if (!seccomp_may_assign_mode(seccomp_mode))
+               goto out;
+
+       ret = seccomp_attach_filter(flags, prepared);
+       if (ret)
                goto out;
+       /* Do not free the successfully attached filter. */
+       prepared = NULL;
+
+       seccomp_assign_mode(current, seccomp_mode);
+out:
+       spin_unlock_irq(&current->sighand->siglock);
+       if (flags & SECCOMP_FILTER_FLAG_TSYNC)
+               mutex_unlock(&current->signal->cred_guard_mutex);
+out_free:
+       seccomp_filter_free(prepared);
+       return ret;
+}
+#else
+static inline long seccomp_set_mode_filter(unsigned int flags,
+                                          const char __user *filter)
+{
+       return -EINVAL;
+}
+#endif
+
+/* Common entry point for both prctl and syscall. */
+static long do_seccomp(unsigned int op, unsigned int flags,
+                      const char __user *uargs)
+{
+       switch (op) {
+       case SECCOMP_SET_MODE_STRICT:
+               if (flags != 0 || uargs != NULL)
+                       return -EINVAL;
+               return seccomp_set_mode_strict();
+       case SECCOMP_SET_MODE_FILTER:
+               return seccomp_set_mode_filter(flags, uargs);
+       default:
+               return -EINVAL;
+       }
+}
+
+SYSCALL_DEFINE3(seccomp, unsigned int, op, unsigned int, flags,
+                        const char __user *, uargs)
+{
+       return do_seccomp(op, flags, uargs);
+}
+
+/**
+ * prctl_set_seccomp: configures current->seccomp.mode
+ * @seccomp_mode: requested mode to use
+ * @filter: optional struct sock_fprog for use with SECCOMP_MODE_FILTER
+ *
+ * Returns 0 on success or -EINVAL on failure.
+ */
+long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)
+{
+       unsigned int op;
+       char __user *uargs;
 
        switch (seccomp_mode) {
        case SECCOMP_MODE_STRICT:
-               ret = 0;
-#ifdef TIF_NOTSC
-               disable_TSC();
-#endif
+               op = SECCOMP_SET_MODE_STRICT;
+               /*
+                * Setting strict mode through prctl always ignored filter,
+                * so make sure it is always NULL here to pass the internal
+                * check in do_seccomp().
+                */
+               uargs = NULL;
                break;
-#ifdef CONFIG_SECCOMP_FILTER
        case SECCOMP_MODE_FILTER:
-               ret = seccomp_attach_user_filter(filter);
-               if (ret)
-                       goto out;
+               op = SECCOMP_SET_MODE_FILTER;
+               uargs = filter;
                break;
-#endif
        default:
-               goto out;
+               return -EINVAL;
        }
 
-       current->seccomp.mode = seccomp_mode;
-       set_thread_flag(TIF_SECCOMP);
-out:
-       return ret;
+       /* prctl interface doesn't have flags, so they are always zero. */
+       return do_seccomp(op, 0, uargs);
 }
index 66a751ebf9d9c77be801d263d0844d40a8b52ad0..ce8129192a26029da0d5b33b75daaeb85b1d08e5 100644 (file)
@@ -1990,12 +1990,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                if (arg2 != 1 || arg3 || arg4 || arg5)
                        return -EINVAL;
 
-               current->no_new_privs = 1;
+               task_set_no_new_privs(current);
                break;
        case PR_GET_NO_NEW_PRIVS:
                if (arg2 || arg3 || arg4 || arg5)
                        return -EINVAL;
-               return current->no_new_privs ? 1 : 0;
+               return task_no_new_privs(current) ? 1 : 0;
        case PR_GET_THP_DISABLE:
                if (arg2 || arg3 || arg4 || arg5)
                        return -EINVAL;
index 36441b51b5dff20bb63792d3ad7ff7e0d30908bf..2904a21059145b61e55a3fee9204a64fce48c9ea 100644 (file)
@@ -213,3 +213,6 @@ cond_syscall(compat_sys_open_by_handle_at);
 
 /* compare kernel pointers */
 cond_syscall(sys_kcmp);
+
+/* operate on Secure Computing state */
+cond_syscall(sys_seccomp);
index 417b165008ee5eae0f0dc8a3094bc0156b87d711..1d1ac51359e3abe9ff1e32ebaba24de04f2909e9 100644 (file)
@@ -2,4 +2,3 @@ hostprogs-y     := genheaders
 HOST_EXTRACFLAGS += -Isecurity/selinux/include
 
 always         := $(hostprogs-y)
-clean-files    := $(hostprogs-y)
index eb365b333441b939f09290a1ad1b613d76f52d76..dba7eff69a00962e99ca2e34ff4a4f47da59f58b 100644 (file)
@@ -2,4 +2,4 @@ hostprogs-y     := mdp
 HOST_EXTRACFLAGS += -Isecurity/selinux/include
 
 always         := $(hostprogs-y)
-clean-files    := $(hostprogs-y) policy.* file_contexts
+clean-files    := policy.* file_contexts
index 452567d3a08e7ccfc5a7e3ae20ae95307554f85c..d97cba3e3849bcdbd577f45720bba4ea95a4935c 100644 (file)
@@ -621,7 +621,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
         * There is no exception for unconfined as change_hat is not
         * available.
         */
-       if (current->no_new_privs)
+       if (task_no_new_privs(current))
                return -EPERM;
 
        /* released below */
@@ -776,7 +776,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
         * no_new_privs is set because this aways results in a reduction
         * of permissions.
         */
-       if (current->no_new_privs && !unconfined(profile)) {
+       if (task_no_new_privs(current) && !unconfined(profile)) {
                put_cred(cred);
                return -EPERM;
        }
index b9d613e0ef143b36bd411246d1ce1f42f00738b0..bab0611afc1eed309a04f9cf2ee05575ea98fc9d 100644 (file)
@@ -421,6 +421,9 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
                cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable);
        }
 
+       cpu_caps->permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
+       cpu_caps->inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
+
        return 0;
 }
 
@@ -822,15 +825,20 @@ int cap_task_setnice(struct task_struct *p, int nice)
  * Implement PR_CAPBSET_DROP.  Attempt to remove the specified capability from
  * the current task's bounding set.  Returns 0 on success, -ve on error.
  */
-static long cap_prctl_drop(struct cred *new, unsigned long cap)
+static int cap_prctl_drop(unsigned long cap)
 {
+       struct cred *new;
+
        if (!ns_capable(current_user_ns(), CAP_SETPCAP))
                return -EPERM;
        if (!cap_valid(cap))
                return -EINVAL;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
        cap_lower(new->cap_bset, cap);
-       return 0;
+       return commit_creds(new);
 }
 
 /**
@@ -848,26 +856,17 @@ static long cap_prctl_drop(struct cred *new, unsigned long cap)
 int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
                   unsigned long arg4, unsigned long arg5)
 {
+       const struct cred *old = current_cred();
        struct cred *new;
-       long error = 0;
-
-       new = prepare_creds();
-       if (!new)
-               return -ENOMEM;
 
        switch (option) {
        case PR_CAPBSET_READ:
-               error = -EINVAL;
                if (!cap_valid(arg2))
-                       goto error;
-               error = !!cap_raised(new->cap_bset, arg2);
-               goto no_change;
+                       return -EINVAL;
+               return !!cap_raised(old->cap_bset, arg2);
 
        case PR_CAPBSET_DROP:
-               error = cap_prctl_drop(new, arg2);
-               if (error < 0)
-                       goto error;
-               goto changed;
+               return cap_prctl_drop(arg2);
 
        /*
         * The next four prctl's remain to assist with transitioning a
@@ -889,10 +888,9 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
         * capability-based-privilege environment.
         */
        case PR_SET_SECUREBITS:
-               error = -EPERM;
-               if ((((new->securebits & SECURE_ALL_LOCKS) >> 1)
-                    & (new->securebits ^ arg2))                        /*[1]*/
-                   || ((new->securebits & SECURE_ALL_LOCKS & ~arg2))   /*[2]*/
+               if ((((old->securebits & SECURE_ALL_LOCKS) >> 1)
+                    & (old->securebits ^ arg2))                        /*[1]*/
+                   || ((old->securebits & SECURE_ALL_LOCKS & ~arg2))   /*[2]*/
                    || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS))   /*[3]*/
                    || (cap_capable(current_cred(),
                                    current_cred()->user_ns, CAP_SETPCAP,
@@ -906,46 +904,39 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
                         */
                    )
                        /* cannot change a locked bit */
-                       goto error;
+                       return -EPERM;
+
+               new = prepare_creds();
+               if (!new)
+                       return -ENOMEM;
                new->securebits = arg2;
-               goto changed;
+               return commit_creds(new);
 
        case PR_GET_SECUREBITS:
-               error = new->securebits;
-               goto no_change;
+               return old->securebits;
 
        case PR_GET_KEEPCAPS:
-               if (issecure(SECURE_KEEP_CAPS))
-                       error = 1;
-               goto no_change;
+               return !!issecure(SECURE_KEEP_CAPS);
 
        case PR_SET_KEEPCAPS:
-               error = -EINVAL;
                if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */
-                       goto error;
-               error = -EPERM;
+                       return -EINVAL;
                if (issecure(SECURE_KEEP_CAPS_LOCKED))
-                       goto error;
+                       return -EPERM;
+
+               new = prepare_creds();
+               if (!new)
+                       return -ENOMEM;
                if (arg2)
                        new->securebits |= issecure_mask(SECURE_KEEP_CAPS);
                else
                        new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
-               goto changed;
+               return commit_creds(new);
 
        default:
                /* No functionality available - continue with default */
-               error = -ENOSYS;
-               goto error;
+               return -ENOSYS;
        }
-
-       /* Functionality provided */
-changed:
-       return commit_creds(new);
-
-no_change:
-error:
-       abort_creds(new);
-       return error;
 }
 
 /**
index a1ac1c5c729b0818d43ea26e03e8c1ca6eb23afa..7740f61588d6493b7c1e2da18f6562ff9317a529 100644 (file)
@@ -161,6 +161,17 @@ static int selinux_peerlbl_enabled(void)
        return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled());
 }
 
+static int selinux_netcache_avc_callback(u32 event)
+{
+       if (event == AVC_CALLBACK_RESET) {
+               sel_netif_flush();
+               sel_netnode_flush();
+               sel_netport_flush();
+               synchronize_net();
+       }
+       return 0;
+}
+
 /*
  * initialise the security for the init task
  */
@@ -6002,6 +6013,9 @@ static __init int selinux_init(void)
        if (register_security(&selinux_ops))
                panic("SELinux: Unable to register with kernel.\n");
 
+       if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
+               panic("SELinux: Unable to register AVC netcache callback\n");
+
        if (selinux_enforcing)
                printk(KERN_DEBUG "SELinux:  Starting in enforcing mode\n");
        else
index 43d507242b42f1ecdbe0f2971074d5d7b80165c5..57c6eae81eacac6226273963ee89602cdf736983 100644 (file)
@@ -17,6 +17,8 @@
 #ifndef _SELINUX_NETIF_H_
 #define _SELINUX_NETIF_H_
 
+void sel_netif_flush(void);
+
 int sel_netif_sid(int ifindex, u32 *sid);
 
 #endif /* _SELINUX_NETIF_H_ */
index df7a5ed6c6943e01008b010acb9782c2b727d824..937668dd30247731dbb416e162c105d29f6e3253 100644 (file)
@@ -27,6 +27,8 @@
 #ifndef _SELINUX_NETNODE_H
 #define _SELINUX_NETNODE_H
 
+void sel_netnode_flush(void);
+
 int sel_netnode_sid(void *addr, u16 family, u32 *sid);
 
 #endif
index 4d965b83d735b976029231274c2ace509b498e1f..d1ce896b2cb0f84bdef2e72ce282cc040f206e64 100644 (file)
@@ -26,6 +26,8 @@
 #ifndef _SELINUX_NETPORT_H
 #define _SELINUX_NETPORT_H
 
+void sel_netport_flush(void);
+
 int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid);
 
 #endif
index ce7852cf526b8e564f69a20c005da00a19a941ca..d1e0b239b6020d32526fc634d55daf854abe1ce2 100644 (file)
@@ -8,6 +8,7 @@
 #ifndef _SELINUX_SECURITY_H_
 #define _SELINUX_SECURITY_H_
 
+#include <linux/compiler.h>
 #include <linux/dcache.h>
 #include <linux/magic.h>
 #include <linux/types.h>
@@ -220,7 +221,7 @@ struct selinux_kernel_status {
        /*
         * The version > 0 supports above members.
         */
-} __attribute__((packed));
+} __packed;
 
 extern void selinux_status_update_setenforce(int enforcing);
 extern void selinux_status_update_policyload(int seqno);
index 694e9e43855f15862227e6caf726189deec5f0fc..3c3de4ca0ebcc81ff7759f4951cc8b5ff7108f01 100644 (file)
@@ -240,7 +240,7 @@ static void sel_netif_kill(int ifindex)
  * Remove all entries from the network interface table.
  *
  */
-static void sel_netif_flush(void)
+void sel_netif_flush(void)
 {
        int idx;
        struct sel_netif *netif;
@@ -252,15 +252,6 @@ static void sel_netif_flush(void)
        spin_unlock_bh(&sel_netif_lock);
 }
 
-static int sel_netif_avc_callback(u32 event)
-{
-       if (event == AVC_CALLBACK_RESET) {
-               sel_netif_flush();
-               synchronize_net();
-       }
-       return 0;
-}
-
 static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
                                             unsigned long event, void *ptr)
 {
@@ -291,10 +282,6 @@ static __init int sel_netif_init(void)
 
        register_netdevice_notifier(&sel_netif_netdev_notifier);
 
-       err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET);
-       if (err)
-               panic("avc_add_callback() failed, error %d\n", err);
-
        return err;
 }
 
index 03a72c32afd738ccad5c188bbe853202c32f53f6..ddf3152608396d3c2cc933467440c78b8c03a504 100644 (file)
@@ -283,7 +283,7 @@ int sel_netnode_sid(void *addr, u16 family, u32 *sid)
  * Remove all entries from the network address table.
  *
  */
-static void sel_netnode_flush(void)
+void sel_netnode_flush(void)
 {
        unsigned int idx;
        struct sel_netnode *node, *node_tmp;
@@ -300,15 +300,6 @@ static void sel_netnode_flush(void)
        spin_unlock_bh(&sel_netnode_lock);
 }
 
-static int sel_netnode_avc_callback(u32 event)
-{
-       if (event == AVC_CALLBACK_RESET) {
-               sel_netnode_flush();
-               synchronize_net();
-       }
-       return 0;
-}
-
 static __init int sel_netnode_init(void)
 {
        int iter;
@@ -322,10 +313,6 @@ static __init int sel_netnode_init(void)
                sel_netnode_hash[iter].size = 0;
        }
 
-       ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET);
-       if (ret != 0)
-               panic("avc_add_callback() failed, error %d\n", ret);
-
        return ret;
 }
 
index d35379781c2c44ab188cb7aed071579da189bb53..73ac6784d091574fc756f01a569ad087a0611869 100644 (file)
@@ -217,7 +217,7 @@ int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid)
  * Remove all entries from the network address table.
  *
  */
-static void sel_netport_flush(void)
+void sel_netport_flush(void)
 {
        unsigned int idx;
        struct sel_netport *port, *port_tmp;
@@ -234,15 +234,6 @@ static void sel_netport_flush(void)
        spin_unlock_bh(&sel_netport_lock);
 }
 
-static int sel_netport_avc_callback(u32 event)
-{
-       if (event == AVC_CALLBACK_RESET) {
-               sel_netport_flush();
-               synchronize_net();
-       }
-       return 0;
-}
-
 static __init int sel_netport_init(void)
 {
        int iter;
@@ -256,10 +247,6 @@ static __init int sel_netport_init(void)
                sel_netport_hash[iter].size = 0;
        }
 
-       ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET);
-       if (ret != 0)
-               panic("avc_add_callback() failed, error %d\n", ret);
-
        return ret;
 }
 
index 377d148e715743250c9836b7a5ca6c1a05fa98e0..62c6773be0b75f69f4f9c6b579d0205aa602ed5c 100644 (file)
@@ -402,19 +402,14 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
        int rc;
        struct cond_expr *expr = NULL, *last = NULL;
 
-       rc = next_entry(buf, fp, sizeof(u32));
+       rc = next_entry(buf, fp, sizeof(u32) * 2);
        if (rc)
-               return rc;
+               goto err;
 
        node->cur_state = le32_to_cpu(buf[0]);
 
-       len = 0;
-       rc = next_entry(buf, fp, sizeof(u32));
-       if (rc)
-               return rc;
-
        /* expr */
-       len = le32_to_cpu(buf[0]);
+       len = le32_to_cpu(buf[1]);
 
        for (i = 0; i < len; i++) {
                rc = next_entry(buf, fp, sizeof(u32) * 2);
index 9c5cdc2caaef7637b4bf9178a8efcbe69558c6b9..bc2a586f095c5f73c214f1c475a2798a5f1439e3 100644 (file)
@@ -1080,6 +1080,26 @@ out:
  * binary representation file.
  */
 
+static int str_read(char **strp, gfp_t flags, void *fp, u32 len)
+{
+       int rc;
+       char *str;
+
+       str = kmalloc(len + 1, flags);
+       if (!str)
+               return -ENOMEM;
+
+       /* it's expected the caller should free the str */
+       *strp = str;
+
+       rc = next_entry(str, fp, len);
+       if (rc)
+               return rc;
+
+       str[len] = '\0';
+       return 0;
+}
+
 static int perm_read(struct policydb *p, struct hashtab *h, void *fp)
 {
        char *key = NULL;
@@ -1100,15 +1120,9 @@ static int perm_read(struct policydb *p, struct hashtab *h, void *fp)
        len = le32_to_cpu(buf[0]);
        perdatum->value = le32_to_cpu(buf[1]);
 
-       rc = -ENOMEM;
-       key = kmalloc(len + 1, GFP_KERNEL);
-       if (!key)
-               goto bad;
-
-       rc = next_entry(key, fp, len);
+       rc = str_read(&key, GFP_KERNEL, fp, len);
        if (rc)
                goto bad;
-       key[len] = '\0';
 
        rc = hashtab_insert(h, key, perdatum);
        if (rc)
@@ -1146,15 +1160,9 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp)
        comdatum->permissions.nprim = le32_to_cpu(buf[2]);
        nel = le32_to_cpu(buf[3]);
 
-       rc = -ENOMEM;
-       key = kmalloc(len + 1, GFP_KERNEL);
-       if (!key)
-               goto bad;
-
-       rc = next_entry(key, fp, len);
+       rc = str_read(&key, GFP_KERNEL, fp, len);
        if (rc)
                goto bad;
-       key[len] = '\0';
 
        for (i = 0; i < nel; i++) {
                rc = perm_read(p, comdatum->permissions.table, fp);
@@ -1321,25 +1329,14 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
 
        ncons = le32_to_cpu(buf[5]);
 
-       rc = -ENOMEM;
-       key = kmalloc(len + 1, GFP_KERNEL);
-       if (!key)
-               goto bad;
-
-       rc = next_entry(key, fp, len);
+       rc = str_read(&key, GFP_KERNEL, fp, len);
        if (rc)
                goto bad;
-       key[len] = '\0';
 
        if (len2) {
-               rc = -ENOMEM;
-               cladatum->comkey = kmalloc(len2 + 1, GFP_KERNEL);
-               if (!cladatum->comkey)
-                       goto bad;
-               rc = next_entry(cladatum->comkey, fp, len2);
+               rc = str_read(&cladatum->comkey, GFP_KERNEL, fp, len2);
                if (rc)
                        goto bad;
-               cladatum->comkey[len2] = '\0';
 
                rc = -EINVAL;
                cladatum->comdatum = hashtab_search(p->p_commons.table, cladatum->comkey);
@@ -1422,15 +1419,9 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
        if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
                role->bounds = le32_to_cpu(buf[2]);
 
-       rc = -ENOMEM;
-       key = kmalloc(len + 1, GFP_KERNEL);
-       if (!key)
-               goto bad;
-
-       rc = next_entry(key, fp, len);
+       rc = str_read(&key, GFP_KERNEL, fp, len);
        if (rc)
                goto bad;
-       key[len] = '\0';
 
        rc = ebitmap_read(&role->dominates, fp);
        if (rc)
@@ -1495,14 +1486,9 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp)
                typdatum->primary = le32_to_cpu(buf[2]);
        }
 
-       rc = -ENOMEM;
-       key = kmalloc(len + 1, GFP_KERNEL);
-       if (!key)
-               goto bad;
-       rc = next_entry(key, fp, len);
+       rc = str_read(&key, GFP_KERNEL, fp, len);
        if (rc)
                goto bad;
-       key[len] = '\0';
 
        rc = hashtab_insert(h, key, typdatum);
        if (rc)
@@ -1565,14 +1551,9 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp)
        if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
                usrdatum->bounds = le32_to_cpu(buf[2]);
 
-       rc = -ENOMEM;
-       key = kmalloc(len + 1, GFP_KERNEL);
-       if (!key)
-               goto bad;
-       rc = next_entry(key, fp, len);
+       rc = str_read(&key, GFP_KERNEL, fp, len);
        if (rc)
                goto bad;
-       key[len] = '\0';
 
        rc = ebitmap_read(&usrdatum->roles, fp);
        if (rc)
@@ -1616,14 +1597,9 @@ static int sens_read(struct policydb *p, struct hashtab *h, void *fp)
        len = le32_to_cpu(buf[0]);
        levdatum->isalias = le32_to_cpu(buf[1]);
 
-       rc = -ENOMEM;
-       key = kmalloc(len + 1, GFP_ATOMIC);
-       if (!key)
-               goto bad;
-       rc = next_entry(key, fp, len);
+       rc = str_read(&key, GFP_ATOMIC, fp, len);
        if (rc)
                goto bad;
-       key[len] = '\0';
 
        rc = -ENOMEM;
        levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC);
@@ -1664,14 +1640,9 @@ static int cat_read(struct policydb *p, struct hashtab *h, void *fp)
        catdatum->value = le32_to_cpu(buf[1]);
        catdatum->isalias = le32_to_cpu(buf[2]);
 
-       rc = -ENOMEM;
-       key = kmalloc(len + 1, GFP_ATOMIC);
-       if (!key)
-               goto bad;
-       rc = next_entry(key, fp, len);
+       rc = str_read(&key, GFP_ATOMIC, fp, len);
        if (rc)
                goto bad;
-       key[len] = '\0';
 
        rc = hashtab_insert(h, key, catdatum);
        if (rc)
@@ -1968,18 +1939,12 @@ static int filename_trans_read(struct policydb *p, void *fp)
                        goto out;
                len = le32_to_cpu(buf[0]);
 
-               rc = -ENOMEM;
-               name = kmalloc(len + 1, GFP_KERNEL);
-               if (!name)
-                       goto out;
-
-               ft->name = name;
-
                /* path component string */
-               rc = next_entry(name, fp, len);
+               rc = str_read(&name, GFP_KERNEL, fp, len);
                if (rc)
                        goto out;
-               name[len] = 0;
+
+               ft->name = name;
 
                rc = next_entry(buf, fp, sizeof(u32) * 4);
                if (rc)
@@ -2045,17 +2010,10 @@ static int genfs_read(struct policydb *p, void *fp)
                if (!newgenfs)
                        goto out;
 
-               rc = -ENOMEM;
-               newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL);
-               if (!newgenfs->fstype)
-                       goto out;
-
-               rc = next_entry(newgenfs->fstype, fp, len);
+               rc = str_read(&newgenfs->fstype, GFP_KERNEL, fp, len);
                if (rc)
                        goto out;
 
-               newgenfs->fstype[len] = 0;
-
                for (genfs_p = NULL, genfs = p->genfs; genfs;
                     genfs_p = genfs, genfs = genfs->next) {
                        rc = -EINVAL;
@@ -2091,15 +2049,9 @@ static int genfs_read(struct policydb *p, void *fp)
                        if (!newc)
                                goto out;
 
-                       rc = -ENOMEM;
-                       newc->u.name = kmalloc(len + 1, GFP_KERNEL);
-                       if (!newc->u.name)
-                               goto out;
-
-                       rc = next_entry(newc->u.name, fp, len);
+                       rc = str_read(&newc->u.name, GFP_KERNEL, fp, len);
                        if (rc)
                                goto out;
-                       newc->u.name[len] = 0;
 
                        rc = next_entry(buf, fp, sizeof(u32));
                        if (rc)
@@ -2189,16 +2141,10 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
                                        goto out;
                                len = le32_to_cpu(buf[0]);
 
-                               rc = -ENOMEM;
-                               c->u.name = kmalloc(len + 1, GFP_KERNEL);
-                               if (!c->u.name)
-                                       goto out;
-
-                               rc = next_entry(c->u.name, fp, len);
+                               rc = str_read(&c->u.name, GFP_KERNEL, fp, len);
                                if (rc)
                                        goto out;
 
-                               c->u.name[len] = 0;
                                rc = context_read_and_validate(&c->context[0], p, fp);
                                if (rc)
                                        goto out;
@@ -2240,16 +2186,11 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
                                if (c->v.behavior > SECURITY_FS_USE_MAX)
                                        goto out;
 
-                               rc = -ENOMEM;
                                len = le32_to_cpu(buf[1]);
-                               c->u.name = kmalloc(len + 1, GFP_KERNEL);
-                               if (!c->u.name)
-                                       goto out;
-
-                               rc = next_entry(c->u.name, fp, len);
+                               rc = str_read(&c->u.name, GFP_KERNEL, fp, len);
                                if (rc)
                                        goto out;
-                               c->u.name[len] = 0;
+
                                rc = context_read_and_validate(&c->context[0], p, fp);
                                if (rc)
                                        goto out;
@@ -2608,7 +2549,7 @@ static int mls_write_range_helper(struct mls_range *r, void *fp)
        if (!eq)
                buf[2] = cpu_to_le32(r->level[1].sens);
 
-       BUG_ON(items > (sizeof(buf)/sizeof(buf[0])));
+       BUG_ON(items > ARRAY_SIZE(buf));
 
        rc = put_entry(buf, sizeof(u32), items, fp);
        if (rc)
@@ -2990,7 +2931,7 @@ static int role_write(void *vkey, void *datum, void *ptr)
        if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
                buf[items++] = cpu_to_le32(role->bounds);
 
-       BUG_ON(items > (sizeof(buf)/sizeof(buf[0])));
+       BUG_ON(items > ARRAY_SIZE(buf));
 
        rc = put_entry(buf, sizeof(u32), items, fp);
        if (rc)
@@ -3040,7 +2981,7 @@ static int type_write(void *vkey, void *datum, void *ptr)
        } else {
                buf[items++] = cpu_to_le32(typdatum->primary);
        }
-       BUG_ON(items > (sizeof(buf) / sizeof(buf[0])));
+       BUG_ON(items > ARRAY_SIZE(buf));
        rc = put_entry(buf, sizeof(u32), items, fp);
        if (rc)
                return rc;
@@ -3069,7 +3010,7 @@ static int user_write(void *vkey, void *datum, void *ptr)
        buf[items++] = cpu_to_le32(usrdatum->value);
        if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
                buf[items++] = cpu_to_le32(usrdatum->bounds);
-       BUG_ON(items > (sizeof(buf) / sizeof(buf[0])));
+       BUG_ON(items > ARRAY_SIZE(buf));
        rc = put_entry(buf, sizeof(u32), items, fp);
        if (rc)
                return rc;
index 4bca49414a40e5a40c4369415f5c5b0aecba6347..2aa9d172dc7e73d87ccbc7f73bbc3e8ad2206e45 100644 (file)
@@ -2277,7 +2277,7 @@ out:
 }
 
 /**
- * security_genfs_sid - Obtain a SID for a file in a filesystem
+ * __security_genfs_sid - Helper to obtain a SID for a file in a filesystem
  * @fstype: filesystem type
  * @path: path from root of mount
  * @sclass: file security class
@@ -2286,11 +2286,13 @@ out:
  * Obtain a SID to use for a file in a filesystem that
  * cannot support xattr or use a fixed labeling behavior like
  * transition SIDs or task SIDs.
+ *
+ * The caller must acquire the policy_rwlock before calling this function.
  */
-int security_genfs_sid(const char *fstype,
-                      char *path,
-                      u16 orig_sclass,
-                      u32 *sid)
+static inline int __security_genfs_sid(const char *fstype,
+                                      char *path,
+                                      u16 orig_sclass,
+                                      u32 *sid)
 {
        int len;
        u16 sclass;
@@ -2301,8 +2303,6 @@ int security_genfs_sid(const char *fstype,
        while (path[0] == '/' && path[1] == '/')
                path++;
 
-       read_lock(&policy_rwlock);
-
        sclass = unmap_class(orig_sclass);
        *sid = SECINITSID_UNLABELED;
 
@@ -2336,10 +2336,32 @@ int security_genfs_sid(const char *fstype,
        *sid = c->sid[0];
        rc = 0;
 out:
-       read_unlock(&policy_rwlock);
        return rc;
 }
 
+/**
+ * security_genfs_sid - Obtain a SID for a file in a filesystem
+ * @fstype: filesystem type
+ * @path: path from root of mount
+ * @sclass: file security class
+ * @sid: SID for path
+ *
+ * Acquire policy_rwlock before calling __security_genfs_sid() and release
+ * it afterward.
+ */
+int security_genfs_sid(const char *fstype,
+                      char *path,
+                      u16 orig_sclass,
+                      u32 *sid)
+{
+       int retval;
+
+       read_lock(&policy_rwlock);
+       retval = __security_genfs_sid(fstype, path, orig_sclass, sid);
+       read_unlock(&policy_rwlock);
+       return retval;
+}
+
 /**
  * security_fs_use - Determine how to handle labeling for a filesystem.
  * @sb: superblock in question
@@ -2370,7 +2392,8 @@ int security_fs_use(struct super_block *sb)
                }
                sbsec->sid = c->sid[0];
        } else {
-               rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, &sbsec->sid);
+               rc = __security_genfs_sid(fstype, "/", SECCLASS_DIR,
+                                         &sbsec->sid);
                if (rc) {
                        sbsec->behavior = SECURITY_FS_USE_NONE;
                        rc = 0;