home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 300-399 / ff319.lzh / CNewsSrc / cnews.orig.lzh / relay / relaynews.c < prev    next >
C/C++ Source or Header  |  1989-06-27  |  12KB  |  468 lines

  1. /*
  2.  * relaynews - relay Usenet news (version C)
  3.  * See the file COPYRIGHT for the copyright notice.
  4.  *
  5.  * relaynews should be setuid-news, setgid-news.  You'll need to install
  6.  * setnewsids setuid-root if setuid(geteuid()) doesn't work on your
  7.  * machine (e.g. on V7 and possibly SystemIII).
  8.  *
  9.  * Written by Geoff Collyer, 15-20 November 1985 and revised periodically
  10.  * since.
  11.  *
  12.  * relaynews parses article headers, rejects articles by newsgroup &
  13.  * message-id, files articles, updates the active & history files,
  14.  * transmits articles, and honours (infrequent) control messages, which do
  15.  * all sorts of varied and rococo things.  Control messages are implemented
  16.  * by separate programs.  relaynews reads a "sys" file to control the
  17.  * transmission of articles but can function as a promiscuous leaf node
  18.  * without one.  See ARPA Internet RFC 1036 nee 850 for the whole story.
  19.  *
  20.  * A truly radical notion: people may over-ride via environment variables
  21.  * the compiled-in default directories so IHCC kludges are not needed and
  22.  * testing is possible (and encouraged) in alternate directories.  This
  23.  * does cause a loss of privilege, to avoid spoofing.
  24.  *
  25.  * The disused old ihave/sendme protocol is dead; it's been broken in
  26.  * B news for ages but no one has noticed because it's essentially
  27.  * useless on the uucp network, especially when batching news articles.
  28.  * It is also only semi-documented, very wasteful and kludgey in the extreme.
  29.  * It is unreasonable to expect an ill-documented protocol to be implemented
  30.  * in new software, particularly one as kludgey as this one.
  31.  * The new ihave sys flag ("I") for NNTP is implemented (trivially).
  32.  *
  33.  * Portability vs SystemV.  relaynews uses dbm(3) and makes no apologies
  34.  * for so doing.  Imitation UNIX (registered trademark of AT&T in the
  35.  * United States) brand operating systems that lack dbm are going to
  36.  * have to use my incredibly slow dbm simulation, or another.
  37.  */
  38.  
  39. #include <stdio.h>
  40. #include <ctype.h>
  41. #include <signal.h>        /* to make locking safe */
  42. #include <sys/types.h>
  43.  
  44. #include "libc.h"
  45. #include "news.h"
  46. #include "config.h"
  47. #include "fgetmfs.h"
  48. #include "active.h"
  49. #include "caches.h"
  50. #include "cpu.h"
  51. #include "fileart.h"
  52. #include "headers.h"
  53. #include "history.h"
  54. #include "transmit.h"
  55.  
  56. /*
  57.  * setuid-root program to set ids to news/news & rexec rnews with
  58.  * NEWSPERMS in the environment to break loops.
  59.  */
  60. #ifndef SETNEWSIDS
  61. #define SETNEWSIDS "setnewsids"
  62. #endif
  63.  
  64. /* exports */
  65. char *progname;
  66. boolean okrefusal = YES;            /* okay to refuse articles? */
  67. char *exclude = NULL;                /* site to exclude, for erik */
  68. boolean histreject = NO;            /* keep history of rejects? */
  69.  
  70. /* internal */
  71. static boolean userealids = NO;
  72.  
  73. /* imports */
  74. extern int optind;            /* set by getopt */
  75. extern char *optarg;
  76. extern statust cpinsart();        /* from procart.c */
  77.  
  78. /* forwards */
  79. extern void prelude(), setids(), procopts(), redirectlogs(), logfile();
  80. extern void getwdandcd();
  81. extern statust procargs(), relnmprocess(), process(), unbatch();
  82. extern boolean batchln();
  83. FORWARD boolean debugon();
  84.  
  85. /*
  86.  * main - take setuid precautions, switch to "news" ids, ignore signals,
  87.  * handle options, lock news system, process files & unlock news system.
  88.  */
  89. int
  90. main(argc, argv)
  91. int argc;
  92. char *argv[];
  93. {
  94.     statust status = ST_OKAY;
  95.     int redirlogs = 0;        /* redirect n std output streams to logs */
  96.     char *origdir = NULL;        /* current directory at start */
  97.  
  98.     progname = argv[0];
  99. #ifdef CSRIMALLOC
  100.     mal_debug(0);    /* was 2; 3 is too slow */
  101.     mal_leaktrace(0);    /* was 1 */
  102. #endif
  103.     prelude(argv);        /* various precautions; switch to "news" */
  104.  
  105.     /* ignore signals (for locking). relaynews runs quickly, so don't worry. */
  106.     (void) signal(SIGINT, (sigarg_t)SIG_IGN);
  107.     (void) signal(SIGQUIT, (sigarg_t)SIG_IGN);
  108.     (void) signal(SIGHUP, (sigarg_t)SIG_IGN);
  109.     (void) signal(SIGTERM, (sigarg_t)SIG_IGN);
  110.  
  111.     procopts(argc, argv, &redirlogs, &okrefusal);
  112.  
  113.     newslock();            /* done here due to dbm internal cacheing */
  114.     if (redirlogs > 0) {
  115.         redirectlogs(redirlogs); /* redirect std output streams to logs */
  116. #ifdef MANYERRORS
  117.         (void) putc('\n', stderr);    /* leave a blank line */
  118.         /* prints "Jun  5 12:34:56" */
  119.         timestamp(stderr, (time_t *)NULL);
  120.         (void) putc('\n', stderr);
  121. #endif
  122.     }
  123.  
  124.     getwdandcd(argc, argv, &origdir);
  125.     status |= procargs(argc, argv, &origdir);
  126.  
  127.     status |= synccaches();        /* being cautious: write & close caches */
  128.     (void) fflush(stdout);        /* log file */
  129.     (void) fflush(stderr);        /* errlog file */
  130.  
  131. #ifdef notdef
  132. #ifdef CSRIMALLOC
  133.     mal_dumpleaktrace(fileno(stderr));
  134. #endif
  135. #endif
  136.     newsunlock();
  137.     exit(status);
  138.     /* NOTREACHED */
  139. }
  140.  
  141. /*
  142.  * reset various environmental things for safety: umask, alarm,
  143.  * environment variables (PATH, IFS), standard file descriptors,
  144.  * user & group ids.
  145.  */
  146. void
  147. prelude(argv)                /* setuid daemon prelude */
  148. char **argv;
  149. {
  150.     register char *newpath;
  151.  
  152.     (void) umask(2);        /* undo silly umasks, ignore newsumask() */
  153.     (void) alarm(0);        /* cancel any pending alarm */
  154.     newpath = malloc(STRLEN("PATH=") + strlen(newspath()) + 1);
  155.     if (newpath == NULL)
  156.         exit(1);        /* no chatter until stdfdopen */
  157.     (void) strcpy(newpath, "PATH=");
  158.     (void) strcat(newpath, newspath());
  159.     if (putenv(newpath) ||
  160.         putenv("IFS= \t\n"))
  161.         exit(1);        /* no chatter until stdfdopen */
  162.     closeall(1);            /* closes all but std descriptors */
  163.     stdfdopen();            /* ensure standard descriptors are open */
  164.     setids(argv);            /* change of real and effective ids */
  165. }
  166.  
  167. /*
  168.  * change real and effective ids to real ids if unprivileged() is called,
  169.  * else to effective ("news") ids.  ctlfile((char *)0) will trigger a call
  170.  * to unprivileged() if any environment variables override the default
  171.  * path names.  unprivileged() in turn sets userealids.
  172.  *
  173.  * If setuid(geteuid()) fails, try execing a small, setuid-root program
  174.  * to just do "getpwnam(), getgrnam() (with NEWSPERMS set), setgid(),
  175.  * setuid()," and exec this program again.  If NEWSPERMS is set,
  176.  * the failure is a fatal error (recursive loop).
  177.  * This program (relaynews) can be setuid-news.
  178.  */
  179. void
  180. setids(argv)
  181. char **argv;
  182. {
  183.     int newsuid, newsgid;
  184.  
  185.     (void) ctlfile((char *)NULL);
  186.     if (userealids)
  187.         newsuid = getuid(), newsgid = getgid();
  188.     else
  189.         newsuid = geteuid(), newsgid = getegid();
  190.     if (setgid(newsgid) < 0 || setuid(newsuid) < 0) {
  191.         if (getenv("NEWSPERMS") != 0)
  192.             error("recursive loop setting ids", "");
  193.         execv(ctlfile(SETNEWSIDS), argv);
  194.         error("can't exec `%s' to set ids", ctlfile(SETNEWSIDS));
  195.         /* NOTREACHED */
  196.     }
  197.     /* we are now running as news, so you can all relax */
  198. }
  199.  
  200. /*
  201.  * parse options and set flags
  202.  */
  203. void
  204. procopts(argc, argv, redirlogsp, okrefusalp)
  205. int argc;
  206. char **argv;
  207. int *redirlogsp;
  208. boolean *okrefusalp;
  209. {
  210.     int c, errflg = 0;
  211.  
  212.     while ((c = getopt(argc, argv, "d:inrsx:")) != EOF)
  213.         switch (c) {
  214.         case 'd':        /* -d debug-options; thanks, henry */
  215.             if (!debugon(optarg))
  216.                 errflg++;    /* debugon has complained */
  217.             break;
  218.         case 'i':        /* redirect stdout to log (inews) */
  219.             *redirlogsp = 1; /* just stdout */
  220.             break;
  221.         case 'n':        /* nntp mode: keep history of rejects */
  222.             histreject = YES;
  223.             break;
  224.         case 'r':        /* redirect std. ostreams to logs (rnews) */
  225.             *redirlogsp = 2; /* stdout & stderr */
  226.             break;
  227.         case 's':        /* dropping input is serious (inews) */
  228.             *okrefusalp = NO;
  229.             break;
  230.         case 'x':        /* -x site: don't send to site */
  231.             /* you're welcome, erik */
  232.             /* erik says he only needs one -x per inews */
  233.             if (exclude != NULL) {
  234.                 (void) fprintf(stderr,
  235.                     "%s: more than one -x site (%s)\n",
  236.                     progname, optarg);
  237.                 errflg++;
  238.             } else
  239.                 exclude = optarg;
  240.             break;
  241.         default:
  242.             errflg++;
  243.             break;
  244.         }
  245.     if (errflg) {
  246.         (void) fprintf(stderr, "usage: %s [-inrs][-d fhlmt][-x site]\n",
  247.             progname);
  248.         exit(2);
  249.     }
  250. }
  251.  
  252. void
  253. unprivileged()        /* called if NEWSARTS, NEWSCTL or NEWSBIN present */
  254. {
  255.     userealids = YES;
  256. }
  257.  
  258. STATIC boolean
  259. debugon(dbopt)
  260. register char *dbopt;
  261. {
  262.     statust status = YES;
  263.  
  264.     for (; *dbopt != '\0'; dbopt++)
  265.         switch (*dbopt) {
  266.         case 'f':
  267.             filedebug(YES);
  268.             break;
  269.         case 'h':
  270.             hdrdebug(YES);
  271.             break;
  272.         case 'l':
  273.             lockdebug(YES);
  274.             break;
  275.         case 'm':
  276.             matchdebug(YES);
  277.             break;
  278.         case 't':
  279.             transdebug(YES);
  280.             break;
  281.         default:
  282.             status = NO;    /* unknown debugging option */
  283.             (void) fprintf(stderr, "%s: bad -d %c\n",
  284.                 progname, *dbopt);
  285.             break;
  286.         }
  287.     return status;
  288. }
  289.  
  290. /*
  291.  * Redirect stdout or stderr into log files at known locations.
  292.  */
  293. void
  294. redirectlogs(count)
  295. int count;
  296. {
  297.     if (count > 0)
  298.         logfile(stdout, ctlfile("log"));
  299.     if (count > 1)
  300.         logfile(stderr, ctlfile("errlog"));
  301. }
  302.  
  303. void
  304. logfile(stream, name)            /* redirect stream into name */
  305. FILE *stream;
  306. char *name;
  307. {
  308.     if (freopen(name, "a", stream) == NULL)
  309.         errunlock("can't redirect standard stream to `%s'", name);
  310. }
  311.  
  312. /*
  313.  * if argv contains relative file name arguments, save current directory name
  314.  * in malloced memory, through origdirp.
  315.  * then change directory to the spool directory ($NEWSARTS).
  316.  */
  317. void
  318. getwdandcd(argc, argv, origdirp)
  319. int argc;
  320. char **argv;
  321. char **origdirp;
  322. {
  323.     register int argind;
  324.     boolean needpwd = NO;
  325.     char dirtmp[MAXPATH];            /* much bigger than needed */
  326.  
  327.     for (argind = optind; argind < argc; argind++)
  328.         if (argv[argind][0] != FNDELIM)
  329.             needpwd = YES;
  330.  
  331.     *origdirp = "/???";            /* pessimism */
  332.     if (needpwd && getcwd(dirtmp, sizeof dirtmp) != 0)
  333.         *origdirp = dirtmp;
  334.     *origdirp = strsave(*origdirp);        /* save a smaller copy */
  335.     cd(fullartfile((char *)NULL));        /* move to spool directory */
  336. }
  337.  
  338. /*
  339.  * process files named as arguments (or implied)
  340.  */
  341. statust
  342. procargs(argc, argv, origdirp)
  343. int argc;
  344. char **argv;
  345. char **origdirp;
  346. {
  347.     register statust status = ST_OKAY;
  348.  
  349.     if (optind == argc)
  350.         status |= process(stdin, "stdin");
  351.     else
  352.         for (; optind < argc; optind++)
  353.             status |= relnmprocess(argv[optind], *origdirp);
  354.     nnfree(origdirp);
  355.     return status;
  356. }
  357.  
  358. statust
  359. relnmprocess(name, origdir)        /* process a (relative) file name */
  360. char *name, *origdir;
  361. {
  362.     register statust status = ST_OKAY;
  363.     register FILE *in;
  364.     register char *fullname;
  365.  
  366.     fullname = nemalloc((unsigned)strlen(origdir) + STRLEN(SFNDELIM) +
  367.         strlen(name) + 1);
  368.     fullname[0] = '\0';
  369.  
  370.     if (name[0] != FNDELIM) {    /* relative path */
  371.         (void) strcat(fullname, origdir);
  372.         (void) strcat(fullname, SFNDELIM);
  373.     }
  374.     (void) strcat(fullname, name);
  375.  
  376.     in = fopenwclex(fullname, "r");
  377.     if (in != NULL) {
  378.         status |= process(in, fullname);
  379.         (void) nfclose(in);
  380.     }
  381.     free(fullname);
  382.     return status;
  383. }
  384.  
  385. /*
  386.  * process - process input file
  387.  * If it starts with '#', assume it's a batch and unravel it,
  388.  * else it's a single article, so just inject it.
  389.  */
  390. statust
  391. process(in, inname)
  392. FILE *in;
  393. char *inname;
  394. {
  395.     register int c;
  396.  
  397.     if ((c = getc(in)) == EOF)
  398.         return ST_OKAY;         /* normal EOF */
  399.     (void) ungetc(c, in);
  400.     if (c == '#')
  401.         return unbatch(in, inname);
  402.     else
  403.         return cpinsart(in, inname, MAXLONG, NO);
  404. }
  405.  
  406. /*
  407.  * Unwind "in" and insert each article.
  408.  * For each article, call cpinsart to copy the article from "in" into
  409.  * a (temporary) file in the news spool directory and rename the temp file
  410.  * to the correct final name if it isn't right already.
  411.  *
  412.  * If the unbatcher gets out of sync with the input batch, the unbatcher
  413.  * will print and discard each input line until it gets back in sync.
  414.  */
  415. statust
  416. unbatch(in, inname)
  417. register FILE *in;
  418. char *inname;
  419. {
  420.     register int c;
  421.     /* register */ char *line;
  422.     register statust status = ST_OKAY;
  423.     long charcnt;
  424.  
  425.     while (!(status&ST_DISKFULL) && (c = getc(in)) != EOF) {
  426.         (void) ungetc(c, in);
  427.         while ((line = fgetms(in)) != NULL &&
  428.             !batchln(line, &charcnt)) {        /* returns charcnt */
  429.             status |= ST_DROPPED;
  430.             (void) fprintf(stderr,
  431.                 "%s: unbatcher out of synch, tossing: ",
  432.                 progname);
  433.                 (void) fputs(line, stderr);
  434.             free(line);
  435.         }
  436.         nnfree(&line);            /* free "#! rnews n" */
  437.         if (!feof(in))
  438.             status |= cpinsart(in, inname, charcnt, YES);
  439.     }
  440.     if (ferror(in))
  441.         errunlock("error reading `%s'", inname);
  442.     return status;
  443. }
  444.  
  445. /*
  446.  * Is line a batcher-produced line (#! rnews count)?
  447.  * If so, return the count through charcntp.
  448.  * This is slightly less convenient than sscanf, but a lot smaller.
  449.  */
  450. boolean
  451. batchln(line, charcntp)
  452. register char *line;
  453. register long *charcntp;
  454. {
  455.     register char *countp;
  456.     static char batchtext[] = "#! rnews ";
  457.  
  458.     countp = line + STRLEN(batchtext);
  459.     if (STREQN(line, batchtext, STRLEN(batchtext)) &&
  460.         isascii(*countp) && isdigit(*countp)) {
  461.         *charcntp = atol(countp);
  462.         return YES;
  463.     } else {
  464.         *charcntp = 0;
  465.         return NO;
  466.     }
  467. }
  468.