PAM詳解

1. PAM 簡介

PAM 的全稱為“可插拔認證模塊(Pluggable Authentication Modules)”。設計的初衷是將不同的底層認證機制集中到一個高層次的API中,從而省去開發人員自己去設計和實現各種繁雜的認證機制的麻煩。如果沒有 PAM ,認證功能只能寫在各個應用程序中,一旦要修改某個認證方法,開發人員可能不得不重寫程序,然后重新編譯程序并安裝;有了 PAM ,認證的工作都交給 PAM ,程序主體便可以不再關注認證問題了:“ PAM 允許你進來,那你就進來吧。”

PAM 機制最初由 Sun 公司提出,并在其 Solaris 系統上實現。后來,各個版本的 UNIX 以及 Linux 也陸續增加了對它的支持。Linux-PAM 便是 PAM 在 Linux 上的實現,獲得了幾乎所有主流 Linux 發行版的支持。Linux-PAM 的目標同 PAM 一致,都是為了給程序的開發人員提供一套統一的認證接口。本系列中對 PAM 的介紹和實驗,如無特別說明,一般都特指 Linux-PAM,實驗的環境也均是 Linux。

我們先用一個例子來直觀感受一下 PAM 。

su 是一個很常用的 Linux 命令,可以讓我們從一個用戶切換到另一個用戶。我們都知道,當用戶使用 root 賬號登錄時,su 到別的用戶是不需要密碼的,而從其他用戶 su 到 root 則需要輸入密碼。在用 su 命令切換用戶的過程中, su 做了兩件事:認證(是否是 root、不是 root 的話是否有目標用戶的密碼)和啟動相應的 Shell。

讓我們來關注一下 su 的認證功能。按照正常的邏輯, su 的開發人員很可能會自己寫出認證的功能:先判斷是不是 root,是則判定認證通過;不是則要求用戶輸入目標賬號的密碼,匹配成功則認證通過,否則不通過。這套邏輯并不復雜,開發人員開發出來便是了。

過了幾天,用戶提出了這樣的一個需求:運維團隊都屬于 wheel 組,能不能讓 wheel 組的用戶也能不輸入密碼而使用 su 切換?看起來也不是什么特別困難的需求,開發人員本可以滿足就是了。但是如果再過幾天,運維小張考慮到安全想要 su 有短信驗證碼功能,而用戶小王為了方便測試想要一個完全不用密碼的 su。認證需求的差異化越來越明顯,開發人員的工作也變得越來越困難。

這時,PAM 出現了。PAM 對開發人員說:“認證的事情交給我,你只要告訴我你想做用戶認證就好,余下的事情由我來解決,能不能通過由我來說了算。”它又對運維人員說:“你們來我這里編寫你們想要的針對 su 的認證策略吧,我將充分保證功能的靈活。”

這下好辦了。su 的開發人員可以專注地為用戶啟動 Shell 服務,而不需要關心用戶認證的細節了;用戶或復雜、或簡單的認證需求也都得到了滿足。真是皆大歡喜。

Linux-PAM 工作的“類別”(type)

PAM 的具體工作主要有以下四種類別(type)accountauthpassword 以及 session。這里,我們用非定義化的語言來簡單解釋一下這四種類別。

  • account:在用戶能不能使用某服務上具有發言權,但不負責身份認證。比如,account 這個 type 可以檢查用戶能不能在一天的某個時間段登錄系統、這個用戶有沒有過期、以及當前的登錄用戶數是否已經飽和等等。通常情況下,在登錄系統時,如果你連 account 這個條件都沒滿足的話,即便有密碼也還是進不去系統的。
  • auth:驗證“你的確是你”的 type 。一般來說,詢問你密碼的就是這個 type。假如你的驗證方式有很多,比如一次性密碼、指紋、虹膜等等,都應該添加在 auth 下。auth 做的另外一件事情是權限授予,比如賦給用戶某個組的組員身份等等。
  • password:主要負責和密碼有關的工作。修改密碼的時候有時會提示“密碼不夠長”、“密碼是個常用單詞”之類的,就是在這里設置的。在這里還設置了保存密碼時使用了哪種加密方式(比如現在常用的 SHA-512)。請注意,這里的密碼不局限于 /etc/shadow 中的密碼,有關認證 token 的管理都應該在此設置:如果你使用指紋登錄 Linux,在設置新指紋時,如果希望首先驗證這是人的指紋而不是狗的指紋,也應該放在這里。
  • session:一個“忙前忙后”的 type,它要在某個服務提供給用戶之前和之后做各種工作。比如用戶登錄之前要將用戶家目錄準備好,或者在用戶登錄之后輸出 motd 等等。

