Add security hooks to binder and implement the hooks for SELinux.
authorStephen Smalley <sds@tycho.nsa.gov>
Wed, 21 Jan 2015 15:54:10 +0000 (10:54 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 25 Jan 2015 17:17:57 +0000 (09:17 -0800)
Add security hooks to the binder and implement the hooks for SELinux.
The security hooks enable security modules such as SELinux to implement
controls over binder IPC.  The security hooks include support for
controlling what process can become the binder context manager
(binder_set_context_mgr), controlling the ability of a process
to invoke a binder transaction/IPC to another process (binder_transaction),
controlling the ability of a process to transfer a binder reference to
another process (binder_transfer_binder), and controlling the ability
of a process to transfer an open file to another process (binder_transfer_file).

These hooks have been included in the Android kernel trees since Android 4.3.

(Updated to reflect upstream relocation and changes to the binder driver,
changes to the LSM audit data structures, coding style cleanups, and
to add inline documentation for the hooks).

Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
Acked-by: Nick Kralevich <nnk@google.com>
Acked-by: Jeffrey Vander Stoep <jeffv@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/android/binder.c
include/linux/security.h
security/capability.c
security/security.c
security/selinux/hooks.c
security/selinux/include/classmap.h

index 8c43521d3f114d54c3c14fc9ec71d1a5f8f1814a..33b09b6568a4a464657b51667ffe8596c7893b2f 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/vmalloc.h>
 #include <linux/slab.h>
 #include <linux/pid_namespace.h>
+#include <linux/security.h>
 
 #ifdef CONFIG_ANDROID_BINDER_IPC_32BIT
 #define BINDER_IPC_32BIT 1
@@ -1400,6 +1401,11 @@ static void binder_transaction(struct binder_proc *proc,
                        return_error = BR_DEAD_REPLY;
                        goto err_dead_binder;
                }
+               if (security_binder_transaction(proc->tsk,
+                                               target_proc->tsk) < 0) {
+                       return_error = BR_FAILED_REPLY;
+                       goto err_invalid_target_handle;
+               }
                if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
                        struct binder_transaction *tmp;
 
@@ -1551,6 +1557,11 @@ static void binder_transaction(struct binder_proc *proc,
                                return_error = BR_FAILED_REPLY;
                                goto err_binder_get_ref_for_node_failed;
                        }
+                       if (security_binder_transfer_binder(proc->tsk,
+                                                           target_proc->tsk)) {
+                               return_error = BR_FAILED_REPLY;
+                               goto err_binder_get_ref_for_node_failed;
+                       }
                        ref = binder_get_ref_for_node(target_proc, node);
                        if (ref == NULL) {
                                return_error = BR_FAILED_REPLY;
@@ -1581,6 +1592,11 @@ static void binder_transaction(struct binder_proc *proc,
                                return_error = BR_FAILED_REPLY;
                                goto err_binder_get_ref_failed;
                        }
+                       if (security_binder_transfer_binder(proc->tsk,
+                                                           target_proc->tsk)) {
+                               return_error = BR_FAILED_REPLY;
+                               goto err_binder_get_ref_failed;
+                       }
                        if (ref->node->proc == target_proc) {
                                if (fp->type == BINDER_TYPE_HANDLE)
                                        fp->type = BINDER_TYPE_BINDER;
@@ -1638,6 +1654,13 @@ static void binder_transaction(struct binder_proc *proc,
                                return_error = BR_FAILED_REPLY;
                                goto err_fget_failed;
                        }
+                       if (security_binder_transfer_file(proc->tsk,
+                                                         target_proc->tsk,
+                                                         file) < 0) {
+                               fput(file);
+                               return_error = BR_FAILED_REPLY;
+                               goto err_get_unused_fd_failed;
+                       }
                        target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
                        if (target_fd < 0) {
                                fput(file);
@@ -2675,6 +2698,9 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp)
                ret = -EBUSY;
                goto out;
        }
+       ret = security_binder_set_context_mgr(proc->tsk);
+       if (ret < 0)
+               goto out;
        if (uid_valid(binder_context_mgr_uid)) {
                if (!uid_eq(binder_context_mgr_uid, curr_euid)) {
                        pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
index ba96471c11bae88b6673faec4d4a268cf550db1b..a1b7dbd127ffc73c1c07f3935ed9cd9118cfec52 100644 (file)
@@ -1281,6 +1281,25 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @alter contains the flag indicating whether changes are to be made.
  *     Return 0 if permission is granted.
  *
+ * @binder_set_context_mgr
+ *     Check whether @mgr is allowed to be the binder context manager.
+ *     @mgr contains the task_struct for the task being registered.
+ *     Return 0 if permission is granted.
+ * @binder_transaction
+ *     Check whether @from is allowed to invoke a binder transaction call
+ *     to @to.
+ *     @from contains the task_struct for the sending task.
+ *     @to contains the task_struct for the receiving task.
+ * @binder_transfer_binder
+ *     Check whether @from is allowed to transfer a binder reference to @to.
+ *     @from contains the task_struct for the sending task.
+ *     @to contains the task_struct for the receiving task.
+ * @binder_transfer_file
+ *     Check whether @from is allowed to transfer @file to @to.
+ *     @from contains the task_struct for the sending task.
+ *     @file contains the struct file being transferred.
+ *     @to contains the task_struct for the receiving task.
+ *
  * @ptrace_access_check:
  *     Check permission before allowing the current process to trace the
  *     @child process.
@@ -1441,6 +1460,14 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
 struct security_operations {
        char name[SECURITY_NAME_MAX + 1];
 
+       int (*binder_set_context_mgr) (struct task_struct *mgr);
+       int (*binder_transaction) (struct task_struct *from,
+                                  struct task_struct *to);
+       int (*binder_transfer_binder) (struct task_struct *from,
+                                      struct task_struct *to);
+       int (*binder_transfer_file) (struct task_struct *from,
+                                    struct task_struct *to, struct file *file);
+
        int (*ptrace_access_check) (struct task_struct *child, unsigned int mode);
        int (*ptrace_traceme) (struct task_struct *parent);
        int (*capget) (struct task_struct *target,
@@ -1739,6 +1766,13 @@ extern void __init security_fixup_ops(struct security_operations *ops);
 
 
 /* Security operations */
+int security_binder_set_context_mgr(struct task_struct *mgr);
+int security_binder_transaction(struct task_struct *from,
+                               struct task_struct *to);
+int security_binder_transfer_binder(struct task_struct *from,
+                                   struct task_struct *to);
+int security_binder_transfer_file(struct task_struct *from,
+                                 struct task_struct *to, struct file *file);
 int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
 int security_ptrace_traceme(struct task_struct *parent);
 int security_capget(struct task_struct *target,
@@ -1927,6 +1961,30 @@ static inline int security_init(void)
        return 0;
 }
 
+static inline int security_binder_set_context_mgr(struct task_struct *mgr)
+{
+       return 0;
+}
+
+static inline int security_binder_transaction(struct task_struct *from,
+                                             struct task_struct *to)
+{
+       return 0;
+}
+
+static inline int security_binder_transfer_binder(struct task_struct *from,
+                                                 struct task_struct *to)
+{
+       return 0;
+}
+
+static inline int security_binder_transfer_file(struct task_struct *from,
+                                               struct task_struct *to,
+                                               struct file *file)
+{
+       return 0;
+}
+
 static inline int security_ptrace_access_check(struct task_struct *child,
                                             unsigned int mode)
 {
index d68c57a62bcf72694b7705292ac3a61592dc67af..070dd46f62f4f57c7262211352775e121439e8a2 100644 (file)
 
 #include <linux/security.h>
 
+static int cap_binder_set_context_mgr(struct task_struct *mgr)
+{
+       return 0;
+}
+
+static int cap_binder_transaction(struct task_struct *from,
+                                 struct task_struct *to)
+{
+       return 0;
+}
+
+static int cap_binder_transfer_binder(struct task_struct *from,
+                                     struct task_struct *to)
+{
+       return 0;
+}
+
+static int cap_binder_transfer_file(struct task_struct *from,
+                                   struct task_struct *to, struct file *file)
+{
+       return 0;
+}
+
 static int cap_syslog(int type)
 {
        return 0;
@@ -930,6 +953,10 @@ static void cap_audit_rule_free(void *lsmrule)
 
 void __init security_fixup_ops(struct security_operations *ops)
 {
+       set_to_cap_if_null(ops, binder_set_context_mgr);
+       set_to_cap_if_null(ops, binder_transaction);
+       set_to_cap_if_null(ops, binder_transfer_binder);
+       set_to_cap_if_null(ops, binder_transfer_file);
        set_to_cap_if_null(ops, ptrace_access_check);
        set_to_cap_if_null(ops, ptrace_traceme);
        set_to_cap_if_null(ops, capget);
index 18b35c63fc0c80cb3f0ce9dfc734f42c33efe98b..b196de34b19ff91a8b44404a733f741a430dcabb 100644 (file)
@@ -135,6 +135,29 @@ int __init register_security(struct security_operations *ops)
 
 /* Security operations */
 
+int security_binder_set_context_mgr(struct task_struct *mgr)
+{
+       return security_ops->binder_set_context_mgr(mgr);
+}
+
+int security_binder_transaction(struct task_struct *from,
+                               struct task_struct *to)
+{
+       return security_ops->binder_transaction(from, to);
+}
+
+int security_binder_transfer_binder(struct task_struct *from,
+                                   struct task_struct *to)
+{
+       return security_ops->binder_transfer_binder(from, to);
+}
+
+int security_binder_transfer_file(struct task_struct *from,
+                                 struct task_struct *to, struct file *file)
+{
+       return security_ops->binder_transfer_file(from, to, file);
+}
+
 int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
 {
 #ifdef CONFIG_SECURITY_YAMA_STACKED
index 6da7532893a1973e660f5eabf525d5769c655fef..9d984bfb978bcee8d145a74afe1ca27136a2499b 100644 (file)
@@ -1933,6 +1933,74 @@ static inline u32 open_file_to_av(struct file *file)
 
 /* Hook functions begin here. */
 
+static int selinux_binder_set_context_mgr(struct task_struct *mgr)
+{
+       u32 mysid = current_sid();
+       u32 mgrsid = task_sid(mgr);
+
+       return avc_has_perm(mysid, mgrsid, SECCLASS_BINDER,
+                           BINDER__SET_CONTEXT_MGR, NULL);
+}
+
+static int selinux_binder_transaction(struct task_struct *from,
+                                     struct task_struct *to)
+{
+       u32 mysid = current_sid();
+       u32 fromsid = task_sid(from);
+       u32 tosid = task_sid(to);
+       int rc;
+
+       if (mysid != fromsid) {
+               rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER,
+                                 BINDER__IMPERSONATE, NULL);
+               if (rc)
+                       return rc;
+       }
+
+       return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__CALL,
+                           NULL);
+}
+
+static int selinux_binder_transfer_binder(struct task_struct *from,
+                                         struct task_struct *to)
+{
+       u32 fromsid = task_sid(from);
+       u32 tosid = task_sid(to);
+
+       return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER,
+                           NULL);
+}
+
+static int selinux_binder_transfer_file(struct task_struct *from,
+                                       struct task_struct *to,
+                                       struct file *file)
+{
+       u32 sid = task_sid(to);
+       struct file_security_struct *fsec = file->f_security;
+       struct inode *inode = file->f_path.dentry->d_inode;
+       struct inode_security_struct *isec = inode->i_security;
+       struct common_audit_data ad;
+       int rc;
+
+       ad.type = LSM_AUDIT_DATA_PATH;
+       ad.u.path = file->f_path;
+
+       if (sid != fsec->sid) {
+               rc = avc_has_perm(sid, fsec->sid,
+                                 SECCLASS_FD,
+                                 FD__USE,
+                                 &ad);
+               if (rc)
+                       return rc;
+       }
+
+       if (unlikely(IS_PRIVATE(inode)))
+               return 0;
+
+       return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file),
+                           &ad);
+}
+
 static int selinux_ptrace_access_check(struct task_struct *child,
                                     unsigned int mode)
 {
@@ -5810,6 +5878,11 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
 static struct security_operations selinux_ops = {
        .name =                         "selinux",
 
+       .binder_set_context_mgr =       selinux_binder_set_context_mgr,
+       .binder_transaction =           selinux_binder_transaction,
+       .binder_transfer_binder =       selinux_binder_transfer_binder,
+       .binder_transfer_file =         selinux_binder_transfer_file,
+
        .ptrace_access_check =          selinux_ptrace_access_check,
        .ptrace_traceme =               selinux_ptrace_traceme,
        .capget =                       selinux_capget,
index be491a74c1edc4a279f76121dab615305c3a47f2..eccd61b3de8aa34f8c3664f0e62955eda52ce3c2 100644 (file)
@@ -151,5 +151,7 @@ struct security_class_mapping secclass_map[] = {
        { "kernel_service", { "use_as_override", "create_files_as", NULL } },
        { "tun_socket",
          { COMMON_SOCK_PERMS, "attach_queue", NULL } },
+       { "binder", { "impersonate", "call", "set_context_mgr", "transfer",
+                     NULL } },
        { NULL }
   };