Logo address

CAP (capability)

目次

履歴

である。venti, fossil, secstore に関しては解説済みである。今回は cap に関して解説を加える。
cap はプロセスが他のユーザのプロセスに変身を行う事を可能にする新しい試みである[注1]。capの機能を使うとホストオーナーはプログラムの中で直接にパスワードを扱わなくても変身ができる。cap のような機能は欲しいと思う。例えば筆者がデザインした web サーバ Pegasus は ユーザ web としてサービスを行っている。ホストオーナーが web に変身するためには、Plan 9 の伝統的な方法では web をユーザ登録し、パスワードを設定し、そのパスワードを web に変身するプログラケムに与えなくてはならない。問題は web のパスワードをどこに格納しておくかである。
筆者は web のパスワートを cpu サーバのファイルに格納し、chmod によってアクセス制限をかけた。この方法は UNIX でよく見られ、たぶん UNIX ユーザはそれで満足するであろうが、筆者はパスワードファイルが cpu サーバの名前空間の中にあることが気に入らなかった。何かの管理ミスがパスワードの漏洩に繋がるからである。
当時は cap が既に使えるようになっていた。この方法は cpu サーバから見える名前空間からパスワードファイルを排除するのを可能にしているように思えたが、cap の使い方がよく分からなかったので結局 UNIX 的な方法で済ませたのである。
今回は cap を論じる事ができる。以下に見るように cap の現在の仕様は UNIX のroot 特権に近い。筆者はこのような大きな権限をホストオーナーに与える必要があるのかと疑問に思っている。まあ、時間がたてば本当に必要な権限のレベルがはっきりしてくるであろう。

注1: CAP によってプロセスを異なるオーナーとして生成できる。UNIX と異なり Plan 9 の場合には、ファイルシステムに対する権利者が変更された事を意味しない。(2004/11/02)

cap の使い方

ここでは UNIX の su に良く似た機能を果たす Plan 9 のプログラム su を紹介する。但し管理者だけに実行が許されている。cap の使い方が凝縮されているので、cap を利用したプログラムを作りたい人に取って参考になると思う。
/*
*
*	usage: su user
*
*	This program illustrate how to use
*		/dev/caphash
*		/dev/capuse
*
*	This program must be run by hostowner
*
*	[1] TRY in fscons: open -AW
*	- hostowner can become any user without authentication
*	- user need not be registered in /adm/users
*	- user need not be registered in /mnt/factotum/ctl
*	[2] TRY on cpu server with regular open mode of fscons (no -APW)
*	- hostowner can become any real user without authentication
*	- user need be registered in /adm/users
*	- user need not be registered in /mnt/factotum/ctl
*
*	Ref:
*	- cap(3)
*	- sechash(2)
*	- /sys/src/cmd/cpu.c
*/
#include <u.h>
#include <libc.h>
#include <mp.h>
#include <libsec.h>
#include <auth.h>
#define ERRLEN 256

static int
waitfor(int pid, char *msg)
{
	Waitmsg *w;
	while((w = wait()) != nil){
		if(w->pid == pid){
			strncpy(msg, w->msg, ERRMAX);
			free(w);
			return 0;
		}
		free(w);
	}
	return -1;
}


void main(int argc, char *argv[])
{	int fd, i, n, pid, status;
	char *user, *newuser, *key;
	uchar hash[SHA1dlen];
	AuthInfo ai;
	char buf[256];
	char msg[ERRLEN];

	USED(argc); USED(argv);
	argv++;
	if(*argv == nil)
		sysfatal("usage: su user");

	newuser = *argv; // a user you want to be
	user = getuser(); // this user must be host owner

	switch(pid = rfork(RFNOTEG|RFPROC|RFFDG|RFNAMEG)) {/* assign = */
	case -1:
		sysfatal("fork");
	case 0: /* child process */
		break;
	default:
		close(0);
		close(1);
		close(2);
		status = waitfor(pid, msg);
		if(status < 0)
			sysfatal("waitfor: %r");
		exits(nil);
	}

	/* key must be a null terminated string */
	srand(truerand());
	for(i = 0; i < 10; i++)
		sprint(buf+2*i, "%2.2ux", rand());
	key = strdup(buf);

	snprint(buf, sizeof buf, "%s@%s", user, newuser);
	hmac_sha1((uchar*)buf, strlen(buf), (uchar*)key, strlen(key), hash, nil);

	/*	only hostowner can write to /dev/caphash  */
	fd = open("/dev/caphash", OWRITE);
	if(fd < 0)
		sysfatal("unable to open /dev/caphash: %r\n");
	n = write(fd, hash, sizeof hash);
	if(n < 0)
		sysfatal("unable to write /dev/caphash: %r\n");
	close(fd);

	ai.cuid = newuser;
	snprint(buf, sizeof buf, "%s@%s@%s", user, newuser, key);
	ai.cap = buf;
	/*	- auth_chuid writes ai.cap internally to /dev/cap	*
	*		look /sys/src/libauth/auth_chuid.c			*
	* 	- you must call auth_chuid within 1 min			*
	*	after you write to caphash 						*/
	if(auth_chuid(&ai, nil) < 0)
		sysfatal("could't become the user:%r");

	putenv("prompt", "su# ");
	execl("/bin/rc", "rc", "-i", nil);
}

