CONFIG_GRKERNSEC_SOCKET_SERVER
を有効にしてgrsecurity
カーネルを使用しています。
[*] 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を使用しています
サーバーソケットを拒否すると_AF_NETLINK
_ソケットも拒否されるため、Chromiumは起動に失敗します。また、何らかの理由で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()
呼び出しで発生します。この呼び出しは、インターフェイスのIPv4またはIPv6アドレスが変更されるたび、およびリンク状態が変更されるたびに通知を受信するネットリンクソケットをセットアップしようとします。
この呼び出しは、カーネル内のsyscallによって処理されます _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といくつかのローカル変数が宣言されます。 syscall宣言がChromiumコードのbind()
呼び出しと一致していることがわかります:_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);
_
udev_monitor_enable_receiving()
はネットリンクソケットをバインドできないため、ここのret
は負であり、_CHECK_EQ
_はここでアサーションエラーを引き起こします( https://src.chromiumを参照)。 org/svn/trunk/src/base/logging.h 実装用)。これにより中止信号が生成され、Chromiumは、使用されているシェルに応じて、ある種の「中止」メッセージで終了します。