FROMGIT: binder: create node flag to request sender's security context
authorTodd Kjos <tkjos@android.com>
Mon, 14 Jan 2019 17:10:21 +0000 (09:10 -0800)
committerJan Altensen <info@stricted.net>
Tue, 15 Oct 2019 12:36:06 +0000 (08:36 -0400)
To allow servers to verify client identity, allow a node
flag to be set that causes the sender's security context
to be delivered with the transaction. The BR_TRANSACTION
command is extended in BR_TRANSACTION_SEC_CTX to
contain a pointer to the security context string.

Signed-off-by: Todd Kjos <tkjos@google.com>
Reviewed-by: Joel Fernandes (Google) <joel@joelfernandes.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit ec74136ded792deed80780a2f8baf3521eeb72f9
 https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
 master)
Change-Id: I44496546e2d0dc0022f818a45cd52feb1c1a92cb
Signed-off-by: Todd Kjos <tkjos@google.com>
drivers/android/binder.c
include/uapi/linux/android/binder.h

index e1284f51d60a25978c9bb692b4c08792c82fdecd..2afba981c74681ec7104e6e8119c09bcfc53911a 100644 (file)
@@ -367,6 +367,7 @@ struct binder_error {
  * @min_priority:         minimum scheduling priority
  *                        (invariant after initialized)
  * @inherit_rt:           inherit RT scheduling policy from caller
+ * @txn_security_ctx:     require sender's security context
  *                        (invariant after initialized)
  * @async_todo:           list of async work items
  *                        (protected by @proc->inner_lock)
@@ -406,6 +407,7 @@ struct binder_node {
                u8 sched_policy:2;
                u8 inherit_rt:1;
                u8 accept_fds:1;
+               u8 txn_security_ctx:1;
                u8 min_priority;
        };
        bool has_async_transaction;
@@ -663,6 +665,7 @@ struct binder_transaction {
        struct binder_priority  saved_priority;
        bool    set_priority_called;
        kuid_t  sender_euid;
+       binder_uintptr_t security_ctx;
        /**
         * @lock:  protects @from, @to_proc, and @to_thread
         *
@@ -1372,6 +1375,7 @@ static struct binder_node *binder_init_node_ilocked(
        node->min_priority = to_kernel_prio(node->sched_policy, priority);
        node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
        node->inherit_rt = !!(flags & FLAT_BINDER_FLAG_INHERIT_RT);
+       node->txn_security_ctx = !!(flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX);
        spin_lock_init(&node->lock);
        INIT_LIST_HEAD(&node->work.entry);
        INIT_LIST_HEAD(&node->async_todo);
@@ -2904,6 +2908,8 @@ static void binder_transaction(struct binder_proc *proc,
        binder_size_t last_fixup_min_off = 0;
        struct binder_context *context = proc->context;
        int t_debug_id = atomic_inc_return(&binder_last_id);
+       char *secctx = NULL;
+       u32 secctx_sz = 0;
 
        e = binder_transaction_log_add(&binder_transaction_log);
        e->debug_id = t_debug_id;
@@ -3127,6 +3133,20 @@ static void binder_transaction(struct binder_proc *proc,
                t->priority = target_proc->default_priority;
        }
 
+       if (target_node && target_node->txn_security_ctx) {
+               u32 secid;
+
+               security_task_getsecid(proc->tsk, &secid);
+               ret = security_secid_to_secctx(secid, &secctx, &secctx_sz);
+               if (ret) {
+                       return_error = BR_FAILED_REPLY;
+                       return_error_param = ret;
+                       return_error_line = __LINE__;
+                       goto err_get_secctx_failed;
+               }
+               extra_buffers_size += ALIGN(secctx_sz, sizeof(u64));
+       }
+
        trace_binder_transaction(reply, t, target_node);
 
        t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
@@ -3143,6 +3163,19 @@ static void binder_transaction(struct binder_proc *proc,
                t->buffer = NULL;
                goto err_binder_alloc_buf_failed;
        }
+       if (secctx) {
+               size_t buf_offset = ALIGN(tr->data_size, sizeof(void *)) +
+                                   ALIGN(tr->offsets_size, sizeof(void *)) +
+                                   ALIGN(extra_buffers_size, sizeof(void *)) -
+                                   ALIGN(secctx_sz, sizeof(u64));
+               char *kptr = t->buffer->data + buf_offset;
+
+               t->security_ctx = (uintptr_t)kptr +
+                   binder_alloc_get_user_buffer_offset(&target_proc->alloc);
+               memcpy(kptr, secctx, secctx_sz);
+               security_release_secctx(secctx, secctx_sz);
+               secctx = NULL;
+       }
        t->buffer->debug_id = t->debug_id;
        t->buffer->transaction = t;
        t->buffer->target_node = target_node;
@@ -3413,6 +3446,9 @@ err_copy_data_failed:
        t->buffer->transaction = NULL;
        binder_alloc_free_buf(&target_proc->alloc, t->buffer);
 err_binder_alloc_buf_failed:
+       if (secctx)
+               security_release_secctx(secctx, secctx_sz);
+err_get_secctx_failed:
        kfree(tcomplete);
        binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
 err_alloc_tcomplete_failed:
@@ -4059,11 +4095,13 @@ retry:
 
        while (1) {
                uint32_t cmd;
-               struct binder_transaction_data tr;
+               struct binder_transaction_data_secctx tr;
+               struct binder_transaction_data *trd = &tr.transaction_data;
                struct binder_work *w = NULL;
                struct list_head *list = NULL;
                struct binder_transaction *t = NULL;
                struct binder_thread *t_from;
+               size_t trsize = sizeof(*trd);
 
                binder_inner_proc_lock(proc);
                if (!binder_worklist_empty_ilocked(&thread->todo))
@@ -4258,41 +4296,47 @@ retry:
                        struct binder_node *target_node = t->buffer->target_node;
                        struct binder_priority node_prio;
 
-                       tr.target.ptr = target_node->ptr;
-                       tr.cookie =  target_node->cookie;
+                       trd->target.ptr = target_node->ptr;
+                       trd->cookie =  target_node->cookie;
                        node_prio.sched_policy = target_node->sched_policy;
                        node_prio.prio = target_node->min_priority;
                        binder_transaction_priority(current, t, node_prio,
                                                    target_node->inherit_rt);
                        cmd = BR_TRANSACTION;
                } else {
-                       tr.target.ptr = 0;
-                       tr.cookie = 0;
+                       trd->target.ptr = 0;
+                       trd->cookie = 0;
                        cmd = BR_REPLY;
                }
-               tr.code = t->code;
-               tr.flags = t->flags;
-               tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
+               trd->code = t->code;
+               trd->flags = t->flags;
+               trd->sender_euid = from_kuid(current_user_ns(), t->sender_euid);
 
                t_from = binder_get_txn_from(t);
                if (t_from) {
                        struct task_struct *sender = t_from->proc->tsk;
 
-                       tr.sender_pid = task_tgid_nr_ns(sender,
-                                                       task_active_pid_ns(current));
+                       trd->sender_pid =
+                               task_tgid_nr_ns(sender,
+                                               task_active_pid_ns(current));
                } else {
-                       tr.sender_pid = 0;
+                       trd->sender_pid = 0;
                }
 
-               tr.data_size = t->buffer->data_size;
-               tr.offsets_size = t->buffer->offsets_size;
-               tr.data.ptr.buffer = (binder_uintptr_t)
+               trd->data_size = t->buffer->data_size;
+               trd->offsets_size = t->buffer->offsets_size;
+               trd->data.ptr.buffer = (binder_uintptr_t)
                        ((uintptr_t)t->buffer->data +
                        binder_alloc_get_user_buffer_offset(&proc->alloc));
-               tr.data.ptr.offsets = tr.data.ptr.buffer +
+               trd->data.ptr.offsets = trd->data.ptr.buffer +
                                        ALIGN(t->buffer->data_size,
                                            sizeof(void *));
 
+               tr.secctx = t->security_ctx;
+               if (t->security_ctx) {
+                       cmd = BR_TRANSACTION_SEC_CTX;
+                       trsize = sizeof(tr);
+               }
                if (put_user(cmd, (uint32_t __user *)ptr)) {
                        if (t_from)
                                binder_thread_dec_tmpref(t_from);
@@ -4303,7 +4347,7 @@ retry:
                        return -EFAULT;
                }
                ptr += sizeof(uint32_t);
-               if (copy_to_user(ptr, &tr, sizeof(tr))) {
+               if (copy_to_user(ptr, &tr, trsize)) {
                        if (t_from)
                                binder_thread_dec_tmpref(t_from);
 
@@ -4312,7 +4356,7 @@ retry:
 
                        return -EFAULT;
                }
-               ptr += sizeof(tr);
+               ptr += trsize;
 
                trace_binder_transaction_received(t);
                binder_stat_br(proc, thread, cmd);
@@ -4320,16 +4364,18 @@ retry:
                             "%d:%d %s %d %d:%d, cmd %d size %zd-%zd ptr %016llx-%016llx\n",
                             proc->pid, thread->pid,
                             (cmd == BR_TRANSACTION) ? "BR_TRANSACTION" :
-                            "BR_REPLY",
+                               (cmd == BR_TRANSACTION_SEC_CTX) ?
+                                    "BR_TRANSACTION_SEC_CTX" : "BR_REPLY",
                             t->debug_id, t_from ? t_from->proc->pid : 0,
                             t_from ? t_from->pid : 0, cmd,
                             t->buffer->data_size, t->buffer->offsets_size,
-                            (u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets);
+                            (u64)trd->data.ptr.buffer,
+                            (u64)trd->data.ptr.offsets);
 
                if (t_from)
                        binder_thread_dec_tmpref(t_from);
                t->buffer->allow_user_free = 1;
-               if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
+               if (cmd != BR_REPLY && !(t->flags & TF_ONE_WAY)) {
                        binder_inner_proc_lock(thread->proc);
                        t->to_parent = thread->transaction_stack;
                        t->to_thread = thread;
@@ -4672,7 +4718,8 @@ out:
        return ret;
 }
 
-static int binder_ioctl_set_ctx_mgr(struct file *filp)
+static int binder_ioctl_set_ctx_mgr(struct file *filp,
+                                   struct flat_binder_object *fbo)
 {
        int ret = 0;
        struct binder_proc *proc = filp->private_data;
@@ -4701,7 +4748,7 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp)
        } else {
                context->binder_context_mgr_uid = curr_euid;
        }
-       new_node = binder_new_node(proc, NULL);
+       new_node = binder_new_node(proc, fbo);
        if (!new_node) {
                ret = -ENOMEM;
                goto out;
@@ -4823,8 +4870,20 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                binder_inner_proc_unlock(proc);
                break;
        }
+       case BINDER_SET_CONTEXT_MGR_EXT: {
+               struct flat_binder_object fbo;
+
+               if (copy_from_user(&fbo, ubuf, sizeof(fbo))) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               ret = binder_ioctl_set_ctx_mgr(filp, &fbo);
+               if (ret)
+                       goto err;
+               break;
+       }
        case BINDER_SET_CONTEXT_MGR:
-               ret = binder_ioctl_set_ctx_mgr(filp);
+               ret = binder_ioctl_set_ctx_mgr(filp, NULL);
                if (ret)
                        goto err;
                break;
index 2ff6b15ac9183c2bcfcc162a94b5331c9e79e71f..d5ba82daefefb59263c9071a258c3eee12190c35 100644 (file)
@@ -86,6 +86,14 @@ enum flat_binder_object_flags {
         * scheduling policy from the caller (for synchronous transactions).
         */
        FLAT_BINDER_FLAG_INHERIT_RT = 0x800,
+
+       /**
+        * @FLAT_BINDER_FLAG_TXN_SECURITY_CTX: request security contexts
+        *
+        * Only when set, causes senders to include their security
+        * context
+        */
+       FLAT_BINDER_FLAG_TXN_SECURITY_CTX = 0x1000,
 };
 
 #ifdef BINDER_IPC_32BIT
@@ -261,6 +269,7 @@ struct binder_node_info_for_ref {
 #define BINDER_VERSION                 _IOWR('b', 9, struct binder_version)
 #define BINDER_GET_NODE_DEBUG_INFO     _IOWR('b', 11, struct binder_node_debug_info)
 #define BINDER_GET_NODE_INFO_FOR_REF   _IOWR('b', 12, struct binder_node_info_for_ref)
+#define BINDER_SET_CONTEXT_MGR_EXT     _IOW('b', 13, struct flat_binder_object)
 
 /*
  * NOTE: Two special error codes you should check for when calling
@@ -319,6 +328,11 @@ struct binder_transaction_data {
        } data;
 };
 
+struct binder_transaction_data_secctx {
+       struct binder_transaction_data transaction_data;
+       binder_uintptr_t secctx;
+};
+
 struct binder_transaction_data_sg {
        struct binder_transaction_data transaction_data;
        binder_size_t buffers_size;
@@ -354,6 +368,11 @@ enum binder_driver_return_protocol {
        BR_OK = _IO('r', 1),
        /* No parameters! */
 
+       BR_TRANSACTION_SEC_CTX = _IOR('r', 2,
+                                     struct binder_transaction_data_secctx),
+       /*
+        * binder_transaction_data_secctx: the received command.
+        */
        BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data),
        BR_REPLY = _IOR('r', 3, struct binder_transaction_data),
        /*