/*************************************************************************
* (C) 2011 AITNET - Sofia/Bulgaria - <office@aitbg.com>
* by Michael Pounov <misho@aitbg.com>
*
* $Author: misho $
* $Id: sux.c,v 1.6 2015/06/17 14:14:17 misho Exp $
*
*************************************************************************/
#include "global.h"
cfg_root_t cfg;
int Verbose;
struct tagProc proc;
FILE *lf;
static inline void
Log(int lvl, const char *fmt, ...)
{
va_list lst, cp;
EVERBS(lvl) {
va_start(lst, fmt);
va_copy(cp, lst);
vfprintf(lf, fmt, lst);
va_end(lst);
fprintf(lf, "\n");
vsyslog(LOG_INFO, fmt, cp);
va_end(cp);
}
}
static inline void
Err(const char *fmt, ...)
{
va_list lst, cp;
va_start(lst, fmt);
va_copy(cp, lst);
vfprintf(lf, fmt, lst);
va_end(lst);
fprintf(lf, "\n");
vsyslog(LOG_ERR, fmt, cp);
va_end(cp);
}
static inline void
DumpProc(const char *txt)
{
Log(1, "%s:: uid:gid=%d:%d UID:GID=%d:%d Prio=%d Class=%s Name=%s Dir=%s Cmd=%s "
"Script=%s From=%s:%s Get=%s", txt ? txt : __func__,
geteuid(), getegid(), AIT_GET_I16(&proc.proc_uid),
AIT_GET_I16(&proc.proc_gid), AIT_GET_I32(&proc.proc_prio),
AIT_GET_STR(&proc.proc_class), AIT_GET_STR(&proc.proc_name),
AIT_GET_STR(&proc.proc_dir), AIT_GET_STR(&proc.proc_cmd),
getenv("PATH_TRANSLATED"), getenv("REMOTE_ADDR"),
getenv("REMOTE_PORT"), getenv("REQUEST_URI"));
}
static int
initProg()
{
AIT_SET_I16(&proc.proc_uid, getuid());
AIT_SET_I16(&proc.proc_gid, getgid());
AIT_SET_I32(&proc.proc_prio, getpriority(PRIO_PROCESS, 0));
AIT_INIT_VAL2(&proc.proc_class, string);
AIT_INIT_VAL2(&proc.proc_dir, string);
AIT_INIT_VAL2(&proc.proc_name, string);
AIT_INIT_VAL2(&proc.proc_cmd, string);
#if 0
lf = fopen(DEFAULT_LOG, "a");
if (!lf)
#endif
lf = stdout;
openlog(PACKAGE_NAME, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
return 0;
}
static void
endProg()
{
AIT_FREE_VAL(&proc.proc_uid);
AIT_FREE_VAL(&proc.proc_gid);
AIT_FREE_VAL(&proc.proc_prio);
AIT_FREE_VAL(&proc.proc_class);
AIT_FREE_VAL(&proc.proc_dir);
AIT_FREE_VAL(&proc.proc_name);
AIT_FREE_VAL(&proc.proc_cmd);
}
static void
Usage()
{
printf( " -= suX =- suExecutor designed for web based applicaions\n"
"(C)`11 AITNET ltd - Sofia/Bulgaria - <office@aitnet.org>\n\n"
" Syntax: %s [options] <program|-> [arguments]\n"
"\t-u <user>\t\t\tUser for suID\n"
"\t-g <group>\t\t\tGroup for suID\n"
"\t-p <priority (-20..20)>\t\tExecute with priority\n"
"\t-d <directory>\t\t\tDirectory for suID\n"
"\t-C <directory>\t\t\tChroot to directory\n"
"\t-c <cfgfile>\t\t\tConfig file\n"
"\t-l <logfile>\t\t\tLog file path (default:/var/log/suX.log)\n"
"\t-o\t\t\t\tForce set UID,GID and Priority for program\n"
"\t-v\t\t\t\tVerbose, (more -v, more verbosity)\n"
"\t-h\t\t\t\tThis help screen!\n\n", PACKAGE_NAME);
}
static inline int
setUIDGID(char flg, const char *name)
{
struct stat sb;
struct passwd *pass;
short uid, gid;
if (name) {
if (stat(name, &sb) == -1) {
ESYSERR(0);
return -1;
}
uid = sb.st_uid;
gid = sb.st_gid;
} else {
pass = getpwnam(getenv("SUX_USER") ? getenv("SUX_USER") : DEFAULT_SUX_USER);
if (!pass) {
Err("Error:: User %s not found", getenv("SUX_USER"));
endpwent();
return -1;
}
uid = pass->pw_uid;
gid = pass->pw_gid;
endpwent();
}
if (!(flg & SUX_GET_UID))
AIT_SET_I16(&proc.proc_uid, uid);
if (!(flg & SUX_GET_GID))
AIT_SET_I16(&proc.proc_gid, gid);
return 0;
}
static inline int
setClassDir()
{
struct passwd *pass;
int ret = 0;
pass = getpwuid(AIT_GET_I16(&proc.proc_uid));
if (!pass) {
Err("Error:: User with this UID %d not found", AIT_GET_I16(&proc.proc_uid));
ret = -1;
} else {
AIT_SET_STR(&proc.proc_class, pass->pw_class);
AIT_SET_STR(&proc.proc_dir, pass->pw_dir);
if (setusercontext(NULL, pass, AIT_GET_I16(&proc.proc_uid),
LOGIN_SETLOGIN | LOGIN_SETGROUP | LOGIN_SETUSER |
LOGIN_SETPRIORITY | LOGIN_SETRESOURCES)) {
Err("Error:: Can't set login class %s", AIT_GET_STR(&proc.proc_class));
ret = -1;
}
}
endpwent();
return ret;
}
static int
LoadCfgData(char flg)
{
const char *str;
char mode = 0;
str = cfg_getAttribute(&cfg, "global", "mode");
if (!str) {
Err("Error:: Unknown mode ...");
return -1;
}
if (!strcasecmp(str, "SCRIPT")) {
mode = 1;
if (setUIDGID(flg, (getenv("SUX_USER") ? NULL : getenv("PATH_TRANSLATED"))) == -1)
return -1;
} else if (!strcasecmp(str, "FILE")) {
mode = 2;
if (setUIDGID(flg, AIT_GET_STR(&proc.proc_name)) == -1)
return -1;
} else if (!strcasecmp(str, "DIR")) {
mode = 3;
str = AIT_GET_STR(&proc.proc_dir) ? AIT_GET_STR(&proc.proc_dir) : ".";
if (setUIDGID(flg, str) == -1)
return -1;
} else {
Err("Error:: Unknown mode %s", str);
return -1;
}
if (!(flg & SUX_GET_PRIO)) {
str = cfg_getAttribute(&cfg, "global", "priority");
if (str)
AIT_SET_I32(&proc.proc_prio, strtol(str, NULL, 10));
}
/* find associate extension */
str = strrchr(AIT_GET_STR(&proc.proc_name), '.');
if (str) {
str++;
str = (*str) ? str : "default";
switch (cfg_loadAttribute(&cfg, "associate", str, &proc.proc_cmd, NULL)) {
case -1:
ELIBERR(cfg);
return -1;
case 0:
cfg_loadAttribute(&cfg, "associate", "default",
&proc.proc_cmd, DEFAULT_CMD);
}
} else
AIT_SET_STR(&proc.proc_cmd, DEFAULT_CMD);
return 0;
}
static int
Run(char **argv, char flg)
{
char **args, *cmd;
array_t *acmd, *aarg;
int n;
if (!argv)
return -1;
n = array_Args(AIT_GET_STR(&proc.proc_cmd), 0, " \t", &acmd);
if (n < 1)
return -1;
if (!(aarg = array_From((const char***) &argv, 0))) {
array_Destroy(&acmd);
return -1;
}
/* '!' exclude associated wrapper aka direct args execution */
if (*array(acmd, 0, char*) == '!') {
if (array_Grow(acmd, 0, 0)) {
array_Destroy(&aarg);
array_Destroy(&acmd);
return -1;
}
cmd = array(aarg, 0, char*);
} else
cmd = array(acmd, 0, char*);
if (!(flg & SUX_GET_STDIN) && array_Concat(acmd, aarg) == -1) {
array_Destroy(&aarg);
array_Destroy(&acmd);
return -1;
}
array_Destroy(&aarg);
if (!(args = array_To(acmd))) {
array_Destroy(&acmd);
return -1;
}
array_Destroy(&acmd);
if (setClassDir()) {
if (args)
e_free(args);
return -1;
}
if (flg & SUX_GET_FORCE) {
if (setegid(AIT_GET_I16(&proc.proc_gid)) == -1)
goto err;
if (seteuid(AIT_GET_I16(&proc.proc_uid)) == -1)
goto err;
if (setpriority(PRIO_PROCESS, 0, AIT_GET_I32(&proc.proc_prio)) == -1)
goto err;
}
EVERBS(3) {
char **el = args - 1;
while (*++el)
Log(3, "args: %s", *el);
}
DumpProc(__func__);
fflush(lf);
execve(cmd, args, environ);
err:
ESYSERR(0);
if (args)
e_free(args);
return -1;
}
int
main(int argc, char **argv)
{
char ch, *str, *wrk, szCfg[MAXPATHLEN], **pp, flg = 0;
struct passwd *pass;
struct group *grp;
FILE *f;
initProg();
strlcpy(szCfg, DEFAULT_CONFIG, sizeof szCfg);
while ((ch = getopt(argc, argv, "hvoC:c:u:g:p:d:l:")) != -1)
switch (ch) {
case 'l':
f = fopen(optarg, "a");
if (!f) {
Err("Error:: logfile #%d - %s", errno, strerror(errno));
return 1;
} else
if (fileno(lf) > 2)
fclose(lf);
lf = f;
break;
case 'd':
AIT_SET_STR(&proc.proc_dir, optarg);
flg |= SUX_GET_DIR;
break;
case 'p':
AIT_SET_I32(&proc.proc_prio, strtol(optarg, NULL, 0));
flg |= SUX_GET_PRIO;
break;
case 'g':
setgrent();
grp = getgrnam(optarg);
if (grp) {
AIT_SET_I16(&proc.proc_gid, grp->gr_gid);
flg |= SUX_GET_GID;
} else
Err("Error:: Group not found!");
endgrent();
break;
case 'u':
setpwent();
pass = getpwnam(optarg);
if (pass) {
AIT_SET_I16(&proc.proc_uid, pass->pw_uid);
flg |= SUX_GET_UID;
} else
Err("Error:: User not found!");
endpwent();
break;
case 'c':
strlcpy(szCfg, optarg, sizeof szCfg);
break;
case 'C':
if (chroot(optarg) == -1)
Err("Error:: chroot to dir");
if ((str = getenv("PATH_TRANSLATED")))
if ((wrk = strstr(str, optarg)))
setenv("PATH_TRANSLATED", str + strlen(optarg), 42);
break;
case 'o':
flg |= SUX_GET_FORCE;
break;
case 'v':
e_incVerbose;
break;
case 'h':
default:
Usage();
endProg();
if (fileno(lf) > 2)
fclose(lf);
return 1;
}
argc -= optind;
argv += optind;
EVERBS(2) {
for (pp = argv; *pp; pp++)
Log(2, "Args=%s\n", *pp);
for (pp = environ; *pp; pp++)
Log(2, "Envs=%s\n", *pp);
}
if (!argc) {
if (!(str = getenv("PATH_TRANSLATED"))) {
Usage();
endProg();
if (fileno(lf) > 2)
fclose(lf);
return 1;
} else
AIT_SET_STR(&proc.proc_name, str);
} else if (strcmp(*argv, "-"))
AIT_SET_STR(&proc.proc_name, *argv);
else {
flg |= SUX_GET_STDIN;
AIT_SET_STR(&proc.proc_name, "-.stdin"); /* hack for associate to stdin */
}
Log(2, "Try to load config %s", szCfg);
if (cfgLoadConfig(szCfg, &cfg)) {
ELIBERR(cfg);
endProg();
if (fileno(lf) > 2)
fclose(lf);
return 2;
} else
if (LoadCfgData(flg) == -1) {
cfgUnloadConfig(&cfg);
endProg();
if (fileno(lf) > 2)
fclose(lf);
closelog();
return 3;
}
cfgUnloadConfig(&cfg);
if (Run(argv, flg) == -1) {
endProg();
if (fileno(lf) > 2)
fclose(lf);
closelog();
return 4;
}
endProg();
if (fileno(lf) > 2)
fclose(lf);
closelog();
return 0;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>