深入Linux PAM體系結構

本文闡述了 Linux-PAM 的概念,同時還與讀者一道分析了 Linux-PAM 的體系結構,作者希望籍此以加深讀者對 Linux-PAM 的理解,以便對其有更深層的把握。

 

一、什麼是Linux-PAM

 

爲安全起見,計算機系統只有經過授權的合法用戶才能訪問,在這裏如何正確鑑別用戶的真實身份是一個關鍵的問題。所謂用戶鑑別,就是用戶向系統以一種安全的方式提交自己的***明,然後由系統確認用戶的身份是否屬實的過程。換句話說,用戶鑑別是系統的門戶,每個用戶進入到系統中都必須經過鑑別這一道關。

 

最初,Linux系統的用戶鑑別過程就像各種Unix系統的一樣:系統管理員爲用戶建立一個帳號併爲其指定一個口令,用戶用此指定的口令登錄後重新設置自己的口令,這樣用戶就具有了一個只有他自己知道的祕密口令。一般情況下,用戶的口令經過加密處理後存放於 /etc/passwd 文件中。用戶登錄時,登錄服務程序提示用戶輸入其用戶名和口令,然後將口令加密並與 /etc/passwd 文件中對應帳號的加密口令進行比較,如果口令相匹配,說明用戶的身份屬實並允許此用戶訪問系統。這種思想基於只有用戶自己知道他的口令,所以輸入的口令是正確的話,那麼系統就認定他是所聲稱的那個人。

 

後來,還採用了許多其他的鑑別用戶的方法,如用於網絡環境的 Kerberos 以及基於智能卡的鑑別系統等。但是這些鑑別方案有一個通病:實現鑑別功能的代碼通常作爲應用程序的一部分而一起編譯,這樣問題就來了------如果發現所用算法存在某些缺陷或想採用另一種鑑別方法時,用戶不得不重寫(修改或替換)然後重新編譯原程序。很明顯,我們原先的鑑別方案缺乏靈活性,這裏的牽一髮而動全身的情形很是讓人惱火。

 

鑑於以上原因,人們開始尋找一種更佳的替代方案:一方面,將鑑別功能從應用中獨立出來,單獨進行模塊化設計,實現和維護;另一方面,爲這些鑑別模塊建立標準 API,以便各應用程序能方便的使用它們提供的各種功能;同時,鑑別機制對其上層用戶(包括應用程序和最終用戶)是透明的。直到 1995 年,SUN 的研究人員提出了一種滿足以上需求的方案--插件式鑑別模塊(PAM)機制並首次在其操作系統 Solaris 2.3 上部分實現。插件式鑑別模塊(PAM)機制採用模塊化設計和插件功能,使得我們可以輕易地在應用程序中插入新的鑑別模塊或替換原先的組件,而不必對應用程序做任何修改,從而使軟件的定製、維持和升級更加輕鬆--因爲鑑別機制與應用程序之間相對獨立。應用程序可以通過 PAM API 方便的使用 PAM 提供的各種鑑別功能,而不必瞭解太多的底層細節。此外,PAM的易用性也較強,主要表現在它對上層屏蔽了鑑別的具體細節,所以用戶不必被迫學習各種各樣的鑑別方式,也不必記住多個口令;又由於它實現了多鑑別機制的集成問題,所以單個程序可以輕易集成多種鑑別機制如 Kerberos 鑑別機制和 Diffie - Hellman 鑑別機制等,但用戶仍可以用同一個口令登錄而感覺不到採取了各種不同鑑別方法。

 

在廣大開發人員的努力下,各版本的 UNIX 系統陸續提供對 PAM 的支持。其中,Linux-PAM 是專門爲 Linux 機器實現的,包括 Caldera 1.3、2.2、Debian 2.2、Turbo Linux 3.6、Red Hat 5.0 以及 SuSE 6.2 及它們的後續版本都提供對 PAM 的支持。FreeBSD 從 3.1 版開始支持 PAM。需要注意的是,除了具體實現不同外,各種版本 Unix 系統上的 PAM 的框架是相同的,所以我們在這裏介紹的 Linux-PAM 框架知識具有普遍性。因此在下文介紹其框架的過程中可以看到,我們並沒有刻意區分 PAM 與 Linux-PAM 這兩個術語。

 

二、PAM 的分層體系結構

 

PAM 爲了實現其插件功能和易用性,它採取了分層設計思想:讓各鑑別模塊從應用程序中獨立出來,然後通過PAM API作爲兩者聯繫的紐帶,這樣應用程序就可以根據需要靈活地在其中"插入"所需鑑別功能模塊,從而真正實現了"鑑別功能,隨需應變"。實際上,這一思路非常符合軟件設計中的"高內聚,低耦合"這一重要思想,PAM 的體系如下簡圖所示:

 

\

 

