记一件糗事:术后清理要做好
今天和 Henry Hu 聊起 iichid
已经进了 FreeBSD 的主线,
想起自己之前鼠标啥的还是都在用 sysmouse
,觉得既然来都来了就索性都改成 evdev
的输入设备算了?
改了 /boot/loader.conf
并增加了下列设置:
iichid_load="YES"
usbhid_load="YES"
hms_load="YES"
hw.usb.usbhid.enable="1" # 防止uhid(4)接管
重启。等等,一闪而过的那是什么?
ntpd is not allowed to log in on /dev/console
为什么 ntpd
(这是 FreeBSD 用于启动 ntpd
的角色用户) 不能在 /dev/console
登录?
看了一眼日志,发现有:
pam_acct_mgmt: Authentication error
等等,这个跟 PAM 有什么关系?
检查了一下 /etc/pam.d/su
和系统默认的一致,这文件好几年没人动过了。唯一不一样的文件是 /etc/pam.d/system
,
这个策略是许多其他 PAM 策略引用的,系统默认的是:
account required pam_login_access.so
而我的是:
account required pam_login_access.so debug
想起来了……去年年底的时候 secteam@ 收到一封报告说 FreeBSD 12.2 里的 pam_login_access(8) 不起作用了。 这个模块的主要功能是检查用户配置的 login.access(5) 并根据策略决定是否允许登录。
我之前并没用过这个模块,收到报告之后,第一件事是测试是否能重现问题。长话短说,我在 /etc/login.access
中增加了这些内容:
+:wheel:ALL
-:ALL:ALL
其含义是,如果用户在 wheel
组,则允许登录,否则拒绝登录。
接下来我创建了一个新的非 wheel
组的用户,发现能够重现问题。查看历史,发现是在 这里 引入的问题,
具体来说是代码重构时,逻辑被搞反了(此处原作者故意用了 ;
并且加了注释,但重构时没有被重构者和复核的人注意到),由于其他功能正常,于是也就合并了。
修起来并不复杂:
diff --git a/lib/libpam/modules/pam_login_access/login_access.c b/lib/libpam/modules/pam_login_access/login_access.c
index 9496081d362e..719808858dac 100644
--- a/lib/libpam/modules/pam_login_access/login_access.c
+++ b/lib/libpam/modules/pam_login_access/login_access.c
@@ -137,10 +137,10 @@ list_match(char *list, const char *item,
if (match != NO) {
while ((tok = strtok((char *) 0, listsep)) && strcmp(tok, "EXCEPT")) {
/* VOID */ ;
- if (tok == NULL || list_match((char *) 0, item, match_fn,
- login_access_opts) == NO) {
+ }
+ if (tok == NULL ||
+ list_match((char *) 0, item, match_fn, login_access_opts) == NO) {
return (match);
- }
}
}
return (NO);
修好之后我手工进行了功能测试,一切正常。我把补丁放到了 secteam@ 的票上,同时请报告的人也去测试一下。因为是安全问题不能立即公开,考虑到我的笔记本并不使用这个功能, 于是我就在本地回退了相关的改动。
这之后一切平静,直到 给笔记本喝了水,在等待新键盘的这段时间,
我的补丁随安全公告 FreeBSD-SA-21:03.pam_login_access
进入了主线。既然修好了就更新一下系统好了,然后重启的时候就没特别注意看日志,直到今天换成 iichid
。
时间线总结:
2020-12-28
(故障引入) 修改/etc/login.access
,增加测试项目禁止非wheel
组用户登录。2020-12-28
修正了pam_login_access(8)
的安全漏洞。此时故障应显现,但因为进行的是单项测试,并未注意到问题。2020-12-28
将补丁交给 FreeBSD Security Team,并回退了本地的pam_login_access(8)
的改动。问题因此被掩盖。2021-02-23
上游将修正合并回主线。2021-03-02
重新编译系统,未重启,因此仍然未能注意到问题。2021-03-06
重启系统并仔细观察输出,发现问题。2021-03-06
(修正问题) 修正/etc/login.access
的测试配置(回退为未修改前的空白版本)并测试确认恢复。
主要的损失是花费了一些时间去进行 ktrace
和阅读改过的代码乃至怀疑人生。
问题溯源:
- ntpd 无法正确启动的原因:
su
失败。 su
失败的原因是pam_login_access(8)
拒绝了ntpd
用户的登录操作。pam_login_access(8)
拒绝ntpd
的登录操作,是因为/etc/login.access
中配置了禁止非wheel
组用户登录,而ntpd
角色用户并不属于wheel
用户组。- 配置
/etc/login.access
禁止非wheel
组用户登录,是为了测试pam_login_access(8)
是否能正确地按照配置拒绝用户登录。 - 存在此配置的原因在于 a) 直接在笔记本的主要系统中进行了测试,而没有创建独立的测试环境,以及 b) 在测试后未完全回退相关的测试变动。
总结经验,为了避免此类问题再次发生,在进行此类修改时,应采取下列改进:
- 将当前系统制作一 ZFS 快照,并采用该快照克隆产生一测试环境。
- 在测试环境(jail/chroot,或是以该测试环境作为BE重启),而非主用环境中进行配置及代码修改。
- 在修改了主用环境之后,应仔细检查修改过的全部配置(例如通过
zfs diff
),而不是仅限于依赖 git 回退代码部分的改动。