File:  [ELWIX - Embedded LightWeight unIX -] / suX / src / sux.c
Revision 1.6: download - view: text, annotated - select for diffs - revision graph
Wed Jun 17 14:14:17 2015 UTC (9 years, 5 months ago) by misho
Branches: MAIN
CVS tags: sux3_3, SUX3_2, HEAD
version 3.2

/*************************************************************************
 * (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>