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.
Unified · Split
| @@ -329,6 +329,8 @@ struct binder_error { · 328 unmodified lines | ||
| 329 | 329 | * (invariant after initialized) |
| 330 | 330 | * @min_priority: minimum scheduling priority |
| 331 | 331 | * (invariant after initialized) |
| 332 | + * @txn_security_ctx: require sender's security context | |
| 333 | + * (invariant after initialized) | |
| 332 | 334 | * @async_todo: list of async work items |
| 333 | 335 | * (protected by @proc->inner_lock) |
| 334 | 336 | * |
| @@ -365,6 +367,7 @@ struct binder_node { · 30 unmodified lines | ||
| 365 | 367 | * invariant after initialization |
| 366 | 368 | */ |
| 367 | 369 | u8 accept_fds:1; |
| 370 | + u8 txn_security_ctx:1; | |
| 368 | 371 | u8 min_priority; |
| 369 | 372 | }; |
| 370 | 373 | bool has_async_transaction; |
| @@ -615,6 +618,7 @@ struct binder_transaction { · 244 unmodified lines | ||
| 615 | 618 | long saved_priority; |
| 616 | 619 | kuid_t sender_euid; |
| 617 | 620 | struct list_head fd_fixups; |
| 621 | + binder_uintptr_t security_ctx; | |
| 618 | 622 | /** |
| 619 | 623 | * @lock: protects @from, @to_proc, and @to_thread |
| 620 | 624 | * |
| @@ -1152,6 +1156,7 @@ static struct binder_node *binder_init_node_ilocked( · 531 unmodified lines | ||
| 1152 | 1156 | node->work.type = BINDER_WORK_NODE; |
| 1153 | 1157 | node->min_priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; |
| 1154 | 1158 | node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); |
| 1159 | + node->txn_security_ctx = !!(flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX); | |
| 1155 | 1160 | spin_lock_init(&node->lock); |
| 1156 | 1161 | INIT_LIST_HEAD(&node->work.entry); |
| 1157 | 1162 | INIT_LIST_HEAD(&node->async_todo); |
| @@ -2778,6 +2783,8 @@ static void binder_transaction(struct binder_proc *proc, · 1620 unmodified lines | ||
| 2778 | 2783 | binder_size_t last_fixup_min_off = 0; |
| 2779 | 2784 | struct binder_context *context = proc->context; |
| 2780 | 2785 | int t_debug_id = atomic_inc_return(&binder_last_id); |
| 2786 | + char *secctx = NULL; | |
| 2787 | + u32 secctx_sz = 0; | |
| 2781 | 2788 | |
| 2782 | 2789 | e = binder_transaction_log_add(&binder_transaction_log); |
| 2783 | 2790 | e->debug_id = t_debug_id; |
| @@ -3020,6 +3027,20 @@ static void binder_transaction(struct binder_proc *proc, · 236 unmodified lines | ||
| 3020 | 3027 | t->flags = tr->flags; |
| 3021 | 3028 | t->priority = task_nice(current); |
| 3022 | 3029 | |
| 3030 | + if (target_node && target_node->txn_security_ctx) { | |
| 3031 | + u32 secid; | |
| 3032 | + | |
| 3033 | + security_task_getsecid(proc->tsk, &secid); | |
| 3034 | + ret = security_secid_to_secctx(secid, &secctx, &secctx_sz); | |
| 3035 | + if (ret) { | |
| 3036 | + return_error = BR_FAILED_REPLY; | |
| 3037 | + return_error_param = ret; | |
| 3038 | + return_error_line = __LINE__; | |
| 3039 | + goto err_get_secctx_failed; | |
| 3040 | + } | |
| 3041 | + extra_buffers_size += ALIGN(secctx_sz, sizeof(u64)); | |
| 3042 | + } | |
| 3043 | + | |
| 3023 | 3044 | trace_binder_transaction(reply, t, target_node); |
| 3024 | 3045 | |
| 3025 | 3046 | t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size, |
| @@ -3036,6 +3057,19 @@ static void binder_transaction(struct binder_proc *proc, · 10 unmodified lines | ||
| 3036 | 3057 | t->buffer = NULL; |
| 3037 | 3058 | goto err_binder_alloc_buf_failed; |
| 3038 | 3059 | } |
| 3060 | + if (secctx) { | |
| 3061 | + size_t buf_offset = ALIGN(tr->data_size, sizeof(void *)) + | |
| 3062 | + ALIGN(tr->offsets_size, sizeof(void *)) + | |
| 3063 | + ALIGN(extra_buffers_size, sizeof(void *)) - | |
| 3064 | + ALIGN(secctx_sz, sizeof(u64)); | |
| 3065 | + char *kptr = t->buffer->data + buf_offset; | |
| 3066 | + | |
| 3067 | + t->security_ctx = (uintptr_t)kptr + | |
| 3068 | + binder_alloc_get_user_buffer_offset(&target_proc->alloc); | |
| 3069 | + memcpy(kptr, secctx, secctx_sz); | |
| 3070 | + security_release_secctx(secctx, secctx_sz); | |
| 3071 | + secctx = NULL; | |
| 3072 | + } | |
| 3039 | 3073 | t->buffer->debug_id = t->debug_id; |
| 3040 | 3074 | t->buffer->transaction = t; |
| 3041 | 3075 | t->buffer->target_node = target_node; |
| @@ -3305,6 +3339,9 @@ err_copy_data_failed: · 263 unmodified lines | ||
| 3305 | 3339 | t->buffer->transaction = NULL; |
| 3306 | 3340 | binder_alloc_free_buf(&target_proc->alloc, t->buffer); |
| 3307 | 3341 | err_binder_alloc_buf_failed: |
| 3342 | + if (secctx) | |
| 3343 | + security_release_secctx(secctx, secctx_sz); | |
| 3344 | +err_get_secctx_failed: | |
| 3308 | 3345 | kfree(tcomplete); |
| 3309 | 3346 | binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); |
| 3310 | 3347 | err_alloc_tcomplete_failed: |
| @@ -4036,11 +4073,13 @@ retry: · 725 unmodified lines | ||
| 4036 | 4073 | |
| 4037 | 4074 | while (1) { |
| 4038 | 4075 | uint32_t cmd; |
| 4039 | − struct binder_transaction_data tr; | |
| 4076 | + struct binder_transaction_data_secctx tr; | |
| 4077 | + struct binder_transaction_data *trd = &tr.transaction_data; | |
| 4040 | 4078 | struct binder_work *w = NULL; |
| 4041 | 4079 | struct list_head *list = NULL; |
| 4042 | 4080 | struct binder_transaction *t = NULL; |
| 4043 | 4081 | struct binder_thread *t_from; |
| 4082 | + size_t trsize = sizeof(*trd); | |
| 4044 | 4083 | |
| 4045 | 4084 | binder_inner_proc_lock(proc); |
| 4046 | 4085 | if (!binder_worklist_empty_ilocked(&thread->todo)) |
| @@ -4240,8 +4279,8 @@ retry: · 193 unmodified lines | ||
| 4240 | 4279 | if (t->buffer->target_node) { |
| 4241 | 4280 | struct binder_node *target_node = t->buffer->target_node; |
| 4242 | 4281 | |
| 4243 | − tr.target.ptr = target_node->ptr; | |
| 4244 | − tr.cookie = target_node->cookie; | |
| 4282 | + trd->target.ptr = target_node->ptr; | |
| 4283 | + trd->cookie = target_node->cookie; | |
| 4245 | 4284 | t->saved_priority = task_nice(current); |
| 4246 | 4285 | if (t->priority < target_node->min_priority && |
| 4247 | 4286 | !(t->flags & TF_ONE_WAY)) |
| @@ -4251,22 +4290,23 @@ retry: · 3 unmodified lines | ||
| 4251 | 4290 | binder_set_nice(target_node->min_priority); |
| 4252 | 4291 | cmd = BR_TRANSACTION; |
| 4253 | 4292 | } else { |
| 4254 | − tr.target.ptr = 0; | |
| 4255 | − tr.cookie = 0; | |
| 4293 | + trd->target.ptr = 0; | |
| 4294 | + trd->cookie = 0; | |
| 4256 | 4295 | cmd = BR_REPLY; |
| 4257 | 4296 | } |
| 4258 | − tr.code = t->code; | |
| 4259 | − tr.flags = t->flags; | |
| 4260 | − tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid); | |
| 4297 | + trd->code = t->code; | |
| 4298 | + trd->flags = t->flags; | |
| 4299 | + trd->sender_euid = from_kuid(current_user_ns(), t->sender_euid); | |
| 4261 | 4300 | |
| 4262 | 4301 | t_from = binder_get_txn_from(t); |
| 4263 | 4302 | if (t_from) { |
| 4264 | 4303 | struct task_struct *sender = t_from->proc->tsk; |
| 4265 | 4304 | |
| 4266 | − tr.sender_pid = task_tgid_nr_ns(sender, | |
| 4267 | − task_active_pid_ns(current)); | |
| 4305 | + trd->sender_pid = | |
| 4306 | + task_tgid_nr_ns(sender, | |
| 4307 | + task_active_pid_ns(current)); | |
| 4268 | 4308 | } else { |
| 4269 | − tr.sender_pid = 0; | |
| 4309 | + trd->sender_pid = 0; | |
| 4270 | 4310 | } |
| 4271 | 4311 | |
| 4272 | 4312 | ret = binder_apply_fd_fixups(t); |
| @@ -4297,15 +4337,20 @@ retry: · 24 unmodified lines | ||
| 4297 | 4337 | } |
| 4298 | 4338 | continue; |
| 4299 | 4339 | } |
| 4300 | − tr.data_size = t->buffer->data_size; | |
| 4301 | − tr.offsets_size = t->buffer->offsets_size; | |
| 4302 | − tr.data.ptr.buffer = (binder_uintptr_t) | |
| 4340 | + trd->data_size = t->buffer->data_size; | |
| 4341 | + trd->offsets_size = t->buffer->offsets_size; | |
| 4342 | + trd->data.ptr.buffer = (binder_uintptr_t) | |
| 4303 | 4343 | ((uintptr_t)t->buffer->data + |
| 4304 | 4344 | binder_alloc_get_user_buffer_offset(&proc->alloc)); |
| 4305 | − tr.data.ptr.offsets = tr.data.ptr.buffer + | |
| 4345 | + trd->data.ptr.offsets = trd->data.ptr.buffer + | |
| 4306 | 4346 | ALIGN(t->buffer->data_size, |
| 4307 | 4347 | sizeof(void *)); |
| 4308 | 4348 | |
| 4349 | + tr.secctx = t->security_ctx; | |
| 4350 | + if (t->security_ctx) { | |
| 4351 | + cmd = BR_TRANSACTION_SEC_CTX; | |
| 4352 | + trsize = sizeof(tr); | |
| 4353 | + } | |
| 4309 | 4354 | if (put_user(cmd, (uint32_t __user *)ptr)) { |
| 4310 | 4355 | if (t_from) |
| 4311 | 4356 | binder_thread_dec_tmpref(t_from); |
| @@ -4316,7 +4361,7 @@ retry: · 4 unmodified lines | ||
| 4316 | 4361 | return -EFAULT; |
| 4317 | 4362 | } |
| 4318 | 4363 | ptr += sizeof(uint32_t); |
| 4319 | − if (copy_to_user(ptr, &tr, sizeof(tr))) { | |
| 4364 | + if (copy_to_user(ptr, &tr, trsize)) { | |
| 4320 | 4365 | if (t_from) |
| 4321 | 4366 | binder_thread_dec_tmpref(t_from); |
| 4322 | 4367 | |
| @@ -4325,7 +4370,7 @@ retry: · 2 unmodified lines | ||
| 4325 | 4370 | |
| 4326 | 4371 | return -EFAULT; |
| 4327 | 4372 | } |
| 4328 | − ptr += sizeof(tr); | |
| 4373 | + ptr += trsize; | |
| 4329 | 4374 | |
| 4330 | 4375 | trace_binder_transaction_received(t); |
| 4331 | 4376 | binder_stat_br(proc, thread, cmd); |
| @@ -4333,16 +4378,18 @@ retry: · 1 unmodified line | ||
| 4333 | 4378 | "%d:%d %s %d %d:%d, cmd %d size %zd-%zd ptr %016llx-%016llx\n", |
| 4334 | 4379 | proc->pid, thread->pid, |
| 4335 | 4380 | (cmd == BR_TRANSACTION) ? "BR_TRANSACTION" : |
| 4336 | − "BR_REPLY", | |
| 4381 | + (cmd == BR_TRANSACTION_SEC_CTX) ? | |
| 4382 | + "BR_TRANSACTION_SEC_CTX" : "BR_REPLY", | |
| 4337 | 4383 | t->debug_id, t_from ? t_from->proc->pid : 0, |
| 4338 | 4384 | t_from ? t_from->pid : 0, cmd, |
| 4339 | 4385 | t->buffer->data_size, t->buffer->offsets_size, |
| 4340 | − (u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets); | |
| 4386 | + (u64)trd->data.ptr.buffer, | |
| 4387 | + (u64)trd->data.ptr.offsets); | |
| 4341 | 4388 | |
| 4342 | 4389 | if (t_from) |
| 4343 | 4390 | binder_thread_dec_tmpref(t_from); |
| 4344 | 4391 | t->buffer->allow_user_free = 1; |
| 4345 | − if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) { | |
| 4392 | + if (cmd != BR_REPLY && !(t->flags & TF_ONE_WAY)) { | |
| 4346 | 4393 | binder_inner_proc_lock(thread->proc); |
| 4347 | 4394 | t->to_parent = thread->transaction_stack; |
| 4348 | 4395 | t->to_thread = thread; |
| @@ -4690,7 +4737,8 @@ out: · 341 unmodified lines | ||
| 4690 | 4737 | return ret; |
| 4691 | 4738 | } |
| 4692 | 4739 | |
| 4693 | −static int binder_ioctl_set_ctx_mgr(struct file *filp) | |
| 4740 | +static int binder_ioctl_set_ctx_mgr(struct file *filp, | |
| 4741 | + struct flat_binder_object *fbo) | |
| 4694 | 4742 | { |
| 4695 | 4743 | int ret = 0; |
| 4696 | 4744 | struct binder_proc *proc = filp->private_data; |
| @@ -4719,7 +4767,7 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp) · 22 unmodified lines | ||
| 4719 | 4767 | } else { |
| 4720 | 4768 | context->binder_context_mgr_uid = curr_euid; |
| 4721 | 4769 | } |
| 4722 | − new_node = binder_new_node(proc, NULL); | |
| 4770 | + new_node = binder_new_node(proc, fbo); | |
| 4723 | 4771 | if (!new_node) { |
| 4724 | 4772 | ret = -ENOMEM; |
| 4725 | 4773 | goto out; |
| @@ -4842,8 +4890,20 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) · 116 unmodified lines | ||
| 4842 | 4890 | binder_inner_proc_unlock(proc); |
| 4843 | 4891 | break; |
| 4844 | 4892 | } |
| 4893 | + case BINDER_SET_CONTEXT_MGR_EXT: { | |
| 4894 | + struct flat_binder_object fbo; | |
| 4895 | + | |
| 4896 | + if (copy_from_user(&fbo, ubuf, sizeof(fbo))) { | |
| 4897 | + ret = -EINVAL; | |
| 4898 | + goto err; | |
| 4899 | + } | |
| 4900 | + ret = binder_ioctl_set_ctx_mgr(filp, &fbo); | |
| 4901 | + if (ret) | |
| 4902 | + goto err; | |
| 4903 | + break; | |
| 4904 | + } | |
| 4845 | 4905 | case BINDER_SET_CONTEXT_MGR: |
| 4846 | − ret = binder_ioctl_set_ctx_mgr(filp); | |
| 4906 | + ret = binder_ioctl_set_ctx_mgr(filp, NULL); | |
| 4847 | 4907 | if (ret) |
| 4848 | 4908 | goto err; |
| 4849 | 4909 | break; |
| @@ -41,6 +41,14 @@ enum { · 40 unmodified lines | ||
| 41 | 41 | enum { |
| 42 | 42 | FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff, |
| 43 | 43 | FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100, |
| 44 | + | |
| 45 | + /** | |
| 46 | + * @FLAT_BINDER_FLAG_TXN_SECURITY_CTX: request security contexts | |
| 47 | + * | |
| 48 | + * Only when set, causes senders to include their security | |
| 49 | + * context | |
| 50 | + */ | |
| 51 | + FLAT_BINDER_FLAG_TXN_SECURITY_CTX = 0x1000, | |
| 44 | 52 | }; |
| 45 | 53 | |
| 46 | 54 | #ifdef BINDER_IPC_32BIT |
| @@ -218,6 +226,7 @@ struct binder_node_info_for_ref { · 171 unmodified lines | ||
| 218 | 226 | #define BINDER_VERSION _IOWR('b', 9, struct binder_version) |
| 219 | 227 | #define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info) |
| 220 | 228 | #define BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref) |
| 229 | +#define BINDER_SET_CONTEXT_MGR_EXT _IOW('b', 13, struct flat_binder_object) | |
| 221 | 230 | |
| 222 | 231 | /* |
| 223 | 232 | * NOTE: Two special error codes you should check for when calling |
| @@ -276,6 +285,11 @@ struct binder_transaction_data { · 52 unmodified lines | ||
| 276 | 285 | } data; |
| 277 | 286 | }; |
| 278 | 287 | |
| 288 | +struct binder_transaction_data_secctx { | |
| 289 | + struct binder_transaction_data transaction_data; | |
| 290 | + binder_uintptr_t secctx; | |
| 291 | +}; | |
| 292 | + | |
| 279 | 293 | struct binder_transaction_data_sg { |
| 280 | 294 | struct binder_transaction_data transaction_data; |
| 281 | 295 | binder_size_t buffers_size; |
| @@ -311,6 +325,11 @@ enum binder_driver_return_protocol { · 29 unmodified lines | ||
| 311 | 325 | BR_OK = _IO('r', 1), |
| 312 | 326 | /* No parameters! */ |
| 313 | 327 | |
| 328 | + BR_TRANSACTION_SEC_CTX = _IOR('r', 2, | |
| 329 | + struct binder_transaction_data_secctx), | |
| 330 | + /* | |
| 331 | + * binder_transaction_data_secctx: the received command. | |
| 332 | + */ | |
| 314 | 333 | BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data), |
| 315 | 334 | BR_REPLY = _IOR('r', 3, struct binder_transaction_data), |
| 316 | 335 | /* |