summaryrefslogtreecommitdiffstats
path: root/daemon/innbbsd/nocem.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/innbbsd/nocem.c')
-rw-r--r--daemon/innbbsd/nocem.c636
1 files changed, 636 insertions, 0 deletions
diff --git a/daemon/innbbsd/nocem.c b/daemon/innbbsd/nocem.c
new file mode 100644
index 00000000..595ecb1d
--- /dev/null
+++ b/daemon/innbbsd/nocem.c
@@ -0,0 +1,636 @@
+/*
+ * NoCeM-INNBBSD Yen-Ming Lee <leeym@cae.ce.ntu.edu.tw> NCMparse(),
+ * NCMverify(), NCMcancel(): return 0 success, otherwise fail;
+ */
+
+#include <stdlib.h>
+#include "externs.h"
+#include "nocem.h"
+#define PGP5
+#undef PGP2
+
+int ncmdebug = 0;
+
+char NCMVER[8];
+char ISSUER[STRLEN];
+char TYPE[8];
+char ACTION[8];
+char NCMID[STRLEN];
+char COUNT[8];
+char THRESHOLD[STRLEN];
+char KEYID[16];
+char SPAMMID_NOW[STRLEN];
+char SPAMMID[MAXSPAMMID][STRLEN];
+
+int NNTP = -1;
+FILE *NNTPrfp = NULL;
+FILE *NNTPwfp = NULL;
+char NNTPbuffer[1024];
+int num_spammid = 0;
+char errmsg[1024] = "nothing";
+int NCMCOUNT = 0;
+ncmperm_t *NCMPERM=NULL, **NCMPERM_BYTYPE=NULL;
+static char *NCMPERM_BUF;
+
+/* ------------------------------------------------------------------ */
+/* NCM initial and maintain */
+/* ------------------------------------------------------------------ */
+
+static int
+ncm_bytypecmp(a, b)
+ ncmperm_t **a, **b;
+{
+ return strcasecmp((*a)->type, (*b)->type);
+}
+
+static int
+ncmcmp(a, b)
+ ncmperm_t *a, *b;
+{
+ return strcasecmp(a->issuer, b->issuer);
+}
+
+#if 0
+ncmperm_t *
+search_issuer(issuer)
+ char *issuer;
+{
+ ncmperm_t ncmt, *find;
+ ncmt.issuer = "*";
+ find = (ncmperm_t *) bsearch((char *) &ncmt, NCMPERM, NCMCOUNT, sizeof(ncmperm_t), ncmcmp);
+ if (find)
+ return find;
+ ncmt.issuer = issuer;
+ find = (ncmperm_t *) bsearch((char *) &ncmt, NCMPERM, NCMCOUNT, sizeof(ncmperm_t), ncmcmp);
+ return find;
+}
+#else
+ncmperm_t *
+search_issuer(issuer)
+ char *issuer;
+{
+ ncmperm_t *find;
+ int i;
+ for (i = 0; i < NCMCOUNT; i++)
+ {
+ find = &NCMPERM[i];
+ if (strstr(find->issuer, "*"))
+ return find;
+ if (strstr(issuer, find->issuer))
+ return find;
+ }
+ return NULL;
+}
+#endif
+
+ncmperm_t *
+search_issuer_type(issuer, type)
+ char *issuer, *type;
+{
+ ncmperm_t *find;
+ int i;
+ for (i = 0; i < NCMCOUNT; i++)
+ {
+ find = &NCMPERM[i];
+ if ((!strcmp(find->issuer, "*") || strstr(issuer, find->issuer)) &&
+ (!strcmp(find->type, "*") || !strcasecmp(find->type, type)))
+ return find;
+ }
+ return NULL;
+}
+
+int
+readNCMfile(inndhome)
+ char *inndhome;
+{
+ FILE *fp;
+ char buff[LINELEN];
+ struct stat st;
+ int i, count;
+ char *ptr, *ncmpermptr;
+
+ sprintf(buff, "%s/ncmperm.bbs", inndhome);
+ fp = fopen(buff, "r");
+ if (fp == NULL)
+ {
+ fprintf(stderr, "read fail %s", buff);
+ return -1;
+ }
+ if (fstat(fileno(fp), &st) != 0)
+ {
+ fprintf(stderr, "stat fail %s", buff);
+ return -1;
+ }
+ if (NCMPERM_BUF == NULL)
+ {
+ NCMPERM_BUF = (char *) mymalloc(st.st_size + 1);
+ }
+ else
+ {
+ NCMPERM_BUF = (char *) myrealloc(NCMPERM_BUF, st.st_size + 1);
+ }
+ i = 0, count = 0;
+ while (fgets(buff, sizeof buff, fp) != NULL)
+ {
+ if (buff[0] == '#')
+ continue;
+ if (buff[0] == '\n')
+ continue;
+ strcpy(NCMPERM_BUF + i, buff);
+ i += strlen(buff);
+ count++;
+ }
+ fclose(fp);
+ if (NCMPERM == NULL)
+ {
+ NCMPERM = (ncmperm_t *) mymalloc(sizeof(ncmperm_t) * (count + 1));
+ NCMPERM_BYTYPE = (ncmperm_t **) mymalloc(sizeof(ncmperm_t *) * (count + 1));
+ }
+ else
+ {
+ NCMPERM = (ncmperm_t *) myrealloc(NCMPERM, sizeof(ncmperm_t) * (count + 1));
+ NCMPERM_BYTYPE = (ncmperm_t **) myrealloc(NCMPERM_BYTYPE, sizeof(ncmperm_t *) * (count + 1));
+ }
+ NCMCOUNT = 0;
+ for (ptr = NCMPERM_BUF; (ncmpermptr = (char *) strchr(ptr, '\n')) != NULL; ptr = ncmpermptr + 1, NCMCOUNT++)
+ {
+ char *nptr;
+ *ncmpermptr = '\0';
+ NCMPERM[NCMCOUNT].issuer = "";
+ NCMPERM[NCMCOUNT].type = "";
+ NCMPERM[NCMCOUNT].perm = 0;
+ NCMPERM_BYTYPE[NCMCOUNT] = NCMPERM + NCMCOUNT;
+ for (nptr = ptr; *nptr && (*nptr == '\t');)
+ nptr++;
+ if (*nptr == '\0')
+ continue;
+ NCMPERM[NCMCOUNT].issuer = nptr;
+ for (nptr++; *nptr && !(*nptr == '\t');)
+ nptr++;
+ if (*nptr == '\0')
+ continue;
+ *nptr = '\0';
+ for (nptr++; *nptr && (*nptr == '\t');)
+ nptr++;
+ if (*nptr == '\0')
+ continue;
+ NCMPERM[NCMCOUNT].type = nptr;
+ for (nptr++; *nptr && !(*nptr == '\t');)
+ nptr++;
+ if (*nptr == '\0')
+ continue;
+ *nptr = '\0';
+ for (nptr++; *nptr && (*nptr == '\t');)
+ nptr++;
+ if (*nptr == '\0')
+ continue;
+ NCMPERM[NCMCOUNT].perm = (strstr(nptr, "y") || strstr(nptr, "Y"));
+ for (nptr++; *nptr && !strchr("\r\n", *nptr);)
+ nptr++;
+ /* if (*nptr == '\0') continue; */
+ *nptr = '\0';
+ }
+ qsort(NCMPERM, NCMCOUNT, sizeof(ncmperm_t), ncmcmp);
+ qsort(NCMPERM_BYTYPE, NCMCOUNT, sizeof(ncmperm_t *), ncm_bytypecmp);
+#if 0
+ NCMregister();
+#endif
+ return 0;
+}
+
+int
+NCMupdate(char *issuer, char *type)
+{
+ FILE *fp;
+ char buff[LINELEN];
+
+ sprintf(buff, "%s/ncmperm.bbs", INNDHOME);
+ if (!isfile(buff))
+ {
+ if ((fp = fopen(buff, "w")) == NULL)
+ {
+ fprintf(stderr, "write fail %s", buff);
+ return -1;
+ }
+ fprintf(fp, "# This is ncmperm.bbs, it's auto-generated by program for first time\n");
+ fprintf(fp, "# The columns *MUST* be separated by [TAB]\n");
+ fprintf(fp, "# If you wanna accept someone's NoCeM notice, change his perm from \'no\' to \'yes\'\n");
+ fprintf(fp, "# put \"*\" in Issuer column means to match all\n");
+ fprintf(fp, "# Any questions ? please e-mail %s\n", LeeymEMAIL);
+ fprintf(fp, "# Issuer\t\tType\tPerm\n");
+ fflush(fp);
+ fclose(fp);
+ bbslog("NCMupdate create %s\n", buff);
+ }
+ if ((fp = fopen(buff, "a")) == NULL)
+ {
+ fprintf(stderr, "attach fail %s", buff);
+ return -1;
+ }
+ fprintf(fp, "%s\t\t%s\tno\n", issuer, type);
+ fflush(fp);
+ fclose(fp);
+ bbslog("NCMupdate add Issuer: %s , Type: %s\n", ISSUER, TYPE);
+ sleep(1);
+ if (readNCMfile(INNDHOME) == -1)
+ bbslog("fail to readNCMfile\n");
+ return 0;
+}
+
+int tcpcommand(char *fmt, ...)
+{
+ va_list ap;
+ char *ptr;
+ va_start(ap, fmt);
+ vfprintf(NNTPwfp, fmt, ap);
+ va_end(ap);
+ fprintf(NNTPwfp, "\r\n");
+ fflush(NNTPwfp);
+
+ fgets(NNTPbuffer, sizeof NNTPbuffer, NNTPrfp);
+ ptr = strchr(NNTPbuffer, '\r');
+ if (ptr)
+ *ptr = '\0';
+ ptr = strchr(NNTPbuffer, '\n');
+ if (ptr)
+ *ptr = '\0';
+ return atoi(NNTPbuffer);
+}
+
+int
+NCMregister()
+{
+ int status;
+ time_t now = time(NULL);
+ char hbuf[80];
+
+ gethostname(hbuf, 80);
+
+ if (!strcmp(hbuf, LeeymBBS))
+ return 1;
+
+ if ((NNTP = inetclient(LeeymBBS, "7777", "tcp")) < 0)
+ {
+ bbslog("NCMregister :Err: server %s %s error: cant connect\n", LeeymBBS, "7777");
+ return 0;
+ }
+
+ if (!(NNTPrfp = fdopen(NNTP, "r")) || !(NNTPwfp = fdopen(NNTP, "w")))
+ {
+ bbslog("NCMregister :Err: fdopen failed\n");
+ return 0;
+ }
+
+ fgets(NNTPbuffer, sizeof NNTPbuffer, NNTPrfp);
+ if (atoi(NNTPbuffer) != 200)
+ {
+ bbslog("NCMregister :Err: server error: %s", NNTPbuffer);
+ return 0;
+ }
+ status = tcpcommand("ADDHIST <%d-%s> NCMregister/%s/%s",
+ now, hbuf, VERSION, NCMINNBBSVER);
+ status = tcpcommand("QUIT");
+ fclose(NNTPwfp);
+ fclose(NNTPrfp);
+ close(NNTP);
+ return 1;
+}
+
+/* ------------------------------------------------------------------ */
+/* PGP verify */
+/* ------------------------------------------------------------------ */
+
+#ifdef PGP5
+int
+run_pgp(char *cmd, FILE ** in, FILE ** out)
+{
+ int pin[2], pout[2], child_pid;
+ char PGPPATH[80];
+
+ sprintf(PGPPATH, "%s/.pgp", BBSHOME);
+ setenv("PGPPATH", PGPPATH, 1);
+
+ *in = *out = NULL;
+
+ pipe(pin);
+ pipe(pout);
+
+ if (!(child_pid = fork()))
+ {
+ /* We're the child. */
+ close(pin[1]);
+ dup2(pin[0], 0);
+ close(pin[0]);
+
+ close(pout[0]);
+ dup2(pout[1], 1);
+ close(pout[1]);
+
+ execl("/bin/sh", "sh", "-c", cmd, NULL);
+ _exit(127);
+ }
+ /* Only get here if we're the parent. */
+ close(pout[1]);
+ *out = fdopen(pout[0], "r");
+
+ close(pin[0]);
+ *in = fdopen(pin[1], "w");
+
+ return (child_pid);
+}
+
+int
+verify_buffer(char *buf, char *passphrase)
+{
+ FILE *pgpin, *pgpout;
+ char tmpbuf[1024] = " ";
+ int ans = NOPGP;
+
+ setenv("PGPPASSFD", "0", 1);
+ run_pgp("/usr/local/bin/pgpv -f +batchmode=1 +OutputInformationFD=1",
+ &pgpin, &pgpout);
+ if (pgpin && pgpout)
+ {
+ fprintf(pgpin, "%s\n", passphrase); /* Send the passphrase in, first */
+ bzero(passphrase, strlen(passphrase)); /* Burn the passphrase */
+ fprintf(pgpin, "%s", buf);
+ fclose(pgpin);
+
+ *buf = '\0';
+ fgets(tmpbuf, sizeof(tmpbuf), pgpout);
+ while (!feof(pgpout))
+ {
+ strcat(buf, tmpbuf);
+ fgets(tmpbuf, sizeof(tmpbuf), pgpout);
+ }
+
+ wait(NULL);
+
+ fclose(pgpout);
+ }
+
+ if (strstr(buf, "BAD signature made"))
+ {
+ strcpy(errmsg, "BAD signature");
+ ans = PGPBAD;
+ }
+ else if (strstr(buf, "Good signature made"))
+ {
+ strcpy(errmsg, "Good signature");
+ ans = PGPGOOD;
+ }
+ else if (strcpy(tmpbuf, strstr(buf, "Signature by unknown keyid:")))
+ {
+ sprintf(errmsg, "%s ", strtok(tmpbuf, "\r\n"));
+ strcpy(KEYID, strrchr(tmpbuf, ' ') + 1);
+ ans = PGPUN;
+ }
+
+ unsetenv("PGPPASSFD");
+ return ans;
+}
+
+int
+NCMverify()
+{
+ int ans;
+ char passphrase[80] = "Haha, I am Leeym..";
+ ans = verify_buffer(BODY, passphrase);
+ return ans;
+}
+#endif
+/* ------------------------------------------------------------------ */
+/* parse NoCeM Notice Headers/Body */
+/* ------------------------------------------------------------------ */
+
+int
+readNCMheader(char *line)
+{
+ if (!strncasecmp(line, "Version", strlen("Version")))
+ {
+ strcpy(NCMVER, line + strlen("Version") + 2);
+ if (!strstr(NCMVER, "0.9"))
+ {
+ sprintf(errmsg, "unknown version: %s", NCMVER);
+ return P_FAIL;
+ }
+ }
+ else if (!strncasecmp(line, "Issuer", strlen("Issuer")))
+ {
+ strcpy(ISSUER, line + strlen("Issuer") + 2);
+ FROM = ISSUER;
+ }
+ else if (!strncasecmp(line, "Type", strlen("Type")))
+ {
+ strcpy(TYPE, line + strlen("Type") + 2);
+ }
+ else if (!strncasecmp(line, "Action", strlen("Action")))
+ {
+ strcpy(ACTION, line + strlen("Action") + 2);
+ if (!strstr(ACTION, "hide"))
+ {
+ sprintf(errmsg, "unsupported action: %s", ACTION);
+ return P_FAIL;
+ }
+ }
+ else if (!strncasecmp(line, "Notice-ID", strlen("Notice-ID")))
+ {
+ strcpy(NCMID, line + strlen("Notice-ID") + 2);
+ }
+ else if (!strncasecmp(line, "Count", strlen("Count")))
+ {
+ strcpy(COUNT, line + strlen("Count") + 2);
+ }
+ else if (!strncasecmp(line, "Threshold", strlen("Threshold")))
+ {
+ strcpy(THRESHOLD, line + strlen("Threshold") + 2);
+ }
+
+ return P_OKAY;
+}
+
+int
+readNCMbody(char *line)
+{
+ char buf[LINELEN], *group;
+ strcpy(buf, line);
+
+ if (!strstr(buf, "\t"))
+ return P_FAIL;
+
+ group = strrchr(line, '\t') + 1;
+
+ if (buf[0] == '<' && strstr(buf, ">"))
+ {
+ strtok(buf, "\t");
+ strcpy(SPAMMID_NOW, buf);
+ }
+
+ if (num_spammid && !strcmp(SPAMMID[num_spammid - 1], SPAMMID_NOW))
+ return 0;
+
+ if (search_group(group))
+ strcpy(SPAMMID[num_spammid++], SPAMMID_NOW);
+}
+
+int
+NCMparse()
+{
+ char *fptr, *ptr;
+ int type = TEXT;
+
+ if (!(fptr = strstr(BODY, "-----BEGIN PGP SIGNED MESSAGE-----")))
+ {
+ strcpy(errmsg, "notice isn't signed");
+ return P_FAIL;
+ }
+
+ for (ptr = strchr(fptr, '\n'); ptr != NULL && *ptr != '\0'; fptr = ptr + 1, ptr = strchr(fptr, '\n'))
+ {
+ int ch = *ptr;
+ int ch2 = *(ptr - 1);
+
+ *ptr = '\0';
+ if (*(ptr - 1) == '\r')
+ *(ptr - 1) = '\0';
+
+ if (num_spammid > MAXSPAMMID)
+ return P_OKAY;
+
+ if (ncmdebug >= 2)
+ bbslog("NCMparse: %s\n", fptr);
+
+ if (!strncmp(fptr, "@@", 2))
+ {
+ if (strstr(fptr, "BEGIN NCM HEADERS"))
+ type = NCMHDR;
+ else if (strstr(fptr, "BEGIN NCM BODY"))
+ {
+ if (NCMVER && ISSUER && TYPE && ACTION && COUNT && NCMID)
+ {
+ ncmperm_t *ncmt;
+ ncmt = (ncmperm_t *) search_issuer_type(ISSUER, TYPE);
+ if (ncmt == NULL)
+ {
+ NCMupdate(ISSUER, TYPE);
+ sprintf(errmsg, "unknown issuer: %s, %s", ISSUER, MSGID);
+ return P_UNKNOWN;
+ }
+ if (ncmt->perm == NULL)
+ {
+ sprintf(errmsg, "disallow issuer: %s, %s", ISSUER, MSGID);
+ return P_DISALLOW;
+ }
+ }
+ else
+ {
+ strcpy(errmsg, "HEADERS syntax not correct");
+ return P_FAIL;
+ }
+ type = NCMBDY;
+ }
+ else if (strstr(fptr, "END NCM BODY"))
+ {
+ *ptr = ch;
+ *(ptr - 1) = ch2;
+ break;
+ }
+ else
+ {
+ strcpy(errmsg, "NCM Notice syntax not correct");
+ return P_FAIL;
+ }
+ *ptr = ch;
+ *(ptr - 1) = ch2;
+ continue;
+ }
+
+ if (type == NCMHDR && readNCMheader(fptr) == P_FAIL)
+ return P_FAIL;
+ else if (type == NCMBDY)
+ readNCMbody(fptr);
+ *ptr = ch;
+ *(ptr - 1) = ch2;
+ }
+ if (NCMVER && ISSUER && TYPE && ACTION && COUNT && NCMID)
+ return P_OKAY;
+ else
+ {
+ strcpy(errmsg, "HEADERS syntax not correct");
+ return P_FAIL;
+ }
+
+ strcpy(errmsg, "I don't know..");
+ return P_FAIL;
+}
+
+int
+NCMcancel()
+{
+ int i, rel, num_ok, num_fail;
+ for (i = rel = num_ok = num_fail = 0; i < num_spammid; i++)
+ {
+ rel = cancel_article_front(SPAMMID[i]);
+ if (rel)
+ num_fail++;
+ else
+ num_ok++;
+ }
+ bbslog("NCMcancel %s %s, count:%d spam:%d, ok:%d fail:%d\n",
+ ISSUER, MSGID, atoi(COUNT), num_spammid, num_ok, num_fail);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* NoCeM-innbbsd */
+/* ------------------------------------------------------------------ */
+
+void
+initial_nocem()
+{
+ bzero(SPAMMID[0], strlen(SPAMMID[0]) * num_spammid);
+ num_spammid = 0;
+ bzero(SPAMMID_NOW, strlen(SPAMMID_NOW));
+}
+
+int
+receive_nocem(void)
+{
+ int rel;
+
+ if (ncmdebug)
+ bbslog("NCM: receive %s\n", MSGID);
+
+ initial_nocem();
+
+ rel = NCMparse();
+
+ if (rel != P_OKAY)
+ {
+ if (rel != P_DISALLOW)
+ bbslog("NCMparse %s\n", errmsg);
+ return 0;
+ }
+
+ if (!num_spammid)
+ {
+ bbslog("NCMparse: nothing to cancel\n");
+ return 0;
+ }
+#ifdef PGP5
+ if (ncmdebug)
+ bbslog("NCM: verifying PGP sign\n");
+
+ rel = NCMverify();
+
+ if (rel != PGPGOOD)
+ {
+ bbslog("NCMverify %s, %s, %s\n", errmsg, MSGID, ISSUER);
+ return 0;
+ }
+#endif
+
+ if (ncmdebug)
+ bbslog("NCM: canceling spam in NoCeM Notice\n");
+ return NCMcancel();
+}