當前位置:
首頁 > 知識 > Linux 用戶身份與進程許可權

Linux 用戶身份與進程許可權

在學習 Linux 系統許可權相關的主題時,我們首先關注的基本都是文件的 ugo 許可權。ugo 許可權信息是文件的屬性,它指明了用戶與文件之間的關係。但是真正操作文件的卻是進程,也就是說用戶所擁有的文件訪問許可權是通過進程來體現的。本文主要介紹進程的許可權,並通過示例解釋用戶身份與進程許可權之間的關係。說明:本文的演示環境為 ubuntu 16.04。


基本概念

用戶

對於支持多任務的 Linux 系統來說,用戶就是獲取資源的憑證。

許可權

許可權用來控制用戶對計算機資源(CPU、內存、文件等)的訪問,一般會分為認證和授權兩步。比如用戶先經過認證機制(authentication)登錄系統,然後由授權系統(authorization)對用戶的操作進行授權。

進程

進程是任何支持多道程序設計的操作系統中的基本概念。通常把進程定義為程序執行時的一個實例。因此,如果有 10 個用戶同時運行 vi,就會有 10 個獨立的進程(儘管它們共享同一份可執行代碼)。

實際上,是進程在幫助我們完成各種任務。進程就是用戶訪問計算機資源的代理,用戶執行的操作其實是帶有用戶身份信息的進程執行的操作。

進程許可權

既然是進程在為用戶執行具體的操作,那麼當用戶要訪問系統的資源時就必須給進程賦予許可權。也就是說進程必須攜帶發起這個進程的用戶的身份信息才能夠進行合法的操作。


從登陸過程觀察進程攜帶的用戶身份信息

在 Linux 系統啟動後,init 系統會 fork 出子進程執行 /sbin/getty 程序等待用戶登錄。當用戶進行登錄操作時,該子進程通過 exec 函數開始執行 /bin/login 程序(此時該進程已經變成了 login 進程)。由 login 進程驗證我們的用戶名和密碼並查詢 /etc/passwd 和 /etc/shadow 確定其合法性。如果是合法的用戶,該進程再次通過 exec 函數執行用戶的默認 shell 程序,此時的 login 進程就變成了 shell 進程(筆者機器上是 bash 進程)。並且該 shell 進程的有效身份被設置成為該用戶的身份,之後 fork 此 shell 進程的子進程都會繼承該有效身份。我們可以通過下圖來理解用戶從 tty 登錄系統的過程(此圖來自互聯網):

Linux 用戶身份與進程許可權

上圖描述了 init 進程、getty 進程、login 進程和 shell 進程的交互。

簡單點說就是:用戶登錄後, shell 進程的有效用戶就是該用戶。下面我們來了解下進程的用戶信息。


進程的 real user id、effective user id 和 saved set user id

通過 cat /proc/<PID>status 命令,我們可以查看到進程所屬的用戶和組相關的信息:

Linux 用戶身份與進程許可權

通過 man proc 可以查詢到第一行的四個數字分別是 real user id, effective user id, saved set user id 和 filesystem UID,第二行則是對應的組 ID。這裡我們只介紹第一行中的前三個 ID,即 real user id, effective user id 和 saved set user id。

real user id

real user id 是執行進程者的 user id,一般情況下就是用戶登錄時的 user id。子進程的 real user id 從父進繼承。通常這個是不更改的,也不需要更改。比如我以用戶 nick 登錄 Linux 系統,我接下來運行的所有命令的進程的 real user id 都是 nick 的 user id。

effective user id

如果要判斷一個進程是否對某個文件有操作許可權,驗證的是進程的 effective user id,而不是 real user id。

通常我們是不建議直接使用 root 用戶進行操作的,但是在很多情況下,程序可能需要特殊的許可權。比如 passwd 程序需要 root 許可權才能夠為普通用戶修改密碼,一些 services 程序的操作也經常需要特殊的許可權。為此,Linux 中設計了一些特殊的許可權,請參考《Linux 特殊許可權 SUID,SGID,SBIT》一文。這裡我們以 passwd 程序為例,為二進位可執行文件 /usr/bin/passwd 設置 set-user-id bit=ON,這個可執行文件被用 exec 啟動之後的進程的 effective user id 就是這個可執行文件的 owner id,而並非父進程的 real user id。如果 set-user-id bit=OFF 的時候,這個被 exec 起來的進程的 effective user id 應該是等於進程的 user id 的。所以,effective user id 存在的意義在於,它可能和 real user id 不同。

saved set user id

saved set user id 相當於是一個 buffer,在 exec 函數啟動之後,它會拷貝 effective user id 位的信息覆蓋自己。對於非 root 用戶來說,可以在未來使用 setuid() 函數將 effective user id 設置成為 real user id 或 saved set user id 中的任何一個。但是不允許非 root 用戶用 setuid() 函數把 effective user id 設置成為任何第三個 user id。

對於 root 用戶來說,調用 setuid() 的時候,將會設置所有的這三個 user id。

