• 进入"运维那点事"后,希望您第一件事就是阅读“关于”栏目,仔细阅读“关于Ctrl+c问题”,不希望误会!

Linux中PAM用户认证机制

安全管理 彭东稳 9年前 (2015-12-17) 28969次浏览 已收录 0个评论

日新月异的计算机技术发展了这么多年,用户认证方式也发生了翻天覆地的变化。比如:密码验证、指纹认证、RFID认证、虹膜认证等,踊跃而志,层出不穷。可是不管采用什么样的认证方式,都有一个不可回避的问题,那就是你得实现它!

Linux作为一个能够同时提供多种服务的操作系统,是不能只提供一个绕不过去的login命令就能保障其不备非法登录的。类似sshdftpd也需认证用户身份。所有能够让用户登陆系统的应用都要单独去实现一套用户认证方法很有重复造轮子的嫌疑,而且重复地区编写这么多用户认证代码是会出错的会被黑客们拿来利用并入侵系统中。

为了解决多个应用的用户认证问题,Linux引入了一套名为PAM的用户认证机制,它的全称是pluggable authentication modules可插拔认证模块。它将用户认证功能从应用中独立出来,单独进行模块化设计,统一实现和维护,并提供了一套标准API,以便各应用程序能够方便地使用它们所提供的各种功能,特别的,这种用户认证机制,对于其上层的用户是透明的。

PAM诞生自1995年,最先由SUN提出并应用于solaris2.3上。后各版本的UNIX系统陆续提供了对PAM的支持,包括FreebsdLinux。其中专门针对Linux实现的PAM通常称为Linux-PAM。但这些PAM除了具体的实现不同外,框架和标准API都是相同的。

PAM的体系结构

PAM为了提供足够高的通用性、灵活性和可配置性,采用了插件机制,这样就可以针对不同的应用灵活组织不同的插件来提供“随需应变”的用户认证机制。这也是其“可插拔”之名的由来。

为了实现“可插拔”性,又要兼顾易用性,PAM采用了分层的体系结构:让各认证模块从应用中独立出来,然后通过PAM API作为两者联系的纽带,应用程序可根据实际功能需要,灵活地在其中“插入”所需类别的认证功能模块。所以,PAM可以被划分为三层:应用成、接口层和认证模块层。如图

Linux中PAM用户认证机制

从上图可以看出,PAM API处在中间位置,负责应用与各模块之间的通信。当应用程序调用PAM API时,相关的API实现代码会按照“配置文件”的固定,加载并调用相应的模块所提供的功能来执行具体的认证操作。这样,只要修改“配置文件”的相关项就可以改变具体应用的认证方式。而且,也可以根据实际需要,任意添加新的认证模块来实现特殊的认证方式。

 

PAM为了提供更为细粒度的认证控制,给模块划分了四种类型,分别代表四种不同的认证任务。分别是:authaccountsessionpassword

其中auth类型的模块用来执行实际的认证工作,比如:提示用户输入密码或判断用户是否为root等;account类型的模块负责对用户的各种属性进行检查,比如:某个用户的密码是否到期、root用户是否允许在这个终端登录等;session类型的模块用于执行用户登陆前、退出后的一些操作,以及对每个会话进行跟踪和记录,比如:给新用户初始化home目录、记录用户登陆的时间等。password类型的模块实现了用户密码的细粒度管理,比如:设定密码的有效期、允许重复输入的次数等。

作为开发人员,不管是要编写需要认证功能的应用,还是准备给PAM编写某种类型的模块,都需要与PAM API打交道。而PAM API所提供的一些接口会与PAM的四类模块有一定的对应关系。例如:用于认证用户的pam_authenticate()接口和用于管理认证凭证的pam_setcred()接口对应着auth类型的模块;用于确认用户是否有权登陆系统的pam_acct_mamt()接口对应着account类型的模块;用于打开和关闭用户对话的pam_open_session()pam_close_session()接口对应着session类型的模块;用于修改用户口令的pam_chauthtok()接口对应着password类型的模块。

PAM API所提供的另外一些接口就不再与具体的模块相对应了,它们的作用是提供一些管理性功能或实现应用与模块之间的通信。管理性的接口有:pam_start()pam_end()pam_get_item()、和pam_set_item()等。实现应用与模块之间的通信接口又:pam_putenv()pam_getenv()pam_getenvlist()等。

