你真的了解Android权限机制吗?@3

举报
G-washington 发表于 2019/09/04 16:21:58 2019/09/04
【摘要】 本篇我们将深入了解框架层的动态和静态权限执行的原理。

接上文https://www.epubit.com/selfpublish/article/6093 @1 https://www.epubit.com/selfpublish/article/6096 @2  敏词shen查太坑人了,代码里不能有小写ID,比如pID,uID什么的。还有SettingBase代码中不可连用,以基地代替,只能拆着发  2. 框架层

  因为 ANDROID 6.0 之前组件不能在运行时改变权限,所以系统的权限检查执行过程是静态的。这个情况下,组件的角色和权限的等安全属性会被放置在元数据中,即 ANDROIDMANIFEST.XML 文件中,而不是组件的本身。系统包管理器会负责记录组件的权限,所以静态权限检查可以从包管理器拿到权限,由运行环境或容器来执行权限检查,这样子可以把业务逻辑和安全决策分离开来,但是灵活性不足。

  那 ANDROID 组件可不可以不预先声明权限在 ANDROIDMANIFEST.XML 中呢?答案是:可以的。ANDROID 的动态权限执行,可以让组件自身执行权限检查,而不是运行环境。

  所以接下来我们将深入了解框架层的动态和静态权限执行的原理。

  动态权限执行

  动态权限执行,最典型的场景,就是 IPC。ANDROID 的核心系统服务统一会注册到服务管理器,任何应用,只要知道服务的注册名称,就可以拿到对应的 BINDER引用,就可使用 BINDER IPC 机制调用服务。因为 BINDER 没有内置的访问控制机制,所以每个系统服务需要自己实现访问控制机制。

  系统服务可以直接检查调用者的 uID,通过限定 uID 来控制访问权限,这种方式简单直接,但是对于非固定uID的应用,就比较棘手了。而且大部分服务,并不关心调用者的 uID,只需要检查调用者是否被赋予特定的权限即可。所以这种方式,比较适合只允许以 ROOT(uID:0) 或 SYSTEM(uID:1000) 运行的进程访问的服务检查。

  那换一种方式,服务怎么拿到调用者的权限列表?我们知道,大部分 uID 都是和包一一对应的,除了共享 uID。(共享 UID 后面再详细解释)

  使用 Binder.getCallingUid() 和 Binder.getCallingPid() 获取调用者的 UID 和 PID,通过 UID 在包管理器中查询到对应应用的权限。android.content.Context 类中就有 checkPermission(String permission, int pid, int uid) 方法。实质上会调用到 PMS 中的 checkUidPermission(String perName, int uid),如下:

  Android 6.0 以下 PMS 中的 checkUidPermission(String perName, int uid)  

图片.png

 Android 6.0 以下的 checkUidPermission() 方法比较简单,首先,基于入参 uid 获取应用的 appId,拿到权限列表对象(也就是 packages.xml 里的   映射),如果 GrantedPermissions 类中的 grantedPermissions 集合包含目标权限,则检查通过。

  如果没有该 GrantedPermissions 对象,则检查目标权限是否可以被自动授予,实际上 mSystemPermissions 就是 platform.xml 文件中的   标签映射缓存,记录了一些系统级应用的 uid 对应的 permission。例:   ...

 Android 6.0 及以上 PMS 中的 checkUidPermission(String perName, int uid)

图片.png

 可以注意到,6.0 之后 checkPermission() 方法有所改变。多了从 mSettings.mPermissions 去查询权限列表。关键就在于这个 mSettings 里面保存的这个 SettingBase 对象,它记录了 PermissionsState 也就是权限的授予情况。

图片.png

  所以检查权限的流程是本来就有的,6.0 之后差异仅在于:危险级别权限可以动态修改授权情况,也就是修改 PermissionState 的 mGranted 值,所以每次权限执行,都会查询下 mGranted 值。

  静态权限执行

  静态权限执行的典型场景,是跨应用组件交互。我们使用隐式 Intent 来表达意图,搜索匹配的组件,如果有多个,弹出选择框,目标组件被选定后,会由 ActivityManagerService 执行权限检查,检查目标组件是否有相应的权限要求,如果有,则把权限检查的工作交给 PMS,去检查调用者有没有被授权这些权限。

  接下来的总体的流程和动态执行流程大致相同:Binder.getCallingUid()和Binder.getCallingPid()获取调用者的 UID 和 PID,然后利用 UID 映射包名,再获得相关权限集合。如果权限集合中含有所需权限即启动,否则抛出 SecurityException 异常。静态权限执行这里,我们可以详细了解下,每种组件的权限检查时机和具体顺序是怎么样的。

  组件权限执行

  思考一下,什么时候会执行对调用者的权限检查?那肯定是在目标组件被调用的时候,去解析目标组件声明的权限,如果有,就执行权限检查。

  Activity 和 Service。Activity 显而易见,会在 startActivity() 和 startActivityForResult() 里解析到声明权限的 Activity 时,就执行权限检查。而 Service startService()、stopService() 和 bindService(),这 3 个方法被调用时都会进行权限检查。

  广播。我们注意到,发送广播除了常用的 sendBroadcast(Intent intent),还有个 sendBroadcast(Intent intent, String receiverPermission),该方法可以要求广播接受者具备特定的权限,但是,调用 sendBroadcast 是不会进行权限检查的,因为广播是异步的,所以权限检查会在 intent 传递到已注册的广播接受者时进行,如果接收者不具备特定的权限,则不会接收到该广播,也不会收到 SecurityException 异常。

  反过来,接收者可以要求广播发送者必须具备的权限,所要求的权限在 manifest 文件中设置   标签的 permission 属性,或者动态注册时指定 registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler),权限检查也是在广播传递时执行。

  所以,收发广播可以分开指定权限。值得一提的是,一些系统广播被声明为 protected,并且只能由系统进程发送,比如 PACKAGE_INSTALLED。只能由系统进程发送,这个限制会在内核层进行检查,对调用者的 UID 进行匹配,只能是 SYSTEM_UID、PHONE_UID、SHELL_UID、BLUETOOTH_UID 或 root。如果其他 UID 的进程试图发送系统广播,则会收到 SecurityException 异常。

  

想了解所有的系统广播,可以打开/system/framework/framework-res.apk 中的 AndroidManifest.xml   标签详细了解。   ContentProvider。ContentProvider 可以为读写分别指定不同的权限,即:调用目标 provider、query() 方法 和 insert()、update()、delete() 都会进行权限检查。

  总结

  综上所述,Android 的权限的检查会在各个层次上实施。

  高层的组件,例如应用和系统服务,通过包管理器查询应用程序被赋予的权限,并决定是否准予访问。

  低层的组件,通常不访问包管理器,比如本地守护进程,依赖于进程的 UID、GID 和补充 GID 来决定赋予。

  访问系统资源时,如设备文件、UNIX 域套接字和网络套接字,则由内核根据所有者、目标资源的访问权限和访问进程的进程属性或者 packages.list 来进行控制。

  共享 UID

  最后简单说下共享 UID,填一下前面挖的坑。虽说 Android 会为每一个应用分配唯一的 UID,但如果应用使用相同的密钥签发,就可以使用相同 UID 运行,也就是运行在同一个进程中。

  这个特性被系统应用和核心框架服务广泛使用,比如:Google Play 和 Google 定位服务,请求同一进程内的 Google 登录服务,从而达到静默自动同步用户数据的体验。

  值得注意的是:Android 不支持将一个已安装的应用,从非共享 UID 切换到共享状态,因为改变了已安装应用的 UID,会导致应用失去对自己文件的访问权限(在一些早期 Android 版本中),所以如果使用共享 UID 必须从一开始就设计好。


本文转载自异步社区

原文链接:https://www.epubit.com/articleDetails?id=Nb6633e88-1df9-4b55-af60-307a3fea108d

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。