Linux PAM 学习笔记

作者:金步国


版权声明

本文作者是一位开源理念的坚定支持者,所以本文虽然不是软件,但是遵照开源的精神发布。

其他作品

本文作者十分愿意与他人分享劳动成果,如果你对我的其他翻译作品或者技术文章有兴趣,可以在如下位置查看现有的作品集:

联系方式

由于作者水平有限,因此不能保证作品内容准确无误。如果你发现了作品中的错误(哪怕是错别字也好),请来信指出,任何提高作品质量的建议我都将虚心接纳。


Linux PAM 是什么?

PAM 是 Pluggable Authentication Modules 的缩写,意为"可插拔认证模块"。它将各种不同的底层认证机制统一到一组高层API中,从而省去了在每个应用程序中分别设计和实现各种认证机制的麻烦。Linux PAM 就是一种 Linux 平台上的 PAM 实现。[注意]官网的文档已经过时且不再更新,最新的文档在 Linux-PAM-GitHub {Linux-PAM-*-docs.tar.xz (doc/sag/html), Linux-PAM-*.tar.xz (modules/*/*.[58])}

以常见的 su 命令来说,它可以实现用户切换,从 root 切换至其他用户不需要密码、从非 root 用户切换至其他用户则需要验证目标用户的密码,一旦认证成功就以目标用户身份启动 shell 以供使用。本质上,su 只做两件事:(1)认证;(2)启动 shell 。按照传统思路,两件事都很容易实现,例如认证逻辑可以用伪代码这样简单的描述:

if ( uid == 0 ) 认证成功
elseif ( 输入的密码 == 目标用户的密码 ) 认证成功
else 认证失败

但是,认证需求不是一成不变的。例如:(1)为了方便运维团队成员(也就是 wheel 组),希望 wheel 组中的用户无需输入密码也能直接进行用户切换;(2)为了加强安全性,希望额外验证手机短信;(3)为了避免频繁输入难记的用户密码,希望可以选用指纹方式进行验证;(4)为了方便某个特定的用户测试,希望仅凭手机短信也能完成验证 ...... 这样一来, su 的开发者将会被迫不断更改 su 的源代码,然后再重新调试、编译、分发,非常辛苦。这种情况下,PAM 就可以对 su 开发者说:"认证的事交给我,能不能通过认证由我说了算,你只需做好其他事情(启动 shell)即可";同时又对用户(系统管理员)说:"只要学会了 PAM 配置语法,就可以利用各种 PAM 模块,编写出千变万化的认证策略。无需打扰 su 开发者,就能立即得到想要的效果"。通过把与认证相关的脏活累活都交给 PAM 来干, su 的开发者与用户之间实现了解耦,彼此皆大欢喜。

推而广之,如果一个应用程序想要使用 PAM 进行认证,只需在源代码中嵌入 PAM 支持即可(也就是引入相应的头文件)。然后开发者无需再为认证部分操心(是否通过认证交给 PAM 决定),只需专注程序的其他部分即可。也就是说,无需修改应用程序就可以切换、修改、升级应用程序使用的认证机制。当然,如果只有一个不支持 PAM 的二进制文件,那就没有办法改造了。

Linux PAM 能做什么?

简单来说,PAM 能够完成如下四种类型(type)的任务:

auth
验证账户的凭据。注意,这里的"凭据"不仅仅指密码,而是泛指一切认证方式,例如:一次性密码、指纹、短信、IP地址、二维码。除验证凭据之外,还可以进一步执行更多的关联操作,例如:修改账户所属的组、显示特定的提示信息、赋予账户某些权限。
account
验证账户自身的权限。例如:账户是否已禁用、凭据是否已过期、是否只能在特定的时间段有效、是否已经达到最大登录数量、是否属于特定的组、是否允许控制台登录、是否允许远程登录。注意,账户自身的权限与是否通过凭据验证无关,例如,禁止从远程登录的账户,即使成功通过了凭据验证,也仍然无法从远程登录。
password
管理账户的凭据。例如:更新账户密码并确保新密码符合复杂性要求、以 SHA512 方式存储密码。注意,这里的"凭据"不仅仅指密码,而是泛指一切认证方式,例如:指纹、声纹、虹膜、二维码。此类型通常都与 auth 类型存在很强的耦合性。
session
在用户获得服务之前/后需要执行的各种操作。例如:创建家目录、设置与撤销环境变量、显示今日消息(motd)、设置 umask 、挂载目录。

上述所有任务都通过位于 /lib/security/ 或 /lib64/security/ 中的各个 PAM 模块来实现,模块文件名一般都符合 pam_*.so 格式。

Linux PAM 配置文件

配置文件的名称

系统管理员维护的配置文件统一放置在 /etc/pam.d/ 目录中(高优先级),软件包自带的默认配置文件统一放置在 /usr/lib/pam.d/ 目录中(低优先级),对同时存在于两个目录中的同名文件来说,前者会覆盖后者。注意,文件名中的字母只能小写,并且拥有特殊的含义,文件名必须等于应用程序内部硬编码的"服务名"(同一个应用程序可以拥有多个不同的"服务名")。例如: su 总是读取 /etc/pam.d/su (若运行"su -l"则读取 /etc/pam.d/su-l )、 sudo 总是读取 /etc/pam.d/sudo (若运行"sudo -i"则读取 /etc/pam.d/sudo-i )、 sshd 总是读取 /etc/pam.d/sshd 。

名为"other"的特殊配置文件,表示当应用程序未能找到所需的配置文件(例如"su"或"sudo")时,应该使用的默认配置(一般是拒绝一切请求)。除"other"之外,还有其他一些配置文件也比较特别,这些配置文件并不对应某个"服务名",而是专用于被其他配置文件引用,例如"password-auth"以及"system-auth"等等。

配置文件的语法

配置文件的语法非常简单,一看便知,具体说来就是:以"#"号开头的行表示注释,空行将被忽略,其余每行表示一条策略,每条策略都有四个字段(第四个字段为可选字段),字段之间用空格分隔。下面是一个示例:

#类型(type)  控制标志(control-flag)      模块路径(module-path)    模块参数(module-arguments)
auth         substack                    password-auth
auth         sufficient                  pam_unix.so              nullok try_first_pass
auth         required                    pam_deny.so
account      include                     system-auth
account      sufficient                  pam_succeed_if.so        uid=0 use_uid quiet
account      required                    pam_deny.so
-password    optional                    pam_gnome_keyring.so     use_authtok
password     include                     password-auth
password     requisite                   pam_pwquality.so         try_first_pass local_users_only
password     required                    pam_deny.so
-session     optional                    pam_systemd.so
session      optional                    pam_xauth.so
session      include                     postlogin
session      [success=1 default=ignore]  pam_succeed_if.so        service !~ gdm* service !~ su* quiet
session      required                    pam_deny.so

第一个字段"类型"(type)就是"Linux PAM 能做什么?"小节中所述的四种类型(auth,account,password,session)之一。若在类型前加上"-"(减号)前缀,则表示即使模块不存在,也不会影响认证结果,更不会将此事件记录到日志中,对于那些可有可无的模块来说,这一特性非常有用。

第二个字段"控制标志"(control-flag)用于控制模块在流程栈中的行为。该字段有两种语法,一种是简单语法,一种是复杂语法,用于描述 PAM 认证流程的执行规则。详见后文的"流程栈"小节。

第三个字段"模块路径"(module-path)表示模块的文件系统路径,既可以是绝对路径,也可以是相对路径(相对于 /lib/security/ 或 /lib64/security/ 目录),模块文件名一般都符合 pam_*.so 格式。注意,当第二个字段(控制标志)的值是"include"或"substack"时,相对路径表示相对于 /etc/pam.d/ 或 /usr/lib/pam.d/ 目录的路径。

第四个字段"模块参数"(module-arguments)用于控制模块的行为,它是一组用空格分隔的参数列表,每个模块能够使用的参数各不相同,详见后文"常用模块"小节。如果想在某个参数中使用空格,那么必须在该参数外围加上中括号(字符串中的"["无需转义,但"]"必须转义为"\]")。例如 passwd=mada [query=select user_name from internet_service where user_name='%u' and password=PASSWORD('%p')]

注意,配置文件中的任何语法错误都会导致认证过程失败,同时,失败信息将会被记录到系统日志(syslog)中。

流程栈

所谓"流程栈"是指执行步骤和规则,非常类似于程序逻辑的概念,它体现在配置文件自上而下的执行顺序中。"栈"就像是一段可以嵌套的程序逻辑,通过"substack"关键字,可以在一个栈(父栈)中嵌入另一个栈(子栈)。 PAM 流程栈的执行规则由配置文件中的第二个字段"控制标志"(control-flag)决定,你可以把"控制标志"想象成逻辑控制语法。该字段有两种语法,一种是简单语法,一种是复杂语法。

简单语法(关键字)

通常使用简单语法,表现为一组关键字:

required
栈成功的必要条件。即使失败,也要继续执行此栈中所有同类型的后继模块(也就是"类型"字段值相同的后继行),但此栈最终必定返回失败结果。如果不想让用户知道被哪个模块拒绝,可以使用此关键字。
requisite
栈成功的必要条件。一旦失败,将立即终止此栈,并立即返回失败结果。栈的返回值取决于第一个失败的"required"或"requisite"模块。如果想防止用户通过不安全的介质输入密码,可使用此关键字。注意,使用此关键字使得攻击者探测帐户名称成为可能,所以在实践中需要权衡可能暴露密码与可能暴露账户之间孰轻孰重。
sufficient
栈成功的充分条件(注意,这样描述并不准确)。(1)当此模块成功时,如果所有先前的"required"模块也都成功,那么立即终止此栈,并立即返回成功结果(此栈中的剩余模块将被舍弃),否则继续执行栈中的后继模块。(2)当此模块失败时,直接忽略此模块(不影响栈的最终结果),并继续执行栈中的后继模块。
optional
栈成功的可选条件。只有当此模块是栈中同类型模块(也就是"类型"字段值相同)中的唯一模块时,此模块的成败才有意义。否则,直接忽略此模块(不影响栈的最终结果),仅在日志中记录失败信息。
include
从"模块路径"(module-path)字段指定的文件中提取所有同类型的行(也就是"类型"字段值相同的行),直接插入此处(等价于直接写在此处)。注意,此关键字并不创建子栈。
substack
从"模块路径"(module-path)字段指定的文件中提取所有同类型的行(也就是"类型"字段值相同的行),组成一个子栈,再将该子栈的运行结果作为该行的结果插入此处(类似于程序设计中的函数调用)。例如,子栈中的"requisite"失败只会导致子栈本身终止并返回失败结果,而不会导致父栈立即终止。

复杂语法(中括号)

复杂语法,表现为用中括号包围的一组以空格分隔的"value=action"列表,形如 [value1=action1 value2=action2 ...] 。由于很少使用,这里就略过此种语法的介绍。

注意事项

默认配置

为防止配置文件缺失可能导致的隐蔽故障与安全漏洞,应该确保存在一个严苛的默认配置(/etc/pam.d/other)。

语法正确

因为配置文件中的任何语法错误都会导致认证过程失败,所以务必确保语法正确。

默认拒绝

严苛的默认配置(/etc/pam.d/other)对于加强系统安全是非常重要的,它可以预防丢失或忘记配置所导致的安全漏洞。但因为 pam_deny.so 在运行时不记录任何信息,所以除非用户在无法执行某个应用的认证时能够与系统管理员联系,否则系统管理员可能很长时间都不知道系统存在配置错误。为了解决这个问题,可以额外加入 pam_warn.so 模块,以确保系统管理员可以从系统日志(syslog)中获得警告信息(auth.notice)。具体内容如下:

#默认拒绝一切请求并发出警告(/etc/pam.d/other)
auth         required      pam_deny.so
auth         required      pam_warn.so
account      required      pam_deny.so
account      required      pam_warn.so
password     required      pam_deny.so
password     required      pam_warn.so
session      required      pam_deny.so
session      required      pam_warn.so

模拟传统

如果你对 Linux PAM 不熟悉,并且也不想使用复杂的认证策略,而应用程序又使用了 PAM 认证,那么一个偷懒的做法是使用下面的配置来模拟传统的 UNIX 认证方式:

#模拟传统认证方式
auth        required       pam_unix.so
account     required       pam_unix.so
password    required       pam_unix.so
session     required       pam_unix.so

在实践中,把这个配置用作复杂认证策略的配置起点也是一个不错的选择。

常用模块

除了 Linux PAM 自身附带的模块之外,还存在许多第三方模块(例如 Google Authenticator),本小节并不打算、也不可能介绍所有模块,而是仅选取一些常用模块进行介绍。[提示]每一个模块标题行末尾的小括号表示该模块的适用类型(对应于配置文件中的"类型"(type)字段)。

基于来源的访问控制 (auth,account,password,session)

pam_access.so [ debug ] [ nodefgroup ] [ noaudit ] [ accessfile=配置文件 ] [ fieldsep=分隔符 ] [ listsep=分隔符 ]

此模块能够基于账号与来源(主机/网络/终端/$DISPLAY/服务名)的组合执行访问控制。访问控制策略保存在主配置文件(accessfile)(默认=/etc/security/access.conf)与配置片段(/etc/security/access.d/*.conf)中,所有配置片段按顺序依次解析,一旦匹配成功就立即停止继续解析。为了给不同的服务分别指定不同的访问控制策略,可以明确指定 accessfile 参数,这将导致仅解析指定的主配置文件(忽略所有配置片段)。因为配置片段的优先级低于主配置文件且容易被忽略,所以不建议使用配置片段。

此模块能够通过几种不同的组合决定允许或拒绝访问,具体如下:(1)首先,按照 (user, 主机名/域名) 或 (user, IP地址/IP网段) 组合进行匹配;(2)其次,如果用户通过终端访问,那么按照 (user, tty) 组合进行匹配;(3)否则,按照 (user, $DISPLAY) 或 (user, 服务名) 组合进行匹配。一旦找到第一个匹配,就立即停止解析并返回允许或拒绝的结果。

#按照从上到下的顺序依次匹配,并且一旦匹配成功就立即返回,所以策略的顺序非常重要。

#允许 root 用户从特定服务以及特定终端(其中":0"是X终端[键盘+鼠标+屏幕])访问
+:root:cron crond :0 tty1 tty2 tty3 tty4 tty5 tty6

#允许 root 用户从特定的IP地址与网段访问
+:root:127.0.0.0/24 ::ffff:127.0.0.0/127 192.168.200.4 192.168.200.9 192.168.201.0/24 2001:4ca0:0:101::1 2001:4ca0:0:101::/64

#允许 remote 组从非本地登录
+:(remote):ALL EXCEPT LOCAL

#允许 root 组、 nis_admins 网络用户组、 admin 用户从任意来源登录
+:(root) @nis_admins admin:ALL

#允许 wheel 组从本地登录
+:(wheel):LOCAL

#允许 wheel 组中除 john 之外的其他成员从 localhost 与 localhost.localdomain 与 foo.bar.org 主机访问
+:(wheel) EXCEPT john:localhost localhost.localdomain foo.bar.org

#允许 wheel 组中除 john 与 tommy 之外的其他成员从 www.bar.org 域名访问
+:(wheel) EXCEPT john tommy:.www.bar.org

#允许 assistants 组从"192.168.5.0/24"网段内除"192.168.5.97-99"之外的其他IP地址访问
+:(assistants):192.168.5.0/24 EXCEPT 192.168.5.97 192.168.5.98 192.168.5.99

#默认兜底策略(拒绝所有来源的所有用户)
-:ALL:ALL

从上面的实例可知,配置语法非常简单,具体说来就是:以"#"号开头的行表示注释,空行将被忽略,其余每行表示一条策略,每条策略都有三个字段,字段之间的分隔符由 fieldsep 参数决定(默认为冒号":")。[提示]不要在字段分隔符两边添加空白。

第一个字段(权限)必须是"+"或"-"字符之一,分别表示允许(+)与拒绝(-)。

第二个字段(账号)有三种不同的语法:(1)关键字,"ALL"表示任意用户;(2)包含列表,表现为以 listsep 参数(默认=空格/水平制表符/逗号)作为分隔符的一系列登录名、常规用户组[用小括号包围,例如"(group)"]、网络用户组[带有"@"前缀,例如"@nis_group"];(3)排除列表,表现为 EXCEPT 表达式。[注意]为兼容老版本配置文件,默认情况下,没有使用小括号包围的登录名会在未能匹配成功时被当作用户组再次匹配。为避免这种含混不清的语法,强烈建议使用 nodefgroup 选项关闭这个坑爹的兼容性。

第三个字段(来源)有三种不同的语法:(1)关键字,"ALL"表示任意来源、"LOCAL"表示本地登录[匹配所有不含"."的字符串];(2)包含列表,表现为以 listsep 参数(默认=空格/水平制表符/逗号)作为分隔符的一系列来源,可以识别的来源形式如下:(a)终端设备名[剥除"/dev/"前缀]、(b) $DISPLAY 变量值、(c)服务名、(d)主机名与域名[域名以"."开头,例如".www.example.com"]、(e)网络主机组[以"@"开头,例如"@nis_hostgroup"]、(f)IP地址与IP网段[IP网段用"network/mask"表示,例如"192.168.5.0/24"];(3)排除列表,表现为 EXCEPT 表达式。

如果在编译 Linux PAM 时开启了审计支持,此模块还将通过系统日志(syslog)报告所有被拒绝的访问(除非使用 noaudit 选项强制关闭审计)。另外,也可以使用 debug 选项在系统日志(syslog)中输出详细的调试信息。

拒绝访问 (auth,account,password,session)

pam_deny.so

此模块没有任何选项或参数,用于无条件的拒绝访问,常用于默认配置(other)之中。

显示文本消息 (auth,account,password,session)

pam_echo.so [file=消息文件]

此模块在流程栈中一般用作 optional 条件,用于显示一些提示消息。文本中的百分号(%)是替换标记,可以识别的替换标记如下:

%H
远程主机名(PAM_RHOST)
%h
本地主机名
%s
服务名(PAM_SERVICE)
%t
正在使用的控制终端(PAM_TTY)
%U
远程用户名(PAM_RUSER)
%u
本地用户名(PAM_USER)

所有其他不能识别的替换标记都只简单的去掉前导百分号(例如"%%"将被替换为"%")。

设置与撤销环境变量 (auth,session)

pam_env.so [debug] [conffile=配置文件] [envfile=变量文件] [readenv=0|1]

此模块用于设置与撤销环境变量(也包括PAM变量,例如"PAM_RHOST")。首先,执行配置文件(conffile)(默认=/etc/security/pam_env.conf)中的指令;然后,根据 readenv 的指示(1=是[默认]|0=否)决定是否继续加载变量文件(envfile)(默认=/etc/environment)。

配置文件(conffile)用于变量的设置/撤销/修改。配置文件的语法非常简单,以井号(#)开头的行表示注释、空行将被忽略、其余的行必须符合如下格式:

变量名 [DEFAULT=[默认值]] [OVERRIDE=[指定值]]

行首的"变量名"一般使用全大写字母且不能有前导空白,可选的 DEFAULT 关键字用于设置变量的默认值(默认为空字符串)(不会覆盖变量的原有值),可选的 OVERRIDE 关键字用于强制覆盖变量的原有值。可以在"默认值/指定值"中使用 ${XXX} 语法引用(可能不存在的)环境变量、使用 @{XXX} 语法引用(可能不存在的)PAM变量。 HOME 与 SHELL 是两个特例,它们既可以是环境变量也可以是PAM变量,当用作PAM变量时, @{HOME} 与 @{SHELL} 的值实际上来源于该用户在 /etc/passwd 中登记的值(可能不等于环境变量 ${HOME} 与 ${SHELL} 的值)。注意,因为应用程序在调用此模块时,有可能尚未设置所需的环境变量,所以某些环境变量在调用此模块时可能尚不存在,例如 ${HOME} 就经常用 @{HOME} 来代替。下面是一个配置文件(conffile)实例:

#/etc/security/pam_env.conf
#为远程主机设置 REMOTEHOST 变量,并将默认值设为"localhost"
REMOTEHOST     DEFAULT=localhost OVERRIDE=@{PAM_RHOST}
#为 DISPLAY 变量设置一个合理的值
DISPLAY        DEFAULT=${REMOTEHOST}:0.0 OVERRIDE=${DISPLAY}
#其他一些常规变量
PAGER          DEFAULT=less
MANPAGER       DEFAULT=less
LESS           DEFAULT="M q e h15 z23 b80"
NNTPSERVER     DEFAULT=localhost
PATH           DEFAULT=${HOME}/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/bin/X11:/usr/bin/X11
XDG_DATA_HOME  DEFAULT=@{HOME}/share/
#一些针对特殊字符的例子(仅支持对 $ 与 @ 的转义,不支持对引号的转义)
DOLLAR         DEFAULT=\$
DOLLARDOLLAR   DEFAULT=        OVERRIDE=\$${DOLLAR}
DOLLARPLUS     DEFAULT=\${REMOTEHOST}${REMOTEHOST}
ATSIGN         DEFAULT=""      OVERRIDE=\@

变量文件(envfile)仅用于环境变量的设置。变量文件的语法非常简单,以井号(#)开头的行表示注释、空行将被忽略、其余的行必须符合 KEY=VAL 格式(若想兼容bash脚本也可以添加可选的 export 指令前缀)。

注意,因为设置PAM变量会影响其他PAM模块的行为,所以应该将此模块放在流程栈的末尾。另外,出于调试目的,还可以使用 debug 选项在系统日志(syslog)中输出详细的调试信息。

调用外部命令 (auth,account,password,session)

pam_exec.so [ debug ] [ expose_authtok ] [ seteuid ] [ quiet ] [ quiet_log ] [ stdout ] [ log=日志文件 ] [ type=模块类型 ] 外部命令 [ 参数 ... ]

此模块用于调用外部命令。子进程(外部命令)除了继承当前进程的环境变量之外,还能使用下列PAM变量:PAM_RHOST, PAM_RUSER, PAM_SERVICE, PAM_TTY, PAM_USER, PAM_TYPE(account, auth, password, open_session, close_session 之一)。注意,父进程的用户决定了外部命令(子进程)能够看到的环境变量。

使用 debug 选项可以在系统日志(syslog)中输出详细的调试信息。使用 expose_authtok 选项表示被调用的外部命令可以从标准输入(STDIN)读取密码。使用 seteuid 选项表示以调用进程(父进程)的 EUID(effective UID) 身份调用外部命令(默认=调用进程的实际UID[real UID])。使用 quiet 选项表示在外部命令出错(退出码≠0)时不显示退出状态(默认=外部命令出错时显示退出状态)。使用 quiet_log 选项表示在外部命令出错(退出码≠0)时不在日志中记录退出状态(默认=外部命令出错时在日志中记录退出状态)。当未使用 stdout 选项时,外部命令的标准输出(STDOUT)将会被记录到 log= 参数指定的日志文件(默认=/dev/null)中;当使用 stdout 选项时,则忽略 log= 参数,并将外部命令的标准输出(STDOUT)重定向至调用进程(父进程),由调用进程负责处理。可以使用 type= 参数限定仅在当前模块的类型与给定的类型(auth,account,password,session 之一)匹配时才调用外部命令。下面是一个实例:

#可用于向 /etc/pam.d/passwd 中添加的行
#在本地密码被修改的同时重建NIS数据库(以EUID身份执行 make -C /var/yp 命令)
password optional pam_exec.so seteuid /usr/bin/make -C /var/yp

传统UNIX密码验证 (auth,account,password,session)

pam_unix.so [nullresetok|nullok] [try_first_pass|use_first_pass] [nodelay] [use_authtok] [shadow] [sha256|sha512] [rounds=N] [minlen=N] [no_pass_expiry] [debug|audit] [quiet]

标准的传统UNIX密码验证(基于 /etc/passwd 与 /etc/shadow 验证和设置帐户信息)。"auth"组件用于验证密码(默认拒绝空密码访问)。"account"组件用于验证账户与密码的有效性、建议更改即将过期的密码、拒绝失效的账户与密码。"password"组件用于更新密码(默认哈希算法取决于 /etc/login.defs 文件中的 ENCRYPT_METHOD 变量)。"session"组件用于记录用户的登录与退出(/var/log/[uwb]tmp)。此模块支持下列选项:

nullresetok|nullok
仅在必须重设密码时才允许使用空密码访问(nullresetok)还是无条件允许使用空密码访问(nullok)
try_first_pass|use_first_pass
"try_first_pass"表示在提示输入密码之前首先尝试流程栈中先前模块的密码,一旦尝试成功,就直接不再提示输入密码。"use_first_pass"表示强制使用流程栈中先前模块的密码(绝不提示输入密码),如果验证失败(包括不存在先前模块的密码),那么直接拒绝访问。
nodelay
取消"auth"组件验证密码失败后的阻塞延迟(默认阻塞延迟2秒)
use_authtok
在修改密码时,强制将新密码设置为前一个 password 模块(例如 pam_passwdqc 模块)提供的密码。
shadow
明确要求使用影子密码(/etc/shadow)
sha256|sha512
保存新密码时所使用的哈希算法
rounds=N
保存新密码时循环哈希的轮数(默认=5000)
minlen=N
限制最小密码长度
no_pass_expiry
不检查密码有效期(例如当 sshd 仅使用公钥认证时可能需要使用此选项)
debug|audit
在系统日志中输出详细(debug)或超级详细(audit)的调试信息。
quiet
停止向系统日志中输出与会话的开启/结束有关的消息(静默模式)。

下面是一个 /etc/pam.d/login 实例:

#验证密码
auth       required   pam_unix.so
#确保账号和密码仍然处于有效期
account    required   pam_unix.so
#在更改密码前首先用 pam_passwdqc 确保新密码符合复杂性要求
password   required   pam_passwdqc.so config=/etc/passwdqc.conf
password   required   pam_unix.so use_authtok nullresetok sha512
#建立会话
session    required   pam_unix.so

模块索引

Linux PAM 内置模块
模块类型简介
pam_access.soauth,account,password,session基于来源(主机/网络/终端/$DISPLAY/服务名)的访问控制
pam_debug.soauth,account,password,session调试PAM栈
pam_deny.soauth,account,password,session无条件的拒绝访问
pam_echo.soauth,account,password,session显示文本消息
pam_env.soauth,session设置与撤销环境变量(也包括PAM变量)
pam_exec.soauth,account,password,session调用外部命令
pam_faildelay.soauth设置认证失败时的阻塞延迟
pam_faillock.soauth,account锁定连续认证失败的账户
pam_filter.soauth,account,password,session在用户与应用程序之间建立输入输出(STDIN/STDOUT)过滤器
pam_ftp.soauth可插拔的匿名FTP访问模式(不安全)
pam_group.soauth将用户额外添加到特定组中
pam_issue.soauth更改用户的提示文件(issue)
pam_keyinit.sosession在默认内核会话密钥环之外额外创建新的内核会话密钥环
pam_lastlog.soauth,account,session显示账户的上次登录时间、锁定长期不登录的帐户
pam_limits.sosession设置会话的资源限制
pam_listfile.soauth,account,password,session基于各种属性(tty|user|rhost|ruser|group|shell)执行访问控制
pam_localuser.soauth,account,password,session仅允许本地用户(/etc/passwd)访问
pam_loginuid.sosession设置进程的 loginuid 属性(主要用于审计目的)
pam_mail.soauth,session提示用户有新邮件
pam_mkhomedir.sosession在开始会话前创建用户的家目录
pam_motd.sosession显示"今日消息"(motd)
pam_namespace.sosession为会话设置私有名字空间
pam_nologin.soauth,account禁止非 root 账户登录
pam_permit.soauth,account,password,session无条件的允许访问(小心使用)
pam_pwhistory.sopassword防止重复使用旧密码
pam_rhosts.soauth使用 rlogin/rsh 网络访问认证(/etc/hosts.equiv 与 ~/.rhosts)
pam_rootok.soauth,account,password仅在 UID=0 时验证成功
pam_securetty.soauth仅允许 root 从指定的"安全"设备登录
pam_selinux.sosession设置默认的 SELinux 安全上下文
pam_sepermit.soauth,account根据 SELinux 的开关状态决定是否通过认证
pam_setquota.sosession在开始会话时设置或修改磁盘限额
pam_shells.soauth,account检查登录shell的有效性(是否列于 /etc/shells 之中)
pam_stress.soauth,account,password,session模拟失败表现
pam_succeed_if.soauth,account,password,session测试账号自身的属性以实现条件分支逻辑
pam_time.soaccount根据时间段决定是否允许访问
pam_timestamp.soauth,session缓存成功的认证以避免频繁的反复认证
pam_tty_audit.sosession开启或关闭TTY审计(内核默认不审计TTY上的输入)
pam_umask.sosession设置本次会话中的 umask 值
pam_unix.soauth,account,password,session传统UNIX密码验证(/etc/passwd 与 /etc/shadow)
pam_userdb.soauth,account使用 Berkeley DB 数据库验证用户名/密码
pam_usertype.soauth,account,password,session根据 /etc/login.defs 中的设置检查已认证用户的类型(系统账户/普通账户)
pam_warn.soauth,account,password,session向系统日志中记录详细的PAM信息(服务名,终端,用户,远程用户,远程主机)
pam_wheel.soauth,account仅允许 wheel 组成员获得 root 权限
pam_xauth.sosession在用户之间转发 xauth 密钥(cookies)
常见第三方模块
模块RHEL(rpm)Debian(deb)关键词
pam_fprintd.sofprintd-pamlibpam-fprintd指纹
pam_gdm.sogdmgdm3GDM(GNOME显示管理器)
pam_gnome_keyring.sognome-keyring-pamlibpam-gnome-keyringgnome-keyring(GNOME密钥管理器)
pam_cap.solibcaplibpam-capLinux Capabilities
pam_pwquality.solibpwqualitylibpam-pwquality密码复杂度
pam_user_map.somariadb-pammariadb-serverMariaDB
pam_oddjob_mkhomedir.sooddjob-mkhomediroddjob-mkhomedir创建家目录
pam_cifscreds.sopam_cifscredscifs-utilsNTLM(NT LAN Manager)
pam_oath.sopam_oathlibpam-oathOATH(一次性密码)
pam_ssh_agent_auth.sopam_ssh_agent_authlibpam-ssh-agent-authssh-agent
pam_winbind.sosamba-winbind-moduleslibpam-winbindWinbind
pam_sss.sosssd-clientlibpam-sssSystem Security Services Daemon
pam_systemd.sosystemd-pamlibpam-systemdsystemd-logind