https://lwn.net/Articles/727490/

在长期的安全实践中, 得到结论, 验证用户输入是很好的方法, 但是当验证失败的时候该做什么又有很多不同的意见. 最近在systemd上报的一个bug又关于这个问题引起了一番讨论. 这段时间讨论了哪些用户输入是systemd需要验证的, 哪些是可以放到底层(译注 运行的service体)去做的. 有些systemd不能接受的一直争论的username, 在有些发行版上是没有问题的

6月下旬, mapleray在github上开了一个bug. 具体为新建一个systemd service file, 其中user项为user=0day, 含义为这个service以用户0day来运行. 令人惊讶的是, mapleray发现这个service是以root来运行的. 原因是以数字开始的username在systemd里面是不允许的, 所以这一行被忽略了, 只打印了一个warning在log中. 因为没有user指定, 所以systemd就以root来运行.

Lennart Poettering回复说systemd就是这样的, 0day不是一个合法的用户名, 所以systemd不应该接受. 然而Lennart Poettering并没有解释单单一个warning log是否足够, 他给这个bug丢了一个’notabug’, 然后就关掉了. 其他人不太确定是什么想法

github上有很多讨论, 都是关于0day是否是一个合法的username, 一些发行版是允许了, 不过adduser又是不认这个username的. 但是更底层一点的useradd又是认得, 或者adduser –force-badname也是认的. POSIX标准比这些发行版都要自由一些, 它允许很多种username, 包括以’-‘, ‘.’开头, 名字内包含’.’, 这些东西在命令行程序里面都让人迷惑.

回到root问题 其他人认为不应该把焦点放在username的合法性问题上, 而是无论0day是否被systemd认为是一个合法的username, systemd都不应该在遇到一个非法用户名的时候以root来运行service. ‘rain-1’在github上写到, ‘真正的问题是service是以root运行的, 但是在service file里面写的是0day’, 至少这是一个错误, systemd没有正确的执行

‘RealDolos’总结了一下 这个问题就好比, 如果在service file中ExecStart是非法的时候, systemd不应该随机的挑选一个二进制命令来执行, 所以在username也是非法的情况下, 找不到具体的username, 也不应该挑选一个随机的uid来执行(root就是uid 0, 也可以被随机的选中)

不久之后, 这个bug被标上了trolls, 表示这个bug被锁住了, 只有该项目的成员才能评论. Poettering在bug上给了一个想法总结, 但是并没有解决systemd是否应该忽略通过user=传递过来的非法用户名. Poettering解释说, 考虑到向后兼容的原因(新的service file需要在老版本的systemd也能用), 像语法错误, 未知选项, 或者选项中有非法的语法错误都应该发出警告, 但是service任然需要继续运行. 但是另一方面, 在service file中, 语义上非非法设置(不存在的用户名)会引起严重错误.

同时, Daniel Skowroński把这个bug发到了oss-security邮件列表, 他主要集中在以root运行的这个问题上, 但是也没有阻止其他人关于username合法性的讨论. 然后Ben Tasker提了一个看起来ok的建议, 以root运行会存在以下问题

在service file中, 标注以0day运行, 仔细检查安装包是否创建了user 0day, 这就可以正常工作了. 真希望有人会添加注释, 但是一般情况下, 不会发生, 除非在某个包中存在漏洞, 被远程攻击(因为获得了root权限), 这种东西, 一般没人感兴趣

在这个讨论中, 像其中的几个人一样, 管理者想应该给这个bug标上CVE, 但是Simon McVittie如果标上CVE的话, 这个问题又不是真正的安全问题. Simon McVittie如是说道, 如果对于漏洞的工作定义延伸得太长的话, 把这个bug都定义为漏洞的话, 会降低快速解决CVE的重要性, 影响软件的整体安全水平. 虽然管理者同意McVittie的担心, 但还是坚决得给这个bug标上CVE

