Binder IPC的权限控制
PS:个人理解:当进程1通过Binder调用组件2时,会将进程1的pid及uid赋给组件2,并检测进程1的pid及uid是否有权限调用组件2.而后组件2需要调用组件3,此时组件2保存的pid及uid为进程1的,但是其实际运行在进程2中。此时调用clearCallingIdentity将组件2的保存的pid及uid转换为进程2的pid和uid并return原来保存的进程1的pid及uid的token。此时组件2调用组件3并检查权限。调用完成后,restoreCallingIdentity将组件2的pid及uid恢复为进程1的。
正文:
看过Android系统源代码的朋友,一定看到过Binder.clearCallingIdentity()
和Binder.restoreCallingIdentity()
这两个方法,其定义在Binder.java
文件:
//作用是清空远程调用端的uid和pid,用当前本地进程的uid和pid替代;
public static final native long clearCallingIdentity(); //作用是恢复远程调用端的uid和pid信息,正好是`clearCallingIdentity`的反过程; public static final native void restoreCallingIdentity(long token);
这两个方法涉及的uid和pid,每个线程都有自己独一无二的IPCThreadState
对象,记录当前线程的pid和uid,可通过方法Binder.getCallingPid()
和Binder.getCallingUid()
获取相应的pid和uid。
clearCallingIdentity(), restoreCallingIdentity()这两个方法使用过程都是成对使用的,这两个方法配合使用,用于权限控制检测功能。
二、原理
从定义这两个方法是native方法,通过Binder的JNI调用,在android_util_Binder.cpp
文件中定义了native方法所对应的jni方法。
2.1 clearCallingIdentity
[-> android_util_Binder.cpp]
static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
{//调用IPCThreadState类的方法执行return IPCThreadState::self()->clearCallingIdentity();
}
2.1.1 IPC.clearCallingIdentity
[-> IPCThreadState.cpp]
int64_t IPCThreadState::clearCallingIdentity()
{int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;clearCaller();return token;
}void IPCThreadState::clearCaller() { mCallingPid = getpid(); //当前进程pid赋值给mCallingPid mCallingUid = getuid(); //当前进程uid赋值给mCallingUid }
- mCallingUid(记为UID),保存Binder IPC通信的调用方进程的Uid;
- mCallingPid(记为PID),保存Binder IPC通信的调用方进程的Pid;
UID和PID是IPCThreadState的成员变量, 都是32位的int型数据,通过移位操作,将UID和PID的信息保存到token
,其中高32位保存UID,低32位保存PID。然后调用clearCaller()方法将当前本地进程pid和uid分别赋值给PID和UID,最后返回token
。
2.2 restoreCallingIdentity
[-> android_util_Binder.cpp]
static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, jlong token)
{//token记录着uid信息,将其右移32位得到的是uidint uid = (int)(token>>32); if (uid > 0 && uid < 999) { //目前Android中不存在小于999的uid,当uid<999则抛出异常。 char buf[128]; jniThrowException(env, "java/lang/IllegalStateException", buf); return; } //调用IPCThreadState类的方法执行 IPCThreadState::self()->restoreCallingIdentity(token); }
2.2.1 IPC.restoreCallingIdentity
[-> IPCThreadState.cpp]
void IPCThreadState::restoreCallingIdentity(int64_t token)
{mCallingUid = (int)(token>>32);mCallingPid = (int)token;
}
从token
中解析出PID和UID,并赋值给相应的变量。该方法正好是clearCallingIdentity
的反过程。
2.3 getCallingPid
[-> android_util_Binder.cpp]
static jint android_os_Binder_getCallingPid(JNIEnv* env, jobject clazz)
{return IPCThreadState::self()->getCallingPid();
}
2.3.1 IPC.getCallingPid
[-> IPCThreadState.cpp]
pid_t IPCThreadState::getCallingPid() const
{return mCallingPid;
}uid_t IPCThreadState::getCallingUid() const
{return mCallingUid;
}
2.4 远程调用
2.4.1 binder_thread_read
binder_thread_read(){while (1) {struct binder_work *w;switch (w->type) {case BINDER_WORK_TRANSACTION:t = container_of(w, struct binder_transaction, work);break; case :... } if (!t) continue; //只有BR_TRANSACTION,BR_REPLY才会往下执行 tr.code = t->code; tr.flags = t->flags; tr.sender_euid = t->sender_euid; //mCallingUid if (t->from) { struct task_struct *sender = t->from->proc->tsk; //当非oneway的情况下,将调用者进程的pid保存到sender_pid tr.sender_pid = task_tgid_nr_ns(sender,current->nsproxy->pid_ns); } else { //当oneway的的情况下,则该值为0 tr.sender_pid = 0; } ... }
2.4.2 IPC.executeCommand
status_t IPCThreadState::executeCommand(int32_t cmd)
{BBinder* obj;RefBase::weakref_type* refs;status_t result = NO_ERROR;switch ((uint32_t)cmd) { case BR_TRANSACTION: { const pid_t origPid = mCallingPid; const uid_t origUid = mCallingUid; mCallingPid = tr.sender_pid; //设置调用者pid mCallingUid = tr.sender_euid;//设置调用者uid ... reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer, &reply, tr.flags); mCallingPid = origPid; //恢复原来的pid mCallingUid = origUid; //恢复原来的uid } case :... } }
关于mCallingPid、mCallingUid的修改过程:是在每次Binder Call的远程进程在执行binder_thread_read()过程, 会设置pid和uid. 然后在IPCThreadState的transact收到BR_TRANSACION则会修改mCallingPid、mCallingUid。
这里需要注意的是,当oneway的的情况下mCallingPid=0,不过mCallingUid可以拿到正确值。
三、用途
3.1 场景分析
场景:首先线程A通过Binder远程调用线程B,然后线程B通过Binder调用当前线程的另一个service或者activity之类的组件。
分析:
- 线程A通过Binder远程调用线程B:则线程B的IPCThreadState中的
mCallingUid
和mCallingPid
保存的就是线程A的UID和PID。这时在线程B中调用Binder.getCallingPid()
和Binder.getCallingUid()
方法便可获取线程A的UID和PID,然后利用UID和PID进行权限比对,判断线程A是否有权限调用线程B的某个方法。 - 线程B通过Binder调用当前线程的某个组件:此时线程B是线程B某个组件的调用端,则
mCallingUid
和mCallingPid
应该保存当前线程B的PID和UID,故需要调用clearCallingIdentity()
方法完成这个功能。当线程B调用完某个组件,由于线程B仍然处于线程A的被调用端,因此mCallingUid
和mCallingPid
需要恢复成线程A的UID和PID,这是调用restoreCallingIdentity()
即可完成。
一句话:图中过程2(调用组件2开始之前)执行clearCallingIdentity()
,过程3(调用组件2结束之后)执行restoreCallingIdentity()
。
3.2 类比分析
看完场景分析,估计还有不少朋友感到迷惑,为何需要这两个方法来多此一举,直接检测最初调用端的权限不就行了吗?为了更加形象明了地说明其用途,下面用一个生活中的场景来类比说明。
场景:假如你的朋友请你帮忙,给她(他)到你的公司以内部价购买公司的某个产品。
分析:这个过程分为两个阶段
- 第一阶段:你的朋友请你帮忙的过程,这个过程并不一定所有朋友都会帮的,这时就需要一个权限检测,那么在你的朋友”远程调用”你执行任务时,你会记录他的”Identity”信息(比如是性别),有了信息那么就可以权限检测,不妨令权限规则是如果这个朋友是女性则答应帮忙,否则就认定权限不够拒绝执行(可能黑客会想到先去一趟泰国,权限控制可能相应需要打补丁了),若答应帮忙则进入第二阶段,否则直接返回。
- 第二阶段:你向自己所在公司的相关部门内购产品的过程,这个过程也并不是所有人都能权限能够内购的,只有自己公司的员工才行,否则你的朋友也不会找你帮忙了。 这个过程同样需要权限检测,但是”Identity”保存的是性别女的信息,公司内购产品如果也以性别来判断,那岂不是公司的所有男员工没有权限内购,那这公司就有点太坑了,这明显不符合实情。
clearCallingIdentity()
是时候该登场了,在第二阶段开始之前,先执行clearCallingIdentity()
过程,也就是把”Identity”信息清空,替换为你的信息(比如员工编码ITCode之类的),那公司相关部门通过ITCode就可以直接判断是否允许内购某产品。当第二阶段完成后,也就是你已经购买到了公司产品,这时你需要将产品交付给你的朋友,需要restoreCallingIdentity
,恢复”Identity”为女的信息,这样就嗯呢该顺便交付给你的女朋友。如果不恢复信息,还是原来的ITCode,你交付的朋友可能是男的,另有其人,这样就不科学了。
相信到此,大家应该都能明白这两个方法的作用,缺一不可,而且要成对出现。
3.3 实例分析
上述过程主要在system_server进程的各个线程中比较常见(普通的app应用很少出现),比如system_server进程中的ActivityManagerService子线程,代码如下:
[–>ActivityManagerService.java]
@Override
public final void attachApplication(IApplicationThread thread) { synchronized (this) { //获取远程Binder调用端的pid int callingPid = Binder.getCallingPid(); //清除远程Binder调用端uid和pid信息,并保存到origId变量 final long origId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid); //通过origId变量,还原远程Binder调用端的uid和pid信息 Binder.restoreCallingIdentity(origId); } }
文章startService流程分析中有讲到attachApplication()
的调用。该方法一般是system_server进程的子线程调用远程进程时使用,而attachApplicationLocked
方法则是在同一个线程中,故需要在调用该方法前清空远程调用者的uid和pid,调用结束后恢复远程调用者的uid和pid。
转自:http://gityuan.com/2016/03/05/binder-clearCallingIdentity/
Binder IPC的权限控制相关推荐
- Android 安全架构及权限控制机制剖析
简介: Android 是业界流行的开源移动平台,受到广泛关注并为多个手机制造商作为手机的操作系统平台,因此,研究其安全架构及权限控制机制具有非常的重要性.本文从 Android 层次化安全架构入手, ...
- Android Binder IPC机制
之前有很多人问过我TaintDroid的内容,所以一点点吧涉及的思路整理一下. 今天又看了一遍,确实需要很多的技术支持以及扎实的功底,不管是Java的还是Android,从系统底层一直到顶层,涉及的知 ...
- Amazon EMR 配置 Hive 对Hue用户的权限控制
Hue 创建用户后无权限访问Hive 在hue中创建完用户后,还需要在主节点中创建这个user并在hive和hadoop中为这个user赋予权限,经过我这边的测试,步骤如下: Hue添加用户 添加管理 ...
- mysql用户控制登录_MySql用户权限控制_MySQL
bitsCN.com MySql用户权限控制 本文将介绍MySql创建帐号,删除帐号,设置和介绍各种帐号的权限 创建用户帐号: www.bitsCN.com [sql] CREATE USER use ...
- 前、后端分离权限控制设计和实现思路
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:8rr.co/9QUT 简述 近几年随着react.angu ...
- cvs linux权限,Linux下CVS+ACL的权限控制是什么? 爱问知识人
考试大Linux站整理:我的CVS服务器端是在linux下,我安装了cvs-1.11.22-cvsacl-1.2.5-patched.tar.gz,利用CVS+ACL来做权限控制.但是现在遇到几个问题 ...
- Java访问权限控制
面向对象的核心思想之一就是封装,只把有限的方法和成员公开给别人,这也是迪米特法则的内在要求,是外部调用方对方法体内的实现细节知道得尽可能少. 如何实现封装呢? 需要使用某些关键字来限制外部对类内属性和 ...
- 基于RESTful API 怎么设计用户权限控制?
作者:JC_Huang http://www.jianshu.com/p/db65cf48c111 1 前言 有人说,每个人都是平等的: 也有人说,人生来就是不平等的: 在人类社会中,并没有绝对的 ...
- 8.Spring Security 权限控制
Spring Security权限控制可以配合授权注解使用,具体有哪些注解可以参考Spring-Security保护方法.接着上一节,要开启这些注解,只需要在Spring Security配置文件中添 ...
最新文章
- IP 管理,几多欣喜几多忧
- order by 索引
- python脚本实例手机端-手机APP自动签到-python实现 - 运维之路
- 【SICP练习】66 练习2.37
- Go语言基础之基本数据类型
- Microsoft Power BI Desktop概念学习系列之Microsoft Power BI Desktop的官网自带示例数据(图文详解)...
- 怎样开发一个 Node.js 命令行工具包
- Linux 的发行版 Ubuntu 的发展简史
- 开课啦 dubbo-go 微服务升级实战
- java类成员方法(成员函数)的初步介绍
- eclipse上svn创建分支、合并、切换
- redis key设计技巧
- 一年级的计算机你,写电脑的一年级作文五篇
- mysql crud,mysql的CRUD操作实现
- 利好消息再释放 重卡自动驾驶的智慧物流“掘金”计划
- 如何使用AE制作文字破碎动画?制作ae破碎文字特效教程分享
- JWS 批注参考WebService注解
- ubuntu下没有中文输入法的解决办法!
- 人工智能的出现,赋予零售业6种应用新生命!
- 网页快照(Snapshot)使用的相关研究(萌新向总结)
热门文章
- Ubuntu14.04安装mysql
- shell脚本 linux
- 在Tomcat中通过JOTM支持JTA
- Node.js 修复4个漏洞
- APACHE OFBIZ XML-RPC 反序列化漏洞 (CVE-2020-9496) 的复现与分析
- SWAP Detector:GrammaTech 开源 DevOps 应用安全测试工具
- 一起等补丁:操作系统漏洞StrandHogg 2.0几乎影响所有安卓设备
- Mvvm Light Toolkit 入门
- 《深入理解OSGi:Equinox原理、应用与最佳实践》一3.2 Bundle状态及转换
- VMware SDS 之一:什么是VSAN??