FreeBSD Daily Topics

2011年9月7日Capsicumを知る - ケーパビリティを取得するcap_new(2)

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

How to get a capability?

今回はケーパビリティを取得するためのcap_new(2)の使い方の基本を紹介します。まず、前回作成したソースコードを少しだけ拡張して、テキストファイルの内容を読み込んで、標準出力に出力するようにします。簡易版cat(1)といったところです。ここでは「read.c」として用意しておきます。

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include <sysexits.h>

int
main(void)
{
	int fd, len;
	char bf[BUFSIZ];

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

 	len = read(fd, bf, BUFSIZ);
	if (-1 == len)
		err(EX_NOPERM, "read error: %d", errno);

	printf("%s\n", bf);

	return 0;
}

コンパイルして実行するとその通りの動作をします。

% clang read.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.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>

int
main(void)
{
	int fd, len;
	char bf[BUFSIZ];

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

	// ファイルディスクリプタを開いたあとで、
	// ケーパビリティモードへ入ります。
	cap_enter();

 	len = read(fd, bf, BUFSIZ);
	if (-1 == len)
		err(EX_NOPERM, "read error: %d", errno);

	printf("%s\n", bf);

	return 0;
}

次のように動作していることが確認できます。ケーパビリティモードに入る前に用意したファイルディスクリプタはそのまま利用でき。

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

%

次にcap_new(2)でケーパビリティを作成して利用する基本的な方法を説明します。cap_new(2)には既存のファイルディスクリプタと、どの権限を与えるのかの指定を与えます。下記の例ではCAP_READとCAP_SEEKを許可しています。どのシステムコールを許可するのか指定するようなものです。

もとのファイルディスクリプタは、ケーパビリティを取得したあとには使いませんので、通常はdup(2)で複製したファイルディスクリプタを渡しておき、ケーパビリティ取得後にはclose(2)しておくのが通例といえます。ファイルは「read-cap2.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>

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


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

	// dup()で複製したファイルディスクリプタを渡して
	// ファイルディスクリプタからケーパビリティを作成
	// します。ここでは読み書き可能なファイルディスクリプタ
	// から、読み込みのみ許可したケーパビリティを作成
	// しているところがポイントです。次回、この権限指定が
	// 機能していることを確認します。
	cap = cap_new(dup(fd), CAP_READ | CAP_SEEK);
	if (-1 == cap)
		err(EX_NOPERM, "cap_new error: %d", errno);

	// 元のファイルディスクリプタはもう使わないので
	// 閉じておきます。
	close(fd);

	// ケーパビリティモードへ入ります。
	cap_enter();

 	len = read(cap, bf, BUFSIZ);
	if (-1 == len)
		err(EX_NOPERM, "read error: %d", errno);

	printf("%s\n", bf);

	return 0;
}

実行すると次のように動作します。

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

%

これがcap_new(2)の基本的な使い方です。cap_new(2)で権限を絞り込み、cap_enter(2)でケーパビリティモードに入ってより制限の厳しい状況で動作する、これがCapsicumの提供する基本的なセキュリティ機能となります。

おすすめ記事

記事・ニュース一覧