最終回 オリジナルセキュリティモジュールを拡張する


Linuxカーネルには、バージョン2.6から「Linux Security Module」(LSM)というセキュリティフレームワークが導入されています。この連載ではLSMの仕組みを紹介するとともに、これを活用してオリジナルのセキュリティモジュールを作り上げていきます(編集部)

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

 第2回「オリジナルセキュリティモジュールの作成」では、 セキュリティモジュールを実装するためのフレームワーク「LSM」を用いて、ネットワーク接続を利用してアクセス制御を行う「Root Connect LSM Module」(以降、Root Connect)を作成しました。ネットワーク上のサーバへの接続を利用してアクセス制御を行い、指定したサーバにアクセス可能な場合にのみroot権限でのコマンド実行を許可するというものです。

 今回は、LSMのほかの機能を利用してRoot Connectの機能を拡張していきます。なお、本稿で機能拡張を行ったRoot Connectのソースコードは、こちらからダウンロードできます。

関連記事:
参考 Linux Kernel Watch 2006年5月版 「LSM不要論」に揺れるSELinux&AppArmor
http://www.atmarkit.co.jp/flinux/rensai/watch2006/watch05b.html
参考 Linux Kernel Watch番外編:セキュリティをやってるやつらは狂っている?!
http://www.atmarkit.co.jp/fsecurity/special/103kernelwatch/kernelwatch01.html

securityfsを活用しよう

 securityfsは、カーネル2.6.14で追加されたRAMベースのファイルシステムです。LSMを用いると、securityfsを利用し、ユーザー空間から自身のメモリへアクセスするためのインターフェイスを、ファイルシステム上に作成することができます。ユーザー空間からは、procfsのように、通常のファイルアクセスを介してセキュリティモジュールのメモリにアクセスすることができます。

 securityfsは、カーネルソース中のsecurity/inode.cに実装されており、以下の3つのAPIによる非常にシンプルな設計になっています。

struct dentry *securityfs_create_file(const char *name, mode_t mode,
        struct dentry *parent, void *data,
        struct file_operations *fops);

struct dentry *securityfs_create_dir(const char *name, struct dentry *parent);

void securityfs_remove(struct dentry *dentry);

インターフェイスの実装

 では、実際にRoot Connectをベースにして、securityfsを利用した設定情報の取得用インターフェイスを実装してみましょう。

 まず、セキュリティモジュールの初期化を行うrootconn_init関数、終了処理を行うrootconn_exit関数のそれぞれで、securityfsの登録および解除の処理を行う必要があります。それぞれの変更点をリスト1に示します。

 1 static int rootconnfs_register(void)
 2 {
 3    int rc = 0;
 4
 5    dir_ino = securityfs_create_dir("rootconn", NULL);
 6
 7    if (IS_ERR(dir_ino))
 8    return PTR_ERR(dir_ino);
 9
10    config_ino = securityfs_create_file("server", S_IRUGO, dir_ino, NULL, &rootconn_file_ops);
11    if (IS_ERR(config_ino)) {
12       rc = PTR_ERR(config_ino);
13       securityfs_remove(dir_ino);
14       return PTR_ERR(config_ino);
15    }
16
17    return rc;
18 }
19
20 static void rootconnfs_unregister(void)
21 {
22       securityfs_remove(config_ino);
23       securityfs_remove(dir_ino);
24 }