圖1 PAM體系結構

 

從上圖可以看出,PAM API 起着承上啓下的作用,它是應用程序和鑑別模塊之間聯繫的紐帶:當應用程序調用 PAM API 時,應用接口層按照配置文件 pam.conf 的規定,加載相應的鑑別模塊。然後把請求(即從應用程序那裏得到的參數)傳遞給底層的鑑別模塊,這時鑑別模塊就可以根據要求執行具體的鑑別操作了。當鑑別模塊執行完相應操作後,將結果返回給應用接口層,然後由接口層根據配置的具體情況將來自鑑別模塊的應答返回給應用程序。

 

上面描述了 PAM 的各個組成部分,以及它們作爲整體的運作機理。下面將對 PAM 的關鍵的低二層分別加以介紹。

 

三、第一層:模塊層

 

模塊層處於整個結構的最底層,它向上爲接口層提供用戶鑑別等服務,也就是說所有具體的鑑別工作都是由該層的模塊來完成的。對於應用程序,有些不但需要驗證用戶的口令,還可能要求驗證用戶的帳戶是否已經過期。此外,有些應用程序也許還會要求記錄當前會話的有關信息或改變口令等,所以 PAM 在模塊層除了提供鑑別模塊外,同時提供了支持帳戶管理、會話管理以及口令管理功能的模塊。當然,這四種模塊並不是所有應用程序所必需的,而是根據需要靈活取捨,比如雖然 login 可能要求訪問所有四種模塊,但是 su 可能僅僅需要使用鑑別組件即可。至於如何取捨則涉及到接口層的 PAM API和配置文件,這部分內容將在下文中加以介紹。

 

四、第二層:應用接口層

 

應用接口層位於 PAM 結構的中間部分,它向上爲應用程序屏蔽了用戶鑑別等過程的具體細節,向下調用模塊層中的具體模塊所提供的特定服務。由圖1可以看出,它主要由 PAM API 和配置文件兩部分組成,下面將逐一介紹。

 

PAM API 可以分爲兩類,一類是用於調用下層特定模塊的接口,這類接口與底層的模塊相對應:

 

1. 鑑別類接口:pam_authenticate()用於鑑別用戶,pam_setcred()用於修改用戶的祕密信息。

 

2. 帳號類接口:pam_acct_mgmt()檢查受鑑別的用戶所持帳戶是否有權登陸系統,以及該帳戶是否已過期等。

 

3. 會話類接口:包括用於會話管理和記帳的 pam_open_session()和 pam_close_session()函數。

 

4. 口令類接口:包括用於修改用戶口令的 pam_chauthtok()。

 

第二類接口通常並不與底層模塊一一對應,它們的作用是對底層模塊提供支持以及實現應用程序與模塊之間的通信等。具體如下:

 

1. 管理性接口

 

每組 PAM 事務從 pam_start()開始,結束於 pam_end()函數。接口 pam_get_item()和 pam_set_item()用來讀寫與 PAM 事務有關的狀態信息。同時,能夠用 pam_str()輸出 PAM 接口的出錯信息。

 

2. 應用程序與模塊間的通訊接口

 

在應用程序初始化期間,某些諸如用戶名之類的數據可以通過 pam_start()將其存放在PAM接口層中,以備將來底層模塊使用。另外,底層模塊還可以使用 pam_putenv()嚮應用程序傳遞特定的環境變量,然後應用程序利用 pam_getenv() 和 pam_getenvlist() 讀取這些變量。

 

3. 用戶與模塊間的通訊接口

 

pam_start()函數可以通過會話式的回調函數,讓底層模塊通過它們讀寫模塊相關的鑑別信息,比如以應用程序所規定的方式提示用戶輸入口令。

 

4. 模塊間通訊接口

 

儘管各模塊是獨立的,但是他們仍然能夠通過 pam_get_item()和 pam_set_item()接口共享某些與鑑別會話有關的公用信息,諸如用戶名、服務名、口令等。此外,這些API還可以用於在調用 pam_start()之後,讓應用程序修改狀態信息。

 

5. 讀寫模塊狀態信息的接口

 

接口 pam_get_data()和 pam_set_data()用以按照PAM句柄要求訪問和更新特定模塊的信息。此外,還可以在這些模塊後附加一個清除數據函數,以便當調用 pam_end()時清除現場。

 

由於 PAM 模塊隨需加載,所以各模塊始化任務在第一次調用時完成。如果某些模塊的清除任務必須在鑑別會話結束時完成,則它們應該使用 pam_set_data()規定清除函數,這些執行清除任務的函數將在應用程序調用 pam_end()接口時被調用。

 

五、配置文件

 

我們注意到,配置文件也放在了在應用接口層中,它與 PAM API 配合使用,從而達到了在應用中靈活插入所需鑑別模塊的目的。它的作用主要是爲應用選定具體的鑑別模塊,模塊間的組合以及規定模塊的行爲。下面是一個示例配置文件:

