The Linux-PAM Administrator Guide (1)

The Linux-PAM Administrator Guide

原文地址: http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/Linux-PAM_SAG.html
作者:Andrew G. Morgan, morgan@linux.kernel.org
翻译:孙国清(Thomas Sun),thomassun@yeah.net
DRAFT v0.71 1999/11/8 这个文档所涉的是系统管理员须知的关于Linux-PAM库的知识. 它涉及了设置PAM的正确语法并讨论维护一个可靠系统的正确的策略.
1. 介绍

Linux-PAM (Linux下的可插入式认证模组) 是一套共享函数库,允许系统管理员来决定应用程式如何识别用户.

换句话说,就是用不着(重写和)重新编译一个(支援PAM的)程式,就可以切换它所用的认证机制. 你可以整个的升级你的认证系统而不用去管应用程式本身.

传统上,当一个应用程序有身份识别的需求,它就不得不把某一种验证算法写进去. 例如,就传统的UN*X系统而言,核对使用者身份的方法就是要求用户输入正确的密码. 这密码,除了开头的两个字符作为"salt",剩下的是加密过的(经由crypt(3)). 接著用户被验证这个加密过的密码是否与他在密码档(就是/etc/passwd文件)中他的那笔记录的第二栏相符.在这样的系统里,绝大部份权限的授予是基于这种单一的认证机制. 权限决定于个人的识别码(uid)和不同群组的成员.服务和程式是否可用由个人和群组的识别码决定.传统上,群组关系经由/etc/group文件中的记录来赋予.

不幸的是,随着计算机速度的不断提高,再加上满世界的关于网路计算的介绍,使得象这样曾是安全的验证机制,变得易受攻击了.面对这样的现实,新的验证方法正在持续的开发中.

Linux-PAM项目的目标是把赋权部分的开发以可靠的合适的鉴定模式从软件中分离出来。这目标已经通过提供一组库函数实现了,应用程序可以用这些函数来请求验证某个用户。这个 PAM 由特定的系统文件配置,/etc/pam.conf (或者是在/etc/pam.d/里的一系列配置文件)  以经由特定的可用的认证模组来鉴定某个用户的请求。 这些个模组通常位于/usr/lib/security目录并且以可动态加载的目标文件的形式出现 (参见 dlopen(3)).
2. 文中的说明

在继续阅读之前, 请记住本文假定提到的文件位于默认的目录。这个默认的目录我们遵循RFC(RFC-86.0,见 bibliography) 中的约定。如果你正用一个支持PAM但是却选择以不同的方式发布这些文件的发布版的Linux(或是其他的OS) (Red Hat就是这样的发布版),  那你从文章中直接拷贝例子的时候就要注意一下了.

举个例子, where it is explicit, 本文假定PAM可加载目标文件(就是 模块)位于这个目录:  /usr/lib/security/. 可是, Red Hat Linux, 为遵循Linux文件系统标准(the FSSTND),把这些文件放在/lib/security. 在使用本文的例子时请小心的做一些转换的工作。
3. 概观

For the uninitiated,我们开始于考虑一个例子。我们来说说一个应用程序提供一些服务给用户; login 就是这样的程序。  Login 做两件事,它首先确认提出请求的用户正是他们自己,第二步提供给他们所请求的服务: 就login而言服务即是一个命令行外壳(command shell)(如bash,tcsh,zsh之类。)以这个用户的身份去跑。

传统上,前一个步骤通过login提示用户输入密码然后确认系统是否同意登入;接着确认(就系统而言)用户确实是提出要求的那人。这类工作就是Linux-PAM的典型应用。

从应用程序员的角度看(在这个例子里就是写login程序的人), Linux-PAM处理认证的工作——确认用户的身份。

Linux-PAM的弹性在于,你,系统管理员有权来决定实施怎样的验证方案。你有权来设定你的系统里的任何支持PAM的应用程序的验证方案。就是说,你可以将验证方案设计成单纯的信任>;任何人(pam_permit)到像偏执狂似的通过视网膜扫描,声音识别和一个密码!

举例说明你所面对的弹性,考虑以下情形:系统管理员(家中的父母)希望提高他的使用者(孩子们)的算术能力。她可以设置他们喜欢玩的 “Shoot ’em up”(游戏,当然得支持PAM)通过提问小于12的一对随机数的乘积的办法来进行认证。很明显不管游戏本身如何,他们会很快学会乘法表。等他们再大些,认证可以升级到包括多位除法!(译者:我已经准备用这种办法来教我宝贝女儿学加减乘除了。)

