第一章:PAM组件

任何PAM系统都使用几种类型的规则来处理登录和身份验证过程。我们将从包含规则的配置文件开始,然后深入到身份验证组件和操作。

PAM 配置文件

您可能会在 /etc/pam.conf 文件中找到PAM配置,或者在 /etc/pam.d/ 目录中的一大堆文件中找到。

当Solaris在1990年代推出PAM时,对于使用PAM的少数服务来说,一个配置文件就足够了。每个PAM规则语句都以应用规则的服务名称开头。访问 rlogind(8) 的规则以 rsh 开头,管理telnet访问的规则以 telnet 开头,以此类推。Solaris派生系统仍然使用单个 pam.conf

然而,PAM像野草一样传播。不久之后,单一的配置文件限制了复杂的系统管理。像Linux-PAM和OpenPAM这样的实现将PAM规则拆分到 /etc/pam.d 目录中,每个服务都有一个以它命名的文件。Linux-PAM和OpenPAM没有用 rlogin 这个词来启动规则,而是将rlogin(8) 的规则放在 /etc/pam.d/rsh 中,将SSH访问规则放在 /etc/pam.d/sshd 中,以此类推。

FreeBSD系统还将配置核心系统组件和附加包分开。虽然系统组件的PAM配置位于 /etc/pam.d 中,但附加包的PAM配置在 /usr/local/etc/pam.d 中。您可能会在其他操作系统中找到类似的变体。

使用 /etc/pam.d 的系统比依赖 /etc/pam.conf 的系统更常见。本书中的示例假设您使用的是按服务的pam配置文件。如果您使用的是基于Solaris的系统,则需要将服务名称添加到 /etc/pam.conf 中每个PAM规则的前面。

PAM 政策

如果您从未查看过PAM文件,请查看 /etc/pam.d/ 中的一个文件。策略文件有一堆这样的语句:

每行是一个PAM语句或规则。每条语句包含四个组件:类型(type)、控制(control)、模块(module)和模块参数(module arguments)。

这里显示的第一个PAM语句具有类型 auth 、控制 required 、模块 pam_unix.so 以及参数 no_warntry_first_passnullok

我们将详细介绍其中的每一个,但了解每个的基础知识将有助于我们更深入地了解这些部分是如何互操作的。

type—— 类型,是身份验证过程的一个组件。管理凭据是身份验证的一部分。设置用户帐户和资源限制以及更改用户密码也是如此。

control —— 控制语句指示PAM应如何对PAM模块的成功和失败做出反应。是否应批准身份验证请求?请求是否应该尝试下一个模块?这里的失败是否应该终止整个过程?这些控制语句提供了PAM的逻辑。

module —— 模块是正在使用的PAM模块。使用模块的文件名,包括扩展名 .so 。(OpenPAM允许您省略 .so )。大多数PAM模块安装在系统特定的位置,如 /usr/lib//lib/x86_64-linux-gnu/security/ 。如果要使用不在标准目录中的模块,请按其完整路径列出。在这里列出一个模块告诉PAM将身份验证信息提供给这个模块并收集响应。例如,pam_unix.so 会检查本地系统的密码文件以获取有效的用户名和密码。如果PAM将用户名和密码交给该模块,该模块将响应“yes,it exists”或“nope,invalid”。控制语句告诉PAM如何处理每种响应。(如第4章所述,一个模块可以回答“是”或“否”以上的问题。)

module arguments —— 模块参数,特定于每个模块。一些参数,如 no_warndebug ,可以被许多模块识别。然而,每个的确切含义取决于模块。并非所有模块都需要或使用参数。一些编码不好的模块根本不接受任何参数。

用反斜杠(\)分隔行之间的长语句。

让我们深入了解身份验证类型。

身份验证类型

PAM将身份验证过程分为四个部分。这些组件可能被称为设施(facilities )或类型(types),具体取决于您阅读的文档。类型是PAM规则中的第一个字段。