\

 

圖2.示例配置文件

 

我們可以看到,配置文件有許多登記項(每行對應一個登記項)組成,每一行又分爲五列(每列對應一欄),詳細解釋如下:

 

第一欄,service表示使用PAM的應用程序,比如login、passwd、rlogin等。這一欄中的OTHER表示所有沒在該文件中顯式列出的應用。也就是說,如果所有程序具有相同的需求,整個配置文件只需要一行即可,並且該行的第一欄爲OTHER。本例中,因爲所有應用程序使用相同的會話模塊,所以實際上可以用單行,即

 

" OTHER auth required pam_unix_auth.so"

 

來代替文件中的這些行:

 

"login session required pam_unix_session.so

 

ftp session required pam_unix_session.so

 

telnet session required pam_unix_session.so"。

 

第二欄,module_type指明程序所用PAM底層模塊的類型:auth表示鑑別類模塊;account表示帳戶類模塊;session表示會話類模塊;password表示口令類模塊。注意,每行只能指定一種類型模塊,如果程序需要多種模塊的話,可在多行中分別規定。

 

第三欄,control_flag規定如何處理模塊的成功和失敗情況。單個應用程序可以調用多種底層模塊,這通常稱爲"堆疊",對應於某程序的按照配置文件中出現順序執行的所有模塊成爲"堆",堆中的各模塊的地位與出錯時的處理由control_flag欄的取值決定,它的五種可能的取值分別爲 required、Requisite、sufficient或_optional,現介紹如下:

 

required--它表示該模塊的成功是用戶通過鑑別的必要條件,換句話說,只有當對應於應用程序的所有帶required標記的模塊全部成功後,該程序才能通過鑑別。同時,如果任何帶required標記的模塊出現了錯誤,PAM並不立刻將錯誤消息返回給應用程序,而是在所有模塊都調用完畢後纔將錯誤消息返回調用它的程序。

 

Requisite--它與required相仿,只有帶此標記的模塊返回成功後,用戶才能通過鑑別,不同之處在於其一旦失敗就不再執行堆中後面的其它模塊,並且鑑別過程到此結束。

 

optional--它表示即便該模塊失敗,用戶仍能通過鑑別。在PAM體系中,帶有該標記的模塊失敗後將繼續處理下一模塊。

 

sufficient--它表示該模塊取得成功是用戶通過鑑別的充分條件,也就是說只要標記爲sufficient的模塊一旦成功,那麼PAM便立即嚮應用程序返回成功而不必嘗試任何其他模塊。當標記爲sufficient的模塊失敗時,sufficient模塊當做optional對待。

 

第四欄,module_path指出PAM模塊的位置。

 

第五欄,options用於向特定模塊傳遞相關的選項,然後由模塊分析解釋這些任選項。比如使用此欄打開模塊調試,或向某模塊傳遞諸如超時值之類的參數等。另外,它還用於支持下文所述的口令映射技術。

 

如果任一欄出現錯誤,或某模塊沒有找到,那麼所在行被忽略並將其作爲嚴重錯誤進行記錄。

 

本例中,login程序使用UNIX口令模塊進行鑑別,而ftp程序卻使用S/Key模塊進行鑑別。如我們想改變ftp程序的鑑別方法,比如也用UNIX口令模塊進行鑑別,那麼我們不必改動源程序,只需將配置文件中的

 

ftp auth required pam_skey_auth.so debug

 

改爲

 

ftp auth required pam_unix_auth.so debug

 

這樣,當用戶使用ftp時,將用傳統的UNIX口令鑑別方式來驗證其身份。由此可見,在PAM體制下爲應用程序改變鑑別機制是一件輕鬆的事情。另外,PAM體制的堆疊功能還使得應用程序能夠支持多種鑑別機制,如下例中的login程序在配置文件中先後出現了三項與鑑別有關的登記項:

 

\

 

圖3.示例配置文件

 

當login程序執行時先用pam_unix.so模塊即傳統的UNIX口令方式鑑別用戶,然後再調用pam_kerb.so模塊即 Kerberos對用戶進行鑑別,最後用pam_rsa.so模塊即RSA方式鑑別用戶。在按上述順序鑑別用戶的過程中,如果pam_unix.so模塊鑑別失敗,它將繼續調用下面的模塊進行鑑別而非立刻向login程序返回錯誤消息;pam_kerb.so模塊也按同樣方式處理,直到順序處理完最後一個 pam_rsa.so模塊後,PAM纔將前面出現的錯誤信息返回給login程序。對於該配置,即使pam_rsa.so模塊順利通過,只要 pam_unix.so模塊和pam_kerb.so模塊中有一個出現錯誤,用戶就不能通過鑑別;相反,即使pam_rsa.so模塊失敗,只要 pam_unix.so模塊和pam_kerb.so模塊都通過了,用戶也能通過鑑別。

 

