/************************************************************ ** $Id: edit.c,v 0.9 2003/12/29 16:45:38 bcx Exp bcx $ ** ** Copyright Bryan Costales and Perforce Software, Inc. 2003 ** ** This code is "open source" as defined by version 1.9 of ** the Open Source Definition from: ** ** http://www.opensource.org/docs/definition.php. ** ** The is the slowedit interface for the milter. ** *************************************************************/ # define EXTERN extern # include "slow.h" extern PRIV *Priv; /* in test.c */ extern struct smfi_str Ctx; /* in test.c */ static int EditMode = 0; typedef struct { char *token; int mode; } EDITCOMMANDS; EDITCOMMANDS EditCommands[] = { # define EDITCOM_ADD 1 {"add", EDITCOM_ADD}, /* add an IP event */ # define EDITCOM_DELETE 2 {"delete", EDITCOM_DELETE}, /* delete an IP and all its events */ # define EDITCOM_LIST 3 {"list", EDITCOM_LIST}, /* list all or selected IP addresses */ # define EDITCOM_FIND 4 {"find", EDITCOM_FIND}, /* find by IP or events */ # define EDITCOM_PURGE 5 {"purge", EDITCOM_PURGE}, /* purge old records */ # define EDITCOM_RE 8 {"rebuild", EDITCOM_RE}, /* rebuild the database */ # define EDITCOM_SUM 9 {"summarize", EDITCOM_SUM}, /* summarize an IP address */ {NULL, 0}, }; bool slowedit(int opt, int argc, char **argv) { int ret, mode; EDITCOMMANDS *e; char *user; extern char *optarg; /* * Format of an edit command is "slowedit command switches IP * On entry to this routine, argv[0] is the command. */ if (argv[opt] == NULL) goto usage; mode = 0; for (e = EditCommands; e->token != NULL; ++e) { if (strcasecmp(argv[opt], e->token) == 0) { mode = e->mode; break; } } if (mode == 0) { usage: printf("Usage: slowedit "); for (e = EditCommands; e->token != NULL; ++e) printf("%s%s", e == EditCommands ? "" : "|", e->token); printf("\n"); return FALSE; } ret = 0; user = cuserid(NULL); EditMode = mode; switch(mode) { /* add IP event */ case EDITCOM_ADD: ret = slowadd(user, opt, argc, argv); break; case EDITCOM_DELETE: ret = slowdelete(user, opt, argc, argv); break; case EDITCOM_LIST: ret = slowlist(user, opt, argc, argv); break; case EDITCOM_FIND: ret = slowfind(user, opt, argc, argv); break; case EDITCOM_RE: ret = slowrebuild(user, opt, argc, argv); break; case EDITCOM_PURGE: case EDITCOM_SUM: ret = slowsummarize(user, opt, argc, argv); break; } return ret; } bool slowadd(char *user, int opt, int argc, char **argv) { char *iptext; char *event_str; char logprefix[BUFSIZ]; test_init(TRUE); ++opt; if ((argc - opt) < 2) { (void) printf("Usage: slowedit add IPnumber \"event,event,...\"\n"); show_events("Combine events from"); return FALSE; } iptext = try_strdup(__FILE__, __LINE__, argv[opt], FALSE); ++opt; event_str = try_strdup(__FILE__, __LINE__, argv[opt], FALSE); if (slowsetpriv(iptext, event_str, FALSE) == FALSE) return FALSE; if (user == NULL) sprintf(logprefix, "edit add: "); else sprintf(logprefix, "%s added: ", user); LogPrefix = logprefix; Dp = open_database(DatabaseLoc, FALSE); Priv->ip = revbytes(inet_network(Priv->iptxt)); (void)smfi_setpriv(&Ctx, Priv); /* * A time of zero means use the current time. */ (void) push_to_db(&Ctx, (time_t)0, FALSE); if (! isdebug(BUG_DELETE)) { (void) printf("slowedit: add %s by %s succeeded\n", iptext, user == NULL ? "noody" : user); } (void) free(event_str); return TRUE; } bool slowdelete(char *user, int opt, int argc, char **argv) { time_t t; char *iptext; PRIMARY_KEY ip; int ret; int cnt; test_init(TRUE); ++opt; if ((argc - opt) < 1) { (void) printf("Usage: slowedit delete IPnumber [date]\n"); (void) printf("\tIf date missing, remove all the IP's records\n"); (void) printf("\telse remove just that event record.\n"); return FALSE; } iptext = try_strdup(__FILE__, __LINE__, argv[opt], FALSE); ip = inet_network(iptext); if ((int)ip == -1) { (void) printf("slowedit delete: %s: cannot interpret\n", iptext); return FALSE; } ip = revbytes(ip); ++opt; if (argv[opt] == NULL) t = 0; else t = parse_date(argv[opt]); Dp = open_database(DatabaseLoc, FALSE); cnt = 0; ret = del_event_database(Dp, &ip, t); if (ret != 0) { struct tm *tt; char buf[BUFSIZ]; tt = localtime(&t); (void) sprintf(buf, "%02d/%02d/%d.%02d:%02d:%02d", tt->tm_mon + 1, tt->tm_mday, tt->tm_year + 1900, tt->tm_hour, tt->tm_min, tt->tm_sec); (void) printf("slowedit: delete %s %s %s\n", iptext, buf, ret == DB_NOTFOUND ? "Entry specified not in datbase": strerror(errno)); return FALSE; } # if HAVE_SYSLOG if (DoLog == TRUE && LogEvents == TRUE) syslog(LOG_INFO, "%s deleted %s %s\n", user == NULL ? Prog : user, iptext, t == 0 ? "" : argv[opt]); # endif (void) printf("slowedit: %s successfully deleted %s event %s\n", user == NULL ? Prog : user, iptext, t == 0 ? "all-events" : argv[opt]); return TRUE; } bool slowlist(char *user, int opt, int argc, char **argv) { char *iptext; PRIMARY_KEY ip; test_init(TRUE); ++opt; if (argc <= opt) { printf("Usage: slowedit list [IPnumber | \"all\"]\n"); return FALSE; } iptext = try_strdup(__FILE__, __LINE__, argv[opt], FALSE); /* ** Close and reopen the database read-only. */ Dp = open_database(DatabaseLoc, DBREADONLY); if (strcasecmp(iptext, "all") == 0) { if (dump_events(Dp, stdout, FALSE, 0) != 0) { milterr(Prog, __FILE__, __LINE__, FALSE, errno, iptext, "list all failed"); } return TRUE; } ip = inet_network(iptext); if ((int)ip == -1) { (void) printf("slowedit list: %s: cannot interpret\n", iptext); return FALSE; } ip = revbytes(ip); if (dump_events(Dp, stdout, FALSE, ip) != 0) { milterr(Prog, __FILE__, __LINE__, FALSE, errno, iptext, "list all failed"); } return TRUE; } bool slowfind(char *user, int opt, int argc, char **argv) { LOGICSTRUCT *logic; DBT key, data; SECONDARY_DATUM *event; PRIMARY_KEY ip; DBC *curs; int ret; struct in_addr in; char ipbuf[MAXHOSTNAMELEN]; char *cp; BITMAP set; test_init(TRUE); ++opt; if (argc == opt) { (void) printf("Usage: slowedit find \"[and|or],relation,relation,...\"\n"); return FALSE; } cp = try_strdup(__FILE__, __LINE__, argv[opt], FALSE); logic = slowlogicparse(cp); if (logic == NULL) return FALSE; Dp = open_database(DatabaseLoc, DBREADONLY); ret = Dp->cursor(Dp, NULL, &curs, 0); if (ret != 0) { Dp->err(Dp, ret, "%s\n", DatabaseLoc); return ret; } (void) memset(&data, 0, sizeof data); (void) memset(&key, 0, sizeof key); key.flags = DB_DBT_REALLOC; data.flags = DB_DBT_REALLOC; for (;;) { ret = curs->c_get(curs, &key, &data, DB_NEXT); if (ret == DB_NOTFOUND) break; if (ret != 0) { Dp->err(Dp, ret, "%s\n", DatabaseLoc); return ret; } if (key.data == NULL || data.data == NULL) { continue; } event = (SECONDARY_DATUM *)data.data; ip = *((PRIMARY_KEY *)key.data); (void) memcpy(&in.s_addr, ((char *)&ip), sizeof(in.s_addr)); in.s_addr = htonl(in.s_addr); (void) sprintf(ipbuf, "%s", inet_ntoa(in)); if (logic->andor == SLOWBLOGICAND) { /* * Since logic AND means everything must match * we continue (skip printing) on any mismatch. */ switch(logic->envrcpts.relation) { case SLOWLOGICEQUAL: if (event->envrcpts != logic->envrcpts.value) continue; break; case SLOWLOGICGREATER: if (event->envrcpts <= logic->envrcpts.value) continue; break; case SLOWLOGICLESSTHAN: if (event->envrcpts >= logic->envrcpts.value) continue; break; case SLOWLOGICNONE: break; default: continue; } switch(logic->hdrrcpts.relation) { case SLOWLOGICEQUAL: if (event->headrcpts != logic->hdrrcpts.value) continue; break; case SLOWLOGICGREATER: if (event->headrcpts <= logic->hdrrcpts.value) continue; break; case SLOWLOGICLESSTHAN: if (event->headrcpts >= logic->hdrrcpts.value) continue; break; case SLOWLOGICNONE: break; default: continue; } switch(logic->honeyrcpts.relation) { case SLOWLOGICEQUAL: if (event->honeyrcpts != logic->honeyrcpts.value) continue; break; case SLOWLOGICGREATER: if (event->honeyrcpts <= logic->honeyrcpts.value) continue; break; case SLOWLOGICLESSTHAN: if (event->honeyrcpts >= logic->honeyrcpts.value) continue; break; case SLOWLOGICNONE: break; default: continue; } switch(logic->bad.relation) { case SLOWLOGICEQUAL: if (event->badrcpts != logic->bad.value) continue; break; case SLOWLOGICGREATER: if (event->badrcpts <= logic->bad.value) continue; break; case SLOWLOGICLESSTHAN: if (event->badrcpts >= logic->bad.value) continue; break; case SLOWLOGICNONE: break; default: continue; } if (logic->bits[SLOWSETBITS] != 0) { /* * The specified bits must be set */ set = logic->bits[SLOWSETBITS] & event->eventmap; if (set == 0) continue; } if (logic->bits[SLOWONLYBITS] != 0) { /* * The specified bits must be the only ones set */ set = logic->bits[SLOWONLYBITS] - event->eventmap; if (set != 0) continue; } if (logic->bits[SLOWCLEARBITS] != 0) { /* * The specified bits must be clear (0) */ set = logic->bits[SLOWCLEARBITS] & ~(event->eventmap); if (set == 0) continue; } if (logic->reg != NULL && event->msgid[0] != '\0') { # if HAVE_REGEX_H && HAVE_REGCOMP regmatch_t pmatch; int rerr; rerr = regexec(logic->reg, event->msgid, 1, &pmatch, 0); if (rerr != 0) continue; # else if (strstr(event->msgid, logic->reg) != 0) continue; # endif } else if (logic->reg != NULL) continue; (void) printf("%s\n", event_to_str(ipbuf, event->date, event, FALSE)); continue; } /* * Since logic OR means anythin may match * jump to print on any match. */ switch(logic->envrcpts.relation) { case SLOWLOGICEQUAL: if (event->envrcpts == logic->envrcpts.value) goto print; break; case SLOWLOGICGREATER: if (event->envrcpts > logic->envrcpts.value) goto print; break; case SLOWLOGICLESSTHAN: if (event->envrcpts < logic->envrcpts.value) goto print; break; } switch(logic->hdrrcpts.relation) { case SLOWLOGICEQUAL: if (event->headrcpts == logic->hdrrcpts.value) goto print; break; case SLOWLOGICGREATER: if (event->headrcpts > logic->hdrrcpts.value) goto print; break; case SLOWLOGICLESSTHAN: if (event->headrcpts < logic->hdrrcpts.value) goto print; break; } switch(logic->honeyrcpts.relation) { case SLOWLOGICEQUAL: if (event->honeyrcpts == logic->honeyrcpts.value) goto print; break; case SLOWLOGICGREATER: if (event->honeyrcpts > logic->honeyrcpts.value) goto print; break; case SLOWLOGICLESSTHAN: if (event->honeyrcpts < logic->honeyrcpts.value) goto print; break; } switch(logic->bad.relation) { case SLOWLOGICEQUAL: if (event->badrcpts == logic->bad.value) goto print; break; case SLOWLOGICGREATER: if (event->badrcpts > logic->bad.value) goto print; break; case SLOWLOGICLESSTHAN: if (event->badrcpts < logic->bad.value) goto print; break; } if (logic->bits[SLOWSETBITS] != 0) { /* * The specified bits must be set */ set = logic->bits[SLOWSETBITS] & event->eventmap; if (set != 0) goto print; } if (logic->bits[SLOWONLYBITS] != 0) { /* * The specified bits must be the only ones set */ set = logic->bits[SLOWONLYBITS] - event->eventmap; if (set == 0) goto print; } if (logic->bits[SLOWCLEARBITS] != 0) { /* * The specified bits must be clear (0) */ set = logic->bits[SLOWCLEARBITS] & ~(event->eventmap); if (set != 0) goto print; } if (logic->reg != NULL && event->msgid[0] != '\0') { # if HAVE_REGEX_H && HAVE_REGCOMP regmatch_t pmatch; if (regexec(logic->reg, event->msgid, 1, &pmatch, 0) == 0) # else if (strstr(event->msgid, logic->reg) == 0) # endif goto print; } continue; print: (void) printf("%s\n", event_to_str(ipbuf, event->date, event, FALSE)); continue; } (void) curs->c_close(curs); (void) close_database(Dp); return TRUE; } bool slowrebuild(char *user, int opt, int argc, char **argv) { char buf[BUFSIZ]; char *cp; char *ep; char *ipdb; char *prdate; int dolog; time_t t, s, e; int count; ++opt; if ((argc - opt) < 2) { (void) printf("Usage: slowedit rebuild newdb < textfile\n"); return FALSE; } ipdb = try_strdup(__FILE__, __LINE__, argv[opt], FALSE); /* * Open and initialize the new database. */ Dp = open_database(ipdb, TRUE); # if HAVE_SYSLOG dolog = DoLog; DoLog = FALSE; LogEvents = FALSE; # endif count = 0; while (fgets(buf, BUFSIZ, stdin) != NULL) { if ((cp = strchr(buf, '\n')) != NULL) *cp = '\0'; cp = strchr(buf, ':'); if (cp == NULL) continue; *cp = '\0'; cp +=2; ep = strchr(cp, ' '); if (ep == NULL) continue; *ep = '\0'; prdate = cp; t = parse_date(cp); cp = ep+1; if (Verbose) (void)time(&s); test_init(TRUE); Priv->ip = revbytes(inet_network(buf)); if (slowsetpriv(buf, cp, TRUE) == FALSE) continue; (void)smfi_setpriv(&Ctx, Priv); (void)push_to_db(&Ctx, (time_t)t, FALSE); if (Verbose) { (void)time(&e); printf("%s %s added in %d seconds\n", buf, prdate, (int)(e-s)); } if ((++count % 25) == 0) printf("%d records added\n", count); } # if HAVE_SYSLOG if (dolog == TRUE) { syslog(LOG_INFO, "rebuild %s ipdb=%s", user == NULL ? "" : user, ipdb); } # endif printf("%d total records added\n", count); return TRUE; } bool slowsummarize(char *user, int opt, int argc, char **argv) { DBT key, data; PRIMARY_KEY ip = 0; int ret; time_t t, now; char *iptext = NULL; PRIMARY_KEY p; SECONDARY_DATUM *event; SECONDARY_SUMMARY *sum; int c; DBC *curs; extern int errno; test_init(TRUE); ++opt; if (argv[opt] == NULL) { if (EditMode == EDITCOM_PURGE) printf("Usage: slowedit purge interval units\n"); else printf("Usage: slowedit summarize IPnumber interval units\n"); return FALSE; } if (EditMode == EDITCOM_SUM) { ip = revbytes(inet_network(argv[opt])); iptext = argv[opt]; ++opt; } if (argv[opt] == NULL || (int)(t = atoi(argv[opt])) <= 0) { printf("slowedit %s: interval may not be <= 0\n", EditMode == EDITCOM_SUM ? "summarize":"purge"); return FALSE; } if (argv[++opt] != NULL) { c = argv[opt][0]; if (isupper(c)) c = tolower(c); switch((int)argv[opt][0]) { case 's': /* seconds */ break; case 'm': /* minutes */ t = t * 60; break; case 'h': /* hours */ t = t * 60 * 60; break; case 'd': /* days */ t = t * 60 * 60 * 24; break; case 'w': /* weeks */ t = t * 60 * 60 * 24 * 7; break; default: printf("slowedit summarize: %c unrecognized units\n", c); return FALSE; } } else t = t * 24 * 60 * 60; /* default is days */ (void)time(&now); if (t > 0) t = now - t; test_init(TRUE); if (EditMode == EDITCOM_PURGE) { int cnt; Dp = open_database(DatabaseLoc, FALSE); ret = Dp->cursor(Dp, NULL, &curs, 0); if (ret != 0) { Dp->err(Dp, ret, "%s\n", DatabaseLoc); return ret; } (void) memset(&data, 0, sizeof data); (void) memset(&key, 0, sizeof key); key.flags = DB_DBT_REALLOC; data.flags = DB_DBT_REALLOC; cnt = 0; for (;;) { ret = curs->c_get(curs, &key, &data, DB_NEXT); if (ret == DB_NOTFOUND) break; if (ret != 0) { Dp->err(Dp, ret, "%s\n", DatabaseLoc); (void) curs->c_close(curs); (void) close_database(Dp); return ret; } if (key.data == NULL || data.data == NULL) continue; event = (SECONDARY_DATUM *)data.data; p = *((PRIMARY_KEY *)key.data); if (p == DATABASE_VERSION) continue; if (event->date >= t) continue; ret = curs->c_del(curs, 0); if (ret != 0) { Dp->err(Dp, ret, "%s\n", DatabaseLoc); (void) curs->c_close(curs); (void) close_database(Dp); return ret; } ++cnt; } printf("slowedit purge: removed %d old records\n", cnt); (void) curs->c_close(curs); (void) close_database(Dp); return TRUE; } sum = try_alloc(__FILE__, __LINE__, sizeof(SECONDARY_SUMMARY), FALSE); (void) memset((char *)sum, '\0', sizeof(SECONDARY_SUMMARY)); Dp = open_database(DatabaseLoc, DBREADONLY); ret = Dp->cursor(Dp, NULL, &curs, 0); if (ret != 0) { Dp->err(Dp, ret, "%s\n", DatabaseLoc); return ret; } (void) memset(&data, 0, sizeof data); (void) memset(&key, 0, sizeof key); key.flags = DB_DBT_REALLOC; data.flags = DB_DBT_REALLOC; for (;;) { ret = curs->c_get(curs, &key, &data, DB_NEXT); if (ret == DB_NOTFOUND) break; if (ret != 0) { Dp->err(Dp, ret, "%s\n", DatabaseLoc); (void) curs->c_close(curs); (void) close_database(Dp); return ret; } if (key.data == NULL || data.data == NULL) continue; event = (SECONDARY_DATUM *)data.data; p = *((PRIMARY_KEY *)key.data); if (p != ip) continue; if (p == DATABASE_VERSION) continue; if (event->date < t) continue; sum->count += 1; sum->envrcpts += event->envrcpts; sum->headrcpts += event->headrcpts; sum->honeyrcpts += event->honeyrcpts; sum->badrcpts += event->badrcpts; if (event->eventmap != 0) { if (isbitset(event->eventmap, BIT_TOO_MANY_HDR_RCPTS)) sum->hed += 1; if (isbitset(event->eventmap, BIT_TOO_MANY_ENV_RCPTS)) sum->env += 1; if (isbitset(event->eventmap, BIT_GOT_HONEYPOT)) sum->hon += 1; if (isbitset(event->eventmap, BIT_BAD_MSG_ID)) sum->mid += 1; if (isbitset(event->eventmap, BIT_BAD_FROM_HEAD)) sum->frm += 1; if (isbitset(event->eventmap, BIT_BAD_HOST)) sum->bho += 1; if (isbitset(event->eventmap, BIT_RBL_BAD)) sum->rbl += 1; if (isbitset(event->eventmap, BIT_MILTER_ABORTED)) sum->abo += 1; if (isbitset(event->eventmap, BIT_ADVANCEWRITE)) sum->pip += 1; if (isbitset(event->eventmap, BIT_FORGED)) sum->frg += 1; if (isbitset(event->eventmap, BIT_FROMMX)) sum->mmx += 1; if (isbitset(event->eventmap, BIT_NOTINET)) sum->net += 1; if (isbitset(event->eventmap, BIT_ADDEDIP)) sum->aip += 1; } } (void) curs->c_close(curs); (void) close_database(Dp); printf("%s:%33d record%s:\n", iptext, sum->count, sum->count == 1 ? "" : "s"); if (sum->count == 0) /* prevent division by zero */ sum->count = 1; printf("\t%-30.30s=%9d (%d%%)\n", "Whitelisted Address", sum->whitelisted, (int)(((float)sum->whitelisted/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Envelope Recipients", sum->envrcpts, (int)(((float)sum->envrcpts/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Header Recipients", sum->headrcpts, (int)(((float)sum->headrcpts/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Honey Pot Recipients", sum->honeyrcpts, (int)(((float)sum->honeyrcpts/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Bad Recipients", sum->badrcpts, (int)(((float)sum->badrcpts/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Excess Header Recipients", sum->hed, (int)(((float)sum->hed/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Excess Envelope Recipients", sum->env, (int)(((float)sum->env/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Any Honey Pot Recipients", sum->hon, (int)(((float)sum->hon/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Bad Message-Id Headers", sum->mid, (int)(((float)sum->mid/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Bad Connecting Hosts", sum->bho, (int)(((float)sum->bho/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Realtime Black Hole Rejects", sum->rbl, (int)(((float)sum->rbl/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Milter Aborts", sum->abo, (int)(((float)sum->abo/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Illegal Pipe-Lining Attempts", sum->pip, (int)(((float)sum->pip/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Sendmail said \"forged\"", sum->frg, (int)(((float)sum->frg/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Received from our MX server", sum->mmx, (int)(((float)sum->mmx/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "Not An IP Network", sum->net, (int)(((float)sum->net/(float)sum->count)*100.0) ); printf("\t%-30.30s=%9d (%d%%)\n", "An Aliased IP entry", sum->aip, (int)(((float)sum->aip/(float)sum->count)*100.0) ); printf("::\n"); return TRUE; } typedef struct { char *event; int index; } EVENT_LIST; EVENT_LIST EventList[] = { # define EVENT_ENVRCPTS 1 {"er", EVENT_ENVRCPTS}, /* envelope recipients */ # define EVENT_HDRRCPTS 2 {"hr", EVENT_HDRRCPTS}, /* header recipients */ # define EVENT_HONEYRCPTS 3 {"ho", EVENT_HONEYRCPTS}, /* honey pot recipients */ # define EVENT_BADRCPTS 4 {"br", EVENT_BADRCPTS}, /* bad envelope recipients */ # define EVENT_TOOHDR 5 {"hed", EVENT_TOOHDR}, /* too many header recipients */ # define EVENT_TOOENV 6 {"env", EVENT_TOOENV}, /* too many envelope recipients */ # define EVENT_HONEY 7 {"hon", EVENT_HONEY}, /* one or more honey pots rcpts */ # define EVENT_MSGID 8 {"mid", EVENT_MSGID}, /* bad or missing message-id header */ # define EVENT_FROMH 9 {"frm", EVENT_FROMH}, /* bad from header */ # define EVENT_BADHO 10 {"bho", EVENT_BADHO}, /* bad envelope sender host */ # define EVENT_RBL 11 {"rbl", EVENT_RBL}, /* reported bad by an RBL host */ # define EVENT_MILT 12 {"abo", EVENT_MILT}, /* The milter aborted */ # define EVENT_ADVW 13 {"pip", EVENT_ADVW}, /* Attempt at an advanced write */ # define EVENT_FORGE 14 {"for", EVENT_FORGE}, /* sendmail reported this as forged */ # define EVENT_FROMMX 15 {"mmx", EVENT_FROMMX}, /* received from one of our MX servers */ # define EVENT_NOTINET 16 {"net", EVENT_NOTINET}, /* not received via a TCP/IP network socket */ # define EVENT_ADDEDIP 17 {"aip", EVENT_ADDEDIP}, /* This is an alias of another IP address */ # define EVENT_MESGID 18 {"msgid", EVENT_MESGID}, /* The text that is the message-id header */ {NULL, 0}, }; void show_events(char *str) { EVENT_LIST *e; int count; printf("%s:\n\t", str); count = 0; for (e = EventList; e->event != NULL; ++e) { if (++count == 6) { printf("\n\t"); count = 0; } else if (e != EventList) printf(", "); if (strlen(e->event) == 2) printf("%s=#", e->event); else printf("%s", e->event); } printf("\n"); } int is_event(char *str) { EVENT_LIST *e; for (e = EventList; e->event != NULL; ++e) { if (strcasecmp(e->event, str) == 0) return e->index; } return 0; } bool slowsetpriv(char *iptext, char *estr, int rebuild) { char **list, **l; char **equate; char *item; int value; int index; list = mkargv(estr, ',', FALSE); equate = NULL; if (list == NULL) { if (rebuild == TRUE) return TRUE; (void) printf("slowedit: events missing\n"); return FALSE; } if (isdebug(BUG_EDIT)) print_list("slowsetpriv", list, stdout); for (l = list; *l != NULL; l++) { value = -1; if (strchr(*l, '=') != NULL) { equate = mkargv(*l, '=', FALSE); if (equate == NULL) continue; item = try_strdup(__FILE__, __LINE__, equate[0], FALSE); if (equate[1] != NULL) value = atoi(equate[1]); else value = 0; } else item = try_strdup(__FILE__, __LINE__, *l, FALSE); index = is_event(item); if (index == 0) { (void) printf("%s: Unrecognized event keyword\n", item); show_events("Combine events from"); return FALSE; } if (strlen(item) == 3 && value >= 0) { (void) printf("slowedit: %s: Three letter events are boolean.", item); (void) printf(" Do not use %s=value form.\n", item); return FALSE; } switch(index) { /* count of envelope recipients */ case EVENT_ENVRCPTS: if (value < 0 && rebuild == FALSE) { milterr(Prog, __FILE__, __LINE__, FALSE, EINVAL, item, "May not be negative"); } else if (value < 0 && rebuild == TRUE) value = 0; Priv->envrcpts = value; break; case EVENT_HDRRCPTS: if (value < 0 && rebuild == FALSE) { milterr(Prog, __FILE__, __LINE__, FALSE, EINVAL, item, "May not be negative"); } else if (value < 0 && rebuild == TRUE) value = 0; Priv->nhdrrcpts = value; break; case EVENT_HONEYRCPTS: if (value < 0 && rebuild == FALSE) { milterr(Prog, __FILE__, __LINE__, FALSE, EINVAL, item, "May not be negative"); } else if (value < 0 && rebuild == TRUE) value = 0; Priv->honeyrcpts = value; break; case EVENT_BADRCPTS: if (value < 0 && rebuild == FALSE) { milterr(Prog, __FILE__, __LINE__, FALSE, EINVAL, item, "May not be negative"); } else if (value < 0 && rebuild == TRUE) value = 0; Priv->badrcpts = value; break; case EVENT_TOOHDR: set_bit(BIT_TOO_MANY_HDR_RCPTS, Priv->bits); break; case EVENT_TOOENV: set_bit(BIT_TOO_MANY_ENV_RCPTS, Priv->bits); break; case EVENT_HONEY: set_bit(BIT_GOT_HONEYPOT, Priv->bits); break; case EVENT_MSGID: set_bit(BIT_BAD_MSG_ID, Priv->bits); break; case EVENT_FROMH: set_bit(BIT_BAD_FROM_HEAD, Priv->bits); break; case EVENT_BADHO: set_bit(BIT_BAD_HOST, Priv->bits); break; case EVENT_RBL: set_bit(BIT_RBL_BAD, Priv->bits); break; case EVENT_MILT: set_bit(BIT_MILTER_ABORTED, Priv->bits); break; case EVENT_ADVW: set_bit(BIT_ADVANCEWRITE, Priv->bits); break; case EVENT_FORGE: set_bit(BIT_FORGED, Priv->bits); break; case EVENT_FROMMX: set_bit(BIT_FROMMX, Priv->bits); break; case EVENT_NOTINET: set_bit(BIT_NOTINET, Priv->bits); break; case EVENT_ADDEDIP: set_bit(BIT_ADDEDIP, Priv->bits); break; case EVENT_MESGID: if (equate != NULL && equate[1] != NULL) Priv->midstr = try_strdup(__FILE__, __LINE__, equate[1], FALSE); break; } (void) free(item); } Priv->iptxt = try_strdup(__FILE__, __LINE__, iptext, FALSE); return TRUE; } LOGICSTRUCT * slowlogicparse(char *estr) { char **list, **l; char **equate; char *item; int val; int index; int ch, rel, whichbits; LOGICSTRUCT *logic; list = mkargv(estr, ',', FALSE); if (list == NULL) { (void) printf("slowedit: events missing\n"); return FALSE; } if (isdebug(BUG_EDIT)) print_list("slowsetpriv", list, stdout); logic = (LOGICSTRUCT *)try_alloc(__FILE__, __LINE__, sizeof(*logic), FALSE); if (logic == NULL) return NULL; (void)memset((char *)logic, '\0', sizeof(*logic)); logic->andor = SLOWBLOGICAND; /* default to AND */ logic->whitelisted = FALSE; for (l = list; *l != NULL; l++) { if (strcasecmp(*l, "and") == 0) { logic->andor = SLOWBLOGICAND; continue; } if (strcasecmp(*l, "or") == 0) { logic->andor = SLOWBLOGICOR; continue; } if (strcasecmp(*l, "whitelisted") == 0) { logic->andor = SLOWBLOGICOR; logic->whitelisted = TRUE; continue; } val = -1; rel = SLOWLOGICNONE; whichbits = SLOWSETBITS; if (strchr(*l, '=') != NULL) { rel = SLOWLOGICEQUAL; ch = '='; } else if (strchr(*l, '>') != NULL) { rel = SLOWLOGICGREATER; ch = '>'; } else if (strchr(*l, '<') != NULL) { rel = SLOWLOGICLESSTHAN; ch = '<'; } else { (void) printf("%s: Unrecognized or missing comparitor\n", *l); (void) printf("\tselect from: = (equal to), < (less than), or > (greater than\n"); exit (EINVAL); } /* * Other than "and" and "or", everyting is in the format * keyword [<>=] value, * For non-bitmap items, value is numeric. * For bitmap items, value must be =[set,only,clear] * For msgid value must be ="regular expression" */ equate = mkargv(*l, ch, FALSE); if (equate == NULL) continue; if (equate[1] == NULL) { (void) printf("%s: Value portion missing from relationshipe.\n", *l); exit(EINVAL); } item = try_strdup(__FILE__, __LINE__, equate[0], FALSE); index = is_event(item); if (index == 0) { (void) printf("%s: Unrecognized event keyword\n", item); show_events("Combine events from"); return FALSE; } if (isdigit((int)(equate[1][0]))) { val = atoi(equate[1]); whichbits = SLOWSETBITS; } else { if (index == EVENT_MESGID) { # if HAVE_REGEX_H && HAVE_REGCOMP int rerr; logic->reg = try_alloc(__FILE__, __LINE__, sizeof(regex_t), FALSE); memset((char *)(logic->reg), '\0', sizeof(regex_t)); rerr = regcomp(logic->reg, equate[1], REG_EXTENDED|REG_ICASE); if (rerr != 0) { char rerrbuf[BUFSIZ]; (void) regerror(rerr, logic->reg, rerrbuf, BUFSIZ); (void) printf("%s: %s\n", item, rerrbuf); exit (rerr); } # else logic->reg = try_strdup(__FILE__, __LINE__, equate[1], FALSE); # endif continue; } val = 0; if (strcasecmp(equate[1], "set") == 0) whichbits = SLOWSETBITS; else if (strcasecmp(equate[1], "only") == 0) whichbits = SLOWONLYBITS; else if (strcasecmp(equate[1], "clear") == 0) whichbits = SLOWCLEARBITS; else { (void) printf("%s: Unrecognized bit keyword\n", item); (void) printf("\tselect from: set, only, or clear\n"); exit (EINVAL); } } switch(index) { case EVENT_ENVRCPTS: logic->envrcpts.relation = rel; logic->envrcpts.value = val; break; case EVENT_HDRRCPTS: logic->hdrrcpts.relation = rel; logic->hdrrcpts.value = val; break; case EVENT_HONEYRCPTS: logic->honeyrcpts.relation = rel; logic->honeyrcpts.value = val; break; case EVENT_BADRCPTS: logic->bad.relation = rel; logic->bad.value = val; break; case EVENT_TOOHDR: set_bit(BIT_TOO_MANY_HDR_RCPTS, logic->bits[whichbits]); break; case EVENT_TOOENV: set_bit(BIT_TOO_MANY_ENV_RCPTS, logic->bits[whichbits]); break; case EVENT_HONEY: set_bit(BIT_GOT_HONEYPOT, logic->bits[whichbits]); break; case EVENT_MSGID: set_bit(BIT_BAD_MSG_ID, logic->bits[whichbits]); break; case EVENT_FROMH: set_bit(BIT_BAD_FROM_HEAD, logic->bits[whichbits]); break; case EVENT_BADHO: set_bit(BIT_BAD_HOST, logic->bits[whichbits]); break; case EVENT_RBL: set_bit(BIT_RBL_BAD, logic->bits[whichbits]); break; case EVENT_MILT: set_bit(BIT_MILTER_ABORTED, logic->bits[whichbits]); break; case EVENT_ADVW: set_bit(BIT_ADVANCEWRITE, logic->bits[whichbits]); break; case EVENT_FORGE: set_bit(BIT_FORGED, logic->bits[whichbits]); break; case EVENT_FROMMX: set_bit(BIT_FROMMX, logic->bits[whichbits]); break; case EVENT_NOTINET: set_bit(BIT_NOTINET, logic->bits[whichbits]); break; } (void) free(item); } return logic; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#6 | 4222 | bryan_costales |
Massive rewrite to speed up the database writes. Using a single database now with duplicate keys where the keys are the IP numbers. Added a purge command and removed the garbage command. Fixed some leaking memory bugs and properly closed the database in a few places were it was not properly closed. Updated the docs to reflect this and bumped both the database version and release number. Running on a FreeBSD 3.x machine and a Solaris 9 machine. |
||
#5 | 4052 | bryan_costales |
Implimented: whitelisting AddMXHost for MX servers that lie Converted to thread safe DNS routines garbage collection RunAsUser and RunAsGroup for root startups rebuild the database summarize by IP number Finished all documentation. Moved release from alpha to beta |
||
#4 | 4030 | bryan_costales |
Finished documenting the configuration file Fixed a race condition and a core dump bug. Added hooks for whitelisting and IP aliasing Added support for Berkeley DB 4.2 Converted to htonl() and ntohl() Known Bugs: ip.db cannot be shared over NFS IP tracking from MX hosts can fail A RunAsUser config option is needed. |
||
#3 | 3998 | bryan_costales |
Brought the whole distribution up to V0.9 Added a huge abount of documentation. Added slowedit find Created startup scripts to launch for testing Fixed numerous bugs. Fixed a few portablity issues. Installed hooks for whitelisting and IP aliases. |
||
#2 | 3957 | bryan_costales |
Added rbl lookup support and testing for same. Folded in support for smfi_stop(). Added lots of slowedit commands Fixed a serious bug in MX lookups. Added to documentation. |
||
#1 | 3890 | bryan_costales |
This is the 0.6 release. The following have been added with the uses indicated: Source files: edit.c -- the slowedit functions compat.c -- missing system files Autoconf: configure.ac, makefile.am config.h aclocal.m4 acinclude.m4 build/ Documents: doc/ -- html and man(1) documents Testing: tests/ -- regressive testing TODO: -- revised to show actual progress |