Understanding SELinux Process Transitions
http://danwalsh.livejournal.com/23944.html
SELinuxの環境で、あるプロセスが他のアプリケーションを実行するときには以下の3つのケースのうちのいずれかが起きる。
もうすこし詳しく見てみよう。
1番目はわかりやすい。実行が許可されていないドメインは拒絶される、それだけだ。
2番目も同様、でも、これはセキュリティホールにならないのだろうか?
制限つきのドメインからは bin_t のタイプのファイルは実行可能だ。そして、大抵のコマンドは bin_t にラベルがついている。これはまずいのでは?たとえば、Apacheから起動されたCGIスクリプトが bin_t のラベル付けがされたコマンドを実行できるのであれば、setenforce 0 なんていうのも実行できてしまってまずくはないだろうか?
実はそんなことはない。SELinuxによって制御されたプロセスが、あるアプリケーションを起動できる、ということと、そのアプリケーションのすべての機能を利用できる、ということは違うのだ。アプリケーションという道具を手にすることはできても、アクセスの権限というものは、元のドメインによる制約のままだ。たとえば、CGIのスクリプトが setenfoce 0を実行しようとしたとしよう、すると、SELinuxの実行状態、という、システムの状態を変えることになるのだが、これは拒絶されてしまうのだ。
さて、3番目が問題だ。これが一番わかりにくい。SELinuxについてテストをしようとした人からしばしば質問をうけるのがこれだ。
「デーモンプロセスをコマンドとして実行したときはうまくいくのに、initプロセスから(サーバの起動時などに)実行するとSELinuxが文句を言うのはなぜなんですか?」
多くのデーモンプロセス(仮にABCとする)のドメインでは、init_daemon_domain というインターフェースが用意されていて、init から起動された時のドメイン initrc_t から、ABC_exec_t というラベルのついたアプリケーションを起動したときに専用の ABC_t というドメインに遷移するような定義ができるようになっている。
例えば、
init_daemon_domain(httpd_t, httpd_exec_t)
という定義をすると、httpd_exec_t というラベルのついたアプリケーションをデーモンのプロセスから起動したときに httpd_t というドメインに遷移することになる。
これを図式化するとこう。
initrc_t -> httpd_exec_t -> httpd_t
この定義によって、Apacheのwebサーバは、httpd_t というドメインで動作することになる。
最初の質問に戻れば、上記の状態と、シェルにログインして、Apacheのwebサーバを直接実行したときとの違いが問題なのだ。
大抵のユーザはシェルにログインした状態では unconfined_t(制限無し) のドメインに居る。unconfined_t のドメインからは、遷移の定義がほとんど存在しない。というのは、シェルにログインしたからにはSELinuxにいちいち指図されるのをユーザは望んでいないからだ。もし、なにかコマンドを実行した拍子にアクセス権が制限されるようなことは誰ものぞまない。
したがって、unconfied_t のプロセスから Apacheのサーバ(httpd_exec_t のラベルがついている)を起動しても、ドメインの遷移はおこらず、Apacheのサーバは unconfiend_t のまま、制限なしのアクセスができる状態のまま実行されてしまう。
ところで、制限無しのプロセスでも、ドメインの遷移が定義されていることがある。それが /etc/init.d に定義されているdaemon起動用のスクリプトを実行する場合で、以下のような遷移をする。
unconfined_t -> initrc_exec_t -> initrc_t
つまり、unconfined_t のプロセスが initrc_exec_t のラベルのついたアプリケーションを起動すると、そのアプリケーションは、initrc_t のドメインで実行されるということだ。シェルにログインしたユーザが /etc/init.d/httpdのスクリプトを使ってApacheのサーバを再起動しようとした場合には、Apacheのサーバ起動スクリプトは initrc_t のドメインで起動できるのである。
(註: initrc_t はtargetd ポリシーでは制限なしのドメインである。他に制約が無いと、デフォルトではなんでもできちゃうので注意が必要だ。)
/etc/init.d にあるスクリプトは上記の initrc_exec_t のラベルがついているので、Apacheサーバ以外のデーモンを制御する場合も同様なことが起こる。
結論としては、シェルにログインしたユーザがApacheのwebサーバを正しく起動したい場合には、/etc/init.d 以下にある initrc_exec_tのラベルがついたスクリプトを使うとよい。スクリプトは initrc_t のドメインで実行され、最終的には、Apacheのhttpdサーバはhttpd_exec_t のラベルがついているので、サーバ自体は起動されると httpd_t のドメインで実行される。まさにこれが望んでいた事態なのだ。
まとめると、遷移は以下のようになる
unconfined_t -> initrc_exec_t(/etc/init.d/httpdのラベル) -> initrc_t(起動スクリプトプロセス) -> httpd_exec_t(httpdのバイナリのラベル) -> httpd_t(webサーバのプロセス)
http://danwalsh.livejournal.com/23944.html
SELinuxの環境で、あるプロセスが他のアプリケーションを実行するときには以下の3つのケースのうちのいずれかが起きる。
- SELinuxが実行を拒絶する:execute AVCメッセージがログに記録される:たとえば、Apacheのウェブサーバから起動されたCGIが /sbin/insmod を実行しようとしたら、拒否されて、ログされる。
- SELinuxが実行を許可する。その際、起動されたプロセスは親プロセスと同じコンテキストのままである:大部分のケースはこれに相当する。ログインしたユーザがコマンドを実行する場合、実行されたコマンドもunconfined_t のラベルがついている。同様に、Apacheサーバから起動されたCGIもhttpd_sys_script_tのラベルを引き継ぐ。
- SELinuxは実行を許可するが、子プロセスは新しいラベルを付けられる:例えば init プロセスが/etc/init.d/ 以下に定義されているスクリプトを実行するときがそれだ。
もうすこし詳しく見てみよう。
1番目はわかりやすい。実行が許可されていないドメインは拒絶される、それだけだ。
2番目も同様、でも、これはセキュリティホールにならないのだろうか?
制限つきのドメインからは bin_t のタイプのファイルは実行可能だ。そして、大抵のコマンドは bin_t にラベルがついている。これはまずいのでは?たとえば、Apacheから起動されたCGIスクリプトが bin_t のラベル付けがされたコマンドを実行できるのであれば、setenforce 0 なんていうのも実行できてしまってまずくはないだろうか?
実はそんなことはない。SELinuxによって制御されたプロセスが、あるアプリケーションを起動できる、ということと、そのアプリケーションのすべての機能を利用できる、ということは違うのだ。アプリケーションという道具を手にすることはできても、アクセスの権限というものは、元のドメインによる制約のままだ。たとえば、CGIのスクリプトが setenfoce 0を実行しようとしたとしよう、すると、SELinuxの実行状態、という、システムの状態を変えることになるのだが、これは拒絶されてしまうのだ。
さて、3番目が問題だ。これが一番わかりにくい。SELinuxについてテストをしようとした人からしばしば質問をうけるのがこれだ。
「デーモンプロセスをコマンドとして実行したときはうまくいくのに、initプロセスから(サーバの起動時などに)実行するとSELinuxが文句を言うのはなぜなんですか?」
多くのデーモンプロセス(仮にABCとする)のドメインでは、init_daemon_domain というインターフェースが用意されていて、init から起動された時のドメイン initrc_t から、ABC_exec_t というラベルのついたアプリケーションを起動したときに専用の ABC_t というドメインに遷移するような定義ができるようになっている。
例えば、
init_daemon_domain(httpd_t, httpd_exec_t)
という定義をすると、httpd_exec_t というラベルのついたアプリケーションをデーモンのプロセスから起動したときに httpd_t というドメインに遷移することになる。
これを図式化するとこう。
initrc_t -> httpd_exec_t -> httpd_t
この定義によって、Apacheのwebサーバは、httpd_t というドメインで動作することになる。
最初の質問に戻れば、上記の状態と、シェルにログインして、Apacheのwebサーバを直接実行したときとの違いが問題なのだ。
大抵のユーザはシェルにログインした状態では unconfined_t(制限無し) のドメインに居る。unconfined_t のドメインからは、遷移の定義がほとんど存在しない。というのは、シェルにログインしたからにはSELinuxにいちいち指図されるのをユーザは望んでいないからだ。もし、なにかコマンドを実行した拍子にアクセス権が制限されるようなことは誰ものぞまない。
したがって、unconfied_t のプロセスから Apacheのサーバ(httpd_exec_t のラベルがついている)を起動しても、ドメインの遷移はおこらず、Apacheのサーバは unconfiend_t のまま、制限なしのアクセスができる状態のまま実行されてしまう。
ところで、制限無しのプロセスでも、ドメインの遷移が定義されていることがある。それが /etc/init.d に定義されているdaemon起動用のスクリプトを実行する場合で、以下のような遷移をする。
unconfined_t -> initrc_exec_t -> initrc_t
つまり、unconfined_t のプロセスが initrc_exec_t のラベルのついたアプリケーションを起動すると、そのアプリケーションは、initrc_t のドメインで実行されるということだ。シェルにログインしたユーザが /etc/init.d/httpdのスクリプトを使ってApacheのサーバを再起動しようとした場合には、Apacheのサーバ起動スクリプトは initrc_t のドメインで起動できるのである。
(註: initrc_t はtargetd ポリシーでは制限なしのドメインである。他に制約が無いと、デフォルトではなんでもできちゃうので注意が必要だ。)
/etc/init.d にあるスクリプトは上記の initrc_exec_t のラベルがついているので、Apacheサーバ以外のデーモンを制御する場合も同様なことが起こる。
結論としては、シェルにログインしたユーザがApacheのwebサーバを正しく起動したい場合には、/etc/init.d 以下にある initrc_exec_tのラベルがついたスクリプトを使うとよい。スクリプトは initrc_t のドメインで実行され、最終的には、Apacheのhttpdサーバはhttpd_exec_t のラベルがついているので、サーバ自体は起動されると httpd_t のドメインで実行される。まさにこれが望んでいた事態なのだ。
まとめると、遷移は以下のようになる
unconfined_t -> initrc_exec_t(/etc/init.d/httpdのラベル) -> initrc_t(起動スクリプトプロセス) -> httpd_exec_t(httpdのバイナリのラベル) -> httpd_t(webサーバのプロセス)
タグ