Chromeを起動できません - バインディングに失敗しました:権限が拒否されました。

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など)を開くことができます。

実際、すべてのWebクライアントはうまく動作します(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.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 アドレスが変更されるたびに、リンク状態が変更されたときに通知を受け取る 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_server1なので、グループ1001で実行すると成功しifsck->sa_family == AF_NETLINKこの場合)、アクセスは拒否されます。

Chromiumコードに戻ると、エラーメッセージが記録され、AbortAndForceOnline()ブラウザがオンラインだと思うように設定されます。したがって、これは起動失敗を説明しません。

さらに進む前に失敗を再現してみました。この目的のために、最初の関数へのバインディングをauthbind防ぐように調整しました。AF_NETLINKlibauthbind.cbind()caseswitch

  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);

retudev_monitor_enable_receiving()netlinkソケットをバインドできず、CHECK_EQここでアサーションが失敗するため、これは負です。https://src.chromium.org/svn/trunk/src/base/logging.h実装のため)。これは中断信号を生成し、Chromiumは使用されているシェルに従って一種の「中断」メッセージで終了します。

おすすめ記事