Quale sarebbe il modo migliore per aggirare questo problema glibc?


26

Gestisco una scatola indurita di Gentoo che utilizza le capacità dei file per eliminare la maggior parte della necessità di file binari setuid-root (ad es. /bin/pingHa CAP_NET_RAW, ecc.).

Infatti, l'unico binario che mi rimane è questo:

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

Se rimuovo il bit setuid, o rimonto il mio filesystem di root nosuid, sshd e GNU Screen smettono di funzionare, perché chiamano i grantpt(3)loro principali pesudoterminals e glibc apparentemente esegue questo programma per chown e chmod lo pseudoterminal slave sotto /dev/pts/, e GNU Screen si preoccupa quando questa funzione non riesce.

Il problema è che la manpage per grantpt(3)esplicitamente afferma che sotto Linux, con il devptsfilesystem montato, non è richiesto tale binario di supporto; il kernel imposterà automaticamente l'UID e GID dello slave sull'UID e GID reali del processo aperto /dev/ptmx(chiamando getpt(3)).

Ho scritto un piccolo programma di esempio per dimostrarlo:

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

Osservalo in azione con il bit setuid sul programma di cui sopra rimosso:

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

Ho solo alcune idee su come aggirare questo problema:

1) Sostituisci il programma con uno scheletro che restituisce semplicemente 0.

2) Patch grantpt () nella mia libc per non fare nulla.

Posso automatizzare entrambi, ma qualcuno ha una raccomandazione l'una sull'altra o raccomandazioni su come altro risolverlo?

Una volta risolto, finalmente posso mount -o remount,nosuid /.


Mentre aspetto una risposta, sono andato con l'approccio 1 e sshd e GNU Screen funzionano ancora.
Aaron Jones,

Quali sono esattamente i programmi che falliscono? Forse si sono rotti e controllare non per il pty(come dovrebbero), ma per il programma?
vonbrand

Qualsiasi programma che non ha CAP_CHOWN e CAP_FOWNER, chiama grantpt () e il binario helper non viene avviato con EUID == 0, avrà un codice di ritorno diverso da zero per grantpt () e i programmi DOVREBBE interrompere la creazione di PTS quando ciò accade , come da ptmx (4).
Aaron Jones,

3
Quella "soluzione" mi dà i willies ... nel migliore dei casi, incavola un bug, probabilmente crea un nuovo bug, nel peggiore dei casi crea una grave vulnerabilità di sicurezza. Si prega di prendere questo con gli sviluppatori glibc.
vonbrand

3
Quindi segnalalo alle persone di glibc.
vonbrand

Risposte:


2

Se il tuo glibc è ragionevolmente attuale e dev Devs impostato correttamente, non dovrebbe essere necessario invocare pt_chownaffatto l' helper.

Potrebbe essersi verificato un problema noto / potenziale durante la rimozione di setuid-root pt_chown.

grantpt()supportato devfsda glibc-2.7 , le modifiche sono state apportate in glibc-2.11 anche se in modo tale che invece di verificare esplicitamente per DEVFS_SUPER_MAGIC, controlla invece di vedere se ha bisogno di fare qualche lavoro prima di tentare chown()o tornare a invocare pt_chown.

A partire dal 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;
    }
  ...

Una stanza simile viene utilizzata per controllare il gid e le autorizzazioni. Il problema è che uid, gid e mode devono corrispondere alle aspettative (tu, tty ed esattamente 620; confermare con /usr/libexec/pt_chown --help). In caso contrario, chown()(che richiederebbe le funzionalità CAP_CHOWN, CAP_FOWNER del binario / processo chiamante) viene tentato e, in caso contrario, viene tentato l' pt_chownhelper esterno (che deve essere setuid-root). Per pt_chownpoter utilizzare le funzionalità con cui è stato compilato (e quindi il tuo glibc) HAVE_LIBCAP. Tuttavia , sembra che pt_chownsia (come di glibc-2.17 , e come avrete notato se non hanno dichiarato la versione) hardcoded a voler geteuid()==0 prescindere di HAVE_LIBCAP, relativo codice da 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'"));

(Aspettarsi geteuid()==0prima di provare a usare le capacità non sembra essere davvero nello spirito delle capacità, andrei con la registrazione di un bug su questo.)

Una possibile soluzione alternativa potrebbe essere quella di assegnare CAP_CHOWN, CAP_FOWNER ai programmi interessati, ma non lo consiglio davvero, dal momento che non puoi limitarlo a ptys ovviamente.

Se ciò non ti aiuta a risolverlo, rattoppare sshded screenè leggermente meno sgradevole rispetto a rattoppare glibc. Dal momento che il problema sta negli glibc, un approccio più pulito sarebbe l' uso selettivo dell'iniezione DLL per implementare un manichino grantpt().


"Dal momento che il problema sta negli glibc, un approccio più pulito sarebbe l'uso selettivo dell'iniezione DLL per implementare un fittpt grantpt ()." - Fantastico. Perché non ci ho pensato? Grazie. :)
Aaron Jones,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.