通过sasl的pam认证机制实现对svnserve的自定义用户认证

      上一篇博文中讲解了svnserve使用sasl协议认证的配置方式,并通过sasl的pam认证机制使用linux自带的用户认证信息实现对svnserve的用户认证。在企业中,通常都有自己的用户管理及认证系统,如AD(活动目录),也有自定义的基于 MySQL 的用户信息并通过HTTP接口的方式对外暴露认证接口,这样做可以进行统一用户管理和认证,在企业中,员工只需要维护一份用户信息记录,即可操作所有需要认证的系统。

      本文以 svnserve 为例,编写自定义的 pam 认证模块实现对 svn 用户的认证鉴权,先决条件如下:

    1、博主已有一个自定义的用户管理系统,并通过 HTTP 接口对外开放鉴权能力;
    2、服务器上已安装 saslauthd 服务,且 svnserve 已配置为通过 sasl 的 pam 认证方式;

    一、修改 /etc/pam.d/svn 文件,内容如下:

    auth    required /lib64/security/pam_moodwu.so https://passport.xxx.com/login/
    account required /lib64/security/pam_moodwu.so https://passport.xxx.com/login/

    上文中,我们使用 pam_moodwu.so 这个自定义的模块实现对 svn 用户的认证和帐号管理,而且传递了一个参数,表示认证的 HTTP 接口地址(用户可根据实际情况修改为自己的用户认证接口地址),该参数会被传递到模块内部,稍后,我们需要开发 pam_moodwu.so 这个模块。

    二、开发 pam 认证模块。

    首先在编写的服务模块的源程序里要包含下列头文件:

    #include <security/pam_modules.h>

    PAM 的服务模块是一个一个的动态链接库(扩展名为.so),PAM 接口库通过 dlopen 来装载这些库,假定我们的源程序文件叫 pam_moodwu.c, 则需要通过以下方式来编译生成动态链接库,Makefile 内容如下:

    INC_FILES=
    
    SRC_FILES= \
        source/pam_moodwu.c
    
    all: ${INC_FILES} ${SRC_FILES}
    	gcc -fPIC -c ${SRC_FILES}
    	ld -x --shared -lcurl -o pam_moodwu.so pam_moodwu.o
    clean:
    	rm -f pam_moodwu.so *.o

    在开发过程中,我们需要用到 libcurl 库来访问 HTTP 服务,所以需要使用 -lcurl 来链接,否则在实际运行时会加载不成功。

    接着,我们需要实现以下 6 个接口函数,在适当的时候接口服务会调用我们的实现,并通过函数的返回值来确定认证的状态。

    #define PAM_SM_AUTH
    #define PAM_SM_ACCOUNT
    #define PAM_SM_SESSION
    #define PAM_SM_PASSWORD
    
    #include <security/pam_modules.h>
    #include <security/pam_ext.h>
    #include <stdio.h>
    #include <curl/curl.h>
    #include <stdlib.h>
    #include <string.h>
    #include <security/pam_appl.h>
    
    /* --- 用户认证函数,用户可根据自已的情况自定义该函数的实现,验证成功时返回 1, 否则返回 0 --- */
    int VerifyUser(const char *lpszUrl, const char *lpszUser, const char *lpszPassword)
    {
        return 1;
    }
    
    /* --- 认证管理函数的实现--- */
    PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
    {
        int result = PAM_AUTH_ERR;
        
        do
        {
            // 配置的认证服务器地址在参数中
            if (argc != 1)
            {
                break;
            }
            const char *url = argv[0];
            
            // 取出用户名和密码
            const char *user = NULL;
            const char *password = NULL;        
            pam_get_user(pamh, &user, "user");
            pam_get_authtok(pamh, PAM_AUTHTOK, &password, "password");
            if (NULL == user || NULL == password)
            {
                break;
            }
            
            if (!VerifyUser(url, user, password))
            {
                break;
            }
    
            result = PAM_SUCCESS;
        } while (0);
        
        return result;
    }
    
    PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
    {
        return PAM_SUCCESS;
    }
    
    /* --- 账号管理函数的实现 --- */
    PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
    {
        return PAM_SUCCESS;
    }
    
    /* --- 口令管理函数的实现 --- */
    PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
    {
        return PAM_SUCCESS;
    }
    
    /* --- 会话管理函数的实现 --- */
    PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
    {
        return PAM_SUCCESS;
    }
    
    PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
    {
        return PAM_SUCCESS;
    }
    
    /* 模块定义结束 */
    /* 静态模块数据 */
    #ifdef PAM_STATIC
    struct pam_module _pam_deny_modstruct = {
       "pam_moodwu",
        pam_sm_authenticate,
        pam_sm_setcred,
        pam_sm_acct_mgmt,
        pam_sm_open_session,
        pam_sm_close_session,
        pam_sm_chauthtok
    };
    #endif

    用户可以根据自己的情况实现以上 VerifiUser 函数,可以直接查询数据库,也可以通过 HTTP 接口进行认证,需要相关的服务器连接信息可以通过 pam 参数传递进来。

    代码编写完成后,执行 make 即可生成 pam_moodwu.so

    三、验证自定义 PAM 认证模块的功能。

    将步骤二编译出来的 pam_moodwu.so 文件拷贝到 /lib64/security/ 目录下,并重启 saslauthd 服务,再通过 svn 进行访问验证。

    数据库用户认证的内容如下:

    mysql> show tables;
    +-----------------------+
    | Tables_in_db_passport |
    +-----------------------+
    | tb_Email              |
    | tb_LocalAuth          |
    | tb_Mobile             |
    | tb_User               |
    +-----------------------+
    4 rows in set (0.00 sec)
    
    mysql> SELECT * FROM tb_LocalAuth WHERE UserName='Lory';
    +----------+--------+----------------------------------+----------------------------------+
    | UserName | UserID | Salt                             | Password                         |
    +----------+--------+----------------------------------+----------------------------------+
    | Lory     | 900001 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy |
    +----------+--------+----------------------------------+----------------------------------+
    1 row in set (0.00 sec)

    作者使用了加盐的算法保存用户的密码信息,密码比较安全,且管理员也无法破解原始密码。

    另外,数据库不对外公布,所以通过部署 nginx+php-fpm, 利用 php 语言编写了一个简单的用户认证接口,返回时为 json 格式,此处略。

    最后,通过 svn 客户端进行验证,略。


    本实验在 CentOS 6.5 x86_64 环境下验证通过。

作者:Lory | 时间:2017-10-15 12:53:36 | 分类:编程经验 | 浏览:64 | 评论:0