|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)uuxqt.c 5.8 (Berkeley) 1/24/86";
3: #endif
4:
5: #include "uucp.h"
6: #include <sys/stat.h>
7: #ifdef NDIR
8: #include "ndir.h"
9: #else
10: #include <sys/dir.h>
11: #endif
12: #include <signal.h>
13:
14: #define BADCHARS "&^|(`\\<>;\"{}\n'"
15: #define RECHECKTIME 60*10 /* 10 minutes */
16:
17: #define APPCMD(d) {\
18: char *p;\
19: for (p = d; *p != '\0';) *cmdp++ = *p++; *cmdp++ = ' '; *cmdp = '\0';}
20:
21: /*
22: * uuxqt will execute commands set up by a uux command,
23: * usually from a remote machine - set by uucp.
24: */
25:
26: #define NCMDS 50
27: char *Cmds[NCMDS+1];
28: int Notify[NCMDS+1];
29: #define NT_YES 0 /* if should notify on execution */
30: #define NT_ERR 1 /* if should notify if non-zero exit status (-z equivalent) */
31: #define NT_NO 2 /* if should not notify ever (-n equivalent) */
32:
33: extern int Nfiles;
34:
35: int TransferSucceeded = 1;
36: int notiok = 1;
37: int nonzero = 0;
38:
39: struct timeb Now;
40:
41: char PATH[MAXFULLNAME] = "PATH=/bin:/usr/bin:/usr/ucb";
42: char Shell[MAXFULLNAME];
43: char HOME[MAXFULLNAME];
44:
45: extern char **environ;
46: char *nenv[] = {
47: PATH,
48: Shell,
49: HOME,
50: 0
51: };
52:
53: /* to remove restrictions from uuxqt
54: * define ALLOK 1
55: *
56: * to add allowable commands, add to the file CMDFILE
57: * A line of form "PATH=..." changes the search path
58: */
59: main(argc, argv)
60: char *argv[];
61: {
62: char xcmd[MAXFULLNAME];
63: int argnok;
64: int notiflg;
65: char xfile[MAXFULLNAME], user[MAXFULLNAME], buf[BUFSIZ];
66: char lbuf[MAXFULLNAME];
67: char cfile[NAMESIZE], dfile[MAXFULLNAME];
68: char file[NAMESIZE];
69: char fin[MAXFULLNAME], sysout[NAMESIZE], fout[MAXFULLNAME];
70: register FILE *xfp, *fp;
71: FILE *dfp;
72: char path[MAXFULLNAME];
73: char cmd[BUFSIZ];
74: char *cmdp, prm[1000], *ptr;
75: char *getprm(), *lastpart();
76: int uid, ret, ret2, badfiles;
77: register int i;
78: int stcico = 0;
79: time_t xstart, xnow;
80: char retstat[30];
81: char **ep;
82:
83: strcpy(Progname, "uuxqt");
84: uucpname(Myname);
85:
86: umask(WFMASK);
87: Ofn = 1;
88: Ifn = 0;
89: while (argc>1 && argv[1][0] == '-') {
90: switch(argv[1][1]){
91: case 'x':
92: chkdebug();
93: Debug = atoi(&argv[1][2]);
94: if (Debug <= 0)
95: Debug = 1;
96: break;
97: default:
98: fprintf(stderr, "unknown flag %s\n", argv[1]);
99: break;
100: }
101: --argc; argv++;
102: }
103:
104: DEBUG(4, "\n\n** START **\n", CNULL);
105: ret = subchdir(Spool);
106: ASSERT(ret >= 0, "CHDIR FAILED", Spool, ret);
107: strcpy(Wrkdir, Spool);
108: uid = getuid();
109: guinfo(uid, User, path);
110: setgid(getegid());
111: setuid(geteuid());
112:
113: DEBUG(4, "User - %s\n", User);
114: if (ulockf(X_LOCK, X_LOCKTIME) != 0)
115: exit(0);
116:
117: fp = fopen(CMDFILE, "r");
118: if (fp == NULL) {
119: logent(CANTOPEN, CMDFILE);
120: Cmds[0] = "rmail";
121: Cmds[1] = "rnews";
122: Cmds[2] = "ruusend";
123: Cmds[3] = NULL;
124: goto doprocess;
125: }
126: DEBUG(5, "%s opened\n", CMDFILE);
127: for (i=0; i<NCMDS && cfgets(xcmd, sizeof(xcmd), fp) != NULL; i++) {
128: int j;
129: /* strip trailing whitespace */
130: for (j = strlen(xcmd)-1; j >= 0; --j)
131: if (xcmd[j] == '\n' || xcmd[j] == ' ' || xcmd[j] == '\t')
132: xcmd[j] = '\0';
133: else
134: break;
135: /* look for imbedded whitespace */
136: for (; j >= 0; --j)
137: if (xcmd[j] == '\n' || xcmd[j] == ' ' || xcmd[j] == '\t')
138: break;
139: /* skip this entry if it has embedded whitespace */
140: /* This defends against a bad PATH=, for example */
141: if (j >= 0) {
142: logent(xcmd, "BAD WHITESPACE");
143: continue;
144: }
145: if (strncmp(xcmd, "PATH=", 5) == 0) {
146: strcpy(PATH, xcmd);
147: i--; /*kludge */
148: continue;
149: }
150: DEBUG(5, "xcmd = %s\n", xcmd);
151:
152: if ((ptr = index(xcmd, ',')) != NULL) {
153: *ptr++ = '\0';
154: if (strncmp(ptr, "Err", 3) == SAME)
155: Notify[i] = NT_ERR;
156: else if (strcmp(ptr, "No") == SAME)
157: Notify[i] = NT_NO;
158: else
159: Notify[i] = NT_YES;
160: } else
161: Notify[i] = NT_YES;
162: if ((Cmds[i] = malloc((unsigned)(strlen(xcmd)+1))) == NULL) {
163: DEBUG(1, "MALLOC FAILED", CNULL);
164: break;
165: }
166: strcpy(Cmds[i], xcmd);
167: }
168: Cmds[i] = CNULL;
169: fclose(fp);
170:
171: doprocess:
172:
173: (void) sprintf(HOME, "HOME=%s", Spool);
174: (void) sprintf(Shell, "SHELL=%s", SHELL);
175: environ = nenv; /* force use if our environment */
176:
177: DEBUG(11,"path = %s\n", getenv("PATH"));
178:
179: DEBUG(4, "process %s\n", CNULL);
180: time(&xstart);
181: while (gtxfile(xfile) > 0) {
182: /* if /etc/nologin exists, exit cleanly */
183: #if defined(BSD4_2) || defined(USG)
184: if (access(NOLOGIN) == 0) {
185: #else !BSD4_2 && ! USG
186: ultouch();
187: if (nologinflag) {
188: #endif !BSD4_2 && !USG
189: logent(NOLOGIN, "UUXQT SHUTDOWN");
190: if (Debug)
191: logent("debugging", "continuing anyway");
192: else
193: break;
194: }
195: DEBUG(4, "xfile - %s\n", xfile);
196:
197: xfp = fopen(subfile(xfile), "r");
198: ASSERT(xfp != NULL, CANTOPEN, xfile, 0);
199:
200: /* initialize to default */
201: strcpy(user, User);
202: strcpy(fin, DEVNULL);
203: strcpy(fout, DEVNULL);
204: strcpy(sysout, Myname);
205: badfiles = 0;
206: while (fgets(buf, BUFSIZ, xfp) != NULL) {
207: switch (buf[0]) {
208: case X_USER:
209: sscanf(&buf[1], "%s %s", user, Rmtname);
210: break;
211: case X_RETURNTO:
212: sscanf(&buf[1], "%s", user);
213: break;
214: case X_STDIN:
215: sscanf(&buf[1], "%s", fin);
216: i = expfile(fin);
217: /* rti!trt: do not check permissions of
218: * vanilla spool file */
219: if (i != 0
220: && (chkpth("", "", fin) || anyread(fin) != 0))
221: badfiles = 1;
222: break;
223: case X_STDOUT:
224: sscanf(&buf[1], "%s%s", fout, sysout);
225: sysout[MAXBASENAME] = '\0';
226: /* rti!trt: do not check permissions of
227: * vanilla spool file. DO check permissions
228: * of writing on a non-vanilla file */
229: i = 1;
230: if (fout[0] != '~' || prefix(sysout, Myname))
231: i = expfile(fout);
232: if (i != 0
233: && (chkpth("", "", fout)
234: || chkperm(fout, (char *)1)))
235: badfiles = 1;
236: break;
237: case X_CMD:
238: strcpy(cmd, &buf[2]);
239: if (*(cmd + strlen(cmd) - 1) == '\n')
240: *(cmd + strlen(cmd) - 1) = '\0';
241: break;
242: case X_NONOTI:
243: notiok = 0;
244: break;
245: case X_NONZERO:
246: nonzero = 1;
247: break;
248: default:
249: break;
250: }
251: }
252:
253: fclose(xfp);
254: DEBUG(4, "fin - %s, ", fin);
255: DEBUG(4, "fout - %s, ", fout);
256: DEBUG(4, "sysout - %s, ", sysout);
257: DEBUG(4, "user - %s\n", user);
258: DEBUG(4, "cmd - %s\n", cmd);
259:
260: /* command execution */
261: if (strcmp(fout, DEVNULL) == SAME)
262: strcpy(dfile,DEVNULL);
263: else
264: gename(DATAPRE, sysout, 'O', dfile);
265:
266: /* expand file names where necessary */
267: expfile(dfile);
268: cmdp = buf;
269: ptr = cmd;
270: xcmd[0] = '\0';
271: argnok = 0;
272: while ((ptr = getprm(ptr, prm)) != NULL) {
273: if (prm[0] == ';' || prm[0] == '^'
274: || prm[0] == '&' || prm[0] == '|') {
275: xcmd[0] = '\0';
276: APPCMD(prm);
277: continue;
278: }
279:
280: if ((argnok = argok(xcmd, prm)) != SUCCESS)
281: /* command not valid */
282: break;
283:
284: if (prm[0] == '~')
285: expfile(prm);
286: APPCMD(prm);
287: }
288: /*
289: * clean up trailing ' ' in command.
290: */
291: if (cmdp > buf && cmdp[0] == '\0' && cmdp[-1] == ' ')
292: *--cmdp = '\0';
293: if (argnok || badfiles) {
294: sprintf(lbuf, "%s XQT DENIED", user);
295: logent(cmd, lbuf);
296: DEBUG(4, "bad command %s\n", prm);
297: notify(user, Rmtname, cmd, "DENIED");
298: goto rmfiles;
299: }
300: sprintf(lbuf, "%s XQT", user);
301: logent(buf, lbuf);
302: DEBUG(4, "cmd %s\n", buf);
303:
304: mvxfiles(xfile);
305: ret = subchdir(XQTDIR);
306: ASSERT(ret >= 0, "CHDIR FAILED", XQTDIR, ret);
307: ret = shio(buf, fin, dfile);
308: sprintf(retstat, "signal %d, exit %d", ret & 0377,
309: (ret>>8) & 0377);
310: if (strcmp(xcmd, "rmail") == SAME)
311: notiok = 0;
312: if (strcmp(xcmd, "rnews") == SAME)
313: nonzero = 1;
314: notiflg = chknotify(xcmd);
315: if (notiok && notiflg != NT_NO &&
316: (ret != 0 || (!nonzero && notiflg == NT_YES)))
317: notify(user, Rmtname, cmd, retstat);
318: else if (ret != 0 && strcmp(xcmd, "rmail") == SAME) {
319: /* mail failed - return letter to sender */
320: #ifdef DANGEROUS
321: /* NOT GUARANTEED SAFE!!! */
322: if (!nonzero)
323: retosndr(user, Rmtname, fin);
324: #else
325: notify(user, Rmtname, cmd, retstat);
326: #endif
327: sprintf(buf, "%s (%s) from %s!%s", buf, retstat, Rmtname, user);
328: logent("MAIL FAIL", buf);
329: }
330: DEBUG(4, "exit cmd - %d\n", ret);
331: ret2 = subchdir(Spool);
332: ASSERT(ret2 >= 0, "CHDIR FAILED", Spool, ret);
333: rmxfiles(xfile);
334: if (ret != 0) {
335: /* exit status not zero */
336: dfp = fopen(subfile(dfile), "a");
337: ASSERT(dfp != NULL, CANTOPEN, dfile, 0);
338: fprintf(dfp, "exit status %d", ret);
339: fclose(dfp);
340: }
341: if (strcmp(fout, DEVNULL) != SAME) {
342: if (prefix(sysout, Myname)) {
343: xmv(dfile, fout);
344: chmod(fout, BASEMODE);
345: } else {
346: char *cp = rindex(user, '!');
347: gename(CMDPRE, sysout, 'O', cfile);
348: fp = fopen(subfile(cfile), "w");
349: ASSERT(fp != NULL, "OPEN", cfile, 0);
350: fprintf(fp, "S %s %s %s - %s 0666\n", dfile,
351: fout, cp ? cp : user, lastpart(dfile));
352: fclose(fp);
353: }
354: }
355: rmfiles:
356: xfp = fopen(subfile(xfile), "r");
357: ASSERT(xfp != NULL, CANTOPEN, xfile, 0);
358: while (fgets(buf, BUFSIZ, xfp) != NULL) {
359: if (buf[0] != X_RQDFILE)
360: continue;
361: sscanf(&buf[1], "%s", file);
362: unlink(subfile(file));
363: }
364: unlink(subfile(xfile));
365: fclose(xfp);
366:
367: /* rescan X. for new work every RECHECKTIME seconds */
368: time(&xnow);
369: if (xnow > (xstart + RECHECKTIME)) {
370: extern int Nfiles;
371: Nfiles = 0; /*force rescan for new work */
372: }
373: xstart = xnow;
374: }
375:
376: if (stcico)
377: xuucico("");
378: cleanup(0);
379: }
380:
381:
382: cleanup(code)
383: int code;
384: {
385: logcls();
386: rmlock(CNULL);
387: #ifdef VMS
388: /*
389: * Since we run as a BATCH job we must wait for all processes to
390: * to finish
391: */
392: while(wait(0) != -1)
393: ;
394: #endif VMS
395: exit(code);
396: }
397:
398:
399: /*
400: * get a file to execute
401: *
402: * return codes: 0 - no file | 1 - file to execute
403: */
404:
405: gtxfile(file)
406: register char *file;
407: {
408: char pre[3];
409: int rechecked;
410: time_t ystrdy; /* yesterday */
411: struct stat stbuf; /* for X file age */
412:
413: pre[0] = XQTPRE;
414: pre[1] = '.';
415: pre[2] = '\0';
416: rechecked = 0;
417: retry:
418: if (!gtwrkf(Spool, file)) {
419: if (rechecked)
420: return 0;
421: rechecked = 1;
422: DEBUG(4, "iswrk\n", CNULL);
423: if (!iswrk(file, "get", Spool, pre))
424: return 0;
425: }
426: DEBUG(4, "file - %s\n", file);
427: /* skip spurious subdirectories */
428: if (strcmp(pre, file) == SAME)
429: goto retry;
430: if (gotfiles(file))
431: return 1;
432: /* check for old X. file with no work files and remove them. */
433: if (Nfiles > LLEN/2) {
434: time(&ystrdy);
435: ystrdy -= (4 * 3600L); /* 4 hours ago */
436: DEBUG(4, "gtxfile: Nfiles > LLEN/2\n", CNULL);
437: while (gtwrkf(Spool, file) && !gotfiles(file)) {
438: if (stat(subfile(file), &stbuf) == 0)
439: if (stbuf.st_mtime <= ystrdy) {
440: char *bnp, cfilename[NAMESIZE];
441: DEBUG(4, "gtxfile: move %s to CORRUPT \n", file);
442: unlink(subfile(file));
443: bnp = rindex(subfile(file), '/');
444: sprintf(cfilename, "%s/%s", CORRUPT,
445: bnp ? bnp + 1 : subfile(file));
446: xmv(subfile(file), cfilename);
447: logent(file, "X. FILE CORRUPTED");
448: }
449: }
450: DEBUG(4, "iswrk\n", CNULL);
451: if (!iswrk(file, "get", Spool, pre))
452: return 0;
453: }
454: goto retry;
455: }
456:
457: /*
458: * check for needed files
459: *
460: * return codes: 0 - not ready | 1 - all files ready
461: */
462:
463: gotfiles(file)
464: register char *file;
465: {
466: struct stat stbuf;
467: register FILE *fp;
468: char buf[BUFSIZ], rqfile[MAXFULLNAME];
469:
470: fp = fopen(subfile(file), "r");
471: if (fp == NULL)
472: return 0;
473:
474: while (fgets(buf, BUFSIZ, fp) != NULL) {
475: DEBUG(4, "%s\n", buf);
476: if (buf[0] != X_RQDFILE)
477: continue;
478: sscanf(&buf[1], "%s", rqfile);
479: expfile(rqfile);
480: if (stat(subfile(rqfile), &stbuf) == -1) {
481: fclose(fp);
482: return 0;
483: }
484: }
485:
486: fclose(fp);
487: return 1;
488: }
489:
490:
491: /*
492: * remove execute files to x-directory
493: */
494:
495: rmxfiles(xfile)
496: register char *xfile;
497: {
498: register FILE *fp;
499: char buf[BUFSIZ], file[NAMESIZE], tfile[NAMESIZE];
500: char tfull[MAXFULLNAME];
501:
502: if((fp = fopen(subfile(xfile), "r")) == NULL)
503: return;
504:
505: while (fgets(buf, BUFSIZ, fp) != NULL) {
506: if (buf[0] != X_RQDFILE)
507: continue;
508: if (sscanf(&buf[1], "%s%s", file, tfile) < 2)
509: continue;
510: sprintf(tfull, "%s/%s", XQTDIR, tfile);
511: unlink(subfile(tfull));
512: }
513: fclose(fp);
514: return;
515: }
516:
517:
518: /*
519: * move execute files to x-directory
520: */
521:
522: mvxfiles(xfile)
523: char *xfile;
524: {
525: register FILE *fp;
526: char buf[BUFSIZ], ffile[MAXFULLNAME], tfile[NAMESIZE];
527: char tfull[MAXFULLNAME];
528: int ret;
529:
530: if((fp = fopen(subfile(xfile), "r")) == NULL)
531: return;
532:
533: while (fgets(buf, BUFSIZ, fp) != NULL) {
534: if (buf[0] != X_RQDFILE)
535: continue;
536: if (sscanf(&buf[1], "%s%s", ffile, tfile) < 2)
537: continue;
538: expfile(ffile);
539: sprintf(tfull, "%s/%s", XQTDIR, tfile);
540: unlink(subfile(tfull));
541: ret = xmv(ffile, tfull);
542: ASSERT(ret == 0, "XQTDIR ERROR", CNULL, ret);
543: }
544: fclose(fp);
545: }
546:
547: /*
548: * check for valid command/argument
549: * *NOTE - side effect is to set xc to the command to be executed.
550: *
551: * return 0 - ok | 1 nok
552: */
553:
554: argok(xc, cmd)
555: register char *xc, *cmd;
556: {
557: register char **ptr;
558:
559: #ifndef ALLOK
560: if (strpbrk(cmd, BADCHARS) != NULL) {
561: DEBUG(1,"MAGIC CHARACTER FOUND\n", CNULL);
562: logent(cmd, "NASTY MAGIC CHARACTER FOUND");
563: return FAIL;
564: }
565: #endif !ALLOK
566:
567: if (xc[0] != '\0')
568: return SUCCESS;
569:
570: #ifndef ALLOK
571: ptr = Cmds;
572: DEBUG(9, "Compare %s and\n", cmd);
573: while(*ptr != NULL) {
574: DEBUG(9, "\t%s\n", *ptr);
575: if (strcmp(cmd, *ptr) == SAME)
576: break;
577: ptr++;
578: }
579: if (*ptr == NULL) {
580: DEBUG(1,"COMMAND NOT FOUND\n", CNULL);
581: return FAIL;
582: }
583: #endif
584: strcpy(xc, cmd);
585: DEBUG(9, "MATCHED %s\n", xc);
586: return SUCCESS;
587: }
588:
589:
590: /*
591: * if notification should be sent for successful execution of cmd
592: *
593: * return NT_YES - do notification
594: * NT_ERR - do notification if exit status != 0
595: * NT_NO - don't do notification ever
596: */
597:
598: chknotify(cmd)
599: char *cmd;
600: {
601: register char **ptr;
602: register int *nptr;
603:
604: ptr = Cmds;
605: nptr = Notify;
606: while (*ptr != NULL) {
607: if (strcmp(cmd, *ptr) == SAME)
608: return *nptr;
609: ptr++;
610: nptr++;
611: }
612: return NT_YES; /* "shouldn't happen" */
613: }
614:
615:
616:
617: /*
618: * send mail to user giving execution results
619: */
620:
621: notify(user, rmt, cmd, str)
622: char *user, *rmt, *cmd, *str;
623: {
624: char text[MAXFULLNAME];
625: char ruser[MAXFULLNAME];
626:
627: if (strpbrk(user, BADCHARS) != NULL) {
628: char lbuf[MAXFULLNAME];
629: sprintf(lbuf, "%s INVALID CHARACTER IN USERNAME", user);
630: logent(cmd, lbuf);
631: strcpy(user, "postmaster");
632: }
633: sprintf(text, "uuxqt cmd (%s) status (%s)", cmd, str);
634: if (prefix(rmt, Myname))
635: strcpy(ruser, user);
636: else
637: sprintf(ruser, "%s!%s", rmt, user);
638: mailst(ruser, text, CNULL);
639: }
640:
641: /*
642: * return mail to sender
643: *
644: */
645: retosndr(user, rmt, file)
646: char *user, *rmt, *file;
647: {
648: char ruser[MAXFULLNAME];
649:
650: if (strpbrk(user, BADCHARS) != NULL) {
651: char lbuf[MAXFULLNAME];
652: sprintf(lbuf, "%s INVALID CHARACTER IN USERNAME", user);
653: logent(file, lbuf);
654: strcpy(user, "postmaster");
655: }
656: if (strcmp(rmt, Myname) == SAME)
657: strcpy(ruser, user);
658: else
659: sprintf(ruser, "%s!%s", rmt, user);
660:
661: if (anyread(file) == 0)
662: mailst(ruser, "Mail failed. Letter returned to sender.\n", file);
663: else
664: mailst(ruser, "Mail failed. Letter returned to sender.\n", CNULL);
665: return;
666: }
667:
668: /*
669: * execute shell of command with fi and fo as standard input/output
670: */
671:
672: shio(cmd, fi, fo)
673: char *cmd, *fi, *fo;
674: {
675: int status, f;
676: int uid, pid, ret;
677: char path[MAXFULLNAME];
678: char *args[20];
679: extern int errno;
680:
681: if (fi == NULL)
682: fi = DEVNULL;
683: if (fo == NULL)
684: fo = DEVNULL;
685:
686: getargs(cmd, args, 20);
687: DEBUG(3, "shio - %s\n", cmd);
688: #ifdef SIGCHLD
689: signal(SIGCHLD, SIG_IGN);
690: #endif SIGCHLD
691: if ((pid = fork()) == 0) {
692: signal(SIGINT, SIG_IGN);
693: signal(SIGHUP, SIG_IGN);
694: signal(SIGQUIT, SIG_IGN);
695: close(Ifn);
696: close(Ofn);
697: close(0);
698: setuid(getuid());
699: f = open(subfile(fi), 0);
700: if (f != 0) {
701: logent(fi, "CAN'T READ");
702: exit(-errno);
703: }
704: close(1);
705: f = creat(subfile(fo), 0666);
706: if (f != 1) {
707: logent(fo, "CAN'T WRITE");
708: exit(-errno);
709: }
710: execvp(args[0], args);
711: exit(100+errno);
712: }
713: while ((ret = wait(&status)) != pid && ret != -1)
714: ;
715: DEBUG(3, "status %d\n", status);
716: return status;
717: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.