Logo address

su

目次

更新履歴

要約

ここではユーザ ID が変化するプログラムを紹介する。そのようなプログラムのコアの部分は cap であり、それは他の頁で su の古い版とともに解説されている。従ってここではプログラム中での factotum の利用の仕方が解説の中心になる。
su ver.1.4 では factotum とのインターフェースを追加した。su ver.1.5 は小さな改訂であり、認証部分に関しては ver.1.4 と違いは無い。コマンド実行に関して相対パスを扱えるようにしただけである。

準備

su を紹介する前に、su の成果を調べるためのツールをまず紹介する。

me

meとは

コマンド 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 の内容

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 のインストール

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 を実行できる。

fact

fact とは

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%

fact のオプション

-l
{ls -l /mnt/factotum} および {ls -l /mnt/term/mntfactotum} を表示します。
-d
factotum からキーを削除します
-a
factotum にキーを追加します

注:

fact の内容

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

su ver.1.4

コマンド 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 がシステムのオーナーであることは su のプログラムの中で仮定されている。

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 のようにつぎはぎの無理を重ねることになり、やはり管理を困難にしセキュリティ上の落とし穴を作って行くことになる。将来にわたるニーズを見定め、柔軟性のレベルを確定する事はシステム設計の中で最も難しい問題であろう。

cap を手にするには?

ホストオーナーでない 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 のキーを持たなくても、リモートホストの中にユーザのプロセスを生成する。このメカニズムは筆者は今のところ分かっていない。

getkey とは?

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 の中では次の連続した手順が踏まれている。
  1. /srv/factotum/mnt にマウントし
  2. anewns("alice", nil) を実行する
ここに anewns()newns() の修正版である。
もしも 1. を省けば su は現在の /mnt/factotum の中に alice の認証データを探し、存在しなければ getkey() を呼び出す。
1. が実行される前には既に bootes の /mnt/factotum が存在する。1. は新たに bootes の /mnt/factotum を作成する。既に存在するものと、新たに作成されるものはどこが違うのか? その違いが今の筆者にはよく分からないのである。

su のオプション

su は次のオプシヨンを持っている。多くは実用的と言うよりも教育的なものである。
-D
多くの情報を表示してくれる。認証がつまずいた場合のディバッグに使える。
-n
名前空間を変更しない
-u
ユーザ(/dev/user)を変更しない
-p password
パスワードをコマンド引数で与える。コマンド引数はホストオーナーは見えるので、この形式はホストオーナーにだけ許されている。
-p.
パスワードをコマンド引数からではなく実行時に入力する。
-w
su -w user cmd arg ...
の形式で指定し、cmd の実行完了を待たずに su を終える
-f
su を実行するユーザがホストオーナーであり、かつ factotum の認証ドメインにマッチする最初のキーのユーザが {/lib/ndb/auth} に hostid で指定されている場合に[注1]、このオプションによってパスワード無しに任意のユーザに変身できる。su を実行するユーザ名が bootes の場合には su の内部で自動的にこのオプションが設定される。
注1: 次の「信頼できるユーザ」を見よ

信頼できるユーザ

認証サーバの /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/authhostid は行の先頭から書き始めなくてはならない。他方 uid は先頭から書いてはいけない。

factotum キーの順序

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 オプションは有効ではない。

注2: ライブラリ関数 newns() の抱えている問題のためにホストオーナーのキーは factotum の先頭に置く必要がある。

su によるコマンド実行

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 はカレントディレクトリにあるとしている。

su ver.1.6

2012/09/09

最近になって、

	auth/as
コマンドが Plan9 の公式配布に含まれているのを発見した。2011 年にリリースされたらしい。これは su と良く似ている。
	auth/login + auth/as + auth/none
の組で、su と非常に近いことが出来る。それでも、su は、これらの組よりも多くの事が可能である。

数年前に Plan9 からの派生OSが生まれた。9front と言う。これに関しては別の機会に触れることとする。Plan9 と 9front の表面的な違いであるが、管理者 id が Plan9 の bootes から glenda に変更されている。ver.1.5 までの su は、管理者 id が bootes である事が使われている。そこで ver.1.6 では、glenda でも構わないように書き換えた。また -w オプションを廃止し、さらに、詳しいヘルプメッセージを出すようにした。