PAM(Pluggable Authentication Modules )是由Sun提出的一種認證機制。它通過提供一些
員可以靈活地根據需要給不同的服務配置不同的認證方式而無需更改服務程式,同時也便於
向系 統中添加新的認證手段。
PAM最初是集成在Solaris中,目前已移植到其它系統中,如Linux、SunOS、HP-UX 9.0等。
一、PAM的結構
PAM的整個框架結構如下圖所示:
系統管理員通過PAM配置檔案來制定認證策略,即指定什麼服務該採用什麼樣的認證方法;應
用程式開發者通過在服務程式中使用PAM API而實現對認證方法的調用;而PAM服務模組(se
rvice module)的開發者則利用PAM SPI(Service Module API)來編寫認證模組(主 要是
引出一些函式pam_sm_xxxx( )供libpam調用),將不同的認證機制(比如傳統的UNIX認證方
法、Kerberos等)加入到系統中;PAM核 心庫(libpam)則讀取配置檔案,以此為根據將服
務程式和相應的認證方法聯繫起來。
二、PAM支持的四種管理界面:
1、認證管理(authentication management)
主要是接受用戶名和密碼,進而對該用戶的密碼進行認證,並負責設定用戶的一些秘密
信息。
2、帳戶管理(account management)
主要是檢查帳戶是否被允許登錄系統,帳號是否已經過期,帳號的登錄是否有時間段的
限制等等。
3、密碼管理(password management)
主要是用來修改用戶的密碼。
4、
會話管理(session management)
主要是提供對會話的管理和記賬(accounting)。
三、PAM的檔案:
/etc/pam.conf或者/etc/pam.d/ PAM配置檔案
/usr/lib/security/pam_*.so 可動態載入的PAM service module
對於RedHat,其目錄不是/usr/lib,而是/lib。
四、PAM的配置:
PAM的配置是通過單個配置檔案/etc/pam.conf。RedHat還支持另外一種配置方式,即通過配
置目錄/etc/pam.d/,且這種的優先權要高於單 個配置檔案的方式。
1、使用配置檔案/etc/pam.conf
該檔案是由如下的行所組成的:
service-name module-type control-flag module-path arguments
service-name 服務的
名字,比如telnet、login、ftp等,服務名字“OTHER”代表所有沒有
在該檔案中明確配置的其它服務。
module-type 模組類型有四種:auth、account、session、password,即對應PAM所支持的
四種管理方式。同一個服務可以調用多個 PAM模組進行認證,這些模組構成一個stack。
control-flag 用來告訴PAM庫該如何處理與該服務相關的PAM模組的成功或失敗情況。它有四
種可能的 值:required,requisite,sufficient,optional。
required 表示本模組必須返回成功才能通過認證,但是如果該模組返回失敗的話,失敗
結果也不會立即通知用戶,而是要等到同一stack 中的所有模組全部執行完畢再將失敗結果
requisite 與required類似,該模組必須返回成功才能通過認證,但是一旦該模組返回
失敗,將不再執行同一stack內的任何模組,而是直 接將控制權返回給
應用程式。是一個必
要條件。註:這種只有RedHat支持,Solaris不支持。
sufficient 表明本模組返回成功已經足以通過
身份認證的要求,不必再執行同一stack
內的其它模組,但是如果本模組返回失敗的話可以 忽略。可以認為是一個充分條件。
optional表明本模組是可選的,它的成功與否一般不會對
身份認證起關鍵作用,其返回
值一般被忽略。
對於control-flag,從Linux-PAM-0.63版本起,支持一種新的語法,具體可參看Linux
PAM文檔。
module-path 用來指明本模組對應的
程式檔案的路徑名,一般採用
絕對路徑,如果沒有給出
絕對路徑,默認該檔案在目錄/usr/lib/security下 面。
arguments 是用來傳遞給該模組的參數。一般來說每個模組的參數都不相同,可以由該模組
的開發者自己定義,但是也有以下幾個共同 的參數:
debug 該模組應當用syslog( )將調試信息寫入到系統日誌檔案中。
no_warn 表明該模組不應把警告信息傳送給
應用程式。
use_first_pass 表明該模組不能提示用戶輸入密碼,而應使用前一個模組從用戶那裡
得到的密碼。
try_first_pass 表明該模組首先應當使用前一個模組從用戶那裡得到的密碼,如果該
密碼驗證不通過,再提示用戶輸入新的密碼。
use_mapped_pass 該模組不能提示用戶輸入密碼,而是使用映射過的密碼。
expose_account 允許該模組顯示用戶的帳號名等信息,一般只能在安全的環境下使用
,因為泄漏用戶名會對安全造成一定程度的威 脅。
2、使用配置目錄/etc/pam.d/(只適用於RedHat Linux)
該目錄下的每個檔案的
名字對應服務名,例如ftp服務對應檔案/etc/pam.d/ftp。如果名為x
xxx的服務所對應的配置檔案/etc/pam.d/xxxx不存 在,則該服務將使用默認的配置檔案/et
c/pam.d/other。每個檔案由如下格式的文本行所構成:
module-type control-flag module-path arguments
每個欄位的含義和/etc/pam.conf中的相同。
3、配置的例子
例一:用/etc/pam.conf配置默認的認證方式。
下面的例子將拒絕所有沒有在/etc/pam.conf中明確配置的服務。OTHER代表沒有明確配置的
其它所有服務,pam_deny模組的作用只是簡 單地拒絕通過認證。
OTHER auth required /usr/lib/security/pam_
OTHER account required /usr/lib/security/pam_deny
OTHER password required /usr/lib/security/pam_
OTHER session required /usr/lib/security/pam_deny
例二:通過/etc/pam.d/rsh檔案配置rsh服務的認證方式。
rsh服務認證用戶時,先使用/etc/hosts.equiv和.rhosts檔案的認證方式,然後再根據/etc
/nologin檔案的存在與否來判斷是否允許該用戶使用 rsh,最後使用password database來認
證用戶。
auth required /lib/security/pam_rhosts_auth
auth required /lib/security/pam_nologin.so
account required /lib/security/pam_pwdb
session required /lib/security/pam_
例三:通過/etc/pam.conf配置ftpd的認證方式。
下面是ftpd服務利用PAM模組進行
用戶認證的三個步驟。首先用pam_ftp模組檢查當前用戶是
否為匿名用戶,如果是匿名用戶,則 sufficient控制標誌表明無需再進行後面的認證步驟,
直接通過認證;否則繼續使用pam_unix_auth模組來進行標準的unix認證,即用/etc/ passw
d和/etc/shadow進行認證;通過了pam_unix_auth模組的認證之後,還要繼續用pam_listfil
e模組來檢查該用戶是否出現在檔案/etc/ ftpusers中,如果是則該用戶被deny掉。
ftpd auth sufficient /usr/lib/security/pam_ftp
ftpd auth required /usr/lib/security/pam_unix_<cite class="highlight" highlight="true" style="background-image: none; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: initial; background-position: initial initial; background-repeat: initial initial; ">auth</cite> use_first_pass
ftpd auth required /usr/lib/security/pam_listfile
onerr=succeed item=user sense=deny file=/etc/ftpuser
s
五、密碼映射(password-mapping)
密碼映射允許用戶在不同的認證機制下使用不同的密碼,其中有一個主密碼(primary pass
word),其它密碼為次密碼(secondary passwords,可能有多個)。主密碼用來對次密碼進
行加密。在主密碼認證通過後,認證模組利用主密碼將加密過的次密碼(也稱為 mapped pa
ssword)解密,並對次密碼進行認證。
註:如果使用了
一次性密碼的機制,就不使用密碼映射。
所有服務模組必須支持如下4個映射選項(在第四部分已經簡單解釋過):
1、use_first_pass
這個選項指示本模組不能提示用戶輸入密碼,而是使用已有的密碼,即從第一個向用戶提示
輸入密碼的模組那裡取得密碼,並對該密碼進 行認證。
2、try_first_pass
這個選項指示本模組首先嘗試使用已有的密碼,即從第一個向用戶提示輸入密碼的模組那裡
取得密碼,並對該密碼進行認證。如果密碼認 證失敗,則再提示用戶輸入密碼。
3、use_mapped_pass
這個選項指示本模組不能向用戶提示輸入密碼,而應使用映射過的密碼,即利用主密碼將加
密過的次密碼解密出來並進行認證。
4、try_mapped_pass
這個選項指示本模組首先嘗試使用映射過的密碼,即利用主密碼將加密過的次密碼解密出來
並進行認證。如果密碼認證失敗,則再提示用 戶輸入密碼。
密碼映射的例子:
下面是/etc/pam.conf中關於login服務的配置。這裡login共有3種認證機制:Kerberos、UN
IX和RSA認證,兩個required控制標誌表明用戶必 須通過
Kerberos認證和UNIX認證才能使用
login服務,optional選項則說明RSA認證是可選的。首先用戶輸入主密碼進行Kerberos認 證
;use_mapped_pass選項指示UNIX認證模組利用主密碼將用於UNIX認證的次密碼解密出來並對
該次密碼進行認證;try_first_pass選項 指示RSA認證模組先使用第一個模組(即Kerberos
模組)的密碼作為進行認證的密碼,當對該密碼認證失敗時才提示用戶輸入用於RSA認 證的
次密碼。
login auth required pam_kerb_<cite class="highlight" highlight="true" style="background-image: none; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: initial; background-position: initial initial; background-repeat: initial initial; ">auth</cite> debug
login auth required pam_unix_<cite class="highlight" highlight="true"</cite> use_mapped_pass
login auth optional pam_rsa_auth try_first_pass
六、PAM API
1、框架API:
任何一個支持PAM的服務程式在進行認證時必須以pam_start( )開始進行初始化,最後以pam
_end( )結束以便進行清理工作。
2、認證管理API:
pam_authenticate( )對用戶名/密碼進行認證。
pam_setcred( )用來修改用戶的秘密信息。
3、帳戶管理API:
pam_acct_mgmt( )檢查帳戶本身是否有許可權登錄系統、帳戶是否過期、帳戶是否有登錄時間
限制等。
4、密碼管理API:
pam_chauthtok( )修改用戶的密碼。
5、會話管理API:
一個會話以pam_open_session( )開始,最後以pam_close_session( )結束。
6、其它:
pam_get_item( )、pam_set_item( )用來讀寫PAM事務(transaction)的狀態信息。
pam_get_data( )、pam_set_data( )用來取得和設定PAM模組及會話的相關信息。
pam_putenv( )、pam_getenv( )、pam_getenvlist( )用來讀寫
環境變數。
pam_strerror( )返回相關的
錯誤信息。
例子程式(摘自Sun的白皮書):
下面的例子使用PAM API寫了一個簡單的login服務程式(註:這不是個完整的程式,所以省
略了對pam_close_session的調用)。
#include <security/pam_appl.h>
static int login_conv(int num_msg, struct pam_message **msg, struct pam_response
**response, void *appdata_ptr);
struct pam_conv pam_conv = {login_conv, NULL};
pam_handle_t *pamh; /* 進行認證的PAM句柄 */
void main(int argc, char *argv[], char **renvp)
{
if ((pam_start("login", user_name, &pam_conv, &pamh)) != PAM_SUCCESS)
login_exit(1);
/* 設定一些參數 */
pam_set_item(pamh, PAM_TTY, ttyn);
pam_set_item(pamh, PAM_RHOST, remote_host);
while (!authenticated && retry < MAX_RETRIES)
{
status = pam_authenticate(pamh, 0); /* 密碼認證管理,檢查用戶輸入
的密碼是否正確 */
authenticated = (status == PAM_SUCCESS);
}
if (status != PAM_SUCCESS)
{
fprintf(stderr,"error: %s\n", pam_strerror(pamh, status)); /* 顯示錯誤原
因 */
login_exit(1);
}
/* 通過了密碼認證之後再調用帳戶管理API,檢查用戶帳號是否已經過期 */
if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS)
{
if (status == PAM_AUTHTOK_EXPIRED)
{
status = pam_chauthtok(pamh, 0); /* 過期則要求用戶更改密碼 */
if (status != PAM_SUCCESS)
login_exit(1);
}
}
/* 通過帳戶管理檢查之後則打開會話 */
if (status = pam_open_session(pamh, 0) != PAM_SUCCESS)
login_exit(status);
/* 設定用戶組 */
setgid(pwd->pw_gid);
/*
* Initialize the supplementary group access list before
* pam_setcred because PAM modules might add groups
* during the pam_setcred call
*/
initgroups(user_name, pwd->pw_gid);
status = pam_setcred(pamh, PAM_ESTABLISH_CRED);
if (status != PAM_SUCCESS)
login_exit(status);
/* 設定真實的用戶ID(或者有效的用戶ID)*/
setuid(pwd->pw_uid);
pam_end(pamh, PAM_SUCCESS); /* PAM事務的結束 */
/*
此處可用來實現與login有關的其它內容
*/
}
/* 出錯則清理現場並退出 */
static void login_exit(int exit_code)
{
if (pamh)
pam_end(pamh, PAM_ABORT);
exit(exit_code);
}
/* 這個
回調函式被PAM認證模組調用以便顯示
錯誤信息或者或者用來取得用戶輸入,採用圖
形界面的服務程式則應使用圖形界面來取得 用戶輸入或顯示提示信息*/
int login_conv(int num_msg, struct pam_message **msg, struct pam_response **resp
onse, void *appdata_ptr)
{
while (num_msg--)
{
switch (m->msg_style)
{
case PAM_PROMPT_ECHO_OFF:
r->resp = strdup(getpass(m->msg));
break;
case PAM_PROMPT_ECHO_ON:
(void) fputs(m->msg, stdout);
r->resp = malloc(PAM_MAX_RESP_SIZE);
fgets(r->resp, PAM_MAX_RESP_SIZE, stdin);
/* add code here to remove \n from fputs */
break;
case PAM_ERROR_MSG:
(void) fputs(m->msg, stderr);
break;
case PAM_TEXT_INFO:
(void) fputs(m->msg, stdout);
break;
default:
log_error();
break;
}
}
return (PAM_SUCCESS);
}
七、PAM SPI
當服務程式(ftpd、telnetd等)調用PAM API函式pam_xxx( )時,由PAM
框架(libpam)根
據該服務在/etc/pam.conf檔案中的配置調用指 定的PAM模組中對應的SPI函式pam_sm_xxx(
)。如下:
API函式的
名字為pam_xxx( ),對應的SPI函式的名字為pam_sm_xxx( ),即每個服務模組需要
引出相應的函式以供libpam調用。為方便對 照,再列一下。
API 對應的 SPI
帳號管理 pam_acct_mgmt( ) pam_sm_acct_mgmt( )
認證管理 pam_authenticate( ) pam_ sm_authenticate( )
密碼管理 pam_chauthtok( ) pam_ sm_chauthtok( )
會話管理 pam_open_session( ) pam_ sm_open_session( )
會話管理 pam_close_session( ) pam_ sm_close_session( )
認證管理 pam_setcred( ) pam_ sm_setcred( )
八、常用的PAM服務模組
下面是Linux提供的PAM模組列表(只是其中一部分):
模組檔案 模組功能描述 相關配置檔案
pam_access 提供logdaemon風格的登錄控制 /etc/security/access.conf
pam_chroot 提供類似chroot命令的功能
pam_cracklib 對密碼的強度進行一定的檢查 庫檔案libcrack和字典檔案
/usr/lib/cracklib_dict
pam_deny 總是無條件地使認證失敗
pam_env 設定或取消
環境變數 /etc/security/pam_env.conf
pam_filter 對輸入輸出流進行過濾 filters
pam_ftp 對匿名ftp用戶進行認證
pam_group 當用戶在指定的終端上請求指定的 /etc/security/group.conf
服務時賦予該用戶相應的組許可權
pam_issue 在提示用戶輸入用戶名之前顯示 /etc/issue
/etc/issue檔案的內容
pam_krb4 對用戶密碼進行Kerberos認證 相應的Kerberos庫檔案
pam_lastlog 在用戶登錄成功後顯示關於 /var/log/lastlog
用戶上次登錄的信息,並維護
/var/log/lastlog檔案。
pam_limits 限制用戶會話所能使用的
系統資源 /etc/security/limits.conf
pam_listfile 根據指定的某個檔案決定是否 例如/etc/ftpusers
允許或禁止提供服務
pam_mail 檢查用戶的信箱中是否有新郵件 /var/spool/mail/xxxx
pam_mkhomedir 為用戶建立主目錄 /etc/skel/
pam_motd 顯示/etc/motd檔案的內容 /etc/motd
pam_nologin 根據/etc/nologin檔案的存在與否 /etc/nologin
來決定用戶認證是否成功
pam_permit 總是無條件地使認證成功
pam_pwdb 作為pam_unix_xxxx模組的一個替代。/etc/pwdb.conf
使用Password Database通用接口
進行認證。
pam_radius 提供遠程身份驗證撥入用戶服務
(RADIUS)的認證
pam_rhosts_auth 利用檔案~/.rhosts和 /etc/hosts.equiv和~/.rhosts
/etc/hosts.equiv對用戶進行認證。
pam_rootok 檢查用戶是否為
超級用戶,如果
是超級用戶則無條件地通過認證。
pam_securetty 提供標準的Unix securetty檢查 /etc/securetty
pam_time 提供基於時間的控制,比如限制 /etc/security/time.conf
用戶只能在某個時間段內才能登錄
pam_unix 提供標準的Unix認證 /etc/passwd和 /etc/shadow
pam_userdb 利用Berkeley DB資料庫來檢查 Berkeley DB
用戶/密碼
pam_warn 利用syslog( )記錄一條告警信息
pam_wheel 只允許wheel組的用戶有超級用戶
的存取許可權。