File:  [ELWIX - Embedded LightWeight unIX -] / suX / src / sux.c
Revision 1.1.1.1.2.4: download - view: text, annotated - select for diffs - revision graph
Fri May 20 16:41:31 2011 UTC (13 years, 1 month ago) by misho
Branches: sux2_0
Diff to: branchpoint 1.1.1.1: preferred, colored
misc fixes

/*************************************************************************
 * (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.4 2011/05/20 16:41:31 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);
		fprintf(lf, "\n");
		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);
	fprintf(lf, "\n");
	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 = fopen(DEFAULT_LOG, "a");
	if (!lf)
		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(0, "UID:GID=%d:%d Prio=%d Class=%s Name=%s Dir=%s Cmd=%s", 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);
	}

	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, szCfg[MAXPATHLEN], flg = 0;
	struct passwd *pass;
	struct group *grp;
	FILE *f;

	initProg();
	strlcpy(szCfg, DEFAULT_CONFIG, sizeof szCfg);

	while ((ch = getopt(argc, argv, "hvc: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':
				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 (fileno(lf) > 2)
					fclose(lf);
				return 1;
		}
	argc -= optind;
	argv += optind;
	if (!argc) {
		if (!(str = getenv("PATH_TRANSLATED"))) {
			Usage();
			if (fileno(lf) > 2)
				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 (fileno(lf) > 2)
			fclose(lf);
		return 2;
	} else
		if (LoadCfgData(flg) == -1) {
			UnloadConfig(&cfg);
			if (fileno(lf) > 2)
				fclose(lf);
			closelog();
			return 3;
		}
	UnloadConfig(&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>