可插拔认证模块(PAM)
最后更新于
最后更新于
原文:
本文介绍了可插拔认证模块(PAM)库的基本原理与机制,解释了如何配置 PAM、如何将 PAM 集成进应用程序、以及如何编写 PAM 模块。
可插拔认证模块(PAM)库是一个通用的、用于认证相关服务的 API,它允许系统管理员仅通过安装新的 PAM 模块来添加新的认证方法,并且可以通过编辑配置文件来修改认证策略。
PAM 由 Sun Microsystems 的 Vipin Samar 和 Charlie Lai 于 1995 年设计和开发,自那以后基本未发生变化。1997 年,Open Group 发布了 X/Open Single Sign-on(XSSO)初步规范,它对 PAM API 进行了标准化,并增加了单点(或者说集成)登录的扩展功能。截至撰写本文时,这项规范尚未成为正式标准。
尽管本文主要聚焦于使用 OpenPAM 的 FreeBSD 5.x,但其内容同样适用于使用 Linux-PAM 的 FreeBSD 4.x,以及其他操作系统,如 Linux 和 Solaris™。
PAM 相关术语相当混乱。Samar 与 Lai 的原始论文以及 XSSO 规范都未尝试对参与 PAM 的各方和实体进行正式定义,他们所使用(但未定义)的术语有时具有误导性或模糊性。第一个建立一致且明确术语体系的尝试是 Andrew G. Morgan(Linux-PAM 作者)在 1999 年撰写的一篇白皮书。尽管 Morgan 的术语选择是一次巨大进步,但在本文作者看来仍不尽完善。以下定义在很大程度上受到 Morgan 的启发,试图为所有参与 PAM 的行为体与实体制定精确而明确的术语。
account(账户) 申请者希望仲裁者授予的一组凭据。
applicant(申请者) 发起认证请求的用户或实体。
arbitrator(仲裁者) 拥有验证申请者凭据的权限以及授予或拒绝请求权力的用户或实体。
chain(链) 响应 PAM 请求而调用的一系列模块。链中包括了调用模块的顺序、传递给模块的参数,以及如何解释模块返回结果的信息。
client(客户端) 代表申请者发起认证请求的应用程序,负责从申请者处获取所需认证信息。
facility(功能组) PAM 提供的四类基本功能之一:认证(authentication)、账户管理(account management)、会话管理(session management)以及认证令牌更新(authentication token update)。
module(模块) 实现某一特定认证功能组的一组相关函数,集合成一个单独(通常是可动态加载)的二进制文件,并以一个名称标识。
policy(策略) 描述如何处理某一服务的 PAM 请求的完整配置语句集。一个策略通常由四条链组成,每条链对应一个功能组,尽管某些服务并不使用全部四个功能组。
server(服务器) 代表仲裁者执行操作的应用程序,用于与客户端交互、获取认证信息、验证申请者凭据,并决定是否授予请求。
service(服务) 提供相似或相关功能,并具有类似认证需求的一类服务器。PAM 策略以服务为单位定义,所有使用相同服务名的服务器都将遵循相同的策略。
session(会话) 服务器为申请者提供服务的上下文环境。PAM 的四个功能组之一 —— 会话管理(session management)—— 专门负责该上下文的创建与销毁。
token(令牌) 与账户关联的一段信息,如密码或口令,申请者需提供此信息以证明其身份。
transaction(事务) 同一申请者对同一服务器实例发起的一系列请求,从认证与会话建立开始,到会话结束为止。
本节通过一些简单的示例,说明上述定义的一些术语的含义。
申请者是 alice
。
账户是 root
。
认证令牌是 xi3kiune
。
申请者是 eve
。
账户是 bob
。
认证令牌是 god
。
尽管本示例中未显示,但仲裁者是 root
。
以下是 FreeBSD 默认的 sshd
策略:
auth
、account
、session
和 password
是四个功能模块。
pam_nologin.so、pam_unix.so、pam_login_access.so、pam_lastlog.so 和 pam_permit.so 是模块。通过这个示例可以看出,pam_unix.so 至少提供了两个功能模块(认证和账户管理)。
PAM API 提供了六个不同的认证原语,这些原语被分组到四个功能模块中,具体描述如下。
auth
认证。 该功能模块关注于认证申请者并建立账户凭证。它提供了两个原语:
account
账户管理。 该功能模块处理与认证无关的账户可用性问题,如基于一天中的时间或服务器工作负载的访问限制。它提供了一个原语:
session
会话管理。 该功能模块处理与会话设置和拆卸相关的任务,如登录会计。它提供了两个原语:
password
密码管理。 该功能模块用于更改与账户关联的认证令牌,可能是因为令牌已过期,或者用户希望更改它。它提供了一个原语:
模块是 PAM 中一个非常核心的概念;毕竟,它们是 “PAM” 中的 "M"。PAM 模块是一个自包含的程序代码,负责实现一个或多个功能模块中的原语,针对特定的机制;例如,认证功能的机制可能包括 UNIX® 密码数据库、NIS、LDAP 和 Radius 等。
FreeBSD 每个机制都实现为一个单独的模块,命名为 pam_mechanism.so
(例如,针对 UNIX® 机制的模块是 pam_unix.so
)。其他实现有时会为不同的功能提供单独的模块,并将功能名称与机制名称一起包括在模块名称中。例如,Solaris™ 有一个名为 pam_dial_auth.so.1
的模块,通常用于认证拨号用户。
FreeBSD 最初基于 Linux-PAM 的 PAM 实现没有为 PAM 模块使用版本号。这通常会导致与旧版应用程序相关的问题,因为这些应用程序可能链接到较旧版本的系统库,而无法加载所需模块的匹配版本。
另一方面,OpenPAM 会查找与 PAM 库(当前版本为 2)具有相同版本号的模块,如果找不到对应版本的模块,则回退到没有版本号的模块。因此,可以为旧版应用程序提供旧版模块,同时允许新(或新构建的)应用程序利用最新的模块。
虽然 Solaris™ PAM 模块通常会有版本号,但它们并没有真正的版本控制,因为版本号是模块名称的一部分,必须在配置中包含该版本号。
一个策略由四个链组成,每个链对应一个 PAM 功能模块。每个链都是一个配置语句的序列,每个语句指定要调用的模块、传递给模块的(可选)参数以及描述如何解释模块返回码的控制标志。
理解控制标志对于理解 PAM 配置文件至关重要。控制标志有四种不同的类型:
binding
如果模块成功并且链中没有任何先前的模块失败,则链会立即终止,请求被批准。如果模块失败,则会执行链中的其余部分,但请求最终会被拒绝。
该控制标志由 Sun 在 Solaris™ 9(SunOS™ 5.9)中引入,OpenPAM 也支持此标志。
required
如果模块成功,则执行链中的其余部分,并且请求被批准,除非其他某个模块失败。如果模块失败,则链中的其余部分也会被执行,但请求最终会被拒绝。
requisite
如果模块成功,则执行链中的其余部分,请求被批准,除非其他某个模块失败。如果模块失败,链会立即终止,请求被拒绝。
sufficient
如果模块成功并且链中的先前模块没有失败,则链会立即终止,请求被批准。如果模块失败,则该模块会被忽略,链中的其余部分会被执行。
由于该标志的语义可能有些混淆,特别是在用于链中的最后一个模块时,如果实现支持,建议使用 binding
控制标志来代替。
optional
模块会被执行,但其结果会被忽略。如果链中的所有模块都标记为 optional
,所有请求将始终被批准。
当服务器调用六个 PAM 原语中的一个时,PAM 会检索与该原语所属功能模块对应的链,并按链中列出的顺序依次调用每个模块,直到到达链的末尾,或确定不再需要进一步处理(无论是因为 binding
或 sufficient
模块成功,还是因为 requisite
模块失败)。请求只有在至少一个模块被调用并且所有非 optional
模块都成功时才会被批准。
请注意,虽然不常见,但在同一链中列出相同模块多次是可能的。例如,一个用于查找用户名称和密码的模块,可以多次调用,指定不同的目录服务器进行查询。PAM 会将同一链中同一模块的不同出现视为不同、无关的模块。
典型 PAM 事务的生命周期如下所述。请注意,如果这些步骤中的任何一个失败,服务器应该向客户端报告适当的错误消息并中止事务。
如有必要,服务器通过 PAM 独立机制获取裁判凭证——最常见的情况是通过 root
启动,或通过 setuid root
。
服务器现在执行客户端请求的任何服务——例如,提供一个 shell 给申请人。
传统的 PAM 策略文件是 /etc/pam.conf。该文件包含系统的所有 PAM 策略。文件中的每一行介绍了链中的一步,如下所示:
字段顺序如下:服务名称、功能名称、控制标志、模块名称和模块参数。任何额外的字段都被解释为额外的模块参数。
为每个服务 / 功能对构建单独的链,因此虽然相同服务和功能的行顺序是重要的,但列出服务和功能的顺序并不重要。原始 PAM 论文中的示例按功能分组配置行,Solaris™ 的默认 pam.conf 文件仍然这样做,但 FreeBSD 的默认配置将配置行按服务分组。两者都可以,任何一种方式都合理。
OpenPAM 和 Linux-PAM 支持一种替代配置机制,这是 FreeBSD 推荐的机制。在这种方案中,每个策略都包含在一个独立的文件中,文件名是应用该策略的服务名。这些文件存储在 /etc/pam.d/ 目录下。
这些每个服务的策略文件只有四个字段,而不是 pam.conf 中的五个:服务名称字段被省略了。因此,在 /etc/pam.d/login 文件中,你会看到如下行,而不是之前的 pam.conf 示例:
由于这种简化的语法,可以通过将每个服务名称链接到相同的策略文件来为多个服务使用相同的策略。例如,要为 su
和 sudo
服务使用相同的策略,可以按如下方式操作:
之所以可行,是因为服务名称是通过文件名确定的,而不是在策略文件中指定的,因此相同的文件可以用于多个不同命名的服务。
由于每个服务的策略存储在单独的文件中,pam.d 机制还使为第三方软件包安装额外策略变得非常容易。
正如我们在前面所看到的,PAM 策略可以在多个地方找到。如果相同服务的策略存在于多个位置,会发生什么呢?
理解 PAM 的配置系统是基于链的这一点至关重要。
服务名称通常是(但不总是)该语句适用的应用程序的名称。如果不确定,请参考各个应用程序的文档,以确定它使用的服务名称。
请注意,如果使用 /etc/pam.d/ 而不是 /etc/pam.conf,服务名称由策略文件的名称指定,并且在实际的配置行中省略,配置行从功能名称开始。
为了正确配置 PAM,理解策略的解释方式至关重要。
当应用程序稍后调用其中一个 PAM 原语时,PAM 库检索对应功能的链,并按配置中列出的顺序调用链中每个模块的相应服务函数。在每次调用服务函数后,模块类型和服务函数返回的错误代码将用于决定接下来发生的事情。以下表格适用于大多数情况,除了几个例外,我们将在下面讨论:
表 1. PAM 链执行摘要
binding
如果(!fail)则跳出;
-
fail = true;
required
-
-
fail = true;
requisite
-
-
fail = true; 跳出;
sufficient
如果(!fail)则跳出;
-
-
optional
-
-
-
如果在链的末尾,或者遇到 "break" 时,fail
为 true,则调度器返回第一个失败模块返回的错误代码。否则,返回 PAM_SUCCESS
。
第一个值得注意的例外是,错误代码 PAM_NEW_AUTHTOK_REQD
被视为成功,除非没有模块失败,并且至少有一个模块返回了 PAM_NEW_AUTHTOK_REQD
,此时调度器将返回 PAM_NEW_AUTHTOK_REQD
。
本节尚未编写。
本节尚未编写。
Making Login Services Independent of Authentication Technologies Vipin Samar. Charlie Lai. Sun Microsystems.
Solaris PAM主页*. Sun Microsystems。
这个简单的例子展示了 alice
使用 切换到 root
。
进程既是客户端也是服务器。
仲裁者是 root
,因此 是 setuid root
。
下面的例子展示了 eve
尝试发起到 login.example.com
的 连接,要求以 bob
身份登录,并成功登录。Bob 应该选择一个更好的密码!
客户端是 Eve 的 进程。
服务器是 login.example.com
上的 进程。
该策略适用于 sshd
服务(不一定仅限于 服务器)。
通过请求认证令牌并将其与存储在数据库中的值或从认证服务器获取的值进行比较来认证申请者。
建立账户凭证,如用户ID、组成员资格和资源限制。
验证请求的账户是否可用。
执行与会话设置相关的任务:在 utmp 和 wtmp 数据库中添加条目,启动 SSH 代理等。
执行与会话拆卸相关的任务:在 utmp 和 wtmp 数据库中添加条目,停止 SSH 代理等。
更改认证令牌,选择性地验证其是否足够难以猜测,是否未曾使用过等。
当服务器发起 PAM 事务时,PAM 库尝试加载在 调用中指定的服务的策略。该策略指定了认证请求应如何处理,并在配置文件中定义。这是 PAM 中的另一个核心概念:管理员可以通过简单地编辑文本文件来调整系统安全策略(在广义上理解)。
服务器调用 来初始化 PAM 库,并指定其服务名称和目标账户,并注册适当的会话函数。
服务器获取与事务相关的各种信息(例如申请人的用户名和客户端运行的主机名称),并使用 将其提交给 PAM。
服务器调用 来验证申请人。
服务器调用 来验证请求的账户是否可用且有效。如果密码正确但已过期, 将返回 PAM_NEW_AUTHTOK_REQD
而不是 PAM_SUCCESS
。
如果前一步返回 PAM_NEW_AUTHTOK_REQD
,服务器现在调用 强制客户端更改请求账户的认证令牌。
既然申请人已经正确地通过了认证,服务器调用 来建立请求账户的凭证。它能够做到这一点,因为它代表裁判行事,并持有裁判的凭证。
待正确的凭证被建立,服务器调用 来设置会话。
待服务器完成为客户端提供服务,它调用 来拆除会话。
最后,服务器调用 来通知 PAM 库它已经完成,可以释放在事务过程中分配的所有资源。
如 中所解释的,每一行 /etc/pam.conf 中包含四个或更多字段:服务名称、功能名称、控制标志、模块名称以及零个或多个模块参数。
功能是 中描述的四个功能关键字之一。
同样,控制标志是 中描述的四个关键字之一,描述如何解释模块返回的代码。Linux-PAM 支持一种替代语法,允许你为每个可能的返回码指定关联的操作,但应避免使用这种语法,因为它是非标准的,并且与 Linux-PAM 调度服务调用的方式紧密相关(与 Solaris™ 和 OpenPAM 的方式差异很大)。不出所料,OpenPAM 不支持这种语法。
当应用程序调用 时,PAM 库加载指定服务的策略,并为每个功能构建四个模块链。如果这些链中的一个或多个为空,则会用 other
服务的策略中的相应链进行替换。
第二个例外是 将 binding
和 sufficient
模块视为 required
模块。
第三个也是最后一个例外是 会运行整个链两次(一次用于初步检查,一次用于实际设置密码),在初步阶段,它将 binding
和 sufficient
模块视为 required
模块。
模块是最简单的模块之一,它对任何请求返回 PAM_AUTH_ERR
。它对于快速禁用某个服务(将其添加到每个链的顶部)或者终止 sufficient
模块链非常有用。
模块将其参数作为 PAM_TEXT_INFO
消息传递给对话函数。它主要用于调试,但也可以用来在开始身份验证过程之前显示消息,例如 "Unauthorized access will be prosecuted"。
模块将其第一个参数作为要执行的程序名,剩余的参数作为命令行参数传递给该程序。一个可能的应用是使用它在登录时运行一个程序来挂载用户的主目录。
模块
模块根据用户是否属于某个特定文件组(通常是 wheel
组,针对 )来接受或拒绝申请人。它主要用于维持 BSD 传统的 行为,但也有其他用途,例如排除某些用户组使用特定服务。
模块允许使用固定登录名的访客登录。可以对密码施加各种要求,但默认行为是只要登录名是访客账户的名字,就允许任何密码。此模块可以轻松用于实现匿名 FTP 登录。
模块
模块
模块
模块提供了一个账户管理原语的实现,强制执行 表中指定的登录限制。
模块在 /var/run/nologin 文件存在时拒绝非 root 用户的登录。这个文件通常由 在剩余时间少于五分钟时创建。
模块
模块是最简单的模块之一,它对任何请求返回 PAM_SUCCESS
。它作为占位符很有用,适用于那些本应为空的服务链。
模块
模块
模块仅在调用它的进程的真实用户 ID 为 0 时才返回成功。这对于非网络服务(如 或 )很有用,其中 root 应该自动有访问权限。
模块
模块仅在申请人的名字与目标账户的名字匹配时返回成功。它最适用于非网络服务,如 ,其中可以轻松验证申请人的身份。
模块提供了身份验证和会话服务。身份验证服务允许拥有密码保护的 SSH 秘密密钥的用户通过输入密码来进行身份验证。会话服务启动 并将解密的密钥预加载到其中。这个特性对于本地登录特别有用,无论是在 X 窗口(使用 或其他支持 PAM 的 X 登录管理器)还是在控制台。
模块
模块实现了传统的 UNIX® 密码身份验证,使用 获取目标账户的密码,并将其与申请人提供的密码进行比较。它还提供账户管理服务(强制执行账户和密码到期时间)以及密码更改服务。这可能是最有用的模块,因为大多数管理员都希望至少为某些服务保留历史行为。
以下是一个使用 PAM 的最小实现示例,基于 。请注意,它使用了 OpenPAM 特有的 对话功能,该功能在 security/openpam.h 中定义。如果你希望在使用不同 PAM 库的系统上构建此应用程序,则必须提供自己的对话功能。实现一个强健的对话功能相当困难;在 中提供的实现是一个不错的起点,但不应在实际应用中使用。
以下是一个最小实现的 ,仅提供身份验证服务。它应当能够与大多数 PAM 实现一起构建和运行,但如果可用,它会利用 OpenPAM 扩展:请注意使用了 ,这大大简化了提示用户输入密码的过程。
以下是一个大大简化版的 OpenPAM 。它是完全功能的,应该能让读者大致了解对话功能的工作方式,但它对于真实世界的使用来说过于简单。即使你不使用 OpenPAM,也可以下载源代码并适应 到你的需求中;我们认为它是一个足够健壮的 tty 导向的对话功能。
. The Open Group. 1-85912-144-6. 1997年6月。
. Andrew G. Morgan. 1999-10-06。
. Sun Microsystems。
Dag-Erling Smørgrav. ThinkSec AS。
Andrew Morgan。