--- title: '使用者切換、認識 PAM' disqus: kyleAlien --- 使用者切換、認識 PAM === ## Overview of Content :::success * 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/) 本篇文章對應的是 [**認識身份驗證與權限管理:UNIX UID、使用者驗證與 PAM | PAM 設定**](https://devtechascendancy.com/unix-uid_auth_pam/) ::: [TOC] ## 認識 UID & UID 切換 像是 `su`、`sudo` 這種個指令允許我們切換使用者,而 `login` 這個系統元件負責控制使用者存取... 這些會涉及核心如何切換使用者 * 更改使用者 ID 一般可以透過以下兩種方式(**`setuid` 的行為由核心負責完成切換**) 1. **檔案為 `setuid` 權限** > 有些可執行檔的執行位元是 `s`(一般為 `x`),表示在執行時會以 **檔案擁有者的身份去執行**,而非當前的用戶去執行 像是 `passwd` 命令就有 `setuid` 權限 > 請看下圖左邊 `-rwsr-xr-x` 中的 `s`,這表示了 `passwd` 命令有 `setuid` 權限 > > ![](https://hackmd.io/_uploads/rJPu1SOY2.png) :::info * 任何程序只有要足夠的檔案存取權限都可以執行 `setuid` 檔案 ::: 2. **呼叫系統設定 `setuid()` 函數** 可用 `setuid()` 函數來處理與程序相關連的所有使用者 ID(之後在詳細介紹) :::warning * **`setuid()` 規範** `setuid()` 規範由核心定義,大多以 `root` 使用者身份才能呼叫 `setuid()`;如果不已 `root` 身份呼叫 `setuid()`,那會有部份限制 > 大多數情況下都只接受 `root` 使用者呼叫 ::: ### 切換 UID:setuid 函數、安全問題 * **`setuid` 這個系統呼叫(System call)調整的是 `euid`** (所有者) * Linux 核心透過 `setuid` 程序核相關系統呼叫來處理使用者切換(還有相關檔案的存取權限),如果沒有注意,可能會導致安全性問題,常出現的問題如下 * **檔案如果有 `s` 顯示,那該檔案將會有 `setuid` 的權限**,會幫普通使用者提權 > 如果你建立了一個 shell 並且 `setuid` 權限為 root,那普通使用者執行時就可以執行它來獲得整個系統的控制權 * **程式功能面問題** 如果你的程式需要 `setuid` 為 `root` 才可以啟動(使用 `sudo`),如果程式出現 Bug 那很可能會導致系統損壞 ### 各種 UID:執行時 UID 切換 * 大多數時候提及的使用者 ID 都是「簡化過」的,**實際上每個程序都有 ++超過一個 UID++,**(或是應該說,UID 其實是有多種不同含義的) UID 有分為以下幾種,如下表… | UID 種類 | 說明 | 補充 | | - | - | - | | `Effective UID` (`euid`) | Effective UID 是用於權限檢查的用戶身份。當程序執行時,Effective UID 獲得了 Real UID 的權限,並可能根據需要被臨時修改(例如使用 setuid) | 執行者,**也就是一般情況下我們所說的 UID** | | `Real UID` (`ruid`) | Real UID 是程序「執行」的實際用戶身份 | 所有者,可以用來對程序發送訊號(像是中止) | | `Saved UID`(`suid`) | 已保存 UID | - | | `File system UID`(`fsuid`) | fsuid 是Linux特有的使用者ID,用於檔案系統權限檢查 | 通常情況下,進程的檔案系統使用者ID(fsuid)與其有效使用者ID(euid)相同 | > 從這裡我們可以看到 Unix 將應用的各個不同功能做了權限的區分,像是「設定」、「執行」... 等等權限分離 ```mermaid graph UID --> euid UID --> ruid UID --> suid ``` :::success * **這麽多 UID 要幹麻**? 簡單來說為了 **方邊控制**,想想如果沒有 UID 的改變,那我們在啟動程式後需要中止(或是資源修改),就會需要不斷的使用 `sudo` 切換 > 詳細可看 `man 2 setuid` 手冊 ::: * 如果我們是切換使用者執行程式(使用到 `setuid`)時,Linux 會將當前用戶的 `Effective UID` 設定為檔案的 `Effective UID`(ruid 不會改變) 1. **root 用戶使用執行應用,而應用中使用到** `setuid` 函數會將實際用戶 ID(`ruid`)、有效用戶 ID(`euid`)、保存用戶 ID(`suid`)設定為 root 用戶 ID ```mermaid graph TB r(root 用戶, 1000) --> |1. 執行 |應用 subgraph 應用 setuid end r(root 用戶, 1000) --> |2. 暫時改變 euid, ruid, suid 為 1000 | 應用 ``` 2. **一般用戶使用執行應用,而應用中使用到 `setuid`** 一般用戶執行 `setuid` 只會將有效用戶 ID(`euid`)設定為當前用戶 ID ```mermaid graph TB r(一般用戶, 6666) --> |1. 執行 |應用 subgraph 應用 setuid end r(一般用戶, 6666) --> |2. 暫時改變 euid 為 6666 | 應用 ``` :::info * 一般情況下 `euid`, `ruid` 會被設定為相同,可用以下命令查看 ```shell= ps -eo pid,euid,ruid,comm ``` > ![](https://hackmd.io/_uploads/rJufDSuth.png) ::: ## 多使用者驗證:使用者標示、認證 > 「使用者標示、認證」通常是指在使用者空間中的應用程序或程式庫使用的功能,用於識別和驗證使用者的身份 多使用者系統必須支援 **使用者標識**、**使用者認證**,像 Ubuntu 就是可以擁有多個使用者一起使用的系統,所以需要 **驗證不同的使用者** | 支援功能 | 說明 | | - | - | | **使用者「標識」**(`identification`) | 判斷是哪位使用者(Linux 核心透過使用者 EUID 來管理程序、檔案權限),並可透過 `setuid` 來切換 | | **使用者「認證」**(`authentication`) | 驗證是否是宣稱的使用者 | > **核心並不關心使用者空間有關認證的事項**(eg. 使用者名稱、密碼);核心只保存資料,而驗證是應用端的規範、應用 ### 傳統取得使用者名稱 * **傳統的 Unix 會經過以下步驟(概略)來獲取使用者名稱**(不包括密碼比對) 1. 程序透過 **系統呼叫 `geteuid()`,取得核心返回的 euid** 2. 開啟、讀取 **`/etc/passwd` 檔案** * 解析欄位,並使用切割的第三個項目來作為使用者 ID(EUID) * 匹配成功後獲取第一個項目作為使用者名稱 ```mermaid graph LR 驗證應用程序 --> |1. System Call, geteuid| 核心 -.-> |euid| 驗證應用程序 pw(/etc/passwd 檔案) 驗證應用程序 --> |2. 開啟、讀取| pw -.-> |第三欄位, uid| 驗證應用程序 pw -.-> |第一欄位, user name| 驗證應用程序 ``` ### 使用函數庫:取得使用者資訊 * 由於使用者名稱的獲取是有固定步驟,所以 Unix 有提供一個標準函數庫供我們使用,簡化步驟如下 1. 程序透過 **系統呼叫 `geteuid()`,取得核心返回的 euid** 2. 程序可以使用函數庫的 `getpwuid` 函數來獲取與該使用者 ID 相關聯的使用者記錄,其中包括使用者名稱 :::warning * 這裡說明的函數並「不包括密碼」的驗證 (`/etc/passwd`);傳統的密碼驗證有以下 **侷限** * 密碼協定、權限 * 加密協定並 **沒有系統等級的標準** * 比對密碼的前提是 **你需要對加密密碼有存取權限** * 傳統驗證會假定 * 假定每當使用者需要存取資源時都需輸入使用者名稱、密碼驗證 * 假定是使用輸入密碼,**無法接受指紋識別、人臉驗證(也就是生物識別技術)**,如果需要則須自己使用插件做功能支援 ::: ## 認識 PAM:高靈活性框架 為了提高使用者驗證的靈活性,Sun 公司在 1995 年提出了一個 **新標準 PAM (`Pluggable Authentiation Module`)** > PAM可以被視為「使用者標示、認證」方面的一個應用… PAM 是一個用於管理系統身份驗證「**框架**」,在許多 Unix-like 系統中被廣泛使用 **PAM 是共享的驗證函式庫**,使用者被移交給 PAM 來決定使用者的驗證方式,這 **方便於插入新的驗證方式**(其靈活性很高),並且也提供一些驗證控制服務 > 驗證函式庫由 `Open source software foundation` 提出 ### PAM 設定:堆疊規則、驗證設定選項 * **PAM 的設定檔通常存放在 `/etc/pam.d` 目錄下**(也可能在 `/etc/pam.conf` 檔案中) ```shell= ls -laF /etc/pam.d ``` > ![](https://hackmd.io/_uploads/rkSju1tth.png) * 我們來看 `/etc/pam.d/chsh` 的內容;在註解中我們可以看到:它只允許使用者使用 `/etc/shells` 中的 Shell ```shell= # This will not allow a user to change their shell unless # their current one is listed in /etc/shells. This keeps # accounts with special shells from changing them. auth required pam_shells.so ``` | 設置欄位(左到右) | chsh 設置 | 說明 | | - | - | - | | 功能類型 | `auth` | 某個使用者應用程式請求 PAM 執行的「驗證」任務;以當前來說是 `auth` 驗證任務 | | 控制參數 | `required` | 任務失敗時的操作;`required` 表示如果這個模組的執行失敗,則整個認證過程將被視為失敗(也就是 **驗證是必須的操作**) | | 模組 | `pam_shells.so` | **可插拔的驗證模組**;以當前來說 `pam_shells.so` 模組檢查使用者 Shell 是否在 `/etc/shells` 中 | :::info * 詳細設置可以透過 `man 5 pam.conf` 手冊查看 ::: 我們在更近一步認識每一個欄位可以設定的數值 * **功能類型**:主要有以下幾類 > 通常我們會結合 功能類型、模組 來確定該行的功能 | 功能類型 | 說明 | 補充 | | -------- | -------- | -------- | | `auth` | 驗證使用者身份 | - | | `account` | 檢查使用者帳號狀態 | eg. 使用者是否有操作的權限 | | `session` | 僅在使用者目前程序內執行 | eg. 顯示當日訊息 | | `password` | 用來更改使用者密碼、其他驗證資訊 | - | * **控制參數者**:有分為 **簡單語法**、**高級語法**,簡單語法有如下幾種 | 控制參數 | 成功後操作 | 失敗後操作 | | -------- | -------- | -------- | | `sufficient` | 驗證就成功,PAM 會忽略其他規則 | PAM 繼續執行其他規則 | | `requisite` | PAM 會繼續執行其他規則 | 驗證失敗,忽略其他 PAM 規則 | | `required` | PAM 繼續執行其他規則 | PAM 繼續執行其他規則,**但最終仍會驗證失敗!** | :::success * **高級語法是使用方括號 `[]` 表示**,它可以根據模組的回傳值手動定義相應的操作 > 可查看 `man -5 pam.conf` 手冊 ::: PAM 的驗證說可堆疊的規則,範例如下 ```shell= auth sufficient pam_rootok.so auth requisite pam_shells.so auth sufficient pam_unix.so auth required pam_deny.so ``` 堆疊規則後的驗證順序如下圖所示 > ![](https://hackmd.io/_uploads/r1w0ZxYt3.png) * **模組參數**:PAM 模組就是運行的函數,使用時也可以帶入參數 ```shell= # 參數 nullok # 表示使用者可不用密碼 auth sufficient pan_unix.so nullok ``` ### PAM 其他知識點:模組查詢 * **模組查詢**:可使用以下指令查看 PAM 相關使用模組的文檔 ```shell= man -k pam_ ``` > ![](https://hackmd.io/_uploads/rkM2NgtY2.png) * **`/etc/pam.d/other`** 檔案:沒有設定檔的程式會套用 `other` 的規則 :::info * 其中 `@include` 語法用來載入整個設定檔(以可以使用控制參數來載入某個特定功能的設定檔) ::: > ![](https://hackmd.io/_uploads/rJOQrgFKn.png) ### 預設會取密碼的方式、PAM 獲得密碼 * **首先提及 `/etc/shadow` 檔案**: 檔案 `/etc/login.defs` 它是 `/etc/shadow` 檔的設定值,其中包含了用於 shadow 密碼檔案加密算法的資訊 :::info * **`login.defs` 的功能**? `login.defs` 的功能 **已經漸漸被 PAM 取代,不過仍有作用**,如果在系統不支援 PAM 時,就會去使用 `login.defs` 的功能 ::: * **PAM 如何獲得密碼加密資訊**? PAM 處理密碼是透過,功能類型的 `auth` & `password` 兩個功能去驗證;如果要查看密碼設定可以使用以下命令 ```shell= grep -r password.*unix /etc/pam.d/* ``` > ![](https://hackmd.io/_uploads/SJHHueKF2.png) 解釋如下 1. **功能參數**:`password` 這表示這個設定行用於密碼驗證步驟 2. **控制參數**:`[success=1 default=ignore]` 這是一個控制指令,用於指定驗證模組的執行流程 * `success=1`:表示如果這個模組成功驗證了密碼,則跳過下一個成功的模組 * `default=ignore`:表示如果這個模組不符合執行條件,則忽略它,並繼續執行下一個模組 3. **模組**:`pam_unix.so` 這是實際執行密碼驗證的 PAM 模組的名稱,並帶入以下參數 * `obscure`:這是 `pam_unix.so` 模組的一個參數,**用於指定密碼的複雜性要求** * `yescrypt`:這是 `pam_unix.so` 模組的另一個參數,**用於指定密碼的加密方法** :::info 目前看起來,PAM 並不知道要使用哪個算法驗證,猜測是按照一訂的規則去一次嘗試解開,這也是它「框架」給予的部分自由度 ::: ## Appendix & FAQ :::info ::: ###### tags: linux