PAM编程

pam编程:
        1 头文件:
        #include         //提供 misc_conv支持, 编译的时候,要加 -lpam_misc
        #include         //用于 客户端程序

        #include
        #include         // 这两个是编写 pam_modules时用到的.

        2. 概念:
        pam: PAM(Pluggable Authentication Modules),可插拔认证模块.
        优点:可以任意配置认证方式.比如,用户程序使用的是linux自己的认证方式,对应的模块是pam_unix.so.当想使用apm_ldap.so时,只要在该应用程序使用的配置文件中,使用的模块改为pam_ldap.so就行了.配置文件那块,有很复杂的配置方法.可以实现,多种认证互相结合的方式.
        只是用户是否合法性的认证,无用户对应权限的分发.比如,用户a,是不是合法用户可以知道,但用户a是否可以访问某个目录,则无法知道.

        3.流程
        
        应用程序 --> 调用pam_**()函数(e.g. pam_authenticate)--> 配置文件(/etc/pam.d/下) ->配置文件中对应pam_**.so -> 调用模块中pam_sm_***()函数
        使用pam的应用程序,会调用pam_authenticate等等的函数,然后,pam机制会找到对应的配置文件,从这些配置文件中,找到所调用的模块.然后,pam机制会调用对应模块的pam_sm_authenticate()函数.实现用户与模块间的数据交互.
        其中,关于应用程序与模块的数据交流,是通过pam_conv()来实现,它是个回调函数,该函数由用户端来定义并实现.
        例如:应用程序的用户与密码信息,会在应用程序所定义的pam_conv函数中所给出,当模块需要用户名与密码信息时,会主动调用该回调函数,通过该回调函数,取得用户与密码信息.然后,完成对应的用户与密码的验证工作.
        

        4. 函数:


        1. int pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation,pam_handle_t **pamh);
        pam_handle_t:是保存所有pam信息的地方.由pam_start函数创建.这些信息,可以通过pam_set_item与pam_get_item来设置与获取.
        service_name指的是/etc/pam.d/下的配置文件名称;user可以为空;pam_conv指的是用于模块与应用程序沟通的接口.
        
        2. int pam_authenticate(pam_handle_t *pamh, int flags);
        用户程序通过该函数进行用户合法性的认证.
        
        3. int pam_end(pam_handle_t *pamh, int pam_status);
        用户程序,通过该函数,释放pam_handle_t中的相关数据及信息.
        
        4. int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv);
        这是模块部分的程序,指的是对应/lib/security/pam_*.so动态库的输出函数.
        
        5. int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item);
           int pam_set_item(pam_handle_t *pamh, int item_type, const void **item);
         通过该函数,可以得到的值有:
         PAM_SERVICE:服务名
         PAM_USER:用户名,该用户名,指的是在pam_start的第二个参数所设置的.也可以通过pam_set_item来设置
         PAM_USER_PROMPT: 当提示输入用户名时的指示字符串.默认为login:
         PAM_TTY:  用户程序对应的tty
         PAM_RHOST: 该用户程序使用的 远程主机信息.
         PAM_CONV:   该用户程序对应的pam_conv()函数.



1. pam_conv:函数

        一般,linux中,有个默认的conv函数:misc_conv

        其中,pam_response是由conv函数来申请空间的.

        *resp指向的是pam_response数组,而不是一个数组指针(pam_response *).他的内存由调用者来进行释放.通过free()函数.

        *msg指向的却是一个pam_message *的数组.

        若调用失败,则应该由conv函数自己来释放resp所指向的内存.

        PAM_PROMPT_ECHO_ON: 输出回显, PAM_PROMPT_ECHO_OFF:输出不回显,所以,前者用于用户名,后者用于密码的获得

