第三章:PAM项目、代码和功能

许多PAM文档都假设系统管理员熟悉PAM的内部工作原理。设计者希望您了解PAM为每种类型的语句调用哪些函数,以及PAM会抛出哪些内部项和错误代码。配置PAM模块和Linux-PAM扩展控件需要理解这些返回代码。

你不需要记住这一章的所有内容。PAM项在调试中至关重要,您确实需要熟悉它们。然而,对于系统管理员来说,特定的PAM错误代码和函数并不那么重要。研究所涉及的原理以及这些代码的用途,但不要担心单个代码和函数的细节。当PAM模块吐出错误代码时,您始终可以查找错误代码,您将很快熟悉困扰您环境的错误。

PAM 项目

PAM在一组定义良好的 items 中携带了一大堆内部状态。使用PAM的过程定义所需的项并将其传递给各种PAM堆栈。所有项目都以undefined开头。应用程序和框架设置它们需要的项目。模块读取项目。

模块根据这些项目做出决定。我经常发现模块以意想不到的方式使用这些项目。当身份验证顽固地拒绝以您期望的方式行事时,回过头来检查这些项目和模块文档是一个很好的起点。

PAM在执行策略时,项目可能会发生变化。应用程序可以在模块提供信息时更改这些项目。当模块返回特定代码时,一些应用程序可能会忘记关键的身份验证信息。在做出决定后,返回该代码的模块需要放在策略的末尾。应用程序可能会为项目创建占位符数据。查看项目的价值是弄清楚为什么PAM在某个项目下出现横盘整理的好方法。

PAM项目可能包含安全敏感信息。因此,我强烈建议在一次性虚拟机上研究和实验PAM项目,而不是在生产环境中。PAM使用项目来包含用户名、主机名、密码和服务等事实。并非所有支持PAM的程序都定义了所有项目。例如,定义远程主机对 su(1) 来说毫无意义。

以下是常见的PAM项目。

PAM_SERVICE

PAM_SERVERCE项包含应用程序请求的策略名称。这通常是调用PAM的程序的名称,如su、ftpd、sshd等。您可以重新编译程序以使用不同的服务名称,但这并不常见。

PAM_USER

您可能会认为PAM_USER会提供请求身份验证的用户的用户名。

你错了。

PAM_USER是某个东西试图验证的用户名。例如,如果您使用FTP登录,PAM_USER等于您的FTP帐户。但是,如果你运行的是su root,那么你正试图以 root 身份进行身份验证。因此,PAM_USER等于 root

PAM_RUSER

PAM_RUSER项包含请求用户。它可能是远程用户。它可能是请求 su(1) 访问的用户。模块可能会将其设置为与PAM_USER相同的值。如果此应用程序仅涉及单个用户帐户,则可能根本不设置此项。

PAM_TTY

通过检查PAM_TTY查看进程正在哪个终端上运行。这可能是像pts/2这样的虚拟终端,也可能是像tty2这样的控制台登录。如果这是一个没有终端的图形应用程序,它可能是$DISPLAY环境变量的内容。

PAM_HOST

这给出了进行身份验证的主机名。它几乎总是local host。

PAM_RHOST

PAM_RHOST项给出了客户端运行的主机。对于FTP等应用程序,它包含客户端的主机名或IP地址。并非所有客户端-服务器应用程序都设置PAM_RHOST。此外,本地运行的应用程序,如 su(1) ,通常不会设置PAM_RHOST。

PAM_CONV

此项包含应用程序所需的PAM对话的数据结构。一个需要深入了解此事的系统管理员遇到了麻烦。

PAM_AUTHTOK

身份验证令牌(authentication token)是用户的当前密码,或类似密码的东西。

PAM_OLDAUTHTOK

这是用户的过期密码。更改密码时,您会看到这一点。

PAM_USER_PROMPT

这包含用于请求用户名进行身份验证的提示。

PAM_AUTHTOK_PROMPT

此项给出请求密码的提示。

PAM_OLDAUTHTOK_PROMPT

这包含用于请求过期密码的提示。当用户需要在登录前更改密码时,您会看到这一点。

PAM_SM_FUNCTION

Linux-PAM添加了PAM_M_FUNCTION来存储调用该模块的PAM服务模块函数。大多数系统管理员不需要这个,但不要让它突然出现的意外情况打扰你。