auth 或authentication类型验证所提供的身份验证信息,并为帐户设置任何限制或资源限制。如果您输入了错误的密码或不存在的用户名,则身份验证类型会将您踢出。如果用户帐户的进程数量有限,可以使用最大内存量,或者属于特定组,则auth类型将处理设置这些限制。

account 类型根据简单身份验证以外的特征控制对帐户的访问。如果用户只能在奇数年的2月28日登录,则在帐户类型中配置。用户可以输入正确的身份验证信息,但如果没有可用的帐户,则无法登录。

session 类型处理提供服务所需的系统端设置。命令行用户需要一个虚拟终端、一个主目录,可能还需要一个表明他们登录的日志条目。匿名FTP用户不需要虚拟终端、个人主目录或shell,但需要FTP特定的资源。当会话结束时,需要删除任何分配的资源。会话类型管理所有此类每会话要求。

最后,当用户的凭据需要在系统上更新时,需要 password 类型。也许用户正在更改密码。也许他们的硬件令牌需要戳一下。密码类型处理更新身份验证凭据所需的任何操作。

主机提供的每项服务都应该有一个或多个每种类型的声明。根据身份验证在您的系统上的工作方式,您可能有几十种类型,两种其他类型的单个语句,而第四种类型则没有。

一组相同类型的语句通常被称为链(chain),有时也被称为堆栈(stack)。这本书使用了“政策”(policy)这个词。请看本章开头的PAM策略示例。每项政策只有一个声明。在下一节中,我们将看到带有几个语句的策略。

使用Linux-PAM,您偶尔会看到名称前带有前导连字符的类型语句:-auth-account-session-password。这些表明,如果系统上未安装该模块,PAM应忽略该错误。当模块是可选的时,例如对于 Kerberossystemd(8) ,您会看到这一点。

PAM 控制

PAM控制,声明特定模块如何影响策略。您可以决定哪些类型的身份验证是强制性的,哪些是自愿的,哪些您不关心。

PAM将用户的身份验证信息发送到策略中的每个模块。每个模块返回成功或失败,这意味着该模块的身份验证尝试成功或失败。例如,密码验证模块确定用户提供的密码是否与为该用户配置的密码匹配。如果密码匹配,则模块返回成功;如果没有,就是失败。负责用户主目录的PAM模块会查看用户的主目录是否存在,如果存在则返回成功,如果不存在则返回失败。(PAM模块可以返回多于这两个代码,如第4章所述,但这会让你开始。)

PAM控制与数据包过滤器、web服务器和其他访问控制列表等应用程序中的严格允许/拒绝语法不同。他们更像是一个有着数百年历史的教育机构中的一个长期委员会,沉浸在传统和仪式中,每个成员都有一个不寻常的名字、巴洛克式的责任和独特的特权。

该委员会以明确、庄严的顺序对认证进行投票。每个成员都有特定的投票方式。也许大校长开始投票,在其他人有机会之前,他可以说“是”或拒绝整个提案。院长可以投“不予置评”或“反对”票,但无权投票赞成任何事情。资深牧马人可以投“否”或“是”票,只要没有其他人反对。如果投票达到了《最近的符文》的讲师,他可以保持沉默或宣布,“是的,该死,投票结束了,我赢了!”

与此同时,图书管理员在桌子旁有一个座位,但只能做笔记和吃花生。

控制语句正式定义了这种结构。每个PAM模块都有一定的投票权限。一些控制语句说,“如果此模块返回成功,则停止处理并立即允许身份验证。”其他控制语句给出指令,如“如果失败,则立即终止”或“如果此模式成功,则继续下一个模式。”在策略结束时,投票决定是否授予或拒绝访问权限。

Linux-PAM用户可以访问更复杂的控制语法,如第4章所述。即便如此,大多数Linux-PAM部署仍然严重依赖这些“传统”控制语句。

PAM有五个主要控件:requiredrequisiteoptionalsufficientbinding

Required (必须的)

具有 required 控制的语句意味着此模块必须返回成功,策略才能允许访问。如果用户输入了错误的密码,则无法登录。如果系统管理员已配置主机,使任何人都无法登录,则他们无法登录。

