有了前面的知识,今天将和大家一起写一个简单的PAM模块
好了废话不说了直接写吧(文件为 pam_test.c)
#define PAM_SM_AUTH
#include <security/pam_modules.h>
#include <security/pam_appl.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#define MODULE_NAME "pam_sample"
#define SAMPLE_PROMPT "Extra Password for root:"
#define PAM_DEBUG_ARG 1
#define DPRINT if (ctrl & PAM_DEBUG_ARG) sample_syslog
#define PAM_RET_CHECK(ret) if(PAM_SUCCESS != ret) \
{ \
return ret; \
}
//if debug is setting this function can write log information /var/log/message
static void sample_syslog(int err, const char *format, ...)
{
va_list args;
char buffer[1024];
va_start(args, format);
vsprintf(buffer, format, args);
/* don't do openlog or closelog, but put our name in to be friendly */
syslog(err, "%s: %s", MODULE_NAME, buffer);
va_end(args);
}
//get the paramters
static int sample_parse(int argc, const char **argv, char *szconf)
{
int ctrl=0;
/*
* If either is not there, then we can't parse anything.
*/
if ((argc == 0) || (argv == NULL)) {
return ctrl;
}
/* step through arguments */
for (ctrl=0; argc-- > 0; ++argv)
{
/* generic options */
if (!strcmp(*argv, "debug"))
{
ctrl |= PAM_DEBUG_ARG;
}
else
{
sample_syslog(LOG_WARNING, "unrecognized option '%s'", *argv);
}
}
return ctrl;
}
//callback function to release a buffer
void sample_pam_free(pam_handle_t *pamh, void *pbuf, int error_status)
{
free(pbuf);
}
//conversation function
//这个是做了封装的对话函数,每次只允许你向应用程序请求一个"问题"
int sample_converse(pam_handle_t *pamh, int msg_style, char *message, char **password)
{
const struct pam_conv *conv;
struct pam_message resp_message;
const struct pam_message *msg[1];
struct pam_response *resp = NULL;
int retval;
resp_message.msg_style = msg_style;
resp_message.msg = message;
msg[0] = &resp_message;
//之前老提到对话函数,我们说过对话函数由应用程序提供,这里可以看到在模块中怎么获得对话函数
//通过pam_get_item 可以获得pam_conv这个结构的一个指针(第二个参数是PAM_CONV表示类型)
//然后就想下面的调用方式,你可以在你的模块中调用这个对话函数,和应用程序交互
retval = pam_get_item(pamh, PAM_CONV, (const void **)&conv);
PAM_RET_CHECK(retval)
retval = conv->conv(1, msg, &resp, conv->appdata_ptr);
PAM_RET_CHECK(retval)
if(password)
{
*password = resp->resp;
free(resp);
}
return PAM_SUCCESS;
}
#ifdef PAM_SM_AUTH
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
char *puser, *ppwd;
int nret;
int nloop;
int nChallenge;
int nValidrsp;
char szbuf[256];
char szconf[256];
char *resp2challenge = NULL;
int ctrl = 0;
memset(szconf, 0, 256);
//通过这个函数获得用户名
nret = pam_get_user(pamh, &puser, "UserName:");
if(PAM_SUCCESS != nret)
{
int *pret = (int *)malloc(sizeof(int));
//makelog("get username failed");
DPRINT(LOG_DEBUG, "Get user name failed");
*pret = nret;
pam_set_data(pamh, "sample_setcred_return", (void *)pret, sample_pam_free);
return PAM_SYSTEM_ERR;
}
else
{
//username
//如果用户名为root,请用户输入附加密码 "123456"
if(!strcasecmp("root", puser))
{
//如果为root 和应用程序交互,让用户输入密码
//如果为root 和应用程序交互,让用户输入密码
nret = sample_converse(pamh, PAM_PROMPT_ECHO_OFF, SAMPLE_PROMPT, &ppwd);
if(PAM_SUCCESS != nret)
{
int *pret = (int *)malloc(sizeof(int));
*pret = nret;
DPRINT(LOG_DEBUG, "Get extra password failed");
pam_set_data(pamh, "sample_setcred_return", (void *)pret, sample_pam_free);
return nret;
}
//you have get the extra password
if(strcasecmp("123456", ppwd))
{
int *pret = (int *)malloc(sizeof(int)); *pret = PAM_AUTH_ERR; DPRINT(LOG_DEBUG, "Invalid extra password"); pam_set_data(pamh, "sample_setcred_return", (void *)pret, sample_pam_free); return PAM_AUTH_ERR;
}
}
}
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
int nret = PAM_SUCCESS, *pret;
pret = &nret;
pam_get_data(pamh, "sample_setcred_return", (void **)&pret);
return *pret;
}
#endif//PAM_SM_AUTH
编译 gcc -o pam_test.so -shared -fPIC ./pam_test.c -lpam
将pam_test.so 拷贝到/lib/security/下(平台不同有差异)
配置 vim /etc/pam.d/login (如果想为login使用这个模块)
在你希望的位置添加 auth sufficient pam_test.so
sufficient 还可以为 required 等具体请参照http://www.ibm.com/developerworks/cn/linux/l-pam/index.html?ca=drs-#resources
至此一个简单的PAM模块就写好了