diff options
Diffstat (limited to 'src/pmdas/linux_proc/contexts.c')
-rw-r--r-- | src/pmdas/linux_proc/contexts.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/src/pmdas/linux_proc/contexts.c b/src/pmdas/linux_proc/contexts.c new file mode 100644 index 0000000..f213c14 --- /dev/null +++ b/src/pmdas/linux_proc/contexts.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "contexts.h" + +static proc_perctx_t *ctxtab; +static int num_ctx; +static uid_t baseuid; +static gid_t basegid; + +static void +proc_ctx_clear(int ctx) +{ + ctxtab[ctx].state = CTX_INACTIVE; + ctxtab[ctx].uid = -1; + ctxtab[ctx].gid = -1; + ctxtab[ctx].threads = 1; + ctxtab[ctx].cgroups = NULL; +} + +void +proc_ctx_end(int ctx) +{ + if (ctx < 0 || ctx >= num_ctx || ctxtab[ctx].state == CTX_INACTIVE) + return; + if (ctxtab[ctx].state & CTX_CGROUPS) + free((void *)ctxtab[ctx].cgroups); + proc_ctx_clear(ctx); +} + +static void +proc_ctx_growtab(int ctx) +{ + size_t need; + + if (ctx < num_ctx) + return; + + need = (ctx + 1) * sizeof(ctxtab[0]); + ctxtab = (proc_perctx_t *)realloc(ctxtab, need); + if (ctxtab == NULL) + __pmNoMem("proc ctx table", need, PM_FATAL_ERR); + while (num_ctx <= ctx) + proc_ctx_clear(num_ctx++); +} + +static void +proc_ctx_set_userid(int ctx, const char *value) +{ + proc_ctx_growtab(ctx); + ctxtab[ctx].uid = atoi(value); + ctxtab[ctx].state |= (CTX_ACTIVE | CTX_USERID); +} + +static void +proc_ctx_set_groupid(int ctx, const char *value) +{ + proc_ctx_growtab(ctx); + ctxtab[ctx].gid = atoi(value); + ctxtab[ctx].state |= (CTX_ACTIVE | CTX_GROUPID); +} + +int +proc_ctx_attrs(int ctx, int attr, const char *value, int length, pmdaExt *pmda) +{ + if (pmDebug & DBG_TRACE_AUTH) { + char buffer[256]; + + if (!__pmAttrStr_r(attr, value, buffer, sizeof(buffer))) { + __pmNotifyErr(LOG_ERR, "Bad Attribute: ctx=%d, attr=%d\n", ctx, attr); + } else { + buffer[sizeof(buffer)-1] = '\0'; + __pmNotifyErr(LOG_INFO, "Attribute: ctx=%d %s", ctx, buffer); + } + } + + switch (attr) { + case PCP_ATTR_USERID: + proc_ctx_set_userid(ctx, value); + break; + case PCP_ATTR_GROUPID: + proc_ctx_set_groupid(ctx, value); + break; + default: + break; + } + return 0; +} + +void +proc_ctx_init(void) +{ + baseuid = getuid(); + basegid = getgid(); +} + +int +proc_ctx_access(int ctx) +{ + proc_perctx_t *pp; + int accessible = 0; + + if (ctx < 0 || ctx >= num_ctx) + return accessible; + pp = &ctxtab[ctx]; + if (pp->state == CTX_INACTIVE) + return accessible; + + if (pp->state & CTX_GROUPID) { + accessible++; + if (basegid != pp->gid) { + if (setegid(pp->gid) < 0) { + __pmNotifyErr(LOG_ERR, "setegid(%d) access failed: %s\n", + pp->gid, osstrerror()); + accessible--; + } + } + } + if (pp->state & CTX_USERID) { + accessible++; + if (baseuid != pp->uid) { + if (seteuid(pp->uid) < 0) { + __pmNotifyErr(LOG_ERR, "seteuid(%d) access failed: %s\n", + pp->uid, osstrerror()); + accessible--; + } + } + } + return (accessible > 1); +} + +int +proc_ctx_revert(int ctx) +{ + proc_perctx_t *pp; + + if (ctx < 0 || ctx >= num_ctx) + return 0; + pp = &ctxtab[ctx]; + if (pp->state == CTX_INACTIVE) + return 0; + + if ((pp->state & CTX_USERID) && baseuid != pp->uid) { + if (seteuid(baseuid) < 0) + __pmNotifyErr(LOG_ERR, "seteuid(%d) revert failed: %s\n", + baseuid, osstrerror()); + } + if ((pp->state & CTX_GROUPID) && basegid != pp->gid) { + if (setegid(basegid) < 0) + __pmNotifyErr(LOG_ERR, "setegid(%d) revert failed: %s\n", + basegid, osstrerror()); + } + return 0; +} + +unsigned int +proc_ctx_threads(int ctx, unsigned int threads) +{ + proc_perctx_t *pp; + + if (ctx < 0 || ctx >= num_ctx) + return threads; /* fallback to default */ + pp = &ctxtab[ctx]; + if (pp->state == CTX_INACTIVE) + return threads; /* fallback to default */ + + if (pp->state & CTX_THREADS) + return pp->threads; /* client setting */ + + return threads; /* fallback to default */ +} + +int +proc_ctx_set_threads(int ctx, unsigned int threads) +{ + proc_perctx_t *pp; + + if (ctx < 0 || ctx >= num_ctx) + return PM_ERR_NOCONTEXT; + pp = &ctxtab[ctx]; + if (pp->state == CTX_INACTIVE) + return PM_ERR_NOCONTEXT; + if (threads > 1) + return PM_ERR_CONV; + + pp->state |= CTX_THREADS; + pp->threads = threads; + return 0; +} + +const char * +proc_ctx_cgroups(int ctx, const char *cgroups) +{ + proc_perctx_t *pp; + + if (ctx < 0 || ctx >= num_ctx) + return cgroups; /* fallback to default */ + pp = &ctxtab[ctx]; + if (pp->state == CTX_INACTIVE) + return cgroups; /* fallback to default */ + + if (pp->state & CTX_CGROUPS) + return pp->cgroups; /* client setting */ + + return cgroups; /* fallback to default */ +} + +int +proc_ctx_set_cgroups(int ctx, const char *cgroups) +{ + proc_perctx_t *pp; + + if (ctx < 0 || ctx >= num_ctx) + return PM_ERR_NOCONTEXT; + pp = &ctxtab[ctx]; + if (pp->state == CTX_INACTIVE) + return PM_ERR_NOCONTEXT; + if (cgroups == NULL || cgroups[0] == '\0') + return PM_ERR_CONV; + + pp->state |= CTX_CGROUPS; + pp->cgroups = cgroups; + return 0; +} |