não pode iniciar o navegador do Google Chrome - a ligação falhou: Permissão negada

3

Estou usando o grsecurity kernel com CONFIG_GRKERNSEC_SOCKET_SERVER ativado:

[*] Socket restrictions  
  [ ]   Deny any sockets to group (NEW)  
  [ ]   Deny client sockets to group (NEW)
  [*]   Deny server sockets to group

Isso impede que um usuário crie um soquete de "servidor" (ou seja, iniciando o Apache), mas permite abrir soquetes do cliente (por exemplo, Firefox).

De fato, todos os clientes de rede funcionam bem (Firefox, telnet, ssh, nc, w3m, ..). Apenas o navegador Chrome (Chromium) não funciona.

Ao iniciar o cromo na linha de comando, recebo o seguinte erro:

ERROR:address_tracker_linux.cc(138)] Could not bind NETLINK socket: Permission denied
libudev: udev_monitor_enable_receiving: bind failed: Permission denied
FATAL:udev_linux.cc(31)] Check failed: 0 == ret (0 vs. -1)
Aborted

e nos logs, vejo:

grsec: denied bind() by /usr/lib/chromium/chromium[NetworkChangeNo:3920]
grsec: denied bind() by /usr/lib/chromium/chromium[WorkerPool/3922:3922]
grsec: denied bind() by /usr/lib/chromium/chromium[Chrome_IOThread:3934]
grsec: denied bind() by /usr/lib/chromium/chromium[NetworkChangeNo:3966]
grsec: denied bind() by /usr/lib/chromium/chromium[WorkerPool/3968:3968]
grsec: denied bind() by /usr/lib/chromium/chromium[Chrome_IOThread:3980]

Alguém pode explicar, por que o chrome falha ao iniciar enquanto todos os outros clientes (Firefox) funcionam bem?

Estou usando o Chrome (Chromium) 37 no Debian Wheezy (64 bits)

    
por Martin Vegter 06.02.2015 / 00:34

1 resposta

3

O Chromium falha ao iniciar porque a negação de soquetes do servidor também nega AF_NETLINK sockets e, por algum motivo, o Chromium precisa se comunicar com udev , o que exigiu AF_NETLINK sockets. Eu não tenho uma fonte obviamente autoritária, mas vou tentar explicar os primeiros princípios usando o código fonte subjacente e espero não cometer muitos erros no processo.

Comecei a investigar a primeira mensagem de erro. O código que produz a mensagem de erro no Chromium é o link linha 138:

  // Request notifications.
  struct sockaddr_nl addr = {};
  addr.nl_family = AF_NETLINK;
  addr.nl_pid = getpid();
  // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993
  addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY |
      RTMGRP_LINK;
  int rv = bind(netlink_fd_,
                reinterpret_cast<struct sockaddr*>(&addr),
                sizeof(addr));
  if (rv < 0) {
    PLOG(ERROR) << "Could not bind NETLINK socket";
    AbortAndForceOnline();
    return;
  }

A falha real relacionada ao grsec ocorre na chamada bind() , que tenta configurar um soquete de link de rede que receberá notificações sempre que uma interface de endereço IPv4 ou IPv6 for alterada e sempre que um estado de link for alterado.

Esta chamada é tratada por um syscall no kernel, em net/socket.c :

SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
{
        struct socket *sock;
        struct sockaddr_storage address;
        int err, fput_needed;

Isto declara o syscall e algumas variáveis locais. Você pode ver que a declaração syscall corresponde à chamada bind() no código do Chromium: int fd, struct sockaddr __user * umyaddr, int addrlen .

        sock = sockfd_lookup_light(fd, &err, &fput_needed);
        if (sock) {

Isso procura o soquete a partir do descritor de arquivos. Se um socket for encontrado ...

                err = move_addr_to_kernel(umyaddr, addrlen, &address);
                if (err >= 0) {

Isso copia os dados fornecidos do espaço do usuário para o espaço do kernel. Se não houver erro ...

                        err = security_socket_bind(sock,
                                                   (struct sockaddr *)&address,
                                                   addrlen);
                        if (!err)

Isto dá a qualquer LSM carregado (SELinux, etc.) a chance de verificar se a chamada é permitida. Se assim for ...

                                err = sock->ops->bind(sock,
                                                      (struct sockaddr *)
                                                      &address, addrlen);

O bind continua em outro lugar, e estamos no que diz respeito à análise do código padrão do kernel aqui.

grsec corrige net/socket.c em vários lugares; em particular, antes da verificação de segurança do LSM, ele adiciona suas próprias verificações (consulte link ; procure por SYSCALL_DEFINE3(bind ):

                        if (gr_handle_sock_server((struct sockaddr *)&address)) {
                                err = -EACCES;
                                goto error;
                        }
                        err = gr_search_bind(sock, (struct sockaddr_in *)&address);
                        if (err)
                                goto error;

O primeiro cheque é o que é relevante aqui; chama gr_handle_sock_server() :

gr_handle_sock_server(const struct sockaddr *sck)
{
#ifdef CONFIG_GRKERNSEC_SOCKET_SERVER
       if (grsec_enable_socket_server &&
           in_group_p(grsec_socket_server_gid) &&
           sck && (sck->sa_family != AF_UNIX) &&
           (sck->sa_family != AF_LOCAL)) {
               gr_log_noargs(GR_DONT_AUDIT, GR_BIND_MSG);
               return -EACCES;
       }
#endif
       return 0;
}

Isso implementa a verificação "negar soquetes do servidor para agrupar". Conforme verificado nos comentários, no seu sistema grsec_enable_socket_server é 1, portanto, ao executar como grupo 1001, o if é bem-sucedido ( sck->sa_family == AF_NETLINK neste caso) e o acesso é negado.

Voltando ao código do Chromium, isso registra uma mensagem de erro e chama AbortAndForceOnline() , que apenas configura as coisas para que o navegador calcule que está on-line. Então isso não explica a falha em começar.

Antes de continuar, tentei reproduzir a falha. Para fazer isso, adaptei authbind para evitar que AF_NETLINK seja vinculado; em libauthbind.c , na função bind() adicionei um case ao primeiro switch :

  case AF_NETLINK:
    puts("Denying AF_NETLINK");
    return -EACCES;

A execução com a biblioteca resultante reproduziu a falha:

% LD_PRELOAD=/usr/lib/authbind/libauthbind.so.1 chromium
Denying AF_NETLINK
 [15858:15876:0214/160730:ERROR:address_tracker_linux.cc(154)] Could not bind NETLINK socket: Success
Denying AF_NETLINK
libudev: udev_monitor_enable_receiving: bind failed: No such file or directory
[15858:15890:0214/160730:FATAL:udev_linux.cc(29)] Check failed: 0 == ret (0 vs. -2)
zsh: abort      LD_PRELOAD=/usr/lib/authbind/libauthbind.so.1 chromium

(As mensagens de erro estranhas, "Success" e "No such file directory", ocorrem porque não estou definindo errno .)

Portanto, a anulação está de fato relacionada a bind() . Verificar udev_linux.cc linha 29 mostra

  int ret = udev_monitor_enable_receiving(monitor_.get());
  CHECK_EQ(0, ret);

ret aqui é negativo porque udev_monitor_enable_receiving() não pode ligar um soquete de netlink, e CHECK_EQ causa uma falha de asserção aqui (veja link para a implementação). Isso produz um sinal de interrupção e o Chromium é encerrado com uma mensagem "Abortada" de algum tipo, dependendo do shell usado.

    
por 12.02.2015 / 00:55