Який був би найкращий спосіб вирішити цю проблему glibc?


26

Я адмініструю поле Gentoo Hardened, яке використовує можливості файлів, щоб усунути більшість потреб у встановлених кореневих бінарних файлах (наприклад, /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, мабуть, виконує цю програму, щоб подавити та chmod під псевдотерміналом підлеглого /dev/pts/, і GNU Screen піклується про те, коли ця функція не вдається.

Проблема полягає в тому, що на сторінці сторінки 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;
}

Слідкуйте за цим дією із встановленим бітом у вищезгаданій програмі:

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;
    }
  ...

Аналогічна строфа використовується для перевірки гідів та дозволів. Проблема полягає в тому, що uid, gid і режим повинні відповідати очікуванням (ви, tty, і точно 620; підтвердьте /usr/libexec/pt_chown --help). Якщо ні, то chown()(що вимагає можливостей CAP_CHOWN, CAP_FOWNER виклику бінарного / процесу), намагається здійснити спробу pt_chownзовнішньої помічниці (яка повинна бути встановлена ​​на 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().


"Оскільки проблема полягає в glibc, більш чітким підходом було б вибіркове використання ін'єкції DLL для впровадження фіктивного гранта ()." - Блискуче. Чому я про це не думав? Спасибі. :)
Аарон Джонс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.