建立時間:2000-06-08
文章屬性:整理
文章來源:原文BY - P H R A C K M A G A Z I N E -56-13.html
文章提交:xundi (xundi_at_xfocus.org)
介紹PAM
BY XUNDI 2000-05-08
本人水平有限,錯誤在所難免,盡請指教xundi@xfocus.org
原文BY - P H R A C K M A G A Z I N E -56-13.html
----| 介紹
此文主要介紹了PAM模組的知識,最後我還加上了一個PAM的簡單使用例子,希望有用。
PAM的英文全稱是Pluggable Authentication Module系統,即此程式是有關執行
使用者鑒別和帳號維護的服務。鑒別部分通常通過一(合法性)質詢-回應的互動
來完成的。使用PAM,管理員可以通過不重編輯評鑑程式來定製一些使用方法。
PAM有四部分組成,第一部分是libpam,是實現PAM API的庫,第二部分是PAM配置
檔案,/etc/pam.conf,第三部分有一套動態可裝載兩進位對象組成,常常用來調
用一些處理實際鑒別(authentication)工作的服務模組。最後模組是使用PAM API
的系統命令組成,如login,us,ftp,telnet,etc...
----| LIBPAM
PAM API的認證(authentication)常規程式有三個主要函數組成:
pam_start( const char *service_name, const char *username,
const struct pam_conv *conv, pam_handle_t **pamh_p );
pam_end( pam_handle_t *pamh, int exit_status );
pam_authenticate( pam_handle_t *pamh, int flags );
pam_start()和pam_end()函數是開始和結束一個PAM會話,傳遞給pam_start()
函數的參數如下所示:
+ service_name: 一定義在pam.conf中的特殊服務(請看下面)
+ username: 需要鑒權的使用者登入名稱。
+ conv: 一指向pam_conf結構的指標
+ pamh_p: 一雙精確度指向pam_handle_t結構的指標。PAM構架會分配或不
分配記憶體給這個結構,並且一應用程式不能直接存取它。它基本上是用來通過PAM
構架(framework)來處理多個並發的PAM會話。
pam_conv結構如下所示:
struct pam_conv {
int (*conv)(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr);
void *appdata_ptr;
}
*conv是指向PAM對話函數的指標,它將會在下面詳細討論,appdata_ptr指標指
向特殊應用程式資料,它並不常用。
pam_end()函數的參數由在pam_start()函數中填充的pam_handle_t*組成,並且返回
的是exit狀態。exit狀態正常情況下是PAM_SUCCESS。pam_edn()會收回與
pam_handle_t*相關聯的記憶體,並且任何企圖重用這個控制代碼將返回一個seg fault
錯誤。
pam_authenticate()函數也再一次由通過pam_start()填充的pam_handle_t*組成,
並且可選的標誌(flags)可以傳遞給結構(framework)
另外一些可以應用於應用程式的PAM API函數如下所示(可以參看你的系統文檔):
+ pam_set_item() - 為PAM會話寫狀態資訊
+ pam_get_item() - 為PAM獲得狀態資訊
+ pam_acct_mgmt() - 檢查目前使用者帳號是否合法
+ pam_open_session() - 開始一個新的會話
+ pam_close_session() - 關閉當前會話進程
+ pam_setcred() -系統管理使用者信任資格(credentials)
+ pam_chauthtok() - 改變使用者的鑒權(authentication)token牌
+ pam_strerror() - 返回錯誤字串,類似與perror()函數
----| PAM.CONF
PAM設定檔通常位於/etc/pam.conf,它可以分為四個部分:鑒權(authentication),
帳號管理,會話管理和密碼管理。一個標準的一行配置如下所示:
login auth required /usr/lib/security/pam_unix.so.1 try_first_pass
第一欄是服務名,這是參照在pam_start()函數中的第一個參數。如果通過pam_start()
的服務要求不列在pam.conf,則將使用預設的"other"服務。"other"服務名字可以
是"su"和"rlogin",如果服務名不止一次說明,模組將會提示"stacked",並且
framework將會通過第三欄的值來決定。
第二欄指示這特定的服務將執行何種類型的行為,合法的值是:
"auth"為鑒權行為;
"account"為帳號管理;
"session"為會話管理;
"password"為密碼管理;
不是所有的應用程式需要使用每一個行為,如,su僅僅需要使用"auth"鑒權行為,
"passwd"只需要來使用"password"管理行為。
第三欄作為一個控制類型的一欄。如果使用者在評鑑行為中失敗,它指示PAM 架構
(framework)行為。此欄正確的值為:"requisite","required", "sufficient", "optional":
+ "requisite"意思指如果使用者在評鑑(quthentication)時失敗,PAM framework
會立即返回一失敗資訊,其中沒有其他模組調用。
+ "required"指示如果一個使用者評鑑(quthentication)時失敗,PAM framework
只在調用其他所有模組後在返回失敗資訊。這樣做的話使用者會不知道哪個模組
鑒權被拒絕,如果一個使用者成功被鑒別,所有"required"模組必須返回成功。
+ "optional"意思是使用者將被允許訪問即使鑒權失敗,失敗的結果是下一個在
堆棧中的模組將被處理。
+ "sufficient"指的是如果使用者傳遞一這特定模組,PAM framework會立即返回
成功,即使隨後的模組有"requisite"或者"required"控制值,類似於"optional"
"sufficient"回允許訪問即使鑒全步驟失敗。
Note that if any module returns success, the user will succeed authentication
with the only exception being if the user previously failed to authenticate
with a "required" module.
在pam.conf第四欄中是認證模組的路徑,各個系統路徑不同,如在Linux-PAM系統
中PAM模組在/usr/lib目錄下,而Solaris在/usr/lib/security中維護模組。
第五欄是一個空格分開的module-dependent選項列表,是傳遞給認證模組調用。
---| 模組 MODULES
每一個PAM模組本質上是一個必須輸出特定函數的庫,這些函數可以被PAM framework
調用,通過庫輸出的函數有如下列表:
+ pam_sm_authenticate()
+ pam_sm_setcred()
+ pam_sm_acct_mgmt()
+ pam_sm_open_session()
+ pam_sm_close_session()
+ pam_sm_chauthtok()
如果實現者不決定在一模組內支援特定的操作,模組會為此操作返回PAM_SUCCESS,
例如:如果一個模組設計成為不支援帳號管理(account management),
pam_sm_acct_mgmt()函數會簡單的返回PAM_SUCCESS。
pam_sm_authenticate()是按下面的方式聲明的:
extern int pam_sm_authenticate( pam_handle_t *pamh, int flags,
int argc, char **argv);
上面的指標是指想一個PAM控制代碼--已經通過framework填充了,flags是應用程式
調用pam_authenticate()傳遞給framework的一組標誌,argc和argv是在pam.conf
中此服務的選項參數的數字和值。
一個簡單的pam_unix 模組中的pam_sm_authenticate()函數應該如下所示:
#include <security/pam_modules.h>
#include <...>
extern int
pam_sm_authenticate( pam_handle_t *pamh, int flgs, int c, char **v )
{
char *user;
char *passwd;
struct passwd *pwd;
int ret;
/* ignore flags and optional arguments */
if ( (ret = pam_get_user( ..., &user )) != PAM_SUCCESS )
return ret;
if ( (ret = pam_get_pass( ..., &passwd )) != PAM_SUCCESS )
return ret;
if ( (pwd = getpwnam(user)) != NULL ) {
if ( !strcmp(pwd->pw_passwd, crypt(passwd)) )
return PAM_SUCCESS;
else
return PAM_AUTH_ERR;
}
return PAM_AUTH_ERR;
}
當然,這個函數非常單純化,但它示範了pam_sm_authenticate()函數的準系統。
它從Framework中獲得使用者的LOGIN名字和密碼,在獲得使用者加密的密碼,最後調用
crypt()函數並把結果和加密了的系統密碼進行比較。pam_get_*()函數調用Framework,
----|應用程式 APPLICATION
一個應用程式處理PAM部分必須由pam_start()和pam_end()組成和一PAM對話函數。
比較幸運的是,user-space PAM API定義的比較成熟和穩定所以對話函數是比較
模板型的代碼(至少對命令列應用程式)。一個簡單的su PAM實現所下所示:
#include <security/pam_appl.h>
#include <...>
int su_conv(int, const struct pam_message **,
struct pam_response **, void *);
static struct pam_conv pam_conv = { su_conv, NULL };
int
main( int argc, char **argv )
{
pam_handle_t *pamh;
int ret;
struct passwd *pwd;
/* assume arguments are correct and argv[1] is the username */
ret = pam_start("su", argv[1], &pam_conv, &pamh);
if ( ret == PAM_SUCCESS )
ret = pam_authenticate(pamh, 0);
if ( ret == PAM_SUCCESS )
ret = pam_acct_mgmt(pamh, 0);
if ( ret == PAM_SUCCESS ) {
if ( (pwd = getpwnam(argv[1])) != NULL )
setuid(pwd->pw_uid);
else {
pam_end(pamh, PAM_AUTH_ERR);
exit(1);
}
}
pam_end(pamh, PAM_SUCCESS);
/* return 0 on success, !0 on failure */
return ( ret == PAM_SUCCESS ? 0 : 1 );
}
int
su_conv(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata)
{
struct pam_message *m = *msg;
struct pam_message *r = *resp;
while ( num_msg-- )
{
switch(m->msg_style) {
case PAM_PROMPT_ECHO_ON:
fprintf(stdout, "%s", m->msg);
r->resp = (char *)malloc(PAM_MAX_RESP_SIZE);
fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin);
m++; r++;
break;
case PAM_PROMPT_ECHO_OFF:
r->resp = getpass(m->msg);
m++; r++;
break;
case PAM_ERROR_MSG:
fprintf(stderr, "%s\n", m->msg);
m++; r++;
break;
case PAM_TEXT_MSG:
fprintf(stdout, "%s\n", m->msg);
m++; r++;
break;
default:
break;
}
}
return PAM_SUCCESS;
}
su_conv()函數就是對話函數 - 它允許模組與使用者"對話"。每一個pam_message
結構是一個資訊類型,其指明模組所需資料的類型。PAM_PROMPT_ECHO_ON和
PAM_PROMPT_ECHO_OFF提示模組需要使用者的資訊(如密碼),prompt通過模組來提供。
在PAM_PROMPT_ECHO_OFF情況中,模組通常需要一個密碼,並且它會在應用程式上
不回顯字元。*_MSG是用來在使用者終端上顯示資訊的。
PAM對話的精彩之處是所有基於字元的輸出可以在不改變認證模組的情況下
可以用不同的顯示系統下調用函數來代替。例如:如果我們想採用基於圖形的
su命令我們可以使用get_gui_passwd()來代替getpass().
注意一個真正的對話函數應該有更複雜。同樣,Linux-PAM的實現提供misc_conv()
對話函數來進行命令列的互動,這個函數是一個標準對話函數所必須的。
最後,是應用程式的free()函數來釋放記憶體。
----| 一些模組有意思的地方
現在你應該對PAM有點熟悉了,我們可以簡明的討論下定製認證的程式了,如:
我們可以方便的修改我們前面的模組以便當認證ROOT使用者時,必須打第二個密碼:
extern int
pam_sm_authenticate( pam_handle_t *pamh, int flgs, int c, char **v )
{
char *user;
char *passwd;
struct passwd *pwd;
int ret;
/* ignore flags and optional arguments */
if ( (ret = pam_get_user( ..., &user )) != PAM_SUCCESS )
return ret;
if ( (ret = pam_get_pass( ..., &passwd )) != PAM_SUCCESS )
return ret;
if ( (pwd = getpwnam(user)) != NULL ) {
if ( !strcmp(pwd->pw_passwd, crypt(passwd)) )
ret = PAM_SUCCESS;
else
ret = PAM_AUTH_ERR;
}
if ( !strcmp(user, "root") ) {
pam_display_message("root user must enter secondary password");
if ( (ret = pam_get_pass( ..., &passwd )) != PAM_SUCCESS )
return ret;
if ( !strcmp(get_second_root_pwd(), crypt(passwd)) )
ret = PAM_SUCCESS;
else
ret = PAM_AUTH_ERR;
}
return ret;
}
這裡我們假定這裡get_second_root_pwd()函數返回一些加密的密碼。當然,這個
例子有點可笑,但它示範了我們可以自由的設計我們想要的PAM模組。
----|結論
重點是PAM模組不光光是調用crypt()函數或者處理使用者密碼的一些類似函數,限制
你的只是你的思想。
----|參考資料
"Making Login Services Independent of Authentication Technologies".
Samar, Vipin and Charlie Lai.
http://www.sun.com/software/solaris/pam/pam.external.pdf
"The Linux-PAM System Administrator's Guide". Morgan, Andrew G.
http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/pam.html
"The Linux-PAM Module Writers' Guide". Morgan, Andrew G.
http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/pam_modules.html
"The Linux-PAM Application Developers' Guide". Morgan, Andrew G.
http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/pam_appl.html
Linux-PAM source code from FreeBSD 3.3 source packages.
http://www.FreeBSD.org/availability.html
一個簡單的PAM配置:
BY-小許
pam可以對登陸數,進程數,記憶體使用量數等進行限制.先要確定
/etc/pam.d/login裡有以下兩行:
session required /lib/security/pam_pwdb.so
session required /lib/security/pam_limits.so
然後在/etc/security/limits.conf裡對使用者進行限制,如:
@hacker hard maxlogins 3
hacker hard nproc 10
hacker hard memlock 2000
前面加@表示對這組使用者進行限制
|EOF|-------------------------------------------------------------------------|
原創地址:http://www.xfocus.net/articles/200006/45.html