hello world

          隨筆 - 2, 文章 - 63, 評論 - 0, 引用 - 0
          數據加載中……

          淺析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月 2808:32 bin
          drwxr-xr-x 3 root root 4096 12月 1809:18 boot
          drwxr-xr-x 2 root root 4096 3月 182012 cdrom
          drwxr-xr-x 15 root root 4380 1月 419:28 dev
          drwxr-xr-x 176 root root 12288 1月 419:01 etc
          drwxr-xr-x 3 root root 4096 4月 162012 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.

          posted on 2017-06-02 21:20 聽風 閱讀(202) 評論(0)  編輯  收藏 所屬分類: 嵌入式

          主站蜘蛛池模板: 花莲市| 叙永县| 南岸区| 长寿区| 岳西县| 聂拉木县| 合川市| 湘西| 田林县| 铅山县| 平昌县| 吉隆县| 隆化县| 栾川县| 玉溪市| 余庆县| 什邡市| 贵州省| 青海省| 梁河县| 新兴县| 湘乡市| 高安市| 巴林左旗| 正定县| 南宫市| 南岸区| 淮北市| 平罗县| 柘城县| 杂多县| 万年县| 大理市| 江都市| 柳江县| 横山县| 灵武市| 慈溪市| 苍南县| 横峰县| 济源市|