capgetシステムコール
cap_header.versionは_LINUX_CAPABILITY_VERSION_1/_LINUX_CAPABILITY_VERSION_2/_LINUX_CAPABILITY_VERSION_3のいずれかで、設定可能CAP数となります。cap_header.pidは取得するスレッドIDで、0ならカレントスレッドのCAPを取得します。
.cap_effectiveがスレッドの実際の権限属性です。.cap_permittedはCAP更新する時の許可属性です。.cap_inheritableはroot権限のプロセスがexecした時のプロセスへ継承する属性ですが、これはセキュアLinuxに依存し、そうでない場合、継承する属性は、.cap_bsetと.cap_inheritableの論理和となり、.cap_bsetはFULL故.cap_inheritableは実際の有効性はありません。
init_credは初期プロセスのCAPで、.cap_inheritableはEMPT、.cap_permitted/.cap_effective/.cap_bsetはFULLで、root権限のプロセスがexecしたプロセスへ継承されます。
Linux2では、capsetシステムコールでcap_header.pidのスレッドのCAPを設定可能となっていましたが、Linux3では、root権限であってもカレントスレッドのみのCAPしか更新できないようになっており、運用面として思うに、root権限で動作させるコマンド等のプロセスで、プログラミングミスによる脆弱性を回避させる故かと思います。
#include <sys/syscall.h>
#include <stdio.h>
#define _LINUX_CAPABILITY_VERSION_1 0x19980330
#define _LINUX_CAPABILITY_U32S_1 1
#define _LINUX_CAPABILITY_VERSION_2 0x20071026
#define _LINUX_CAPABILITY_U32S_2 2
#define _LINUX_CAPABILITY_VERSION_3 0x20080522
#define _LINUX_CAPABILITY_U32S_3 2
typedef unsigned int __u32;
struct cap_header {
__u32 version;
int pid;
};
struct cap_data {
__u32 effective;
__u32 permitted;
__u32 inheritable;
};
void main(int argc, char *argv[])
{
struct cap_header hdrp;
struct cap_data datap[2];
int i, ret;
hdrp.pid = 0;
hdrp.version = _LINUX_CAPABILITY_VERSION_2;
ret = syscall(SYS_capget, &hdrp, &datap);
if (!ret) {
printf("effective :%08x:%08x\n", datap[1].effective, datap[0].effective);
printf("permitted :%08x:%08x\n", datap[1].permitted, datap[0].permitted);
printf("inheritable:%08x:%08x\n", datap[1].inheritable, datap[0].inheritable);
}
else {
printf("err:%d\n", ret);
}
}
実行結果[root@localhost test]# ./a.out effective :ffffffff:ffffffff permitted :ffffffff:ffffffff inheritable:00000000:00000000 [kitamura@localhost test]$ ./a.out effective :00000000:00000000 permitted :00000000:00000000 inheritable:00000000:00000000Linux 3では35ケのCAPをサポートしています。実装できるCAP数は、_LINUX_CAPABILITY_VERSION_1なら32、_LINUX_CAPABILITY_VERSION_2/_LINUX_CAPABILITY_VERSION_3なら64です。
.cap_effectiveがスレッドの実際の権限属性です。.cap_permittedはCAP更新する時の許可属性です。.cap_inheritableはroot権限のプロセスがexecした時のプロセスへ継承する属性ですが、これはセキュアLinuxに依存し、そうでない場合、継承する属性は、.cap_bsetと.cap_inheritableの論理和となり、.cap_bsetはFULL故.cap_inheritableは実際の有効性はありません。
init_credは初期プロセスのCAPで、.cap_inheritableはEMPT、.cap_permitted/.cap_effective/.cap_bsetはFULLで、root権限のプロセスがexecしたプロセスへ継承されます。
#define CAP_WAKE_ALARM 35
#define CAP_LAST_CAP CAP_WAKE_ALARM
#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
# define CAP_EMPTY_SET ((kernel_cap_t){{ 0, 0 }})
# define CAP_FULL_SET ((kernel_cap_t){{ ~0, ~0 }})
struct cred init_cred = {
.usage = ATOMIC_INIT(4),
#ifdef CONFIG_DEBUG_CREDENTIALS
.subscribers = ATOMIC_INIT(2),
.magic = CRED_MAGIC,
#endif
.securebits = SECUREBITS_DEFAULT,
.cap_inheritable = CAP_EMPTY_SET,
.cap_permitted = CAP_FULL_SET,
.cap_effective = CAP_FULL_SET,
.cap_bset = CAP_FULL_SET,
.user = INIT_USER,
.user_ns = &init_user_ns,
.group_info = &init_groups,
#ifdef CONFIG_KEYS
.tgcred = &init_tgcred,
#endif
};
SYSCALL_DEFINE2(capget, cap_user_header_t, header, cap_user_data_t, dataptr)
{
int ret = 0;
pid_t pid;
unsigned tocopy;
kernel_cap_t pE, pI, pP;
ret = cap_validate_magic(header, &tocopy);
if ((dataptr == NULL) || (ret != 0))
return ((dataptr == NULL) && (ret == -EINVAL)) ? 0 : ret;
if (get_user(pid, &header->pid))
return -EFAULT;
if (pid < 0)
return -EINVAL;
ret = cap_get_target_pid(pid, &pE, &pI, &pP);
if (!ret) {
struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
unsigned i;
for (i = 0; i < tocopy; i++) {
kdata[i].effective = pE.cap[i];
kdata[i].permitted = pP.cap[i];
kdata[i].inheritable = pI.cap[i];
}
if (copy_to_user(dataptr, kdata, tocopy
* sizeof(struct __user_cap_data_struct))) {
return -EFAULT;
}
}
return ret;
}
static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy)
{
__u32 version;
if (get_user(version, &header->version))
return -EFAULT;
switch (version) {
case _LINUX_CAPABILITY_VERSION_1:
warn_legacy_capability_use();
*tocopy = _LINUX_CAPABILITY_U32S_1;
break;
case _LINUX_CAPABILITY_VERSION_2:
warn_deprecated_v2();
case _LINUX_CAPABILITY_VERSION_3:
*tocopy = _LINUX_CAPABILITY_U32S_3;
break;
default:
if (put_user((u32)_KERNEL_CAPABILITY_VERSION, &header->version))
return -EFAULT;
return -EINVAL;
}
return 0;
}
static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
kernel_cap_t *pIp, kernel_cap_t *pPp)
{
int ret;
if (pid && (pid != task_pid_vnr(current))) {
struct task_struct *target;
rcu_read_lock();
target = find_task_by_vpid(pid);
if (!target)
ret = -ESRCH;
else
ret = security_capget(target, pEp, pIp, pPp);
rcu_read_unlock();
} else
ret = security_capget(current, pEp, pIp, pPp);
return ret;
}
static inline int security_capget(struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted)
{
return cap_capget(target, effective, inheritable, permitted);
}
int cap_capget(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
const struct cred *cred;
rcu_read_lock();
cred = __task_cred(target);
*effective = cred->cap_effective;
*inheritable = cred->cap_inheritable;
*permitted = cred->cap_permitted;
rcu_read_unlock();
return 0;
}
補足
suコマンドでのuidを変更は、シェルからforkでプロセスを作成し、かかるuidを設定した後、execでシェルを起動することになり、この時、forkでのプロセスのCAPに応じたCAPが設定されるのですが、rootでない場合、無条件にすべてのCAPはクリアされます。Linux2では、capsetシステムコールでcap_header.pidのスレッドのCAPを設定可能となっていましたが、Linux3では、root権限であってもカレントスレッドのみのCAPしか更新できないようになっており、運用面として思うに、root権限で動作させるコマンド等のプロセスで、プログラミングミスによる脆弱性を回避させる故かと思います。