Linux-PAM处理四种独立的(管理)工作。它们是: 认证管理; 帐号管理; 会话期间管理;和密码管理。  The association of the preferred management scheme with the behavior of an application is made with entries in the relevant Linux -PAM configuration file. 管理的功能由配置文件中指定的模块 来完成。这文件的语法在 below部分讨论。

下面的插图描述了Linux-PAM的整个组织结构。

By way of explanation, 图的左边表示一个应用程序:X. 这应用程序有和Linux-PAM 库的接口并且在认证方面没有什么特别之处. Linux-PAM 函数库 (图的中部) 查询PAM配置文件的内容并且装入适用于程序 X 的模块. 这些模块进入四个管理组(  图的中下部)中的一个,并且以它们出现在配置文件中的顺序堆叠起来 . 这些模组由Linux-PAM呼叫后,为应用程序执行不同的认证工作 . 需要用户提供或提供给用户的文本信息,可以通过使用应用程序提供的conversation函数来交换.

3.1 Getting started

以下段落由Seth Chaiklin供稿:

到现在为止,我们描述了PAM如何工作在一个理想世界里,
在这儿所有应用程序都被正确编写.
然而,到此时(1998年10月), 这距离现实还太远.
因此,在你试图把PAM用于你的系统时,还得考虑一些实际因素.

Why bother, is it really worth all the trouble? 

如果你运行Linux作为单用户系统, 或者在一个所由用户都可信任的环境, 那么用PAM就没什么实际优势.

Ed: 事实上还是有一个好处, 你可以令认证变哑, 就象没有任何认证….像Win95.

在网络环境里, 很显然,关于用户你必需多想点, it is clear that you need to think a little more about how users etc., are authenticated:]

如果运行Linux作为服务器, 提供一些不同的服务 (e.g., 经由密码控制的WWW区域限制, PPP),
那PAM就有一些实际的和有趣的价值. 尤其是, 通过使用使用模块, PAM 能够使
一个程序经过几个不同的密码库来查找, 哪怕那个程序没有专为那密码库写的代码.
下面有一些例子Here are some examples of the possibilities
that this enables.

   o  Apache 有一个模块提供PAM服务.  认证特定的目录权限可以让PAM来控制,
      这意味着,所有PAM能调用的模块都可以被Apache使用,包括RADIUS,NIS,NCP
      (NCP用来经由Novell的密码库认证).

   o  pppd 有一个PAM化了的版本(来自 RedHat)  现在有可能用一系列的数据库来验证
      ppp用户. 作为对基于Linux的密码库(/etc/passwd,/etc/shadow)的扩充, 你可以
      用PAM模块来进行基于Novell密码库或NT(NTLM)密码库的认证.

   o  以上两则例子可以有组合应用.想象一下你办公室/部门的用户已经经过用户名/密码
      认证登入Novell或NT.如果你想要他在Linux下的应用也用相同的用户名/密码
      (为PPP登入,web服务,或者只是普通的shell登入),你可以通过PAM进行基于这些已
      存在的数据库(译者注:Novell的或者NT的)的用户认证,而无须在Linux和LAN服务器里
      各自维护独立的数据库.

我可以让所有需要有用户认证的程序都来用PAM吗?

有的可以,有的不行.可以的是那些你能得到源代码,并且可以加入适当的PAM函数的程序.
不行的是那些你无法得到源代码,并且可执行程序没有加入PAM的功能.

也就是说, 如果一个程序打算要用PAM,那么它必须在程序里直接的包含PAM函数.
不这样做就不可能用PAM.

我怎么知道程序是否已经含有PAM的代码了呢?

一个快速的(但不总是可靠)的方法是执行ldd <程序>;
如果 libpam 和 libpam_misc 不在程序所需的函数库之列,那么它将不会用PAM.
然而,这两个函数库还是可能已经包含进程序了,不过问题依然存在,因为把PAM
写死在程序不会如你期望的工作.所以一个更可靠的方法是做一下的测试:

在/etc/pam.d目录里,需要为程序设有一个配置文件.具体的文件名是写死在程序里的.
通常和程序名一样,但却不总是这样.为举例说明,假设程序名字叫"pamprog",配置文件
是/etc/pam.d/pamprog.

在/etc/pam.d/pamprog里写这两行:

auth    required  pam_permit.so
auth    required  pam_warn.so

