Mar*_*ter 2 udev grsecurity chrome browser socket
我正在使用启用的grsecurity内核CONFIG_GRKERNSEC_SOCKET_SERVER:
[*] Socket restrictions
[ ] Deny any sockets to group (NEW)
[ ] Deny client sockets to group (NEW)
[*] Deny server sockets to group
Run Code Online (Sandbox Code Playgroud)
这可以防止用户创建“服务器”套接字(即启动 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
Run Code Online (Sandbox Code Playgroud)
在日志中,我看到:
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]
Run Code Online (Sandbox Code Playgroud)
有人可以解释一下,为什么 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;
}
Run Code Online (Sandbox Code Playgroud)
实际与 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;
Run Code Online (Sandbox Code Playgroud)
这声明了系统调用和一些局部变量。你可以看到系统调用声明相匹配bind()的铬代码调用:int fd, struct sockaddr __user * umyaddr, int addrlen。
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
Run Code Online (Sandbox Code Playgroud)
这会从文件描述符中查找套接字。如果找到套接字...
err = move_addr_to_kernel(umyaddr, addrlen, &address);
if (err >= 0) {
Run Code Online (Sandbox Code Playgroud)
这将从用户空间提供的数据复制到内核空间。如果没有错误...
err = security_socket_bind(sock,
(struct sockaddr *)&address,
addrlen);
if (!err)
Run Code Online (Sandbox Code Playgroud)
这使任何已加载的 LSM(SELinux 等)都有机会检查是否允许调用。如果是这样的话...
err = sock->ops->bind(sock,
(struct sockaddr *)
&address, addrlen);
Run Code Online (Sandbox Code Playgroud)
绑定在别处进行,就标准内核代码的分析而言,我们已经在这里完成了。
net/socket.c许多地方的grsec 补丁;特别是,在 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;
Run Code Online (Sandbox Code Playgroud)
第一个检查是与此处相关的检查;它调用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;
}
Run Code Online (Sandbox Code Playgroud)
这实现了“拒绝服务器套接字分组”检查。正如评论中验证的那样,在您的系统上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;
Run Code Online (Sandbox Code Playgroud)
使用生成的库运行重现了失败:
% 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
Run Code Online (Sandbox Code Playgroud)
(奇怪的错误消息,“成功”和“没有这样的文件目录”,因为我没有设置errno。)
所以中止确实与bind(). 检查udev_linux.cc第 29 行显示
int ret = udev_monitor_enable_receiving(monitor_.get());
CHECK_EQ(0, ret);
Run Code Online (Sandbox Code Playgroud)
ret这里是否定的,因为udev_monitor_enable_receiving()无法绑定 netlink 套接字,并CHECK_EQ在此处导致断言失败(有关实现,请参见https://src.chromium.org/svn/trunk/src/base/logging.h)。这会产生一个中止信号,Chromium 会以某种“中止”消息退出,具体取决于正在使用的外壳。
| 归档时间: |
|
| 查看次数: |
2421 次 |
| 最近记录: |