1: /*************************************************************************
2: * (C) 2011 AITNET - Sofia/Bulgaria - <office@aitbg.com>
3: * by Michael Pounov <misho@aitbg.com>
4: *
5: * $Author: misho $
6: * $Id: sux.c,v 1.6 2015/06/17 14:14:17 misho Exp $
7: *
8: *************************************************************************/
9: #include "global.h"
10:
11:
12: cfg_root_t cfg;
13: int Verbose;
14: struct tagProc proc;
15: FILE *lf;
16:
17:
18: static inline void
19: Log(int lvl, const char *fmt, ...)
20: {
21: va_list lst, cp;
22:
23: EVERBS(lvl) {
24: va_start(lst, fmt);
25: va_copy(cp, lst);
26: vfprintf(lf, fmt, lst);
27: va_end(lst);
28: fprintf(lf, "\n");
29: vsyslog(LOG_INFO, fmt, cp);
30: va_end(cp);
31: }
32: }
33:
34: static inline void
35: Err(const char *fmt, ...)
36: {
37: va_list lst, cp;
38:
39: va_start(lst, fmt);
40: va_copy(cp, lst);
41: vfprintf(lf, fmt, lst);
42: va_end(lst);
43: fprintf(lf, "\n");
44: vsyslog(LOG_ERR, fmt, cp);
45: va_end(cp);
46: }
47:
48: static inline void
49: DumpProc(const char *txt)
50: {
51: Log(1, "%s:: uid:gid=%d:%d UID:GID=%d:%d Prio=%d Class=%s Name=%s Dir=%s Cmd=%s "
52: "Script=%s From=%s:%s Get=%s", txt ? txt : __func__,
53: geteuid(), getegid(), AIT_GET_I16(&proc.proc_uid),
54: AIT_GET_I16(&proc.proc_gid), AIT_GET_I32(&proc.proc_prio),
55: AIT_GET_STR(&proc.proc_class), AIT_GET_STR(&proc.proc_name),
56: AIT_GET_STR(&proc.proc_dir), AIT_GET_STR(&proc.proc_cmd),
57: getenv("PATH_TRANSLATED"), getenv("REMOTE_ADDR"),
58: getenv("REMOTE_PORT"), getenv("REQUEST_URI"));
59: }
60:
61: static int
62: initProg()
63: {
64: AIT_SET_I16(&proc.proc_uid, getuid());
65: AIT_SET_I16(&proc.proc_gid, getgid());
66: AIT_SET_I32(&proc.proc_prio, getpriority(PRIO_PROCESS, 0));
67: AIT_INIT_VAL2(&proc.proc_class, string);
68: AIT_INIT_VAL2(&proc.proc_dir, string);
69: AIT_INIT_VAL2(&proc.proc_name, string);
70: AIT_INIT_VAL2(&proc.proc_cmd, string);
71:
72: #if 0
73: lf = fopen(DEFAULT_LOG, "a");
74: if (!lf)
75: #endif
76: lf = stdout;
77:
78: openlog(PACKAGE_NAME, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
79: return 0;
80: }
81:
82: static void
83: endProg()
84: {
85: AIT_FREE_VAL(&proc.proc_uid);
86: AIT_FREE_VAL(&proc.proc_gid);
87: AIT_FREE_VAL(&proc.proc_prio);
88: AIT_FREE_VAL(&proc.proc_class);
89: AIT_FREE_VAL(&proc.proc_dir);
90: AIT_FREE_VAL(&proc.proc_name);
91: AIT_FREE_VAL(&proc.proc_cmd);
92: }
93:
94: static void
95: Usage()
96: {
97: printf( " -= suX =- suExecutor designed for web based applicaions\n"
98: "(C)`11 AITNET ltd - Sofia/Bulgaria - <office@aitnet.org>\n\n"
99: " Syntax: %s [options] <program|-> [arguments]\n"
100: "\t-u <user>\t\t\tUser for suID\n"
101: "\t-g <group>\t\t\tGroup for suID\n"
102: "\t-p <priority (-20..20)>\t\tExecute with priority\n"
103: "\t-d <directory>\t\t\tDirectory for suID\n"
104: "\t-C <directory>\t\t\tChroot to directory\n"
105: "\t-c <cfgfile>\t\t\tConfig file\n"
106: "\t-l <logfile>\t\t\tLog file path (default:/var/log/suX.log)\n"
107: "\t-o\t\t\t\tForce set UID,GID and Priority for program\n"
108: "\t-v\t\t\t\tVerbose, (more -v, more verbosity)\n"
109: "\t-h\t\t\t\tThis help screen!\n\n", PACKAGE_NAME);
110: }
111:
112: static inline int
113: setUIDGID(char flg, const char *name)
114: {
115: struct stat sb;
116: struct passwd *pass;
117: short uid, gid;
118:
119: if (name) {
120: if (stat(name, &sb) == -1) {
121: ESYSERR(0);
122: return -1;
123: }
124: uid = sb.st_uid;
125: gid = sb.st_gid;
126: } else {
127: pass = getpwnam(getenv("SUX_USER") ? getenv("SUX_USER") : DEFAULT_SUX_USER);
128: if (!pass) {
129: Err("Error:: User %s not found", getenv("SUX_USER"));
130: endpwent();
131: return -1;
132: }
133: uid = pass->pw_uid;
134: gid = pass->pw_gid;
135: endpwent();
136: }
137:
138: if (!(flg & SUX_GET_UID))
139: AIT_SET_I16(&proc.proc_uid, uid);
140: if (!(flg & SUX_GET_GID))
141: AIT_SET_I16(&proc.proc_gid, gid);
142:
143: return 0;
144: }
145:
146: static inline int
147: setClassDir()
148: {
149: struct passwd *pass;
150: int ret = 0;
151:
152: pass = getpwuid(AIT_GET_I16(&proc.proc_uid));
153: if (!pass) {
154: Err("Error:: User with this UID %d not found", AIT_GET_I16(&proc.proc_uid));
155: ret = -1;
156: } else {
157: AIT_SET_STR(&proc.proc_class, pass->pw_class);
158: AIT_SET_STR(&proc.proc_dir, pass->pw_dir);
159:
160: if (setusercontext(NULL, pass, AIT_GET_I16(&proc.proc_uid),
161: LOGIN_SETLOGIN | LOGIN_SETGROUP | LOGIN_SETUSER |
162: LOGIN_SETPRIORITY | LOGIN_SETRESOURCES)) {
163: Err("Error:: Can't set login class %s", AIT_GET_STR(&proc.proc_class));
164: ret = -1;
165: }
166: }
167:
168: endpwent();
169: return ret;
170: }
171:
172: static int
173: LoadCfgData(char flg)
174: {
175: const char *str;
176: char mode = 0;
177:
178: str = cfg_getAttribute(&cfg, "global", "mode");
179: if (!str) {
180: Err("Error:: Unknown mode ...");
181: return -1;
182: }
183: if (!strcasecmp(str, "SCRIPT")) {
184: mode = 1;
185: if (setUIDGID(flg, (getenv("SUX_USER") ? NULL : getenv("PATH_TRANSLATED"))) == -1)
186: return -1;
187: } else if (!strcasecmp(str, "FILE")) {
188: mode = 2;
189: if (setUIDGID(flg, AIT_GET_STR(&proc.proc_name)) == -1)
190: return -1;
191: } else if (!strcasecmp(str, "DIR")) {
192: mode = 3;
193: str = AIT_GET_STR(&proc.proc_dir) ? AIT_GET_STR(&proc.proc_dir) : ".";
194: if (setUIDGID(flg, str) == -1)
195: return -1;
196: } else {
197: Err("Error:: Unknown mode %s", str);
198: return -1;
199: }
200: if (!(flg & SUX_GET_PRIO)) {
201: str = cfg_getAttribute(&cfg, "global", "priority");
202: if (str)
203: AIT_SET_I32(&proc.proc_prio, strtol(str, NULL, 10));
204: }
205:
206: /* find associate extension */
207: str = strrchr(AIT_GET_STR(&proc.proc_name), '.');
208: if (str) {
209: str++;
210: str = (*str) ? str : "default";
211: switch (cfg_loadAttribute(&cfg, "associate", str, &proc.proc_cmd, NULL)) {
212: case -1:
213: ELIBERR(cfg);
214: return -1;
215: case 0:
216: cfg_loadAttribute(&cfg, "associate", "default",
217: &proc.proc_cmd, DEFAULT_CMD);
218: }
219: } else
220: AIT_SET_STR(&proc.proc_cmd, DEFAULT_CMD);
221:
222: return 0;
223: }
224:
225: static int
226: Run(char **argv, char flg)
227: {
228: char **args, *cmd;
229: array_t *acmd, *aarg;
230: int n;
231:
232: if (!argv)
233: return -1;
234:
235: n = array_Args(AIT_GET_STR(&proc.proc_cmd), 0, " \t", &acmd);
236: if (n < 1)
237: return -1;
238: if (!(aarg = array_From((const char***) &argv, 0))) {
239: array_Destroy(&acmd);
240: return -1;
241: }
242: /* '!' exclude associated wrapper aka direct args execution */
243: if (*array(acmd, 0, char*) == '!') {
244: if (array_Grow(acmd, 0, 0)) {
245: array_Destroy(&aarg);
246: array_Destroy(&acmd);
247: return -1;
248: }
249: cmd = array(aarg, 0, char*);
250: } else
251: cmd = array(acmd, 0, char*);
252:
253: if (!(flg & SUX_GET_STDIN) && array_Concat(acmd, aarg) == -1) {
254: array_Destroy(&aarg);
255: array_Destroy(&acmd);
256: return -1;
257: }
258: array_Destroy(&aarg);
259: if (!(args = array_To(acmd))) {
260: array_Destroy(&acmd);
261: return -1;
262: }
263: array_Destroy(&acmd);
264:
265: if (setClassDir()) {
266: if (args)
267: e_free(args);
268: return -1;
269: }
270:
271: if (flg & SUX_GET_FORCE) {
272: if (setegid(AIT_GET_I16(&proc.proc_gid)) == -1)
273: goto err;
274: if (seteuid(AIT_GET_I16(&proc.proc_uid)) == -1)
275: goto err;
276: if (setpriority(PRIO_PROCESS, 0, AIT_GET_I32(&proc.proc_prio)) == -1)
277: goto err;
278: }
279:
280: EVERBS(3) {
281: char **el = args - 1;
282: while (*++el)
283: Log(3, "args: %s", *el);
284: }
285:
286: DumpProc(__func__);
287:
288: fflush(lf);
289:
290: execve(cmd, args, environ);
291: err:
292: ESYSERR(0);
293: if (args)
294: e_free(args);
295: return -1;
296: }
297:
298:
299: int
300: main(int argc, char **argv)
301: {
302: char ch, *str, *wrk, szCfg[MAXPATHLEN], **pp, flg = 0;
303: struct passwd *pass;
304: struct group *grp;
305: FILE *f;
306:
307: initProg();
308: strlcpy(szCfg, DEFAULT_CONFIG, sizeof szCfg);
309:
310: while ((ch = getopt(argc, argv, "hvoC:c:u:g:p:d:l:")) != -1)
311: switch (ch) {
312: case 'l':
313: f = fopen(optarg, "a");
314: if (!f) {
315: Err("Error:: logfile #%d - %s", errno, strerror(errno));
316: return 1;
317: } else
318: if (fileno(lf) > 2)
319: fclose(lf);
320: lf = f;
321: break;
322: case 'd':
323: AIT_SET_STR(&proc.proc_dir, optarg);
324: flg |= SUX_GET_DIR;
325: break;
326: case 'p':
327: AIT_SET_I32(&proc.proc_prio, strtol(optarg, NULL, 0));
328: flg |= SUX_GET_PRIO;
329: break;
330: case 'g':
331: setgrent();
332: grp = getgrnam(optarg);
333: if (grp) {
334: AIT_SET_I16(&proc.proc_gid, grp->gr_gid);
335: flg |= SUX_GET_GID;
336: } else
337: Err("Error:: Group not found!");
338: endgrent();
339: break;
340: case 'u':
341: setpwent();
342: pass = getpwnam(optarg);
343: if (pass) {
344: AIT_SET_I16(&proc.proc_uid, pass->pw_uid);
345: flg |= SUX_GET_UID;
346: } else
347: Err("Error:: User not found!");
348: endpwent();
349: break;
350: case 'c':
351: strlcpy(szCfg, optarg, sizeof szCfg);
352: break;
353: case 'C':
354: if (chroot(optarg) == -1)
355: Err("Error:: chroot to dir");
356: if ((str = getenv("PATH_TRANSLATED")))
357: if ((wrk = strstr(str, optarg)))
358: setenv("PATH_TRANSLATED", str + strlen(optarg), 42);
359: break;
360: case 'o':
361: flg |= SUX_GET_FORCE;
362: break;
363: case 'v':
364: e_incVerbose;
365: break;
366: case 'h':
367: default:
368: Usage();
369: endProg();
370: if (fileno(lf) > 2)
371: fclose(lf);
372: return 1;
373: }
374: argc -= optind;
375: argv += optind;
376:
377: EVERBS(2) {
378: for (pp = argv; *pp; pp++)
379: Log(2, "Args=%s\n", *pp);
380: for (pp = environ; *pp; pp++)
381: Log(2, "Envs=%s\n", *pp);
382: }
383:
384: if (!argc) {
385: if (!(str = getenv("PATH_TRANSLATED"))) {
386: Usage();
387: endProg();
388: if (fileno(lf) > 2)
389: fclose(lf);
390: return 1;
391: } else
392: AIT_SET_STR(&proc.proc_name, str);
393: } else if (strcmp(*argv, "-"))
394: AIT_SET_STR(&proc.proc_name, *argv);
395: else {
396: flg |= SUX_GET_STDIN;
397: AIT_SET_STR(&proc.proc_name, "-.stdin"); /* hack for associate to stdin */
398: }
399: Log(2, "Try to load config %s", szCfg);
400: if (cfgLoadConfig(szCfg, &cfg)) {
401: ELIBERR(cfg);
402: endProg();
403: if (fileno(lf) > 2)
404: fclose(lf);
405: return 2;
406: } else
407: if (LoadCfgData(flg) == -1) {
408: cfgUnloadConfig(&cfg);
409: endProg();
410: if (fileno(lf) > 2)
411: fclose(lf);
412: closelog();
413: return 3;
414: }
415: cfgUnloadConfig(&cfg);
416:
417: if (Run(argv, flg) == -1) {
418: endProg();
419: if (fileno(lf) > 2)
420: fclose(lf);
421: closelog();
422: return 4;
423: }
424:
425: endProg();
426: if (fileno(lf) > 2)
427: fclose(lf);
428: closelog();
429: return 0;
430: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>