Linux系统是一个多用户、多进程的,并且,它提供了众多的系统和网络服务给用户使用。因此,从应用角度来说,它不可避免地需要对大量的应用及其用户进行安全认证,只有通过了安全认证的用户才能合理、合法地使用相应的系统和网络服务。PAM机制是一个非常成熟的安全认证机制,可以为Linux多种应用提供安全、可靠的认证服务。本文将对PAM机制的原理、配置及其应用进行详细介绍,用户可以高效地使用该技术保证Linux系统的安全。
1 PAM认证机制简介
为安全起见,计算机系统只有经过授权的合法用户才能访问,在这里如何正确鉴别用户的真实身份是一个关键的问题。所谓用户鉴别,就是用户向系统以一种安全的方式提交自己的身份证明,然后由系统确认用户的身份是否属实的过程。换句话说,用户鉴别是系统的门户,每个用户进入到系统中都必须经过鉴别这一道关。
嵌入式认证(PAM)机制采用模块化设计和插件功能,使得我们可以轻易地在应用程序中插入新的鉴别模块或替换原先的组件,而不必对应用程序做任何修改,从而使软件的定制、维持和升级更加轻松,因为鉴别机制与应用程序之间相对独立。应用程序可以通过PAM API方便的使用PAM提供的各种鉴别功能,而不必了解太多的底层细节。
此外,PAM的易用性也较强,主要表现在它对上层屏蔽了鉴别的具体细节,所以用户不必被迫学习各种各样的鉴别方式,也不必记住多个口令;又由于它实现了多鉴别机制的集成问题,所以单个程序可以轻易集成多种鉴别机制如Kerberos鉴别机制和Diffie-Hellman鉴别机制等,但用户仍可以用同一个口令登录而感觉不到采取了各种不同鉴别方法。
在广大开发人员的努力下,各版本的UNIX系统陆续提供对PAM的支持。其中,Linux-PAM(Pluggable Authentication Modules for Linux)是专门为Linux操作系统实现的,包括Debian Linux 2.2、Turbo Linux 3.6、Red Hat Linux 5.0以及SuSE Linux6.2及它们的后续版本都提供对PAM的支持。FreeBSD从3.1版开始支持PAM。需要注意的是:除了具体实现不同外,各种版本Unix系统上的PAM的框架是相同的,所以本文介绍的Linux-PAM框架知识具有普遍性。
2 Linux-PAM的配置
Linux-PAM的目标就是为系统管理者提供最大限度的灵活性。系统管理者可以通过两种形式对Linux-PAM进行配置:单一配置文件/etc/pam.conf;或者是/etc/pam.d/目录。下面我们将讨论其配置文件的语法,接着给出一些实际应用的例子,以供读者参考。
2.1 Linux-PAM单一配置文件的语法
通过图1读者可能会注意到,配置文件也放在了在应用接口层中,它与 PAM API 配合使用,从而达到了在应用中灵活插入所需鉴别模块的目的。它的作用主要是为应用选定具体的鉴别模块,模块间的组合以及规定模块的行为。
在使用该配置文件前,读者首先应该明白Linux-PAM的记号是大小写敏感的。有两个特殊的符号:“#”和“.”。配置文件中的注释以#开头,一般配置文件中每行是一个入口(除注释外),但是如果某个入口的定义很长,可以通过使用转义符回行,而下一行也被看作是这个入口的一部分。
一般/etc/pam.conf文件每行的格式如下:
service-name module-type control-flag module-path arguments
其中,每个字符段的具体含义如下:
service-name:为这个入口分配的服务名。通常这是给定应用程序的会话名。例如:ftpd、rlogind、su等等。Linux-PAM还为默认的验证机制保留一个特殊的服务名,就是ohter,大小写均可。另外,如果某个模块指定了以命名的服务,那other就应该被忽略。
modle-type:Linux-PAM当前有四种类型的模块:
auth:这种类型的模块为用户验证提供两方面的服务:让应用程序提示用户输入密码或者其它的标记,确认用户的合法性;通过它的凭证许可权限,设定组成员关系或者其它优先权。
account:这类模块执行基于非验证的账户管理。它主要用来限制/允许用户对某个服务的访问时间,当前有效的系统资源(最多可以有多少个用户),限制用户的位置(例如:root用户只能从控制台登录)。
session:这类模块的主要用途是处理为用户提供服务之前/后需要做的一些事情,包括:记录打开/关闭数据的信息,监视目录等。
password:用来升级用户验证标记。
control-flag:控制标志用来设置验证成功或者失败后PAM需要作出的反应。因为模块可以层叠,控制标志可以决定每个模块的重要性。应用程序不会意识到单个模块成功或者失败,它只会收到Linux-PAM库成功或者失败的综合反应信息。
层叠模块的执行顺序取决于/etc/pam.conf文件的入口顺序,入口列前的模块先执行。从Linux-PAM 6.0开始可以使用两种语法定义控制标志。简单的一种是使用单一关键词定义控制标志。有四个这样的关键词:required、requisite、sufficient和optional。Linux-PAM通过如下方式解释这些关键词:
required:表示即使某个模块对用户的验证失败,也要等所有的模块都执行完毕之后,PAM才返回错误信息。这样做是为了不让用户知道被哪个模块拒绝。如果对用户验证成功,所有的模块都会返回成功信息。
requisite:如果特定的模块对用户的验证失败,PAM马上返回一个错误信息,把控制权交回应用程序,不再执行其它模块进行验证。
sufficient:表示如果一个用户通过这个模块的验证,PAM结构就立刻返回验证成功信息,把控制权交会应用程序。后面的层叠模块即使使用requisite或者required控制标志,也不再执行。如果验证失败sufficient的作用和optional相同。
optional:表示即使本行指定的模块验证失败,也允许用户享受应用程序提供的服务。使用这个标志,PAM框架会忽略这这个模块产生的验证错误,继续顺序执行下一个层叠模块。
module-path:PAM验证模块的路径。如果以/开头,就表示是完整的路径;如果不是以/打头,就表示是相对于/usr/lib/security的相对路径。
args:传递给模块的参数。类似于通常的Linux Shell命令行参数。有效的参数包括一些通用参数和特定于给定模块的参数。无效的参数将被忽略,并会把错误信息记录到syslog。
需要特别注意:配置文件中的任何一行错误都会导致验证失败,同时相关错误信息被记录到syslog。
举一个简单的例子如下:
Login auth required pam_unix.so debug
Login auth required pam_kerb.so use_mapped_pass
Login auth optional pam_rsa.so use_first_pass
这样,当login程序执行时先用pam_unix.so模块即传统的UNIX口令方式鉴别用户,然后再调用pam_kerb.so模块即Kerberos对用户进行鉴别,最后用pam_rsa.so模块即RSA方式鉴别用户。在按上述顺序鉴别用户的过程中,如果pam_unix.so模块鉴别失败,它将继续调用下面的模块进行鉴别而非立刻向login程序返回错误消息;
pam_kerb.so模块也按同样方式处理,直到顺序处理完最后一个pam_rsa.so模块后,PAM才将前面出现的错误信息返回给login程序。对于该配置,即使pam_rsa.so模块顺利通过,只要pam_unix.so模块和pam_kerb.so模块中有一个出现错误,用户就不能通过鉴别;相反,即使pam_rsa.so模块失败,只要pam_unix.so模块和pam_kerb.so模块都通过了,用户也能通过鉴别。
再举一个有关的例子如下:
ftp auth required pam_unix_auth.so debug
这样,当用户使用ftp时,将用传统的UNIX口令鉴别方式来验证其身份。
值得一提的是:在有的Linux操作系统(比如)下并没有现成的/etc/pam.conf文件可以使用,用户需要按照本节的介绍自行进行生成和编辑。
2.2 口令映射机制
在同一个机器上使用多个鉴别机制,尤其是一个应用程序集成多种鉴别机制可能导致用户需要记忆多个口令,这会让用户觉得很不舒服。虽然可以让所有机制使用相同的口令来获取易用性,但是这将削弱系统的安全性--如果其中任何一个机制的口令泄露了,则所有机制都会受到牵累。
此外,不同的鉴别机制在口令长度、容许的字符、更新间隔、有效期等方面可能具有他们特有的要求,这些要求也是为多鉴别机制使用同一个口令必须考虑的一个问题。
PAM为我们提供了这样一种解决,它不排除为所有鉴别机制共用一个口令,同时允许通过口令映射技术让每个机制使用不同的口令。该方案用用户的"主口令"加密其他的"副口令",并且将这些经过加密的副口令存放在一个用户能访问的地方。主口令一旦经过验证,鉴别就能用它解密那些加密的副口令从而获得相应口令,然后将所需口令传递给鉴别模块。这称为"口令映射"。
如果口令映射出现错误,或如果映射不存在,那么各鉴别模块应该提示用户输入口令。为支持口令映射,主口令应保存在PAM第二层并且在需要时由其提供给堆叠的各个鉴别模块。同时,口令要在pam_authenticate函数返回之前清除。为了保障口令映射的安全,主口令必须足够强壮,可以考虑使其的长度更长、组成口令字符的类型多样化并使用混合类型的字符组成口令等有效措施。
口令如何加密及其完全取决于具体的实现:它能够将加密的副口令(也称作"映射口令")存储在可靠或不可靠的地方,诸如智能卡、本地文件或目录服务。当然,如果加密的口令保存在一个不可靠的允许公共访问的地方,会留下受到字典攻击的隐患。
为实现口令映射,所有鉴别模块应支持以下四个映射选项∶
use_first_pass∶它表示当该模块执行时不提示用户输入口令,而将该模块之前的提示用户输入的主口令作为它们的公共口令进行验证。如果用户没能通过主口令的鉴别,则该模块不提示用户输入口令。此选项一般说来在系统管理员想强制用同一个口令通过多模块时使用。
try_first_pass∶除了如果主口令不正确,将提示用户输入口令之外,它的用法与use_first_pass相同。
use_mapped_pass∶它表示使用口令映射技术得到此模块的有效口令。也就是说,该模块执行时不提示用户输入口令,而是用映射口令即用主口令解密得到的该模块的副口令作为本模块的口令输入进行验证。如果在此之前用户没能通过主口令的鉴别,则该模块也不会提示输入口令。
try_mapped_pass∶除了如果主口令不正确,它将提示用户输入口令之外,该项与use_mapped_pass用法相同。
当口令更换后,PAM会保存所有新旧口令,并且使有关模块能够访问到它们。其他模块能够使用此信息更新加密的口令而不必强制用户再次输入口令。
现以上面使用的有关login的配置文件为例讲解口令映射:
Login auth required pam_unix.so debug
Login auth required pam_kerb.so use_mapped_pass
Login auth optional pam_rsa.so use_first_pass
在这里login程序集成了三种鉴别方式:传统UNIX口令鉴别、Kerberos和RSA鉴别,但通常情况下用户仅输入一次口令便能通过鉴别了。当程序调用pam_unix.so模块时,PAM提示用户输入他们的UNIX口令,然后由pam_kerb.so模块对用户输入的UNIX口令进行鉴别。
继而调用pam_kerb.so模块,由于该模块的选项为use_mapped_pass,它将利用口令映射机制进行认证,也就是说,如果UNIX口令鉴别通过的话,就将其作为pam_kerb.so模块的主口令来解密其对应的映射口令从而进行Kerberos鉴别。
如果pam_unix.so模块所需口令没能通过验证,则无法进行口令映射,那么PAM将直接调用下一鉴别模块而不提示用户输入其Kerberos口令。最后一个模块的选项为use_first_pass,所以pam_rsa.so模块将使用前边输入的主口令来鉴别用户,如果口令错误也不提示用户输入RSA口令。所以,只要第一次输入的口令是正确的,并且映射口令存在,则一个口令便足以通过鉴别。
2.3 基于目录的配置形式
从Linux-PAM 5.6版开始引入了一种基于目录的配置方式,通过/etc/pam.d/目录下的文件对PAM进行配置。这种方式比单一的配置文件具有更大的灵活性。这个目录下的所有配置文件都以某个服务名命名(小写)。不过,这两种配置方式不能同时起作用,也就是说,自己只能使用其中一种对Linux-PAM进行配置。一般/etc/pam.d/优先。
/etc/pam.d/目录下的配置文件的语法和/etc/pam.conf文件的语法相似,形式如下(参见图 1所示的关于sshd的PAM配置):
module-type control-flag module-path arguments
和/etc/pam.conf文件语法的唯一不同就是没有服务名(service-name),服务名由文件名设置。例如:/etc/pam.d/login文件保存对login服务的设置。
这种配置方式与单一配置文件相比,具有很大的优越性:
减少了配置错误的几率。
更易于维护。
可以通过使用不同配置文件的符号连接决定系统的验证策略。
可以加快对于配置文件的解析。
可以对单个的Linux-PAM配置文件设置不同的存取权限。
更易于软件包的管理。
3.2 使用Linux-PAM控制Samba用户的共享登录
在Samba中集成PAM的访问控制功能,非常简单,只需要完成如下两个步骤即可。首先,在Samba服务器的配置文件smb.conf的[global]配置段中,保证如下的语句未被屏蔽:
obey pam restrictions = yes
然后,保证有关Samba的PAM配置/etc/pam.d/samba文件正常即可,该配置文件如图 3所示:
3.3 使用Linux-PAM控制用户的登录
假设Linux系统提供FTP服务,但是不希望任何用户都可以轻易地访问该服务,只允许在一个事先定义好的文件中规定的用户可以使用该FTP服务。那么,我们就可以使用Linux-PAM机制来实现该控制。
当然,值得注意的是该FTP服务需要支持Linux-PAM。幸运的是,当前Linux中的proftpd、vsftpd等都对其完全支持。在下面的例子中,我们将以最新的vsftpd为例来进行举例说明。
首先,我们需要定义与认证相关的/etc/pam.d/vsftpd文件如图 4所示:
其中的pam_listfile.so模块就是用来实现基于用户的FTP控制。Item=user就表明是通过用户名进行控制,sense=allow表示如果用户名出现在/etc/vsftpd.ftpusers文件中就返回认证成功信息,file=/etc/vsftpd.ftpusers指定配置文件,onerr=succeed表示如果出现某些错误(比如无法打开配置文件等)时返回的结果,这里是成功信息。
然后,我们需要编辑/etc/vsftpd/ftpusers文件,并在其中加入可以进行ftp访问的用户名,要注意每个用户占一行(如下所示),之后重启动vsftpd即可,就可以根据这个配置文件通过用户名来对FTP访问进行控制了,如图 5所示: