Qual seria a melhor maneira de contornar esse problema da glibc?

26

Eu administro uma caixa Gentoo Hardened que usa recursos de arquivo para eliminar a maior parte da necessidade de binários com raiz setuid (por exemplo, /bin/ping tem CAP_NET_RAW, etc).

De fato, o único binário que me resta é este:

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

Se eu remover o bit setuid ou remontar meu sistema de arquivos raiz nosuid , sshd e GNU Screen pararão de funcionar, porque eles chamam grantpt(3) em seus pesudoterminals mestres e glibc aparentemente executa este programa para chown e chmod o pseudoterminal secundário /dev/pts/ e o GNU Screen se preocupa quando esta função falha.

O problema é que a página de manual para grantpt(3) declara explicitamente que sob o Linux, com o sistema de arquivos devpts montado, nenhum binário auxiliar é necessário; o kernel irá definir automaticamente o UID & GID do escravo para o UID real & GID do processo que abriu /dev/ptmx (chamando getpt(3) ).

Eu escrevi um pequeno programa de exemplo para demonstrar isso:

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

Observe em ação com o bit setuid no programa mencionado acima removido:

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

Tenho apenas algumas ideias sobre como resolver este problema:

1) Substitua o programa por um esqueleto que simplesmente retorne 0.

2) Patch grantpt () na minha libc para não fazer nada.

Eu posso automatizar ambos, mas alguém tem uma recomendação sobre a outra ou recomendações de como resolver isso?

Quando isso for resolvido, posso finalmente mount -o remount,nosuid / .

    
por Aaron Jones 16.03.2013 / 17:09

1 resposta

2

Se o seu glibc estiver razoavelmente atualizado e devpts estiver configurado corretamente, não será necessário chamar o pt_chown helper.

Você pode estar se deparando com um problema conhecido / potencial removendo o setuid-root de pt_chown .

grantpt() suportado devfs de glibc-2.7 , as alterações foram feitas em glibc-2.11 embora, em vez de verificar explicitamente por DEVFS_SUPER_MAGIC , verifica em vez disso, para ver se ele precisa fazer algum trabalho antes de tentar chown() ou voltar a invocar pt_chown .

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

Uma sub-rotina semelhante é usada para verificar o gid e as permissões. O problema é que o uid, gid e mode devem corresponder às expectativas (você, tty e exatamente 620; confirme com /usr/libexec/pt_chown --help ). Se não, chown() (o que exigiria recursos CAP_CHOWN, CAP_FOWNER do processo / binário de chamada) é tentado e, se isso falhar, o assistente externo pt_chown (que deve ser root setuid) é tentado. Para que pt_chown seja capaz de usar recursos, ele (e, portanto, seu glibc) deve ter sido compilado com HAVE_LIBCAP . No entanto , parece que pt_chown é (como em glibc-2.17 , e como você notou que você não declarou a versão) codificado para querer geteuid()==0 independentemente de HAVE_LIBCAP , código relevante de 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'"));

(Esperar geteuid()==0 antes de tentar usar recursos não parece estar realmente no espírito das capacidades, eu iria com o registro de um bug em um presente.)

Uma possível solução alternativa pode ser dar CAP_CHOWN, CAP_FOWNER aos programas afetados, mas eu realmente não recomendo isso, já que você não pode restringir isso a ptys, é claro.

Se isso não ajudar você a resolvê-lo, aplicar sshd e screen será um pouco menos desagradável do que corrigir o glibc. Já que o problema está no glibc, uma abordagem mais limpa seria o uso seletivo de de injeção de DLL para implementar um dummy grantpt() .

    
por 24.04.2013 / 21:34