如果 required 的控制失败,PAM将处理策略中的其余模块。登录仍然返回失败,拒绝访问,但其他模块有机会进行他们需要的任何日志记录或记帐。

如果 required 模块成功,PAM将继续处理策略,让其他模块有机会拒绝访问。

PAM必须成功访问每个 required 模块。即使一个 required 模块失败,其他模块的成功也是不够的。请考虑此示例策略:

此策略首先将身份验证信息传递给 pam_breathalyzer.so 模块,然后传递给 pam_ddr.so ,再传递给 pam_genescan.so ,所有这些都具有 required 控制。这项政策需要一致同意。如果这些模块中的任何一个返回失败,则身份验证请求失败。不过,这三个模块都会被处理,因此它们可以执行辅助任务,如记录对系统管理员有帮助的信息。

根据模块的功能和策略类型, required 语句是多因素身份验证的关键部分。

许多其他控制语句(稍后描述)声称如果成功则授予访问权限。但是,如果较早的 required 模块失败,PAM将拒绝访问。一个失败的 required 控制就像是“不”的大锤。

Requisite(必要条件)

requisite 控制指示模块必须成功才能授予访问权限。

如果 requisite 控制成功,PAM将继续处理模块。除非后来被拒绝,否则该请求将被批准。

如果 requisite 控制失败,PAM立即停止处理模块,并告诉应用程序请求被拒绝。这使得 requiredrequisite 不同。

让我们用 requisite 控制来检查这个PAM策略:

第一个模块 pam_breathalyzer.so 是必需的(required)。如果用户没有通过酒精测试(breathalyzer test),她就无法登录,仅此而已。无论该模块成功还是失败,PAM都会继续执行策略中的下一个模块。

第二个模块 pam_ddr.so 是必要的(requisite)。PAM必须成功授予访问权限。如果此模块失败,PAM将立即停止处理策略,并告诉应用程序身份验证被拒绝。

第三个模块 pam_genescan.so 也是必需的(required)。然而,只有当第二个模块成功时,才会触发第三个模块。pam_ddr.so 中的失败意味着 pam_genescan.so 不会被检查。

使用必要的(requisite)控件可以向用户提示身份验证尝试失败的位置。入侵者可以利用这些信息更精确地瞄准他们的攻击。仅当您有非常具体的原因不运行以后的控件时,才使用必要的(requisite)。不要花费时间、精力或注意力来优化登录失败。这项政策使用了必要的(requisite)控制,因为基因扫描很昂贵,避免不必要地节省资金。

Optional(可选的)

带有 optional 控制的语句对成功或失败的影响很小。操作系统使用可选控件来管理可能部署或配置的功能,如SSH代理和Kerberos。您还将部署可选控件,以向身份验证会话添加其他功能。

当且仅当策略中没有其他模块表达意见时,可选控件可以允许或拒绝访问。如果你有一堆充分(sufficient)语句和一个可选语句,但没有一个充分语句允许访问,那么可选语句可以允许或拒绝访问。

这是一个使用可选控件的策略:

我们的示例中有三个模块依然是 required :所有模块都必须成功,用户才能访问。

新模块 pam_faildelay.so 设置了登录尝试之间的延迟。如果用户的登录尝试被拒绝,则模块会延迟几秒钟向用户返回身份验证提示。此模块不处理用户的身份验证凭据。由于它只改变PAM的行为方式,您会期望它总是返回成功。然而,在 pam_faildelay.so 失败的不太可能的情况下,您不希望失败阻止登录。

带有 optional 控件的语句通常位于策略的前面。您可能不希望必要控件的故障阻止可选模块。然而,我故意在这项政策的末尾加上 pam_faildelay 。我不想在第一次登录尝试之前引入延迟。

我应该提到,使用 pam_faildelay 根本不是一个好主意;这种延迟应该内置在应用程序中,而不是通过PAM提供,正如各种安全建议所显示的那样。理想情况下,每次身份验证尝试都应该花费恒定的时间,而不是在尝试之间提供恒定的延迟。不过,这是一个流行的模块,所以你至少需要认识到它。

会话类型是 optional 控件的最常见用户。

Sufficient(充分的)

sufficient 控制意味着,只要之前所需的控制没有失败,此模块中的成功就足以提供访问。 如果 sufficient 控制成功,PAM会立即授予访问权限。它不处理策略中的其他模块。

如果 sufficient 控制失败,PAM不会拒绝访问。sufficient 控制的失败被视为 optional 控制的失败。PAM将此故障记录为 optional 故障。不触发失败允许用户尝试其他身份验证方法。考虑以下政策。

这三个模块都是 sufficient ,这意味着成功验证其中任何一个模块都会立即允许访问。

请思考一下这项政策。我们有三次机会创造成功,但这项政策可能造成的唯一失败是 optional 。当没有明确的接受或拒绝时,您的PAM实现会做什么?OpenPAM默认拒绝请求。

Linux-PAM通常会拒绝请求,但在过去的二十年里,我遇到了某些配置和发行版,除非特别拒绝,否则允许访问。最佳实践要求明确拒绝访问。Linux-PAM和OpenPAM都包含用于此应用程序的 pam_deny

所有对 pam_deny 的请求都失败,从而创建了一个故障安全机制。如果策略中的最后一条规则使用了 sufficient 控件,请在其后添加 required pam_deny 语句。您将在第3章中获得有关 pam_deny 的更多详细信息。

添加此项后,这些测试中的任何一个都会通过,或者请求会被拒绝。

sufficient 控制允许使用/或身份验证方法。看看我们下一个政策的例子:

第一个模块 pam_breathalyzer.sosufficient 。如果用户通过此模块,PAM将认为请求成功并停止处理策略。但是,如果此模块发生故障,PAM会记录一个 optional 故障并继续执行策略。

第二和第三项声明是 required 。只有当第一个模块返回失败时,您才会遇到这些语句。

最终结果?用户可以使用 pam_breathalyzer.so 进行身份验证,也可以使用 pam_ddr.sopam_genescan.so 进行身份验证。由于此策略以 required 控件结束,因此我们不需要在末尾使用故障保护 pam_deny.so 语句。

Binding(绑定)

binding 控件几乎是一种 required 控件,一旦成功,它会立即停止处理策略。binding 控件很少使用——我从未在现实世界中见过它的部署。Sun公司的工程师们在首次提出该标准时认为绑定看起来很有用,但现实并不同意。我只在LDAP上下文中的PAM语句中看到过“bind”这个词。它甚至没有在Linux-PAM中实现。出于这些原因,虽然我将在这里解释 binding ,但本书的其余部分假装它不存在。

如果具有 binding 控件的语句成功,并且没有较早的 required 语句失败,则通知应用程序立即授予访问权限。PAM不处理政策中的任何进一步声明。

如果带有 binding 控件的语句失败,PAM将拒绝访问。策略的其余规则被处理,允许它们执行其功能,但访问请求最终被拒绝。

如果您正在考虑使用 binding ,请尝试使用 sufficient

Include(包含)

Linux-PAM和OpenPAM都支持 include 语句,允许您将一个策略拉入另一个策略。该策略可能来自 /etc/pam.d/etc/pam.conf 中的文件。Debian系统会拉入引用的整个文件,而OpenPAM和大多数Linux-pam系统只拉入相关类型的语句。这是CentOS的 /etc/pam.d/sudosudo(1) 的pam配置:

此策略具有单个身份验证规则。它包括策略 system-auth 。这个命令告诉PAM在 /etc/pam.conf 中检查文件 /etc/pam.d/system-auth 或该名称的策略,获取该类型的所有规则,并将它们放在这里。/etc/pam.d/system-auth 文件包含此身份验证策略。

当用户访问登录服务时,他们会通过此策略。

使用 includes 可以让系统管理员在一个位置维护多个服务的PAM配置。更改 includes 文件会使更改立即传播到所有相关服务。

然而,所有操作系统都使用略有不同的 includes 策略设计。许多CentOS PAM模块都包含系统身份验证配置文件。FreeBSD使用 /etc/pam.d/system 。Debian按类型(如 common-auth, common-session 等)划分中心包含文件。