现在试着执行pamprog. 配置文件的第一行是说所有用户都被允许. 第二行会在你的
syslog 文件(或者其它你的syslog会写的文件) 里写一个warning.
如果这测试是成功的,那么你就知道你有一个"懂"pam的程序,并且你可以开始更有趣的工作:
决定如何在你的/etc/pam.d/pamprog里堆彻PAM模块.

4. Linux-PAM的设定档

Linux-PAM 给系统管理员提供了相当大的弹性来设定系统里程式的权限赋予. 由PAM控制的系统安全的本地配置可以包含在以下两个地方:  或者是一个单一的系统文件: /etc/pam.conf; 或者是/etc/pam.d/ 目录下的文件. 本章我们来讨论这些文件的语法和一般的选项.
4.1 设定档的语法

请注意, 在这些文件里 Linux-PAM 特有的符号是不区分大小写的. 而模块的路径,是大小写敏感的,因为它标识的是Linux下的文件的名字. 而任何模块参数的大小写分别由各个模块定义.

除以下的行之外,为系统管理员方便,还有两个特殊的字符: 注解由’#’开头,结束于行结束,另外,模块的描述行可以以 ‘\’脱字符延续到下一行.

通常/etc/pam.conf里的每一行有以下格式:

service-name   module-type   control-flag   module-path   arguments

接下来,我们来解释每个栏位的意思.另一种(也是常被采用的)设置Linux-PAM 的方法是通过/etc/pam.d/里的文件来实现.在我们解释完上面的那行之后, 我们就来讨论这种方法.

