Linux PAM的一些记录
本文内容感谢: 金步国的 Linux PAM 学习笔记
最新文档:Linux PAM (Pluggable Authentication Modules for Linux) project
PAM配置文件格式
类型(type) | 控制标志(control-flag) | 模块路径(module-path) | 模块参数(module-arguments) |
---|---|---|---|
Linux PAM 能做什么 | 用于控制模块在流程栈中的行为 有两种语法,一种是简单语法,一种是复杂语法 | 表示模块的文件系统路径 既可以是绝对路径,也可以是相对路径(相对于 /lib/security/ 或 /lib64/security/ 目录) | 用于控制模块的行为 |
- CentOS Linux release 8.5.2111 X86_64位 PAM模块位于 的路径为
/usr/lib64/security/pam_xx.so
- Ubuntu 22.04.2 LTS aarch64 (Linux 22-11-6-0759 5.15.0-1038-oracle) PAM模块位于 的路径为
/usr/lib/aarch64-linux-gnu/security/pam_xx.so
- 例子
#类型(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
- 以”#”号开头的行表示注释,空行将被忽略
- 若在类型前加上”-“(减号)前缀,则表示即使模块不存在,也不会影响认证结果,更不会将此事件记录到日志中,对于那些可有可无的模块来说,这一特性非常有用
- 如果想在控制标志中使用空格,那么必须在该参数外围加上中括号。例如
passwd=mada [query=select user_name from internet_service where user_name='%u' and password=PASSWORD('%p')]
- 字符串中的”[“无需转义,但”
]
“必须转义为”\]
“
注意,配置文件中的任何语法错误都会导致认证过程失败,同时,失败信息将会被记录到系统日志(syslog)中。
类型
auth
验证账户的凭据。注意,这里的”凭据”不仅仅指密码,而是泛指一切认证方式,例如:一次性密码、指纹、短信、IP地址、二维码。除验证凭据之外,还可以进一步执行更多的关联操作,例如:修改账户所属的组、显示特定的提示信息、赋予账户某些权限。
account
验证账户自身的权限。例如:账户是否已禁用、凭据是否已过期、是否只能在特定的时间段有效、是否已经达到最大登录数量、是否属于特定的组、是否允许控制台登录、是否允许远程登录。注意,账户自身的权限与是否通过凭据验证无关,例如,禁止从远程登录的账户,即使成功通过了凭据验证,也仍然无法从远程登录。
password
管理账户的凭据。例如:更新账户密码并确保新密码符合复杂性要求、以 SHA512 方式存储密码。注意,这里的”凭据”不仅仅指密码,而是泛指一切认证方式,例如:指纹、声纹、虹膜、二维码。此类型通常都与 auth 类型存在很强的耦合性。
session
在用户获得服务之前/后需要执行的各种操作。例如:创建家目录、设置与撤销环境变量、显示今日消息(motd)、设置 umask 、挂载目录。
控制标志
简单语法
required
栈成功的必要条件。即使失败,也要继续执行此栈中所有同类型的后继模块(也就是”类型”字段值相同的后继行),但此栈最终必定返回失败结果。如果不想让用户知道被哪个模块拒绝,可以使用此关键字。
requisite
栈成功的必要条件。一旦失败,将立即终止此栈,并立即返回失败结果。栈的返回值取决于第一个失败的”required”或”requisite”模块。如果想防止用户通过不安全的介质输入密码,可使用此关键字。注意,使用此关键字使得攻击者探测帐户名称成为可能,所以在实践中需要权衡可能暴露密码与可能暴露账户之间孰轻孰重。
sufficient
栈成功的充分条件(注意,这样描述并不准确)。(1)当此模块成功时,如果所有先前的”required”模块也都成功,那么立即终止此栈,并立即返回成功结果(此栈中的剩余模块将被舍弃),否则继续执行栈中的后继模块。(2)当此模块失败时,直接忽略此模块(不影响栈的最终结果),并继续执行栈中的后继模块。
optional
栈成功的可选条件。只有当此模块是栈中同类型模块(也就是”类型”字段值相同)中的唯一模块时,此模块的成败才有意义。否则,直接忽略此模块(不影响栈的最终结果),仅在日志中记录失败信息。
include
从”模块路径”(module-path)字段指定的文件中提取所有同类型的行(也就是”类型”字段值相同的行),直接插入此处(等价于直接写在此处)。注意,此关键字并不创建子栈。
substack
从”模块路径”(module-path)字段指定的文件中提取所有同类型的行(也就是”类型”字段值相同的行),组成一个子栈,再将该子栈的运行结果作为该行的结果插入此处(类似于程序设计中的函数调用)。例如,子栈中的”requisite”失败只会导致子栈本身终止并返回失败结果,而不会导致父栈立即终止。
复杂语法
复杂语法,表现为用中括号包围的一组以空格分隔的”value=action”列表,形如 [value1=action1 value2=action2 …] 。由于很少使用,这里就略过此种语法的介绍。
Linux PAM 内置模块
模块 | 类型 | 简介 |
---|---|---|
pam_access.so | auth,account,password,session | 基于来源(主机/网络/终端/\$DISPLAY/服务名)的访问控制 |
pam_debug.so | auth,account,password,session | 调试PAM栈 |
pam_deny.so | auth,account,password,session | 无条件的拒绝访问 |
pam_echo.so | auth,account,password,session | 显示文本消息 |
pam_env.so | auth,session | 设置与撤销环境变量(也包括PAM变量) |
pam_exec.so | auth,account,password,session | 调用外部命令 |
pam_faildelay.so | auth | 设置认证失败时的阻塞延迟 |
pam_faillock.so | auth,account | 锁定连续认证失败的账户 |
pam_filter.so | auth,account,password,session | 在用户与应用程序之间建立输入输出(STDIN/STDOUT)过滤器 |
pam_ftp.so | auth | 可插拔的匿名FTP访问模式(不安全) |
pam_group.so | auth | 将用户额外添加到特定组中 |
pam_issue.so | auth | 更改用户的提示文件(issue) |
pam_keyinit.so | session | 在默认内核会话密钥环之外额外创建新的内核会话密钥环 |
pam_lastlog.so | auth,account,session | 显示账户的上次登录时间、锁定长期不登录的帐户 |
pam_limits.so | session | 设置会话的资源限制 |
pam_listfile.so | auth,account,password,session | 基于各种属性(tty|user|rhost|ruser|group|shell)执行访问控制 |
pam_localuser.so | auth,account,password,session | 仅允许本地用户(/etc/passwd)访问 |
pam_loginuid.so | session | 设置进程的 loginuid 属性(主要用于审计目的) |
pam_mail.so | auth,session | 提示用户有新邮件 |
pam_mkhomedir.so | session | 在开始会话前创建用户的家目录 |
pam_motd.so | session | 显示”今日消息”(motd) |
pam_namespace.so | session | 为会话设置私有名字空间 |
pam_nologin.so | auth,account | 禁止非 root 账户登录 |
pam_permit.so | auth,account,password,session | 无条件的允许访问(小心使用) |
pam_pwhistory.so | password | 防止重复使用旧密码 |
pam_rhosts.so | auth | 使用 rlogin/rsh 网络访问认证(/etc/hosts.equiv 与 ~/.rhosts) |
pam_rootok.so | auth,account,password | 仅在 UID=0 时验证成功 |
pam_securetty.so | auth | 仅允许 root 从指定的”安全”设备登录 |
pam_selinux.so | session | 设置默认的 SELinux 安全上下文 |
pam_sepermit.so | auth,account | 根据 SELinux 的开关状态决定是否通过认证 |
pam_setquota.so | session | 在开始会话时设置或修改磁盘限额 |
pam_shells.so | auth,account | 检查登录shell的有效性(是否列于 /etc/shells 之中) |
pam_stress.so | auth,account,password,session | 模拟失败表现 |
pam_succeed_if.so | auth,account,password,session | 测试账号自身的属性以实现条件分支逻辑 |
pam_time.so | account | 根据时间段决定是否允许访问 |
pam_timestamp.so | auth,session | 缓存成功的认证以避免频繁的反复认证 |
pam_tty_audit.so | session | 开启或关闭TTY审计(内核默认不审计TTY上的输入) |
pam_umask.so | session | 设置本次会话中的 umask 值 |
pam_unix.so | auth,account,password,session | 传统UNIX密码验证(/etc/passwd 与 /etc/shadow) |
pam_userdb.so | auth,account | 使用 Berkeley DB 数据库验证用户名/密码 |
pam_usertype.so | auth,account,password,session | 根据 /etc/login.defs 中的设置检查已认证用户的类型(系统账户/普通账户) |
pam_warn.so | auth,account,password,session | 向系统日志中记录详细的PAM信息(服务名,终端,用户,远程用户,远程主机) |
pam_wheel.so | auth,account | 仅允许 wheel 组成员获得 root 权限 |
pam_xauth.so | session | 在用户之间转发 xauth 密钥(cookies) |
基于来源的访问控制 pam_access.so
基于来源的访问控制 (auth,account,password,session). 通过man pam_access
可以看到规则的配置文件默认为/etc/security/access.conf
,如果有其他自定义的配置文件,放置于/etc/security/access.d/
pam_access.so [ debug ] [ nodefgroup ] [ noaudit ] [ accessfile=配置文件 ] [ fieldsep=分隔符 ] [ listsep=分隔符 ]
- 例子
#按照从上到下的顺序依次匹配,并且一旦匹配成功就立即返回,所以策略的顺序非常重要。
#允许 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 表达式。
拒绝访问 pam_deny.so
- auth,account,password,session
- 此模块没有任何选项或参数,用于无条件的拒绝访问,常用于默认配置(other)之中。
显示文本消息 pam_echo.so
- 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)
- 所有其他不能识别的替换标记都只简单的去掉前导百分号(例如”%%”将被替换为”%”)。
设置与撤销环境变量 pam_env.so
- auth,session
- pam_env.so [debug] [conffile=配置文件] [envfile=变量文件] [readenv=0|1]
- 默认的配置文件为/etc/security/pam_env.conf
- 首先,执行配置文件(conffile)(默认=/etc/security/pam_env.conf)中的指令;然后,根据 readenv 的指示(1=是[默认]|0=否)决定是否继续加载变量文件(envfile)(默认=/etc/environment)。
变量名 [DEFAULT=[默认值]] [OVERRIDE=[指定值]]
- 例子
#/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=\@
- 井号(#)开头的行表示注释、空行将被忽略
调用外部命令 pam_exec.so
- 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 之一)
- 父进程的用户决定了外部命令(子进程)能够看到的环境变量
- 例子
#可用于向 /etc/pam.d/passwd 中添加的行
#在本地密码被修改的同时重建NIS数据库(以EUID身份执行 make -C /var/yp 命令)
password optional pam_exec.so seteuid /usr/bin/make -C /var/yp
传统UNIX密码验证 pam_unix.so
- 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
pam_cracklib
- 格式:pam_cracklib.so 参数1 参数2 参数3
- 这是一个用于检查用户输入密码强度的PAM插件。
- 工作方式:先调用Cracklib检查密码是否存在于密码字典中,如果不存在,则按照相关配置进行创建
- 例子 ( /etc/pam.d/passwd )
password required pam_cracklib.so difok=3 minlen=15 dcredit=2 ocredit=2
参数 | 作用 |
---|---|
debug | 开启debug模式,将信息写入到syslog中 |
authtok_type=XXX | 原文是 The default action is for the module to use the following prompts when requesting passwords: “New UNIX password: ” and “Retype UNIX password: “. The example word UNIX can be replaced with this option, by default it is empty。(是不是说可以替代UNIX呢?改了没用。也有说是认证的方式,为了GPT,告诉我有 password :用于验证用户密码的类型。oldpassword :用于验证用户更改密码时输入的旧密码。newpassword :用于验证用户更改密码时输入的新密码。tokencode :用于验证使用令牌的用户身份。oldtokencode :用于验证用户更改令牌代码时输入的旧代码。newtokencode :用于验证用户更改令牌代码时输入的新代码。试了下,好像都不行。 默认为空。也有后面就只写复杂度的,多少长度,字符等。具体作用,待大佬指点 了。) |
retry=N | 允许提示的次数,默认是1 |
difok=N | 指定新密码中必须与旧密码不同的字符数。默认为 5 个字符 |
minlen=N | 指定密码的最小长度。默认为 9 个字符 |
dcredit=N | 如果N大于0,则表示密码中包含的数字上限,如果N小于0,则表示密码中必须包含的数字个数 |
ucredit=N | 如果N大于0,则表示密码中包含的大写字母上限,如果N小于0,则表示密码中必须包含的大写字母个数 |
lcredit=N | 如果N大于0,则表示密码中包含的小写字母上限,如果N小于0,则表示密码中必须包含的小写字母个数 |
ocredit=N | 如果N大于0,则表示密码中包含的特殊字符数上限,如果N小于0,则表示密码中必须包含的特殊字符数个数 |
minclass=N | 指定新密码中必须包含的字符类别数,一共4类,大写,小写,数字,特殊字符 |
maxrepeat=N | 指定新密码中允许的最大字符重复次数,如果为0,则不检查此项 |
maxsequence=N | 指定新密码中允许的最大顺序字符数,如‘12345’,‘abcdefg’,如果为0,则不检查此项 |
maxclassrepeat=N | 拒绝包含 N 个以上同类连续字符的密码,如果为0,则不检查此项 |
reject_username | 禁止新密码中包含用户名(不管是顺序还是逆序)。(全部) |
gecoscheck | 检查新密码中是否包含 GECOS 字段(通常为用户全名)中长度超过 3 个字符的直式或反式单词。如果发现任何此类单词,新密码将被拒绝。 |
enforce_for_root | 强制 root 用户使用此模块进行密码检查。只提示/检查失败,但是不影响密码修改。因为root不会要求提供就旧密码(,也不受制于密码策略?? 需要验证) |
use_authtok | 该参数用于强制模块不提示用户输入新密码,而是使用先前堆叠的密码模块提供的密码。在某个与密码相关的验证模块后使用此选项。 理解下,在centos7下 /etc/pam.d/system-auth中包含: password sufficient pam_unix.so md5 shadow nullok try_first_pass use_authtok |
dictpath=PATH | 指定cracklib目录路径 |
Gecos Field:通常,这是一组以逗号分隔的值,用于说明与用户相关的更多详细信息。GECOS字段的格式表示以下信息:
用户全名
地址和门牌号码或联系人
办公电话号码
家庭电话号码
任何其他联系信息