トップ «前の日記(2014-01-26 (Sun)) 最新 次の日記(2014-02-12 (Wed))» 編集   RSS 1.0 FEED  

Journal InTime


2014-02-11 (Tue) [長年日記]

_ [Linux][FreeBSD][C][Ruby] ユーザIDを変更するプログラムのcoreダンプ

UnixライクなOSでは、ユーザIDを変更するプログラムでSEGVが発生した場合、ulimitなどでcoreを吐くように設定していてもcoreが吐かれないことがある。

これはセキュリティ上の理由で、例えばsetuidプログラムでは、一般ユーザに見られてはまずい情報がcoreファイルから漏洩したり、root権限のファイルを上書きしてしまうといった問題があるので、単純にroot権限でcoreファイルを作成してしまうことは好ましくない。 このため、setuidプログラムや、seteuid(2)などでユーザID/グループIDを変更するプログラムは、デフォルトではcoreを吐かないようになっている。

この動作は通常は問題ないが、デバッグの際に不便なので、Linuxなどでは設定で動作を変えられるようになっている。

グローバルな設定

Linuxの場合は、fs.suid_dumpableというカーネルパラメータでグローバルにこの挙動を変えることができ、sysctl -w fs.suid_dumpable=1のように設定する。設定できる値は以下の三つ。

0
ユーザID/グループIDを変更するプログラムはcoreを吐かない。
1
すべてのプログラムがcoreを吐く。
2
"0"のモードでcoreを吐かないケースでは、rootしか読めないパーミッションでcoreを吐き、既存のファイルがある場合は上書きしない。

proc(5)のマニュアルによると0がデフォルトのことだが、手元のUbuntuでは2がデフォルトになっているので、ディストリビューションによってデフォルトが異なるかもしれない。必要ならsysctl.confで設定しておくとよいだろう。

FreeBSDにもkern.sugid_coredumpというカーネルパラメータがあるが、上記の0と1のモードのみで、2のようなモードはない。

手元に環境がないが、Solarisの場合はcoreadmコマンドで設定できるようだ。

プロセス毎の設定

Linuxの場合、プロセス毎にフラグを持っていて、以下のようにprctl(2)で設定できる。*1

prctl(PR_SET_DUMPABLE, 0); /* coreダンプしないように設定 */
prctl(PR_SET_DUMPABLE, 1); /* coreダンプするように設定 */

fs.suid_dumpableが0の場合はseteuid(2)などを呼ぶとこのフラグが自動的に0にセットされるので、ユーザIDを変更した後でprctl(2)を使って値を再設定すればよい。*2

例えば、ApacheはCoreDumpDirectoryが設定されている場合はprctl(PR_SET_DUMPABLE, 1)を実行するようになっている。

FreeBSDもP_SUGIDという同様のフラグ(ただしオンオフが逆)があるが、Linuxのようにシステムコールで設定することはできない。 ただし、実ユーザIDと実効ユーザID、実グループIDと実効グループIDがそれぞれ同じ状態でexecve(2)を呼ぶとクリアされる(これはLinuxも同様)。

FreeBSDのソースを読むと実は__setugid(2)というシステムコールが存在するが、リグシッションテスト用のシステムコールのようで、以下のように普通にビルドしたカーネルではENOSYSを返す。

int
sys___setugid(struct thread *td, struct __setugid_args *uap)
{
#ifdef REGRESSION
        struct proc *p;

        p = td->td_proc;
        switch (uap->flag) {
        case 0:
                PROC_LOCK(p);
                p->p_flag &= ~P_SUGID;
                PROC_UNLOCK(p);
                return (0);
        case 1:
                PROC_LOCK(p);
                p->p_flag |= P_SUGID;
                PROC_UNLOCK(p);
                return (0);
        default:
                return (EINVAL);
        }
#else /* !REGRESSION */

        return (ENOSYS);
#endif /* REGRESSION */
}

ただ、フラグの取得はissetugid(2)で行うことができ、RubyからもProcess::Sys.issetugidで取得できる。

p Process::Sys.issetugid

*1  古いカーネルではfs.suid_dumpableと同じように2も設定できたようだが、一般ユーザが設定できるのはセキュリティ上問題があるということで、今は0と1しか設定できないように変更されている。

*2  Rubyの場合は、標準ではAPIが提供されていないので、Kernel#syscallやFiddleなどを使う必要がある。