從總體上來看,進程中 real user id, effective user id 和 saved set user id 的設計是為了讓 unprivilege user 可以獲得兩種不同的許可權。同時我們也可以得出下面的結論:

Linux 系統通過進程的有效用戶 ID(effective user id) 和有效用戶組 ID(effective group id) 來決定進程對系統資源的訪問許可權。

其實我們通過 ps aux 查看的結果中,第一列顯示的就是進程的 effective user:

Linux 用戶身份與進程許可權


Shell 中外部命令的執行方式

在 shell 中執行的命令分為內部命令和外部命令兩種。

  • 內部命令:內建的,相當於 shell 的子函數
  • 外部命令:在文件系統的某個路徑下的一個可執行文件

外部命令的執行過程如下:

  1. Shell 通過 fork() 函數建立一個新的子進程,新的子進程為當前 shell 進程的一個副本。
  2. 在新的進程里,從 PATH 變數所列出的目錄中尋找指定的命令程序。當命令名稱包含有斜杠(/)符號時,將略過路徑查找步驟。
  3. 在新的進程里,通過 exec 系列函數,以所找到的新程序替換 shell 程序並執行。
  4. 子進程退出後,最初的 shell 會接著從終端讀取並執行下一條命令。

我們通過下面的例子來理解在 shell 中執行外部命令的過程,例子很簡單就是通過 cat 命令查看一個文本文件 test.log:

$ cat test.log

我們先來檢查一下當前用戶以及相關文件的許可權:

Linux 用戶身份與進程許可權

當前用戶 nick 的 real user id 為 1000,/bin/cat 文件的所有者為 root,但是所有人都有執行許可權,test.log 文件的所有者為 nick。我們結合下圖來介紹 cat test.log 命令的執行過程:

Linux 用戶身份與進程許可權

當我們在 shell 中執行一個外部程序的時候,默認情況下進程的 effective user ID 等於 real user ID,進程的 effective group ID 等於 real group ID(接下來的介紹中省略 group ID)。當我們以用戶 nick 登錄系統,並在 bash 中鍵入 cat test.log 命令並回車後。Bash 先通過 fork() 建立一個新的子進程,這個新的子進程是當前 bash 進程的一個副本。新的進程在 PATH 變數指定的路徑中搜索 cat 程序,找到 /bin/cat 程序後檢查其許可權。/bin/cat 程序的所有者為 root,但是其他人具有讀和執行的許可權,所以新進程可以通過 exec 函數用 cat 程序的代碼段替換當前進程中的代碼段(把 /bin/cat 程序載入到了內存中,此時的進程已經變成了 cat 進程,cat 進程會從 _start 函數開始執行)。由於 cat 進程是由用戶 nick 啟動的,所以 cat 進程的 effective user ID 是 1000(nick)。同時 cat 進程的 effective user ID 和 test.log 文件的 owner ID 相同(都是 1000),所以 cat 進程擁有對此文件的 rw- 許可權,那麼順理成章地就可以讀寫 test.log 文件的內容了。

下面我們演示一個通過設置特殊許可權 set uid ID 改變進程 effective user ID 的例子。

創建文件 root.log,許可權為 640,此時只有 root 有許可權讀寫該文件的內容,用戶 nick 連讀取該文件的許可權都沒有:

Linux 用戶身份與進程許可權

然後通過設置特殊許可權 set uid ID 讓運行 cat 程序的進程具有 root 許可權:

$ sudo chmod 4755 /bin/cat

Linux 用戶身份與進程許可權

現在可以了!因為運行 cat 程序的進程的 effective user ID 變成了 root。記得要把 /bin/cat 的許可權改回去呀:

$ sudo chmod 755 /bin/cat

Shell 腳本的執行方式

在 shell 中執行腳本的方式和執行外部命令的方式差不多,比如我們要執行下面的腳本:

$ /bin/bash ./test.sh

這時同樣會 fork 出一個子進程。只不過腳本與程序相比沒有代碼段,也沒有 _start 函數,此時 exec 函數就會執行另外一套機制。比如我們在 test.sh 文件的第一行通過 #!/bin/bash 指定了一個解釋器,那麼解釋器程序的代碼段會用來替換當前進程的代碼段,並且從解釋器的 _start 函數開始執行,而這個文本文件被當作命令行參數傳給解釋器。所以上面的命令執行過程為:Bash 進程 fork/exec 一個子 bash 進程用於執行腳本,子 bash 進程繼承父進程的環境變數、用戶信息等內容,父進程等待子 bash 進程終止。

總結

文件上的許可權信息和用戶的信息都是靜態的,而進程是動態的,它把自身攜帶的用戶信息和將要進行的操作結合起來,從而實現許可權管理。至此我們也基本上搞明白了 Linux 許可權系統的工作原理。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

SQL Server 與 MySQL 中排序規則與字符集相關知識的一點總結
oracle索引和用戶許可權

TAG:程序員小新人學習 |