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