現代化驗證:使用 GNU Guile 撰寫 Linux-PAM 模組

超過三十年來,Linux-PAM(可插拔驗證模組)一直是 Linux 系統上驗證的基石。它的設計在當時是革命性的,允許像 sulogin 這類與安全相關的應用程式遵循一致的政策,而不必在政策變更時重新編譯。然而,依賴共享 C 物件往往使得編寫和維護自訂模組的過程變得繁瑣且不透明。

Guile-PAM 作為一種現代替代方案出現,使系統管理員能夠使用 GNU Guile(Scheme 的一種方言)撰寫、配置與維護 PAM 模組。透過彌合低階 PAM API 與高階直譯語言之間的鴻溝,Guile-PAM 將驗證邏輯從靜態編譯的二進位檔轉變為彈性且易讀的腳本。

Guile-PAM 的架構

在核心,Guile-PAM 透過一個特別製作的共享物件 pam_guile.so 運作。此物件充當閘道,呼叫 GNU Guile 腳本來處理驗證任務。此架構允許三種不同層級的整合:

  1. 任務特定模組:使用 pam_guile.so 作為標準 Linux-PAM 共享物件,以在 Guile 中執行單一特定任務。
  2. 服務取代:將整個 PAM 配方(特定服務)取代,讓 pam_guile.so 成為唯一的共享物件,並在 Guile 中處理所有邏輯。
  3. 系統範圍協調:將 /etc/pam.d 中的所有服務取代為 pam_guile.so,實質上將配方選擇與邏輯都移入 Guile 環境。

了解「Pamda」

在 Guile-PAM 中,邏輯的基本單位是 Pamda(PAM 與 Lambda 的合成詞)。Pamda 是一個匿名程序,具有以下簽名:

(lambda (action handle flags options) ...)
  • Action:六個符號之一(例如 pam_sm_authenticatepam_sm_open_session),代表 PAM 入口點。
  • Handle:一個不透明變數,提供對內部 PAM 資料的存取,例如使用者名稱與對話函式。
  • Flags:整數位元遮罩。
  • Options:從服務定義傳遞的字串列表。

重新實作 PAM 堆疊

Guile-PAM 最強大的功能之一是以 Scheme 完全重新實作 Linux-PAM 堆疊。開發者不必依賴 Linux-PAM 的靜態設定檔,而是可以以程式方式定義「gate」與「plan」。

Gate 與 Plan

gate 程序包裝一個 pamda 並指派給它一個 plan(例如 requiredoptionalsufficientrequisite)。stack 程序則依序評估這些 gate。

對於從傳統設定遷移的使用者,Guile-PAM 提供 control-string->plan,可將標準 Linux-PAM 控制字串(例如 success=ok ignore=ignore default=bad)直接轉換為 Guile-PAM 的 plan。

混合堆疊

Guile-PAM 不需要完全拋棄現有的 C 基礎模組。透過 call-shared-object,Guile-PAM 堆疊可以呼叫舊有的 .so 檔案。為防止穩定性問題——因為許多舊模組在被呼叫未實作的動作時會回傳 PAM_SERVICE_ERR——Guile-PAM 允許開發者使用 #:implements 關鍵字明確指定共享物件實作的動作。

進階使用案例:超越簡單驗證

由於 Guile-PAM 是一種可存取系統的直譯語言,它能實現標準 PAM 模組難以或無法完成的功能。

範例:使用者層級掛載

Guile-PAM 的主要動機之一是能以實際使用者而非 root 身份掛載磁碟。雖然有 pam_mount.so,但它通常以 root 身份掛載,導致在 FUSE 資料夾或使用 Kerberos 的 NFSv4 時失敗。

Guile-PAM 允許一種工作流程:在 pam_sm_authenticate 期間收到的 token 被儲存,然後在 pam_sm_open_session 時使用,將密碼傳入如 gocryptfs 的使用者空間工具,於登入過程中以使用者身份掛載家目錄。

安全性與效能考量

將驗證邏輯移至直譯語言會帶來特定的考量:

安全性

  • 可讀性:作者認為 Scheme 程式碼通常比 C 更易於審核,提升同行審查的可能性。
  • 注入風險:為防止攻擊,提醒開發者不要對未知輸入使用 Guile 的 read 程序。
  • 環境隔離pam_guile.so 隔離環境變數如 GUILE_LOAD_PATH,以防止未授權的模組替換。

效能

實務測試顯示 Guile-PAM 的速度足以應付。有趣的是,作者指出在驗證中極速並非總是目標,因為常會刻意加入延遲以防止暴力破解攻擊。

實作注意事項

使用者應注意 Guile-PAM 目前仍處於 alpha 階段。與成熟的 Linux-PAM 相比,存在關鍵行為差異:

  • 動作分組:Linux-PAM 會將某些動作(如 authsetcred)分組,若第一個回傳 PAM_IGNORE,則可能跳過第二個。Guile-PAM 的 (pam stack) 實作會對每個動作評估所有模組。
  • 跳過計數:在具有明確跳過計數的舊指令集裡,解鎖的 Guile-PAM 堆疊總是取得結果,而 Linux-PAM 的副作用則依動作而異。

結論

Guile-PAM 代表了向更透明且彈性的系統管理轉變。將驗證視為可程式化的邏輯問題,而非二進位檔的配置,使其能開啟新穎的分支策略,並與其他自由軟體系統(如 OpenBSD 腳本)更深度整合。對於使用 GNU Guix 的使用者而言,整合更為順暢,允許在系統設定中定義整個驗證堆疊。

Sources