/* * This software is Copyright (c) 1986 by Rick Adams. * * Permission is hereby granted to copy, reproduce, redistribute or * otherwise use this software as long as: there is no monetary * profit gained specifically from the use or reproduction or this * software, it is not sold, rented, traded or otherwise marketed, and * this copyright notice is included prominently in any copy * made. * * The author make no claims as to the fitness or correctness of * this software for any use whatsoever, and it is provided as is. * Any use of this software is at the user's own risk. * * Control message handling code. Deal with messages which are to be * acted on by netnews itself rather than by people. * * See defs.h "news_version" for the real version of netnews. */ #ifdef SCCSID static char *SccsId = "@(#)control.c 2.43 3/19/86"; #endif /* SCCSID */ #include "iparams.h" #define eq(msg) (strcmp(msg, cargv[0]) == 0) int cargc; char **cargv; FILE *hfopen(); FILE *popen(), *mhopen(), *mailhdr(); char *senderof(); #ifdef u370 static struct hbuf htmp; #endif /* u370 */ control(h) struct hbuf *h; { register char *ctlmsgtext; if (strncmp(h->title, "cmsg ", 5) == 0) { register char *cp1, *cp2; cp1 = h->title; cp2 = h->title + 5; while (*cp1++ = *cp2++) ; } if (*h->ctlmsg) ctlmsgtext = h->ctlmsg; else ctlmsgtext = h->title; log("Ctl Msg %s from %s: %s", h->nbuf, h->path, ctlmsgtext); /* * Control messages have the standard format * command [args] * much like shell commands. Each site has the option * of customizing this code to deal with control messages * as they see fit, but we would like to buy back the * code, ifdeffed or otherwise parameterized, to simplify * the maintenence issues. */ argparse(ctlmsgtext); if (eq("cancel")) return c_cancel(cargc, cargv); else if (eq("newgroup")) c_newgroup(cargc, cargv); else if (eq("ihave")) c_ihave(cargc, cargv); else if (eq("sendme")) c_sendme(cargc, cargv); else if (eq("sendbad")) c_sendme(cargc, cargv); else if (eq("rmgroup")) c_rmgroup(cargc, cargv); else if (eq("sendsys")) c_sendsys(cargc, cargv); else if (eq("senduuname")) c_senduuname(cargc, cargv); else if (eq("version")) c_version(cargc, cargv); else if (eq("checkgroups")) c_checkgroups(cargc, cargv); else if (eq("delsub")) c_unimp(cargc, cargv); else c_unknown(h, ctlmsgtext); return 0; } /* * Parse the string str into separate words in cargc and cargv * as per the usual UNIX convention. Nothing fancy here, just * blanks and tabs separating words. */ argparse(str) char *str; { static char *cavpbuf[20]; static char cavbuf[256]; char *nextfree = cavbuf; if (str == '\0') xerror("Control message %s has no title", header.ident); cargc = (*str != '\0'); cargv = cavpbuf; cargv[0] = cavbuf; while (*str) { if (*str <= ' ') { *nextfree++ = 0; cargv[cargc] = nextfree; cargc++; /* skip over white space */ while (*str != '\0' && *str <= ' ') str++; if (*str == '\0') /* line ends in white space */ return; } else *nextfree++ = *str++; } } /* * ihave * The other system is telling you it has article , in case * you decide you want it to transmit it to you. */ c_ihave(argc, argv) char **argv; { char tl[256], ng[256]; #ifndef u370 struct hbuf htmp; #endif /* !u370 */ if (argc < 2) xerror("ihave: Not enough arguments."); /* * Check that we haven't already seen it (history) * and then send back a "sendme" message if we subscribe. */ (void) strncpy(htmp.ident, argv[1], BUFLEN); if (history(&htmp) == 0) { /* Should probably check SUBFILE here. */ (void) sprintf(tl, "sendme %s %s", argv[1], FULLSYSNAME); (void) sprintf(ng, "to.%s.ctl", argv[2]); xmitmsg(argv[2], tl, ng); } } /* * sendme ... * The other system wants me to send him article . */ c_sendme(argc, argv) char **argv; { int i; FILE *fp; struct srec srec; #ifndef u370 struct hbuf htmp; #endif /* !u370 */ if (argc < 2) xerror("sendme: Not enough arguments."); /* Don't ask for it from myself */ if (strncmp(FULLSYSNAME, argv[argc], SNLN) == 0) return; /* Find the sys record */ s_openr(); while (s_read(&srec)) { if (strncmp(srec.s_name, argv[argc], SNLN)) continue; /* It's the right one. Send them. */ for (i=1; i * A new newsgroup has been created. * The body of the article, if present, is a description of the * purpose of the newsgroup. * */ c_newgroup(argc, argv) char **argv; { FILE *fd; int didcreate = 0; if (argc < 1) xerror("newgroup: Not enough arguments."); if (validng(argv[1])) return; if (header.approved[0] == '\0') xerror("newgroup: %s not approved", argv[1]); #ifndef NONEWGROUPS #ifdef ORGDISTRIB if (!strcmp(ORGDISTRIB, header.distribution)) { didcreate++; #endif /* ORGDISTRIB */ actfp = xfopen(ACTIVE, "a"); fprintf(actfp, "%s 00000 00001 y\n", argv[1]); (void) fclose(actfp); #ifdef ORGDISTRIB } #endif /* ORGDISTRIB */ #endif /* !NONEWGROUPS */ #ifdef NOTIFY fd = mailhdr((struct hbuf *)NULL, "creation of new newsgroup"); if (fd != NULL) { if (didcreate) fprintf(fd, "A new newsgroup called '%s' has been created by %s.\n", argv[1], header.path); else fprintf(fd, "%s requested that a new newsgroup called '%s' be created.\n", header.path, argv[1]); fprintf(fd, "It was approved by %s\n\n", header.approved); #ifdef ORGDISTRIB fprintf(fd, "You can accomplish this by sending a newgroup control message with a\n"); fprintf(fd, "distribution code of %s; in other words, by executing the command:\n", ORGDISTRIB); fprintf(fd, "%s/inews -n net.news -d %s -t \"cmsg newgroup %s\"\n", LIB, ORGDISTRIB, argv[1]); #endif /* ORGDISTRIB */ (void) mclose(fd); } } #endif /* NOTIFY */ /* * rmgroup * An old newsgroup is being cancelled on a network wide basis. */ c_rmgroup(argc, argv) char **argv; { FILE *fd; int shouldremove = 0; if (argc < 1) xerror("rmgroup: Not enough arguments."); if (!validng(argv[1])) return; if (header.approved[0] == '\0') xerror("rmgroup: %s not approved", argv[1]); #ifndef MANUALLY #ifdef ORGDISTRIB if (!strcmp(ORGDISTRIB, header.distribution)) #endif /* ORGDISTRIB */ shouldremove++; #endif /* !MANUALLY */ #ifdef NOTIFY fd = mailhdr((struct hbuf *)NULL, "rmgroup control message"); if (fd != NULL) { if (shouldremove) { fprintf(fd, "A newsgroup called '%s' has been removed by %s.\n\n", argv[1], header.path); # ifdef USG fprintf(fd, "You may need to remove the directory %s by hand\n", dirname(argv[1])); # endif } else { fprintf(fd, "%s has requested that newsgroup %s be removed.\n", header.path, argv[1]); fprintf(fd, "You should remove it by hand\n"); fprintf(fd, "To do this, execute the command\n"); fprintf(fd, "\t%s/rmgroup %s\n", LIB, argv[1]); } (void) mclose(fd); } #endif /* NOTIFY */ if (shouldremove) { int rc; /* We let the shell do all the work. * See the rmgrp shell script. */ (void) setuid(geteuid()); /* otherwise it won't rmdir the dir */ (void) sprintf(bfr, "exec %s/rmgroup %s", LIB, argv[1]); rc = system(bfr); log("system(%s) status %d", bfr, rc); } } /* * cancel * Cancel the named article */ c_cancel(argc, argv) char **argv; { char *line, *p, *q, *r, *poster; char *findhist(); register FILE *fp; char whatsisname[BUFLEN], nfilename[BUFLEN]; time_t t; int su = 0; #ifndef u370 struct hbuf htmp; #endif /* !u370 */ if (argc < 1) xerror("cancel: Not enough arguments."); (void) strcpy(whatsisname, senderof(&header)); line = findhist(argv[1]); if (line == NULL) { struct tm *tm; log("Can't cancel %s: non-existent", argv[1]); (void) time(&t); tm = localtime(&t); #ifdef USG sprintf(bfr,"%s\t%2.2d/%2.2d/%d %2.2d:%2.2d\tcancelled", #else /* !USG */ sprintf(bfr,"%s\t%02d/%02d/%d %02d:%02d\tcancelled", #endif /* !USG */ argv[1], tm->tm_mon+1, tm->tm_mday, tm->tm_year, tm->tm_hour, tm->tm_min); savehist(bfr); return 1; } q = index(line, '\t'); p = index(q+1, '\t'); if (p == NULL || *++p == '\0' || *p == '\n') { *q = '\0'; log("Expired article %s", line); return 1; } if (strcmp(p, "cancelled") == 0) { *q = '\0'; log("Already Cancelled %s", line); return 1; } else log("Cancelling %s", line); if ((uid == ROOTID||uid == 0) && strcmp(header.distribution, "local") == 0) su = 1; while (*p) { q = index(p, ' '); if (q) *q = '\0'; (void) strcpy(nfilename, dirname(p)); fp = fopen(nfilename, "r"); if (fp == NULL) { log("Already Cancelled %s", line); return 1; } htmp.unrec[0] = NULL; if (hread(&htmp, fp, TRUE) == NULL) { if (bfr[0] == '/') { fp = fopen(bfr, "r"); if (fp == NULL || hread(&htmp, fp, TRUE) == NULL) xerror("Article is garbled."); } else xerror("Article is garbled."); } (void) fclose(fp); poster = senderof(&htmp); /* only compare up to '.' or ' ' */ r = index(poster,'.'); if (r == NULL) r = index(poster,' '); if (r != NULL) *r = '\0'; if (!su && strncmp(whatsisname, poster,strlen(poster))) { xerror("Not contributor: posted by %s, and you are %s", poster, whatsisname); } (void) unlink(nfilename); p = q+1; } return 0; } /* * sendsys (no arguments) * * Mail the sys file to the person submitting the article. * POLICY: the contents of your sys file are public information * and as such, you should not change this code. You may feel * free to arrange for it to manually notify you, in the event * that you want to do something to clean it up before it goes out. * Secret sites on the net are expressly frowned on. * * The purpose of this command is for making a network map. The * details of your link and which newsgroups are forwarded are not * important, in case you want to sanitize them. Since the definition * of USENET is those sites getting net.announce, you can disable this * on sites not getting net articles, but if you take out the list of * forwarded newsgroups, and you have sites that only get local newsgroups, * you should make this clear, or remove those sites from what you send out. */ /* ARGSUSED */ c_sendsys(argc, argv) char **argv; { register FILE *f, *u; int c; #ifdef NOTIFY f = mailhdr((struct hbuf *)NULL, "sendsys control message"); if (f != NULL) { fprintf(f, "%s requested your %s/sys file.\n", header.path, LIB); fprintf(f, "It has been sent.\n"); (void) mclose(f); } #endif f = mailhdr(&header, "response to your sendsys request"); u = fopen(SUBFILE, "r"); if (f != NULL && u != NULL) { while ((c=getc(u)) != EOF) putc(c, f); (void) fclose(u); (void) mclose(f); } } /* * senduuname (no arguments) * * Run the "uuname" command and send it back to the person who submitted * the article. The purpose of this control message is for attempting to * make a uucp net map. * * POLICY: If you view this information as not public (because you have * a connection you consider secret, or know a site that considers itself * secret) you can feel free to change this code in whatever way is * appropriate, so long as it sends some response back to the sender. If * you don't run uucp, this code does not make sense, and so an error * message (or garbage, such as "research") will be mailed back. * * If you wish to add or remove sites from the output of uuname, you * may wish to use the euuname.sh shell script here. */ /* ARGSUSED */ c_senduuname(argc, argv) char **argv; { char buf[256]; FILE *fd, *u; int c; #ifdef NOTIFY fd = mailhdr((struct hbuf *)NULL, "uuname control message"); fprintf(fd, "%s requested your uuname output\n", header.path); (void) mclose(fd); #endif fd = mailhdr(&header, "response to your senduuname request"); #ifdef UUPROG if (UUPROG[0] == '/') (void) strcpy(buf, UUPROG); else (void) sprintf(buf, "%s/%s", LIB, UUPROG); #else (void) strcpy(buf, "uuname"); #endif u = popen(buf, "r"); if (fd != NULL && u != NULL) { while ((c=getc(u)) != EOF) putc(c, fd); (void) pclose(u); (void) mclose(fd); } } /* * Send the version number to the right person. */ /* ARGSUSED */ c_version(argc, argv) char **argv; { register FILE *f; f = mailhdr(&header, "Our news version"); if (f == NULL) xerror("Cannot send back error message"); fprintf(f, "Currently running news version %s.\n\n", news_version); fprintf(f, "The header of your message follows:\n\n"); (void) hwrite(&header, f); (void) mclose(f); } /* * Check the active file for old or missing newsgroups * Body of article is list of valid groups */ /* ARGSUSED */ c_checkgroups(argc, argv) char **argv; { int rc; (void) setuid(geteuid()); /* dont change the cat %s| to < %s, it breaks some "unix" systems */ (void) sprintf(bfr, "cat %s | %s/checkgroups %s", INFILE, LIB, #ifdef NOTIFY (TELLME && *TELLME) ? TELLME : NEWSUSR ); #else /* !NOTIFY */ NEWSUSR); #endif /* !NOTIFY */ rc = system(bfr); log("system(%s) status %d", bfr, rc); } /* * An unknown control message has been received. */ c_unknown(h, ctlmsgtext) struct hbuf *h; char *ctlmsgtext; { register FILE *f; log("UNKNOWN Ctl Msg %s from %s", ctlmsgtext, h->path); #ifdef NOTIFY f = mailhdr((struct hbuf *)NULL, "Unrecognized Control Message"); if (f != NULL) { fprintf(f, "Currently running news version %s.\n\n", news_version); fprintf(f, "The header of the message follows:\n\n"); (void) hwrite(h, f); (void) mclose(f); } #endif /* NOTIFY */ } /* ARGSUSED */ c_unimp(argc, argv) char **argv; { register FILE *f; #ifdef NOTIFY f = mailhdr((struct hbuf*)NULL, "Unimplemented Control Message"); if (f != NULL) { fprintf(f, "Currently running news version B %s.\n\n", news_version); fprintf(f, "The header of the message follows:\n\n"); (void) hwrite(&header, f); (void) mclose(f); } #endif /* NOTIFY */ } xmitmsg(tosys, title, ng) char *tosys, *title, *ng; { #ifndef u370 struct hbuf htmp; #endif /* !u370 */ struct srec srec; FILE *tfp; char *fname; /* Make an article called ARTICLE */ (void) sprintf(htmp.from, "%s@%s%s", "usenet", FULLSYSNAME, MYDOMAIN); (void) strcpy(htmp.path, NEWSUSR); (void) strcpy(htmp.nbuf, ng); (void) strcpy(htmp.title, title); (void) strcpy(htmp.ctlmsg, title); (void) strcpy(htmp.subdate, ""); (void) strcpy(htmp.expdate, ""); getident(&htmp); dates(&htmp); tfp = xfopen(fname = mktemp("/tmp/xmsgXXXXXX"), "w"); hwrite(&htmp, tfp); (void) fclose(tfp); /* Find the sys record */ s_openr(); while (s_read(&srec)) { if (strncmp(srec.s_name, tosys, SNLN)) continue; tfp = xfopen(fname, "r"); (void) transmit(&srec, tfp, 0, (char **)0, 0); (void) unlink(fname); return; } log("Can't find sys record for %s", tosys); xerror("Cannot find sys record"); } /* * This is a modified version of popen, made more secure. Rather than * forking off a shell, you get a bare process. You must have exactly * one argument, and the command must be mail (or sendmail if you have it). */ #define RDR 0 #define WTR 1 static int mopen_pid[20]; char *replyname(); FILE * mhopen(hptr) struct hbuf *hptr; { int p[2]; register myside, hisside, pid; char *sendto = "usenet"; if (hptr) sendto = replyname(hptr); else { #ifdef NOTIFY if (TELLME && *TELLME) sendto = TELLME; #endif /* NOTIFY */ if (sendto == NULL) return NULL; } verifyname(sendto); if(pipe(p) < 0) return NULL; myside = p[WTR]; hisside = p[RDR]; if((pid = fork()) == 0) { /* myside and hisside reverse roles in child */ (void) close(myside); (void) close(0); (void) dup(hisside); (void) close(hisside); (void) setgid(gid); (void) setuid(uid); #ifdef SENDMAIL execl(SENDMAIL, "sendmail", "-oi", "-oeq", sendto, (char *)NULL); #endif /* SENDMAIL */ #ifdef MMDF execl(MMDF, "inews-mail", "-smuxto,cc*", (char *)NULL); #endif /* MMDF */ execl("/bin/mail", "mail", sendto, (char *)NULL); execl("/usr/bin/mail", "mail", sendto, (char *)NULL); execl("/usr/ucb/mail", "mail", sendto, (char *)NULL); _exit(1); } if(pid == -1) return NULL; mopen_pid[myside] = pid; (void) close(hisside); return(fdopen(myside, "w")); } mclose(ptr) FILE *ptr; { register f, r, (*hstat)(), (*istat)(), (*qstat)(); int status; f = fileno(ptr); (void) fclose(ptr); istat = signal(SIGINT, SIG_IGN); qstat = signal(SIGQUIT, SIG_IGN); hstat = signal(SIGHUP, SIG_IGN); while((r = wait(&status)) != mopen_pid[f] && r != -1) ; if(r == -1) status = -1; signal(SIGINT, istat); signal(SIGQUIT, qstat); signal(SIGHUP, hstat); return status; } /* * mhopen a pipe to mail, write out a std header, and return the file ptr. * * We don't include a From: field because this is probably uucp, i.e. * explicitly routed. Leave it up to the recipient's mailer. * Always include the To: field because if we ge back failed mail, we * might be able to deliver it by hand if we know to wom it was addressed. * By convention, hptr==NULL means to send the message to the local contact person. */ FILE * mailhdr(hptr, subject) struct hbuf *hptr; char *subject; { FILE *fp; time_t now; char *to = "usenet"; #ifdef NOTIFY if (TELLME && *TELLME) to = TELLME; #endif /* NOTIFY */ if (hptr) to = replyname(hptr); if ((fp = mhopen(hptr)) != NULL) { (void) time(&now); fprintf(fp, "Date: %s\n", arpadate(&now)); #ifdef MMDF fprintf(fp, "From: The News System \n", FULLSYSNAME, MYDOMAIN); #endif /* MMDF */ fprintf(fp, "To: %s\n", to); fprintf(fp, "Subject: %s\n", subject); #ifdef HIDDENNET if (strcmp(LOCALSYSNAME, FULLSYSNAME)) fprintf(fp, "Responding-System: %s.%s%s\n\n", LOCALSYSNAME, FULLSYSNAME, MYDOMAIN); #endif /* !HIDDENNET */ fprintf(fp, "Responding-System: %s%s\n\n", FULLSYSNAME, MYDOMAIN); } return fp; } /* * verify that the name mail is being sent to does not contain any * nasty hooks to invoke funny functions from the shell or the like. */ verifyname(sendto) char *sendto; { /* Be sure we DO allow alphabetics, !, :, ., -, @. *. */ char *nasty = "\"'\\`^|;& <>/~"; register char *p; if (sendto[0] <= ' ') { log("nasty mail name %s from %s", sendto, header.path); xxit(1); } for (p=sendto; *p; p++) { if (*p == ' ') { *p = 0; break; } } if (strpbrk(sendto, nasty) != NULL) xerror("nasty mail name %s from %s", sendto, header.path); for (nasty = sendto; (nasty = index(nasty, '.')) != NULL; ) { if (*++nasty == '.') /* check for .. */ xerror("nasty mail name %s from %s", sendto, header.path); } } /* * Checks to make sure the control message is OK to post. */ ctlcheck() { char msg[BUFLEN]; char *p; if (!is_ctl) return; if (header.ctlmsg[0]) (void) strcpy(msg, header.ctlmsg); else (void) strcpy(msg, header.title); p = index(msg, ' '); if (p) *p = 0; if (strcmp(msg, "ihave") == 0) { } else if (strcmp(msg, "sendme") == 0) { return; /* no restrictions */ } else if (strcmp(msg, "newgroup") == 0) { suser(); } else if (strcmp(msg, "rmgroup") == 0) { suser(); } else if (strcmp(msg, "sendsys") == 0) { suser(); } else if (strcmp(msg, "senduuname") == 0) { suser(); } else if (strcmp(msg, "checkgroups") == 0) { suser(); } else if (strcmp(msg, "version") == 0) { return; /* no restrictions */ } else if (strcmp(msg, "cancel") == 0) { return; /* no restrictions at this level */ } else if (strcmp(msg, "delsub") == 0) { if (!prefix(header.nbuf, "to.")) { printf("Must be in a 'to.system' newsgroup."); xxit(0); } return; } else { printf("Unrecognized control message - %s\n", msg); xxit(0); } } /* Make sure this guy is special. */ suser() { if (uid == 0 || uid == ROOTID) return; /* * We assume that since our real uid is the same as NEWSUSR * (the euid) we were run by rootid and it did a setuid. * Too bad we can't set just the effective uid like suid does. */ if (uid == geteuid()) return; #ifdef IHCC printf("Please use the command:\n\ttoolnews providers\n"); printf("then call one of the news people.\n"); #else printf("Get your local netnews contact to do it for you.\n"); #endif xxit(0); }