這裡,我們主要介紹 Linux
系統中,許可權控制的基本原理。
安全模型
在 Linux
系統中,我們所有的操作實質都是在進行程式訪問檔案的操作。我們訪問檔案需要先取得相應的訪問許可權,而訪問許可權是通過 Linux
系統中的安全模型獲得的。
對於 Linux
系統中的安全模型,我們需要知道下面兩點
Linux
系統上最初的安全模型叫DAC
, 全稱是Discretionary Access Control
,翻譯為自主訪問控制。- 後來又增加設計了一個新的安全模型叫
MAC
, 全稱是Mandatory Access Control
, 翻譯為強制訪問控制。
注意, MAC
和 DAC
不是互斥的, DAC
是最基本的安全模型,也是通常我們最常用到的訪問控制機制是 Linux
必須具有的功能, 而 MAC
是構建在 DAC
之上的加強安全機制,屬於可選模組。訪問前, Linux系統通常都是先做 DAC
檢查, 如果沒有通過則操作直接失敗; 如果通過 DAC
檢查並且系統支援 MAC
模組,再做 MAC
許可權檢查。
為區分兩者,我們將支援 MAC
的 Linux
系統稱作 SELinux
, 表示它是針對 Linux
的安全加強系統。
這裡,我們將講述 Linux
系統中的 DAC
安全模型。
DAC
安全模型
DAC
的核心內容是:在 Linux
中,程式理論上所擁有的許可權與執行它的使用者的許可權相同。其中涉及的一切內容,都是圍繞這個核心進行的。
使用者和組ID資訊控制
使用者、組、口令資訊
通過 /etc/passwd
和 /etc/group
儲存使用者和組資訊,通過 /etc/shadow
儲存密碼口令及其變動資訊, 每行一條記錄。
使用者和組分別用 UID
和 GID
表示,一個使用者可以同時屬於多個組,預設每個使用者必屬於一個與之 UID
同值同名的 GID
。
對於 /etc/passwd
, 每條記錄欄位分別為 使用者名稱:口令(在 /etc/shadow 加密儲存):UID:GID(預設UID):描述註釋:主目錄:登入shell(第一個執行的程式)
對於 /etc/group
, 每條記錄欄位分別為 組名:口令(一般不存在組口令):GID:組成員使用者列表(逗號分割的使用者UID列表)
對於 /etc/shadow
,每條記錄欄位分別為: 登入名:加密口令:最後一次修改時間:最小時間間隔:最大時間間隔:警告時間:不活動時間:
舉例
以下是對使用者和組資訊的舉例。 /etc/shadow
中的口令資訊為加密儲存,不舉例。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$cat /etc/passwd |head -n 5 root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync $cat /etc/group |head -n 5 root:x:0: daemon:x:1: bin:x:2: sys:x:3: adm:x:4:miracle |
檔案許可權控制資訊
檔案型別
Linux
中的檔案有如下型別:
- 普通檔案, 又包括文字檔案和二進位制檔案, 可用
touch
建立; - 套接字檔案, 用於網路通訊,一般由應用程式在執行中間接建立;
- 管道檔案是有名管道,而非無名管道, 可用
mkfifo
建立; - 字元檔案和塊檔案均為裝置檔案, 可用
mknod
建立; - 連結檔案是軟連結檔案,而非硬連結檔案, 可用
ln
建立。
訪問許可權控制組
分為三組進行控制:
user
包含對檔案屬主設定的許可權group
包含對檔案屬組設定的許可權others
包含對其他者設定的許可權
可設定的許可權
下面給出常見(但非全部)的許可權值, 包括:
r
表示具有讀許可權。w
表示具有寫許可權。x
一般針對可執行檔案/目錄,表示具有執行/搜尋許可權。s
一般針對可執行檔案/目錄,表示具有賦予檔案屬主許可權的許可權,只有user
和group
組可以設定該許可權。t
一般針對目錄,設定粘滯位後,有許可權的使用者只能寫、刪除自己的檔案,否則可寫、刪除目錄所有檔案。舊系統還表示可執行檔案執行後將text拷貝到交換區提升速度。
舉例
通過 ls -l
可以檢視到其檔案型別及許可權,通過 chmod
修改許可權。
舉例來說,
1 2 3 4 5 6 7 8 9 10 11 |
$ ls -l /usr/bin/qemu-i386 -rwxr-xr-x 1 root root 2149080 8月 13 2014 /usr/bin/qemu-i386 $ chmod 1775 test/ $ ls -l |grep test drwxrwxr-t 2 miracle video 4096 7月 20 09:31 test $ chmod 2777 test2/ $ ls -l |grep test2 drwxrwsrwx 2 miracle video 4096 7月 20 09:32 test2 $ chmod 4777 test3/ $ ls -l |grep test3 drwsrwxrwx 2 miracle video 4096 7月 20 09:33 test3 |
輸出中, 第1個字元表示檔案型別,其中,普通檔案(-
)、目錄檔案 (d
)、套接字檔案(s
),管道檔案(p
),字元檔案(c
),塊檔案(b
),連結檔案(l
); 第2個字元開始的 -rwxr-xr-x
部分表示檔案的許可權位,共有9位。
對於檔案 /usr/bin/qemu-i386
, 這個許可權控制的含義是:
- 第2~4位的
rwx
表示該檔案可被它的owner
(屬主)以r
或w
或x
的許可權訪問。 - 第5~7位的
r-x
表示該檔案可被與該檔案同一屬組的使用者以r
或x
的許可權訪問 - 第8~10位的
r-x
表示該檔案可被其它未知使用者以r
或x
的許可權訪問。
對於 test/, test2/, test3/
設定的許可權:
r,w,x
許可權對每一許可權控制組的許可權用一位8進位制來表示; 例如:755
表示rwxr-xr-x
。s,t
許可權會替代x
位置顯示;設定s,t
許可權則需在對應的、用於控制r,w,x
的8進位制許可權控制組前追加數字;s
許可權用於屬主屬組控制,t
用於其它控制。- 設定屬主
s
需追加4
, 設定屬組s
追加2
, 設定其它者t
許可權追加1
; 例如前面對test/
設定t
, 則用1775
, 表示rwxrwxr-t
。
程式許可權控制資訊
程式許可權
對於程式,有如下屬性與檔案訪問許可權相關:
effective user id
: 程式訪問檔案許可權相關的UID
(簡寫為euid
)。effective group id
: 程式訪問檔案許可權相關的GID
(簡寫為egid
)。real user id
: 建立該程式的使用者登入系統時的UID
(簡寫為ruid
)。real group id
: 建立該程式的使用者登入系統時的GID
(簡寫為rgid
)。saved set user id
: 拷貝自euid
。saved set group id
: 拷貝自egid
。
舉例
我們可以使用 ps
和 top
選擇檢視具有 euid
和 ruid
的程式。或者通過 top
來檢視程式的 euid
和 ruid
通過 top
來檢視的例子:
- 首先輸入
top
得到類似如下
1234567891011$top -d 10.10top - 15:50:39 up 9 days, 1:42, 9 users, load average: 0.13, 0.16, 0.21Tasks: 287 total, 2 running, 284 sleeping, 0 stopped, 1 zombieCpu(s): 20.8%us, 4.6%sy, 0.0%ni, 72.5%id, 2.1%wa, 0.0%hi, 0.0%si, 0.0%stMem: 7707276k total, 7574252k used, 133024k free, 154872k buffersSwap: 1998844k total, 223744k used, 1775100k free, 3330212k cachedPID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND31603 miracle 20 0 2368m 681m 52m S 6 9.1 206:07.74 firefox1507 root 20 0 451m 188m 97m S 2 2.5 193:49.86 Xorg....
這裡通過-d
選項延長top
的重新整理頻率便於操作。此處可見,只有USER
欄位,表示相應程式的effective user id
. - 開啟
read user id
的顯示選項- 在
top
命令執行期間,輸入f
, 可以看見類似如下行:
1c: RUSER = Real user name - 輸入
c
即可開啟Real user name
的顯示開關。
1* C: RUSER = Real user name - 最後
Return
回車回到top
中,即可看到real user id
的選項此時輸入o
,可調整列次序最終我們可看到包含effective user id
和real user id
的輸出如下:
12345678910top - 15:57:58 up 9 days, 1:49, 9 users, load average: 0.23, 0.22, 0.23Tasks: 286 total, 1 running, 284 sleeping, 0 stopped, 1 zombieCpu(s): 3.9%us, 1.4%sy, 0.0%ni, 94.6%id, 0.1%wa, 0.0%hi, 0.0%si, 0.0%stMem: 7707276k total, 7539776k used, 167500k free, 154996k buffersSwap: 1998844k total, 225132k used, 1773712k free, 3300036k cachedPID USER RUSER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND31603 miracle miracle 20 0 2376m 688m 52m S 4 9.2 206:24.14 firefox1507 root root 20 0 451m 188m 97m S 3 2.5 194:06.27 Xorg....
其中,PID
是對應程式,USER
是對應的effective user
,RUSER
是對應的real user
。
- 在
程式訪問檔案的許可權控制策略
規則
程式訪問檔案大致許可權控制策略
對於程式訪問檔案而言,最重要的是 euid
, 所以其許可權屬性均以 euid
為 “中心”。
- 程式的
euid
一般預設即為 其ruid
值 - 若可執行檔案的可執行許可權位為
s
,程式對其呼叫exec
後,其euid
被設定為該可執行檔案的user id
- 程式的
saved set user id
拷貝自euid
. - 當程式的
euid
與檔案的user id
匹配時,程式才具有檔案user
許可權位所設定的許可權 - 組許可權
egid
的控制規則類似。
通過 exec
執行檔案修改許可權屬性
通過 exec
呼叫可執行檔案之時:
- 程式
ruid
值始終不變; saved set-user ID
始終來自euid
;euid
值取決於檔案的set-user-ID
位是否被設定。
如下:
ID | set-user-ID bit off | set-user-ID bit on |
---|---|---|
real user ID | unchanged | unchanged |
effective user ID | unchanged | set from userID of program file |
saved set-user ID | copied from effective user ID | copied from effective user ID |
通過 setuid(uid)
系統呼叫修改許可權屬性
通過 setuid(uid)
修改許可權屬性之時:
superuser
可順利修改ruid
,euid
,saved set-user ID
;unprivileged user
只能在uid
與ruid
相等時修改euid
, 其它無法修改。
如下:
ID | superuser | unprivileged user |
---|---|---|
real user ID | set to uid | unchanged |
effective user ID | set to uid | set to uid |
saved set-user ID | set to uid | unchanged |
舉例
再舉幾個比較特別的例子:
設定了 set-user-id
1 2 |
$ ls -l /usr/bin/sudo -rwsr-xr-x 1 root root 71288 2月 28 2013 /usr/bin/sudo |
如前所述,這個輸出的含義是,對於 /usr/bin/sudo
檔案,
- 第1~3位的
rws
表示該檔案可被它的owner(屬主)以r
或w
或s
的許可權訪問 - 第4~6位的
r-x
表示該檔案可被與該檔案同一屬組的使用者以r
或x
的許可權訪問。 - 第7~9位的
r-x
表示該檔案可被其它未知使用者以r
或x
的許可權訪問。
這樣設定之後,對於owner,具有讀、寫、執行許可權,這一點沒有什麼不同。但是對於不屬於 root
組的普通使用者程式來說,卻大不相同。
普通使用者程式執行 sudo
命令時通過其 others
中的 x
獲得執行許可權,再通過 user
中的 s
使得普通使用者程式臨時具有了 sudo
可執行檔案屬主( root
)的許可權,即超級許可權。
這也是為什麼通過 sudo
命令就可以讓普通使用者執行許多管理員許可權的命令的原因。
設定了 stick-bit
1 2 |
$ ls -l / |grep tmp drwxrwxrwt 25 root root 12288 7月 20 09:09 tmp |
這樣設定之後,對於 /tmp
目錄,任何人都具有讀、寫、執行許可權,這一點沒有什麼不同。但是對於 others
部分設定了粘滯位 t
, 其功能卻大不相同。
若目錄沒設定粘滯位,任何對目錄有寫許可權者都則可刪除其中任何檔案和子目錄,即使他不是相應檔案的所有者,也沒有讀或寫許可; 設定粘滯位後,使用者就只能寫或刪除屬於他的檔案和子目錄。
這也是為什麼任何人都能向 /tmp
目錄寫檔案、目錄,卻只能寫和刪除自己擁有的檔案或目錄的原因。
舉一個 man
程式的應用片斷,描述 set-user-id
和 saved set-user-id
的使用
man
程式可以用來顯示線上幫助手冊, man
程式可以被安裝指定 set-user-ID
或者 set-group-ID
為一個指定的使用者或者組。
man
程式可以讀取或者覆蓋某些位置的檔案,這一般由一個配置檔案(通常是 /etc/man.config
或者 /etc/manpath.config
)或者命令列選項來進行配置。
man
程式可能會執行一些其它的命令來處理包含顯示的 man
手冊頁的檔案。
為防止處理出錯, man
會從兩個特權之間進行切換:執行 man
命令的使用者特權,以及 man
程式的擁有者的特權。
需要抓住的主線:當只執行 man
之時,程式特權就是 man
使用者的特權, 當通過 man
執行子程式(如通過 !bash
引出shell命令)時,使用者切換為當前使用者,執行完又切換回去。
過程如下:
- 假設
man
程式檔案被使用者man
所擁有,並且已經被設定了它的set-user-ID
位,當我們exec
它的時候,我們有如下情況:real user ID
= 我們的使用者UIDeffective user ID
= man使用者UIDsaved set-user-ID
= man使用者UID
man
程式會訪問需要的配置檔案和man
手冊頁。這些檔案由man
使用者所擁有,但是由於effective user ID
是man
,檔案的訪問就被允許了。- 在
man
為我們執行任何命令的時候,它會呼叫setuid(getuid()))
(getuid()
返回的是real user id
).因為我們不是superuser
程式,這個變化只能改變effective user ID
. 我們會有如下情況:real user ID
= 我們的使用者UID(不會被改變)effective user ID
= 我們的使用者UIDsaved set-user-ID
= man 的使用者UID(不會被改變)
現在
man
程式執行的時候把我們得UID作為它的effective user ID
.這也就是說,我們只能訪問我們擁有自己許可權的檔案。也就是說,它能夠代表我們安全地執行任何filter
. - 當
filter
做完了的時候,man
會呼叫setuid(euid)
.這裡,euid
是man
使用者的UID.(這個ID是通過man
呼叫geteuid
來儲存的)這個呼叫是可以的,因為setuid
的引數和saved set-user-ID
是相等的。(這也就是為什麼我們需要saved set-user-ID
).這時候我們會有如下情況:real user ID
= 我們的使用者UID(不會被改變)effective user ID
= man的UIDsaved set-user-ID
= man 的使用者UID(不會被改變)
- 由於
effective user ID
是man
,現在man
程式可以操作它自己的檔案了。通過這樣使用saved set-user-ID
,我們可以在程式開始和結束的時候通過程式檔案的set-user-ID
來使用額外的許可權。然而,期間我們卻是以我們自己的許可權執行的。如果我們無法在最後切換回saved set-user-ID
,我們就可能會在我們執行的時候保留額外的許可權。
下面我們來看看如果 man
啟動一個 shell
的時候會發生什麼:
- 這裡的
shell
是man
使用fork
和exec
來啟動的。 - 因為這時
real user ID
和effective user ID
都是我們的普通使用者UID(參見step3), 所以shell
沒有其它額外的許可權. - 啟動的
shell
無法訪問man
的saved set-user-ID(man)
,因為shell
的saved set-user-ID
是由exec
從effective user ID
拷貝過來的。 - 在執行
exec
的子程式(shell
)中,所有的user ID
都是我們的普通使用者ID.
實際上,我們描述 man
使用 setuid
函式的方法不是特別正確,因為程式可能會 set-user-ID
為 root
.這時候, setuid
會把所有三種uid都變成你設定的id,但是我們只需要設定 effective user ID
.