mod_ruid2はDSO版のPHP等を効率良くある程度安全に使うにはかなり良いモジュールである。
「ある程度」と書いたのは、それなりに脆弱性があるためで、それは今後紹介するとして今回はそこに関しては言及しない。
簡単なmod_ruid2の動作だが、preforkされているサーバプロセスに対してリクエストがあった場合、サーバプロセスそのものをリクエスト対象のファイルの権限(uidやgid等)もしくは任意 の権限に変更し、そのごリクエスト処理を行なって、最後に変更されたサーバプロセスの権限を元の権限に戻す。このあたりの細かい話はLinux Capabilityの話になってくるので省略する。
ところが、今回色々試していると、サーバプロセスのユーザー、例えばapacheユーザー等がgroupsの設定を持っていた場合には、groupsの権限は戻せない実装になっていた。そのため、ある ディレクトリをgroupsで設定された別のgroup、例えばwww-admin等で権限設定されていた場合、元のapacheユーザーはwww-adminにアクセスできるが、mod_ruidの処理後そのgroups設定がなくなってしまい、アクセスできなくなる。
例えば、/etc/groupsに以下のような設定がされていて、
www-admin:x:510:apache
とあるVirtualHost配下のディレクトリの権限が以下のようになっていた場合
drwxr-x--- 8 matsumotory www-admin 4096 10月 8 17:32 docroot/
mod_ruid2で権限が戻った後でもgroupsの設定が存在しないプロセス権限になってしまって、アクセスできなくなる。厳密には、preforkされているプロセスの数だけアクセスできた後、403で一切アクセスできなくなる。
mod_ruid2のgroupsの設定戻しの実装は以下のようになっていた。該当の関数がそれほど大きくなかったので、そのまま貼る。
377 /* run during request cleanup */
378 static apr_status_t ruid_suidback (void *data)
379 {
380 request_rec *r = data;
381
382 ruid_config_t *conf = ap_get_module_config (r->server->module_config, &ruid2_module);
383 core_server_config *core = (core_server_config *) ap_get_module_config(r->server->module_config, &core_module);
384
385 cap_t cap;
386 cap_value_t capval[3];
387
388 if (cap_mode == RUID_CAP_MODE_KEEP) {
389
390 cap=cap_get_proc();
391 capval[0]=CAP_SETUID;
392 capval[1]=CAP_SETGID;
393 capval[2]=CAP_SYS_CHROOT;
394 cap_set_flag(cap, CAP_EFFECTIVE, (conf->chroot_dir ? 3 : 2), capval, CAP_SET);
395 if (cap_set_proc(cap)!=0) {
396 ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR %s:cap_set_proc failed before setuid", MODULE_NAME, __func__);
397 }
398 cap_free(cap);
399
400 setgroups(0,NULL);
401 setgid(unixd_config.group_id);
402 setuid(unixd_config.user_id);
403
404 /* set httpd process dumpable after setuid */
405 if (coredump) {
406 prctl(PR_SET_DUMPABLE,1);
407 }
408
409 /* jail break */
410 if (conf->chroot_dir) {
411 if (fchdir(chroot_root) < 0) {
412 ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s failed to fchdir to root dir (%d) (%s)", MODULE_NAME, chroot_root, strerror(errno));
413 } else {
414 if (chroot(".") != 0) {
415 ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s jail break failed", MODULE_NAME);
416 }
417 }
418 core->ap_document_root = old_root;
419 }
420
421 cap=cap_get_proc();
422 capval[0]=CAP_SETUID;
423 capval[1]=CAP_SETGID;
424 capval[2]=CAP_SYS_CHROOT;
425 cap_set_flag(cap, CAP_EFFECTIVE, 3, capval, CAP_CLEAR);
426 if (cap_set_proc(cap)!=0) {
427 ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR %s:cap_set_proc failed after setuid", MODULE_NAME, __func__);
428 }
429 cap_free(cap);
430 }
431
432 return DECLINED;
433 }
上記実装の400から402行目において、元のサーバプロセスの権限にもどしているのだが、setgroupsは無しの設定に戻している。
400 setgroups(0,NULL);
401 setgid(unixd_config.group_id);
402 setuid(unixd_config.user_id);
ということで、これはよろしくないので、groupsを適宜取得し(child_initやinitでやるとうまくいかない)groupsも元通りにするpatchを書いた。以下ソース。
--- mod_ruid2.c.orig 2011-02-24 23:25:28.000000000 +0900
+++ mod_ruid2.c 2011-10-21 16:06:53.000000000 +0900
@@ -78,10 +78,12 @@
typedef struct
{
+ int server_groups_size;
uid_t default_uid;
gid_t default_gid;
uid_t min_uid;
gid_t min_gid;
+ gid_t *server_groups;
int8_t stat_used;
const char *chroot_dir;
const char *document_root;
@@ -150,6 +152,8 @@
conf->stat_used=UNSET;
conf->chroot_dir=NULL;
conf->document_root=NULL;
+ conf->server_groups=NULL;
+ conf->server_groups_size=0;
return conf;
}
@@ -397,7 +401,11 @@
}
cap_free(cap);
- setgroups(0,NULL);
+ if (conf->server_groups_size == 0)
+ setgroups(0, NULL);
+ else
+ setgroups(conf->server_groups_size, conf->server_groups);
+
setgid(unixd_config.group_id);
setuid(unixd_config.user_id);
@@ -541,6 +549,16 @@
cap_t cap;
cap_value_t capval[2];
+ int gidsize;
+ gid_t *grouplist;
+
+ gidsize = getgroups(0, NULL);
+ grouplist = apr_pcalloc(r->pool, gidsize * sizeof(gid_t));
+ getgroups(gidsize, grouplist);
+
+ conf->server_groups = grouplist;
+ conf->server_groups_size = gidsize;
+
if (dconf->ruid_mode==RUID_MODE_STAT) capval[ncap++] = CAP_DAC_READ_SEARCH;
if (chroot_root != UNSET) capval[ncap++] = CAP_SYS_CHROOT;
if (ncap) {
これで、よりmod_ruidな使い方ができるんではないだろうか。もしくは、ここで詰まっていた人は是非patch当ててみてほしい。ただし、mod_ruid2の使い方について気をつけないといけない 事があるので、それは次回に説明しようと思う。ヒントはREADMEに書かれた以下の文章。
there are some security issues, for instance if attacker successfully exploits the httpd
process, he can set effective capabilities and setuid to root. i recommend to use some
security patch in kernel (grsec),or something..
「mod_ruid2はgroupsを元の状態に戻せないのでpatch作成」への1件のフィードバック
コメントは受け付けていません。