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;
}