binder: create node flag to request sender's security context

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>
This commit is contained in:
Todd Kjos 2019-01-14 09:10:21 -08:00 committed by Greg Kroah-Hartman
parent 8fdf906265
commit ec74136ded
2 changed files with 102 additions and 23 deletions

View File

@ -329,6 +329,8 @@ struct binder_error {
* (invariant after initialized) * (invariant after initialized)
* @min_priority: minimum scheduling priority * @min_priority: minimum scheduling priority
* (invariant after initialized) * (invariant after initialized)
* @txn_security_ctx: require sender's security context
* (invariant after initialized)
* @async_todo: list of async work items * @async_todo: list of async work items
* (protected by @proc->inner_lock) * (protected by @proc->inner_lock)
* *
@ -365,6 +367,7 @@ struct binder_node {
* invariant after initialization * invariant after initialization
*/ */
u8 accept_fds:1; u8 accept_fds:1;
u8 txn_security_ctx:1;
u8 min_priority; u8 min_priority;
}; };
bool has_async_transaction; bool has_async_transaction;
@ -615,6 +618,7 @@ struct binder_transaction {
long saved_priority; long saved_priority;
kuid_t sender_euid; kuid_t sender_euid;
struct list_head fd_fixups; struct list_head fd_fixups;
binder_uintptr_t security_ctx;
/** /**
* @lock: protects @from, @to_proc, and @to_thread * @lock: protects @from, @to_proc, and @to_thread
* *
@ -1152,6 +1156,7 @@ static struct binder_node *binder_init_node_ilocked(
node->work.type = BINDER_WORK_NODE; node->work.type = BINDER_WORK_NODE;
node->min_priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; node->min_priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
node->txn_security_ctx = !!(flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX);
spin_lock_init(&node->lock); spin_lock_init(&node->lock);
INIT_LIST_HEAD(&node->work.entry); INIT_LIST_HEAD(&node->work.entry);
INIT_LIST_HEAD(&node->async_todo); INIT_LIST_HEAD(&node->async_todo);
@ -2778,6 +2783,8 @@ static void binder_transaction(struct binder_proc *proc,
binder_size_t last_fixup_min_off = 0; binder_size_t last_fixup_min_off = 0;
struct binder_context *context = proc->context; struct binder_context *context = proc->context;
int t_debug_id = atomic_inc_return(&binder_last_id); 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 = binder_transaction_log_add(&binder_transaction_log);
e->debug_id = t_debug_id; e->debug_id = t_debug_id;
@ -3020,6 +3027,20 @@ static void binder_transaction(struct binder_proc *proc,
t->flags = tr->flags; t->flags = tr->flags;
t->priority = task_nice(current); t->priority = task_nice(current);
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); trace_binder_transaction(reply, t, target_node);
t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size, t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
@ -3036,6 +3057,19 @@ static void binder_transaction(struct binder_proc *proc,
t->buffer = NULL; t->buffer = NULL;
goto err_binder_alloc_buf_failed; 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->debug_id = t->debug_id;
t->buffer->transaction = t; t->buffer->transaction = t;
t->buffer->target_node = target_node; t->buffer->target_node = target_node;
@ -3305,6 +3339,9 @@ static void binder_transaction(struct binder_proc *proc,
t->buffer->transaction = NULL; t->buffer->transaction = NULL;
binder_alloc_free_buf(&target_proc->alloc, t->buffer); binder_alloc_free_buf(&target_proc->alloc, t->buffer);
err_binder_alloc_buf_failed: err_binder_alloc_buf_failed:
if (secctx)
security_release_secctx(secctx, secctx_sz);
err_get_secctx_failed:
kfree(tcomplete); kfree(tcomplete);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
err_alloc_tcomplete_failed: err_alloc_tcomplete_failed:
@ -4036,11 +4073,13 @@ static int binder_thread_read(struct binder_proc *proc,
while (1) { while (1) {
uint32_t cmd; 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 binder_work *w = NULL;
struct list_head *list = NULL; struct list_head *list = NULL;
struct binder_transaction *t = NULL; struct binder_transaction *t = NULL;
struct binder_thread *t_from; struct binder_thread *t_from;
size_t trsize = sizeof(*trd);
binder_inner_proc_lock(proc); binder_inner_proc_lock(proc);
if (!binder_worklist_empty_ilocked(&thread->todo)) if (!binder_worklist_empty_ilocked(&thread->todo))
@ -4240,8 +4279,8 @@ static int binder_thread_read(struct binder_proc *proc,
if (t->buffer->target_node) { if (t->buffer->target_node) {
struct binder_node *target_node = t->buffer->target_node; struct binder_node *target_node = t->buffer->target_node;
tr.target.ptr = target_node->ptr; trd->target.ptr = target_node->ptr;
tr.cookie = target_node->cookie; trd->cookie = target_node->cookie;
t->saved_priority = task_nice(current); t->saved_priority = task_nice(current);
if (t->priority < target_node->min_priority && if (t->priority < target_node->min_priority &&
!(t->flags & TF_ONE_WAY)) !(t->flags & TF_ONE_WAY))
@ -4251,22 +4290,23 @@ static int binder_thread_read(struct binder_proc *proc,
binder_set_nice(target_node->min_priority); binder_set_nice(target_node->min_priority);
cmd = BR_TRANSACTION; cmd = BR_TRANSACTION;
} else { } else {
tr.target.ptr = 0; trd->target.ptr = 0;
tr.cookie = 0; trd->cookie = 0;
cmd = BR_REPLY; cmd = BR_REPLY;
} }
tr.code = t->code; trd->code = t->code;
tr.flags = t->flags; trd->flags = t->flags;
tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid); trd->sender_euid = from_kuid(current_user_ns(), t->sender_euid);
t_from = binder_get_txn_from(t); t_from = binder_get_txn_from(t);
if (t_from) { if (t_from) {
struct task_struct *sender = t_from->proc->tsk; struct task_struct *sender = t_from->proc->tsk;
tr.sender_pid = task_tgid_nr_ns(sender, trd->sender_pid =
task_active_pid_ns(current)); task_tgid_nr_ns(sender,
task_active_pid_ns(current));
} else { } else {
tr.sender_pid = 0; trd->sender_pid = 0;
} }
ret = binder_apply_fd_fixups(t); ret = binder_apply_fd_fixups(t);
@ -4297,15 +4337,20 @@ static int binder_thread_read(struct binder_proc *proc,
} }
continue; continue;
} }
tr.data_size = t->buffer->data_size; trd->data_size = t->buffer->data_size;
tr.offsets_size = t->buffer->offsets_size; trd->offsets_size = t->buffer->offsets_size;
tr.data.ptr.buffer = (binder_uintptr_t) trd->data.ptr.buffer = (binder_uintptr_t)
((uintptr_t)t->buffer->data + ((uintptr_t)t->buffer->data +
binder_alloc_get_user_buffer_offset(&proc->alloc)); 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, ALIGN(t->buffer->data_size,
sizeof(void *)); 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 (put_user(cmd, (uint32_t __user *)ptr)) {
if (t_from) if (t_from)
binder_thread_dec_tmpref(t_from); binder_thread_dec_tmpref(t_from);
@ -4316,7 +4361,7 @@ static int binder_thread_read(struct binder_proc *proc,
return -EFAULT; return -EFAULT;
} }
ptr += sizeof(uint32_t); ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &tr, sizeof(tr))) { if (copy_to_user(ptr, &tr, trsize)) {
if (t_from) if (t_from)
binder_thread_dec_tmpref(t_from); binder_thread_dec_tmpref(t_from);
@ -4325,7 +4370,7 @@ static int binder_thread_read(struct binder_proc *proc,
return -EFAULT; return -EFAULT;
} }
ptr += sizeof(tr); ptr += trsize;
trace_binder_transaction_received(t); trace_binder_transaction_received(t);
binder_stat_br(proc, thread, cmd); binder_stat_br(proc, thread, cmd);
@ -4333,16 +4378,18 @@ static int binder_thread_read(struct binder_proc *proc,
"%d:%d %s %d %d:%d, cmd %d size %zd-%zd ptr %016llx-%016llx\n", "%d:%d %s %d %d:%d, cmd %d size %zd-%zd ptr %016llx-%016llx\n",
proc->pid, thread->pid, proc->pid, thread->pid,
(cmd == BR_TRANSACTION) ? "BR_TRANSACTION" : (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->debug_id, t_from ? t_from->proc->pid : 0,
t_from ? t_from->pid : 0, cmd, t_from ? t_from->pid : 0, cmd,
t->buffer->data_size, t->buffer->offsets_size, 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) if (t_from)
binder_thread_dec_tmpref(t_from); binder_thread_dec_tmpref(t_from);
t->buffer->allow_user_free = 1; 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); binder_inner_proc_lock(thread->proc);
t->to_parent = thread->transaction_stack; t->to_parent = thread->transaction_stack;
t->to_thread = thread; t->to_thread = thread;
@ -4690,7 +4737,8 @@ static int binder_ioctl_write_read(struct file *filp,
return ret; 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; int ret = 0;
struct binder_proc *proc = filp->private_data; struct binder_proc *proc = filp->private_data;
@ -4719,7 +4767,7 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp)
} else { } else {
context->binder_context_mgr_uid = curr_euid; context->binder_context_mgr_uid = curr_euid;
} }
new_node = binder_new_node(proc, NULL); new_node = binder_new_node(proc, fbo);
if (!new_node) { if (!new_node) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
@ -4842,8 +4890,20 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
binder_inner_proc_unlock(proc); binder_inner_proc_unlock(proc);
break; 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: case BINDER_SET_CONTEXT_MGR:
ret = binder_ioctl_set_ctx_mgr(filp); ret = binder_ioctl_set_ctx_mgr(filp, NULL);
if (ret) if (ret)
goto err; goto err;
break; break;

View File

@ -41,6 +41,14 @@ enum {
enum { enum {
FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff, FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff,
FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100, FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100,
/**
* @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 #ifdef BINDER_IPC_32BIT
@ -218,6 +226,7 @@ struct binder_node_info_for_ref {
#define BINDER_VERSION _IOWR('b', 9, struct binder_version) #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_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_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 * NOTE: Two special error codes you should check for when calling
@ -276,6 +285,11 @@ struct binder_transaction_data {
} 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_sg {
struct binder_transaction_data transaction_data; struct binder_transaction_data transaction_data;
binder_size_t buffers_size; binder_size_t buffers_size;
@ -311,6 +325,11 @@ enum binder_driver_return_protocol {
BR_OK = _IO('r', 1), BR_OK = _IO('r', 1),
/* No parameters! */ /* 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_TRANSACTION = _IOR('r', 2, struct binder_transaction_data),
BR_REPLY = _IOR('r', 3, struct binder_transaction_data), BR_REPLY = _IOR('r', 3, struct binder_transaction_data),
/* /*