#include <sys/param.h>
#include <sys/acct.h>
#include <sys/caps.h>
#include <sys/systm.h>
#include <sys/sysmsg.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/pioctl.h>
#include <sys/resourcevar.h>
#include <sys/jail.h>
#include <sys/lockf.h>
#include <sys/spinlock.h>
#include <sys/sysctl.h>
#include <sys/spinlock2.h>
__read_mostly static int caps_available = 1;
SYSCTL_INT(_kern, OID_AUTO, caps_available,
CTLFLAG_RW, &caps_available, 0 , "");
static __inline
int
caps_check_cred(struct ucred *cred, int cap)
{
__syscapelm_t elm;
cap &= ~__SYSCAP_XFLAGS;
elm = cred->cr_caps.caps[__SYSCAP_INDEX(cap)];
return ((int)(elm >> __SYSCAP_SHIFT(cap)) & __SYSCAP_ALL);
}
int
sys_syscap_get(struct sysmsg *sysmsg, const struct syscap_get_args *uap)
{
struct ucred *cred;
int cap = uap->cap & ~__SYSCAP_XFLAGS;
int res;
int error;
if (cap < 0)
return EINVAL;
if (cap >= __SYSCAP_COUNT)
return EOPNOTSUPP;
if (uap->bytes && uap->bytes < sizeof(syscap_base_t))
return EINVAL;
error = 0;
if (uap->cap & __SYSCAP_INPARENT) {
struct proc *pp;
pp = pfind(curproc->p_ppid);
if (pp == NULL)
return EINVAL;
lwkt_gettoken_shared(&pp->p_token);
cred = pp->p_ucred;
crhold(cred);
lwkt_reltoken(&pp->p_token);
PRELE(pp);
} else {
cred = curthread->td_ucred;
}
if (uap->data && uap->bytes) {
syscap_base_t base;
base.res = SYSCAP_RESOURCE_EOF;
base.len = sizeof(base);
error = copyout(&base, uap->data, sizeof(base));
}
if (error == 0) {
res = (int)(cred->cr_caps.caps[__SYSCAP_INDEX(cap)] >>
__SYSCAP_SHIFT(cap));
res &= __SYSCAP_BITS_MASK;
sysmsg->sysmsg_result = res;
}
if (uap->cap & __SYSCAP_INPARENT)
crfree(cred);
return error;
}
int
sys_syscap_set(struct sysmsg *sysmsg, const struct syscap_set_args *uap)
{
struct ucred *cred;
struct proc *pp;
int cap = uap->cap & ~__SYSCAP_XFLAGS;
int res;
int error;
int flags = uap->flags;
__syscapelm_t anymask;
if (cap < 0 || cap >= __SYSCAP_COUNT)
return EINVAL;
if (flags & ~__SYSCAP_BITS_MASK)
return EINVAL;
if (uap->data || uap->bytes)
return EINVAL;
error = 0;
if (uap->cap & __SYSCAP_INPARENT) {
pp = pfind(curproc->p_ppid);
if (pp == NULL)
return EINVAL;
if (pp->p_ucred->cr_uid != curproc->p_ucred->cr_uid ||
pp->p_ucred->cr_prison != curproc->p_ucred->cr_prison)
{
PRELE(pp);
return EINVAL;
}
} else {
pp = curproc;
}
lwkt_gettoken(&pp->p_token);
cred = pp->p_ucred;
res = (int)(cred->cr_caps.caps[__SYSCAP_INDEX(cap)] >>
__SYSCAP_SHIFT(cap));
res &= __SYSCAP_BITS_MASK;
if (error == 0) {
if (res != (res | flags)) {
cred = cratom_proc(pp);
anymask = (__syscapelm_t)flags <<
__SYSCAP_SHIFT(SYSCAP_ANY);
atomic_set_64(&cred->cr_caps.caps[0], anymask);
atomic_set_64(&cred->cr_caps.caps[ __SYSCAP_INDEX(cap)],
((__syscapelm_t)uap->flags <<
__SYSCAP_SHIFT(cap)));
}
sysmsg->sysmsg_result = res | uap->flags;
}
lwkt_reltoken(&pp->p_token);
if (uap->cap & __SYSCAP_INPARENT)
PRELE(pp);
return error;
}
void
caps_exec(struct proc *p)
{
struct ucred *cred;
__syscapelm_t elm;
int changed = 0;
int i;
cred = p->p_ucred;
for (i = 0; i < __SYSCAP_NUMELMS; ++i) {
elm = cred->cr_caps.caps[i];
elm = ((elm & __SYSCAP_EXECMASK) >> 1) |
(elm & __SYSCAP_EXECMASK);
if (elm != cred->cr_caps.caps[i])
changed = 1;
}
if (changed) {
cratom_proc(p);
cred = p->p_ucred;
for (i = 0; i < __SYSCAP_NUMELMS; ++i) {
elm = cred->cr_caps.caps[i];
elm = ((elm & __SYSCAP_EXECMASK) >> 1) |
(elm & __SYSCAP_EXECMASK);
cred->cr_caps.caps[i] = elm;
}
}
}
int
caps_get(struct ucred *cred, int cap)
{
int res;
cap &= ~__SYSCAP_XFLAGS;
if (cap < 0 || cap >= __SYSCAP_COUNT)
return 0;
res = (int)(cred->cr_caps.caps[__SYSCAP_INDEX(cap)] >>
__SYSCAP_SHIFT(cap));
res &= __SYSCAP_BITS_MASK;
return res;
}
void
caps_set_locked(struct proc *p, int cap, int flags)
{
struct ucred *cred;
__syscapelm_t elm;
cap &= ~__SYSCAP_XFLAGS;
if (cap < 0 || cap >= __SYSCAP_COUNT)
return;
cred = cratom_proc(p);
elm = (__syscapelm_t)flags << __SYSCAP_SHIFT(SYSCAP_ANY);
atomic_set_64(&cred->cr_caps.caps[0], elm);
elm = (__syscapelm_t)flags << __SYSCAP_SHIFT(cap);
atomic_set_64(&cred->cr_caps.caps[ __SYSCAP_INDEX(cap)], elm);
}
int
caps_priv_check(struct ucred *cred, int cap)
{
int res, gcap;
if (cred == NULL) {
if (cap & __SYSCAP_NULLCRED)
return 0;
return EPERM;
}
if (cred->cr_uid != 0 && (cap & __SYSCAP_NOROOTTEST) == 0) {
if ((cap & __SYSCAP_WHEELOK) == 0 || !groupmember(0, cred))
return EPERM;
}
res = caps_check_cred(cred, cap);
if (cap & __SYSCAP_GROUP_MASK) {
gcap = (cap & __SYSCAP_GROUP_MASK) >> __SYSCAP_GROUP_SHIFT;
res |= caps_check_cred(cred, gcap);
}
if (res & __SYSCAP_SELF)
return EPERM;
return (prison_priv_check(cred, cap));
}
int
caps_priv_check_td(thread_t td, int cap)
{
struct ucred *cred;
if (td->td_lwp == NULL)
return 0;
cred = td->td_ucred;
return caps_priv_check(cred, cap);
}
int
caps_priv_check_self(int cap)
{
return (caps_priv_check_td(curthread, cap));
}