int myconv(int num_msg, const pam_message **msg, pam_response **resp, void *appdata_ptr) {

        pam_response *tresp = NULL;

        tresp = (pam_response *)calloc(num_msg, sizeof(pam_response));
        if (tresp == NULL) {
                return PAM_CONV_ERR;
        }

        char* user = NULL;
        
        const pam_message *tmsg;
        for (int i = 0; i < num_msg; ++i) {
                tmsg                  = msg[i];
                switch (tmsg->msg_style) {
                case PAM_PROMPT_ECHO_ON:
                        fprintf(stdout, "\n%s\n", tmsg->msg);
                        tresp[i].resp = (char *)malloc(100);
                        fgets(tresp[i].resp, 99, stdin);
                        break;
                case PAM_PROMPT_ECHO_OFF:
                        user          = getpass(tmsg->msg);
                        tresp[i].resp = (char *)malloc(100);
                        strcpy(tresp[i].resp, user);
                        break;
                case PAM_ERROR_MSG:
                case PAM_TEXT_INFO:
                default:
                        free(tresp[i].resp);
                        free(tresp);
                        break;

                }
        }

        *resp = tresp;

        return PAM_SUCCESS;
}


2. main函数:

        调用规则:pam_start()开始,pam_end()结束 .其中,pam_start函数,第二个参数,可以为空,则可以通过,pam_conv来交互数据.

       const char* sName = "";
        const char* user  = "";
        const char* pwd   = "";
        if (argc < 3) {
                printf("argc < 3 \n");
                return 0;
        }

        sName = argv[1];
        user  = argv[2];
        pwd   = argv[3];

        pam_conv* pConv = NULL;
        pam_handle_t* pHandle = NULL;

        do {

                int ret = pam_start( sName, user, &stn, &pHandle);
                if (ret != PAM_SUCCESS) {

                        myerr(pHandle, ret, "pam_start");
                        break;

                }

                ret = pam_authenticate(pHandle, 0);
                if (ret != PAM_SUCCESS) {
                        myerr(pHandle, ret, "pam_auth");
                        break;
                }

                ret = pam_acct_mgmt(pHandle, 0);
                if (ret != PAM_SUCCESS) {
                        myerr(pHandle, ret, "pam_acct_mgmt");
                        break;
                }

                printf("success \n");

        }while (false);

        pam_end(pHandle, 0);

3. PAM模块函数:

        该模块是pam_authenticate被调用时,模块对应的函数.

        如这种情况.pam_authenticate函数被调用,然后,找到对应的/etc/;pam.d/下的配置文件,该模块中 auth 块中,第三列后的参数,就是指 argc与argv.

        该模块被编译后,成*.so文件,放在/lib/security的目录下(默认路径).

int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
        pam_conv* pamconv = NULL;

        int ret = pam_get_item(pamh, PAM_CONV, (const void **)&pamconv);

        if (ret != PAM_SUCCESS) {
                return PAM_AUTH_ERR;
        }

        pam_message* msg[2] = {NULL};

        msg[0] = new pam_message;
        msg[1] = new pam_message;

        msg[0]->msg       = "hello , please input your name:";
        msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
        msg[1]->msg       = "please input passwd:";
        msg[1]->msg_style = PAM_PROMPT_ECHO_OFF;

        pam_response *resp = NULL;

        ret = pamconv->conv(2, (const pam_message **)&msg, &resp, pamconv->appdata_ptr);

        if (ret != PAM_SUCCESS) {
                fputs("ret is error\n", stderr);
                return PAM_MAXTRIES;
        }

        char buf[100];
        sprintf(buf, "user:(%s), pwd:(%s) \n", resp[0].resp, resp[1].resp);
        fputs(buf, stdout);

        free(msg[0]);
        free(msg[1]);
        free(resp[0].resp);
        free(resp[1].resp);
        free(resp);

        return PAM_SUCCESS;
}


4. 配置文件;/etc/pam.d/***

auth        required      pam_**.so
account     required      pam_**.so
password    required      pam_**.so
session     required      pam_**.so




你可能感兴趣的:(linux编程)