第2回 オリジナルセキュリティモジュールの作成


村上 純一
株式会社フォティーンフォティ技術研究所
研究開発部 αUnit シニア・リサーチエンジニア
2008/4/24


サーバ接続に基づきアクセスを制御する「Root Connect」

 Root Connectは、ベンダIDとプロダクトIDの代わりに、サーバのIPアドレスとTCPのポート番号を指定して、ネットワーク上に存在するサーバへの接続を利用してアクセス制御を行います。

 アプリケーション層でのプロトコルは実装しておらず、単純なTCPレベルでの3ウェイハンドシェイクの成否が、root権限でのコマンド実行の可否に直結します。そのため、リモートのサーバ上で動作するサーバプログラムのオン/オフによって、root権限でのコマンド実行の許可/不許可を制御することができます。

 実装はRoot Plug同様、bprm_security_checkフックを利用して、実行するプログラムの実効グループIDに基づいた処理を行っています。Root Connectのbprm_security_checkフックのコールバック関数をリスト4に示します。

 1 static int rootconn_bprm_check_security(struct linux_binprm *bprm)
 2 {
 3     if (bprm->e_gid == 0) {
 4         if (connect_server(bprm)) {
 5             return -EPERM;
 6         }
 7     }
 8
 9     return 0;
10 }
リスト4 rootconn_bprm_security_check関数

 3行目で、Root Plugのときと同様、実効グループIDが0(root)でないかどうかをチェックし、0であった場合は、connect_server関数を呼び出します(リスト5)。

 1 static int connect_server(struct linux_binprm *bprm)
 2 {
 3     struct socket *s;
 4     struct sockaddr_in addr;
 5     struct msghdr msg;
 6     struct kvec iov;
 7     char *buf;
 8     int err;
 9     int len;
10
11     if ((err = sock_create_kern(PF_INET, SOCK_STREAM,
IPPROTO_TCP, &s)) < 0) {
12         printk(KERN_INFO "socket_create_kern error: %d\n", err);
13         return err;
14     }
15
16     memset(&addr, 0, sizeof(addr));
17     addr.sin_family = PF_INET;
18     addr.sin_port = htons(server_port);
19     addr.sin_addr.s_addr = server_addr;
20
21     if ((err = kernel_connect(s, (struct sockaddr *) &addr, sizeof(addr), 0))) {
22         printk(KERN_INFO "kernel_connect error: %d\n", err);
23         sock_release(s);
24         return err;
25     }
26
27     buf = bprm->filename;
28     len = strlen(buf);
29
30     iov.iov_base = buf;
31     iov.iov_len = len;
32     msg.msg_name = (struct sockaddr *) &addr;
33     msg.msg_namelen = sizeof(struct sockaddr);
34     msg.msg_iov = (struct iovec *) &iov;
35     msg.msg_iovlen = 1;
36     msg.msg_control = NULL;
37     msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL;
38
39     err = kernel_sendmsg(s, &msg, &iov, 1, len);
40
41     sock_release(s);
42
43     return 0;
44 }
リスト5 connect_server関数

 connect_server関数では、11行目のsock_create_kern関数でTCPソケットを作成し、続く21行目のkernel_connect関数でTCPソケットによるサーバへの接続を行います。接続処理が失敗した場合は、TCPソケットの開放を行い、エラー値を返却します。接続に成功した場合は、linux_binprm構造体のポインタから実行するプログラムのファイル名を参照し、接続先のサーバに送信します。

 次に、Root Connectのビルド手順を以下に示します。

1.root_conn.cをsecurity/に配置します。

2.Root Connectのターゲットを、security/Makefileの最終行に次のようにして追加します。

obj-$(CONFIG_SECURITY)              += security.o dummy.o inode.o
# Must precede capability.o in order to stack properly.
obj-$(CONFIG_SECURITY_SELINUX)      += selinux/built-in.o
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG)     += commoncap.o root_plug.o
obj-$(CONFIG_SECURITY_SECLVL)       += seclvl.o
+obj-$(CONFIG_SECURITY_ROOTCONN)    += commoncap.o root_conn.o

3.security/KconfigにSECURITY_ROOTCONNシンボルを追加します。

