当 /etc/{passwd,shadow,group} 是符号链接时,无法使用 useradd/adduser(debian 挤压)

Mic*_*cca 6 debian

当我将 /etc/passwd /etc/shadow /etc/group 从 /etc 移动到 /home 并创建符号链接以使 /etc/{passwd,shadow,group} 分别指向 / 时,我遇到了 useradd 问题家/{passwd,shadow,group}

我无法创建任何用户并让 useradd 输出:

root@client:/home# useradd testuser
Adding user `testuser' ...
Adding new group `testuser' (1000) ...
groupadd: cannot open /etc/group
Run Code Online (Sandbox Code Playgroud)

顺便说一句 useradd 输出是

root@client:/home# adduser testuser
useradd: cannot open /etc/passwd
Run Code Online (Sandbox Code Playgroud)

jau*_*ume 28

为什么useradd拒绝打开一个符号链接/etc/passwd

要回答这个问题,我们需要查看源代码useradd(我在 Ubuntu 12.04 上这样做,在 Debian 上它可能略有不同):

  1. 找出哪个包拥有/usr/sbin/useradd

    $ dpkg-query -S /usr/sbin/useradd
    passwd: /usr/sbin/useradd
    
    Run Code Online (Sandbox Code Playgroud)
  2. 安装源码:

    $ apt-get source passwd
    Reading package lists... Done
    Building dependency tree       
    Reading state information... Done
    Picking 'shadow' as source package instead of 'passwd'
    (...)
    dpkg-source: info: extracting shadow in shadow-4.1.4.2+svn3283
    dpkg-source: info: unpacking shadow_4.1.4.2+svn3283.orig.tar.gz
    dpkg-source: info: applying shadow_4.1.4.2+svn3283-3ubuntu5.1.diff.gz
    (...)
    
    Run Code Online (Sandbox Code Playgroud)
  3. cd 到源目录:

    $ cd shadow-4.1.4.2+svn3283/
    
    Run Code Online (Sandbox Code Playgroud)
  4. 在目录中搜索useradd的源文件,理想情况下应称为useradd.c

    $ find . -name useradd.c
    ./src/useradd.c
    
    Run Code Online (Sandbox Code Playgroud)

    答对了!

  5. 查找错误消息cannot open /etc/passwd(实际上我只搜索cannot open,因为整个字符串不返回任何结果):

    $ grep -B 1 'cannot open' src/useradd.c
    (...)
      if (pw_open (O_RDWR) == 0) {
          fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ());
    (...)
    
    Run Code Online (Sandbox Code Playgroud)

    -B 1 表示在匹配行之前打印 1 行前导上下文。

    这是生成您看到的错误消息的地方。函数pw_open控制是否/etc/passwd可以打开或应该抛出错误。

    pw_open不是 Linux系统调用apropos pw_open不返回任何结果),所以它可能在这个包中实现。让我们搜索一下。

  6. 追踪pw_open导致:

    $ grep -R pw_open * 
    (...)
    lib/pwio.c:int pw_open (int mode)
    (...)
    
    Run Code Online (Sandbox Code Playgroud)

    pw_open 实施是:

    $ grep -A 3 'int pw_open (int mode)' lib/pwio.c 
    int pw_open (int mode)
    {
        return commonio_open (&passwd_db, mode);
    }
    
    Run Code Online (Sandbox Code Playgroud)

    越来越近了,但我们还没有到那里。commonio_open是我们的新目标。

  7. 搜索commonio_open:

    $ grep -R commonio_open *
    (...)
    lib/commonio.c:int commonio_open (struct commonio_db *db, int mode)
    
    Run Code Online (Sandbox Code Playgroud)
  8. 打开lib/commonio.c并滚动到功能commonio_open

    int commonio_open (struct commonio_db *db, int mode)
    {
    (...)
    
        fd = open (db->filename,
                     (db->readonly ? O_RDONLY : O_RDWR)
                   | O_NOCTTY | O_NONBLOCK | O_NOFOLLOW);
    
    Run Code Online (Sandbox Code Playgroud)

    你看到了O_NOFOLLOW吗?这是罪魁祸首(来自man 2 open):

    O_NOFOLLOW 
          If pathname is a symbolic link, then the open fails.
    
    Run Code Online (Sandbox Code Playgroud)

总而言之,useradd.c使用pw_open,然后使用commonio_open/etc/passwd使用open带有选项的系统调用打开O_NOFOLLOW,拒绝符号链接。

虽然符号链接可以在许多(我会说大多数)情况下用作文件的替换,useradd但它非常挑剔并拒绝它,可能是因为符号链接/etc/passwd强烈表明/etc已被篡改。

我为什么要离开passwd/etc

有几个文件/etc需要启动和登录,例如(但不限于): ,fstab,,和在init脚本。任何系统管理员都希望这些文件在那里,而不是符号链接到任何地方。inittabpasswdshadowinit.d//home

所以,即使你能,你应该离开passwd/etc

此外,Linux 中的文件系统结构定义良好,请在此处查看:http : //www.pathname.com/fhs/pub/fhs-2.3.html。也有一章/etc。不建议移动东西。

  • 多么精美且经过深入研究的答案。如果可以的话,我会给它+10。 (2认同)