第1回 知られざるセキュリティフレームワーク「LSM」の役割
村上 純一
株式会社フォティーンフォティ技術研究所
研究開発部 αUnit シニア・リサーチエンジニア
2008/3/18
ソースコードで見るLSMの動作
では、SELinuxがどのようにLSMを利用しているのか、実際のコードを交えて見てみましょう。
まず、カーネルソースをダウンロードして展開する必要があります。ここでは、CentOS 5.1での手順を紹介します。
% cd ~
% mkdir rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
% echo "%_topdir %(echo $HOME)/rpmbuild" > .rpmmacros
% sudo yum install rpm-build
% rpm -i http://mirror.centos.org/centos/5/updates/SRPMS/kernel-2.6.18-53.1.13.el5.src.rpm
% cd ~/rpmbuild/SPECS
% rpmbuild -bp --target=`uname -m` kernel-2.6.spec
|
最近のCentOSでは、カーネル全体のソースコードは標準のパッケージには含まれていないため、別途ダウンロードして展開する必要があります。5.1以外のバージョンを利用している場合は、以下のURLを参考にインストールしてください。
展開に成功すると、~/rpmbuild/BUILD/kernel-2.6.18以下にカーネル全体のソースコードが生成されます。セキュリティモジュールはカーネルソースのsecurity/以下に配置されています。security/security.cがLSMの本体であり、セキュリティモジュールの登録や登録解除など、セキュリティモジュールを管理するための機能が実装されています。
セキュリティモジュールの登録を行うregister_security関数、登録解除の解除を行うunregister_security関数のコードをリスト1、2に示します。
1 int register_security(struct security_operations *ops)
2 {
3 if (verify(ops)) {
4 printk(KERN_DEBUG "%s could not verify "
5 "security_operations structure.\n", __FUNCTION__);
6 return -EINVAL;
7 }
8
9 if (security_ops != &dummy_security_ops)
10 return -EAGAIN;
11
12 security_ops = ops;
13
14 return 0;
15 }
|
|
リスト1 register_security関数 |
1 int unregister_security(struct security_operations *ops)
2 {
3 if (ops != security_ops) {
4 printk(KERN_INFO "%s: trying to unregister "
5 "a security_opts structure that is not "
6 "registered, failing.\n", __FUNCTION__);
7 return -EINVAL;
8 }
9
10 security_ops = &dummy_security_ops;
11
12 return 0;
13 }
|
|
リスト2 unregister_security関数 |
register_security関数は、引数に指定されたsecurity_operations構造体のアドレスをチェックした後、グローバル変数であるsecurity_ops変数にセットします。dummy_security_opsは、LSMの初期化時にsecurity_ops変数にセットされる空のセキュリティモジュールです。security_operations構造体は、リスト3に示すようにセキュリティモジュールのコールバック関数のアドレスを保持する関数ポインタの集合になっています。
1 struct security_operations {
2 int (*ptrace) (struct task_struct * parent, struct task_struct * child);
3 int (*capget) (struct task_struct * target,
4 kernel_cap_t * effective,
5 kernel_cap_t * inheritable, kernel_cap_t * permitted);
6 int (*capset_check) (struct task_struct * target,
7 kernel_cap_t * effective,
8 kernel_cap_t * inheritable,
9 kernel_cap_t * permitted);
10 void (*capset_set) (struct task_struct * target,
11 kernel_cap_t * effective,
12 kernel_cap_t * inheritable,
13 kernel_cap_t * permitted);
14 int (*capable) (struct task_struct * tsk, int cap);
15 int (*acct) (struct file * file);
16 int (*sysctl) (struct ctl_table * table, int op);
……中略……
243 int (*key_permission)(key_ref_t key_ref,
244 struct task_struct *context,
245 key_perm_t perm);
246
247 #endif /* CONFIG_KEYS */
248
249 };
|
|
リスト3 security_operations構造体 |
前述のsecurity_file_permission関数は、include/linux/security.hにおいてリスト4のように定義されています。カーネル内の処理は、security_ops変数を介してセキュリティモジュール内のコールバック関数を呼び出していることが分かります。
1 static inline int security_file_permission (struct file *file, int mask)
2 {
3 return security_ops->file_permission (file, mask);
4 }
|
|
リスト4 security_file_permission関数 |
一方unregister_security関数は、単にsecurity_ops変数を、空のセキュリティモジュールであるdummy_security_opsのアドレスにセットし直すだけです。
|
図2 vfs_readdir()関数からSELinuxのセキュリティモジュールが呼び出される |
SELinuxのコードは、security/selinux以下に配置されています。hooks.cに定義されているselinux_init関数が、SELinuxのセキュリティモジュールのエントリポイントです。コードをリスト5に示します。26行目で、先ほど説明したregister_security関数を呼び出していることが分かります。また引数に指定しているselinux_opsは、リスト6のようにSELinuxのコールバック関数群で初期化されています。
1 static __init int selinux_init(void)
2 {
3 struct task_security_struct *tsec;
4
5 if (!selinux_enabled) {
6 printk(KERN_INFO "SELinux: Disabled at boot.\n");
7 return 0;
8 }
9
10 printk(KERN_INFO "SELinux: Initializing.\n");
11
12 /* Set the security state for the initial task. */
13 if (task_alloc_security(current))
14 panic("SELinux: Failed to initialize initial task.\n");
15 tsec = current->security;
16 tsec->osid = tsec->sid = SECINITSID_KERNEL;
17
18 sel_inode_cache = kmem_cache_create("selinux_inode_security",
19 sizeof(struct inode_security_struct),
20 0, SLAB_PANIC, NULL, NULL);
21 avc_init();
22
23 original_ops = secondary_ops = security_ops;
24 if (!secondary_ops)
25 panic ("SELinux: No initial security operations\n");
26 if (register_security (&selinux_ops))
27 panic("SELinux: Unable to register with kernel.\n");
28
29 if (selinux_enforcing) {
30 printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n");
31 } else {
32 printk(KERN_DEBUG "SELinux: Starting in permissive mode\n");
33 }
34
35 #ifdef CONFIG_KEYS
36 /* Add security information to initial keyrings */
37 selinux_key_alloc(&root_user_keyring, current,
38 KEY_ALLOC_NOT_IN_QUOTA);
39 selinux_key_alloc(&root_session_keyring, current,
40 KEY_ALLOC_NOT_IN_QUOTA);
41 #endif
42
43 return 0;
44 }
|
|
リスト5 selinux_init関数 |
1 static struct security_operations selinux_ops = {
2 .ptrace = selinux_ptrace,
3 .capget = selinux_capget,
4 .capset_check = selinux_capset_check,
5 .capset_set = selinux_capset_set,
6 .sysctl = selinux_sysctl,
7 .capable = selinux_capable,
8 .quotactl = selinux_quotactl,
9 .quota_on = selinux_quota_on,
10 .syslog = selinux_syslog,
11 .vm_enough_memory = selinux_vm_enough_memory,
……中略……
170
171 #ifdef CONFIG_KEYS
172 .key_alloc = selinux_key_alloc,
173 .key_free = selinux_key_free,
174 .key_permission = selinux_key_permission,
175 #endif
176 };
|
|
リスト6 selinux_ops構造体 |
フレームワークとしてのLSM
このようにソースコードを見ると、LSMがフレームワークであることがよくご理解いただけたのではないでしょうか? 次回は、LSMを利用して実際にセキュリティモジュールのサンプルを作成し、その動作を検証してみたいと思います。
|
2/2 |
|
Index |
Inside Linux Security Module
第1回 知られざるセキュリティフレームワーク「LSM」の役割 |
|
|
Page
1
SELinuxの背後にいるLSM
紆余曲折を経て……開発の背景
LSMが提供する2つの機能
実はこんなに! LSMを用いたセキュリティモジュール |
|
Page 2
ソースコードで見るLSMの動作
フレームワークとしてのLSM |
|
Linux Squareフォーラム Linux/システム学習関連記事 |
Linux & OSS 記事ランキング
本日
月間