浅析Android权限机制(一) —— Android的权限机制

第一章 Android的权限机制

    Android是基于Linux的系统,其权限访问控制自然离不开Linux的权限访问控制,而在第一章当中,将分成两个部分来剖析Android的权限控制系统。

一. Linux权限机制
     Linux的权限访问是由进程(访问者)和文件(被访问者)两部分组成的。其中相当一部分内容参考至APUE[1]。

1.1 Llinux文件权限
     我们在Linux当中输入命令    

$ls -l

我们可以看到这样类似如下的结果

drwxr-xr-x 2 root root 4096 11月 28 08:32 bin
drwxr-xr-x 3 root root 4096 12月 18 09:18 boot
drwxr-xr-x 2 root root 4096 3月 18 2012 cdrom
drwxr-xr-x 15 root root 4380 1月 4 19:28 dev
drwxr-xr-x 176 root root 12288 1月 4 19:01 etc
drwxr-xr-x 3 root root 4096 4月 16 2012 home

    第一列使用的如drwxr-xr-x的10位字段,表示的是该文件的文件类型和其权限。下表描述了各个标志位的含义

9 6 - 8 3 - 5 0 - 2
文件类型 拥有者访问权限 所在用户组访问权限 其它用户访问权限

p 管道文件

d 目录文件

l 符号连接文件

- 普通文件

s socket文件

c 字符设备文件

b 块设备文件

分别为读写执行权限,

-表示没有该位上的权限

读取权限: r

写入权限: w

执行权限: x

              s,S 表示设置了SUID位.

              s表示该原标志为x,

              S表示该原标志为-

分别为读写执行权限,

-表示没有该位上的权限

读取权限: r

写入权限: w

执行权限: x

              s,S 表示设置了GUID位.

              s表示该位原标志为x,

              S表示该位原标志为-

分别为读写执行权限,

-表示没有该位上的权限

读取权限: r

写入权限: w

执行权限: x

              s,S 表示设置了Sticky位.

              s表示该位原标志为x,

              S表示该位原标志为-

表1 Linux文件权限标识符

    特殊权限SGID标志位:普通文件设置了该标志位,则表示该进程的egid变成被运行的程序的所有者的gid。没有设置该位,则进程的egid为运行该程序的用户的gid。
    特殊权限SUID标志位:普通文件设置了该标志位,则表示该进程的euid变成被运行的程序的所有者的uid。没有设置该位,则进程的euid为运行该程序的用户的uid。 
    特殊权限Sticky标志位:旧的UNIX系统定义该位为指示操作系统在程序退出后,保留程序的代码段到swap空间。而在linux系统当中,该位表示防删除位,意味着该位被设置之后,只有文件的拥有者和root用户才能删除该文件。[1][2]

    SGID和SUID的存在意义在于,当一个非特权进程可以通过执行设置了SGID和SUID标志的程序,来获得特定权限。例如su,当它没有设置SGID和SUID标志位的时候,实际上它是不能创建一个具有root权限的shell进程的。

    以上的文件权限标识符在Linux当中实际上是使用二进制来表示的,例如rwxrw-rw-,转成二进制形式就是111110110,但实际情况下,我们为了更方便阅读,我们使用的是八进制进行标识,也就是766。但是文件标识符当中除了基本读写执行之外,再算上特殊权限,实际上Linux权限访问控制使用的是12位二进制数字(3位特殊权限 + 9位基础权限)来表示访问权限。比如rwsrw-rw-,转化成八进制表示方式,就是4766。

1.2 linux进程权限
    假设,我们在系统当中运行了一个程序,然后我们通过ps命令进行查询,得知该进程的pid为1025,之后我们在linux当中输入命令

$cat proc/1025/status

    我们可以看到其中有一段

    Name:    live.androidpad
    Uid:    10040    10040    10040    10040
    Gid:    10040    10040    10040    10040
    Groups:    1007 1015 3003

    其中,Uid行有四列,它们分别为RUID,EUID,SUID,FSUID
    RUID(实际用户id:Real User ID):进程的创建用户。
    EUID(有效用户id:Effective User ID):进程的有效用户,用于权限访问控制。
    SUID(保存设置用户id:Saved Set-User-ID):在程序执行(exec)之后作为EUID的副本,用于进程切换自己的EUID时使用,对用户来说实际意义不大。参考[1]
    FSUID(文件系统用户id:File System User ID):Linux新引进的一类用户、组,用于文件访问控制。(推测,文件访问上FSUID优先于EUID)
    Gid行有四列,它们分别为RGID,EGID,SGID,FSGID
    RGID(实际用户id:Real User ID):进程的创建用户组
    EGID(有效用户id:Effective User ID):进程的有效用户组,用于权限访问控制。
    SGID(保存设置用户id:Saved Set-User-ID):在程序执行(exec)之后作为EGID的副本,用于进程切换自己的EGID时使用,对用户来说实际意义不大。参考[1]
    FSGID(文件系统用户id:File System User ID):Linux新引进的一类用户、组,用于文件访问控制。(推测,文件访问上FSGID优先于EGID)
    Groups行是组id,里面是一组使用空格分开的数字,这些数字就是是用户组的id,它同样用于权限访问控制。

    对于FSGID和FSUID,这个东西是Linux中引进的,很多时候它的值是直接复制EGID和EUID的。而Unix系统当中,RUID\EUID\SUID、RGID\EGID\SGID和Groups作为标配,我们这里只讨论进程的这7个参数。正如我们使用命令输出的结果一样,除了Groups参数使用整形数组来表示之外,其余6个参数在Linux系统当中使用的都是整形来表示。而,这几个参数都会决定进程的权限等特性,而它们是基于什么规则来赋值的呢?

    虽然实际上跟文件访问权限有关的仅仅是EUID、EGID和Groups,但是因为文章的受众很可能是只了解Android系统的开发者,所以我这里也多讲一些。Linux当中所有的进程创建都是通过fork函数创建的,当进程被fork之后,子进程会继承父进程的RUID\EUID和RGID\EGID,而SUID和SGID会在exec之后作为EUID和EGID的副本被赋值(关于fork和exec的更多讲解,请参考APUE[1]和[3])。而在进程创建之后,子进程可以通过setuid和setgid修改自身的RUID\EUID\SUID、RGID\EGID\SGID,但是这是有固定规则的。

    以下是setuid的使用规则,setgid也与之类似:

    1.若进程拥有超级权限,则setuid函数将RUID\EUID\SUID设置为uid。

    2.若进程没有超级权限,而uid的值等于RUID或者SUID,则setuid将会把EUID设置为uid。而不会改变RUID或者SUID的值。

    3.如果上述两个条件都不满足,则返回失败。

