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