請注意:PAM 不僅僅在用戶登錄時才發揮作用,sudo命令,su命令,passwd命令都會用到 PAM。前文中所有提及“登錄”的地方都僅僅是舉例,您完全可以用其他需要用戶認證的服務(或者命令)去舉例,從而更全面地理解 PAM。


2. Linux-PAM 的配置文件綜述

PAM 的各個模塊一般存放在 /lib/security//lib64/security/ 中,以動態庫文件的形式存在(可參閱 dlopen(3)),文件名格式一般為 pam_*.so。PAM 的配置文件可以是 /etc/pam.conf 這一個文件,也可以是 /etc/pam.d/ 文件夾內的多個文件。如果 /etc/pam.d/ 這個文件夾存在,Linux-PAM 將自動忽略 /etc/pam.conf

/etc/pam.conf 類型的格式如下:

服務名稱  工作類別  控制模式  模塊路徑  模塊參數

/etc/pam.d/ 類型的配置文件通常以每一個使用 PAM 的程序的名稱來命令。比如 /etc/pam.d/su/etc/pam.d/login 等等。還有些配置文件比較通用,經常被別的配置文件引用,也放在這個文件夾下,比如 /etc/pam.d/system-auth。這些文件的格式都保持一致:

工作類別  控制模式  模塊路徑  模塊參數

不難看出,文件夾形式的配置文件中只是沒有了服務名稱這一列:服務名稱已經是文件名了。

由于很難在時下的發行版本中找到使用 /etc/pam.conf 這一獨立文件作為 PAM 配置的例子,此處僅就 /etc/pam.d/ 格式舉例。在筆者安裝的 CentOS 7.2.1511 x64 中,/etc/pam.d/login 的內容如下:

#%PAM-1.0
auth [user_unknown=ignore success=ok ignore=ignore default=bad] pam_securetty.so
auth       substack     system-auth
auth       include      postlogin
account    required     pam_nologin.so
account    include      system-auth
password   include      system-auth
# pam_selinux.so close should be the first session rule
session    required     pam_selinux.so close
session    required     pam_loginuid.so
session    optional     pam_console.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session    required     pam_selinux.so open
session    required     pam_namespace.so
session    optional     pam_keyinit.so force revoke
session    include      system-auth
session    include      postlogin
-session   optional     pam_ck_connector.so

# 表示注釋。

每一行代表一條規則。但也可以用 \ 來放在行末,來連接該行和下一行。

例子的最后一行開頭有一個短橫線 -,意思是如果找不到這個模塊,導致無法被加載時,這一事件不會被記錄在日志中。這個功能適用于那些認證時非必需的、安裝時可能沒被安裝進系統的模塊。

工作類別(type)、流程棧(stack)和控制模式(control)

我們在[第一篇]({% post_url 2016-03-30-pam-tutorial-1-intro %})中接觸了 Linux-PAM 的四種工作類別(type)。在上面的例子中,工作類別作為第一列出現。

講到這里,我們有必要聊一聊 PAM 的流程棧(stack)概念:它是認證時執行步驟和規則的堆疊。在某個服務的配置文件中,它體現在了配置文件中的自上而下的執行順序中。棧是可以被引用的,即在一個棧(或者流程)中嵌入另一個棧。我們之后和它會有更多的接觸。

第二列為控制模式(control),用于定義各個認證模塊在給出各種結果時 PAM 的行為,或者調用在別的配置文件中定義的認證流程棧。該列有兩種形式,一種是比較常見的“關鍵字”模式,另一種則是用方括號([])包含的“返回值=行為”模式。

