/************************************************************************* * (C) 2011 AITNET - Sofia/Bulgaria - * by Michael Pounov * * $Author: misho $ * $Id: sux.c,v 1.5 2013/04/10 00:04:11 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_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 inline void DumpProc(const char *txt) { Log(0, "%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 - \n\n" " Syntax: %s [options] [arguments]\n" "\t-u \t\t\tUser for suID\n" "\t-g \t\t\tGroup for suID\n" "\t-p \t\tExecute with priority\n" "\t-d \t\t\tDirectory for suID\n" "\t-C \t\t\tChroot to directory\n" "\t-c \t\t\tConfig file\n" "\t-l \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")); 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") && getenv("SUX_USER")) { mode = 1; if (setUIDGID(flg, NULL) == -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; } 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 (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; } DumpProc(__func__); EVERBS(3) { char **el = args - 1; while (*++el) Log(3, "args: %s", *el); } 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 AIT_SET_STR(&proc.proc_name, *argv); 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; }