作为Linux系统管理员,要针对某些特殊的应用指定特别的用户认证方案或添加新的PAM模块,就需要与配置文件打交道了。PAM的配置文件通常是pam.conf文件或pam.d目录,它们都会保存在/etc目录下。具体是使用pam.conf还是pam.d目录,不同的发行版可能不同,但是它们一般不会同时出现(如centos系列使用的是pam.d目录)。使用pam.d目录时,每个应用会有一个与它对应的配置文件,对应方式就是具体的配置文件名会与对应的应用名相同。

配置PAM

不管采用何种形式,配置文件的格式都差不多,由很多条记录组成。一般地,一条记录就是一行,并且使用空白字符将其划分成多个字段。各字段的定义按照从左至右顺序为:应用名、模块类型、控制标记、模块路径、模块参数。但是由于pam.d目录的形式会给每个应用提供一个单独配置文件,这就使得“应用名”这个字段没有存在的必要了。所以会比pam.conf文件少一列。此外,如果觉得一条记录占一行会影响可读性,也可以使用斜杠\续行。以下提供centos系统也就是pam.d格式的用户登陆认证的配置文件内容(/etc/pam.d/system-auth)

从这个文件中可以清楚地看到与我们之前说的那些内容了,认证文件使用了authaccountpasswordsession四个模块,每条记录由模块、控制标记、模块路径、模块参数,注意没有应用名称因为这是pam.d格式的。我们可以看到这四类模块可以堆叠使用,也就是说,一个认证动作可以同时使用多个相同类型的模块共同去完成。这就像“陪审团”去判断一个用户是否“有罪”一样,只要达到相应的“比例”就可以做出结论。而且每个具体的模块也不会仅限制与一种类型,它们的“身份”也会随时发生变化,具体会怎样,一切由配置文件说的算。

模块就不用说了,前面已经介绍过了。那么接下来就说一下第二个字段控制标记,下面说说控制标记。对于规定模块重要程序的控制标记一共有四个,它们分别是:requiredrequisitesufficientoptional这四类。其含义如下:

required:表示该模块的认证成功是用户通过认证的必要条件,也就是说,只要有一个被标明为required的模块认证失败,用户就一定不会通过认证。但是,即便这类模块认证失败,PAM也不会立即将错误消息返回给应用,而是继续将其他模块都调用完毕之后才返回。这样做的目的就是为了麻痹“敌人”,让他们搞不清楚到底是哪里认证失败的。所以,required是最常用的控制标记。

requisite:与required差不多,但是只要用它标记的模块认证失败,就会立即返回给应用。具体的返回值与第一个认证失败的模块有关。一般被requisite标记的模块多数用来判定当前用户所处的环境,如果环境不够安全,即便是合法的用户也不会通过验证。这是最严苛的要求,一般很少使用。但是requisite能够降低黑客利用不安全媒介获得输入密码的机会。

sufficient:表示该模块验证成功是用户通过认证的充分条件。只要这个模块验证成功了,就代表没有必要继续去认证这个用户了。那么相应的行为就是只要被sufficient标记的模块一旦认证成功,就会立即返回给应用,报告成功;但是需要注意的是sufficient的优先级低于required,那么如果有required失败,则最终的结果也是失败的;当sufficient认证失败时,相当于optional

optional:这表示即便该模块认证失败,用户也可能通过认证,除非别无它选。换句话说,它仅供参考。而实际应用中,optional所标记的模块只是显示写信息,根本不去做什么认证工作。

为了更进一步地说明这四个“控制标记”,可以参考下图所示的PAM用户认证的基本流程。

Linux中PAM用户认证机制

以上四个控制标签是说明模块的重要性,下面再看一个ssh认证的配置文件(/etc/pam.d/sshd)如下:

眼神没有问题就会发现又多了一个include的标签,就是本意包含的意思,include要求当前配置文件包含另一个配置文件,所以它所描述的模块并非真正的模块,而是与当前配置文件包含具有相同路径的另一个配置文件。在ssh配置文件中system-auth这个配置文件就是上面说的系统登陆认证文件。需要注意的是include属于控制标记,只会对“模块类型”字段所标记的一类模块起作用。即便system-auth可能包含了全部四种类型的模块(authaccountpasswordsession),也只有当前记录所对应的那一类模块的那些记录会被包含进来。如ssh配置中的

那么include在包含system-auth时,并不会包含它所有的模块只需要auth模块即可。

目前所介绍的这些“控制标记”都还是很简单的,但是也有非常复杂的如登陆认证文件中的此代码(/etc/pam.d/login)

这就是描述了对模块具体的返回状态的处理方式。类似user_unknownsuccessignored等这些内容就是某个模块的返回状态。那么相应的ignoreokbad等就是对这些返回状态的处理方式标记,或者说是“动作”。PAM所支持的常用状态如下:

success – 认证成功

auth_err – 认证失败

new_authtok_reqd – 需要新的认证令牌,有三种情况会返回这个状态:一时根据案情策略需要更改密码;而是密码为空;三是密码以及过期

ignore – 忽略底层account类型模块的返回状态,而且无论是否被requiredsufficientoptional修饰,处于安全性考虑,这个返回状态通常要被忽略掉,这样其他模块的返回状态就会被参考。

user_unknown – 未知用户

try_again – 已经通过了密码服务的初步检测

default – 所有没有注明的返回状态

对应的是所支持的常用动作如下:

ignore – 忽略这个返回状态,不会对验证结果产生影响

bad – 表示这个返回状态代表验证失败,余下的模块调用也永远失败

die – bad相同,但是应该立即返回到应用,不在继续调用余下模块

ok – 表示这个返回状态应该被看做是验证成功,继续调用余下模块

done – ok相同,但是应该立即返回到应用,不再继续调用余下模块

N – ok相同,但是要跳过N个模块才能继续

reset – 清除所有的返回状态,继续调用余下模块

那么配置文件中模块和控制标记说完了,再说说余下的两个字段:模块路径和模块参数,应该不难理解的,对于模块路径,可以有两种方式表达:一是使用绝对路径从根开始的路径,二是只给出一个文件名,PAM会在模块的默认路径中去搜索,PAM模块的默认搜索路径是/lib/security,如64位系统就是/lib64/security。这些都是PAM编译时的默认位置。如果有特别的非要改个地址的发行版那就是比较操蛋。

某些特殊的模块和其参数配合起来可以获得非常神奇的效果,比如system-auth文件中的pam_succed_if.so这个模块。它可以是满足全部四种类型模块的功能需求。它的参数很多时候就是一个条件表达式,如 user != rootuser_id >=500等。如果表达式的计算结果为真,那么pam_succeed_if.so模块会返回success状态,表示通过认证。等等功能。

总之PAM机制是非常复杂的,提供了很多模块,如果你想了解某个模块可以查看Linux联机帮助,都有详细的文档资料。

密码映射

既然PAM提供了一套机制,允许使用多种方法的组合进行用户身份确认,那么就不可避免地遇到一个问题——不同的方法有不同的密码体系。为这个问题开脱的最好理由就是“安全”。因为黑客要攻破这的系统,就必须攻破所有的密码体系,但是一旦采用了多种密码体系,就避免不了在进行一次认证的过程中要求用户多次输入不同的密码。显然如果急性不太好会遇到很大的麻烦。但是统一密码体系统一密码,就会消弱系统来之不易的安全性。PAM的密码映射机制提供了两全其美的解决方案。

密码映射机制引入了“主密码”和“副密码”这两个概念,主密码加密其他密码体系的密码而形成副密码,并且将这些经过加密的副密码存放在一个用户能访问的地方。主密码一旦通过认证,就可以利用它来解密副密码而获得相应的密码继续认证。这个过程就是“密码映射”。如果密码映射出现错误,或者映射不存在,那么需要密码的相关模块就会提示用户输入密码。主密码一般被保存在PAM API层,并在需要时提供给堆叠的各个认证模块。处于安全考虑,密码要在pam_authenticate函数返回之前清除,而且主密码必须足够强壮。密码如何加密和密码如何存储则完全取决于PAM的具体实现。

为了实现密码映射,所有类型的认证模块应该支持以下四个映射选项:user_first_pass

try_first_passuse_mapped_passtry_mapped_pass

密码映射机制是PAM比较复杂的机制,可以显著提高系统的安全性同时又保障了系统的易用性。当前大多数企业级的Linux发行版并不默认提供利用这种机制的用户认证方案,唯一的应用可能就是我在前面给出的那个password-auth文件的例子。 主要是因为“安全”这个问题对于不同的企业是有不同的要求和实施策略,服务器供应商很难做到面面俱到。为了能够让一个Linux系统达到足够强健的安全性而又不是去易用性,作为系统管理员,可能需要付出更多的汗水和经验值去保障。


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (0)
[资助本站您就扫码 谢谢]
分享 (0)

您必须 登录 才能发表评论!