抽丝剥茧:理解Android权限机制

  前一段时间面试官问我Android在Linux的基础上,权限做了哪些改变。霹雳呱啦说了一堆,但是说着说着,始终感觉自己说的缺了点东西,自己理解还是不够到位
,而且网上的很多文章在原理上基本都是大同小异,很多地方都是语焉不详,所以,自己半看源码半看文章的总结了一下。

一:Android权限是什么

  这个是老生长谈的东西了,说到底,权限就是告诉系统我需要干什么,从访问物理数据,到访问第三方组件均是。

二:权限赋予

  权限的赋予可分为两类,一类是高层的组件,例如应用和系统服务,这一部分一般采用包管理器依赖进行管理,查询。而另一部分则是低层的组件,这一部分则是利用了传统了Linux DAC机制进行管理,一般不直接访问包管理器。

  1:低层权限管理

  这里包括但不限于设备文件,UNIX套接字,网络套接字。Android进程主要通过UID,GID以及一组补充的GID实现的。众所周知,Android沙箱是以UID为基础实现的,每个进程拥有自己独特的UID(先不考虑共享UID)。进程的UID和GID会由包管理器映射到应用程序的UID。而补充gid则为额外的权限。值得一提的是,内置权限到组的映射是静态的。部分源码如下

<permission name="android.permission.BLUETOOTH_ADMIN" >
        <group gid="net_bt_admin" />
    </permission>
    <permission name="android.permission.BLUETOOTH" >
        <group gid="net_bt" />
    </permission>
    <permission name="android.permission.BLUETOOTH_STACK" >
        <group gid="bluetooth" />
        <group gid="wakelock" />
    </permission>

  如上,android.permission.BLUETOOTH_ADMIN,android.permission.BLUETOOTH 和GID net_bt_admin组是关联的.而在 android_filesystem_config.h中,组和GID是想映射的,如下

static struct android_id_info android_ids[] = {
  ....

  { "shell", AID_SHELL, },
  { "cache", AID_CACHE, },
  { "net_bt_admin", AID_NET_BT_ADMIN, },
  ....
}

#define
AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */ #define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */ #define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */ #define AID_NET_RAW 3004 /* can create raw INET sockets */ #define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */ #define AID_NET_BW_STATS 3006 /* read bandwidth statistics */

  可得android.permission.BLUETOOTH_ADMIN映射的GID的3001。总而言之,包管理器在读取platfrom.xml时,并维护一个权限到GID的列表。在对一个安装中的包进行授权时,包管理器会检查每个权限是否有对应的GID。如果有,则加入在补充GID列表。当然,到这里只是确定了进程需要赋予哪些额外的gid。

  并没有说怎么赋权的,这里要谈到一个叫zygote的进程,顾名思义,当Android启动新进程的时候,为了减少程序所需内存以及加快启动时间,Android会直接fork()zygote进程,并执行Android特有的函数进行分化而不执行固有的exec函数。简化代码如下(android / platform / dalvik / 7033bed / . / vm / native / dalvik_system_Zygote.cpp forkAndSpecializeCommon()):

pid = fork();

if(pid ==0 ){
    err = setgroupsIntarray(gids);          //设置补充gid
    err = setrlimitsFromArray(rlimits);       //设置资源限制
    err = setresgid(gid, gid, gid);          //设置实际用户/组id
    err = setresuid(uid, uid, uid);          //设置有效用户/组id
    err = setCapabilities(permittedCapabilities, effectiveCapabilities);      //设置进程权能
    err = set_sched_policy(0, SP_DEFAULT);      //设置调度策略
    err = setSELinuxContext(uid, isSystemServer, seInfo, niceName);      //SElinux
}

  2.高层权限管理

  在仔细讲解之前,先来看看包管理器所维护的安装程序包核心数据库,这个数据库以xml文件的形式放进了/data/system/packages.xml里,先来看看里面究竟有些什么。

<package name="com.android.protips" codePath="/system/app/Protips" nativeLibraryPath="/system/app/Protips/lib" flags="572997" ft="1560a280490" it="1560a280490" ut="1560a280490" version="1" userId="10040">
        <sigs count="1">
            <cert index="0" />
        </sigs>
        <proper-signing-keyset identifier="2" />
        <signing-keyset identifier="2" />
    </package>
    <package name="com.android.launcher" codePath="/system/priv-app/Launcher2" nativeLibraryPath="/system/priv-app/Launcher2/lib" flags="1078509125" ft="1560a29ae58" it="1560a29ae58" ut="1560a29ae58" version="22" userId="10007">
        <sigs count="1">
            <cert index="3" key="308204a1406035504071302fc58d017971bd0f6b52c262d70819d191967e158dfd3a2c7f1b30fa1eaafc2a556f84" />
        </sigs>
        <proper-signing-keyset identifier="3" />
        <signing-keyset identifier="3" />
    </package>
  

 <package name="com.android.widgetpreview" codePath="/data/app/WidgetPreview" nativeLibraryPath="/data/app/WidgetPreview/lib" flags="572996" ft="15bbc858e28" it="1560a27f8d8" ut="1560a27f8d8"       version="22" userId="10052">
    <sigs count="1">
    <cert index="0" />
  </sigs>
  <perms>
  <item name="android.permission.READ_EXTERNAL_STORAGE" />
  <item name="android.permission.WRITE_EXTERNAL_STORAGE" />
  </perms>
  <proper-signing-keyset identifier="2" />
  <signing-keyset identifier="2" />
</package>

 

  从上面可以发现,这里包括了安装路径,版本号,签名证书,每个包的权限。上层的管理都是通过和包管理器和这个数据库进行交互的。由于组件不能在运行时改变权限,所以权限执行检查都是静态的。但它的执行一般分为两类,一类是静态,另一类是动态的。静态执行和动态执行流程大致相同:Binder.getCallingUid()和Binder.getCallingPid()获取调用者的UID和PID,然后利用UID映射包名,再获得相关权限。如果权限集合中含有所需权限即启动,否则抛出SecurityException异常。

 

三:共享UID(补充)

  使用相同的密匙签发的Android应用可以使用共同的UID运行 ,并且可以运行在同一进程中。这个属性可以简单通过在AndroidManifest.xml的根元素中添加shareUserId属性即可开启,但是不能在已安装的应用里添加该属性,这只会导致它修改自身uid,以至于失去对自身文件的访问权限。

  shareid内置了以下几种:

    android.uid.system(SYSTEM_UID,1000)

    android.uid.phone(PHONE_UID,1001)

    android.uid.bluetooth(BLUETOOH_UID,1002)

    android.uid.log(LOG_UID,1007)

    android.uid.nfc(NFC_UID,1027)

  它们在系统引导时自动添加。具有相同userid的进程,可以访问相同的系统资源,还可以对统一资源的组件进行特殊访问控制。

  

posted @ 2017-05-04 13:20  0xJDchen  阅读(4019)  评论(0编辑  收藏  举报