![]() |
![]() |
/mnt/factotum かそれとも /mnt/term/mnt/factotum か?
/mnt/factotum か?
/lib/ndb/auth
ここではユーザ ID が変化するプログラムを紹介する。そのようなプログラムのコアの部分は cap であり、それは他の頁で su の古い版とともに解説されている。従ってここではプログラム中での factotum の利用の仕方が解説の中心になる。
su ver.1.4 では factotum とのインターフェースを追加した。su ver.1.5 は小さな改訂であり、認証部分に関しては ver.1.4 と違いは無い。コマンド実行に関して相対パスを扱えるようにしただけである。
su を紹介する前に、su の成果を調べるためのツールをまず紹介する。
コマンド me はプロセスを生成するユーザ ID とファイルを生成するユーザ ID を表示する。例えば me を実行すると
term% me arisawa 111 0:00 0:00 184K Pread ps --rw-rw-rw- M 9 arisawa none 0 Dec 7 17:44 /usr/none/tmp/me
me の実行例
のように表示される。me のやっていることは単純で実際にプロセスとファイルを生成してみて生成者の ID を表示する。1行目が生成されたプロセスのユーザ ID、2行目が生成されたファイルのユーザ ID である。UNIX と異なり Plan 9 の場合にはこの2つのユーザ ID は同じとは限らない。
me の内容は
#!/bin/rc # note: chmod 777 /usr/none/tmp f=/usr/none/tmp/me ps|grep ' ps$' if(test -e $f) rm $f touch $f; ls -l $f
me の内容
である。
me はファイルを /usr/none/tmp に生成する。このディレクトリは Plan 9 の標準設定ではユーザ none 以外の書き込めないので
chmod 777 /usr/none/tmp
を実行しておく必要がある。
me はどのユーザからも実行できるようにしておくのがよい。筆者はそのようなファイルは /usr/local/bin に置いている。
chmod 755 /usr/local/bin/rc/me
をお忘れなく。どのユーザも使えるようにするには /bin に自動的に追加されるようにするのが手っ取り早い。
/lib/namespace に次の行が含まれている事を確認する。
. /lib/namespace.local
含まれていない場合には /lib/namespace が古いのでシステムを更新する。
/lib/namespace.local を作成する。
bind -a /usr/local/bin/386 /bin bind -a /usr/local/bin/rc /bin
/lib/namespace.local の内容
これで、どのユーザも me を実行できる。
factotum の内容をいろいろ変更して実験する事になるでしょうから、効率の良い factotum の管理ツール fact を用意しました。次のように使います。
term% fact --rw-r--r-- M 11 arisawa arisawa 0 Dec 7 21:30 /mnt/factotum/ctl key proto=p9sk1 dom=aichi-u.ac.jp user=arisawa !password? key proto=p9sk1 dom=aichi-u.ac.jp user=bootes !password? key proto=p9sk1 dom=aichi-u.ac.jp user=alice !password? term% fact -l -lrw------- M 11 arisawa arisawa 0 Dec 7 21:30 /mnt/factotum/confirm --rw-r--r-- M 11 arisawa arisawa 0 Dec 7 21:30 /mnt/factotum/ctl -lr-------- M 11 arisawa arisawa 0 Dec 7 21:30 /mnt/factotum/log -lrw------- M 11 arisawa arisawa 0 Dec 7 21:30 /mnt/factotum/needkey --r--r--r-- M 11 arisawa arisawa 0 Dec 7 21:30 /mnt/factotum/proto --rw-rw-rw- M 11 arisawa arisawa 0 Dec 7 21:30 /mnt/factotum/rpc term% fact -d deleting from /mnt/factotum/ctl delkey proto=p9sk1 dom=aichi-u.ac.jp user=arisawa !password? delkey proto=p9sk1 dom=aichi-u.ac.jp user=bootes !password? delkey proto=p9sk1 dom=aichi-u.ac.jp user=alice !password? send and ctl-D delkey proto=p9sk1 dom=aichi-u.ac.jp user=alice !password? term% fact -a adding to /mnt/factotum/ctl put data and ctl-D key proto=p9sk1 dom=aichi-u.ac.jp user=alice !password=xxxxx term%
ls -l /mnt/factotum および ls -l /mnt/term/mntfactotum を表示します。注:
send」で送り、ctl-D で削除されます。複数のキーを選択できます。send」で送り、ctl-D で追加されます。複数のキーを選択できます。/mnt/factotum/ctl への書き込みによってキーの削除や追加を行いますが、場合によっては /mnt/term/mnt/factotum/ctl にアクセスする必要があり、オプションの -d や -a に続く文字(1または2)によってどちらであるかを選択できます。fact の内容は次の通りです。
#!/bin/rc
usage='usage: fact [-adl]'
fn addkey {
echo adding to $1
echo 'put data and ctl-D'
read -m >/$1
}
fn delkey {
echo deleting from $1
sed s/key/delkey/g < $1
echo 'send and ctl-D'
read -m > $1
}
ctl=()
f=(/mnt /mnt/term/mnt)^/factotum/ctl
if(test -w $f(2)) ctl=$f(2)
if(test -w $f(1)) ctl=$f(1)
while(~ $1 -*){
switch($1){
case -d
delkey $ctl
exit
case -d1
delkey $f(1)
exit
case -d2
delkey $f(2)
exit
case -a
addkey $ctl
exit
case -a1
addkey $f(1)
exit
case -a2
addkey $f(2)
exit
case -l
f=(/mnt /mnt/term/mnt)^/factotum
for (x in $f)
if(test -e $x) ls -l $x
exit
case -*
echo $usage
exit usage
}
shift
}
for (x in $f)
if(test -w $x){
ls -l $x
cat $x
}
これを me と同様に /usr/local/bin/rc に置くとよいでしょう。chmod 755 をお忘れなく。
コマンド su は UNIX の su と同様にユーザ ID を変更する。また UNIX の sudo の機能を併せ持っている。su のシンタックスは
su [-fnuwD] [-p password] [user [cmd arg ...]]
である。多彩な使い方ができるが、とりあえず最も基本的な使い方、すなわち
su alice
のように単にユーザ名のみを与えた場合に我々の関心を集中することにしよう。su はこの場合でも必要に応じてパスワードの入力を求めてくる:
term% su alice password: blackcat su# me alice 122 0:00 0:00 184K Pread ps --rw-rw-rw- M 28 alice none 0 Dec 7 17:46 /usr/none/tmp/me su#
入力を求められたパスワードなどの情報は、 /mnt/factotum が su を実行するユーザのものであれば、 factotum に自動的に登録される仕組みになっている。cpu コマンドでホストにログインして su を使う場合には
bind -b /mnt/term/mnt/factotum /mnt/factotum
を事前に実行しておけばユーザの端末の factotum を使用できる。端末の factotum とは独立した factotum を新たに作成したい場合には
auth/factotum
あるいは secstore からキーを取り込まない場合には
auth/factotum -n
を実行しておくとよい。
ユーザ名が省略された場合には (UNIX と異なり)最も弱いユーザである none が仮定される。
注意: 認証にはパスワードの他に hostdomain の値が必要である。Plan 9 の現在の版には /dev/hostdomain が存在する。しかしこれは今のところ利用されていない。ここは hostdomain の値を入れる場所のはずである。su は /dev/hostdomain を利用している。ここへの書き込みの権限はホストオーナーだけが持っている。/rc/bin/cpurc と /rc/bin/termrc に次のような行を入れればよい。
echo -n aichi-u.ac.jp >/dev/hostdomain
ここに aichi-u.ac.jp は筆者のシステムの hostdomain でありシステム毎に異なる。/dev/hostdomain に値が設定されていない場合には su は hostdomain の値の入力を要求してくる。
su ver.1.4 は http://plan9.aichi-u.ac.jp/netlib/cmd/su/ から手に入る
ここでは bootes を Plan 9 システムのオーナー、bob を端末のオーナー、alice をユーザとする。
bootes や bob が su によってパスワードなしに alice に変身できるか否かがここでの関心である。
su alice
を実行した場合にパスワードが必要であれば su はパスワードの入力を求める。パスワードの入力が求められなくても、su を実行するユーザの factotum に alice のパスワードが含まれていることが変身可能な理由であれば、変身にはパスワードが必要であると考えよう。すると結果は次のように纏める事ができる。
| カーネル | 実行者 | パスワード | 結果(プロセス) | 結果(ファイル) |
|---|---|---|---|---|
| 9pcdisk | bob | 無 | OK | OK |
| 9pcf | bob | 無 | OK | OK |
| 9pc | bob | 無 | OK | NO |
| 9pc | bob | 有 | OK | OK |
| 9pcauth | bootes | 無 | OK | OK |
| 9pcauth | bob | 無 | NO | NO |
| 9pcauth | bob | 有 | OK | OK |
| 9pccpu | bootes | 無 | OK | OK |
| 9pccpu | bootes | 有 | OK | OK |
| 9pccpu | bob | 無 | NO | NO |
| 9pccpu | bob | 有 | OK | OK |
ここに「結果」欄は me で調べたプロセスやファイルの生成者が alice になれるか否かを問題にしている。
この表は複雑なように見えるが、以下の点から容易に理解できる。
この表には載せなかったが Plan 9 端末もリモートサービスを実行し、端末のホストオーナー bob 以外のユーザ carol が su を実行する事ができる。その場合にはパスワードが要求される。これは cpu サーバや認証サーバで bob が su を実行するのと同じ関係である。
コメント: bootes がパスワード無しで任意のユーザのファイルを生成できるのは認証サーバの /lib/ndb/auth でその権限が与えられているからである。
ここでは su ver.1.4 の要点を解説する。読者はソースコードを読める事が想定されている。
su の以前のバージョンに対する su ver.1.4 の特徴は factotum とのインターフェースをとったことにある。従ってプログラム中での factotum の利用の仕方が解説の中心になる。factotum の解説は本来は作成者である Russ Cox が行ってくれるのが一番良いが、彼は忙しすぎる。筆者の解説はいわゆるハッキングによるもので、今だに分からないところを含み、また誤りの可能性を残している。気がついたことがあれば筆者にメールで知らせて頂きたい。
まずは ver.1.4 を作成する中で筆者が気がついた要点を纏めておく。
/mnt/factotum かそれとも /mnt/term/mnt/factotum か?factotum は /mnt/factotum あるいは (cpu コマンドでログインした場合には) /mnt/term/mnt/factotum に置かれているが、認証に関する場面では Plan 9 のライブラリはどれも /mnt/factotum を使っている。(従って su もそのスタイルを守っている)
/mnt/factotum か?認証のプロセスの中で /mnt/factotum は場合によっては su を実行するユーザのもの、場合によってはホストオーナーのものでなくてはならない。このことがプログラミングでは混乱の原因になる。例えば bob が
su alice
を実行するとしよう。この時 bob が alice のキーを知っている事を bob の factotum の中に alice のキーを持つ事によって示す事ができる。この場面では /mnt/factotum は bob のものでなくてはならない。他方 bob はホストオーナーに対して alice のプロセスの生成者となるようにパスワードを添えてお願いしなくてはならない。この場面では /mnt/factotum はホストオーナーのものでなくてはならない。
標準ライブラリ newns(char *user, char *nsfile) は問題を抱えている。引数 user が実際上働いていないのである(環境変数の設定にしか使われていない)。これは factotum を導入したことに伴う問題点(バグ)である。(Plan 9 にとって本質的なものではなく、ライブラリの修正によって克服可能な問題点である。)
現状の newns では最初の factotum キーが参照される。例えば bob のキーが
key proto=p9sk1 dom=aichi-u.ac.jp user=alice !password?
key proto=p9sk1 dom=aichi-u.ac.jp user=bob !password?
key proto=p9sk1 dom=aichi-u.ac.jp user=carol !password?
の場合には newns("bob", nil) や newns("carol", nil) が実行されても、生成される名前空間は alice のもの、即ち bob はファイルシステムに対しては alice として認証される。この事は newns() を利用したアプリケーションを作成するときにセキュリティ上の落とし穴になる。
克服の方向は2つ考えられる。1つは引数 user を名前空間の構築の中で生かす事、もう1つは newns の引数から user を捨てる事。後者の場合には user は /dev/user を参照して決定する事になる。筆者は /dev/user によって決定される名前空間が好ましいと考えるが、これまでの newns() との互換性と将来の柔軟性を重視すれば前者の方が選択されるであろう。su ver.1.4 の中には修正した newns.c が含まれている。(su の構成に必要な分だけ含まれており、そのため addns() は捨てられている。)
コメント: 不必要な柔軟性は管理を困難にしセキュリティ上の落とし穴を作る。他方硬直したシステム設計はニーズに的確に対応できず、現在の UNIX のようにつぎはぎの無理を重ねることになり、やはり管理を困難にしセキュリティ上の落とし穴を作って行くことになる。将来にわたるニーズを見定め、柔軟性のレベルを確定する事はシステム設計の中で最も難しい問題であろう。
ホストオーナーでない bob が alice のプロセスの生成者となるにはホストオーナーから cap を貰わなくてはならない。この場合にはライブラリ関数
AuthInfo* auth_userpasswd(char *user, char *passwd)
によって、ユーザ名とパスワードを与えて cap が手に入る。(この時には /mnt/factotum はホストオーナーのものを使う。) パスワードはコマンド引数などによって明示的に与えてもよいが、factotum から読み取らせてもよい。factotum から読み取らせる場合には
UserPasswd* auth_getuserpasswd(AuthGetkey *getkey, char* fmt, ...)
を使う。この時の factotum は su を実行するユーザ bob のものである。factotum からパスワードを貰えるためにはキーの形式は proto=pass である。(この場合には factotum は生のパスワードを記憶しており、それを渡す事ができる。)
注目: cpu コマンドはユーザが factotum 中に proto=pass のキーを持たなくても、リモートホストの中にユーザのプロセスを生成する。このメカニズムは筆者は今のところ分かっていない。
factotum は認証に必要な情報が欠落している場合に会話的にユーザにそれらの情報の入力を求める機能を持っている。そのインターフェースになっているのが getkey である。Plan 9 はそのための標準ライブラリ関数 auth_getkey() を持っている。
auth_getkey は /dev/cons を使用する。パスワードの入力の際にエコーバックを止めたいからである。しかし /dev/cons はホストオーナーの所有であり、ホストオーナーだけが使用できる。ホストオーナーでない bob が su を実行し、factotum が auth_getkey を呼び出したら、ここで致命的なエラーが発生するとこになる。従って su は auth_getkey() を使わない。su はエコーバックの問題を犠牲にして標準入出力を使用する。その関数 getkey() が su.c に含まれている。
さて bob が
su alice
を実行すると、su の getkey() は bob から受け取った alice の認証情報を、bob の factotum に格納する。それは bob が所有する /mnt/factotum/ctl に書き出す事によって実現される。このファイルはプロセス factotum による仮想ファイルであり、そのオーナーは factotum の生成者である。従って su は、もしも /mnt/factotum/ctl が bob のものでないなら、factotum を生成しなくてはならない。
bob が
su alice
を実行し、su はホストオーナーから cap を貰って首尾よくプロセス空間としての alice になったとしよう。この時点では su はファイルの名前空間としてはまだ alice にはなっていない。su が alice としてルートファイルシステムをマウントする際に factotum は認証情報が不足しているのを発見したら入力を bob に促す。su は bob が入力した alice の認証情報を bob の /mnt/factotum/ctl に書き出そうとすると(既に su のプロセスは alice のものだから)書き込みを拒否される。
この問題を回避するために su の getkey() ではもう1つの工夫がされている。getkey() で得られた認証情報を確実に bob の factotum に送り込むために、bob の /mnt/factotum/ctl を最初に開いてしまい、その fd (ファイルディスクリプタ) を使い続けるのである。
ホストオーナー bootes が
su alice
を実行した場合 bootes は alice にパスワード無しに変身できる。即ち getkey() を呼び出さない。そのために su の中では次の連続した手順が踏まれている。
/srv/factotum を /mnt にマウントしanewns("alice", nil) を実行するここに anewns() は newns() の修正版である。
もしも 1. を省けば su は現在の /mnt/factotum の中に alice の認証データを探し、存在しなければ getkey() を呼び出す。
1. が実行される前には既に bootes の /mnt/factotum が存在する。1. は新たに bootes の /mnt/factotum を作成する。既に存在するものと、新たに作成されるものはどこが違うのか? その違いが今の筆者にはよく分からないのである。
su は次のオプシヨンを持っている。多くは実用的と言うよりも教育的なものである。
su -w user cmd arg ...
/lib/ndb/auth に hostid で指定されている場合に[注1]、このオプションによってパスワード無しに任意のユーザに変身できる。su を実行するユーザ名が bootes の場合には su の内部で自動的にこのオプションが設定される。/lib/ndb/auth認証サーバの /lib/ndb/auth には「信頼できるユーザ」が定義されている。この内容は標準設定では
hostid=bootes
uid=!sys uid=!adm uid=*
となっている。これは bootes のキーによって、adm と sys を除く他のユーザを認証することを意味している。もしも
hostid=bootes
uid=!sys uid=!adm uid=*
hostid=bob
uid=alice
となっていれば、その他に bob は alice に対してのみパスワード無しでファイルサーバに認証されることになる。
/lib/ndb/auth の hostid は行の先頭から書き始めなくてはならない。他方 uid は先頭から書いてはいけない。bob がパスワード無しで alice に変身できるためには bob の factotum の最初の行が信頼できるユーザのキーでなくてはならない。
例えば bob が su を実行するとして、bob の factotum が
term% fact --rw-r--r-- M 11 arisawa arisawa 0 Dec 7 21:30 /mnt/factotum/ctl key proto=p9sk1 dom=aichi-u.ac.jp user=bootes !password? key proto=p9sk1 dom=aichi-u.ac.jp user=bob !password? term%
の場合には bob は bootes のキーを知っていることになる。そして bootes は /lib/ndb/auth の中で hostid で指定されている。従って su の -f オプションは有効である。 普通は bob のキーは factotum の先頭に置かれる[注2]。
term% fact --rw-r--r-- M 11 arisawa arisawa 0 Dec 7 21:30 /mnt/factotum/ctl key proto=p9sk1 dom=aichi-u.ac.jp user=bob !password? key proto=p9sk1 dom=aichi-u.ac.jp user=bootes !password? term%
その場合には bob も hostid で指定されていない限り -f オプションは有効ではない。
newns() の抱えている問題のためにホストオーナーのキーは factotum の先頭に置く必要がある。su でコマンドを実行する場合のシンタックスは
su [-fnuwD] [- p password] user cmd [arg ... ]
user は必ず必要である。ユーザ none の場合には user には none を指定する。cmd は実行すべきコマンド名であり、arg はそれに与える引数である。コマンド名には絶対パス、相対パス、あるいは /bin からの相対パスが指定できる。
su ver.1.5 では: 絶対パスは / で始まる。相対パスは ./ あるいは ../ で始まる。いずれでもないものは /bin からの相対パスであると見なされる。
su none ps
su alice me
su alice ./foo
最後の例では実行ファイル foo はカレントディレクトリにあるとしている。