25
26 static int __init rootconn_init(void)
27 {
28    int rc = 0;
29
30    if (server_addr_string == NULL || server_port == 0) {
31       printk(KERN_INFO "please specify addr and port parameters\n");
32       return -EINVAL;
33    }
34
35    server_addr = in_aton(server_addr_string);
36
37    if ((rc = rootconnfs_register())) {
38       printk(KERN_INFO "Failure registering rootconnfs\n");
39       return rc;
40    }

41
42    ...
43
44    return 0;
45 }
46
47 static void __exit rootconn_exit(void)
48 {
49    rootconnfs_unregister();
50
51    ...
52
53    printk(KERN_INFO "Root Connect module removed\n");
54 }
リスト1 root_conn.cの変更箇所(1) ※変更箇所は黄色で表記

 rootconn_init関数の37行目でrootconnfs_register関数を呼び出しています。rootconnfs_register関数は、始めにsecurityfs_create_dir関数を呼び出して、「rootconn」という名前のディレクトリエントリを作成します。

 次に、securityfs_create_file関数に作成したディレクトリエントリを引数として渡すことで、ディレクトリ内に「server」という名前のファイルを作成します。

 securityfs_create_file関数の第5引数に指定されているrootconn_file_opsは、ユーザー空間からこのファイルにアクセスした際に呼び出されるコールバック関数の関数ポインタ群を含む、file_operations構造体のポインタです。rootconn_file_ops変数の定義をリスト2に示します。

 一方のrootconn_exit関数では、関数の先頭でrootconnfs_unregister関数を呼び出しています。rootconnfs_unregister関数は、securityfs_unregister関数をrootconnfs_register関数で作成した「rootconn」ディレクトリ、「server」ファイルそれぞれを引数に呼び出し、削除します。

 1 static struct file_operations rootconn_file_ops = {
 2     .read = read_server_config,
 3 };
リスト2 rootconn_file_ops変数の定義

 rootconn_files_opsは、readメンバーのみ、read_server_config関数のアドレスで初期化しています。この関数ポインタ(readメンバー)は、名前のとおりユーザー空間のプロセスがreadシステムコールを発行した際に呼び出されます。read_server_config関数をリスト3に示します。

 1 static ssize_t read_server_config(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
 2 {
 3     char tmp[256];
 4     ssize_t len;
 5
 6     len = scnprintf(tmp, sizeof(tmp), "%s:%d\n", server_addr_string, server_port);
 7     return simple_read_from_buffer(buf, count, ppos, tmp, len);
 8 }
リスト3 read_server_config関数

 read_server_config関数は、ファイルオブジェクトのポインタ、ユーザー空間のバッファのポインタ、バッファ長、オフセットが引数として渡されます。今回は、Root Connectをロードした際に指定したサーバのIPアドレス、ポート番号をプロセスに返すため、scnprintf関数を呼び出して、コロン区切りの文字列をローカルバッファに生成しています。

 次に、simple_read_from_buffer関数を呼び出して生成した文字列を、ユーザー空間のバッファにコピーします。simple_read_from_buffer関数は、カーネルソース中のfs/libfs.cに実装されており、copy_to_user関数を呼び出すことで、引数に指定されたカーネル空間のメモリ領域の内容をユーザー空間のメモリ領域にコピーします。単純にmemcpy関数を利用していないのは、ユーザー空間のメモリ領域は、スワップアウトを考慮する必要があるからです。

 動作確認を行う際は、前回同様、ソースコード(root_conn.c)をsecurity/以下に配置し、カーネルソースのルートディレクトリから以下のコマンドを実行します。ビルド環境、手順の詳細については、前々回、前回を参照ください。

% make security/root_conn.ko
% sudo make modules_install

 前回同様、TCP接続が可能な適当なサーバのIPアドレス、ポート番号を引数に指定して、Root Connectをカーネルにロードします。

% sudo modprobe root_conn addr=192.168.1.10 port=1111

 securityfsは、デフォルトではマウントされないため、以下のコマンドを実行してマウントします。

% sudo mount -t securityfs securityfs /sys/kernel/security

 この状態で、/sys/kernel/security配下にrootconnディレクトリ、またその配下に「server」ファイルが生成されていることが確認できます。「server」ファイルの内容を表示すると、ロード時に指定したパラメータを確認することができます。

% cat /sys/kernel/security/rootconn/server
192.168.1.10:1111

第2回へ
1/2

Index
Inside Linux Security Module
 最終回 オリジナルセキュリティモジュールを拡張する
Page 1
 securityfsを活用しよう
 インターフェイスの実装
  Page 2
 PF_PACKETとRAW Socketによるデータ送信の禁止
 気軽にカーネルへのチャレンジを


 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 記事ランキング

本日 月間