PAM_TYPE

Linux-PAM还添加了PAM_TYPE来显示调用模块的策略类型。这包含身份验证、帐户、密码或会话。Linux-PAM调用几乎总是定义PAM_TYPE。

我将在本章稍后讨论PAM功能和服务模块功能。

使用 pam_exec 读取项目

虽然PAM模块传递项目,但它们并没有真正提供一个向系统管理员显示项目的界面。Linux PAM系统提供PAM_warn来转储PAM中的信息,但它并不是普遍可用的。我们将在第6章中看到pam_warn。然而,每个类Unix系统都支持使用pam_exec模块从策略中复制调试信息。第6章和第7章详细介绍了pam_exec,但我们将在这里进行讨论。

pam_exec允许您将任意命令作为策略的一部分运行。通常,这是一个shell脚本。(您可以将简单的命令直接放入pam_exec语句中,但我觉得这更脆弱。)我们将使用一个简单的示例将策略中的所有pam项复制到系统日志中。几乎每个操作系统都包含pam_exec。

这是一个简单的脚本,用于捕获PAM项并将其写入系统日志。我将其安装为 /usr/local/scripts/pamvarlog.sh ,但您可以将其放在首选位置。

这会拉取所有环境项,抓取包含PAM的任何项或值,并将其发送到系统日志。

现在,将此脚本附加到服务的PAM策略中,最好在顶部附近。您不希望在获得数据之前, requisite 语句来阻止脚本运行。

将以下语句添加到测试系统的主身份验证策略中:FreeBSD上的 /etc/pam.d/system ,CentOS上的 /etc/pam.d/system-auth ,以及Debian的四个 /etc/pam.d/common 文件。您将在 /var/log/messages 中收到这样的消息。

这立即表明用户 mwl 在终端pts/0上运行了 su(1) ,并且请求同时符合身份验证和帐户策略。该脚本不会记录su请求的结果,但大多数使用身份验证的程序都可以执行自己的日志记录。

PAM模块通常希望您了解它使用哪些项目。我们将在第5章中看到这种行为的一个例子。

PAM 返回代码

成功和失败并不总是清晰的划分。正如政治在“是的,绝对!”和“就是这样,我要搬到加拿大/俄罗斯/南极洲/Discworld”之间有一大堆灰色地带一样,PAM除了“是”和“否”之外还有很多可能的答案。“错误的密码”和“系统管理员错误地配置了这个PAM模块”之间存在差异,两者都与“LDAP服务器宕机”不同。

当PAM调用一个模块时,该模块只返回一个返回码。返回代码的确切解释取决于该模块——如果您在本地有密码,则“密码过期”错误的含义与LDAP中的不同。

PAM包括30个返回码,编号为0到29。其中许多只是在奇怪的情况下或系统配置错误时出现的。不匹配的二进制类型和不可加载的库会出现在系统日志中,并且是每个系统管理员故障排除例程中熟悉但恼人的部分。有关完整列表,请查看X/Open Single Sign-on Service(XSSO)可插拔身份验证模块规范或 pam(3)

每个返回代码都有一个正式名称,通常以大写字母开头,以PAM开头。您将看到PAM_UCCESS、PAM_AUTH_ERR等返回码。您可能会看到从0到29的返回码编号。您可能还会看到小写的返回代码名,没有前导PAM_。也就是说,返回码 PAM_SUCCESS0success 都表示同一件事。

许多返回代码似乎重叠。对于系统程序员来说,它们不会,但对于系统管理员来说,它们会。返回PAM_CRED_EXPIRED和PAM_AUTHTOK_EXPIRED等代码意味着身份验证凭据或密码已过期。您会遇到哪个错误取决于您的身份验证方式,但对于系统管理员来说,这两者都意味着用户的密码或其他身份验证令牌不再有效。

您不需要知道每个PAM返回代码。您确实需要知道返回代码的存在,每个PAM调用只返回一个代码,以及如何在每个代码出现时了解更多信息。

以下是您最有可能遇到的PAM返回代码。

PAM_SUCCESS (0)

请求完全成功。用户已正确通过身份验证。这是一个无条件的肯定。

PAM_SERVICE_ERR (3)