望ましい姿

su を実行してみれば分かると思うが、hostowner の権限が UNIX 並みに強い。これはこれまでに Plan 9 が守って来たシステムコンセプト「パスワードを知っているものだけがそのユーザになる事ができる」に反する。筆者はこのコンセプトを守って欲しい。Plan 9 第4版は secstore と factotum を持っており、これらの組み合わせによって、パスワードを使いながらシステムエージェントとしての仮想ユーザに安全に変身する仕組みはできるはずである。(筆者は当然そのようになっていると思っていたのだが、現在のリリースのバグであろうか?)

問題の起源

2004/10/25 追加
Plan 9 は、これまでに Plan 9 が守って来たシステムコンセプト「パスワードを知っているものだけがそのユーザになる事ができる」を捨てたのであろうか?
su が可能になったメカニズムを探っていくと、OS 自体の仕組みとしてはそうではないことが分かる。問題は factotum にあるのだ。
factotum は認証代理人である。Plan 9 のユーザは認証を受けたホストに対する認証情報を factotum に渡す。これは自動的に行われるのでユーザは意識しない。また、cpu サーバが立ち上がるときに factotum は bootes のパスワードの入力を要求し、この情報が bootes の factotum に渡される。この事は
	cat /mnt/factotum/ctl
で確認できる。
従って cpu サーバが立ち上がってしまえば cpu サーバの owner である bootes はパスワードを入力しなくても factotum のお陰で認証サーバにアクセスできる。この能力が su を可能にしているのである[注意1]。これは便利ではあるが、cpu サーバで動いている bootes のプロセスがこの能力を抱えている事になる。
注意1: factotum が認証サーバのパスワードを見ていると言っているのではない。
cpu サーバの bootes の factotum から bootes のバスワードを削除すればどうなるか? su はもはや威力を失う。しかし、Plan 9 システムの現在の版ではトラブルをもたらす。例えば端末から cpu コマンドで cpu サーバにアクセスできなくなる。認証に cap を利用している cpu コマンドのようなプログラムが動作しなくなるのである。
筆者は bootes の factotum が cpu サーバに無くてもよいようにシステムが動作するようにすべきであると思うが、皆さんはどう思いますか ?

su ver.1.2

su ver.1.2

su の実用的な版が http://plan9.aichi-u.ac.jp/netlib/cmd/ に置かれている。この su はホストオーナーでないユーザが他のユーザに変身できるように、パスワードを使えるようになっている。その場合には
	adom=aichi-u.ac.jp
	su -p xxxxx alice
のように使う。環境変数 adom はパスワードを使うときに必要になる環境変数で認証ドメインを与える。aichi-u.ac.jp は筆者のシステムの認証ドメインで、もちろんシステム毎に違う。xxxxx は alice のパスワード。
su を使うと Plan 9 の認証に対する考え方が良く分かる。

注意: 29 日版の su (ver.1.0) は問題点を抱えていた。パスワードを与えた時のファイルシステムに対する十分な認証能力を持つていなかった。他方 11月02版の su (ver.1.1) は cpu サーバにおける bootes の能力を低めていた。今回の su は大丈夫だと思われる。

UNIX

ユーザ alice はプロセスのオーナーでもありファイルのオーナーでもある。UNIX ではこの二つを区別する必要はなく同一のユーザ名を持っている。プロセスのオーナーである alice が作成するファイルは alice の所有物である。そしてプロセスのオーナーとなる権利が与えられれば自動的にファイルのオーナーとなる権利も与えられるのである。この考え方は一見すると当然のように思えるが、分散ファイルシステムではうまく働かない[注3]。
注3: 例えば管理組織が異なれば与えられるユーザ名が異なる。それらを一つの名前空間に統一できなくなるのである。

