Was wäre der beste Weg, um dieses Problem zu umgehen?

26

Ich verwalte eine Gentoo Hardened-Box, die /bin/pingDateifunktionen verwendet, um die Notwendigkeit von setuid-root-Binärdateien (z. B. CAP_NET_RAW usw.) zum größten Teil zu beseitigen .

Tatsächlich ist die einzige Binärdatei, die ich noch habe, die folgende:

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

Wenn ich das setuid-Bit entferne oder mein Root-Dateisystem wieder nosuideinbinde, funktionieren sshd und GNU Screen nicht mehr, weil sie grantpt(3)ihre Master-Pesudoterminals aufrufen und glibc anscheinend dieses Programm ausführt, um das Slave-Pseudoterminal darunter zu chownen und chmodieren /dev/pts/, und GNU Screen kümmert sich darum, wann diese Funktion funktioniert scheitert.

Das Problem ist, dass in der Manpage für grantpt(3)explizit angegeben ist, dass unter Linux bei devptseingehängtem Dateisystem keine solche Hilfsbinärdatei erforderlich ist. Der Kernel setzt automatisch die UID & GID des Slaves auf die reale UID & GID des Prozesses, der geöffnet wurde /dev/ptmx(durch Aufrufen getpt(3)).

Ich habe ein kleines Beispielprogramm geschrieben, um dies zu demonstrieren:

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

Beobachten Sie es in Aktion, während das setuid-Bit des oben genannten Programms entfernt ist:

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

Ich habe nur ein paar Ideen, wie ich dieses Problem umgehen kann:

1) Ersetzen Sie das Programm durch ein Skelett, das einfach 0 zurückgibt.

2) Patch grantpt () in meiner libc, um nichts zu tun.

Ich kann beide automatisieren, aber hat jemand eine Empfehlung für eine über die andere, oder Empfehlungen, wie man dies sonst lösen kann?

Sobald dies gelöst ist, kann ich endlich mount -o remount,nosuid /.

Aaron Jones
quelle
Während ich auf eine Antwort warte, habe ich Ansatz 1 gewählt und sshd und GNU Screen funktionieren immer noch.
Aaron Jones
Was genau sind die Programme, die fehlschlagen? Vielleicht sind sie kaputt und suchen nicht nach dem pty(wie sie sollen) sondern nach dem Programm?
Vonbrand
Jedes Programm, das nicht über CAP_CHOWN und CAP_FOWNER verfügt, grantpt () aufruft und die Hilfsbinärdatei nicht mit EUID == 0 gestartet wird, hat einen Rückkehrcode ungleich Null für grantpt (), und Programme MÜSSEN in diesem Fall die PTS-Erstellung abbrechen gemäß ptmx (4).
Aaron Jones
3
Diese "Lösung" gibt mir die Qual der Wahl ... im besten Fall wird ein Fehler beseitigt, es wird wahrscheinlich ein neuer Fehler erzeugt, im schlimmsten Fall wird eine ernsthafte Sicherheitslücke geschaffen. Bitte nehmen Sie dies mit den glibc-Entwicklern auf.
Vonbrand
3
Dann melde dies den glibc Leuten.
Vonbrand

Antworten:

2

Wenn Ihre Glibc einigermaßen aktuell ist und devpts korrekt eingerichtet ist, sollte es nicht nötig sein, den pt_chownHelfer überhaupt aufzurufen .

Möglicherweise stößt du auf ein bekanntes / potenzielles Problem beim Entfernen von setuid-root von pt_chown.

grantpt()unterstützt devfsvon glibc-2.7 , wurden Änderungen in glibc-2.11 vorgenommen, so dass statt explizit zu prüfen DEVFS_SUPER_MAGIC, ob Arbeit erforderlich ist, bevor versucht wird chown()oder auf das Aufrufen zurückgegriffen wird pt_chown.

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

Eine ähnliche Zeilengruppe wird zum Überprüfen der GID und der Berechtigungen verwendet. Der Haken ist, dass uid, gid und mode den Erwartungen entsprechen müssen (you, tty und genau 620; bestätigen mit /usr/libexec/pt_chown --help). Wenn dies nicht chown()der Fall ist (was die Fähigkeiten CAP_CHOWN, CAP_FOWNER der aufrufenden Binärdatei / des aufrufenden Prozesses erfordern würde), wird versucht, das pt_chownexterne Hilfsprogramm (das setuid-root sein muss) zu verwenden , wenn dies fehlschlägt . Um pt_chownFähigkeiten nutzen zu können, muss sie (und damit Ihre Glibc) mit kompiliert worden sein HAVE_LIBCAP. Es sieht jedoch so aus , als ob pt_chown(ab glibc-2.17 und wie Sie bereits bemerkt haben, obwohl Sie die Version nicht angegeben haben) fest programmiert ist, um geteuid()==0 unabhängig vom HAVE_LIBCAPrelevanten Code Folgendes zu wollen 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'"));

(Wenn geteuid()==0ich erwarte, bevor ich versuche, Fähigkeiten zu nutzen, scheint das nicht wirklich im Geiste der Fähigkeiten zu sein, würde ich damit fortfahren, einen Fehler in dieser zu protokollieren.)

Eine mögliche Problemumgehung könnte darin bestehen, den betroffenen Programmen CAP_CHOWN und CAP_FOWNER zuzuweisen, aber das kann ich wirklich nicht empfehlen, da Sie das natürlich nicht auf ptys beschränken können.

Wenn das nicht hilft, lösen Sie es, das Patchen sshdund screenist etwas weniger unangenehm als glibc Patchen. Da das Problem jedoch in glibc liegt, wäre ein saubererer Ansatz die selektive Verwendung der DLL-Injektion zur Implementierung eines Dummy grantpt().

mr.spuratic
quelle
"Da das Problem in glibc liegt, wäre ein sauberer Ansatz die selektive Verwendung von DLL-Injection zur Implementierung eines Dummy-Grantpt ()." -- Brillant. Warum habe ich nicht daran gedacht? Vielen Dank. :)
Aaron Jones