Debian使用 @include 来拉取整个文件。这是Debian的 /etc/pam.d/sudo 配置:

此配置表示“使用与其他所有人相同的身份验证,与其他所有内容相同的帐户类型,以及与任何其他非交互式进程相同的会话设置。”

包含这样的文件的优点是,它使在策略的开头或结尾添加条目变得非常容易。如果我只想为一个程序的类型添加一个PAM模块,我可以将其添加到该程序的文件中。在这里,我告诉Debian的sudo除了所有常见的身份验证方法外,还需要进行酒精测试。

包含文件可以提高您的灵活性,而不需要您维护PAM策略的多个副本。

Linux-PAM也支持子堆栈(substacks),类似于includes。有关子堆栈的讨论,请参阅第4章。

模块和参数

每个PAM语句末尾的模块及其参数决定了规则实现的功能或行为。每个模块都提供检查密码、配置主目录或分配终端等功能。

我们将通过安装和使用PAM模块来安装大多数功能。

模块上下文

当您探索PAM模块时,您会注意到许多模块被几种不同的PAM类型调用。例如, pam_unix.so 模块出现在身份验证、帐户和密码策略中。模块的行为方式及其提供的服务因调用它的类型而异。如果身份验证类型调用 pam_unix.so ,则模块会检查密码。帐户类型从 pam_unix.so 获取帐户可用性信息。密码类型使用 pam_unix.so 更改系统密码文件。

并非每个模块都为所有类型提供服务。一些非常有用的模块只支持一种类型,但为该类型提供重要服务。例如,模块 pam_mkhomedir.so 为经过身份验证的用户创建了不存在的主目录,作为会话类型的一部分。这与密码无关,但对大型企业来说至关重要。

模块参数

模块参数或标志配置模块本身。模块是否应该告诉用户它拒绝请求的原因?您想激活可选功能吗?模块参数启用和禁用这些可选功能。

在这里,我们为 pam_unix 模块设置了 nulloktry_first_pass 标志。

一些标志有自己的参数,允许您设置限制或切换函数。

酒精测试仪(Breathalyzers )本身并不知道你的组织对酒精的容忍度。在这个例子中,如果用户的血液酒精含量低于0.08,则该帐户可用。我是怎么知道这个论点的?我阅读了模块文档。同样,根据美国反歧视法,我们告诉基因扫描仪不要进行尼安德特人(Neanderthal)检查。

当在不同的上下文中使用时,模块可能需要完全不同的参数。

常见模块参数

虽然任何人都可以编写PAM模块,但这并不意味着每个人都可以重新设计模块配置。大多数PAM模块都接受这些常见的参数来实现通用功能。

PAM模块不需要接受这些参数中的任何一个。不支持标志功能的正确编码的PAM模块将默默地忽略它。

debug(调试)

debug 标志告诉模块通过syslog记录调试信息。PAM调试消息以优先调试的身份验证工具记录。将调试标志添加到 pam_unix.so 并查看系统日志将教会您一大堆关于PAM实现如何处理身份验证请求的知识。

大多数但并非所有PAM模块都支持调试。如果PAM模块在使用调试标志时表现不佳,则将其视为该模块编程不佳的暗示。你可能会在使用该模块时遇到困难,但至少你得到了警告。

在OpenPAM中,debug 标志不仅会触发PAM行为的调试输出,还会触发共享库中的调试。您将看到哪个服务函数被调用以及它返回什么,如第3章所述。

通用PAM错误,如列出不存在的模块,会出现在系统的安全日志中(通常为 /var/log/secure/var/log/auth )。

no_warn

PAM模块可能会提供关于拒绝访问请求的原因的反馈。启用 no_warn 会使该反馈静音。虽然酒精测试仪模块通常会告诉用户,他们无法登录,因为他们太醉了,找不到自己的脸,更不用说程序了,但添加 no_warn 会关闭警告。

use_first_pass