总结如下, 有些后台程序监听来自外部的连接, 此时, 正以root运行, (尽管你预期的是这个后台以普通用户运行), 如果有人攻破了这个程序, 并运行了恶意代码, 他就有了root权限

恕我直言, 之前讨论这个service file是如何进入系统的是没有意义的

错误已经发生了, 不管是因为软件包的reviewer忽略了在service file中username的合法性, 还是一些sb把service file文件变成可写(过去我曾见过在公共生产机器上, kernel module文件的权限是0777)

Kurt Seifried给这个bug标上了CVE , CVE-2017-1000082, on July 6.

另一个来自oss-security的子讨论组担心这个bug在github上处于locked状态, Pali Rohár写到, "lock状态阻碍了进一步的讨论, 这对安全相关的问题来说是很不好的". 另一波人又表示只有当bug的讨论偏离了æ­£常的轨道的时候, 才会lock住bug, 不然的话, 就会更糟糕. Alan Coopersmith说

老实说, 对于像这个2边争论不休的问题, 一旦每个人都开始加入的时候, lock这个report是我能看见的仅有的最好的方法. 强制FOSS 维护者接受一大堆垃圾建议是非常可怕的, 会降低安全性. 最终所有的FOSS维护者就会放弃软件维护

即将到来的改变

关于这个bug, 以及以root代替非法username是否需要改变, 在systemd这一层有很多重复的讨论. Systemd开发者Zbigniew Jędrzejewski-Szmek回复道, 估计会这样

我同意当service file中有关键的项解析失败的失败, 应该完全拒绝这个service file, 比如说ExecStart, WorkingDirectory, 或者User, 但是不要一开始就这么想

当添加一个新选项的时候, 这个service file总是会被用在老的systemd的系统上, 老的systemd就会警告, 忽略它不懂得这些选项, 简单来说, 很多配置选项会变成不可用即使在相同的架构上, 只是编译选项有一些不同. 在这种情况下, 当前的警告, 忽略的做法也相当于提供了'软降级'

为了正确的处理这个事情, 我们需要指定哪些选项是1, 很重要的, 2,在所有的编译选项和架构都支持的, 然后添加一个通用的机制来处理fatal error

很多关于systemd级别的讨论, 转到了为什么很多合法的username会被systemd拒绝. Poettering给了个漫长的防御经验, 指出不同意POSIX规则. 他说, systemd username的规则来自很多发行版最通用的子集, 他还指出, 问题中的用户是系统用户, 而不是像常规用户那样,会起像'j.random.hacker'(这种用户名也会被systemd拒绝, 就跟0day一样)

需要注意的是, 系统用户和常规用户有着不同的概念, 系统用户是系统服务需要的, 通常是开发者, 打包者, 管理员, 他们都懂这些东西, 并能起个好名字

"keszybz" on GitHub 创建了一个pull request, 用来fix root权限运行的这个问题, 已经合并到master分支. 与此同时, systemd的版本, 234, 都应该包含这个fix. 从release 233 开始之后的变更, 计划在7.12 merge. (即使并没有因为这篇文章被标记). 所以以后随着systemd的更新, 将不会存在, 当输入一个非法user的时候, 以root权限来运行

为什么这个bug会引起这样的一个骚动呢? 在Jędrzejewski-Szmek的PR里面, Justin Azoff给了一个恰当的解释

如果我把User=app写成User=app0, 这个service 就不会启动, 如果写成User=0app的话, 这个service就会以root运行, 这种行为不可靠的

如果service file 里面写着User=something, 就是说administrator 并不想以root来运行这个service

有一个争论就是, systemd不应该检查自己的username, 而应该通过getpwnam()来咨询系统, 但是systemd自以为是这件事该这么做, 那件事该这么做, 所以systemd和系统有冲突也不是一件奇怪的事情, 就像Poettering 指出, 很多时候, systemd是一个自由软件, 用户, 发行者, 其他所有人都可以依照自己的需求来修改. 所以它是否工作得合理, 有些行为就会像中立