歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux資訊 >> 更多Linux

PAM 的應用開發和內部實現源碼分析

  1 引言  身份認證是操作系統安全的重要機制之一,系統通過認證機制核查用戶的身份證明,並作為用戶進入系統的判定條件,是防止惡意用戶進入系統的第一道門檻。近年來認證理論和技術得到了迅速發展,產生了各種認證機制,如口令機制,RSA, DCE, kerberos認證體制,S/Key和基於智能卡的身份認證等。然而,當系統中引入新的認證機制時,一些系統入口登錄服務如login, rlogin和telnet等應用程序就必須改寫以適應新的認證機制。為了解決這個問題,1995年Sun公司的Vipin Samar和 Charlie Lai提出了PAM(Pluggable Authentication Modules),並將其應用在Solaris系統上。PAM框架將應用程序與具體的認證機制分離,使得系統改變認證機制時,不再需要修改采用認證機制的應用程序,而只要由管理員配置應用程序的認證服務模塊,極大地提高了認證機制的通用性與靈活性。    現在大多數操作系統都采用PAM實現身份認證,有Linux系統的Linux-PAM和FreeBSD5.x采用的OpenPAM(FreeBSD 4.x采用的是Linux-PAM)等。它們的實現原理一樣,只有實現細節不同而已。下面從PAM的應用開發開始介紹。    2 PAM的應用開發  2.1 PAM框架概覽  PAM即可插拔認證模塊。它提供了對所有服務進行認證的中央機制,適用於login,遠程登錄(telnet,rlogin,fsh,FTP,點對點協議(PPP)),su等應用程序中。系統管理員通過PAM配置文件來制定不同應用程序的不同認證策略;應用程序開發者通過在服務程序中使用PAM API(pam_xxxx( ))來實現對認證方法的調用;而PAM服務模塊的開發者則利用PAM SPI來編寫模塊(主要是引出一些函數pam_sm_xxxx( )供PAM接口庫調用),將不同的認證機制加入到系統中;PAM接口庫(libpam)則讀取配置文件,將應用程序和相應的PAM服務模塊聯系起來。PAM框架結構如圖所示。       其中,pamh是一個pam_handle類型的結構,它是一個非常重要的處理句柄,是PAM與應用程序通信的唯一數據結構,也是調用PAM接口庫API的唯一句柄。pam_handle數據結構將在下面的源代碼分析一節的介紹。    另外,如上圖所示的服務模塊分auth(認證管理)、account(賬號管理)、session(會話管理)、passwd(口令管理)四種類型,各個類型模塊的作用以及配置文件的四個組成部分模塊類型、控制標志、模塊路徑、模塊參數等在很多講PAM的配置管理的文章裡都有介紹,這裡就不再贅述了。    2.2 在應用程序中使用PAM認證  每個使用PAM認證的應用程序都以pam_start開始,pam_end結束。PAM還提供了pam_get_item和pam_set_item共享有關認證會話的某些公共信息,例如用戶名,服務名,密碼和會話函數。應用程序在調用了pam_start ()後也能夠用這些APIs來改變狀態信息。實際做認證工作的API函數有六個(以下將這六個函數簡稱為認證API):    認證管理--包括pam_authenticate ()函數認證用戶,pam_setcred ()設置,刷新,或銷毀用戶證書。  賬號管理--包括pam_acc_mgmt ()檢查認證的用戶是否可以訪問他們的賬戶,該函數可以實現口令有效期,訪問時間限制等。  會話管理--包括pam_open_session ()和pam_close_session ()函數用來管理會話和記賬。例如,系統可以存儲會話的全部時間。  口令管理--包括pam_chauthok ()函數用來改變密碼。  下面看一個簡單的login模擬程序:    /* 使用PAM所必需的兩個頭文件*/  #include   #include     void main(int argc, char *argv[], char **renvp)  {  /* 初始化,並提供一個回調函數 */  if ((pam_start("login", user_name, &pam_conv, &pamh)) != PAM_SUCCESS)  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);/* 認證,檢查用戶輸入的密碼是否正確 */  }    /* 認證失敗則應用程序退出*/  if (status != PAM_SUCCESS)  {  ……  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)  exit(1);  }  }  /* 通過帳戶管理檢查之後則打開會話 */  if (status = pam_open_session(pamh, 0) != PAM_SUCCESS)  exit(status);  ……  /* 建立認證服務的用戶證書*/  status = pam_setcred(pamh, PAM_ESTABLISH_CRED);  if (status != PAM_SUCCESS)  exit(status);  ……  pam_end(pamh, PAM_SUCCESS); /* PAM事務的結束 */  ……  }    從上面程序中,我們可以了解到使用PAM認證的一般流程,同時也可以看出PAM API使得使用認證的應用程序不僅不用關心底層使用的服務模塊,而且編寫起來簡潔明了得多。    有關開發使用PAM的應用程序更加詳細完整的闡述請參考The Linux-PAM Application Developers' Guide。    2.3 怎樣開發PAM服務模塊  首先在編寫的服務模塊的源程序裡要包含下列頭文件:    #include     PAM的服務模塊是一個一個的動態鏈接庫文件(也可以是靜態庫),PAM接口庫通過dlopen來裝載這些庫。假設源程序名為pam_module-name.c,則需要用下列命令將其編譯成動態鏈接庫:    gcc -fPIC -c pam_module-name.c  ld -x --shared -o pam_module-name.so pam_module-name.o    選項-fPIC是指位置無關代碼(Position Independent Code),這類代碼支持大偏移。使用--shared選項將目標代碼放進共享目標庫中。    四種類型的模塊各自要實現的函數如下表所示:       當然同一個服務模塊可以同時屬於多種類型,只要這些類型模塊要實現的函數都實現了就可以,比如PAM自帶的經典口令認證機制模塊pam_unix.so 就可以支持四種模塊類型。    下面來看一個最簡單的pam_deny模塊的源程序pam_deny.c:    1.#define PAM_SM_AUTH  2.#define PAM_SM_ACCOUNT  3.#define PAM_SM_SESSION  4.#define PAM_SM_PASSWord  5.#include "../../libpam/include/security/pam_modules.h"  6./* --- 認證管理函數的實現--- */  7.PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc  8.,const char **argv)  9.{  10.return PAM_AUTH_ERR;  11.}  12.PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc  13.const char **argv)  14.{  15.return PAM_CRED_UNAVAIL;  16.}  17./* --- 賬號管理函數的實現 --- */  18.PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc  19.,const char **argv)  20.{  21.return PAM_ACCT_EXPIRED;  22.}  23./* --- 口令管理函數的實現 --- */  24.PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc  25.const char **argv)  26.{  27.return PAM_AUTHTOK_ERR;  28.}  29./* --- 會話管理函數的實現 --- */  30.PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh,int flags,int argc  31.const char **argv)  32.{  33.return PAM_SYSTEM_ERR;  34.}  35.PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc  36.,const char **argv)  37.{  38.return PAM_SYSTEM_ERR;  39.}  40./* 模塊定義結束 */  41./* 靜態模塊數據 */  42.#ifdef PAM_STATIC  43.struct pam_module _pam_deny_modstruct = {  44."pam_deny",  45.pam_sm_authenticate,  46.pam_sm_setcred,  47.pam_sm_acct_mgmt,  48.pam_sm_open_session,  49.pam_sm_close_session,  50.pam_sm_chauthtok  51.};  52.#endif    很容易看出,pam_deny模塊支持四種模塊類型。前4行包含靜態模塊的一些原型申明,第37-39行實現了四種模塊類型的函數,因為這只是一個簡單的拒絕服務的模塊,所以這些函數只是簡單地返回認證錯或系統錯等PAM錯誤。最後幾行定義了該程序被編譯成靜態模塊所需的一個模塊數據結構。    因此,PAM SPI使得服務模塊的開發也相當簡單和專一,因為服務模塊不再需要考慮和應用程序的交互,只要將自己采用的算法實現好就可以了。    模塊源程序可用的flags參數值和返回值的定義這裡不作全面介紹,有興趣者請參考The Linux-PAM Module Writers' Guide。    3 PAM接口庫源代碼分析  上面我們介紹了怎樣使用PAM和怎樣開發PAM服務模塊,要想對PAM的內部機制有個透徹的理解,還需要進一步分析PAM接口庫的代碼。下面基於Linux 的pam-0.75-40.src.rpm包所得的源




Copyright © Linux教程網 All Rights Reserved