主机的身份验证系统可能被设置为尝试通过几种方法中的每一种来验证用户名和密码。例如,一个主机可能会尝试本地密码文件和LDAP。虽然每个PAM模块都可以提示用户输入密码,但 use_first_pass 选项告诉模块使用已输入的密码。如果没有此选项,模块可能会再次提示用户输入密码。

如果用户从未输入过密码,她无论如何都会收到提示。

如果现有密码不起作用,则模块失败。

use_first_pass 选项通常仅出现在身份验证规则中。

try_first_pass

use_first_pass 类似,try_first_pass 选项告诉模块尝试使用以前输入的密码进行身份验证。但是,如果密码不正确,该模块可以提示用户输入密码。如果该密码失败,则模块失败。 try_first_pass 选项通常仅出现在身份验证规则中。

use_mapped_pass

use_mapped_pass 选项允许您对用户输入的密码进行哈希或加密。与 binding 控件一样, use_mapped_pass 现在很少使用。我只是为了完整起见才把它包括在内。

expose_account

人类不善于听从指示。机器要求输入用户名,我们键入密码。出于这样的原因,模块往往对故障保持沉默。他们对用户名、用户的主目录等信息保持沉默。expose_account 选项告诉模块发布这些信息。启用此选项时,某些模块将打印诸如用户mwl身份验证失败或主目录 /home/mwl 不可用等消息。

expose_account 的支持因模块而异。

默认策略

并非所有应用程序都需要自己的策略。许多应用程序可以共享一组常见的默认值。如果PAM找不到应用程序的策略文件,它将调用 /etc/pam.d/other 中的“other”策略。

这些系统默认策略在系统之间差异很大。FreeBSD的策略与大多数其他策略中包含的系统策略文件非常相似,而CentOS则拒绝一切。检查一下你的操作系统。

政策处理和结果

PAM policychain (链)是服务的一组相同类型的规则。应用程序可能具有身份验证、帐户、会话和密码策略,也可能仅具有其中一些策略。毕竟,FTP不需要(也不应该需要!)用户更改密码的方法。

这是FreeBSD系统的完整默认系统身份验证:

auth 策略有三个语句长。帐户策略是两条语句,而会话和密码只是一条语句。策略可能要长得多——在大多数Linux系统上,身份验证策略至少有六条语句,并在每个协议的基础上添加更多语句。苹果和基于OpenSolaris的系统通常有一个声明政策。(这就是为什么我们在这里通过一个适度的OpenPAM示例。)

每个策略都有允许或拒绝该类型访问请求的任务。它根据政策中的声明做出决定。

考虑上面的三语句 auth 策略。当用户运行调用此策略的程序时,PAM首先调用模块 pam_opie 并向其提供所需的信息。pam_opie 模块具有足够的控制权:如果成功,身份验证策略会立即显示“允许访问”并停止处理该策略。第一个模块的失败并不意味着该模块反对让用户登录——它只是不会说“是”。当一个 sufficient 模块失败(fails)时,PAM会继续处理策略。这相当于“去问你妈妈”。

第二个 auth 验证模块 pam_opieaccess 具有 requisite 控制。如果此模块返回成功,则允许访问。但是,如果模块返回失败,PAM会立即停止处理策略并返回失败。这是一个艰难的是/否决定。

auth 策略中的最后一个模块 pam_unixrequired 。如果模块返回成功,则允许访问。如果模块返回故障,则拒绝访问。

总体而言,auth 策略可以通过几种方式进行处理。如果第一个 sufficient 模块成功,则立即允许访问。如果失败,处理将转到策略中的下两个规则。这两者都必须返回成功才能允许访问。最后两个模块中的任何一个出现故障都会拒绝访问。

在大多数部署中,策略对拒绝的重视程度高于允许访问。大多数控制类型都有否决权,即使政策中的后续声明表示同意,他们也可以拒绝访问。

如您所见,PAM策略处理不同于一般访问控制列表的通常允许/拒绝(allow/deny)语法。将PAM策略视为类似于防火墙规则会让你感到痛苦。PAM使用其特殊语言来明确这一点。

现在您已经了解了PAM工作原理的一些基础知识,让我们来看看一些常见的模块,看看它们是如何配置的。