/*************************************************************************
* (C) 2011 AITNET - Sofia/Bulgaria - <office@aitbg.com>
* by Michael Pounov <misho@aitbg.com>
*
* $Author: misho $
* $Id: sux.c,v 1.3.2.1 2013/04/09 08:17:40 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;
if (lvl <= Verbose) {
va_start(lst, fmt);
va_copy(cp, lst);
vfprintf(lf, fmt, lst);
va_end(lst);
fprintf(lf, "\n");
vsyslog(LOG_WARNING, 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 void
initProg()
{
char d[MAXPATHLEN];
proc.proc_uid = getuid();
proc.proc_gid = getgid();
proc.proc_prio = getpriority(PRIO_PROCESS, 0);
getcwd(d, sizeof d);
AIT_SET_STR(&proc.proc_dir, d);
#if 0
lf = fopen(DEFAULT_LOG, "a");
if (!lf)
#endif
lf = stdout;
openlog(PACKAGE_NAME, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
}
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-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;
if (stat(name, &sb) == -1) {
Err("Error:: %s stat #%d - %s", name, errno, strerror(errno));
return -1;
}
if (!(flg & 1))
proc.proc_uid = sb.st_uid;
if (!(flg & 2))
proc.proc_gid = sb.st_gid;
return 0;
}
static inline int
SetClass()
{
login_cap_t *cap;
struct passwd *pass;
pass = getpwuid(proc.proc_uid);
if (!pass) {
Err("Error:: User with this UID %d not found", proc.proc_uid);
endpwent();
return -1;
} else
AIT_SET_STR(&proc.proc_class, pass->pw_class);
cap = login_getclass(AIT_GET_STR(&proc.proc_class));
if (!cap) {
Err("Error:: Cant get login class %s", AIT_GET_STR(&proc.proc_class));
endpwent();
return -1;
}
if (setusercontext(cap, pass, proc.proc_uid, LOGIN_SETALL)) {
Err("Error:: Cant set login class %s", AIT_GET_STR(&proc.proc_class));
login_close(cap);
endpwent();
return -1;
}
login_close(cap);
endpwent();
return 0;
}
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, AIT_GET_STR(&proc.proc_name)) == -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") &&
(str = cfg_getAttribute(&cfg, "global", "directory"))) {
mode = 3;
if (!(flg & 8))
AIT_SET_STR(&proc.proc_dir, str);
if (setUIDGID(flg, AIT_GET_STR(&proc.proc_dir)) == -1)
return -1;
} else {
Err("Error:: Unknown mode %s", str);
return -1;
}
if (!(flg & 4)) {
str = cfg_getAttribute(&cfg, "global", "priority");
if (str)
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 **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;
} else 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 (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 (SetClass()) {
if (args)
free(args);
return -1;
}
/*
if (setgid(proc.proc_gid) == -1) {
Err("Error:: setgid #%d - %s\n", errno, strerror(errno));
if (args)
free(args);
return -1;
}
if (setuid(proc.proc_uid) == -1) {
Err("Error:: setuid #%d - %s\n", errno, strerror(errno));
if (args)
free(args);
return -1;
}
if (setpriority(PRIO_PROCESS, 0, proc.proc_prio) == -1) {
Err("Error:: setpriority #%d - %s\n", errno, strerror(errno));
if (args)
free(args);
return -1;
}
*/
Log(0, "UID:GID=%d:%d Prio=%d Class=%s Name=%s Dir=%s Cmd=%s Script=%s",
proc.proc_uid, proc.proc_gid, 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"));
if (3 <= Verbose) {
char **el = args - 1;
while (*++el)
Log(3, "args: %s", *el);
}
fflush(lf);
execve(cmd, args, environ);
if (args)
free(args);
Err("Error:: in exec() #%d - %s", errno, strerror(errno));
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, "hvC: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 |= 8;
break;
case 'p':
proc.proc_prio = strtol(optarg, NULL, 0);
flg |= 4;
break;
case 'g':
setgrent();
grp = getgrnam(optarg);
if (grp) {
proc.proc_gid = grp->gr_gid;
flg |= 2;
} else
Err("Error:: Group not found!");
endgrent();
break;
case 'u':
setpwent();
pass = getpwnam(optarg);
if (pass) {
proc.proc_uid = pass->pw_uid;
flg |= 1;
} 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 'v':
Verbose++;
break;
case 'h':
default:
Usage();
if (fileno(lf) > 2)
fclose(lf);
return 1;
}
argc -= optind;
argv += optind;
if (2 <= Verbose) {
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();
if (fileno(lf) > 2)
fclose(lf);
return 1;
} else
AIT_SET_STR(&proc.proc_name, str);
} else
AIT_SET_STR(&proc.proc_name, *argv);
Log(2, "Try to load config %s", szCfg);
if (cfgLoadConfig(szCfg, &cfg)) {
Err("Error:: can`t load config #%d - %s\n", cfg_GetErrno(), cfg_GetError());
if (fileno(lf) > 2)
fclose(lf);
return 2;
} else
if (LoadCfgData(flg) == -1) {
cfgUnloadConfig(&cfg);
if (fileno(lf) > 2)
fclose(lf);
closelog();
return 3;
}
cfgUnloadConfig(&cfg);
if (Run(argv) == -1) {
if (fileno(lf) > 2)
fclose(lf);
closelog();
return 4;
}
if (fileno(lf) > 2)
fclose(lf);
closelog();
return 0;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>