---
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` 權限
>
> 
:::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
```
> 
:::
## 多使用者驗證:使用者標示、認證
> 「使用者標示、認證」通常是指在使用者空間中的應用程序或程式庫使用的功能,用於識別和驗證使用者的身份
多使用者系統必須支援 **使用者標識**、**使用者認證**,像 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
```
> 
* 我們來看 `/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
```
堆疊規則後的驗證順序如下圖所示
> 
* **模組參數**:PAM 模組就是運行的函數,使用時也可以帶入參數
```shell=
# 參數 nullok
# 表示使用者可不用密碼
auth sufficient pan_unix.so nullok
```
### PAM 其他知識點:模組查詢
* **模組查詢**:可使用以下指令查看 PAM 相關使用模組的文檔
```shell=
man -k pam_
```
> 
* **`/etc/pam.d/other`** 檔案:沒有設定檔的程式會套用 `other` 的規則
:::info
* 其中 `@include` 語法用來載入整個設定檔(以可以使用控制參數來載入某個特定功能的設定檔)
:::
> 
### 預設會取密碼的方式、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/*
```
> 
解釋如下
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