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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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