![Chrome을 시작할 수 없습니다 - 바인딩 실패: 권한이 거부되었습니다.](https://linux55.com/image/47350/Chrome%EC%9D%84%20%EC%8B%9C%EC%9E%91%ED%95%A0%20%EC%88%98%20%EC%97%86%EC%8A%B5%EB%8B%88%EB%8B%A4%20-%20%EB%B0%94%EC%9D%B8%EB%94%A9%20%EC%8B%A4%ED%8C%A8%3A%20%EA%B6%8C%ED%95%9C%EC%9D%B4%20%EA%B1%B0%EB%B6%80%EB%90%98%EC%97%88%EC%8A%B5%EB%8B%88%EB%8B%A4..png)
grsecurity
활성화된 커널을 사용하고 있습니다 CONFIG_GRKERNSEC_SOCKET_SERVER
.
[*] Socket restrictions
[ ] Deny any sockets to group (NEW)
[ ] Deny client sockets to group (NEW)
[*] Deny server sockets to group
이는 사용자가 "서버" 소켓을 생성(예: Apache 시작)하는 것을 방지하지만 클라이언트 소켓(예: Firefox)을 열 수 있도록 합니다.
실제로 모든 웹 클라이언트는 잘 작동합니다(Firefox, telnet, ssh, nc, w3m,...). Chrome 브라우저(Chromium)만 작동하지 않습니다.
명령줄에서 Chrome을 시작할 때 다음 오류가 발생합니다.
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
로그에 다음이 표시됩니다.
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]
다른 클라이언트(Firefox)는 모두 정상적으로 작동하는데 Chrome이 시작되지 않는 이유를 설명할 수 있는 사람이 있습니까?
Debian Wheezy(64비트)에서 Chrome(Chromium) 37을 사용하고 있습니다.
답변1
서버 소켓을 거부하면 AF_NETLINK
소켓도 거부되기 때문에 Chromium이 시작되지 않으며, 어떤 이유로 Chromium은 서버와 통신해야 하며 udev
, 이를 위해서는 AF_NETLINK
소켓이 필요합니다. 확실하고 권위 있는 소스는 없지만 기본 소스 코드를 사용하여 첫 번째 원칙부터 설명하려고 노력할 것이며 그 과정에서 너무 많은 실수를 저지르지 않기를 바랍니다.
나는 첫 번째 오류 메시지를 조사하기 시작했습니다. 오류 메시지를 생성하는 Chromium의 코드는 다음과 같습니다.https://src.chromium.org/svn/trunk/src/net/base/address_tracker_linux.cc138행:
// 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;
}
실제 grsec 관련 실패는 bind()
인터페이스 IPv4 또는 IPv6 주소가 변경될 때마다 그리고 링크 상태가 변경될 때 알림을 받을 netlink 소켓을 설정하려고 시도하는 호출에서 발생합니다.
이 호출은 커널의 시스템 호출에 의해 처리됩니다.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;
이는 시스템 호출과 일부 지역 변수를 선언합니다. 시스템 호출 선언이 bind()
Chromium 코드의 호출과 일치하는 것을 볼 수 있습니다 int fd, struct sockaddr __user * umyaddr, int addrlen
.
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
파일 설명자에서 소켓을 찾습니다. 콘센트를 찾으면..
err = move_addr_to_kernel(umyaddr, addrlen, &address);
if (err >= 0) {
이는 사용자 공간에서 제공하는 데이터를 커널 공간으로 복사합니다. 오류가 없다면...
err = security_socket_bind(sock,
(struct sockaddr *)&address,
addrlen);
if (!err)
이렇게 하면 로드된 모든 LSM(SELinux 등)에 호출이 허용되는지 확인할 수 있는 기회가 제공됩니다. 만일이 경우라면...
err = sock->ops->bind(sock,
(struct sockaddr *)
&address, addrlen);
바인딩은 다른 곳에서 수행되며 표준 커널 코드 분석이 완료되었습니다.
grsec은 net/socket.c
특히 여러 위치에서 패치되었으며 LSM 보안 검사 전에 자체 검사를 추가합니다(참조).https://grsecurity.net/test/grsecurity-3.0-3.18.6-201502062100.patch;찾다 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;
첫 번째 확인은 다음과 관련이 있습니다 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;
}
이는 "그룹에 대한 서버 소켓 거부" 검사를 구현합니다. 주석에서 확인된 대로 시스템에서는 grsec_enable_socket_server
1이므로 그룹 1001로 실행하면 if
성공하고( sck->sa_family == AF_NETLINK
이 경우) 액세스가 거부됩니다.
Chromium 코드로 돌아가면 오류 메시지가 기록되고 AbortAndForceOnline()
브라우저가 온라인이라고 생각하도록 설정됩니다. 따라서 이것은 시작 실패를 설명하지 않습니다.
더 진행하기 전에 실패를 재현해 보았습니다. 이를 위해 첫 번째 함수 에 바인딩을 authbind
방지하도록 조정했습니다 .AF_NETLINK
libauthbind.c
bind()
case
switch
case AF_NETLINK:
puts("Denying AF_NETLINK");
return -EACCES;
생성된 라이브러리로 실행하면 오류가 재현됩니다.
% 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
("성공" 및 "해당 파일 디렉터리 없음"이라는 이상한 오류 메시지는 내가 설정하지 않았기 때문입니다 errno
.)
따라서 정지는 실제로 관련이 있습니다 bind()
. udev_linux.cc
다음을 보여주는 29행을 확인하세요 .
int ret = udev_monitor_enable_receiving(monitor_.get());
CHECK_EQ(0, ret);
ret
udev_monitor_enable_receiving()
netlink 소켓을 바인딩할 수 없고 CHECK_EQ
여기에서 어설션이 실패하게 되므로 이는 음수입니다 .https://src.chromium.org/svn/trunk/src/base/logging.h구현을 위해). 이는 중단 신호를 생성하고 Chromium은 사용된 셸에 따라 일종의 "중단" 메시지와 함께 종료됩니다.