2014-02-11 (Tue) [長年日記]
_ ユーザ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