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