#include <sys/task.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <project.h>
#include <rctl.h>
#include <secdb.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <nss_dbdefs.h>
#include <pwd.h>
#include <pool.h>
#include <libproc.h>
#include <priv.h>
#include <priv_utils.h>
#include <zone.h>
#include <sys/pool.h>
#include <sys/pool_impl.h>
#include <sys/rctl_impl.h>
static void
xstrtolower(char *s)
{
for (; *s != '\0'; s++)
*s = tolower(*s);
}
static void
remove_spaces(char *s)
{
char *current;
char *next;
current = next = s;
while (*next != '\0') {
while (isspace(*next))
next++;
*current++ = *next++;
}
*current = '\0';
}
int
build_rctlblk(rctlblk_t *blk, int comp_num, char *component)
{
char *signam;
int sig = 0;
uint_t act = rctlblk_get_local_action(blk, &sig);
if (comp_num == 0) {
xstrtolower(component);
if (strcmp("basic", component) == 0) {
rctlblk_set_privilege(blk, RCPRIV_BASIC);
return (0);
}
if (strcmp("priv", component) == 0 ||
strcmp("privileged", component) == 0) {
rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED);
return (0);
}
return (-1);
}
if (comp_num == 1) {
unsigned long long val;
char *t;
if (strchr(component, '-') != NULL)
return (-1);
errno = 0;
val = strtoull(component, &t, 10);
if (errno != 0 || t == component || *t != '\0')
return (-1);
rctlblk_set_value(blk, (rctl_qty_t)val);
return (0);
}
if (comp_num >= 2) {
if (strcmp("none", component) == 0) {
rctlblk_set_local_action(blk, 0, 0);
return (0);
}
if (strcmp("deny", component) == 0) {
act |= RCTL_LOCAL_DENY;
rctlblk_set_local_action(blk, act, sig);
return (0);
}
if ((signam = strchr(component, '=')) == NULL)
return (-1);
*signam++ = '\0';
if (strcmp("sig", component) == 0 ||
strcmp("signal", component) == 0) {
if (strncmp("SIG", signam, 3) == 0)
signam += 3;
if (str2sig(signam, &sig) == -1)
return (-1);
act |= RCTL_LOCAL_SIGNAL;
rctlblk_set_local_action(blk, act, sig);
return (0);
}
}
return (-1);
}
#define INPAREN 0x1
#define SETFAILED (-1)
#define COMPLETE 1
#define NESTING 2
#define UNCLOSED 3
#define CLOSEBEFOREOPEN 4
#define BADSPEC 5
static void
reinit_blk(rctlblk_t *blk, int local_action)
{
rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED);
rctlblk_set_value(blk, 0);
rctlblk_set_local_flags(blk, 0);
rctlblk_set_local_action(blk, local_action, 0);
}
static int
rctl_set(char *ctl_name, char *val, struct ps_prochandle *Pr, int flags)
{
int error = 0;
uint_t component = 0;
int valuecount = 0;
uint_t state = 0;
char *component_head;
rctlblk_t *blk;
rctlblk_t *ablk;
int project_entity = 0;
int count = 0;
char *tmp;
int local_act;
rctlblk_t *rnext;
int teardown_basic = 0;
int teardown_priv = 0;
if (strncmp(ctl_name, "zone.", strlen("zone.")) == 0) {
return (SETFAILED);
}
remove_spaces(val);
if (strncmp(ctl_name, "project.", strlen("project.")) == 0) {
project_entity = 1;
} else if ((strncmp(ctl_name, "process.", strlen("process.")) != 0) &&
(strncmp(ctl_name, "task.", strlen("task.")) != 0)) {
return (SETFAILED);
}
for (tmp = val; *tmp != '\0'; tmp++) {
if (*tmp == '(')
count++;
}
if ((count == 0) || ((ablk =
(rctlblk_t *)malloc(rctlblk_size() * count)) == NULL)) {
return (SETFAILED);
}
blk = ablk;
(void) pr_getrctl(Pr, ctl_name, NULL, blk, RCTL_FIRST);
rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED);
rctlblk_set_value(blk, 0);
rctlblk_set_local_flags(blk, 0);
if (rctlblk_get_global_flags(blk) & RCTL_GLOBAL_DENY_ALWAYS)
local_act = RCTL_LOCAL_DENY;
else
local_act = RCTL_LOCAL_NOACTION;
rctlblk_set_local_action(blk, local_act, 0);
for (; ; val++) {
switch (*val) {
case '(':
if (state & INPAREN) {
error = NESTING;
break;
}
state |= INPAREN;
component_head = (char *)val + 1;
break;
case ')':
if (state & INPAREN) {
*val = '\0';
if (component < 2) {
error = BADSPEC;
break;
}
if (build_rctlblk(blk, component,
component_head) == -1) {
error = BADSPEC;
break;
}
state &= ~INPAREN;
component = 0;
valuecount++;
if (project_entity &&
(rctlblk_get_privilege(blk) ==
RCPRIV_BASIC)) {
error = SETFAILED;
} else {
if (rctlblk_get_privilege(blk)
== RCPRIV_BASIC)
teardown_basic = 1;
if (rctlblk_get_privilege(blk)
== RCPRIV_PRIVILEGED)
teardown_priv = 1;
if (valuecount > count) {
free(ablk);
return (SETFAILED);
}
if (valuecount != count) {
blk = RCTLBLK_INC(ablk,
valuecount);
reinit_blk(blk,
local_act);
}
}
} else {
error = CLOSEBEFOREOPEN;
}
break;
case ',':
if (state & INPAREN) {
*val = '\0';
if (build_rctlblk(blk, component,
component_head) == -1)
error = BADSPEC;
component++;
component_head = (char *)val + 1;
}
break;
case '\0':
if (valuecount == 0)
error = BADSPEC;
else if (state & INPAREN)
error = UNCLOSED;
else
error = COMPLETE;
break;
default:
if (!(state & INPAREN))
error = BADSPEC;
break;
}
if (error)
break;
}
if (valuecount == 0)
error = BADSPEC;
if (error != COMPLETE) {
free(ablk);
return (error);
}
if (!project_entity) {
if ((rnext = (rctlblk_t *)malloc(rctlblk_size())) == NULL) {
free(ablk);
return (SETFAILED);
}
restart:
if (pr_getrctl(Pr, ctl_name, NULL, rnext, RCTL_FIRST) == 0) {
while (1) {
if ((rctlblk_get_privilege(rnext) ==
RCPRIV_PRIVILEGED) &&
(teardown_priv == 1)) {
(void) pr_setrctl(Pr, ctl_name, NULL,
rnext, RCTL_DELETE);
goto restart;
}
if ((rctlblk_get_privilege(rnext) ==
RCPRIV_BASIC) && (teardown_basic == 1)) {
(void) pr_setrctl(Pr, ctl_name, NULL,
rnext, RCTL_DELETE);
goto restart;
}
if (pr_getrctl(Pr, ctl_name, rnext, rnext,
RCTL_NEXT) == -1)
break;
}
}
free(rnext);
}
blk = ablk;
if (project_entity) {
if (pr_setprojrctl(Pr, ctl_name, blk, count, flags) == -1)
error = SETFAILED;
} else {
valuecount = 0;
while (valuecount < count) {
if (pr_setrctl(Pr, ctl_name,
NULL, blk, RCTL_INSERT) == -1) {
error = SETFAILED;
break;
}
valuecount++;
blk = RCTLBLK_INC(ablk, valuecount);
}
}
free(ablk);
if (error != COMPLETE)
return (error);
return (0);
}
static int
rctlwalkfunc(const char *name, void *data)
{
if (strcmp(name, (char *)data) == 0)
return (-1);
else
return (0);
}
static int
pools_enabled(void)
{
pool_status_t status;
int fd;
if (getzoneid() != GLOBAL_ZONEID)
return (0);
if ((fd = open("/dev/pool", O_RDONLY)) < 0)
return (0);
if (ioctl(fd, POOL_STATUSQ, &status) < 0) {
(void) close(fd);
return (0);
}
(void) close(fd);
return (status.ps_io_state);
}
static int
bind_to_pool(const char *pool_name, pid_t pid, int force)
{
pool_value_t *pvals[] = { NULL, NULL };
pool_t **pools;
uint_t nelem;
uchar_t bval;
pool_conf_t *conf;
const char *nm;
int retval;
if ((conf = pool_conf_alloc()) == NULL)
return (-1);
if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY) < 0) {
pool_conf_free(conf);
return (0);
}
if (pool_name != NULL && pool_get_pool(conf, pool_name) != NULL) {
(void) pool_conf_close(conf);
pool_conf_free(conf);
if (pool_set_binding(pool_name, P_PID, pid) != PO_SUCCESS) {
if (pool_error() != POE_SYSTEM)
errno = EINVAL;
return (-1);
}
return (0);
}
if ((pvals[0] = pool_value_alloc()) == NULL) {
(void) pool_conf_close(conf);
pool_conf_free(conf);
return (-1);
}
if (!force && pool_get_property(conf, pool_conf_to_elem(conf),
"system.bind-default", pvals[0]) != POC_BOOL ||
pool_value_get_bool(pvals[0], &bval) != PO_SUCCESS ||
bval == PO_FALSE) {
pool_value_free(pvals[0]);
(void) pool_conf_close(conf);
pool_conf_free(conf);
errno = pool_name == NULL ? EACCES : ESRCH;
return (-1);
}
(void) pool_value_set_name(pvals[0], "pool.default");
pool_value_set_bool(pvals[0], PO_TRUE);
if ((pools = pool_query_pools(conf, &nelem, pvals)) == NULL) {
pool_value_free(pvals[0]);
(void) pool_conf_close(conf);
pool_conf_free(conf);
errno = pool_name == NULL ? EACCES : ESRCH;
return (-1);
}
if (nelem != 1 ||
pool_get_property(conf, pool_to_elem(conf, pools[0]), "pool.name",
pvals[0]) != POC_STRING) {
free(pools);
pool_value_free(pvals[0]);
(void) pool_conf_close(conf);
pool_conf_free(conf);
return (0);
}
free(pools);
(void) pool_conf_close(conf);
pool_conf_free(conf);
(void) pool_value_get_string(pvals[0], &nm);
if (pool_set_binding(nm, P_PID, pid) != PO_SUCCESS) {
if (pool_error() != POE_SYSTEM)
errno = EINVAL;
retval = -1;
} else {
retval = 0;
}
pool_value_free(pvals[0]);
return (retval);
}
projid_t
setproject_proc(const char *project_name, const char *user_name, int flags,
pid_t pid, struct ps_prochandle *Pr, struct project *proj)
{
char pwdbuf[NSS_BUFLEN_PASSWD];
char prbuf[PROJECT_BUFSZ];
projid_t projid;
struct passwd pwd;
int i;
int unknown = 0;
int ret = 0;
kva_t *kv_array;
struct project local_proj;
const char *pool_name = NULL;
if (project_name != NULL) {
if (strcmp(project_name, "") == 0 ||
user_name == NULL) {
errno = EINVAL;
return (SETPROJ_ERR_TASK);
}
if (proj == NULL) {
if ((proj = getprojbyname(project_name, &local_proj,
prbuf, PROJECT_BUFSZ)) == NULL) {
errno = ESRCH;
return (SETPROJ_ERR_TASK);
}
if (getpwnam_r(user_name, &pwd,
pwdbuf, NSS_BUFLEN_PASSWD) == NULL) {
errno = ESRCH;
return (SETPROJ_ERR_TASK);
}
if (pwd.pw_uid != (uid_t)0 &&
!inproj(user_name, project_name, prbuf,
PROJECT_BUFSZ)) {
errno = ESRCH;
return (SETPROJ_ERR_TASK);
}
}
projid = proj->pj_projid;
} else {
projid = getprojid();
}
if ((kv_array = _str2kva(proj->pj_attr, KV_ASSIGN,
KV_DELIMITER)) != NULL) {
for (i = 0; i < kv_array->length; i++) {
if (strcmp(kv_array->data[i].key,
"project.pool") == 0) {
pool_name = kv_array->data[i].value;
}
if (strcmp(kv_array->data[i].key, "task.final") == 0) {
flags |= TASK_FINAL;
}
}
}
if (pools_enabled() == 1) {
char *old_pool_name;
old_pool_name = pool_get_binding(pid);
if (bind_to_pool(pool_name, pid, 0) != 0) {
if (old_pool_name)
free(old_pool_name);
_kva_free(kv_array);
return (SETPROJ_ERR_POOL);
}
if (pr_settaskid(Pr, projid, flags & TASK_MASK) == -1) {
int saved_errno = errno;
(void) bind_to_pool(old_pool_name, pid, 1);
if (old_pool_name)
free(old_pool_name);
_kva_free(kv_array);
errno = saved_errno;
return (SETPROJ_ERR_TASK);
}
if (old_pool_name)
free(old_pool_name);
} else {
if (pr_settaskid(Pr, projid, flags & TASK_MASK) == -1) {
_kva_free(kv_array);
return (SETPROJ_ERR_TASK);
}
}
if (project_name == NULL) {
_kva_free(kv_array);
return (projid);
}
if (kv_array == NULL)
return (0);
for (i = 0; i < kv_array->length; i++) {
errno = 0;
if (rctl_walk(rctlwalkfunc, (void *)kv_array->data[i].key)
== 0)
continue;
if (errno) {
_kva_free(kv_array);
return (SETPROJ_ERR_TASK);
}
ret = rctl_set(kv_array->data[i].key,
kv_array->data[i].value, Pr, flags & TASK_PROJ_MASK);
if (ret && unknown == 0) {
unknown = i + 1;
}
if (ret && ret != SETFAILED) {
break;
}
}
_kva_free(kv_array);
return (unknown);
}
projid_t
setproject(const char *project_name, const char *user_name, int flags)
{
return (setproject_proc(project_name, user_name, flags, P_MYID, NULL,
NULL));
}
priv_set_t *
setproject_initpriv(void)
{
static priv_t taskpriv = PRIV_PROC_TASKID;
static priv_t rctlpriv = PRIV_SYS_RESOURCE;
static priv_t poolpriv = PRIV_SYS_RES_CONFIG;
static priv_t schedpriv = PRIV_PROC_PRIOCNTL;
int res;
priv_set_t *nset;
if (getzoneid() == GLOBAL_ZONEID) {
res = __init_suid_priv(0, taskpriv, rctlpriv, poolpriv,
schedpriv, (char *)NULL);
} else {
res = __init_suid_priv(0, taskpriv, rctlpriv, (char *)NULL);
}
if (res != 0)
return (NULL);
nset = priv_allocset();
if (nset != NULL) {
priv_emptyset(nset);
(void) priv_addset(nset, taskpriv);
(void) priv_addset(nset, rctlpriv);
if (getzoneid() == GLOBAL_ZONEID) {
(void) priv_addset(nset, poolpriv);
(void) priv_addset(nset, schedpriv);
}
}
return (nset);
}