“關鍵字”模式下,有以下幾種控制模式:

  • required:如果本條目沒有被滿足,那最終本次認證一定失敗,但認證過程不因此打斷。整個棧運行完畢之后才會返回(已經注定了的)“認證失敗”信號。
  • requisite:如果本條目沒有被滿足,那本次認證一定失敗,而且整個棧立即中止并返回錯誤信號。
  • sufficient:如果本條目的條件被滿足,且本條目之前沒有任何required條目失敗,則立即返回“認證成功”信號;如果對本條目的驗證失敗,不對結果造成影響。
  • optional:該條目僅在整個棧中只有這一個條目時才有決定性作用,否則無論該條驗證成功與否都和最終結果無關。
  • include:將其他配置文件中的流程棧包含在當前的位置,就好像將其他配置文件中的內容復制粘貼到這里一樣。
  • substack:運行其他配置文件中的流程,并將整個運行結果作為該行的結果進行輸出。該模式和 include 的不同點在于認證結果的作用域:如果某個流程棧 include 了一個帶 requisite 的棧,這個 requisite 失敗將直接導致認證失敗,同時退出棧;而某個流程棧 substack 了同樣的棧時,requisite 的失敗只會導致這個子棧返回失敗信號,母棧并不會在此退出。

“返回值=行為”模式則更為復雜,其格式如下:

[value1=action1 value2=action2 ...]

其中,valueN 的值是各個認證模塊執行之后的返回值。有 successuser_unknownnew_authtok_reqddefault 等等數十種。其中,default 代表其他所有沒有明確說明的返回值。返回值結果清單可以在 /usr/include/security/_pam_types.h 中找到,也可以查詢 pam(3) 獲取詳細描述。

流程棧中很可能有多個驗證規則,每條驗證的返回值可能不盡相同,那么到底哪一個驗證規則能作為最終的結果呢?這就需要 actionN 的值來決定了。actionN 的值有以下幾種:

  • ignore:在一個棧中有多個認證條目的情況下,如果標記 ignore 的返回值被命中,那么這條返回值不會對最終的認證結果產生影響。
  • bad:標記 bad 的返回值被命中時,最終的認證結果注定會失敗。此外,如果這條 bad 的返回值是整個棧的第一個失敗項,那么整個棧的返回值一定是這個返回值,后面的認證無論結果怎樣都改變不了現狀了。
  • die:標記 die 的返回值被命中時,馬上退出棧并宣告失敗。整個返回值為這個 die 的返回值。
  • ok:在一個棧的運行過程中,如果 ok 前面沒有返回值,或者前面的返回值為 PAM_SUCCESS,那么這個標記了 ok 的返回值將覆蓋前面的返回值。但如果前面執行過的驗證中有最終將導致失敗的返回值,那 ok 標記的值將不會起作用。
  • done:在前面沒有 bad 值被命中的情況下,done 值被命中之后將馬上被返回,并退出整個棧。
  • N(一個自然數):功效和 ok 類似,并且會跳過接下來的 N 個驗證步驟。如果 N = 0 則和 ok 完全相同。
  • reset:清空之前生效的返回值,并且從下面的驗證起重新開始。

我們在前文中已經介紹了控制模式(contro)的“關鍵字”模式。實際上,“關鍵字”模式可以等效地用“返回值=行為”模式來表示。具體的對應如下:

  • required
    [success=ok new_authtok_reqd=ok ignore=ignore default=bad]
  • requisite
    [success=ok new_authtok_reqd=ok ignore=ignore default=die]
  • sufficient
    [success=done new_authtok_reqd=done default=ignore]
  • optional
    [success=ok new_authtok_reqd=ok default=ignore]
模塊路徑和模塊參數

正如前文所述,模塊一般保存在 /lib/security/lib64/security 中(取決于操作系統位數)。Linux-PAM 配置文件中的模塊位置可以是相對于上述文件夾的相對路徑,也可以是文件的全路徑。

模塊參數用空格與模塊路徑相隔。該參數將只和特定模塊相關,因此某個模塊的文檔中一定包含其參數的信息。如果需要在單個參數中使用空格,可以將整個參數用方括號([])包裹起來。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容