config SECURITY_SECLVL
    tristate "BSD Secure Levels"
    depends on SECURITY
    select CRYPTO
    select CRYPTO_SHA1
    help
      Implements BSD Secure Levels as an LSM. See
      <file:Documentation/seclvl.txt> for instructions
      on how to use this module.

      If you are unsure how to answer this question, answer N.

+config SECURITY_ROOTCONN
+      tristate "Root Connect Support"
+      depends on SECURITY
+      help
+        Root Connect *sample LSM module

source security/selinux/Kconfig

endmenu

 この状態でmake menuconfigコマンドを実行すると、Security optionsのカテゴリに「Root Connect Support」のエントリが追加される状態となります。これをモジュールにするよう選択し、以下のコマンドを実行します。

% make security/root_conn.ko
% sudo make modules_install

サーバプログラムを用いて挙動をテスト

 Root Connectの接続先として指定するサーバは、TCPレベルでの接続性さえ存在すればどんなものでも構いません。

 今回は、せっかくですから専用のサーバプログラム(以降、RootConnServer)を用意しました。RootConnServerは、forkを利用したシンプルな並行サーバになっています。クライアントからのTCP接続を受け付けると、送信されたデータを受信して、標準出力に表示します。

 では、実際にroot_conn.koとRootConnServerを利用してみましょう。

 まず、RootConnServer.cをネットワーク上の適当なサーバにコピーし、ビルドします。

・サーバ側(192.168.1.10)

% gcc -Wall RootConnServer.c -o RootConnServer

 サーバ側でRootConnServerを起動し、クライアント側でRoot Connectをカーネルにロードします。ロードする際には、addr、portパラメータを用いて、サーバのアドレスとポート番号を指定する必要があります。

・サーバ側(192.168.1.10)

% ./RootConnServer
Usage: ./RootConnServer port
% ./RootConnServer 1111

・クライアント側(192.168.1.1)

% sudo modprobe root_conn addr=192.168.1.10 port=1111

 Root Connectロード後に、sudoコマンドからwhoamiコマンドを実行するとRootConnServerが以下のような出力を行います。

2008-04-05 08:47: 192.168.1.1:3775 /usr/bin/whoami

 また、クライアント側にRoot ConnectをロードしたままRootConnServerを停止すると、Root Plug同様、root権限でのコマンド実行が制限されることを確認できます。

 なおRoot Connectは、原理上、接続先のサーバやサーバに接続するためのネットワーク自体に障害が発生した場合、クライアント側でのroot権限によるコマンド実行が一切できない状態になります。動作確認を行う場合は、検証環境を用意したうえで実行してください。

アイコン アイデア次第で広がるモジュール

 いかがだったでしょうか? Root Plug、Root Connectどちらも実環境で利用するためには、いろいろと手を加える必要がありますが、セキュリティモジュールの開発にチャレンジするには良い足掛かりではないでしょうか。どちらも利用しているのは、まだLSMのほんの一部の機能だけですから、アイデア次第で、もっと面白いことができると思います。

 次回は、Root Connectにさらにいくつかの機能拡張を加えていきましょう。

2/2

Index
Inside Linux Security Module
 第2回 オリジナルセキュリティモジュールの作成
  Page 1
 オリジナルモジュールでアクセス制御を実現
 USBデバイスの接続で権限を制御する「Root Plug」
 セキュリティモジュールのビルドとロード  
  Page 2
 サーバ接続に基づきアクセスを制御する「Root Connect」
 サーバプログラムを用いて挙動をテスト
 アイデア次第で広がるモジュール


 Linux Squareフォーラム Linux/システム学習関連記事
連載:Windowsユーザーに教えるLinuxの常識(全12回)
Windowsのセオリーが通用しないLinux。Linux初心者向けに、LinuxというOSの考え方/常識をゼロから伝授!
連載:LFSで作って学ぶLinuxの仕組み(全4回)
管理者(root)は、何をしなければならないのか? 管理に際して検討すべきことは? 管理のための技術とは? など、駆け出し管理者のための考え方や方法論を検討する
連載:Linux管理者への道(全8回)
「Linux From Scratch」というシンプルなLinuxをインストール&環境構築する作業を通して、LinuxがOSとして機能するための仕組みや設定を見直そう
Linux Squareフォーラム全記事インデックス

MONOist組み込み開発フォーラムの中から、Linux関連記事を紹介します


Linux & OSS フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Linux & OSS 記事ランキング

本日 月間