1.3 Linux的权限访问控制

    这部分很简单,所有的系统调用最终都到内核当中,内核作为管理中枢,对所有的文件访问调用进行了核查。而APUE描述了内核对读写执行权限的测试算法:
    1.若进程的EUID是0,则允许访问。
    2.若进程的EUID等于所有者ID,那么:若所有者对应的访问权限位被设置,则允许访问,否则拒绝访问。
    3.若进程的EGID或者附加组ID之一等于文件的组ID,那么:若组对应的访问权限位被设置,则允许访问,否则拒绝访问。
    4.若其它用户对应的访问权限位被设置,则允许访问,否则拒绝访问。

二. Android权限机制
    原本想对这部分内容进行详细解析的,但后来发现涉及的内容包含了PKMS,AMS,应用程序安装,应用程序启动等内容。假若我来描写这些内容,第一,篇幅太多,第二,自己的描述能力有限容易误导别人。所以我就不深入说了,有兴趣的朋友可以参考[4][5][6]。

    在这里我们只需要知道,Android的策略是这样的:

    1.文件和设备访问,使用Linux的权限访问控制。部分权限声明之后,应用程序启动的时候,AMS会从PKMS那里获得该应用进程的uid,gid和组id信息,然后通过Zygote来创建一个指定id的进程。获得指定组id的进程,也会获得部分文件的访问权限,例如声明android.permission.WRITE_EXTERNAL_STORAGE来访问sdcard会被赋予sdcard_rw的组id。权限所对应的组id在frameworks/base/data/etc/platform.xml当中。

    特别注意:第一章也描述了,内核检查id的顺序是EUID然后再到EGID和组ID,所以,当你声明android.permission.WRITE_EXTERNAL_STORAGE的同时,声明shareUserId为system,是没有读写sdcard权限的。

    2.Android接口调用控制,首先是root用户和system用户拥有所有的接口调用权限,然后对于其它用户使用Context以下这几个函数来实现  

Context.checkCallingOrSelfPermission(String);
Context.checkCallingOrSelfUriPermission(Uri, int );
Context.checkCallingPermission(Permission);
Context.checkCallingUriPermission(Uri, int );
Context.checkPermission(String, int , int );
Context.checkUriPermission(Uri, int , int , int );
Context.checkUriPermission(Uri,String,String, int , int , int );
 
Context.enforceCallingOrSelfPermission(String,String);
Context.enforceCallingOrSelfUriPermission(Uri, int ,String);
Context.enforceCallingPermission(String,String);
Context.enforceCallingUriPermission(String,String);
Context.enforcePermission(String, int , int ,String);
Context.enforceUriPermission(Uri, int , int , int ,String);
Context.enforceUriPermission(Uri,String,String, int , int , int ,String);

    其中check开头的,只做检查。enforce开头的,不单检查,没有权限的还会抛出异常。

    这几个函数最后会调用到PKMS的checkUidPermission,该函数通过对比应用权限信息来判断该应用是否获得权限。

    3.Android权限等级划分为normal,dangerous,signature,signatureOrSystem,system,development,其中

    signature需要签名才能赋予权限,

    signatureOrSystem需要签名或者系统级应用(放置在/system/app目录下)才能赋予权限,

    system系统级应用(放置在/system/app目录下)才能赋予权限,系统权限的描述在frameworks/base/core/res/AndroidManifest.xml当中。

    这就解答了,为什么有时候声明一些权限没有起作用,例如android.permission.WRITE_MEDIA_STORAGE。

 

    如果我们想知道某个权限怎么使用,有什么制约怎么办?

pm list permissions -f

   来查看系统所有权限的描述

 

    如果我们需要在系统中增加一个权限,怎么办?那我们照下列的步骤来做

    1.确定你的权限属于文件访问控制,还是接口调用控制。

    2.在frameworks/base/core/res/AndroidManifest.xml,中增加你的权限描述。

    3.如果是文件访问控制,那就在frameworks/base/data/etc/platform.xml为你的权限依附指定的组id。

    4.如果是接口调用控制,那就在你的接口调用里面,加入上述Context检查权限的函数。

    

    (这段内容确实不大好写,酝酿了好久,再酝酿就胎死腹中了,再度吐槽一下自己的描述能力。:-)第二章内容会讲述一下Android root的原理。)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值