Tetsuo Handa
from-****@I-lov*****
2010年 2月 18日 (木) 21:43:35 JST
熊猫です。 今までのドメイン遷移には execve() が必要でした。そのため、 execve() を 伴わないで実行される Apache のCGIでは、ドメイン遷移が行われないため、 権限を分割することができませんでした。 しかし、バーチャルホストやCGIに対応して欲しいという意見が出てきています。 そこで、 execve() を伴わないでドメイン遷移を行う仕組みについて検討しています。 <実装案> ・アトミックな書込み操作で行う Apache は一般ユーザの権限で動作します。そのため、 root 以外のユーザに対しても execve() を伴わないドメイン遷移を認める必要があります。 ドメイン遷移を行うには、遷移先を指定する文字列をカーネルが受け取る必要が あります。文字列を複数回の呼び出しに分けて受け取ることを認めた場合、カーネル 内でメモリを割り当てる必要が生じます。すると、悪意あるユーザが文字列の 一部だけを渡すことにより、カーネル内のメモリを消費させるという攻撃が成立して しまいます。 このような攻撃を回避するために、文字列は1回の呼び出しで受け取るように 制限をかけようと思います。具体的には、「文字列+'\0'」という形式で1回の write() で渡すようにしたいと思います。 疑問点としては、ライブラリなどが提供するバッファリング機構により、 ユーザに対しては1回の書き込み要求として処理されても、カーネルに対しては 複数回の write() システムコールとして処理されてしまう可能性がある点です。 write() システムコールを呼び出すことができる言語であれば問題ないのですが、 そうではない言語も存在するかもしれません。 write() システムコールではなく、 他のシステムコールを使う方が良いのかもしれません。 ・安全上の理由から、文字列の先頭に // を付与する /bin/bash が制御を保持したまま /usr/bin/passwd ドメインに遷移することを 認めてしまうと、 /bin/bash に対して /etc/shadow へのアクセスを認めてしまう ことになり危険です。 そのため、 execve() を伴うドメイン遷移と execve() を伴わないドメイン遷移とを 区別できるようにするべきだと考えています。具体的には、 execve() を伴わない ドメイン遷移の場合には、カーネル側で自動的に // というプレフィックスを 付与するようにしたいと思います。 execve() を伴うドメイン遷移の場合には 正規化されたパス名が用いられるため、( aggregator キーワードを用いて マッピングしない限り) // で始まることはありません。 ・ keep_domain や initialize_domain の適用対象外とする ドメイン遷移を行うことが目的ですので、 keep_domain キーワードにより ドメイン遷移が抑制されてしまっては意味がありません。 また、 initialize_domain キーワードによりドメイン遷移を初期化する価値が 見当たらないので、とりあえず適用対象外にしようと思います。 <使用方法> ccs-patch-1.7.1-20100214.tar.gz に対するカーネルパッチが http://sourceforge.jp/projects/tomoyo/svn/view/branches/transit3.diff?root=tomoyo&revision=3463 にあります。 # ccs-patch-1.7.1-20100110.tar.gz に対するパッチが #(パッチのURL) #にあります。 この機能を Apache 2.x で使うと、 http://sourceforge.jp/projects/tomoyo/svn/view/branches/apache-1.png?root=tomoyo&revision=3463 http://sourceforge.jp/projects/tomoyo/svn/view/branches/apache-2.png?root=tomoyo&revision=3463 のようにバーチャルホスト名やCGIアプリケーション名単位で権限を分割することが できるようになります。 # wget -O mod_ccs.c 'http://sourceforge.jp/projects/tomoyo/svn/view/branches/mod_ccs.c?root=tomoyo&revision=3463' # apxs -c mod_ccs.c # apxs -i -a mod_ccs.la でインストールできます。(環境によっては apxs ではなく apxs2 を使います。) そして、 /usr/share/horde3/\* appname=horde /usr/share/horde3/\{\*\}/\* appname=horde /var/www/cgi-bin/main.cgi /var/www/cgi-bin/main.cgi /var/www/cgi-bin/\* appname=cgi-bin /var/www/cgi-bin/\{\*\}/\* appname=cgi-bin /\{\*\}/\*.pl appname=perl-programs /\{\*\}/\*.php appname=php-programs /\{\*\}/\* default /\* default のように、 要求されたパス名のパターン1 遷移先のドメイン名1 要求されたパス名のパターン2 遷移先のドメイン名2 要求されたパス名のパターン3 遷移先のドメイン名3 ・ ・ ・ というリストを /etc/apache_domain_map.conf として保存すれば準備完了です。 この機能を Android で使うためには --- mydroid.orig/dalvik/vm/native/dalvik_system_Zygote.c +++ mydroid/dalvik/vm/native/dalvik_system_Zygote.c @@ -25,6 +25,8 @@ #include <sys/wait.h> #include <grp.h> #include <errno.h> +#include <fcntl.h> /* open() */ +#include <sys/stat.h> /* open() */ #if defined(HAVE_PRCTL) # include <sys/prctl.h> @@ -358,6 +360,16 @@ static pid_t forkAndSpecializeCommon(con LOGW("cannot setuid(%d) errno: %d", uid, errno); } + { + const int fd = open("/proc/ccs/.transition", O_WRONLY); + if (fd >= 0) { + char buffer[128]; + memset(buffer, 0, sizeof(buffer)); + snprintf(buffer, sizeof(buffer) - 1, "uid=%d/gid=%d", uid, gid); + write(fd, buffer, strlen(buffer) + 1); + close(fd); + } + } /* * Our system thread ID has changed. Get the new one. */ という修正を行います。すると、 http://sourceforge.jp/projects/tomoyo/svn/view/branches/android-1.png?root=tomoyo&revision=3463 http://sourceforge.jp/projects/tomoyo/svn/view/branches/android-2.png?root=tomoyo&revision=3463 のようにユーザIDやグループIDを単位として権限を分割できるようになります。 com.android.browser のような名前による分割には未対応ですが、名前が判明している 場所からこの機能を呼び出すようにしてやればできるはずです。 <質問/意見募集> (1) AppArmor のように元のドメインに戻れる機能が必要ですか? mod_ccs のように clone() + wait() を使えば戻れなくても問題ないはずです。 制御を渡さないのであれば、戻れるようにしておくと便利な場合があります。 しかし、戻れるようにする場合には不正に戻されないようにするための工夫が 必要になります。 AppArmor では64ビットの乱数を用いて防御していますが、 TOMOYO 1.x で実現する場合にはタスク構造体に変数を追加しないと実現 できません。 (2)子ドメインへの遷移以外に、親ドメインや任意のドメインに遷移できる機能も 必要ですか? 親ドメインや任意のドメインに遷移できる機能はセキュリティ上好ましくないので 実装しない方が良いと考えています。親ドメインや任意のドメインに遷移できると 便利になる実例をお持ちでしたら検討しますのでお知らせください。 (3) mod_ccs について、バーチャルホスト名によるドメイン遷移はバーチャル ホスト名を自動的に使うようにしますか?それとも、バーチャルホスト名とドメイン 名とのマッピングテーブルを参照するようにしますか? バーチャルホスト名をそのまま使うようにした場合、バーチャルホストの数が 2つとか3つであれば問題ないでしょうが、バーチャルホストの数だけドメインが 作成されるので、100とか1000とかある場合にはグループ化できないと 困るかもしれません。 (4)その他なんでも