记一件糗事:术后清理要做好

Xin LI 2021-03-07 10:34

今天和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。

时间线总结:

  1. 2020-12-28(故障引入) 修改/etc/login.access,增加测试项目禁止非wheel组用户登录。
  2. 2020-12-28修正了pam_login_access(8)的安全漏洞。此时故障应显现,但因为进行的是单项测试,并未注意到问题。
  3. 2020-12-28将补丁交给 FreeBSD Security Team,并回退了本地的pam_login_access(8)的改动。问题因此被掩盖。
  4. 2021-02-23上游将修正合并回主线。
  5. 2021-03-02重新编译系统,未重启,因此仍然未能注意到问题。
  6. 2021-03-06重启系统并仔细观察输出,发现问题。
  7. 2021-03-06(修正问题) 修正/etc/login.access的测试配置(回退为未修改前的空白版本)并测试确认恢复。

主要的损失是花费了一些时间去进行ktrace和阅读改过的代码乃至怀疑人生。

问题溯源:

  1. ntpd 无法正确启动的原因:su失败。
  2. su失败的原因是pam_login_access(8)拒绝了ntpd用户的登录操作。
  3. pam_login_access(8)拒绝ntpd的登录操作,是因为/etc/login.access中配置了禁止非wheel组用户登录,而ntpd角色用户并不属于wheel用户组。
  4. 配置/etc/login.access禁止非wheel组用户登录,是为了测试pam_login_access(8)是否能正确地按照配置拒绝用户登录。
  5. 存在此配置的原因在于 a) 直接在笔记本的主要系统中进行了测试,而没有创建独立的测试环境,以及 b) 在测试后未完全回退相关的测试变动。

总结经验,为了避免此类问题再次发生,在进行此类修改时,应采取下列改进:

  1. 将当前系统制作一 ZFS 快照,并采用该快照克隆产生一测试环境。
  2. 在测试环境(jail/chroot,或是以该测试环境作为BE重启),而非主用环境中进行配置及代码修改。
  3. 在修改了主用环境之后,应仔细检查修改过的全部配置(例如通过zfs diff),而不是仅限于依赖 git 回退代码部分的改动。

[返回] [原文链接]