PAM模块所需的某些服务未能满足您的需求。这在集中式身份验证中最为常见,当Kerberos、LDAP或NIS服务器出现故障时。

PAM_SYSTEM_ERR (4)

该模块在尝试运行时遇到操作系统级别错误。

PAM_PERM_DENIED (7)

该模块表示此用户缺乏返回成功所需的权限。此权限错误可能位于应用程序堆栈中的任何位置。检查您的系统日志。

PAM_MAXTRIES (8)

该模块对您可以尝试输入正确身份验证凭据的最大次数有限制。你已经超越了这一点。

PAM_AUTH_ERR (9)

身份验证发生错误。用户输入了错误的密码。

PAM_NEW_AUTHTOK_REQD (10)

最常见的情况是,收到此返回码意味着用户的密码已过期,必须更改。然而,在与密码无关的PAM模块中,当某些类似密码的实体需要调整或替换时,它可能会出现。

只有帐户策略应该返回此代码。

PAM_USER_UNKNOWN (13)

身份验证系统无法识别此用户。

PAM_IGNORE (25)

PAM策略应忽略此模块的结果,并且不允许它对允许或拒绝访问进行投票。

功能(Functions)

每种类型的PAM请求都有定义良好的编程接口来执行任务。这些API既赋予了PAM灵活性,又限制了其范围。当然,系统管理员不必知道如何使用这些功能。不过,如果它们出现在文档或系统日志中,大致了解该函数的作用可以帮助调试问题。

PAM设置和资源

设置PAM会话(session)需要程序调用 pam_start 函数。当PAM会话完成时,pam_end 函数会终止会话并释放会话使用的所有资源。

程序的PAM会话是程序和PAM库之间的对话。PAM应用程序需要一个“对话回调”,这是PAM堆栈将消息发送回应用程序的一种方式,由 pam_conv 函数提供。

除了本章前面讨论的项目外,PAM还包括一系列内部项目。即使是呈现给用户的提示也是一个项目。pam_set_itempam_get_time 函数允许pam操纵这些项。

pam_setenvpam_getenvpam_putenvpam_getenvlist 函数允许PAM在登录前操纵用户环境。例如,像 pam_krb5 这样的模块将使用这些函数将$KRB5CCNAME设置为登录时存储票证的凭据缓存。

PAM模块可能需要为其自身的内部功能分配内存。虽然PAM编程接口是严格定义的,但PAM模块可以在内部做任何它喜欢的事情。pam_set_datapam_get_data 允许pam会话按名称创建和操作这些内存块。

身份验证功能

顾名思义,pam_authenticate 函数对用户进行身份验证。该函数接收用户名和身份验证令牌(密码、指纹、基因扫描结果等),并将其与身份验证数据库进行核对。

pam_setcred 函数管理用户的凭据。用户的凭据可以包括用户的用户名和组成员资格等项目,以及Kerberos票证或其他单点登录设备等项目。

PAM模块不设置用户的UID或GID。它们也不执行为用户打开虚拟终端等操作。这些操作通常需要由子进程完成。如果PAM模块在调用 pam_setcred 时将权限从 root 删除到用户,则该模块将无法访问特权操作,如更新lastlog。相反,服务器为用户的活动生成一个子进程,而服务器进程则保持其特权并在自身之后进行清理。

帐户、会话和密码功能

PAM使用 pam_acct_mgmt 函数来强制执行帐户策略。对登录时间、密码过期或其他帐户对账单的任何限制都通过 pam_acct_mgmt 传递。

会话语句触发 pam_open_sessionpam_close_session 函数。

最后,pam_chauthtok 函数允许服务器更改用户的密码。

PAM服务功能

除了用于调用PAM的函数外,您还将看到PAM本身用于调用模块的函数。这些服务模块功能是每个模块如何响应一种类型的请求。每个服务模块函数都对应一个常规PAM函数,并以 pam_sm_ 而不仅仅是 pam_ 开头。

服务模块函数为pam_sm_acct_mgmtpam_sm_authenticatepam_sm_chauthtokpam_s_mclose_sessionpam_sim_open_sessionpam_sm_setcred 。因此,pam_sm_acct_mgmtpam_acct_mgmt 类似,但在模块中使用,而不是PAM。

你为什么需要知道这一切?一方面,Linux-PAM的扩展控件大量使用PAM返回代码。