使用者/群組 ID 與行程憑證(process credentials)
當一個行程試圖開啟檔案、殺掉另一個行程、或更改系統設定時,作業系統得做出判斷:這被允許嗎?它的回答方式是看這個行程是以「誰」的身分在行動。一個行程沒有使用者名稱;它有數字憑證——一個使用者 ID(UID)與一個或多個群組 ID(GID)——說明它帶著哪個帳號的權限。這些數字是行程的身分識別證,核心在每次權限判斷時都會檢查它們。想像一張能開某些門、開不了其他門的識別證。
細節比一個號碼豐富一些。每個行程其實有一個真實 UID(誰啟動它/誰擁有它)與一個有效 UID(此刻用於權限檢查的身分),群組也有同樣的一對,外加一份補充群組清單。通常真實與有效是一致的。它們在一個重要情況下不同:set-user-ID(setuid)程式。若一個可執行檔設了 setuid 位元、且擁有者是比方 root,那麼當你執行它時,你的行程拿到你的真實 UID,但得到 root 的有效 UID——在執行期間它暫時以 root 的權限行動。經典例子是 passwd:一般使用者執行它,但它必須編輯一個 root 擁有的密碼檔,所以它以 setuid-root 執行。關鍵在於,憑證會跨 fork() 繼承(子行程拿到父行程的 UID/GID),並在 exec() 後存留下來,除非新的程式檔是 setuid 的;這正是一個降權到非特權使用者的常駐程式,能讓它啟動的一切都維持非特權的方式。
為何重要:行程憑證是所有 Unix 權限與安全的基礎。它們決定你能讀哪些檔案、能對誰的行程發信號、核心會允許什麼特權操作。真實對有效的區別,是正確做特權提升的基礎(做錯則是討厭錯誤的來源):謹慎的程式只在需要的那個短暫操作期間提升有效權限、隨即放下,而粗心的 setuid 程式是攻擊者最愛的目標。即使你一輩子只寫普通的程式碼,知道你的行程以某個 UID/GID 執行,就能解釋你這輩子會碰到的每一個「Permission denied」。
執行 id,它印出你行程的憑證:uid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo)。執行 ls -l /usr/bin/passwd,你看到 -rwsr-xr-x,本該是 x 的位置出現了 s:那個 s 就是 setuid 位元,所以即使是 alice 啟動 passwd,它也以 root 的有效 UID 執行。
id 顯示行程的 UID/GID;setuid 位元(s)讓程式以檔案擁有者的有效 UID 執行。
憑證會跨 fork 繼承、並在 exec 後存留(除非新檔案是 setuid 的)。一個在 fork 工作者前先降權的常駐程式,會讓那些工作者維持非特權——這是一個刻意而重要的安全手段。