オープン FD とセキュリティ
目次
最近 Russ Cox による Plan 9 のセキュリティ上のバグが 9fans にアナウンスされた*。これは cron が dev/caphash をオープンした時のファイルを閉じないまま、そのディスクリプタを子プロセスに渡していることから発生するセキュリティ上の危険性を問題にしている。ここではこれを、もう少し補足しよう。
注*: [9fans] security vulnerability - cron and /dev/caphash (2007年5月17日)
オープンモード
Plan 9 には、ファイルのオープン時に指定できる、セキュリティ上の問題点を回避するのに便利なオプションが 2 つ存在する。ORCLOSE と OCEXEC である。
ORCLOSE
close されたら削除するこの有り難みはよくわかる。
一時ファイルを作って、作業中に異常終了した場合のことを考えてみたらよい。
その時、作成された一時ファイルが自動的に削除されるのだから。
OCEXEC
いわゆる close-on-exec のオプションで、exec または execl が実行されたときにファイルを閉じる
Plan9 の場合には
fd = open("foo", OREAD|OCEXEC); ... execl(....)
UNIX の場合には
fd = open("foo", O_RDONLY) fcntl (fd, F_SETFD, FD_CLOEXEC) .... execl(....)である。
これは何を意図しているか?
明示的に
fd = open(....); ... close(fd); exec(...);で構わないはずだが、あえて OCEXEC を導入するのは、fd 問題で発生するセキュリティホールが多いからか?
mount
mount(2) には mount() が使用する fd についてThe file descriptor fd is automatically closed by a successful mount call.
すなわち
fd = open("/srv/foo",ORDWR) mount(fd,...)で自動的に fd が close される。
まあ、確かに fd はマウントが完了すれば用なしだから。しかし mount が失敗したらどうなるのだろう? fd は close されないことになるが...
BUGS
Mount will not return until it has successfully attached to
the file server, so the process doing a mount cannot be the
one serving.
オープン FD の検査
ファイルディスクリプタ fd を close しないまま、exec を実行すると、その fd が子プロセスに渡る。これをオープン fd と言う。子プロセスはオープン fd のファイルを自由に操る事ができる。オープン fd はプロセスのオーナーが変わるケースでセキュリティ問題に発展する。
筆者の su のように、 su を実行するユーザと、それによって生成されたプロセスを操るユーザが同一人物であれば問題は発生しない。しかし、他のユーザが操るような場合には問題が発生する。このようなニーズはホストオーナーによって生成されるサービスプロセスで発生する。この代表的なケースが telnetd や、 cron や、筆者の mon である。
アクセス制御を掛けなくてはならないファイルのオープン fd はセキュリティを破る。特に
open("/dev/caphash",...)や
open("/mnt/factotum/ctl",...)は大きな問題をもたらすだろう。
一般的に言えば、uid が変わった場合、子プロセスに引き継がせても良い fd は 0,1,2 だけである。
Plan 9 の場合にはオープン fd は容易に検証できる。子プロセスから
ls /fdを実行してみればよい。
term% ls /fd /fd/0 /fd/0ctl /fd/1 /fd/1ctl /fd/2 /fd/2ctl /fd/3 /fd/3ctl term%この場合はオープン fd は 0,1,2 だけである。最後の 3 は ls が開いた fd であり、親プロセスのものではない。実際 fd=2 は
term% cat /fd/2ctl 2 w M 1892 (0000000000000001 0 00) 8192 25834 /dev/cons term%のように、その実体は /dev/cons であるが fd=3 は存在しないことが分かる。
term% cat /fd/3ctl cat: can't open /fd/3ctl: '/fd/3ctl' file does not exist term%
他方
ls -l /fdの場合には次のようになる。
term% ls -l /fd --r-------- d 0 arisawa arisawa 0 Aug 10 2005 /fd/0 --r-------- d 0 arisawa arisawa 0 Aug 10 2005 /fd/0ctl ---w------- d 0 arisawa arisawa 0 Aug 10 2005 /fd/1 --r-------- d 0 arisawa arisawa 0 Aug 10 2005 /fd/1ctl ---w------- d 0 arisawa arisawa 0 Aug 10 2005 /fd/2 --r-------- d 0 arisawa arisawa 0 Aug 10 2005 /fd/2ctl --r-------- d 0 arisawa arisawa 0 Aug 10 2005 /fd/3 --r-------- d 0 arisawa arisawa 0 Aug 10 2005 /fd/3ctl --r-------- d 0 arisawa arisawa 0 Aug 10 2005 /fd/4 --r-------- d 0 arisawa arisawa 0 Aug 10 2005 /fd/4ctl term% cat /fd/3ctl cat: can't open /fd/3ctl: '/fd/3ctl' file does not exist term% cat /fd/4ctl cat: can't open /fd/4ctl: '/fd/4ctl' file does not exist term%今度は 3 と 4 が余計に現れたのは
ls -l /fdによってディレクトリ /fd のオープンと、その中に存在する個々のファイルが順にオープン、クローズされて行くからである。
次に筆者の su を見る。
term% su su# ls /fd /fd/0 /fd/0ctl /fd/1 /fd/1ctl /fd/2 /fd/2ctl /fd/3 /fd/3ctl /fd/4 /fd/4ctl su# cat /fd/3ctl 3 rw M 11 (0000000000000007 0 00) 8192 0 /mnt/factotum/ctl su# cat /fd/4ctl cat: can't open /fd/4ctl: '/fd/4ctl' file does not exist su#つまり su は factotum を開きっぱなしにして子プロセスに渡している。su はサービス用に作成されたものではないので、通常の使い方をしている限りセキュリティ上の問題にはならないが、好ましくはないであろう。サービス用に使用される可能性があるからだ。su の最新版はこの問題点は潰されている。
Pegasus で使用されている筆者の mon はどうであろうか?
term% mon rc term% ps none 7087 0:00 0:00 188K Pread ps term% ls /fd /fd/0 /fd/0ctl /fd/1 /fd/1ctl /fd/2 /fd/2ctl /fd/3 /fd/3ctl term% cat /fd/2ctl 2 w M 1892 (0000000000000001 0 00) 8192 27050 /dev/cons term% cat /fd/3ctl cat: can't open /fd/3ctl: '/fd/3ctl' file does not exist term%OK である。
rfork(RFCFDG)
マニュアル rfork(2) には RFCFDG について、If set, the new process starts with a clean file descriptor tableとある。RFCFDG はファイル記述子のテーブルを完全にクリアするので、かなり特殊な使い方である。
fd の 0,1,2 が必要であれば、明示的に開かなくてはならない。例えば
if((rfork(RFPROC|RFCFDG|RFENVG|RFNAMEG))==0){ putenv("prompt", "#: "); fd = open("/dev/cons", ORDWR); dup(fd,0); dup(fd,1); dup(fd,2); execl("/bin/rc","rc",nil); } waitpid();のようにする。
文献
オープン fd のセキュリティに関する URL がネットにあったので、ついでに紹介する。
- Secure C Programming
By Rich Teer, June 2001
http://developers.sun.com/solaris/articles/secure.html