[轉載請註明出處] http://kezeodsnx.pixnet.net/blog

作者: kezeodsnx

講的比唱的好聽

pam.overview.1.gif

 

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:

Using PAM

PAM (Pluggable Authentication Module)--Overview

pam_unix--traditional password authentication

 

kezeodsnx 發表在 痞客邦 PIXNET 留言(0) 人氣()