会话函数也叫转换函数,是服务(如sshd,vsftpd)中的函数。Pam模块可以取到这个函数并且使用这个函数。
会话函数的作用就是处理与用户之间的会话。就是说,可以向用户、服务或设备显示消息,并从中收集输入内容。会话可采用多种形式,例如,文本终端设备中常见的”Login:”提示
此部分主要包括会话函数的注册、实现、获得、使用。其中注册、实现在服务(sshd服务)中完成。获得、使用在pam模块中完成。
1.1 会话函数的注册
应用服务程序(如sshd)调用 pam_start函数发起PAM会话时,将对会话函数进行注册。函数原型:
int pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh); 参数const struct pam_conv *pam_conversation即为要注册的会话函数。
1.2 会话函数的实现
下面是sshd服务源码中的会话函数:
static int sshpam_thread_conv(int n, sshpam_const struct pam_message **msg, struct pam_response **resp, void *data) { Buffer buffer; struct pam_ctxt *ctxt; struct pam_response *reply; int i; debug3("PAM: %s entering, %d messages", __func__, n); *resp = NULL; if (data == NULL) { error("PAM: conversation function passed a null context"); return (PAM_CONV_ERR); } ctxt = data; if (n <= 0 || n > PAM_MAX_NUM_MSG) return (PAM_CONV_ERR); if ((reply = malloc(n * sizeof(*reply))) == NULL) return (PAM_CONV_ERR); memset(reply, 0, n * sizeof(*reply)); buffer_init(&buffer); for (i = 0; i < n; ++i) { //会话函数的核心就是下面的代码 switch (PAM_MSG_MEMBER(msg, i, msg_style)) { case PAM_PROMPT_ECHO_OFF: //用于取得密码,不回显 buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg)); //终端显示提示,如 Password: if (ssh_msg_send(ctxt->pam_csock, PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) goto fail; //取得密码 if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) goto fail; if (buffer_get_char(&buffer) != PAM_AUTHTOK) goto fail; //密码存在reply[i].resp中 reply[i].resp = buffer_get_string(&buffer, NULL); break; case PAM_PROMPT_ECHO_ON: //用于取得用户名,回显 buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg)); //终端显示提示,如 Login: if (ssh_msg_send(ctxt->pam_csock, PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) goto fail; //取得用户名 if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) goto fail; if (buffer_get_char(&buffer) != PAM_AUTHTOK) goto fail; //用户名存在reply[i].resp中 reply[i].resp = buffer_get_string(&buffer, NULL); break; case PAM_ERROR_MSG: //错误信息显示 buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg)); if (ssh_msg_send(ctxt->pam_csock, PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) goto fail; break; case PAM_TEXT_INFO: //信息显示 buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg)); if (ssh_msg_send(ctxt->pam_csock, PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) goto fail; break; default: goto fail; } buffer_clear(&buffer); } buffer_free(&buffer); *resp = reply; return (PAM_SUCCESS); fail: for(i = 0; i < n; i++) { if (reply[i].resp != NULL) xfree(reply[i].resp); } xfree(reply); buffer_free(&buffer); return (PAM_CONV_ERR); }
1.3 会话函数的获得
Pam模块与终端通话,即显示信息,取得用户名、密码,都需要使用会话函数才能完成。
在pam模块中,通过下面方法取得会话函数(如sshd服务的会话函数)
pam_get_item(pamh, PAM_CONV, (const void **) &conversation); conversation是包含会话函数的结构体
1.4 会话函数的使用
上面取得会话函数后,就可以通过会话函数取得密码、用户名,以及显示信息了。
r 获取用户名:
message.msg = " Please input authen ID: "; int retval; message.msg_style = PAM_PROMPT_ECHO_ON; //设置回显 retval = conversation->conv(1, &pmessage, &res, conversation->appdata_ptr);//会话函数 if (retval != PAM_SUCCESS) LOGOUT("pam_conv error %d (%s).", retval, pam_strerror(pamh, retval)); strcpy(user, res->resp); //保存用户名
r 获取密码:
message.msg = "Enter fixed password: "; int retval; message.msg_style = PAM_PROMPT_ECHO_OFF; //设置不回显 retval = conversation->conv(1, &pmessage, &res, conversation->appdata_ptr);//会话函数 if (retval != PAM_SUCCESS) LOGOUT("error %d (%s).", retval, pam_strerror(pamh, retval)); strcpy(passwd, res->resp);
pam配置好后,通过telnet登录系统,上面程序的显示结果为:
Please input authen ID:
Enter fixed password: