Linux ACL 許可權之進階篇

發表於2018-09-28

筆者在《Linux ACL 許可權》一文中介紹了 Linux ACL 許可權的基本用法,本文筆者將嘗試探究 ACL 中的基本概念和實現原理,希望能夠通過進一步的加深對 Linux 許可權系統的理解。說明:本文的演示環境為 ubuntu 16.04。

ACL 中的基本概念

ACL 的型別

  • access ACL:我們可以認為每一個物件(檔案/目錄)都可以關聯一個 ACL 來控制其訪問許可權,這樣的 ACL 被稱為 access ACL。
  • default ACL:目錄也可以關聯一個 ACL 來控制在該目錄中建立的物件的預設 ACL,這樣的 ACL(目錄關聯的 ACL)被稱為 default ACL。

ACL 條目
一個 ACL 由多個 ACL 條目組成。一個 ACL 條目指定一個使用者或者一組使用者對所關聯物件的讀、寫、執行許可權。下圖展示了 ACL 條目的型別及含義:

ACL 許可權與 ugo 許可權的對應關係

ACL 定義的許可權是 ugo 許可權的超集。

  • 檔案的 owner 許可權對應於 ACL 許可權中的 ACL_USER_OBJ 條目。
  • 當 ACL 許可權中具有 ACL_MASK 條目時,檔案的 group 許可權對應於 ACL 許可權中的 ACL_MASK 條目。否則,當 ACL 許可權中具沒有 ACL_MASK 條目時,檔案的 group 許可權對應於 ACL 許可權中的 ACL_GROUP_OBJ 條目。
  • 檔案的 other 許可權對應於 ACL 許可權中的 ACL_OTHER_OBJ 條目。

檔案的 ugo 許可權總是與對應的 ACL 條目保持一致。修改檔案的 ugo 許可權會導致修改相關的 ACL 條目,同樣的,修改這些 ACL 條目會導致修改對應的 guo 許可權。

新建檔案的 default ACL

一個檔案的 access ACL 會在通過 creat()、mkdir()、mknod()、mkfifo() 和 open() 函式建立該檔案時被初始化。

如果一個目錄被設定了 default ACL,那麼將會由檔案建立函式的 mode 引數和目錄的 default ACL 共通決定新檔案的 ACL 許可權:

  • 新的檔案繼承父目錄的 default ACL 作為自己的 access ACL。
  • 修改與 ugo 許可權對應的 access ACL 條目,使其不包含檔案建立函式的 mode 引數不包含的許可權。

說明:此時 umask 被忽略。

如果一個目錄沒有被設定 default ACL,那麼將由檔案建立函式的 mode 引數和 umask 共同決定新檔案的 ACL 許可權:

  • 新建檔案的 access ACL 包含 ACL_USER_OBJ, ACL_GROUP_OBJ, 和 ACL_OTHER 條目。這些條目的許可權被設定為由 umask 決定的許可權。
  • 修改與 ugo 許可權對應的 access ACL 條目,使其不包含檔案建立函式的 mode 引數不包含的許可權。

檔案許可權檢查的演算法(Access Check Algorithm)

