[轉載請註明出處] http://kezeodsnx.pixnet.net/blog
作者: kezeodsnx
講的比唱的好聽
PAM這個機制一開始是SUN提出並實作,目前各大Linux都有技援。整個framwork是由四個部分組成:
。PAM consumers
。PAM library
。pam.conf
。PAM service modules (PAM providers)
PAM提供了一致性的流程來做認證,因此開發者不需要了解認證的sematics。只需把module插入,要修改的只是pam.conf (現在是/etc/pam.d/),原本的程式不需改動。PAM的架構就如上圖,上層的應用程式透過PAM API (application programming interfac)跟PAM library溝通,PAM library透過PAM SPI (service provider interface)來與最底層的PAM service modules溝通。這些PAM service modules就是實際做認證的部分。而pam.conf則設定溝通的細節。因此,整個PAM就是一層層層往下問,每層都定義了interface,再一層層把結果往上送。
在這樣的機制下,更安全的認證可以無痛的升級,因其模組化並具有一致的API。使用者仍然是transparent,但實際上系統安全性已經提升了。
舉個例子,使用者在ftp敲入username/password後,ftp會呼叫pam_xxxx,此時libpam則根據/etc/pam.d/下的設定檔,找到其所設定的service module (通常在/lib/security),下後呼叫對應的pam_sm_xxxx。因此,有下列的對應:
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 service module與上層的應用程式還有其他的溝通方式,可以取得其他相關資訊,略述如下:
pam_get_item,pam_set_item:
。pam_get_item(pamh, PAM_RHOST, (const **)&remote_ip): 可用來取得其IP
。pam_set_item(pamh, PAM_USER, (const void *) DEFAULT_USER): 重設user
pam_getenv,pam_putenv: 存取環境變數
pam_get_data,pam_set_data: 這兩個是PAM自己用的,與應用程式無關
code
以下列出PAM service module的片段程式碼:
pam_sm_authenticate: 以下的目的是為了取的uid,然後做一些處理,只貼出取得uid的部分
PAM_EXTERN
int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc, const char **argv)
{
int retval;
const char *user=NULL;
/*
* authentication requires we know who the user wants to be
*/
//取得user name
retval = pam_get_user(pamh, &user, NULL);
if (retval != PAM_SUCCESS)
{
return retval;
}
if (user == NULL || *user == '\0')
{
//若有任何問題,則指定預設的username
pam_set_item(pamh, PAM_USER, (const void *) DEFAULT_USER);
return PAM_AUTH_ERR;
}
//取得該user的passwd structure
struct passwd * pwd=getpwnam(user);
uid_t uid;
if (pwd !=NULL)
uid=pwd->pw_uid;
return PAM_SUCCESS;
}
PAM_EXTERN
int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc, const char **argv)
{
int retval,ret;
const char *user=NULL;
char * password=NULL;
/*
* authentication requires we know who the user wants to be
*/
retval = pam_get_user(pamh, &user, NULL);
if (retval != PAM_SUCCESS)
{
D(("get user returned error: %s", pam_strerror(pamh,retval)));
return retval;
}
struct passwd * pwd=getpwnam(user);
uid_t uid=0;
gid_t gid=0;
/*retrieve uid and gid from passwd file*/
if (pwd !=NULL)
{
uid=pwd->pw_uid;
gid=pwd->pw_gid;
}
//也可取得password
if ((ret =read_password(pamh, "password:",&password)) != PAM_SUCCESS)
{
log_msg("FTP","DEBUG","can't read password\n");
return ret;
}
return PAM_SUCCESS;
}
不是每個SPI都需要實作:
PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc ,const char **argv)
{
return PAM_SUCCESS;
}
Reference:
PAM (Pluggable Authentication Module)--Overview
pam_unix--traditional password authentication
留言列表