File:  [ELWIX - Embedded LightWeight unIX -] / suX / src / sux.c
Revision 1.3.2.6: download - view: text, annotated - select for diffs - revision graph
Tue Apr 9 15:51:11 2013 UTC (11 years, 2 months ago) by misho
Branches: sux3_0
Diff to: branchpoint 1.3: preferred, colored
optimize

/*************************************************************************
 * (C) 2011 AITNET - Sofia/Bulgaria - <office@aitbg.com>
 *  by Michael Pounov <misho@aitbg.com>
 *
 * $Author: misho $
 * $Id: sux.c,v 1.3.2.6 2013/04/09 15:51: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 Prio=%d Class=%s Name=%s Dir=%s Cmd=%s Script=%s From=%s:%s%s", 
			txt ? txt : __func__, 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 void
initProg()
{
	char d[MAXPATHLEN];

	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);
	getcwd(d, sizeof d);
	AIT_SET_STR(&proc.proc_dir, d);
	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);
}

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;

	if (stat(name, &sb) == -1) {
		ESYSERR(0);
		return -1;
	}

	if (!(flg & SUX_GET_UID))
		AIT_SET_I16(&proc.proc_uid, sb.st_uid);
	if (!(flg & SUX_GET_GID))
		AIT_SET_I16(&proc.proc_gid, sb.st_gid);

	return 0;
}

static inline int
SetClass()
{
	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));
		endpwent();
		return -1;
	} else 
		AIT_SET_STR(&proc.proc_class, pass->pw_class);

	if (setusercontext(NULL, pass, AIT_GET_I16(&proc.proc_uid), 
				LOGIN_SETRESOURCES | LOGIN_SETGROUP | LOGIN_SETLOGIN | 
				LOGIN_SETPRIORITY)) {
		Err("Error:: Cant 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, 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 & SUX_GET_DIR))
			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 & 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 (SetClass()) {
		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;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>