Linux 可插拔身份验证模块
您是否曾想过修改登录以使其按您想要的方式工作需要多少工作量?也许您希望它引用影子密码,或者您不希望用户在一天中的某些时间登录。也许您的策略是确保 root 只能在控制台登录;也许,当系统连接到网络时,root 永远不应该登录。您可能会实现 MD5 安全密码,或使用 RIPEMD-160 或 SHA 安全的密码?也许您已决定密码对于您的需求来说不够安全,相反,您希望用户插入他们的身份证到插槽中才能登录。此外,您希望将您的系统集成到像 Kerberos 这样的网络安全环境中;用户的主要登录需要激活他们相对于 Kerberos 服务器的身份。变化和组合是无穷无尽的。没有完美的解决方案。永远不会有。
如果有足够的动机来实现新的用户身份验证方案,传统的解决方案一直是修改或重写所有系统访问应用程序(login、su、passwd、ftp、xdm 等)——这需要时间和资源。这种升级的一个令人恼火的副产品是,修改一个应用程序需要您同时升级其他应用程序,以维护系统的一致入口策略。除非您自己编写应用程序,否则简单地插入代码并确保您已填补所有漏洞并不容易。
系统安全策略的修订也存在以下潜在弱点:编写有吸引力的用户界面的人并不总是足够偏执,以编写安全软件。这一点与面向访问的(login 等)和信息安全(电子邮件加密等)软件都相关。
当前关于计算机相关安全的想法是将安全策略与应用程序提供的服务分开。这允许依赖安全的应用程序的作者使用应用程序编程接口 (API) 来处理安全相关问题,并将精力集中在编写一个好的(健壮但用户友好)应用程序上。
API 通常由描述应用程序程序员可以依赖的一组功能的文档定义。例如,libc是许多 API 的实现。协作组织(如 ANSI、POSIX 或 X/Open)生成定义 API 的文档,然后供应商(或 Linux 的爱好者)实现它。安全相关的 API 存在于用户身份验证和数据加密等任务中。
在 linux-security 电子邮件列表上的一条评论的提示下,Ted Ts'o 提出了以下问题:“有人考虑过实现 PAM 吗?”,因此,他启动了 Linux-PAM 项目。Marc Ewing(Red Hat Software 的)在 1996 年 1 月快速编码了大部分库的框架。从那时起,我一直在维护 Linux-PAM 发行版。感谢许多人的共同努力(我在本文末尾包含了一个“基本”完整的列表),我很自豪地说 Linux-PAM 现在已成为现实。更好的是,人们正在使用它。
定义 PAM 的文档是由 SunSoft, Inc. 的 Vipin Samar 和 Roland J. Schemers III 撰写的征求意见稿 (RFC)。具体来说,它是 OSF-RFC 86.0,1995 年 10 月,“使用可插拔身份验证模块 (PAM) 的统一登录”。本文末尾的 Linux-PAM URL 包含指向此文档的指针。
PAM-API 将身份验证业务分解为四个独立的管理组。这四个组是
身份验证/凭据获取
帐户管理
会话管理
身份验证令牌(密码)更新
通常,登录应用程序在授予候选用户访问系统的权限时需要使用这些组中的每一个。像passwd这样的应用程序只需要访问这些组中的最后一组。
PAM-API 的新颖性和强大之处在于前导“P”代表可插拔性。读者可能会惊讶地注意到,这是 PAM 中与登录应用程序编写者无关的部分。相反,它在为登录选择身份验证方案的过程中引入了系统管理员的角色。
Linux 的老用户会记得围绕从旧的 a.out 系统二进制文件迁移到 ELF 的所有炒作。这种转变的一个理想特性是引入了一个库函数dlopen(3)。此函数调用为正在运行的程序提供了一种可靠的方法来动态加载一些代码以供执行。它的同级函数dlclose(3)用于在不再需要时卸载或丢弃此类代码。PAM 的实现利用这些函数将应用程序程序动态绑定到本地指定的身份验证模块。也就是说,PAM 的可插拔性是动态的,因此由本地系统管理员自行决定。
使用基于 PAM 的登录应用程序,系统管理员可以完全更改应用程序使用的身份验证方案,而无需修改或重新编译它。原则上,这甚至可以在不重启计算机的情况下完成。但是,在完全部署新的身份验证方案之前,最好的方法是将计算机与任何网络隔离,并在受控条件下对其进行测试,以确保新安排是稳健的。
在本文的其余部分,我将简要概述如何编写和使用基于 PAM 的应用程序。潜在的作者和感兴趣的管理员应查看 Linux-PAM URL(在末尾)以获取更完整的详细信息。本文的目的是仅提供您可以对 Linux-PAM 执行的操作的初步了解。特别是,我不会讨论如何编写身份验证模块的问题。有关这些详细信息,您应该查阅 Linux-PAM URL 提供的完整文档。
让我们考虑一个通用的登录类型应用程序。我们将看到哪些职责委托给 PAM-API,哪些职责由应用程序保留。最后,我们将介绍本地管理员如何配置应用程序以适应本地口味。
图 1 是一个图形,描绘了基于 PAM 的工作应用程序的三个组件。左侧是链接到 libpam.so 共享库的应用程序。在中间,我们有 PAM 库,它解析配置文件,并使用其中列出的条目来加载配置的选择身份验证模块(PAM)。此外,应用程序提供了一个对话函数,该函数为模块提供了一种与用户直接对话的方式。
在列表 1 中,显示了登录类型应用程序的骨架。它可以与定义 PAM 的 OSF-RFC 中给出的示例应用程序进行比较。差异反映了自 RFC 编写以来 PAM 的增强功能。请注意,该列表不是很安全;它很少关注框架返回的可能错误,仅旨在帮助读者理解。
应用程序使用对pam_start()的调用初始化库,该调用静默地解析配置文件并加载那些适合此应用程序的身份验证模块。然后,它进入一个循环,尝试验证申请用户。重复此过程,直到用户被正确验证,或者加载的身份验证模块指示他们的耐心已耗尽。
一旦用户通过身份验证,pam_acct_mgmt()函数用于确定是否允许用户此时登录。帐户管理类型的模块可用于限制用户在一天/一周中的某些时间登录,或用于强制执行密码过期。后一种情况被拦截,并且在用户使用pam_chauthtok()函数成功更新密码之前,阻止用户访问系统。
用户的登录会话被两组函数调用包围。外部函数调用pam_open_session()和pam_close_session()标记了 PAM 身份验证会话的开始和结束。会话初始化和终止通常包括诸如使系统资源可用(挂载用户的家目录)和建立审计跟踪之类的任务。内部函数调用pam_setcred()首先建立,最后释放用户的 PAM 可配置身份。这些可以包括诸如访问票证和补充组成员资格之类的凭据。
注销后,用户的 PAM 可配置凭据将被删除,并且会话将通过调用 pam_close_session() 函数关闭。
最后,通过调用pam_end(),登录应用程序断开其与 PAM 库的连接。PAM 被卸载,动态分配的内存被清除并返回给系统。
这个简单的应用程序演示了 PAM 范式提供的大部分功能。对话机制灵活地将与用户的直接交互模式完全留给应用程序自行决定。通过这种方式,模块可以同时用于基于图形的程序(xdm等)及其基于文本的等效程序(login等)。
获得基于 PAM 的应用程序后,有必要将身份验证模块附加到它。在撰写本文时,有两种旧方法和一种新方法可以做到这一点。旧方法对应于 RFC 中提倡的方法,并且基于单个 PAM 配置文件 /etc/pam.conf 的内容。新方法是将各个服务的条目分解为独立的配置文件,这些文件都位于 /etc/pam.d/ 目录中。包含给定应用程序配置的文件名是服务名称(小写字母)。
配置文件(或多个文件)的功能是将应用程序的服务名称映射到提供原始应用程序身份验证服务的模块选择。在列表 1 的源代码程序的情况下,服务名称只是“login”。(这是 pam_start() 函数调用的第一个参数。)
与系统上存在的每个 PAM 感知服务的类似条目一起,旧配置文件 (/etc/pam.conf) 可能包含以下形式的条目
... # Here is the module configuration for login as it # might appear in "/etc/pam.conf" # login auth requisite pam_securetty.so login auth required pam_unix_auth.so login account required pam_unix_acct.so login session optional pam_cfs.so \ keys=/etc/security/cfs.keys login session required pam_unix_sess.so login password sufficient pam_unix_passwd.so login password required pam_warn.so # ...
前四个字段是:服务名称、模块类型、控制标志和模块文件名。第五个和更大的字段是可选参数,这些参数特定于各个身份验证模块。
配置文件中的第二个字段是模块类型,它指示相应的模块将为应用程序提供四个 PAM 管理服务中的哪一个。我们的示例配置文件引用了所有四个组
auth:标识在应用程序调用 pam_authenticate() 和 pam_setcred() 时调用的 PAM。
account:映射到 pam_acct_mgmt() 函数。
session:指示 pam_open_session() 和 pam_close_session() 调用的映射。
password:组引用 pam_chauthtok() 函数。
通常,您只需要为特定应用程序需要的功能提供映射。例如,标准密码更改应用程序 passwd 仅需要一个 password 组条目;任何其他条目都将被忽略。
第三个字段指示根据相应模块的成功或失败要采取的操作。用于填充此字段的令牌选择是
requisite:失败会立即将控制权返回给应用程序,指示第一个模块失败的性质。
required:所有这些模块都必须成功,libpam才能向应用程序返回成功。
sufficient:假设所有先前的模块都已成功,则此模块的成功会导致立即成功返回应用程序(此模块的失败将被忽略)。
optional:通常不记录此模块的成功或失败。
第四个字段包含可加载模块 pam_*.so 的名称。为了便于阅读,未给出每个模块的完整路径名。在 Linux-PAM-0.56 发布之前,没有对默认身份验证模块目录的支持。如果您安装了早期版本的 Linux-PAM,则必须为每个模块指定完整路径。您的发行版很可能将这些模块专门放置在以下目录之一中:/lib/security/ 或 /usr/lib/security/。
可以通过新配置安排,通过独立的登录配置文件获得我们登录应用程序的等效功能
#%PAM-1.0 #(The above "magic" header is optional) # The modules for login as they might appear in # "/etc/pam.d/login" this configuration is # accepted by Linux-PAM-0.56 and higher. # auth requisite pam_securetty.so auth required pam_unix_auth.so account required pam_unix_acct.so session optional pam_cfs.so \ keys=/etc/security/cfs.keys session required pam_unix_sess.so password sufficient pam_unix_passwd.so password required pam_warn.so # end of file.
较新的配置文件与旧配置文件的不同之处在于它缺少服务名称字段。不需要此字段,因为服务特定配置文件的名称根据定义就是应用程序的服务名称。
应该注意的是,/etc/pam.d/ 目录的内容优先于任何 /etc/pam.conf 文件的内容。
请注意,该示例包含多个用于 auth、session 和 password 管理组的模块映射。此功能称为堆叠,使单个应用程序可以一次使用多个模块。模块堆叠的顺序与它们被调用的顺序相同。
两个堆叠的 auth 模块用于 pam_authenticate() 用户。第一个模块 (pam_securetty.so) 检查用户是否为 root,并阻止 root 从不安全的终端登录。控制标志的 requisite 值用于强制在 securetty 模块失败时立即进行身份验证失败。如果发生这种情况,则不再执行任何 auth 模块。这样做的好处是防止 root 错误地在不安全的终端行上键入密码。另一个可以用来防止此类登录尝试的流行模块是 pam_listfile.so。它可以配置为基于指定文件中的令牌列表执行多种类型的访问控制。
当非超级用户,例如 joe,被 securetty 模块成功评估时,控制权将传递给堆栈中的下一个模块 pam_unix_auth.so。此模块执行标准 Unix 身份验证。它提示输入密码并根据本地系统中存储的密码进行检查。如果您的 libc 可以处理它,它可以在影子系统和非影子系统上工作。pam_unix_...so 的增强替代方案是 pam_pwdb.so 模块。此模块使用密码数据库库 libpwdb.so,可以执行 MD5 密码并提供 RADIUS 支持。重要的一点是,系统管理员通过简单地插入相应的模块来决定要实施哪种身份验证策略。
auth 模块还为 pam_setcred() 函数提供绑定。它链接到身份验证过程,因为用户身份验证的方法与用户的身份紧密相关。例如,Kerberos 需要基于网络的身份验证,并产生一个票证(用户的凭据),用户可以使用该凭据获得网络服务,例如远程登录和打印请求。
我们配置文件中的 account 模块行用于检查是否允许用户登录。这与确定用户是否是他们所说的人不同。帐户管理处理强制密码过期和防止在系统时间期间登录。我们的登录示例使用标准 pam_unix_acct.so 模块来强制执行影子密码老化。在这里,它(与 password 模块类型结合使用)用于强制用户更新密码。
对于此领域的实验,管理员可能希望尝试 pam_time.so。可以配置此模块以根据用户的终端线路、他们登录的时间以及他们打算做什么来允许或拒绝用户访问。
接下来,我们来看会话模块。堆栈中的第一个 pam_cfs.so 当前不存在。(由于 ITAR 出口限制,我不会编写它。)但是,我已将其包含在内以说明 PAM 会话概念。在用户在系统上的会话开始(和结束)时,此模块将挂载/卸载用户加密保护的家目录,从 cfs.keys 文件中获取用户的家目录/密钥映射信息。使用 PAM,某人可以提供单个模块,并且该模块可以用于任何 PAM 感知应用程序。将 optional 值用于控制标志可确保即使没有此类目录可用,用户也可以登录。
第二个模块 pam_unix_sess.so 使用 syslog(3) 记录消息,以宣布用户进入和退出系统。
最后,我们来看 password 管理组。在此处,当用户更改其身份验证令牌时,将调用堆叠的模块。传统上,此更改可能是为了更新他们的密码,但它有可能扩展到刷新智能卡或每年更新员工的视网膜扫描。在登录示例中,我们只是请求替换用户的 Unix 密码。由于 pam_unix_passwd.so 模块被标记为 sufficient,因此仅在用户未能成功输入新密码的情况下,pam_warn.so 模块才会记录警告。
除了配置特定的服务名称外,还有一个默认映射,给定的服务名称为 other。它可以通过为本地安全策略提供适当的默认模块选择来简化新服务的集成。相反,它可以用来拒绝访问任何没有特定 pam.conf 条目的应用程序。这是推荐的用法,例如,我们可以使用 pam_deny.so(始终拒绝访问)和 pam_warn(syslog(3) 信息性警告)模块,如下所示
#%PAM-1.0 #(The above "magic" header is optional) # The modules for defaulting services as defined # in "/etc/pam.d/other" this configuration is # accepted by Linux-PAM-0.56 and higher. # auth required pam_deny.so auth required pam_warn.so account required pam_deny.so session required pam_deny.so password required pam_warn.so password required pam_deny.so # end of file.
此配置始终拒绝用户访问应用程序。与以前一样,pam_warn.so 用于向 syslog(3) 发送警告消息以进行管理操作。此配置可用于确保只有特定服务在您的系统上可用。请注意,如果您编写了一个使用 PAM 的应用程序,并且此配置文件不足以阻止对其的服务,则您的应用程序没有以正确的方式使用 PAM。
感谢 Red Hat 的人员,许多应用程序都已修改为支持 PAM 用户身份验证方法。但是,在这方面,还需要做更多的工作。实际上,一些重要的应用程序仍然没有支持。最值得注意的是,应该移植应用程序ssh。目前正在努力提供灵活的基于 X 的对话功能,我感觉这将立即导致 PAM 集成的一些更友好的用途,例如流行的游戏。
当前 Linux-PAM 的大部分开发都集中在生产更强大和更多样化的模块上。最后一次计数时,编写了超过 20 个模块,并且数量还在增加——反映了 Linux 社区中人们使用的各种身份验证方案。您可以肯定,如果能够访问相应的硬件,那么有人会在那里编写视网膜扫描仪的身份验证模块。
最近对中央 PAM 库 libpam.so 的工作旨在测试我们实现的安全性。由于 OSF-RFC 中记录良好的简单设计以及与维护 Sun 实现的人员进行的友好对话,这并没有被证明是一项艰巨的任务。在可用的六个月中,仅出现了一两个重大的安全问题。目前,Linux-PAM 处于 beta 测试阶段(尽管 Red Hat 的人员在为其提供生产级支持方面做得非常出色)。特别是,libpam.so.1.00 甚至可能在您阅读本文时可用。
在达到 1.00 版本之前,libpam 还将支持可插拔的密码映射,这是一种以安全方式将多个不同密码链接在一起的方法。RFC 中讨论了此概念(以非可插拔形式),但是,X/Open 组此后对其进行了修订,我们将在未来几个月内在其最终规范中实现它。
更 आगे,在发布 1.0 版本后,我们将修改 PAM 配置文件的语法。当前的想法与增强控制标志字段以使其更加灵活有关。随着使用 PAM 的管理员数量的增加,可能会提出并采纳其他更改。
最后,Linux-PAM 是 PAM 的唯一实现吗?Linux 是唯一的吗?这些问题的答案是“是”和“否”。在撰写本文时,Linux PAM 的实现是唯一公开可用的 PAM 完全功能版本。这种情况至少已经持续了半年。但是,Sun 的 Solaris 2.5 中内部存在 PAM 的部分实现(没有 /etc/pam.conf 文件)。有传言表明 Solaris 2.6 将包含一个完整的、可配置的实现,该实现应该在今年可用。Sun 的 PAM 实现已贡献给 X/Open 组,因此请在未来几年内在其他 Unix 变体中寻找它。如果跨平台兼容性对您很重要,则应咨询您的供应商以获取更多信息。或者,Linux-PAM 的所有源代码都是免费提供的,因此如果您的需求紧急,只需将 Linux 视为免费的同义词,并立即在您的其他平台上尝试 Linux-PAM。
