Capabilityを抑制するPAM
kernel-2.6.24から2.6.25?にかけて、POSIX Capabilityに関する動きが大きい。
まず、kernel-2.6.24 では File POSIX Capability の機能が入る。Fedora rawhideでは既にEnabledになっている(汗
これは従来のSetUIDプログラムを置き換える可能性がある。
例えば、/bin/ping は cap_net_raw 特権だけが必要だが、SetUID=0により、全てのroot特権を与えていた。
File POSIX Capabilityにより、一般ユーザが/bin/pingを実行した時に、cap_net_raw特権だけを与えることができるようになる。
実はこの機能、ずいぶん昔に私が作っていたのだが、
(http://www.kaigai.gr.jp/index.php?FrontPage#b556e50d)
それと同じアイデアでIBMのSerge君が実装したものがマージされている。ただ、それを設定するためのツールは私のものをポーティングしており、libcap-2.01に含まれている。
http://git.kernel.org/?p=libs/libcap/libcap.git;a=commit;h=7fc5f38e2156201580bab15b22fa581b42f5da91
その他、kernel-2.6.24には間に合わないが、Capabilityの64bit化と、Per-Process Capability Bounding Setの機能がある。
64bit化というのは読んで字の如く。
Per-Process Capability Bounding Set というのは、Serge君が投げた以下のパッチで、プロセスに適用可能なCapabilityの範囲を事前に狭くしておくことができる。
http://marc.info/?t=119082124300001&r=1&w=2
元々、こういった機構は存在したが、これをプロセス単位で設定できるようにしたと言う点がミソ。
【ルール】
1.プロセスのCapabilityは、Capability Bounding Setでマスクされる。
2.Capability Bounding Setは、子プロセスに継承される
3.Capability Bounding Setは、落とすことしかできない
セッションの開始時に、Capability Bounding Setをいくつか落としておくと、そこからfork()した子プロセスが仮にSetUID=0なプログラムを実行しても、全てのroot権限を持つわけではなく、あくまでCapability Bounding Setでマスクされた権限しか持たない。
Serge君はSecurity Containerでの利用を意図しているように見えるが、一般的な機能なので、別にどこで使っても構わない。
例えばPAMモジュールで利用するというのも良いだろう・・・と言うことで、実際に作ってみた。
[ビルド&インストール] # gcc -Wall -c pam_cap_drop.c # gcc -Wall -shared -Xlinker -x -o pam_cap_drop.so pam_cap_drop.o -lpam # cp pam_cap_drop.so /lib/security
[設定] <b>/etc/passwdを編集</b> tak:x:1004:100:cap_drop=cap_net_raw:/home/tak:/bin/bash ^^^^^^^^^^^^^^^^^^^^ /bin/ping できなくする <b>/etc/pam.d/system-authを編集</b> session required pam_cap_drop.so を加える
[実行例] [kaigai@masu ~]$ ssh tak@localhost tak@localhost's password: ← takでログイン Last login: Sat Dec 1 10:09:47 2007 from masu.myhome.cx [tak@masu ~]$ ping 192.168.1.1 ← pingできない ping: icmp open socket: 許可されていない操作です [tak@masu ~]$ su パスワード: pam_cap_bset[9254]: user root does not have 'cap_drop=' property [root@masu tak]# cat /proc/self/status | grep ^Cap CapInh: 0000000000000000 CapPrm: 00000000ffffdfff ← rootになっても、Capabilityは CapEff: 00000000ffffdfff 落ちたまま [root@masu tak]#
小さなプログラムなので、そのままベタッと貼り付けてみる。
/* * pam_cap_drop.c module -- drop capabilities bounding set * * Copyright: 2007 KaiGai Kohei <kaigai@kaigai.gr.jp> */ #include <errno.h> #include <pwd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <syslog.h> #include <sys/prctl.h> #include <sys/types.h> #include <security/pam_modules.h> #ifndef PR_CAPBSET_DROP #define PR_CAPBSET_DROP 24 #endif static char *captable[] = { "cap_chown", "cap_dac_override", "cap_dac_read_search", "cap_fowner", "cap_fsetid", "cap_kill", "cap_setgid", "cap_setuid", "cap_setpcap", "cap_linux_immutable", "cap_net_bind_service", "cap_net_broadcast", "cap_net_admin", "cap_net_raw", "cap_ipc_lock", "cap_ipc_owner", "cap_sys_module", "cap_sys_rawio", "cap_sys_chroot", "cap_sys_ptrace", "cap_sys_pacct", "cap_sys_admin", "cap_sys_boot", "cap_sys_nice", "cap_sys_resource", "cap_sys_time", "cap_sys_tty_config", "cap_mknod", "cap_lease", "cap_audit_write", "cap_audit_control", "cap_setfcap", NULL, }; PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { struct passwd *pwd; char *pos, *buf; char *username = NULL; /* open system logger */ openlog("pam_cap_bset", LOG_PERROR | LOG_PID, LOG_AUTHPRIV); /* get the unix username */ if (pam_get_item(pamh, PAM_USER, (void *) &username)!=PAM_SUCCESS || !username) return PAM_USER_UNKNOWN; /* get the passwd entry */ pwd = getpwnam(username); if (!pwd) return PAM_USER_UNKNOWN; /* Is there "cap_drop=" ? */ pos = strstr(pwd->pw_gecos, "cap_drop="); if (pos) { buf = strdup(pos + sizeof("cap_drop=") - 1); if (!buf) return PAM_SESSION_ERR; pos = strtok(buf, ","); while (pos) { int rc, i; for (i=0; captable[i]; i++) { if (!strcmp(pos, captable[i])) { rc = prctl(PR_CAPBSET_DROP, i); if (rc < 0) { syslog(LOG_NOTICE, "user %s could not drop %s (%s)", username, captable[i], strerror(errno)); break; } syslog(LOG_NOTICE, "user %s drops %s\n", username, captable[i]); goto next; } } break; next: pos = strtok(NULL, ","); } free(buf); } else { syslog(LOG_NOTICE, "user %s does not have 'cap_drop=' property", username); } return PAM_SUCCESS; } PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { /* do nothing */ return PAM_SUCCESS; }