1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 #include "uucp.h"
31 #include "log.h"
32
33 /*
34 * execute commands set up by a uux command,
35 * usually from a remote machine - set by uucp.
36 */
37
38 #ifndef V7
39 #define LOGNAME "LOGNAME=uucp"
40 #else
41 #define LOGNAME "USER=uucp"
42 #endif
43
44 #define C_COMMAND 1
45 #define C_FILE 2
46 #define BAD_COMMAND 1
47 #define BAD_FILE 2
48 #define USAGEPREFIX "Usage:"
49 #define USAGE "[-x DEBUG] [-s SYSTEM]"
50
51 char _Xfile[MAXFULLNAME];
52 char _Cmd[2 * BUFSIZ]; /* build up command buffer */
53 int _CargType; /* argument type of next C argument */
54
55 static void retosndr(), uucpst();
56 static int chkFile();
57 static int doFileChk();
58 void cleanup(), xprocess();
59
60 int
main(argc,argv,envp)61 main(argc, argv, envp)
62 int argc;
63 char *argv[];
64 char *envp[];
65 {
66 DIR *fp1;
67 struct limits limitval;
68 int ret, maxnumb;
69 char dirname[MAXFULLNAME], lockname[MAXFULLNAME];
70 void onintr();
71
72 /* Set locale environment variables local definitions */
73 (void) setlocale(LC_ALL, "");
74 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
75 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
76 #endif
77 (void) textdomain(TEXT_DOMAIN);
78
79 (void) signal(SIGILL, onintr);
80 (void) signal(SIGTRAP, onintr);
81 (void) signal(SIGIOT, onintr);
82 (void) signal(SIGEMT, onintr);
83 (void) signal(SIGFPE, onintr);
84 (void) signal(SIGBUS, onintr);
85 (void) signal(SIGSEGV, onintr);
86 (void) signal(SIGSYS, onintr);
87 (void) signal(SIGPIPE, onintr);
88 (void) signal(SIGTERM, SIG_IGN);
89
90 /* choose LOGFILE */
91 (void) strcpy(Logfile, LOGUUXQT);
92
93 /*
94 * get local system name
95 */
96 Env = envp;
97 Nstat.t_qtime = time((time_t *)0);
98 (void) strcpy(Progname, "uuxqt");
99 Pchar = 'Q';
100 uucpname(Myname);
101 Ofn = 1;
102 Ifn = 0;
103 dirname[0] = dirname[MAXFULLNAME-1] = NULLCHAR;
104 while ((ret = getopt(argc, argv, "s:x:")) != EOF) {
105 switch (ret) {
106
107 /*
108 * debugging level
109 */
110 case 'x':
111 Debug = atoi(optarg);
112 if (Debug <= 0)
113 Debug = 1;
114 break;
115
116 case 's':
117 /*
118 * fake out uuxqt and use the argument as if
119 * it were the spool directory for the purpose
120 * of determining what subdirectories to search
121 * EX: mkdir /tmp/foo; touch /tmp/foo/[baz, gorp]
122 * uuxqt -s/tmp/foo
123 * this will cause uuxqt to only run on the sub
124 * baz and gorp in the Spool directory. Trust me.
125 */
126 (void) strlcpy(dirname, optarg,
127 (MAXFULLNAME - sizeof (SEQLOCK)));
128 break;
129
130 default:
131 (void) fprintf(stderr, "%s %s %s\n",
132 gettext(USAGEPREFIX), Progname, gettext(USAGE));
133 exit(1);
134 }
135 }
136 if (argc != optind) {
137 (void) fprintf(stderr, "%s %s %s\n",
138 gettext(USAGEPREFIX), Progname, gettext(USAGE));
139 exit(1);
140 }
141
142 DEBUG(4, "\n\n** START **\n%s", "");
143 acInit("rexe");
144 scInit("rexe");
145 if (scanlimit("uuxqt", &limitval) == FAIL) {
146 DEBUG(1, "No limits for uuxqt in %s\n", LIMITS);
147 } else {
148 maxnumb = limitval.totalmax;
149 if (maxnumb < 0) {
150 DEBUG(4, "Non-positive limit for uuxqt in %s\n", LIMITS);
151 DEBUG(1, "No limits for uuxqt\n%s", "");
152 } else {
153 DEBUG(4, "Uuxqt limit %d -- ", maxnumb);
154 ret = cuantos(X_LOCKPRE, X_LOCKDIR);
155 DEBUG(4, "found %d -- ", ret);
156 if (maxnumb >= 0 && ret >= maxnumb) {
157 DEBUG(4, "exiting.%s\n", "");
158 exit(0);
159 }
160 DEBUG(4, "continuing.%s\n", "");
161 }
162 }
163
164 /*
165 * determine user who started uuxqt (in principle)
166 */
167 strcpy(User, "uucp"); /* in case all else fails (can't happen) */
168 Uid = getuid();
169 Euid = geteuid(); /* this should be UUCPUID */
170 guinfo(Euid, User);
171
172 if (Uid == 0)
173 (void) setuid(UUCPUID);
174
175 setuucp(User);
176 DEBUG(4, "User - %s\n", User);
177 guinfo(Uid, Loginuser);
178
179
180
181 DEBUG(4, "process\n%s", "");
182
183 fp1 = opendir(Spool);
184 ASSERT(fp1 != NULL, Ct_OPEN, Spool, errno);
185 if (dirname[0] != NULLCHAR) {
186 /* look for special characters in remote name */
187 if (strpbrk(dirname, Shchar) != NULL) {
188 /* ignore naughty name */
189 DEBUG(4, "Bad remote name '%s'", dirname);
190 errent("BAD REMOTE NAME", dirname, 0, __FILE__, __LINE__);
191 closedir(fp1);
192 cleanup(101);
193 }
194
195
196 (void) snprintf(lockname, sizeof (lockname), "%s.%s",
197 X_LOCK, dirname);
198 if (mklock(lockname) == SUCCESS) {
199 xprocess(dirname);
200 rmlock(CNULL);
201 }
202 } else {
203 while (gdirf(fp1, dirname, Spool) == TRUE) {
204 if (strpbrk(dirname, Shchar) != NULL) {
205 /* skip naughty names */
206 errent("BAD REMOTE NAME", dirname, 0,
207 __FILE__, __LINE__);
208 continue;
209 }
210 (void) snprintf(lockname, sizeof (lockname), "%s.%s",
211 X_LOCK, dirname);
212 if (mklock(lockname) != SUCCESS)
213 continue;
214 xprocess(dirname);
215 rmlock(CNULL);
216 }
217 }
218
219 closedir(fp1);
220 cleanup(0);
221 /* NOTREACHED */
222 return (0);
223 }
224
225 void
cleanup(code)226 cleanup(code)
227 int code;
228 {
229 rmlock(CNULL);
230 exit(code);
231 }
232
233 /*
234 * catch signal then cleanup and exit
235 */
236 void
onintr(inter)237 onintr(inter)
238 int inter;
239 {
240 char str[30];
241 (void) signal(inter, SIG_IGN);
242 (void) sprintf(str, "QSIGNAL %d", inter);
243 logent(str, "QCAUGHT");
244 acEndexe(cpucycle(), PARTIAL); /* stop collecting accounting log */
245 cleanup(-inter);
246 }
247
248 #define XCACHESIZE (4096 / (MAXBASENAME + 1))
249 static char xcache[XCACHESIZE][MAXBASENAME + 1]; /* cache for X. files */
250 static int xcachesize = 0; /* how many left? */
251
252 /*
253 * stash an X. file so we can process them sorted first by grade, then by
254 * sequence number
255 */
256 static void
xstash(file)257 xstash(file)
258 char *file;
259 {
260 if (xcachesize < XCACHESIZE) {
261 DEBUG(4, "stashing %s\n", file);
262 (void) strlcpy(xcache[xcachesize++], file, (MAXBASENAME + 1));
263 }
264 }
265
266 /*
267 * xcompare
268 * comparison routine for for qsort()
269 */
270 static int
xcompare(f1,f2)271 xcompare(f1, f2)
272 const void *f1, *f2;
273 {
274 /* assumes file name is X.siteG1234 */
275 /* use -strcmp() so that xstash is sorted largest first */
276 /* pull files out of the stash from largest index to smallest */
277
278 return (-strcmp((char *)f1 + strlen((char *)f1) - 5,
279 (char *)f2 + strlen((char *)f2) - 5));
280 }
281
282 /*
283 * xsort
284 * sort the cached X. files,
285 * largest (last) to smallest (next to be processed)
286 */
287 static void
xsort()288 xsort()
289 {
290 DEBUG(4, "xsort: first was %s\n", xcache[0]);
291 qsort(xcache, xcachesize, MAXBASENAME + 1, xcompare);
292 DEBUG(4, "xsort: first is %s\n", xcache[0]);
293 }
294
295 /*
296 * xget
297 * return smallest X. file in cache
298 * (hint: it's the last one in the array)
299 */
300 static int
xget(file)301 xget(file)
302 char *file;
303 {
304 if (xcachesize > 0) {
305 strlcpy(file, xcache[--xcachesize], (MAXBASENAME + 1));
306 DEBUG(4, "xget: returning %s\n", file);
307 return (1);
308 } else {
309 /* avoid horror of xcachesize < 0 (impossible, you say?)! */
310 xcachesize = 0;
311 return (0);
312 }
313 }
314
315
316 /*
317 * get a file to execute
318 * file -> a read to return filename in
319 * returns:
320 * 0 -> no file
321 * 1 -> file to execute
322 */
323 int
gt_Xfile(file,dir)324 gt_Xfile(file, dir)
325 char *file, *dir;
326 {
327 DIR *pdir;
328
329 if (xcachesize == 0) {
330 /* open spool directory */
331 pdir = opendir(dir);
332 /* this was an ASSERT, but it's not so bad as all that */
333 if (pdir == NULL)
334 return (0);
335
336 /* scan spool directory looking for X. files to stash */
337 while (gnamef(pdir, file) == TRUE) {
338 DEBUG(4, "gt_Xfile got %s\n", file);
339 /* look for x prefix */
340 if (file[0] != XQTPRE)
341 continue;
342
343 /* check to see if required files have arrived */
344 if (gotfiles(file))
345 xstash(file);
346 if (xcachesize >= XCACHESIZE)
347 break;
348 }
349 closedir(pdir);
350 xsort();
351 }
352
353 return (xget(file));
354 }
355
356 /*
357 * check for needed files
358 * file -> name of file to check
359 * return:
360 * 0 -> not ready
361 * 1 -> all files ready
362 */
363 int
gotfiles(file)364 gotfiles(file)
365 char *file;
366 {
367 FILE *fp;
368 struct stat stbuf;
369 char buf[BUFSIZ], rqfile[MAXNAMESIZE];
370
371 fp = fopen(file, "r");
372 if (fp == NULL)
373 return (FALSE);
374
375 while (fgets(buf, BUFSIZ, fp) != NULL) {
376 DEBUG(4, "%s\n", buf);
377
378 /*
379 * look at required files
380 */
381 if (buf[0] != X_RQDFILE)
382 continue;
383 (void) sscanf(&buf[1], "%63s", rqfile);
384
385 /*
386 * expand file name
387 */
388 expfile(rqfile);
389
390 /*
391 * see if file exists
392 */
393 if (stat(rqfile, &stbuf) == -1) {
394 fclose(fp);
395 return (FALSE);
396 }
397 }
398
399 fclose(fp);
400 return (TRUE);
401 }
402
403 /*
404 * remove execute files to x-directory
405 *
406 * _Xfile is a global
407 * return:
408 * none
409 */
410 void
rm_Xfiles()411 rm_Xfiles()
412 {
413 FILE *fp;
414 char buf[BUFSIZ], file[MAXNAMESIZE], tfile[MAXNAMESIZE];
415 char tfull[MAXFULLNAME];
416
417 if ((fp = fopen(_Xfile, "r")) == NULL) {
418 DEBUG(4, "rm_Xfiles: can't read %s\n", _Xfile);
419 return;
420 }
421
422 /*
423 * (void) unlink each file belonging to job
424 */
425 while (fgets(buf, BUFSIZ, fp) != NULL) {
426 if (buf[0] != X_RQDFILE)
427 continue;
428 if (sscanf(&buf[1], "%63s%63s", file, tfile) < 2)
429 continue;
430 (void) snprintf(tfull, sizeof (tfull), "%s/%s", XQTDIR, tfile);
431 (void) unlink(tfull);
432 }
433 fclose(fp);
434 }
435
436 /*
437 * move execute files to x-directory
438 * _Xfile is a global
439 * return:
440 * none
441 */
442 void
mv_Xfiles()443 mv_Xfiles()
444 {
445 FILE *fp;
446 char buf[BUFSIZ], ffile[MAXFULLNAME], tfile[MAXNAMESIZE];
447 char tfull[MAXFULLNAME];
448
449 if ((fp = fopen(_Xfile, "r")) == NULL) {
450 DEBUG(4, "mv_Xfiles: can't read %s\n", _Xfile);
451 return;
452 }
453
454 while (fgets(buf, BUFSIZ, fp) != NULL) {
455 if (buf[0] != X_RQDFILE)
456 continue;
457 if (sscanf(&buf[1], "%63s%63s", ffile, tfile) < 2)
458 continue;
459
460 /*
461 * expand file names and move to
462 * execute directory
463 * Make files readable by anyone
464 */
465 expfile(ffile);
466 (void) snprintf(tfull, sizeof (tfull), "%s/%s", XQTDIR, tfile);
467
468 if (chkpth(ffile, CK_READ) == FAIL)
469 continue; /* execution will fail later */
470 if (chkpth(tfull, CK_WRITE) == FAIL) {
471 /*
472 * tfull will have been canonicalized. If
473 * it still points to XQTDIR, allow us to
474 * write there.
475 */
476 if (!PREFIX(XQTDIR, tfull))
477 continue; /* execution will fail later */
478 /* otherwise, keep going */
479 }
480
481 ASSERT(xmv(ffile, tfull) == 0, "XMV ERROR", tfull, errno);
482 chmod(tfull, PUB_FILEMODE);
483 }
484 fclose(fp);
485 }
486
487 /*
488 * undo what mv_Xfiles did
489 * _Xfile is a global
490 * return:
491 * none
492 */
493 void
unmv_Xfiles()494 unmv_Xfiles()
495 {
496 FILE *fp;
497 char buf[BUFSIZ], ffile[MAXNAMESIZE], tfile[MAXNAMESIZE];
498 char tfull[MAXFULLNAME], ffull[MAXFULLNAME], xfull[MAXFULLNAME];
499
500 (void) snprintf(xfull, MAXFULLNAME, "%s/%s", RemSpool, _Xfile);
501 if ((fp = fopen(xfull, "r")) == NULL) {
502 DEBUG(4, "unmv_Xfiles: can't read %s\n", xfull);
503 return;
504 }
505
506 while (fgets(buf, BUFSIZ, fp) != NULL) {
507 if (buf[0] != X_RQDFILE)
508 continue;
509 if (sscanf(&buf[1], "%63s%63s", ffile, tfile) < 2)
510 continue;
511
512 /*
513 * expand file names and move back to
514 * spool directory
515 * Make files readable by uucp
516 */
517 (void) snprintf(ffull, MAXFULLNAME, "%s/%s", RemSpool, ffile);
518 /* i know we're in .Xqtdir, but ... */
519 (void) snprintf(tfull, MAXFULLNAME, "%s/%s", XQTDIR, tfile);
520
521 if (chkpth(ffull, CK_WRITE) == FAIL ||
522 chkpth(tfull, CK_READ) == FAIL)
523 continue;
524
525 ASSERT(xmv(tfull, ffull) == 0, "XMV ERROR", ffull, errno);
526 (void) chmod(ffull, (mode_t)0600);
527 }
528 fclose(fp);
529 }
530
531 /*
532 * chkpart - checks the string (ptr points to it) for illegal command or
533 * file permission restriction - called recursively
534 * to check lines that have `string` or (string) form.
535 * _Cmd is the buffer where the command is built up.
536 * _CargType is the type of the next C line argument
537 *
538 * Return:
539 * BAD_FILE if a non permitted file is found
540 * BAD_COMMAND if non permitted command is found
541 * 0 - ok
542 */
543
544 static int
chkpart(char * ptr)545 chkpart(char *ptr)
546 {
547 char prm[BUFSIZ], whitesp[BUFSIZ], rqtcmd[BUFSIZ], xcmd[BUFSIZ];
548 char savechar[2]; /* one character string with NULL */
549 int ret;
550
551 /* _CargType is the arg type for this iteration (cmd or file) */
552 while ((ptr = getprm(ptr, whitesp, prm)) != NULL) {
553 DEBUG(4, "prm='%s'\n", prm);
554 switch (*prm) {
555
556 /* End of command delimiter */
557 case ';':
558 case '^':
559 case '&':
560 case '|':
561 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
562 (void) strlcat(_Cmd, prm, sizeof (_Cmd));
563 _CargType = C_COMMAND;
564 continue;
565
566 /* Other delimiter */
567 case '>':
568 case '<':
569 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
570 (void) strlcat(_Cmd, prm, sizeof (_Cmd));
571 continue;
572
573 case '`': /* don't allow any ` commands */
574 case '\\':
575 return (BAD_COMMAND);
576
577 /* Some allowable quoted string */
578 case '(':
579 case '"':
580 case '\'':
581 /* must recurse */
582 savechar[0] = *prm;
583 savechar[1] = NULLCHAR;
584 /* put leading white space & first char into command */
585 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
586 (void) strlcat(_Cmd, savechar, sizeof (_Cmd));
587 savechar[0] = prm[strlen(prm)-1];
588 prm[strlen(prm)-1] = NULLCHAR; /* delete last character */
589
590 /* recurse */
591 if (ret = chkpart(prm+1)) { /* failed */
592 return (ret);
593 }
594 /* put last char into command */
595 (void) strlcat(_Cmd, savechar, sizeof (_Cmd));
596 continue;
597
598 case '2':
599 if (*(prm+1) == '>') {
600 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
601 (void) strlcat(_Cmd, prm, sizeof (_Cmd));
602 continue;
603 }
604 /* fall through if not "2>" */
605
606 default: /* check for command or file */
607 break;
608 }
609
610 if (_CargType == C_COMMAND) {
611 (void) strlcpy(rqtcmd, prm, sizeof (rqtcmd));
612 if (*rqtcmd == '~')
613 expfile(rqtcmd);
614 if ((cmdOK(rqtcmd, xcmd)) == FALSE)
615 return (BAD_COMMAND);
616 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
617 (void) strlcat(_Cmd, xcmd, sizeof (_Cmd));
618 _CargType = C_FILE;
619 continue;
620 }
621
622 (void) strlcpy(rqtcmd, prm, sizeof (rqtcmd));
623 if (*rqtcmd == '~')
624 expfile(rqtcmd);
625 if (chkFile(rqtcmd)) {
626 return (BAD_FILE);
627 } else {
628 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
629 (void) strlcat(_Cmd, rqtcmd, sizeof (_Cmd));
630 }
631 }
632 if (whitesp[0] != '\0')
633 /* restore any trailing white space */
634 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
635 return (0); /* all ok */
636 }
637
638 /*
639 * chkFile - try to find a path name in the prm.
640 * if found, check it for access permission.
641 *
642 * check file access permissions
643 * if ! in name assume that access on local machine is required
644 *
645 * Return:
646 * BAD_FILE - not permitted
647 * 0 - ok
648 */
649
650 static int
chkFile(char * prm)651 chkFile(char *prm)
652 {
653 char *p, buf[BUFSIZ];
654
655 (void) strlcpy(buf, prm, sizeof (buf));
656 switch (*prm) {
657 case '~':
658 case '/':
659 if (doFileChk(buf))
660 return (BAD_FILE);
661 else
662 return (0);
663 /*NOTREACHED*/
664
665 case '!':
666 return (chkFile(buf+1));
667 /*NOTREACHED*/
668
669 default:
670 break;
671 }
672
673 if ((p = strchr(buf, '!')) == NULL) { /* no "!", look for "/" */
674 if ((p = strchr(buf, '/')) == NULL) { /* ok */
675 return (0);
676 }
677 if (doFileChk(p))
678 return (BAD_FILE);
679 else
680 return (0);
681 }
682
683 /* there is at least one '!' - see if it refers to my system */
684 if (PREFIX(Myname, buf)) /* my system so far, check further */
685 return (chkFile(p+1)); /* recurse with thing after '!' */
686 else /* not my system - not my worry */
687 return (0);
688 }
689
690 /*
691 * doFileChk - check file path permission
692 * NOTE: file is assumed to be a buffer that expfile an
693 * write into.
694 * Return
695 * BAD_FILE - not allowed
696 * 0 - ok
697 */
698
699 static int
doFileChk(char * file)700 doFileChk(char *file)
701 {
702 expfile(file);
703 DEBUG(7, "fullname: %s\n", file);
704 if (chkpth(file, CK_READ) == FAIL ||
705 chkpth(file, CK_WRITE) == FAIL)
706 return (BAD_FILE);
707 else
708 return (0);
709 }
710
711
712 /*
713 * return stuff to user
714 * user -> user to notify
715 * rmt -> system name where user resides
716 * file -> file to return (generally contains input)
717 * cmd -> command that was to be executed
718 * buf -> user friendly face saving uplifting edifying missive
719 * errfile -> stderr output from cmd xeqn
720 * return:
721 * none
722 */
723 static void
retosndr(user,rmt,file,cmd,buf,errfile)724 retosndr(user, rmt, file, cmd, buf, errfile)
725 char *user, *rmt, *file, *cmd, *buf, *errfile;
726 {
727 char ruser[BUFSIZ], msg[BUFSIZ], subj[BUFSIZ];
728
729 (void) snprintf(msg, sizeof (msg), "%s\t[%s %s (%s)]\n\t%s\n%s\n",
730 gettext("remote execution"), gettext("uucp job"),
731 *Jobid ? Jobid : &_Xfile[2], timeStamp(), cmd, buf);
732
733 DEBUG(5, "retosndr %s, ", msg);
734
735 if (EQUALS(rmt, Myname))
736 (void) strlcpy(ruser, user, sizeof (ruser));
737 else
738 (void) snprintf(ruser, sizeof (ruser), "%s!%s", rmt, user);
739
740 (void) strlcpy(subj, gettext("remote execution status"), sizeof (subj));
741 mailst(ruser, subj, msg, file, errfile);
742 }
743
744
745 /*
746 * uucpst - send the status message back using a uucp command
747 * NOTE - this would be better if the file could be appended.
748 * - suggestion for the future - if rmail would take a file name
749 * instead of just person, then that facility would be correct,
750 * and this routine would not be needed.
751 */
752
753 static void
uucpst(rmt,tofile,errfile,cmd,buf)754 uucpst(rmt, tofile, errfile, cmd, buf)
755 char *rmt, *tofile, *errfile, *cmd, *buf;
756 {
757 char arg[MAXFULLNAME], tmp[NAMESIZE], msg[BUFSIZ];
758 pid_t pid, ret;
759 int status;
760 FILE *fp, *fi;
761
762 (void) snprintf(msg, sizeof (msg), "%s %s (%s) %s\n\t%s\n%s\n",
763 gettext("uucp job"), *Jobid ? Jobid : &_Xfile[2], timeStamp(),
764 gettext("remote execution"), cmd, buf);
765
766 (void) snprintf(tmp, sizeof (tmp), "%s.%ld", rmt, (long)getpid());
767 if ((fp = fopen(tmp, "w")) == NULL)
768 return;
769 (void) fprintf(fp, "%s\n", msg);
770
771 /* copy back stderr */
772 if (*errfile != '\0' && NOTEMPTY(errfile) &&
773 (fi = fopen(errfile, "r")) != NULL) {
774 fputs("\n\t===== stderr was =====\n", fp);
775 if (xfappend(fi, fp) != SUCCESS)
776 fputs("\n\t===== well, i tried =====\n", fp);
777 (void) fclose(fi);
778 fputc('\n', fp);
779 }
780
781
782 (void) fclose(fp);
783 (void) snprintf(arg, sizeof (arg), "%s!%s", rmt, tofile);
784
785 /* start uucp */
786
787 if ((pid = vfork()) == 0) {
788 (void) close(0);
789 (void) close(1);
790 (void) close(2);
791 (void) open("/dev/null", 2);
792 (void) open("/dev/null", 2);
793 (void) open("/dev/null", 2);
794 (void) signal(SIGINT, SIG_IGN);
795 (void) signal(SIGHUP, SIG_IGN);
796 (void) signal(SIGQUIT, SIG_IGN);
797 ucloselog();
798
799 (void) execle("/usr/bin/uucp", "UUCP",
800 "-C", tmp, arg, (char *)0, Env);
801 _exit(100);
802 }
803
804 if (pid == -1)
805 return;
806
807 while ((ret = wait(&status)) != pid)
808 if (ret == -1 && errno != EINTR)
809 break;
810
811 (void) unlink(tmp);
812 }
813
814 void
xprocess(dirname)815 xprocess(dirname)
816 char *dirname;
817 {
818 char fdgrade(); /* returns default service grade on system */
819 int return_stdin; /* return stdin for failed commands */
820 int cmdok, ret, badfiles;
821 mode_t mask;
822 int send_zero; /* return successful completion status */
823 int send_nonzero; /* return unsuccessful completion status */
824 int send_nothing; /* request for no exit status */
825 int store_status; /* store status of command in local file */
826 char lbuf[BUFSIZ];
827 char dqueue; /* var to hold the default service grade */
828 char *errname = ""; /* name of local stderr output file */
829 char *p;
830 char sendsys[MAXNAMESIZE];
831 char dfile[MAXFULLNAME], cfile[MAXFULLNAME], incmd[BUFSIZ];
832 char errDfile[BUFSIZ];
833 char fin[MAXFULLNAME];
834 char fout[MAXFULLNAME], sysout[NAMESIZE];
835 char ferr[MAXFULLNAME], syserr[NAMESIZE];
836 char file[MAXFULLNAME], tempname[NAMESIZE];
837 char _Sfile[MAXFULLNAME]; /* name of local file for status */
838 FILE *xfp, *fp;
839 struct stat sb;
840 char buf[BUFSIZ], user[BUFSIZ], retaddr[BUFSIZ], retuser[BUFSIZ],
841 msgbuf[BUFSIZ];
842 char origsys[MAXFULLNAME], origuser[MAXFULLNAME];
843
844 (void) strlcpy(Rmtname, dirname, sizeof (Rmtname));
845 chremdir(Rmtname);
846 (void) mchFind(Rmtname);
847 while (gt_Xfile(_Xfile, RemSpool) > 0) {
848 DEBUG(4, "_Xfile - %s\n", _Xfile);
849
850 if ((xfp = fopen(_Xfile, "r")) == NULL) {
851 toCorrupt(_Xfile);
852 continue;
853 }
854 ASSERT(xfp != NULL, Ct_OPEN, _Xfile, errno);
855
856 if (stat(_Xfile, &sb) != -1)
857 Nstat.t_qtime = sb.st_mtime;
858 /*
859 * initialize to defaults
860 */
861 (void) strlcpy(user, User, sizeof (user));
862 (void) strcpy(fin, "/dev/null");
863 (void) strcpy(fout, "/dev/null");
864 (void) strcpy(ferr, "/dev/null");
865 (void) sprintf(sysout, "%.*s", MAXBASENAME, Myname);
866 (void) sprintf(syserr, "%.*s", MAXBASENAME, Myname);
867 badfiles = 0;
868 *incmd = *retaddr = *retuser = *Jobid = NULLCHAR;
869 initSeq();
870 send_zero = send_nonzero = send_nothing = 0;
871 store_status = 0;
872 return_stdin = 0;
873
874 while (fgets(buf, BUFSIZ, xfp) != NULL) {
875 /*
876 * interpret JCL card
877 */
878 switch (buf[0]) {
879 case X_USER:
880 /*
881 * user name
882 * (ignore Rmtname)
883 * The utmpx username field is 32 characters long;
884 * UUCP usage truncates system name to 14 bytes.
885 */
886 (void) sscanf(&buf[1], "%32s%14s", user, origsys);
887 (void) strlcpy(origuser, user, sizeof (origuser));
888 break;
889
890 case X_STDIN:
891 /*
892 * standard input
893 */
894 (void) sscanf(&buf[1], "%256s", fin);
895 expfile(fin);
896 if (chkpth(fin, CK_READ)) {
897 DEBUG(4, "badfile - in: %s\n", fin);
898 badfiles = 1;
899 }
900 break;
901
902 case X_STDOUT:
903 /*
904 * standard output
905 */
906 (void) sscanf(&buf[1], "%256s%14s", fout, sysout);
907 if ((p = strpbrk(sysout, "!/")) != NULL)
908 *p = NULLCHAR; /* these are dangerous */
909 if (*sysout != NULLCHAR && !EQUALS(sysout, Myname))
910 break;
911
912 expfile(fout);
913 if (chkpth(fout, CK_WRITE)) {
914 badfiles = 1;
915 DEBUG(4, "badfile - out: %s\n", fout);
916 }
917 break;
918
919 case X_STDERR: /* standard error */
920 (void) sscanf(&buf[1], "%256s%14s", ferr, syserr);
921 if ((p = strpbrk(syserr, "!/")) != NULL)
922 *p = NULLCHAR; /* these are dangerous */
923 if (*syserr != NULLCHAR && !EQUALS(syserr, Myname))
924 break;
925
926 expfile(ferr);
927 if (chkpth(ferr, CK_WRITE)) {
928 badfiles = 1;
929 DEBUG(4, "badfile - error: %s\n", ferr);
930 }
931 break;
932
933
934 case X_CMD: /* command to execute */
935 (void) strlcpy(incmd, &buf[2], sizeof (incmd));
936 if (*(incmd + strlen(incmd) - 1) == '\n')
937 *(incmd + strlen(incmd) - 1) = NULLCHAR;
938 break;
939
940 case X_MAILF: /* put status in _Sfile */
941 store_status = 1;
942 (void) sscanf(&buf[1], "%256s", _Sfile);
943 break;
944
945 case X_SENDNOTHING: /* no failure notification */
946 send_nothing++;
947 break;
948
949 case X_SENDZERO: /* success notification */
950 send_zero++;
951 break;
952
953 case X_NONZERO: /* failure notification */
954 send_nonzero++;
955 break;
956
957 case X_BRINGBACK: /* return stdin on command failure */
958 return_stdin = 1;
959 break;
960
961
962 case X_RETADDR:
963 /*
964 * return address -- is user's name
965 * put "Rmtname!" in front of it so mail
966 * will always get back to remote system.
967 */
968 (void) sscanf(&buf[1], "%s", retuser);
969
970 /*
971 * Creates string of Rmtname!Rmtname!user which
972 * confuses rmail.
973 * (void) strcat(strcat(strcpy(retaddr, Rmtname), "!"),
974 * retuser);
975 */
976 break;
977
978 case X_JOBID:
979 /*
980 * job id for notification
981 * (should be MAXBASENAME, not 14, but no can do)
982 */
983 (void) sscanf(&buf[1], "%14s", Jobid);
984 break;
985
986 default:
987 break;
988 }
989 }
990
991 fclose(xfp);
992 DEBUG(4, "fin - %s, ", fin);
993 DEBUG(4, "fout - %s, ", fout);
994 DEBUG(4, "ferr - %s, ", ferr);
995 DEBUG(4, "sysout - %s, ", sysout);
996 DEBUG(4, "syserr - %s, ", syserr);
997 DEBUG(4, "user - %s\n", user);
998 DEBUG(4, "incmd - %s\n", incmd);
999
1000 scRexe(origsys, origuser, Loginuser, incmd);
1001
1002 if (retuser[0] != NULLCHAR)
1003 (void) strlcpy(user, retuser, sizeof (user)); /* pick on this guy */
1004
1005 /* get rid of stuff that can be dangerous */
1006 if ((p = strpbrk(user, Shchar)) != NULL) {
1007 *p = NULLCHAR;
1008 }
1009
1010 if (incmd[0] == NULLCHAR) {
1011 /* this is a bad X. file - just get rid of it */
1012 toCorrupt(_Xfile);
1013 continue;
1014 }
1015
1016 /*
1017 * send_nothing must be explicitly requested to avert failure status
1018 * send_zero must be explicitly requested for success notification
1019 */
1020 if (!send_nothing)
1021 send_nonzero++;
1022
1023 /*
1024 * command execution
1025 */
1026
1027 /*
1028 * generate a temporary file (if necessary)
1029 * to hold output to be shipped back
1030 */
1031 if (EQUALS(fout, "/dev/null"))
1032 (void) strcpy(dfile, "/dev/null");
1033 else {
1034 gename(DATAPRE, sysout, 'O', tempname);
1035 (void) snprintf(dfile, sizeof (dfile), "%s/%s", WORKSPACE,
1036 tempname);
1037 }
1038
1039 /*
1040 * generate a temporary file (if necessary)
1041 * to hold errors to be shipped back
1042 */
1043 /*
1044 * This is what really should be done. However for compatibility
1045 * for the interim at least, we will always create temp file
1046 * so we can return error output. If this temp file IS conditionally
1047 * created, we must remove the unlink() of errDfile at the end
1048 * because it may REALLY be /dev/null.
1049 * if (EQUALS(ferr, "/dev/null"))
1050 * (void) strcpy(errDfile, "/dev/null");
1051 * else {
1052 */
1053 gename(DATAPRE, syserr, 'E', tempname);
1054 (void) snprintf(errDfile, sizeof (errDfile), "%s/%s",
1055 WORKSPACE, tempname);
1056 /*
1057 * }
1058 */
1059
1060 /* initialize command line */
1061 /* set up two environment variables, remote machine name */
1062 /* and remote user name if available from R line */
1063 /*
1064 * xcu4 requires that uucp *does* expand wildcards and uux *does not*
1065 * expand wild cards... Further restrictions are that uux must work
1066 * with every other uucp / uux that initiated a request, so nothing
1067 * strange can been done to communicate that it was uucp that sent
1068 * the request and not uux, What we settle on here is looking for
1069 * the command name uucp and expanding wildcards in only that case.
1070 * It is true that a user can spoof this using uux, but in reality
1071 * this would be identical to using the uucp command to start with.
1072 */
1073 if (strncmp(incmd, "uucp ", 5) == 0) {
1074 (void) snprintf(_Cmd, sizeof (_Cmd),
1075 "%s %s UU_MACHINE=%s UU_USER=%s "
1076 " export UU_MACHINE UU_USER PATH; ",
1077 PATH, LOGNAME, Rmtname, user);
1078 } else {
1079 (void) snprintf(_Cmd, sizeof (_Cmd),
1080 "%s %s UU_MACHINE=%s UU_USER=%s "
1081 " export UU_MACHINE UU_USER PATH; set -f; ",
1082 PATH, LOGNAME, Rmtname, user);
1083 }
1084
1085 /*
1086 * check to see if command can be executed
1087 */
1088 _CargType = C_COMMAND; /* the first thing is a command */
1089 cmdok = chkpart(incmd);
1090
1091 if (badfiles || (cmdok == BAD_COMMAND) || cmdok == BAD_FILE) {
1092 if (cmdok == BAD_COMMAND) {
1093 (void) snprintf(lbuf, sizeof (lbuf), "%s!%s XQT DENIED",
1094 Rmtname, user);
1095 (void) snprintf(msgbuf, sizeof (msgbuf),
1096 "execution permission denied to %s!%s", Rmtname, user);
1097 } else {
1098 (void) snprintf(lbuf, sizeof (lbuf),
1099 "%s!%s XQT - STDIN/STDOUT/FILE ACCESS DENIED",
1100 Rmtname, user);
1101 (void) snprintf(msgbuf, sizeof (msgbuf),
1102 "file access denied to %s!%s", Rmtname, user);
1103 }
1104 logent(incmd, lbuf);
1105 DEBUG(4, "bad command %s\n", incmd);
1106
1107 scWlog(); /* log security vialotion */
1108
1109 if (send_nonzero)
1110 retosndr(user, Rmtname, return_stdin ? fin : "",
1111 incmd, msgbuf, "");
1112 if (store_status)
1113 uucpst(Rmtname, _Sfile, "", incmd, msgbuf);
1114 goto rmfiles;
1115 }
1116
1117 (void) snprintf(lbuf, sizeof (lbuf), "%s!%s XQT", Rmtname, user);
1118 logent(_Cmd, lbuf);
1119 DEBUG(4, "cmd %s\n", _Cmd);
1120
1121 /* move files to execute directory and change to that directory */
1122
1123 mv_Xfiles();
1124
1125 ASSERT(chdir(XQTDIR) == 0, Ct_CHDIR, XQTDIR, errno);
1126 acRexe(&_Xfile[2], origsys, origuser, Myname, Loginuser, incmd);
1127
1128 /* invoke shell to execute command */
1129
1130 mask = umask(0);
1131 DEBUG(7, "full cmd: %s\n", _Cmd);
1132
1133 cpucycle();
1134 ret = shio(_Cmd, fin, dfile, errDfile);
1135 if (ret == 0)
1136 acEndexe(cpucycle(), COMPLETE);
1137 else
1138 acEndexe(cpucycle(), PARTIAL);
1139
1140 umask(mask);
1141 if (ret == -1) { /* -1 means the fork() failed */
1142 unmv_Xfiles(); /* put things back */
1143 errent(Ct_FORK, buf, errno, __FILE__, __LINE__);
1144 cleanup(1);
1145 }
1146
1147 if (ret == 0) { /* exit == signal == 0 */
1148 (void) strcpy(msgbuf, "exited normally");
1149 } else { /* exit != 0 */
1150 int exitcode = (ret >> 8) & 0377;
1151
1152 if (exitcode) {
1153 /* exit != 0 */
1154 (void) snprintf(msgbuf, sizeof (msgbuf),
1155 "exited with status %d", exitcode);
1156 } else {
1157 /* signal != 0 */
1158 (void) snprintf(msgbuf, sizeof (msgbuf),
1159 "terminated by signal %d", ret & 0177);
1160 }
1161 DEBUG(5, "%s\n", msgbuf);
1162 (void) snprintf(lbuf, sizeof (lbuf), "%s - %s", incmd, msgbuf);
1163 logent(lbuf, "COMMAND FAIL");
1164 }
1165
1166 /* change back to spool directory */
1167
1168 chremdir(Rmtname);
1169
1170 /* remove file */
1171
1172 rm_Xfiles();
1173
1174 /*
1175 * We used to append stderr to stdout. Since stderr can
1176 * now be specified separately, never append it to stdout.
1177 * It can still be gotten via -s status file option.
1178 */
1179
1180 if (!EQUALS(fout, "/dev/null")) {
1181 /*
1182 * if output is on this machine copy output
1183 * there, otherwise spawn job to send to send
1184 * output elsewhere.
1185 */
1186
1187 if (EQUALS(sysout, Myname)) {
1188 if ((xmv(dfile, fout)) != 0) {
1189 logent("FAILED", "COPY");
1190 scWrite();
1191 (void) snprintf(msgbuf + strlen(msgbuf),
1192 (sizeof (msgbuf) - strlen(msgbuf)),
1193 "\nCould not move stdout to %s,", fout);
1194 if (putinpub(fout, dfile, origuser) == 0)
1195 (void) snprintf(msgbuf + strlen(msgbuf),
1196 (sizeof (msgbuf) - strlen(msgbuf)),
1197 "\n\tstdout left in %s.", fout);
1198 else
1199 (void) strlcat(msgbuf, " stdout lost.",
1200 sizeof (msgbuf));
1201 }
1202 } else {
1203 char *bname;
1204
1205 if (eaccess(GRADES, 04) != -1)
1206 dqueue = fdgrade();
1207 else
1208 dqueue = Grade;
1209 gename(CMDPRE, sysout, dqueue, tempname);
1210 (void) snprintf(cfile, sizeof (cfile), "%s/%s",
1211 WORKSPACE, tempname);
1212 fp = fdopen(ret = creat(cfile, CFILEMODE), "w");
1213 ASSERT(ret >= 0 && fp != NULL, Ct_OPEN, cfile, errno);
1214 bname = BASENAME(dfile, '/');
1215 (void) fprintf(fp, "S %s %s %s -d %s 0666\n",
1216 bname, fout, user, bname);
1217 fclose(fp);
1218 (void) snprintf(sendsys, sizeof (sendsys), "%s/%c", sysout,
1219 dqueue);
1220 sendsys[MAXNAMESIZE-1] = '\0';
1221 wfcommit(dfile, BASENAME(dfile, '/'), sendsys);
1222 wfcommit(cfile, BASENAME(cfile, '/'), sendsys);
1223 }
1224 }
1225 if (!EQUALS(ferr, "/dev/null")) {
1226 /*
1227 * if stderr is on this machine copy output
1228 * there, otherwise spawn job to send to send
1229 * it elsewhere.
1230 */
1231 if (EQUALS(syserr, Myname)) {
1232 errname = ferr;
1233 if ((xmv(errDfile, ferr)) != 0) {
1234 logent("FAILED", "COPY");
1235 scWrite();
1236 (void) snprintf(msgbuf + strlen(msgbuf),
1237 (sizeof (msgbuf) - strlen(msgbuf)),
1238 "\nCould not move stderr to %s,", ferr);
1239 if (putinpub(ferr, errDfile, origuser) == 0) {
1240 (void) snprintf(msgbuf+strlen(msgbuf),
1241 (sizeof (msgbuf) - strlen(msgbuf)),
1242 "\n\tstderr left in %s", ferr);
1243 } else {
1244 errname = errDfile;
1245 (void) strlcat(msgbuf, " stderr lost.",
1246 sizeof (msgbuf));
1247 }
1248 }
1249 } else {
1250 char *bname;
1251
1252 if (eaccess(GRADES, 04) != -1)
1253 dqueue = fdgrade();
1254 else
1255 dqueue = Grade;
1256 gename(CMDPRE, syserr, dqueue, tempname);
1257 (void) snprintf(cfile, sizeof (cfile), "%s/%s",
1258 WORKSPACE, tempname);
1259 fp = fdopen(ret = creat(cfile, CFILEMODE), "w");
1260 ASSERT(ret >= 0 && fp != NULL, Ct_OPEN, cfile, errno);
1261 bname = BASENAME(errDfile, '/');
1262 (void) fprintf(fp, "S %s %s %s -d %s 0666\n",
1263 bname, ferr, user, bname);
1264 fclose(fp);
1265 (void) snprintf(sendsys, sizeof (sendsys), "%s/%c",
1266 syserr, dqueue);
1267 sendsys[MAXNAMESIZE-1] = '\0';
1268 wfcommit(errDfile, BASENAME(errDfile, '/'), sendsys);
1269 wfcommit(cfile, BASENAME(cfile, '/'), sendsys);
1270 }
1271 } else {
1272 /*
1273 * If we conditionally create stderr tempfile, we must
1274 * remove this unlink() since errDfile may REALLY be /dev/null
1275 */
1276 unlink(errDfile);
1277 }
1278
1279 if (ret == 0) {
1280 if (send_zero)
1281 retosndr(user, Rmtname, "", incmd, msgbuf, "");
1282 if (store_status)
1283 uucpst(Rmtname, _Sfile, "", incmd, msgbuf);
1284 } else {
1285 if (send_nonzero)
1286 retosndr(user, Rmtname, return_stdin ? fin : "",
1287 incmd, msgbuf, errname);
1288 if (store_status)
1289 uucpst(Rmtname, _Sfile, errname, incmd, msgbuf);
1290 }
1291
1292 rmfiles:
1293
1294 /* delete job files in spool directory */
1295 xfp = fopen(_Xfile, "r");
1296 ASSERT(xfp != NULL, Ct_OPEN, _Xfile, errno);
1297 while (fgets(buf, BUFSIZ, xfp) != NULL) {
1298 if (buf[0] != X_RQDFILE)
1299 continue;
1300 (void) sscanf(&buf[1], "%63s", file);
1301 expfile(file);
1302 if (chkpth(file, CK_WRITE) != FAIL)
1303 (void) unlink(file);
1304 }
1305 (void) unlink(_Xfile);
1306 fclose(xfp);
1307 }
1308 }
1309