/*************************************************************************
* (C) 2011 AITNET - Sofia/Bulgaria - <office@aitbg.com>
* by Michael Pounov <misho@aitbg.com>
*
* $Author: misho $
* $Id: sux.c,v 1.1.1.1.2.2 2011/05/20 16:26:53 misho Exp $
*
*************************************************************************/
#include "global.h"
sl_config cfg;
int Verbose;
struct tagProc proc;
FILE *lf;
static inline void
Log(int lvl, const char *fmt, ...)
{
va_list lst;
if (lvl <= Verbose) {
va_start(lst, fmt);
vfprintf(lf, fmt, lst);
vsyslog(LOG_WARNING, fmt, lst);
va_end(lst);
}
}
static inline void
Err(const char *fmt, ...)
{
va_list lst;
va_start(lst, fmt);
vfprintf(lf, fmt, lst);
vsyslog(LOG_ERR, fmt, lst);
va_end(lst);
}
static void
initProg()
{
proc.proc_uid = getuid();
proc.proc_gid = getgid();
proc.proc_prio = getpriority(PRIO_PROCESS, 0);
getcwd(proc.proc_dir, sizeof proc.proc_dir);
lf = stdout;
openlog(PACKAGE_NAME, LOG_CONS | LOG_PID | LOG_NDELAY | LOG_PERROR, 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-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
strlcpy(proc.proc_class, pass->pw_class, sizeof proc.proc_class);
cap = login_getclass(proc.proc_class);
if (!cap) {
Err("Error:: Cant get login class %s", proc.proc_class);
endpwent();
return -1;
}
if (setusercontext(cap, pass, proc.proc_uid, LOGIN_SETALL)) {
Err("Error:: Cant set login class %s", proc.proc_class);
login_close(cap);
endpwent();
return -1;
}
login_close(cap);
endpwent();
return 0;
}
static int
LoadCfgData(char flg)
{
char *str, mode = 0;
str = (char*) cfg_GetAttribute(&cfg, CFG("global"), CFG("mode"));
if (!str) {
Err("Error:: Unknown mode ...");
return -1;
}
if (!strcasecmp(str, "SCRIPT")) {
mode = 1;
if (setUIDGID(flg, proc.proc_name) == -1)
return -1;
} else if (!strcasecmp(str, "FILE")) {
mode = 2;
if (setUIDGID(flg, proc.proc_name) == -1)
return -1;
} else if (!strcasecmp(str, "DIR") &&
(str = (char*) cfg_GetAttribute(&cfg, CFG("global"), CFG("directory")))) {
mode = 3;
if (!(flg & 8))
strlcpy(proc.proc_dir, str, sizeof proc.proc_dir);
if (setUIDGID(flg, proc.proc_dir) == -1)
return -1;
} else {
Err("Error:: Unknown mode %s", str);
return -1;
}
if (!(flg & 4)) {
str = (char*) cfg_GetAttribute(&cfg, CFG("global"), CFG("priority"));
if (str)
proc.proc_prio = strtol(str, NULL, 10);
}
/* find associate extension */
str = strrchr(proc.proc_name, '.');
if (str) {
str++;
str = (*str) ? str : "default";
switch (cfg_LoadAttribute(&cfg, CFG("associate"), CFG(str),
CFG(proc.proc_cmd), sizeof proc.proc_cmd, NULL)) {
case -1:
Err("Error:: can`t load attribute #%d - %s\n", cfg_GetErrno(), cfg_GetError());
return -1;
case 0:
cfg_LoadAttribute(&cfg, CFG("associate"), CFG("default"),
CFG(proc.proc_cmd), sizeof proc.proc_cmd, DEFAULT_CMD);
}
} else
strlcpy(proc.proc_cmd, DEFAULT_CMD, sizeof proc.proc_cmd);
return 0;
}
static int
Run(char **argv)
{
char **args, *cmd;
array_t *acmd, *aarg;
int n;
if (!argv)
return -1;
n = io_arrayMake(proc.proc_cmd, 0, " \t", &acmd);
if (n < 1)
return -1;
if (!(aarg = io_arrayFrom((const char**) argv, 0))) {
io_arrayDestroy(&acmd);
return -1;
} else if (*io_arrayGet(acmd, 0, char*) == '!') {
if (io_arrayGrow(acmd, 0)) {
io_arrayDestroy(&aarg);
io_arrayDestroy(&acmd);
return -1;
}
cmd = io_arrayGet(aarg, 0, char*);
} else
cmd = io_arrayGet(acmd, 0, char*);
if (io_arrayConcat(acmd, aarg) == -1) {
io_arrayDestroy(&aarg);
io_arrayDestroy(&acmd);
return -1;
}
io_arrayDestroy(&aarg);
if (!(args = io_arrayTo(acmd))) {
io_arrayDestroy(&acmd);
return -1;
}
io_arrayDestroy(&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(1, "UID:GID=%d:%d Prio=%d Class=%s Name=%s Dir=%s Cmd=%s\n", proc.proc_uid, proc.proc_gid,
proc.proc_prio, proc.proc_class, proc.proc_name, proc.proc_dir, proc.proc_cmd);
if (3 <= Verbose) {
char **el = args - 1;
while (*++el)
Log(3, "args: %s", *el);
}
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, szCfg[MAXPATHLEN], szLog[MAXPATHLEN], flg = 0;
struct passwd *pass;
struct group *grp;
FILE *f;
initProg();
strlcpy(szCfg, DEFAULT_CONFIG, sizeof szCfg);
strlcpy(szLog, DEFAULT_LOG, sizeof szLog);
while ((ch = getopt(argc, argv, "hvc:u:g:p:d:l:")) != -1)
switch (ch) {
case 'l':
strlcpy(szLog, optarg, sizeof szLog);
f = fopen(szLog, "a+");
if (!f) {
Err("Error:: logfile #%d - %s", errno, strerror(errno));
return 1;
} else
if (flg & 16 && fileno(lf) > 2)
fclose(lf);
lf = f;
flg |= 16;
break;
case 'd':
strlcpy(proc.proc_dir, optarg, sizeof proc.proc_dir);
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 'v':
Verbose++;
break;
case 'h':
default:
Usage();
if (flg & 16)
fclose(lf);
return 1;
}
argc -= optind;
argv += optind;
if (!argc) {
if (!(str = getenv("PATH_TRANSLATED"))) {
Usage();
if (flg & 16)
fclose(lf);
return 1;
} else
strlcpy(proc.proc_name, str, sizeof proc.proc_name);
} else
strlcpy(proc.proc_name, *argv, sizeof proc.proc_name);
Log(2, "Try to load config %s", szCfg);
if (LoadConfig(szCfg, &cfg)) {
Err("Error:: can`t load config #%d - %s\n", cfg_GetErrno(), cfg_GetError());
if (flg & 16)
fclose(lf);
return 2;
} else
if (LoadCfgData(flg) == -1) {
UnloadConfig(&cfg);
if (flg & 16)
fclose(lf);
closelog();
return 3;
}
UnloadConfig(&cfg);
if (Run(argv) == -1) {
if (flg & 16)
fclose(lf);
closelog();
return 4;
}
if (flg & 16)
fclose(lf);
closelog();
return 0;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>