Что было бы лучшим способом обойти эту проблему glibc?

26

Я управляю коробкой Gentoo Hardened, которая использует файловые возможности для устранения большей части необходимости в двоичных файлах setuid-root (например, /bin/pingимеет CAP_NET_RAW и т. Д.).

Infact, единственный двоичный файл, который я оставил, является этим:

abraxas ~ # find / -xdev -type f -perm -u=s
/usr/lib64/misc/glibc/pt_chown
abraxas ~ # 

Если я удаляю бит setuid или перемонтирую мою корневую файловую систему nosuid, sshd и GNU Screen перестают работать, потому что они вызывают grantpt(3)свои главные псевдотерминалы, и glibc, по-видимому, выполняет эту программу для chown и chmod подчиненного псевдотерминала /dev/pts/, и GNU Screen заботится о том, когда эта функция выходит из строя.

Проблема в том, что man-страница grantpt(3)явно заявляет, что в Linux, с установленной devptsфайловой системой, такой вспомогательный двоичный файл не требуется; ядро автоматически установит UID & GID ведомого устройства к реальному UID & GID процесса, который открылся /dev/ptmx(путем вызова getpt(3)).

Я написал небольшой пример программы, чтобы продемонстрировать это:

#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
    int master;
    char slave[16];
    struct stat slavestat;
    if ((master = getpt()) < 0) {
        fprintf(stderr, "getpt: %m\n");
        return 1;
    }
    printf("Opened a UNIX98 master terminal, fd = %d\n", master);
    /* I am not going to call grantpt() because I am trying to
     * demonstrate that it is not necessary with devpts mounted,
     * the owners and mode will be set automatically by the kernel.
     */
    if (unlockpt(master) < 0) {
        fprintf(stderr, "unlockpt: %m\n");
        return 2;
    }
    memset(slave, 0, sizeof(slave));
    if (ptsname_r(master, slave, sizeof(slave)) < 0) {
        fprintf(stderr, "ptsname: %m\n");
        return 2;
    }
    printf("Device name of slave pseudoterminal: %s\n", slave);
    if (stat(slave, &slavestat) < 0) {
        fprintf(stderr, "stat: %m\n");
        return 3;
    }
    printf("Information for device %s:\n", slave);
    printf("    Owner UID:  %d\n", slavestat.st_uid);
    printf("    Owner GID:  %d\n", slavestat.st_gid);
    printf("    Octal mode: %04o\n", slavestat.st_mode & 00007777);
    return 0;
}

Наблюдайте это в действии с удаленным битом setuid в вышеупомянутой программе:

aaron@abraxas ~ $ id
uid=1000(aaron) gid=100(users) groups=100(users)
aaron@abraxas ~ $ ./ptytest 
Opened a UNIX98 master terminal, fd = 3
Device name of slave pseudoterminal: /dev/pts/17
Information for device /dev/pts/17:
    Owner UID:  1000
    Owner GID:  100
    Octal mode: 0620

У меня есть только несколько идей относительно того, как обойти эту проблему:

1) Заменить программу на скелет, который просто возвращает 0.

2) Патч grantpt () в моем libc ничего не делает.

Я могу автоматизировать оба из них, но есть ли у кого-нибудь рекомендации по одному над другим или рекомендации, как еще решить эту проблему?

Как только это решится, я наконец смогу mount -o remount,nosuid /.

Аарон Джонс
источник
Пока я жду ответа, я пошел с подходом 1, и sshd и GNU Screen все еще работают.
Аарон Джонс
Какие именно программы терпят неудачу? Возможно, они сломаны и проверяют не на pty(как следует), а на программу?
vonbrand
Любая программа, которая не имеет CAP_CHOWN и CAP_FOWNER, вызывает grantpt () и вспомогательный двоичный файл, не запущенный с EUID == 0, будет иметь ненулевой код возврата для grantpt (), и программы ДОЛЖНЫ прервать создание PTS, когда это произойдет. согласно ptmx (4).
Аарон Джонс
3
Это «решение» дает мне волю ... в лучшем случае оно исправляет ошибку, возможно, создает новую ошибку, в худшем случае создает серьезную уязвимость безопасности. Пожалуйста, обсудите это с разработчиками glibc.
vonbrand
3
Тогда сообщите об этом людям glibc.
vonbrand

Ответы:

2

Если ваш glibc достаточно актуален и devpts настроен правильно, не нужно вообще вызывать pt_chownпомощника.

Возможно, вы столкнулись с известной / потенциальной проблемой при удалении setuid-root pt_chown.

grantpt()поддерживается devfsс Glibc-2.7 , были внесены изменения в Glibc-2.11 , хотя так , что вместо того , чтобы явно проверяя DEVFS_SUPER_MAGIC, он проверяет , вместо того, чтобы увидеть , если это необходимо , чтобы сделать любую работу , прежде чем пытаться chown()или падения обратно в призыве pt_chown.

От glibc-2.17/sysdeps/unix/grantpt.c

  ...
  uid_t uid = __getuid ();
  if (st.st_uid != uid)
    {
       if (__chown (buf, uid, st.st_gid) < 0)
       goto helper;
    }
  ...

Аналогичная строфа используется для проверки gid и разрешений. Проблема в том, что uid, gid и mode должны соответствовать ожиданиям (вы, tty и ровно 620; подтвердите с помощью /usr/libexec/pt_chown --help). Если нет, то chown()(что потребовало бы возможности CAP_CHOWN, CAP_FOWNER вызывающего двоичного файла / процесса) выполняется попытка, а если это не удается, выполняется попытка pt_chownвнешнего помощника (который должен быть setuid-root). Для того, pt_chownчтобы иметь возможность использовать возможности, он (и, следовательно, ваш glibc) должен быть скомпилирован HAVE_LIBCAP. Тем не менее , кажется , что pt_chownэто (как из Glibc-2,17 , и , как вы заметили , что Вы не высказали версию) жестко хотеть geteuid()==0 независимо от HAVE_LIBCAP, соответствующего кода из glibc-2.17/login/programs/pt_chown.c:

  ...
  if (argc == 1 && euid == 0)
    {
#ifdef HAVE_LIBCAP
  /* Drop privileges.  */
     if (uid != euid)
  ...
#endif
    /* Normal invocation of this program is with no arguments and
       with privileges.  */
    return do_pt_chown ();
  }
...
  /* Check if we are properly installed.  */
  if (euid != 0)
    error (FAIL_EXEC, 0, gettext ("needs to be installed setuid `root'"));

(Ожидание того, что geteuid()==0прежде чем пытаться использовать возможности, похоже, не совсем соответствует духу возможностей, я бы остановился на регистрации ошибки на этом.)

Потенциальный обходной путь может заключаться в предоставлении CAP_CHOWN, CAP_FOWNER затронутым программам, но я действительно не рекомендую этого, поскольку вы, конечно, не можете ограничить это ptys.

Если это не поможет вам решить, заплаты sshdи screenчуть менее неприятен , чем латать Glibc. Поскольку проблема лежит в glibc, более чистый подход - выборочное использование DLL-инъекций для реализации фиктивной переменной grantpt().

mr.spuratic
источник
«Поскольку проблема заключается в glibc, более чистый подход - выборочное использование DLL-инъекций для реализации фиктивной grantpt ()». - Отлично. Почему я не подумал об этом? Спасибо. :)
Аарон Джонс