當一個程式訪問(讀、寫、執行)一個被 ACL 保護的檔案時,檔案許可權檢查的演算法決定了是否授權給程式訪問該檔案。
下面我們以 下面我們以虛擬碼的方式來解釋檔案許可權檢查的演算法。
第一步:if 程式的 effective user ID 與檔案 owner 匹配
if ACL 的 ACL_USER_OBJ 條目包含了請求所需的許可權,此時就被授權訪問檔案
else 訪問被拒絕
第二步:else if 程式的 effective user ID 匹配檔案 ACL 許可權中任何一個 ACL_USER 條目中的 user
if 匹配的 ACL_USER 條目和 ACL_MASK 條目包含了請求所需的許可權,此時就被授權訪問檔案
else 訪問被拒絕
第三步:else if 程式的 effective group ID 或者任何一個補充的(supplementary) group ID 匹配檔案的 group 或 ACL 許可權中任何一個 ACL_GROUP 條目的 group
if ACL 許可權包含 ACL_MASK 條目
if ACL_MASK 條目和匹配的任何 ACL_GROUP_OBJ 或 ACL_GROUP 條目包含了請求所需的許可權,此時就被授權訪問檔案
(註釋:ACL_MASK 與其它項是 and 的關係,用來控制最大許可權)
else 訪問被拒絕
else (注意:沒有 ACL_MASK 條目,就沒有 ACL_GROUP 條目)
if ACL_GROUP_OBJ 條目包含了請求所需的許可權,此時就被授權訪問檔案
else 訪問被拒絕
第四步:else if ACL_OTHER 條目包含了請求所需的許可權,此時就被授權訪問檔案
第五步:else 訪問被拒絕

ACL 的文字描述格式

有兩種格式來描述 ACL 條目,分別是長格式和短格式。它們非常類似,都是通過兩個冒號把一個 ACL 條目分為三個部分:
ACL 條目的型別:ACL 條目 qualifier:許可權資訊

我們在前面已經介紹過 ACL 條目的型別,許可權資訊就是用 rwx 來表示的資訊,不支援某個許可權的話可以使用 – 表示。這裡介紹一下 ACL 條目 qualifier(不知道該咋翻譯這貨)。

  • 當 ACL 條目的型別為 ACL_USER 或 ACL_GROUP 時,ACL 條目 qualifier 包含與 ACL 條目關聯的使用者和組的識別符號。
  • 當 ACL 條目的型別為其它時,ACL 條目 qualifier 為空。

其中的使用者識別符號可以是使用者名稱也可以是 user ID,組識別符號可以是組名也可以是 group ID。

下面是一組長格式的示例:
user::rw-
user:tester:rw- #effective:r–
group::r–
group:tester1:rw- #effective:r–
mask::r–
other::r–

下面是一組短格式的示例:
u::rw-,u:tester:rw-,g::r–,g:tester1:rw-,m::r–,o::r–
g:tester1:rw,u:tester:rw,u::wr,g::r,o::r,m::r

解釋幾個常見的許可權變化的例子

前文我們的重點是介紹 ACL 許可權的基本用法。有了本文前面介紹的基礎內容,我們就可以解釋前文中出現的一些比較怪異的現象。
建立使用者 tester, tester1:

先建立檔案 aclfile,檢查其預設的 ACL 許可權資訊:

然後為 tester 使用者賦予讀寫 aclfile 檔案的許可權:

此時檢視 aclfile 檔案的許可權:

貌似並沒有發生什麼變化,只是在描述許可權的地方多出了一個 “+” 號。下面再看看 acl 許可權:

上圖的黃框中出現了 ACL_MASK 條目,這就是我們的第一個問題:
**************************************************************
我們並沒有顯式的設定 ACL_MASK 條目,為什麼它出現了?
An ACL that contains entries of ACL_USER or ACL_GROUP tag types must contain exactly one entry of the ACL_MASK tag type. If an ACL contains no entries of ACL_USER or ACL_GROUP tag types, the ACL_MASK entry is optional.
上面的解釋大意是當新增了 ACL_USER 或 ACL_GROUP 後,必須有一個對應的 ACL_MASK 條目。在當前的情況下,ACL_MASK 是被自動建立的,它的許可權被設定成了 group(其實是 group class) 的許可權即 rw-。
**************************************************************

下面我們接著更新 aclfile 的 ACL 許可權:

檢視檔案許可權:

