第一章 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 bindrwxr-xr-x 3 root root 4096 12月 18 09:18 bootdrwxr-xr-x 2 root root 4096 3月 18 2012 cdromdrwxr-xr-x 15 root root 4380 1月 4 19:28 devdrwxr-xr-x 176 root root 12288 1月 4 19:01 etcdrwxr-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的原理。)

 

 

参考资料

[1]《Advanced Programming in the UNIX Environment》, W.Richard Stevens.

[2] Sticky标志位, http://en.wikipedia.org/wiki/Sticky_bit

[3] Linux下Fork与Exec使用, http://www.cnblogs.com/hicjiajia/archive/2011/01/20/1940154.html

[4]《Android 内核剖析》,柯元旦.

[5]《深入理解Android》 ,邓平凡.

[6] Android权限官方文档 ,http://developer.android.com/intl/zh-CN/guide/topics/security/permissions.html.


from: http://www.cnblogs.com/senix/archive/2013/01/15/2853733.html

更多相关文章

  1. NPM 和webpack 的基础使用
  2. 【阿里云镜像】使用阿里巴巴DNS镜像源——DNS配置教程
  3. Android基础和运行机制
  4. Android(安卓)wps文件下载、预览
  5. Android(安卓)开发手记之NDK 编程实例
  6. Unity5与Android交互通信(使用Android(安卓)Studio2.4) 详细操作一
  7. Android(安卓)项目在Eclipse中的目录结构
  8. Android命令行下运行JAVA程序之StatusBar控制
  9. Android系统上部署usb打印机

随机推荐

  1. Android沉浸式状态栏和手机虚拟按钮不兼
  2. Android 布局中 如何使控件居中
  3. Android 沉浸式状态栏实现,以及遇到的问题
  4. android的四大组件及其生命周期
  5. Android(安卓)获取设备和系统信息
  6. Android开发前奏
  7. Android(安卓)百度地图蓝点定位
  8. android ndk环境配置
  9. android 永远锁屏解决方法
  10. TensorFlow Android demo