Subject: How can I get setuid shell scripts to work?
Date: Thu Mar 18 17:16:55 EST 1993

4.7) setuid されたシェルスクリプトを動かすにはどうすれば いいでしょうか。

[この質問に対する回答はかなり長くなります。しかしながら、この質問は複 雑で、とてもよくあるものです。この質問に回答し、以下に掲載する "indir" プログラムを作ってくれた Maarten Litmaath に感謝します。]

まず、読者がいわゆる「実行可能シェルスクリプト」を認識するUNIX種 (例え ば、4.3BSDとかSunOS) を使っていると仮定します。そのようなスクリプトは 次のような行で始まっているでしょう。

#!/bin/sh
このようなスクリプトはいわゆる「マジックナンバ」(実行形式の種類を示す) ではじまる本物の (バイナリ) 実行形式に似ているため、「実行形式である」 といわれます。この場合、マジックナンバは '#!'で、OSは最初の行の '#!' 以降をスクリプトのインタプリタ (1文字のオプションが続くことがあります) であると解釈します。以下に例を示します。
#!/bin/sed -f
この例で挙げた上記の行で始まるスクリプトを 'foo' と呼ぶことにしましょう。 このスクリプトは /bin にあるとします。ここで、次のように入力した場合に ついて考えます。
foo arg1 arg2 arg3
OS はこの入力を、以下のような入力があった、というように再配列します。
/bin/sed -f /bin/foo arg1 arg2 arg3
しかしながら、一つ違いがあります。'foo' の setuid 許可ビットが設定され ている場合、すなわち第一番目の例では、コマンド(foo)の許可ビットに従うこ とになります。もし、2番目のように実際に入力したときには、OSは /bin/sed の許可ビットに従います。当然ながら、/bin/sed は setuid されていません。

---------- さて次に、もしシェルスクリプトが '#!' 行で始まっていない場合、もしくは OS が '#!' を認識していない場合はどうなるでしょう。

この場合には、ファイルが正当なマジックナンバーで始まっていないないため、 シェル(か他のプログラム)がスクリプトを実行しようとした時OSがエラーを返 します。シェルはこのエラーを受け取ると、当該ファイルがシェルスクリプト であると「仮定」しまして、以下のような実行をしてみます:

しかしながら、上述のようにこの場合には 'shell_script' に対する setuid ビットは効力を発揮しません。

---------- さて、次に setuid シェルスクリプトがセキュリティ上、危ない点について述 べましょう。

'/etc/setuid_script' というスクリプトが以下のように始まっているとします。

#!/bin/sh
ここで、次のような順序でコマンドを実行したら何が起こるでしょうか。
    
$ cd /tmp
$ ln /etc/setuid_script -i
$ PATH=.
$ -i
最後のコマンドが以下のようになるのは上述の通りです。
/bin/sh -i
このコマンドはスクリプトの持ち主に setuid された、インタラクティブな シェルを起動することになります!まあ、このセキュリティホールは第一行を 次のようにすることで簡単に塞ぐことが出来ます。
#!/bin/sh -
'-' はオプション列の終りを示します。次の引数の '-i' は本来そうあるべき であるように、コマンド群を読み込むべきファイル名として解釈されます。

--------- しかしながらもっと重大なセキュリティに関する問題があります。

$ cd /tmp
$ ln /etc/setuid_script temp
$ nice -20 temp &
$ mv my_script temp
3番目のコマンドは次のように解釈されます。
nice -20 /bin/sh - temp
nice をかけることで、コマンドの実行をずっと遅くできるので、'temp' を シェルが読み込む「前」に4番目のコマンドによりもともとの 'temp' を 'my_script' に置き換えることが可能です。このセキュリティホールを塞ぐ には以下の4つの方法があります。
  1. OS が setuid スクリプトを別の安全な方法で実行するようにする。 System V R4 と 4.4BSD ではスクリプトのファイルディスクリプタをインタ プリタに渡す時に /dev/fd ドライバを使っています。
  2. スクリプトが間接的に実行されるようにする。実際のインタプリタがスクリ プトの実行を開始する前に、すべての環境が正当かどうかをチェックするフ ロントエンドを通す。もし、comp.sources.unix に流れた 'indir' プログ ラムを使うのでしたら、setuid スクリプトは次のようになります。
        
    #!/bin/indir -u
    #?/bin/sh /etc/setuid_script
    
  3. 'binary wrapper'(バイナリによるカバー)を作る。setuid した本当の実行 形式(バイナリ)を作ります。その実行形式はスクリプトの名前を引数として 受け取り、インタプリタを実行することだけを行ないます。
  4. 汎用の 'setuid スクリプトサーバ' を作る。このサーバは要求のあった 'サービス' をデータベース中の正当なスクリプト群から探し、見つかった ら正当な引数を使って正しいインタプリタを起動する、というものです。
--------- 正当なファイルが解釈実行される、というところまでうまくいったとしましょ う。他に危険はないでしょうか。

そうです!シェルスクリプト中で、PATH 変数に安全なパスを明確に指定するこ とを忘れてはいけません。何故だかわかりますか。これとは別に適切に設定さ れていないと面倒を引き起こし得る IFS という変数もあります。他にも、同様 にセキュリティの低下に影響する環境変数がいくつかあります。例えば、SHELL 等です。更に、スクリプト中のコマンド群が対話シェルエスケープを許してい ないことを確認しなければなりません!そして、変に設定されているかも知れ ない umask という値があります...

その他諸々。で、setuid スクリプトはスクリプト中で呼び出すすべてのコマン ドのバグとセキュリティに関する危険を「受け継ぐ」ということを肝に命じて おいて下さい!

概して、setuid シェルスクリプトは非常に危険なものであるということがおわ かり頂けたと思います。かわりに C のプログラムを書く方がより良い解決にな るでしょう。


UNIX FAQ LIST / Copyright(c)1994,Ted Timar / tmatimar@isgtec.com


Maintainer: あさだ たくや