Plan 9

Plan 9 ではプロセスのオーナーとなる権利とファイルのオーナーとなる権利は別のものであると考えられている。そのために Plan 9 ではファイルシステム毎にユーザが登録されるのである。小さなシステムの中でユーザ登録されれば両方の権利が与えられる。その場合にはユーザ alice はプロセスのオーナーとしての alice であり、ファイルのオーナーとしての alice でもある。しかしこの同一性は必ず守られるわけではない。プロセスのオーナー alice がファイルのオーナー carol として活動することもありえるのである。

私は誰?

プロセスの owner

プロセスのオーナーは
	ps | grep ps
を実行してみれば直ちに分かる。これはまた /dev/user の値でもある。プロセスのオーナー alice によって生成された仮想ファイルシステムは alice にファイルを提供する。例えば alice が ramfs を実行すれば ramfs に作成されるファイルは全てが alice のファイルとなる。alice が factotum を実行すれば、その factotum は alice のために仮想ファイル /mnt/factotum を提供する、と言った具合である。

ファイルの owner

ファイルの作成者を調べるには chmod 777 のディレクトリを作成し、そこに作成されたファイルのオーナーを見ればよい。(この方法はちょっと面倒だが、筆者はこれ以外の方法を知らない。)
su で遊んでみれば、alice のプロセスによって作成されたファイルが必ずしも alice のファイルを作成しないことが分かるはずである[注4]。
注4: su に実行オプション -n を与えればファイルに関してはそのままである。

認証の基本的な考え方

プロセスの認証

プロセスのオーナーを変更するにはホストオーナーにお願いする。これが cap である。従ってホストオーナーは自由にプロセスのオーナーを変更できる。ホストオーナーでないユーザが他のユーザに変身するにはそのユーザのパスワードを知っている事を認証サーバに示し、認証サーバから貰った認証(cap)を介してホストオーナーにお願いすればホストオーナーは許可してくれる。

ファイルの認証

ファイルシステムに対するオーナーを変更するには認証サーバの認証(ticket)を持ってファイルサーバにお願いする。この時にはパスワードが必須である。但し bootes のプロセスはパスワードなしにファイルシステムに対しても任意のユーザに変身できる[注6]。
注6: /mnt/factotum/ctl に bootes のバスワードが必要である。

実験

以下の説明では Plan 9 システムのホストオーナーを bootes とする。また alice と bob は一般のユーザを代表している。alice と bob はシステムのホストオーナー bootes に一致していてもよい。
実験結果の合理性を判定する基準はセキュリティと使いやすさである。この2つの関係は一般的には一方を立てれば他方が立たなくなるのだが、Plan 9 では両方をうまくやっていることが分かるであろう。

認証のいらない Plan 9 端末

Plan 9 端末が認証なしに動作するのは自分自身のファイルシステムによって動いている場合だけである。この場合にはカーネルは 9pcdisk (kfs ベース) あるいは 9pcf (fossil ベース) が使用される。
Plan 9 にとってディスクベースの端末は個人利用あるいは完全な信頼関係のある小さなグループの利用を想定している。ここには誤操作を防止する機能はあってもセキュリティは存在しない。
筆者の家庭の fossil の設定は open -AW モード、即ち認証はいらず、chmod は誰でもどのファイルに対してもかけられると言う甘いモードで動いている。信頼できる環境ではこれが使いやすい。以下の実験は 9pcf の場合には、このモードでのものである。
bob をPlan 9 端末のホストオーナーとする。bob は Plan 9 システムのユーザの一人でもある。
	su alice
結果: bob による su はパスワードなしでどのユーザにも変身を可能にする。プロセスに対してもファイルに対しても alice となる。

認証サーバでは

筆者の環境では認証サーバはファイルサーバを兼ねている。まだ kfs をベースとして、fossil と venti がここで動いている。以下の結果はこの環境を前提としている。
認証サーバのホストオーナーは任意の認証情報にアクセスできる。従って認証サーバで実行される bootes のプロセスは絶対的な権限を持つ。
bootes による
	su alice
結果: bootes はパスワードなしで任意のユーザに変身できる。プロセスに対してもファイルに対しても alice となる。
bob による
	su -p xxxxx alice
結果: bob は任意のユーザに変身できる。プロセスに対してもファイルに対しても alice となる。