service-name

    这笔记录相关的服务名称.通常这服务名称是特定应用程序的名字.比如, `ftpd’, `rlogind’ 和 `su’ 这些. .

    有一个保留的服务名称,它是用来定义默认的认证机制的. 它就是`OTHER’,大小写无关.注意,当已经有为指定服务定义了模块,那么 `OTHER’ 记录会被忽略.
module-type

    模块的四种(目前是)类型.这四种类型是:

        * auth; 这种模块类型确定有关用户认证的两方面. 第一,它确认用户就是他们自己,这通过指示应用程序提示用户输入密码或者其它证实身份的方法.第二,这类模块会赋予成员资格 Secondly,  the module can grant group membership (independently of the /etc/groups file discussed above) or other privileges through its credential granting properties.
        * account; 这些模块处理非认证级的帐号管理. 典型的用法是基于一天的不同时间段来限制/允许访问某服务,当前可用的系统资源 (最大用户数)或者限制特定用户—‘root只能从控制台登录.
        * session; 首先, 这类模块和一系列动作有关,指在用户得到/失去服务时要做的事. 这包括记录用户的登录/登出,挂载必须的目录等等.
        * password; 这最后一种类型在更新用户的认证标志时需要. 通常,各个基于"质问/回答"(译注:指传统的用户名/密码的认证方法)的认证方法(auth)有一个对应的此模块.

control-flag

    控制符用来指示当某一模块返回成功或失败时PAM如何动作. 既然模块可以被堆叠 (同种类型的模块按先后顺序执行,一个接一个), 控制符决定每个模块的重要程度.应用程序不会直接接收’/etc/pam.conf’ 里列出的每一个模块的成功或失败的结果. 相应的是,它只从 Linux-PAM接收一个 成功 或 失败 的结论. 这些模块的执行顺序就是它们在 /etc/pam.conf里的记录的顺序;排在前面的记录在排在后的记录之前被执行. 在Linux-PAM 0.60版本里, 这个control-flag 可以有两种语法来定义.

    简单一些(也是过去的)语法是用一个限定词指示相关模块的重要程度. 有四个关键字: required, requisite, sufficient 和 optional.

    Linux-PAM 将这四个关键字解释为:

        * required; 需要的,这表明此模块返回成功值对于整个module-type的成功是必要的. 此模块的返回失败并不会传回给用户直到剩下的模块(同样module-type)都执行过.
        * requisite; 必要的,类似 required, 只不过, 当这类模块返回失败时,整个控制会立刻回到应用程序.  返回值同第一个 需要的 或 必要的模块返回的失败. 注意,这标志可以用来防止  required or requisite module to fail. Note, this flag can be used to protect against the possibility of a user getting the opportunity to enter a password over an unsafe medium. It is conceivable that such behavior might inform an attacker of valid accounts on a system. This possibility should be weighed against the not insignificant concerns of exposing a sensitive password in a hostile environment.
        * sufficient;  充分的,这模块返回的成功会被认为已经 充分满足Linux-PAM 库确认这类模块 (module-type)是成功的条件. 如果没有先前的requisite 模块返回了失败,那么不再会有其它’堆叠’ 的模块被呼叫. (注意,  这种情况下,随后的requisite 模块就不会 被呼叫.). 这模块返回的失败不会看作是致命的错误而至影响应用程序从这module-type  得到成功的结果.
        * optional; 可选的,正如这名字一样,此 control-flag 致使模块对最终的成功或失败的结果不会产生决定性的影响. 一般,Linux-PAM 在确定整个模块堆成功活失败时忽略这模块. 然而, 当从之前或后续的模块得不到明确的成功或失败的结果时,这模块将决定返回给应用程序的状态. 后种情况会出现在比如当其它模块返回PAM_IGNORE 时.

    更复杂(新)的语法则更明确,使管理员有更多的控制用户认证的空间. 这种控制符以方括号包含,由一系列的value=action 对组成:

    [value1=action1 value2=action2 …]

    这里的 valueI 有以下值 return values: success; open_err; symbol_err;  service_err; system_err; buf_err; perm_denied; auth_err; cred_insufficient;  authinfo_unavail; user_unknown; maxtries; new_authtok_reqd; acct_expired;  session_err; cred_unavail; cred_expired; cred_err; no_module_data; conv_err;  authtok_err; authtok_recover_err; authtok_lock_busy; authtok_disable_aging;  try_again; ignore; abort; authtok_expired; module_unknown; bad_item; conv_again;  incomplete; 和 default. 最后的 (default) 被用来定义当没有明确定义时的默认动作.

    actionI 可以是一个正数或者是以下标识: ignore; ok; done; bad; die; 和 reset. 当以一个正数J 作为action 时,它的作用是指示以下J个模块将被跳过. 通过这种手段, 管理员可以开发出适度复杂的模块堆叠,它以许多不同的路径执行. 至于以何种路径则决定于某个模块的反应.

        * ignore – 此类模块的返回状态将不会影响应用程序所得到的返回值.
        * bad – 这表示相应的返回值将被认为是模块失败. 如果此模块是堆叠中的第一个失败的模块, 它的状态值将作为整个堆叠的状态.
        * die – 和 bad 相同,不过会终止整个模块堆叠,PAM立即返回到应用程序.
        * ok – 这告诉PAM 管理员要让此返回值直接作用于整个模块堆叠的返回. 换句话说, 如果堆叠的原先状态会导致返回  PAM_SUCCESS, 这模块的返回值将会覆盖这结果. 请注意:如果堆叠的原先状态保存着一些意为模块失败的值,这’ok’将不会用来覆盖那样的值.
        * done – 和 ok 一样, 不过会终止整个模块堆叠, PAM立即返回到应用程序.
        * reset – 清除储存模块堆叠状态的内存并且重新开始下一组堆叠.

    来体会一下这种新语法的强大之处, 从 Linux-PAM-0.63起, 客户插件代理的概念被引进. 这多少使PAM支援从C/S应用程序固有的传输协议进行机器-机器的认证成为可能.(?) 通过 “[ … value=action … ]” 的语法, 使应用程序对支援binary prompt?的客户端采用binary prompt?, 而对旧有的客户端则向后兼容的采用另一种认证模式. 够灵活吧?
module-path

    是可动态加载目标文件的路径; 也即可插入式模块 本身. 如果路径的首字符是 `/’, 那它被作为一个完整路径. 如果不是这样, 那模块的路径则会加上默认的模块路径: /usr/lib/security (见 above).
args

    args 是一组传给模块的参数. 类似一般Linux的指令的参数. 通常, 合法的参数是可选的并且是模块特有的. 无效的参数会被模块忽略, 可是, 当抓到一个无效参数, 模块被要求写一个错误信息到 syslog(3). 下一节有一个一般 选项的列表.

设定档里如果有任一行有格式错误, 那通常会导致认证流程失败. 相应的错误信息会通过呼叫syslog(3)写入系统日志.
4.2 基于目录的设定

从0.56版开始有比单个配置文件更灵活的做法, 可以通过/etc/pam.d/里的文件来配置libpam. 这种方法,/etc/pam.d/里的文件名和服务名(service-name)相同(小写): 这是各服务的个性化设置档.

