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.