六、口令映射

 

在同一個機器上使用多個鑑別機制,尤其是一個應用程序集成多種鑑別機制可能導致用戶需要記憶多個口令,這會讓用戶覺得很不舒服。雖然可以讓所有機制使用相同的口令來獲取易用性,但是這將削弱系統的安全性--如果其中任何一個機制的口令泄露了,則所有機制都會受到牽累。此外,不同的鑑別機制在口令長度、容許的字符、更新間隔、有效期等方面可能具有他們特有的要求,這些要求也是爲多鑑別機制使用同一個口令必須考慮的一個問題。

 

PAM爲我們提供了這樣一種解決方案,它不排除爲所有鑑別機制共用一個口令,同時允許通過口令映射技術讓每個機制使用不同的口令。該方案用用戶的" 主口令"加密其他的"副口令",並且將這些經過加密的副口令存放在一個用戶能訪問的地方。主口令一旦經過驗證,鑑別模塊就能用它解密那些加密的副口令從而獲得相應口令,然後將所需口令傳遞給鑑別模塊。這稱爲"口令映射"。如果口令映射出現錯誤,或如果映射不存在,那麼各鑑別模塊應該提示用戶輸入口令。爲支持口令映射,主口令應保存在PAM第二層並且在需要時由其提供給堆疊的各個鑑別模塊。同時,口令要在pam_authenticate函數返回之前清除。爲了保障口令映射的安全,主口令必須足夠強壯,可以考慮使其的長度更長、組成口令字符的類型多樣化並使用混合類型的字符組成口令等有效措施。

 

口令如何加密及其存儲完全取決於具體的實現:它能夠將加密的副口令(也稱作"映射口令")存儲在可靠或不可靠的地方,諸如智能卡、本地文件或目錄服務。當然,如果加密的口令保存在一個不可靠的允許公共訪問的地方,會留下受到字典***的隱患。

 

爲實現口令映射,所有鑑別模塊應支持以下四個映射選項∶

 

1. use_first_pass∶它表示當該模塊執行時不提示用戶輸入口令,而將該模塊之前的提示用戶輸入的主口令作爲它們的公共口令進行驗證。如果用戶沒能通過主口令的鑑別,則該模塊不提示用戶輸入口令。此選項一般說來在系統管理員想強制用同一個口令通過多模塊時使用。

 

2. try_first_pass∶除了如果主口令不正確,將提示用戶輸入口令之外,它的用法與use_first_pass相同。

 

3. use_mapped_pass∶它表示使用口令映射技術得到此模塊的有效口令。也就是說,該模塊執行時不提示用戶輸入口令,而是用映射口令即用主口令解密得到的該模塊的副口令作爲本模塊的口令輸入進行驗證。如果在此之前用戶沒能通過主口令的鑑別,則該模塊也不會提示輸入口令。

 

4. try_mapped_pass∶除了如果主口令不正確,它將提示用戶輸入口令之外,該項與use_mapped_pass用法相同。

 

當口令更換後,PAM會保存所有新舊口令,並且使有關模塊能夠訪問到它們。其他模塊能夠使用此信息更新加密的口令而不必強制用戶再次輸入口令。

 

現以下面的配置文件爲例講解口令映射:

 

\

 

圖4. 示例配置文件

 

在這裏login程序集成了三種鑑別方式:傳統UNIX口令鑑別、Kerberos和RSA鑑別,但通常情況下用戶僅輸入一次口令便能通過鑑別了。當程序調用pam_unix.so模塊時,PAM提示用戶輸入他們的UNIX口令,然後由pam_kerb.so模塊對用戶輸入的UNIX口令進行鑑別。繼而調用pam_kerb.so模塊,由於該模塊的選項爲use_mapped_pass,它將利用口令映射機制進行認證,也就是說,如果UNIX口令鑑別通過的話,就將其作爲pam_kerb.so模塊的主口令來解密其對應的映射口令從而進行Kerberos鑑別。如果pam_unix.so模塊所需口令沒能通過驗證,則無法進行口令映射,那麼PAM將直接調用下一鑑別模塊而不提示用戶輸入其Kerberos口令。最後一個模塊的選項爲 use_first_pass,所以pam_rsa.so模塊將使用前邊輸入的主口令來鑑別用戶,如果口令錯誤也不提示用戶輸入RSA口令。所以,只要第一次輸入的口令是正確的,並且映射口令存在,則一個口令便足以通過鑑別。

 

七、結束語

 

Linux-PAM是一種使用靈活功能強大的用戶鑑別機制,本文對它的組成結構以及各部分之間的關係進行了相應的分析,希望對讀者理解PAM的機制有所幫助。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章