如何使用Linux使用者身份與程式許可權的設定?

03ngnntds發表於2019-02-21
在學習 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 登入系統的過程(此圖來自網際網路):
  上圖描述了 init 程式、getty 程式、login 程式和 shell 程式的互動。
  簡單點說就是:使用者登入後, shell 程式的有效使用者就是該使用者。下面我們來了解下程式的使用者資訊。
  程式的 real user id、effective user id 和 saved set user id
透過 cat /proc/<PID>status 命令,我們可以檢視到程式所屬的使用者和組相關的資訊:
  透過 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 程式的操作也經常需要特殊的許可權。這裡我們以 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:
Shell 中外部命令的執行方式
  在 shell 中執行的命令分為內部命令和外部命令兩種。
  內部命令:內建的,相當於 shell 的子函式
  外部命令:在檔案系統的某個路徑下的一個可執行檔案
  外部命令的執行過程如下:
  Shell 透過 fork() 函式建立一個新的子程式,新的子程式為當前 shell 程式的一個副本。
  在新的程式裡,從 PATH 變數所列出的目錄中尋找指定的命令程式。當命令名稱包含有斜槓(/)符號時,將略過路徑查詢步驟。
  在新的程式裡,透過 exec 系列函式,以所找到的新程式替換 shell 程式並執行。
  子程式退出後,最初的 shell 會接著從終端讀取並執行下一條命令。
  我們透過下面的例子來理解在 shell 中執行外部命令的過程,例子很簡單就是透過 cat 命令檢視一個文字檔案 test.log:
  $ cat test.log
  我們先來檢查一下當前使用者以及相關檔案的許可權:
  當前使用者 nick 的 real user id 為 1000,/bin/cat 檔案的所有者為 root,但是所有人都有執行許可權,test.log 檔案的所有者為 nick。我們結合下圖來介紹 cat test.log 命令的執行過程:
  當我們在 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 連讀取該檔案的許可權都沒有:
  然後透過設定特殊許可權 set uid ID 讓執行 cat 程式的程式具有 root 許可權:
  $ sudo chmod 4755 /bin/cat
  現在可以了!因為執行 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 許可權系統的工作原理。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69911024/viewspace-2636620/,如需轉載,請註明出處,否則將追究法律責任。

相關文章