分类:
2010-01-20 10:52:14
通过示例程序学习如何使用可插拔验证模块(Pluggable Authentication Modules,PAM)提供的会话函数。
内容
在 本系列的第一篇文章中,我们介绍了使用口令进行用户验证的基础知识。我们对 身份验证和 授权这两个术语进行了定义,讨论了基于文件的本地密码存储和加密算法,并介绍了如何使用Solaris操作系统提供的API读取和加密口令。最后,我们展示了一个示例程序,它向调用者询问密码并与登录密码进行比较。
第二篇文章首先对可插拔验证模块( Pluggable Authentication Modules,PAM)进行了简单介绍,然后描述了PAM框架的各个组成部分。我们讨论了PAM服务模块、PAM 配置文件、 /etc/pam.conf
,并描述了如何将服务模块连接起来。然后,我们介绍了PAM API中的一些重要函数,最后对系列文章第一部分中编写的密码比较程序进行了重写,使它具有PAM感知功能。
在这篇文章中,我们将介绍会话函数并简要查看PAM API提供的其他一些函数。
顾名思义, 会话函数的作用就是处理与用户之间的会话。就是说,可以向用户、服务或设备显示消息,并从中收集输入内容。会话可采用多种形式,例如,文本终端设备中常见的”Login:”提示、目前最为流行的GUI登录管理器,甚至还有指纹读取器。
当使用PAM进行身份验证的应用程序(称为 PAM使用者)调用 pam_start
函数(上期文章介绍了该函数)发起PAM会话时,将对会话函数进行注册。会话函数由PAM服务模块调用,其原型如下所示:
int conv_func (int |
其中:
PAM_MAX_NUM_MSG
之间,并包括首末值)PAM负责分配和释放 msg使用的内存,而 resp使用的内存则由应用程序分配,并由服务模块释放。
我们编写的会话函数不应考虑PAM与客户进行通信的方式。相反,会话函数应与用户交换消息直至操作完成。同样,来自PAM 的消息在显示时不应被修改(服务模块负责对自身信息进行本地化)。对单独的消息没有格式限制,可以包含若干行、空白或控制符。
消息使用 pam_message
结构保存,其成员如下:
struct pam_message { |
msg
成员指实际的消息。 msg_style
表示消息的类型,并且它的值可以是下面四个值的其中一个:
PAM_PROMPT_ECHO_OFF
:提示用户,禁止回传响应。PAM_PROMPT_ECHO_ON
:提示用户,可以回传响应。PAM_ERROR_MSG
:输出错误消息。PAM_TEXT_INFO
:输出普通信息消息。同样,验证模块的响应保存在 pam_response
结构中,其成员如下所示:
struct pam_response { |
resp
成员包含实际的响应,而 resp_retcode
包含返回代码。后者并不经常使用,因此应将其设置为0。如果会话函数返回错误,必须将响应指针设置为NULL。
成员函数还有另外一项职责:必须去掉 PAM_PROMPT_ECHO_OFF
和 PAM_PROMPT_ECHO_ON
消息中的所有终止换行符,并在需要时向 PAM_ERROR_MSG
和 PAM_TEXT_INFO
消息添加新的换行符。
我们已经讨论了会话函数以及其作用,现在来查看一个示例程序。源文件中提供了两个函数。一个是帮助器函数 free_resp
,用于在发生错误时释放响应,第二个函数就是会话函数。
源文件中的前几行包含了我们需要的各种头文件:
1 #include |
下面是 free_resp
的源代码:
9 static void free_resp (int num_msg, struct pam_response *resp) |
让我们研究一下这个由16行组成的函数。
13-14:
如果 resp
指针为NULL,则不执行任何操作,然后返回。
16-22:
循环遍历每条消息。如果消息不为NULL,将其内存清零然后释放它。之所以在释放它之前清空内存,是因为它可能包含敏感数据,例如密码。
23:
最后,释放第一个响应。
下面展示了会话函数的源代码。这些代码对本系列前一篇文章中的函数进行了改进。
25 int check_conv (int num_msg, struct pam_message **msg, |
让我们进一步查看一下这个共46行组成的函数。
33-37:
验证提供的消息数是否有效(即是否在0和 PAM_MAX_NUM_MSG
之间)。
38-39:
为响应分配缓冲区(如果有的话)。
41-44:
对于传递的所有消息,如果消息指针为NULL,则标记一个错误。
45-46:
设置最后面的换行(如果有的话):如果文本为提示,则删除换行符。如果文本为消息,则在显示文本时添加换行。
47-48:
初始化响应结构。
49-63:
如果消息类型为 PAM_PROMPT_ECHO_OFF
,调用 getpassphrase
以显示保存在 m->msg
中的提示并从用户处收集密码,并且不需要回传。如果消息类型为 PAM_PROMPT_ECHO_ON
,则输出 m->msg
中保存的提示即可(注意,真正的会话函数在执行这些操作后不会读取用户响应,但是我们在示例中忽略了这一点)。如果消息类型为 PAM_ERROR_MSG
或 PAM_TEXT_INFO
,则输出保存在 m->msg
中的消息,后跟一个换行。前一种情况将把消息输出给 stderr
,而后一种情况将把消息输出给 stdout
。
65:
我们已经成功处理了所有的消息,因此返回success消息。
66-69:
出现一个错误,因此清除并返回failure消息。
要使输出与系列第1部分中的输出相同,需将第51行修改为下面的内容,然后再按照第2部分编译示例程序:
ct_passwd = getpassphrase ("Enter password "); |
我们已经了解了会话函数,现在来简单了解一下PAM API提供的其他一些函数:
pam_acct_mgmt
pam_open_session
pam_close_session
pam_setcred
pam_set_item
pam_get_item
pam_acct_mgmt
函数 pam_acct_mgmt
函数将执行帐户验证过程,其原型如下所示:
#include |
该函数将执行帐户有效性检查(例如,密码和帐户是否到期以及访问时间限制),它通常在完成身份验证(方法是调用 pam_authenticate
)之后执行调用。
pam_open_session
和 pam_close_session
函数 启动或终止会话的PAM使用者应该调用这两个函数之一。
#include |
使用 pam_authenticate
和 pam_acct_mgmt
对用户进行成功验证之后,程序创建新会话时应该会调用 pam_open_session
。这将通知会话模块打开新的会话。相反,关闭会话时应该调用 pam_close_session
,以通知会话模块关闭会话。
pam_setcred
函数 pam_setcred
函数可以修改验证服务的用户凭证。
#include |
当用户通过验证并打开了一个会话后,需要使用 pam_setcred
函数建立、修改或删除用户凭证。
pam_set_item
和 pam_get_item
函数 PAM使用者和服务模块可以使用 pam_set_item
和 pam_get_item
处理PAM信息。
#include |
应用程序和服务模块可以使用 pam_set_item
更新PAM信息。 item_type参数表示所更新信息的类型,并且可以表示多个类型。类型示例包括PAM服务名、用户名、tty名和用户验证标记。在 pam_set_item
手册页, pam_set_item(3PAM)
中可以找到完整的类型列表。
可以使用 pam_get_item
访问每种消息类型的值。
因空间有限,我们无法使用这些函数展示示例,但是感兴趣的读者可以通过在 中搜索相应的函数名了解它们的用法。
在这篇文章中,我们介绍了PAM会话函数的概念以及它们的作用(即会话函数的功能)。我们介绍了如何使用会话函数处理消息,并描述了各种不同的消息类型。
随后,我们展示了一个非常普通的示例会话函数,它对系列第2部分中的函数进行了改进。
最好,我们简单介绍了PAM API提供的其他一些函数: pam_acct_mgmt
、 pam_open_session
、 pam_close_session
、 pam_setcred
、 pam_set_item
和 pam_get_item
。
本系列下一篇文章(也是最后一篇)中,我们将研究如何编写PAM服务模块。