capsetシステムコール
capsetシステムコールは、header.versionで設定サイズを32バイトか64バイトで、captgetシステムコールと同じ引数となりますが、header.pidに関係なくカレントスレッドしかCAP更新できません。
header.versionにかかるCAPのサイズ分を、引数のdataからeffective, inheritable, permittedに設定した後、security_capset()でstruct cred *newに設定し、commit_creds()でカレントプロセスのcredと差し替えます。
デフォルトで、rootならCAP_SETPCAP/SECURITY_CAP_AUDITを有して、cap_bsetはfull故に、inheritableはfullでの設定が可能です。
rootでないなら、cap_inheritable/cap_permittedは0で、エラーとなり、他のいかなるCAPも更新できません。permittedは、プロセスのpermittedのサブセットでなければなりません。effectiveは、設定するpermittedのサブセットでなければなりません。
permittedのサブセットでのeffectiveの変更可能ですが、permittedはpermittedを超えられず、従ってrootであってもpermittedを超えての権限拡張はできません。また、permittedを縮小して設定しても、前の状態のpermittedに戻すこともできません。
cap_bsetは有していません。cap_bsetはコンパイル時にデフォルトとして設定され更新することはできません。独自のCAPの取り扱いを規定するシステムを構築する目的故の実装かと思います。
header.versionにかかるCAPのサイズ分を、引数のdataからeffective, inheritable, permittedに設定した後、security_capset()でstruct cred *newに設定し、commit_creds()でカレントプロセスのcredと差し替えます。
SYSCALL_DEFINE2(capset, cap_user_header_t, header, const cap_user_data_t, data)
{
struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
unsigned i, tocopy, copybytes;
kernel_cap_t inheritable, permitted, effective;
struct cred *new;
int ret;
pid_t pid;
ret = cap_validate_magic(header, &tocopy);
if (ret != 0)
return ret;
if (get_user(pid, &header->pid))
return -EFAULT;
if (pid != 0 && pid != task_pid_vnr(current))
return -EPERM;
copybytes = tocopy * sizeof(struct __user_cap_data_struct);
if (copybytes > sizeof(kdata))
return -EFAULT;
if (copy_from_user(&kdata, data, copybytes))
return -EFAULT;
for (i = 0; i < tocopy; i++) {
effective.cap[i] = kdata[i].effective;
permitted.cap[i] = kdata[i].permitted;
inheritable.cap[i] = kdata[i].inheritable;
}
while (i < _KERNEL_CAPABILITY_U32S) {
effective.cap[i] = 0;
permitted.cap[i] = 0;
inheritable.cap[i] = 0;
i++;
}
new = prepare_creds();
if (!new)
return -ENOMEM;
ret = security_capset(new, current_cred(),
&effective, &inheritable, &permitted);
if (ret < 0)
goto error;
audit_log_capset(pid, new, current_cred());
return commit_creds(new);
error:
abort_creds(new);
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;
}
inheritablはプロセスがCAP_SETPCAP/ SECURITY_CAP_AUDITを有してないなら、cap_inheritable | cap_permitted、有しているならcap_inheritable | cap_bsetのサブセットでなければなりません。デフォルトで、rootならCAP_SETPCAP/SECURITY_CAP_AUDITを有して、cap_bsetはfull故に、inheritableはfullでの設定が可能です。
rootでないなら、cap_inheritable/cap_permittedは0で、エラーとなり、他のいかなるCAPも更新できません。permittedは、プロセスのpermittedのサブセットでなければなりません。effectiveは、設定するpermittedのサブセットでなければなりません。
permittedのサブセットでのeffectiveの変更可能ですが、permittedはpermittedを超えられず、従ってrootであってもpermittedを超えての権限拡張はできません。また、permittedを縮小して設定しても、前の状態のpermittedに戻すこともできません。
static inline int security_capset(struct cred *new,
const struct cred *old,
const kernel_cap_t *effective,
const kernel_cap_t *inheritable,
const kernel_cap_t *permitted)
{
return cap_capset(new, old, effective, inheritable, permitted);
}
int cap_capset(struct cred *new,
const struct cred *old,
const kernel_cap_t *effective,
const kernel_cap_t *inheritable,
const kernel_cap_t *permitted)
{
if (cap_inh_is_capped() &&
!cap_issubset(*inheritable,
cap_combine(old->cap_inheritable,
old->cap_permitted)))
return -EPERM;
if (!cap_issubset(*inheritable,
cap_combine(old->cap_inheritable,
old->cap_bset)))
return -EPERM;
if (!cap_issubset(*permitted, old->cap_permitted))
return -EPERM;
if (!cap_issubset(*effective, *permitted))
return -EPERM;
new->cap_effective = *effective;
new->cap_inheritable = *inheritable;
new->cap_permitted = *permitted;
return 0;
}
static inline int cap_inh_is_capped(void)
{
if (cap_capable(current_cred(), current_cred()->user->user_ns,
CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0)
return 0;
return 1;
}
cap_drop()でsetの設定ビットのaのビットをクリアし、 cap_isclear()でその結果が0かどうかで、サブセットのチェックをします。
static inline int cap_issubset(const kernel_cap_t a, const kernel_cap_t set)
{
kernel_cap_t dest;
dest = cap_drop(a, set);
return cap_isclear(dest);
}
static inline kernel_cap_t cap_drop(const kernel_cap_t a,
const kernel_cap_t drop)
{
kernel_cap_t dest;
CAP_BOP_ALL(dest, a, drop, &~);
return dest;
}
#define CAP_BOP_ALL(c, a, b, OP) \
do { \
unsigned __capi; \
for (__capi = 0; __capi < _KERNEL_CAPABILITY_U32S; ++__capi) { \
c.cap[__capi] = a.cap[__capi] OP b.cap[__capi]; \
} \
} while (0)
static inline int cap_isclear(const kernel_cap_t a)
{
unsigned __capi;
for (__capi = 0; __capi < _KERNEL_CAPABILITY_U32S; ++__capi) {
if (a.cap[__capi] != 0)
return 0;
}
return 1;
}
static inline kernel_cap_t cap_combine(const kernel_cap_t a,
const kernel_cap_t b)
{
kernel_cap_t dest;
CAP_BOP_ALL(dest, a, b, |);
return dest;
}
static inline kernel_cap_t cap_intersect(const kernel_cap_t a,
const kernel_cap_t b)
{
kernel_cap_t dest;
CAP_BOP_ALL(dest, a, b, &);
return dest;
}
補足
Linux2.6のcapsetシステムコールで、pid!=0なら、find_task_by_pid()でpidプロセスを、pid<0ならpidのプロセスグループの全プロセスのCATを更新していました。cap_bsetは有していません。cap_bsetはコンパイル時にデフォルトとして設定され更新することはできません。独自のCAPの取り扱いを規定するシステムを構築する目的故の実装かと思います。
asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
{
kernel_cap_t inheritable, permitted, effective;
__u32 version;
task_t *target;
int ret;
pid_t pid;
if (get_user(version, &header->version))
return -EFAULT;
if (version != _LINUX_CAPABILITY_VERSION) {
if (put_user(_LINUX_CAPABILITY_VERSION, &header->version))
return -EFAULT;
return -EINVAL;
}
if (get_user(pid, &header->pid))
return -EFAULT;
if (pid && !capable(CAP_SETPCAP))
return -EPERM;
if (copy_from_user(&effective, &data->effective, sizeof(effective)) ||
copy_from_user(&inheritable, &data->inheritable, sizeof(inheritable)) ||
copy_from_user(&permitted, &data->permitted, sizeof(permitted)))
return -EFAULT;
spin_lock(&task_capability_lock);
read_lock(&tasklist_lock);
if (pid > 0 && pid != current->pid) {
target = find_task_by_pid(pid);
if (!target) {
ret = -ESRCH;
goto out;
}
} else
target = current;
ret = -EPERM;
if (security_capset_check(target, &effective, &inheritable, &permitted))
goto out;
if (!cap_issubset(inheritable, cap_combine(target->cap_inheritable,
current->cap_permitted)))
goto out;
if (!cap_issubset(permitted, cap_combine(target->cap_permitted,
current->cap_permitted)))
goto out;
if (!cap_issubset(effective, permitted))
goto out;
ret = 0;
if (pid < 0) {
if (pid == -1) /* all procs other than current and init */
cap_set_all(&effective, &inheritable, &permitted);
else /* all procs in process group */
cap_set_pg(-pid, &effective, &inheritable, &permitted);
} else {
security_capset_set(target, &effective, &inheritable, &permitted);
}
out:
read_unlock(&tasklist_lock);
spin_unlock(&task_capability_lock);
return ret;
}




