FreeBSD Daily Topics

2011年9月12日Capsicumを知る - ケーパビリティモードはfork()したプロセスにも継承

9月末または10月のリリースが予定されているFreeBSD 9.0-RELEASEには新しいセキュリティ機能「Capsicum」が登場します。FDTではしばらく概念的な説明や、実際の実装例などを紹介して「Capsicum」の機能を紹介していきます。

Capability mode going into a child process

ケーパビリティモードに入ったプロセスから子プロセスを生成した場合、その子プロセスもケーパビリティモードが引き継がれます。たとえば、これまで使ってきたソースコードを子プロセスを生成して、子プロセス側でファイルの読み込みを標準出力へ書きだすようにすると次のようになります。⁠read-cap-process.c」として用意しておきます。

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include <sysexits.h>
#include <sys/capability.h>
#include <sys/types.h>
#include <sys/wait.h>

int
main(void)
{
	int fd, cap, len, stat;
	char bf[BUFSIZ];
	pid_t pid;

	fd = open("COPYRIGHT", O_RDWR);
	if (-1 == fd)
		err(EX_NOPERM, "open error: %d", errno);

	cap = cap_new(dup(fd), CAP_READ | CAP_SEEK);
	if (-1 == cap)
		err(EX_NOPERM, "cap_new error: %d", errno);

	close(fd);

	cap_enter();

	// 子プロセスを作ります
	pid = fork();
	if (-1 == pid)
		err(EX_NOPERM, "fork error: %d", errno);

	if (0 == pid) {
		// 子プロセス側で出力処理をします。
 		len = read(cap, bf, BUFSIZ);
		if (-1 == len)
			err(EX_NOPERM, "read error: %d", errno);

		printf("%s\n", bf);
		fflush(NULL);
		sleep(10);
	} else if (0 < pid) {
		// 親プロセスはその間スリープ
		sleep(10);
	} 

	return 0;
}

ただ処理を子プロセスへ振っただけですので、このサンプルはそのまま動作します。

% clang read-cap-process.c 
% ./a.out | head -3
# $FreeBSD: head/COPYRIGHT 216848 2010-12-31 18:07:16Z bz $
#       @(#)COPYRIGHT   8.2 (Berkeley) 3/21/94

%

今度は子プロセス側で読み込みではなく、書き込みを実施するように処理を変更してみます。⁠read-cap-process2.c」として用意しました。

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include <sysexits.h>
#include <sys/capability.h>
#include <sys/types.h>
#include <sys/wait.h>

int
main(void)
{
	int fd, cap, len, stat;
	char bf[BUFSIZ], bf2[] = "a";
	pid_t pid;

	fd = open("COPYRIGHT", O_RDWR);
	if (-1 == fd)
		err(EX_NOPERM, "open error: %d", errno);

	cap = cap_new(dup(fd), CAP_READ | CAP_SEEK);
	if (-1 == cap)
		err(EX_NOPERM, "cap_new error: %d", errno);

	close(fd);

	cap_enter();

	pid = fork();
	if (-1 == pid)
		err(EX_NOPERM, "fork error: %d", errno);

	if (0 == pid) {
		// 子プロセス側で書き込みを試みます。
 		len = write(cap, bf2, 1);
		if (-1 == len)
			err(EX_NOPERM, "write error: %d", errno);

		sleep(10);
	} else if (0 < pid) {
		sleep(10);
	} 

	return 0;
}

実行すると次のようにケーパビリティにおいてエラーが発生することが確認できます。

% clang read-cap-process2.c
% ./a.out | head -3
a.out: write error: 93: Capabilities insufficient
% 

一度ケーパビリティモードに入ったプロセスはケーパビリティモードを抜け出すことはできません。ちなみに、プロセスの親子関係はps(1)コマンドに「-d」のオプションを指定すると視覚的に判断できるようになります。便利なので覚えておくといいでしょう。

おすすめ記事

記事・ニュース一覧