無法啟動 chrome 瀏覽器 - 綁定失敗:權限被拒絕
我正在使用啟用的
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]
有人可以解釋一下,為什麼 chrome 無法啟動,而所有其他客戶端(Firefox)都可以正常工作?
我在 Debian Wheezy(64 位)上使用 Chrome (Chromium) 37
Chromium 無法啟動,因為拒絕伺服器套接字也會拒絕
AF_NETLINK
套接字,並且由於某種原因,Chromium 需要與udev
需要AF_NETLINK
套接字的 進行通信。我沒有明顯的權威來源,但我會嘗試使用底層原始碼從基本原理進行解釋,並希望在此過程中不會犯太多錯誤。我開始調查第一條錯誤消息。在 Chromium 中產生錯誤消息的程式碼是https://src.chromium.org/svn/trunk/src/net/base/address_tracker_linux.cc第 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; }
實際與 grsec 相關的故障發生在
bind()
呼叫中,該呼叫嘗試建立一個 netlink 套接字,只要介面 IPv4 或 IPv6 地址發生變化以及鏈路狀態發生變化,該套接字就會接收通知。此呼叫由核心中的系統呼叫處理,在
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;
這聲明了系統呼叫和一些局部變數。您可以看到 syscall 聲明與
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 會以某種“已中止”消息退出,具體取決於所使用的 shell。