認証の必要な Plan 9 端末

これは例えばファイルサーバが提供するファイルシステムをルートファイルシステムとしている端末( 9pc カーネルで動いている端末)である。
端末のホストオーナー bob はサーバの管理者とは異なる一般のユーザーである。
	su alice
結果: bob はパスワードなしで任意のユーザに変身できるが、ファイルに関しては bob のままである。
この場合にはファイルシステムに対しても alice であるためにはパスワードが必要になる。
	su -p xxxxxx alice
結果: bob は alice としてファイルにアクセスできる。

CPU サーバでは

bootes による
	su alice
結果: bootes はパスワードなしで任意のユーザに変身できる。
bob による
	su -p xxxxxx alice
結果: bob はパスワードの下で任意の実ユーザに変身できる。bob は alice としてファイルにアクセスできる。

チェックポイント

認証がうまく行われない、あるいは何か変な時のチェックポイントを纏めておく。
注意: /adm/users を書き換えただけでは変更は反映されない。kfs の場合には
	disk/kfscmd users
fossil の場合には fossilcons で
	users -r /active/adm/users
を実行する事。

まとめ

su ver.1.2 による実験結果をまとめてみる。
カーネル 実行者 コマンド 結果(プロセス) 結果(ファイル)
9pcdisk bob su alice OK OK
9pcf bob su alice OK OK
9pc bob su alice OK NO
9pc bob su -p xxxx alice OK OK
9pcauth bootes su alice OK OK
9pcauth bob su alice NO NO
9pcauth bob su -p xxxx alice OK OK
9pccpu bootes su alice OK OK
9pccpu bootes su -p xxxx alice OK OK
9pccpu bob su alice NO NO
9pccpu bob su -p xxxx alice OK OK
bob は端末のホストオーナであり Plan 9 システム ( 認証サーバ + ファイルサーバ + CPU サーバ ) のユーザでもある。bootes は Plan 9 システムのオーナである。alice は任意のユーザである。

パスワードなしでどのユーザにも変身できる仕組みは cron の実現に不可欠である。Plan 9 では cron が認証サーバで実行されてきたのは、認証サーバの bootes だけがその能力を持っていたからである。しかし現在のリリースでは cpu サーバもその能力を持つ事になる。(2004/11/04 訂正)

ユーザ none について

none は特殊なユーザである。どのシステムユーザも none になれる。ユーザ名の指定なしで su を実行すると none に変身する。
none の特殊性は、ファイルに対する none のアクセス権にある。none はその他大勢(others)と同じ扱いを受ける。そのために none は /usr/none のファイルに書き込めない。なぜなら標準設定ではディレクトリは chmod 775、ファイルは chmod 664 となっているからである。none はネットワークサービスの主体になっているので、none が所有するファイルがサービスプログラムのバグによって影響を受けないようにしているのである。
こうした強い権利の制限はプロセスに対しても課されている。 none は他のユーザのプロセスはおろか、他の none のプロセスにもアクセスできない。自身の子プロセスを除いては。

none の扱いは Plan 9 の歴史の中でいろいろな変遷を辿って来たと思う。詳しくは "ユーザ none" を見よ。

エラーメッセージ

2004/11/03 追加

パスワードを与えていないときに発生するエラー

次のコマンドを想定する。
	su alice
エラーメッセージと考えられる原因:

パスワードを与えたときに発生するエラー

次のコマンドを想定する。
	su -p xxxxx alice
エラーメッセージと考えられる原因:

おわりに

筆者が su の能力とその限界に関心を持ったのは、将来における Pegasus の webdav のサポートのためである。安全に運用し、かつ認証を受けたユーザに変身したプロセスによってファイルへのアクセスを行う必要がある。安全に運用するためには Pegasus に bootes の特権を与えるわけにはいなかい。もっと弱いユーザである、例えば現在の Pegasus が行っている仮想ユーザ web の下で実行が開始され、パスワードによってそのユーザに変身するメカニズムが望ましい。これは UNIX ではなし得ないが Plan 9 の下では可能である。
筆者は su の能力とその限界を調べて来たが、その過程で多くの混乱した議論を行ってしまった。様々な su を試してみて、様々な異なった結果を得る。ある部分の改良の結果、他の部分の改悪を招いてしまったのである。目標とするのは最大の能力を発揮する su である。その意味で su ver.1.1 の能力表に問題があることを山梨さんからのメールで判明しました。感謝します。