Linux-PAM 可以编译成两种模式之一. 首选的模式是使用/etc/pam.d/ 或/etc/pam.conf设定,但不是同时两者皆可.也就是说, 如果</etc/pam.d/目录存在那么libpam仅使用这目录里的文件当作设定档. 当/etc/pam.d/不存在时就用 /etc/pam.conf. 另一种模式是(是RedHat4.2以上版本的模式)按顺序同时用/etc/pam.d/和 /etc/pam.conf. 在这种模式下,/etc/pam.d/里的设定会覆盖/etc/pam.conf.

/etc/pam.d/里的文件的语法和/etc/pam.conf相似,由以下格式组成:

module-type   control-flag   module-path   arguments

唯一的差别是service-name不再出现.service-name是给定的配置文件名. 例如,/etc/pam.d/login 里包含了login的配置.

此种设置方法比起处理单个文件有很多优点. 列举如下以帮助读者来决定采用何种方案:

    * 遗漏应用程序的配置的机会比较小. 当手工编辑设定档时也少设定一个栏位.
    * 更易于维护. 配置某个应用程序不会有妨碍到别的应用程序的危险.
    * 建立不同的服务的配置文件为到某个文件的符号连接也是可能的. 这使保持不同的应用程序采用一致的系统策略变得容易些.  (It should be noted, to conserve space, it is equally possible to hard link a number of configuration files. However, care should be taken when administering this arrangement as editing a hard linked file is likely to break the link.)
    * 更快的配置文件解析,只有和此服务相关的条目才会解析.
    * 采用文件系统的文件权限可以限制个别Linux-PAM设定档的读权限.
    * 包管理变得更简单. 每次当有新程序安装时,只需要附上/etc/pam.d/xxxxxx.

4.3 通用的可选参数

一下是一些基本所有模块都识别的可选参数.参数(包含这些)通常是可选的.

debug

    呼叫 syslog(3) 函数在系统日志里记录下debug信息.
no_warn

    指示模块不要丢warning信息给应用程序.
use_first_pass

    这模块将不会提示用户输入密码,它可以得到前面输入过的密码(从上一个auth模块)并且用它.如果那密码不正确,那么用户就不被认证.(这模块只对auth和password模块有意义.)
try_first_pass

    这模块会先拿前面输入过的密码来认证(从上一个auth模块).如果通不过认证,就提示用户输入密码.(这模块只对auth模块有意义.)
use_mapped_pass

    这参数目前没有任何Linux-PAM发布的模块支援,因为可能会违反美国加密软件出口限制.在美国国内,模块开发者当然可以自由的实现它(他国的开发者也一样).  For compatibility reasons we describe its use as suggested in the DCE-RFC 86.0, see section bibliography for a pointer to this document.

    use_mapped_pass 指示模块拿前一个模块生成的认证明文来产生一个加/解密的的键 ,以这键来安全的存储/取得此模块所需的认证标识.  In this way the user can enter a single authentication token and be quietly authenticated by a number of stacked modules. Obviously a convenient feature that necessarily requires some reliably strong encryption to make it secure. This argument is intended for the auth and password module types only.
expose_account

    通常模块暴露用户帐号的有关信息不是个安全的策略. 有时象用户名,用户的家目录,首选的shell这些信息可被用来攻击这帐号. 可是,这些信息不被视为威胁:在一个可信的环境里当要求输入密码时显示用户的全名, 这被叫做"易用性".expose_account是个标准的模块参数,当管理员觉得合适(译注: 指暴露用户帐号的信息)时可以使模块和帐户信息少些离散.

4.4 设定档的范例

在这章,我们给出一些可以出现在Linux-PAM的设定档里的条目的范例. 作为第一次尝试配置你的系统,你可能会得的比实现它们更糟糕.
默认策略

对于关键的系统,最好是有个适度安全的OTHER条目.以下是个"偏执狂"的设定 (以此开始不是个坏想法!):

#
# default; deny access
#
OTHER   auth     required       /usr/lib/security/pam_deny.so
OTHER   account  required       /usr/lib/security/pam_deny.so
OTHER   password required       /usr/lib/security/pam_deny.so
OTHER   session  required       /usr/lib/security/pam_deny.so

Whilst fundamentally a secure default, this is not very sympathetic to a misconfigured system. For example, such a system is vulnerable to locking everyone out should the rest of the file become badly written.

pam_deny (在后面的章节里解释)不够复杂.比如,它被呼叫时不会记录任何信息, 如此除非用户告诉管理员说他不能执行某应用程序,管 …

This entry was posted in PAM and tagged , . Bookmark the permalink.