免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3174 | 回复: 0
打印 上一主题 下一主题

Solaris操作系统的用户认证:第3部分:PAM会话函数 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-20 10:52 |只看该作者 |倒序浏览

通过示例程序学习如何使用可插拔验证模块(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
num_msg, struct pam_message **
msg,
      struct pam_response **
resp, void
*app_data);
其中:

  • num_msg参数包含发送给函数的消息数量(必须在0和 PAM_MAX_NUM_MSG之间,并包括首末值)
  • msg指针指向保存显示给用户的消息的缓冲区(例如,密码提示)
  • resp指针指向保存来自用户的消息的缓冲区(例如输入的密码)
  • app_data指针指向的缓冲区包含特定于应用程序的数据

PAM负责分配和释放 msg使用的内存,而 resp使用的内存则由应用程序分配,并由服务模块释放。
我们编写的会话函数不应考虑PAM与客户进行通信的方式。相反,会话函数应与用户交换消息直至操作完成。同样,来自PAM 的消息在显示时不应被修改(服务模块负责对自身信息进行本地化)。对单独的消息没有格式限制,可以包含若干行、空白或控制符。
消息使用 pam_message结构保存,其成员如下:
struct pam_message {
      int msg_style;
      char *msg;
};
msg成员指实际的消息。 msg_style表示消息的类型,并且它的值可以是下面四个值的其中一个:

  • PAM_PROMPT_ECHO_OFF:提示用户,禁止回传响应。
  • PAM_PROMPT_ECHO_ON:提示用户,可以回传响应。
  • PAM_ERROR_MSG:输出错误消息。
  • PAM_TEXT_INFO:输出普通信息消息。

同样,验证模块的响应保存在 pam_response结构中,其成员如下所示:
struct pam_response {
     char *resp;
     int resp_retcode;
};
resp成员包含实际的响应,而 resp_retcode包含返回代码。后者并不经常使用,因此应将其设置为0。如果会话函数返回错误,必须将响应指针设置为NULL。
成员函数还有另外一项职责:必须去掉 PAM_PROMPT_ECHO_OFF和 PAM_PROMPT_ECHO_ON消息中的所有终止换行符,并在需要时向 PAM_ERROR_MSG和 PAM_TEXT_INFO消息添加新的换行符。
示例会话函数


我们已经讨论了会话函数以及其作用,现在来查看一个示例程序。源文件中提供了两个函数。一个是帮助器函数 free_resp,用于在发生错误时释放响应,第二个函数就是会话函数。
源文件中的前几行包含了我们需要的各种头文件:
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
下面是 free_resp的源代码:
9 static void free_resp (int num_msg, struct pam_response *resp)
10 {
11     int i;
12     struct pam_response *r;
13     if (resp == NULL)
14         return;
15     r = resp;
16     for (i = 0; i resp) {
18             bzero (r->resp, strlen (r->resp));
19             free (r->resp);
20             r->resp = NULL;
21         }
22     }
23     free (resp);
24 }
让我们研究一下这个由16行组成的函数。
13-14:如果 resp指针为NULL,则不执行任何操作,然后返回。
16-22:循环遍历每条消息。如果消息不为NULL,将其内存清零然后释放它。之所以在释放它之前清空内存,是因为它可能包含敏感数据,例如密码。
23:最后,释放第一个响应。
下面展示了会话函数的源代码。这些代码对本系列前一篇文章中的函数进行了改进。
25 int check_conv (int num_msg, struct pam_message **msg,
26     struct pam_response **resp, void *app_data)
27 {
28     int i;
29     struct pam_message *m;
30     struct pam_response *r;
31     char *ct_passwd;
32     m = *msg;
33     if ((num_msg = PAM_MAX_NUM_MSG)) {
34         fprintf (stderr, "Invalid number of messages\n");
35         *resp = NULL;
36         return (PAM_CONV_ERR);
37     }
38     if ((*resp = r = calloc (num_msg, sizeof (struct pam_response))) == NULL)
39         return (PAM_BUF_ERR);
40     for (i = 0; i msg == NULL) {
42             fprintf (stderr, "Message %d: %d/NULL\n", i, m->msg_style);
43             goto err;
44         }
45         if (m->msg[strlen (m->msg)] == '\n')
46             m->msg[strlen (m->msg)] = '\0';
47         r->resp = NULL;
48         r->resp_retcode = 0;
49         switch (m->msg_style) {
50             case PAM_PROMPT_ECHO_OFF:
51                 ct_passwd = getpassphrase (m->msg);
52                 r->resp = strdup (ct_passwd);
53                 break;
54             case PAM_PROMPT_ECHO_ON:
55                 printf ("%s", m->msg);
56                 break;
57             case PAM_ERROR_MSG:
58                 fprintf (stderr, "%s\n", m->msg);
59                 break;
60             case PAM_TEXT_INFO:
61                 printf ("%s\n", m->msg);
62                 break;
63         }
64     }
65     return (PAM_SUCCESS);
66 err:
67     free_resp (i, r);
68     *resp = NULL;
69     return (PAM_CONV_ERR);
70 }
让我们进一步查看一下这个共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 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
int pam_acct_mgmt (pam_handle_t *
pamh, int
flags);
该函数将执行帐户有效性检查(例如,密码和帐户是否到期以及访问时间限制),它通常在完成身份验证(方法是调用 pam_authenticate)之后执行调用。
pam_open_session和 pam_close_session函数
启动或终止会话的PAM使用者应该调用这两个函数之一。
#include
int pam_open_session (pam_handle_t *
pamh, int
flags);
int pam_close_session (pam_handle_t *
pamh, int
flags);
使用 pam_authenticate和 pam_acct_mgmt对用户进行成功验证之后,程序创建新会话时应该会调用 pam_open_session。这将通知会话模块打开新的会话。相反,关闭会话时应该调用 pam_close_session,以通知会话模块关闭会话。
pam_setcred函数
pam_setcred函数可以修改验证服务的用户凭证。
#include
int pam_setcred (pam_handle_t *
pamh, int
flags);
当用户通过验证并打开了一个会话后,需要使用 pam_setcred函数建立、修改或删除用户凭证。
pam_set_item和 pam_get_item函数
PAM使用者和服务模块可以使用 pam_set_item和 pam_get_item处理PAM信息。
#include
int pam_set_item (pam_handle_t *
pamh, int
item_type,
     const void *
item);
int pam_get_item (const pam_handle_t *
pamh, int
item_type,
     void **
item);
应用程序和服务模块可以使用 pam_set_item更新PAM信息。 item_type参数表示所更新信息的类型,并且可以表示多个类型。类型示例包括PAM服务名、用户名、tty名和用户验证标记。在 pam_set_item手册页, pam_set_item(3PAM)中可以找到完整的类型列表。
可以使用 pam_get_item访问每种消息类型的值。
因空间有限,我们无法使用这些函数展示示例,但是感兴趣的读者可以通过在
OpenSolaris源代码
中搜索相应的函数名了解它们的用法。
结束语


在这篇文章中,我们介绍了PAM会话函数的概念以及它们的作用(即会话函数的功能)。我们介绍了如何使用会话函数处理消息,并描述了各种不同的消息类型。
随后,我们展示了一个非常普通的示例会话函数,它对系列第2部分中的函数进行了改进。
最好,我们简单介绍了PAM API提供的其他一些函数: pam_acct_mgmt、 pam_open_session、 pam_close_session、 pam_setcred、 pam_set_item和 pam_get_item。
本系列下一篇文章(也是最后一篇)中,我们将研究如何编写PAM服务模块。


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/23363/showart_2154421.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP