分类: 网络与安全
2009-10-21 11:49:21
介绍
PAM(Pluggable Authentication
Modules)是一个好东西,在现代的Linux系统中已经必不可少了。以前也接触过,也修改过他的一些配置,不过都是依葫芦画瓢,没有仔细看过有关说
明文档。今天在linux-mag上看到了这样的文章,正好可以仔细阅读一番。介绍分为两篇,这篇是介绍PAM的基本常识和配置文件的规则,下一篇是利用
这些规则做一些联系。下面是阅读后的一些笔记:
PAM 配置文件
大部分现代Linux发行版本都使用/etc/pam.d/下面的文件来配置PAM。这个目录下的每一个文件是以程序提供的服务命名的,有些软件包
提供了缺省的PAM配置文件,你也可以自己选择配置,特别是采用不同的协议来实现认证手段,比如你可以使用本地认证工具来配置su程序,而采用LDAP的
方式来认证SSH登录。
配置文件的目的是用来创建一个或多个模块堆栈来为每一个程序服务。一个模块堆栈是一系列PAM模块,他们被调用以和特定的
程序进行交互。每一个模块执行一个特定的任务,比如为网络服务娇艳密码,坚持用户是否有新邮件。作为登录过程的一部分,模块也可以执行用来执行一些非认证
的任务,有时会显得比较方便。
/etc/pam.d/下的配置文件格式都是由很多行组成,每行有下面四个域:
management_group control_flag module[options]
每一个域都有其特定的含义,解释如下:
老系统通常使用单个/etc/pam.conf配置文件。这个文件的工作方式就等同于现在的/etc/pam.d/下的配置文件,除了每行开头需要一个引导入口来表示这个配置应用在哪个程序上之外。
因为这个基本都很少见了,所以这里也就不详细介绍了。
PAM模块堆栈(PAM Module Stacks)
一个PAM配置文件有一系列模块堆栈组成,他们安装相同的管理组集合在一起。当一个程序通过管理组来寻求处理时,PAM依次调用每一个模块,最终根据每一个模块的返回值以及与此相关连的control_flag域来返回一个代码。
一个典型的认证堆栈类似下面这样
auth required pam_env.so
auth required pam_unix.so likeauth nullok
这个配置我文件意味着pam_env.so和pam_unix.so将依次调用,当PAM被要求认证用户的时候。
每一个模块有自己特定的任务来执行,后面会描述。
堆栈里的每一个模块返回成功或者失败代码,这些代码和模块的control_flag表示合作,用来最终返回一个结果,下面给出control_flag的总结:
模块控制标识以及返回值表
控制标志 成功结果 失败结果
----------------------------------------------
requisite 堆栈继续执行 堆栈立刻以失败方式中断
required 堆栈继续执行 堆栈继续执行,但是以失败方式中断
sufficient 堆栈立刻以成功方式中断,除非之前模块失败了 堆栈继续执行
optional 堆栈继续执行 堆栈继续执行,只有当模块丢失或者给出一个非决定性结果才失败。
理想情况下,不同模块的结果不会发生冲突,如果冲突了,那么规则能拒绝。比如,假设你想配置一个计算机使用本地密码数据(用pam_unix.so表达)或通过Kerberos(用pam_krb5.so表达)服务来认证用户。你可能会这样写配置文件。
auth required pam_env.so
auth required pam_unix.so likeauth nullok
auth sufficient pam_krb5.so try_first_pass
(这个堆栈也包括了pam_env.so,pam_deny.so,现在你可以忽略)。这个例子指定pam_krb5.so是sufficient 的。其目标是当Kerberos服务宕了胡或者帐号没有存储在这里时,通过本地数据库也可以登录。 但问题就出在pam_unix.so的控制标识required上。他表示用户必须通过本地认证。也就没有达到我们预期的目的,因为Kerberos模块 没有起到作用。为了达到期望目标,我们应该把所有的模块的控制标志改成sufficient的。
auth required pam_env.so
auth sufficient pam_unix.so likeauth nullok
auth sufficient pam_krb5.so try_first_pass
但是,问题又来了,当一个模块成功后,他会忽略其后的任何一个模块。在这个例子中,不会有这个问题,但是一些缺省的配置会在认证堆栈中包括额外的模块。有时候忽略接下里的模块不是问题,但有时候却是(下一篇会提到多认证方式)。
大部分使用PAM的服务要求多堆栈的帮助。典型的就是登录服务,他使用auth,account,session堆栈,有时候甚至定义至少一个password模块。
通用PAM模块(Common PAM Modules)
PAM提供了非常多样的模块来帮助实现很多功能特征,另外,一些程序也提供了自己的PAM模块来扩展PAM功能。大部分模块可以在一个或多个管理组
中工作。特命执行一项任务当他们作为一个堆栈的一部分被调用时同时也可以执行另外一项任务当作为另外一个管理组堆栈的一部分被调用时。
下面的表给出了一些重要的PAM模块的解释,虽然这个表远不够完整。
通用PAM模块
模块名
称
管理组
效果
-------------------------------------------------
pam_unix.so auth, account, session, password 基于/etc/passwd和/etc/shadow认证用户
pam_unix2.so auth, account, session, password pam_unix.so变体,提供额外的一些功能
pam_securetty.so auth 阻止root登录,除非在/etc/securetty文件中设置PAM_TTY环境变量
pam_env.so
auth
从/etc/security/pam_env.conf 文件中设置环境变量
pam_mail.so auth,session 根据指定的dir=diretory选项来检查邮件和给用户提供检查结果。
pam_lastlog.so auth 显示用户最后登录的消息
pam_deny.so auth, account, session, password 总是表示失败。
pam_limits.so
session
用来设置用户对CPU,内存等资源的消耗限制,配置文件
是
/etc/security/limits.conf
pam_mkhomedir.so
session
为用户创建一个家目录,如果它已经不存在了。这为那些通
过
网络登录服务器验证而登录的用户创造了便利。
pam_pwcheck.so password
根据/etc/login.defs文件规则来检查用户密码,以便用户在
修
改密码时提高安全度。
pam_cracklib.so password 根据字典来检查密码,以确保密码至少显得显得比较安全和随机。
pam_stack.so auth, account, session, password 调用在system-auth的堆栈,返回其结果。
如果你仔细研究你的PAM配置文件,你应该可以看到上面的绝大部分模块。你也可以来确定这些模块的效果,比如我增加pam_mail.so模块看是否能达到上面说的作用。
通用模块选项(Common Module Options)
大多数PAM模块接受可选项,而这些可选现对很多模块是非常有帮助的。更多的通用的可选现包括:
大部分情况下,你不用管这些选项,尽管有时你可以想使用他,特别是在增加认证工具的时候。比如try_first_pass 或 use_first_pass经常用来和auth堆栈中的第二个认证模块联合起来。
实践
具有调用PAM的应用程序可以用来认证用户,登录服务,屏保,密码工具,可以和其他任何认证工具交互。
要实现这些功能,你需要配置或者是标准的/etc/passwd文件,或者是LDAP服务,或者是windows域控制器,或者是其他任何PAM支持的东西。
虽然,配置标准的PAM看上去比较简单,但是PAM却是非常灵活的,他可以把一个正在工作的系统折腾的不知所以然,所以我们在配置的时候还是要格外小心。接下来,希望给出一些例子,结合上一篇文章,更加全面的了解PAM。
在开始之前,你必须意识到修改PAM文件会有潜在的风险,如果修改不当,有可能无法登录,因此修复起来比较麻烦。所以修改之前备份相关的配置文件, 还有即使任何时候保留一个root已经登录的终端,防止出现问题后,还有一个登录终端可以操作。有点罗嗦,但绝对不是危言耸听。
一般性PAM配置
大部分Linux发行版本的PAM配置文件都在/etc/pam.d目录下,
每一个文件--包括cups,gdm,imap,login,passwd,samba,sshd,su,sudo,system-
auth,useradd,xdm,xscreensave等--指定一个或多个PAM堆栈来告诉PAM如何为特定的程序或服务认证用户。
一个重要的文件便是/etc/pam.d/system-auth,他是pam_stack.so模块的标准控制文件,后面给给出描述。
现在给出一个PAM配置文件的例子,内容如下(list one):
#%PAM-1.0
auth requisite pam_unix2.so nullok
auth required pam_securetty.so
auth required pam_nologin.so
auth required pam_env.so
auth required pam_mail.soaccount required pam_unix2.so
password required pam_pwcheck.so nullok
password required pam_unix2.so nullok use_first_pass use_authtoksession required pam_unix2.so none
session required pam_limits.so
上面这个文件来自SuSE
Linux的/etc/pam.d/login文件。这里面的有些模块只是为了方便而不是去执行认证的任务。举个例子,上面的auth堆栈中调用的
pam_mail.so模块并不执行认证操作,pam_mail.so只是检查新邮件然后告诉当前的用户。
上面例子中大部分的模块在上一篇文章中
都已经简单介绍了。不过一个新的模块pam_nologin.so似乎没有介绍。pam_nologin.so模块检查/etc/nologin文件。如
果这个文件存在,这个模块将会拒绝所有的用户登录,并显示/etc/nologin的内容,但是root除外(root总是有例外和特权)
上面例子的一个关键点是pam_unix2.so模块是做为requisite模块在auth堆栈中被调用的,而剩余的模块采用的是
required。上次的帖子说道,如果是requisite类型的模块失败(比如输入错误的密码),PAM将绕过这个堆栈后面剩余的模块。与此相反,一
个required模块在这种情况会要求剩余的模块继续执行,尽管整个堆栈最终仍然是以失败退出。
那么上面的这个例子,就会导致这样的一个情况发生,如果用户输入错误的密码,非认证的auth模块将会忽略,这有时候是有用的,万一这些模块中的某一个做了不应该做的事情,在一次认证失败的事情驱动过程中。虽然不是所有的认证堆栈都会这样预防。
有些发行版本的/etc/pam.d/login文件的结构可能和我们上面举的例子完全不同。许多发行版本是调用pam_unix.so而不是 pam_unix2.so,另外模块的调用顺序也不同。有些发行版本更喜欢使用required控制方式,但是更常见的是requisite。
使用pam_stack.so模块
许多发行版本会大量使用pam_stack.so模块,
下面这个例子显示了一个典型的login配置文件是如何调用这个模块的(来自Gentoo)(list two)
#%PAM-1.0auth required pam_securetty.so
auth required pam_stack.so service=system-auth
auth required pam_nologin.soaccount required pam_stack.so service=system-auth
password required pam_stack.so service=system-auth
session required pam_stack.so service=system-auth
session optional pam_console.so
这个例子采用的是调用pam_stack.so模块而不是pam_unix.so模块或pam_unix2.so模块,通过service选项将配 置文件的名字传递给该模块(pam_stack.so)。这个传递过去的配置文件(system-auth)的内如如下(list three):
#%PAM-1.0
auth required pam_env.so
auth required pam_unix.so likeauth nullokaccount required pam_unix.so
password required pam_cracklib.so retry=3
password required pam_unix.so nullok md5 shadow use_authtoksession required pam_limits.so
session required pam_unix.so use_first_pass
例子中pam_stack.so模块返回成功与否依赖于模块定义的配置文件的返回值(典型的是system-auth)。如果你仅仅只是配置一个服 务,那么这种方式可能没有什么好处。但是显然,你的系统肯定运行比较多的这些服务器程序,因此采用这种模式的好处就显而易见了:你仅仅只需要修改一个配置 文件就能使整个调用该配置文件的服务发生改变。
例子2中(/etc/pam.d/login)在调用了pam_stack.so模块后,调用了pam_nologin.so模块。 pam_nologin.so模块阻止非root用户登录,如果/etc/nologin文件存在,你可能考虑把它当成一个安全措施而应用在shell登 录服务上,但是不会应用在POP邮件获取上(仅举这个例子)。因此把这个调用放到login服务的文件本身里比放到auth-system文件里更有意 思。
增加网络认证工具
一种典型的修改PAM认证方式的案例是增加一些模块,使得系统能够使用类似LDAP服务或者Windows 域控制器服务等来认证用户。(我的这篇""里就是用的这个方法。)
为了做到这点,首先你要你的系统是否使用pam_stack.so模块。如果没有,你可以直接修改登录服务所使用的配置文件来增加新的认证工具。这
意味着你可能需要修改一个到几个文件;反过来,如果使用pam_stack.so模块,那你可以修改服务对应的配置文件来达到你的目的,或者你修改
/etc/pam.d/system-auth 来改变整个认证服务。
为了增加一个新的认证模块,你必须增加一个对该模块的引用
(reference)到相关的堆栈中去。很明显,auth堆栈是肯定需要修改的,而account堆栈对认证来说也是重要的。如果你准备修改/etc
/pam.d/system-auth文件,你也应该增加新认证模块的引用到session堆栈和password堆栈中,作为可选方案,你可以修改这个
新的认证工具的配置文件,使得他本身能够从新协议中使用这些服务,比如为passwd工具服务的passwd文件。
一个大问题是,尽管你知道如何修改堆栈。但是修改的细节而因为不同的系统版本,不同的配置方式而不同。一般来说,你应该做这么几件事情:
还是举个例子,考虑例子3(list three)的auth堆栈。你如果想增加一个LDAP服务来认证用户,那么你应该这样加入pam_ldap.so模块到堆栈中:
auth required pam_env.so
auth sufficient pam_ldap.so
auth required pam_unix.so likeauth nullok try_first_pass
因为这是一个system-auth配置方式,他将影响所有依赖PAM认证的程序。新的系统首先会去尝试LDAP认证,如果成功,pam_stack.so模块成功,pam_unix.so模块不会被调用;如果LDAP认证失败,pam_unix.so将被调用,他的结果决定了pam_stack.so模块的成功与否。
任何情况下,你都应该记得修改auth堆栈和account堆栈,为了那些主要使用PAM认证的工具(login,pop,sshd,xdm,gdm,etc).其他工具,比如passwd配置文件,还要求有别的改变或者说出了除此之外的其他改变(后面会提到)。
同时你也要记得改变那些并不是PAM需求的配置文件,最明显的,如果你使用了网络认证工具,你可能需要修改名字服务交换(Name Service Switch,NSS)配置文件,他校验帐号的存在性。至于如何配置就依赖于你使用的网络认证工具了。
增加有用的登录模块
另外一种类型的PAM修改方式根本就不会改变登录认证,相反,他调用增加的模块来执行额外的任务。上次的帖子总结了这些模块中的几个,他们会执行各种有用的任务,比如创建用户主目录,执行密码强度检查。
增加这样的模块一般都简单明了:把需要的模块加入到已经存在的堆栈中去。(确定放到了正确的堆栈上,有些模块仅仅在某种堆栈上工作。)在大部分情况 下,把这些模块配置为required类型。这些模块不会设计成用来授权或者拒绝用户访问系统,他们通常情况下返回成功代码,因此他不会导致认证出现失 败。
应该把模块加在正确认证模块之前还是之后呢?大部分情况下,两种方式都工作,至少如果你仅仅获得一个认证模块,把这些附加模块放到认证模块的前面会更好。
Password堆栈的特殊考虑
密码服务,比如passwd程序,值得多关注一下。典型的,当增加一个新的认证方式时,你必须在/etc/pam.d/passwd文件中修改 auth,account,password堆栈。auth和account堆栈设计为required用来获得对密码设置的访问,即,他们控制了你输入 存在的密码的认证方式。
改变password堆栈有点不同,具体来说,通常你增加新的一行来提供新的服务,比如对auth,account堆栈的修改,但是你应该把新的服务设置为optional而不是sufficient.
这种配置方式的结果是即使某个用户是在本地定义而不是在远程定义,也会返回成功。
以前的默认的行(pam_unix.so or pam_unix2.so),你应该保留不动(除了增加use_first_pass选项外,如果默认没有的话);可能是觉得required标志的规则可能会导致问题发生,当帐号不再本地存在的时候,实际上,它不会。