在修改了 tester 的許可權並新增了 tester1 group 的許可權後,我們看到的組許可權居然變成了 rwx !
這是我們的第二個問題:
**************************************************************
為什麼 aclfile 檔案的組許可權變成了 rwx?
這是因為我們設定了  u:tester:rwx 導致的:
在設定了 ACL 許可權後,group 顯示的許可權為 ACL_MASK 條目中的許可權。而 ACL_MASK 條目中的許可權表示 ACL_USER、ACL_GROUP_OBJ 和 ACL_GROUP 條目能夠被授予的最大許可權,所以當 tester 被設定了 rwx 許可權時,ACL_MASK 條目中的許可權也發生了相應的變化。並最終導致我們看到了上面的結果:-rw-rwxr–+。
**************************************************************

接下來我們看看 acl 許可權:

使用者 tester 具有 aclfile 的讀寫執行許可權,tester1 group 具有 aclfile 的讀許可權,但是這裡的 mask 卻變成了 rwx!
這是我們的第三個問題:
**************************************************************
上面的設定並沒有顯式的指定 mask 項,為什麼 mask 的值卻變了?
其實我們在第二個問題中已經回答了這個問題,是因為 tester 被設定了 rwx 許可權,最終導致 ACL_MASK 條目中的許可權也發生了相應的變化。

這裡我們可以思考一下:ACL 許可權中為什麼需要 ACL_MASK 條目?
這是一個需要從長計議的話題,我們應該從 ACL 許可權與 ugo 許可權的對應說起。在 ugo 許可權模型中,定義了 3 個 class 來表示 owner、group、other 的許可權。Owner class 表示檔案所有者具有的訪問許可權,group class 表示 owner group 具有的訪問許可權,other class 表示其它使用者所具有的訪問許可權。在沒有顯式的設定 ACL 許可權時,檔案的 ACL 許可權與 ugo 許可權的對應關係如下圖所示:

我們把重點放在 group class 上,此時 group class 的許可權和 owner group 的許可權是完全一樣的。

但是在我們新增了 ACL 許可權之後,情況就變得有些複雜了:

此時 group class 中還可能包含 ACL_USER 和 ACL_GROUP 條目中的許可權。這樣就會出現 owner group 許可權與 group class 不一致的情況。
解決的辦法就是為 ACL 許可權引入 ACL_MASK 條目:

  • 沒有設定 ACL 許可權時,group class 的許可權和 owner group 的許可權是完全一樣的。
  • 設定 ACL 許可權後,group class 的許可權對映到了 ACL_MASK 條目的許可權,ACL_GROUP_OBJ 條目僅僅用來表示 owner group 的許可權。

**************************************************************

最後我們再來設定一下 aclfile 的 mask:

ACL 許可權的最後一道防線就是 mask 。它決定了一個使用者或組能夠得到的最大的許可權。上圖中的 #effective 顯示了對應行的實際許可權。檔案許可權也反應了上面的變化:

我們的最後一個問題:
**************************************************************
為什麼需要 effective 許可權?
ACL_MASK 條目 限制的是 ACL_USER、ACL_GROUP_OBJ 和 ACL_GROUP 條目的最大許可權,所以在應用了 ACL_MASK 條目後,需要通過 effective 許可權來獲得 ACL_USER、ACL_GROUP_OBJ 和 ACL_GROUP 條目的真正許可權(如上圖所示)。當 ACL_USER、ACL_GROUP_OBJ 和 ACL_GROUP 條目包含不包含在 ACL_MASK 條目中的許可權,則該條目後面會有一個 “#” 號和字串 “effective”,以及該條目的有效訪問許可權。
但是 mask 只對 ACL_USER、ACL_GROUP_OBJ 和 ACL_GROUP 條目有影響(紅框中的內容),對 owner 和 other 的許可權是沒有任何影響的。
**************************************************************

總結

本文先介紹了 ACL 許可權中的基本概念,然後解釋了筆者在使用 ACL 許可權過程中碰到的一些疑問。希望這些內容可以幫助大家更好的理解和使用 ACL 許可權。

參考:
acl man page
POSIX Access Control Lists on Linux

相關文章