From c879576d51d8099395bf9286c8e939a3c6484839 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Thu, 26 Feb 2004 07:28:56 +0000 Subject: Add -g option for greylisting support for spamd. The greylisting techinque originates from a paper by Evan Harris which can be found at http://projects.puremagic.com/greylisting/. This implementation makes spamd allow for non-blacklisted addresses to be treated as "greylisted". where they are tracked in a db file, and whitelisted by addition to a pf table when the same envelope from and to are retried from the same source IP address. Testing by many, ok deraadt@ --- libexec/spamlogd/Makefile | 9 ++ libexec/spamlogd/spamlogd.8 | 78 +++++++++++++++ libexec/spamlogd/spamlogd.c | 234 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 321 insertions(+) create mode 100644 libexec/spamlogd/Makefile create mode 100644 libexec/spamlogd/spamlogd.8 create mode 100644 libexec/spamlogd/spamlogd.c (limited to 'libexec/spamlogd') diff --git a/libexec/spamlogd/Makefile b/libexec/spamlogd/Makefile new file mode 100644 index 00000000000..ce40ac8143f --- /dev/null +++ b/libexec/spamlogd/Makefile @@ -0,0 +1,9 @@ +# $OpenBSD: Makefile,v 1.1 2004/02/26 07:28:55 beck Exp $ + +PROG= spamlogd +SRCS= spamlogd.c +MAN= spamlogd.8 + +CFLAGS+= -Wall -Wstrict-prototypes -ansi -I ${.CURDIR}/../spamd + +.include diff --git a/libexec/spamlogd/spamlogd.8 b/libexec/spamlogd/spamlogd.8 new file mode 100644 index 00000000000..61e411efb2a --- /dev/null +++ b/libexec/spamlogd/spamlogd.8 @@ -0,0 +1,78 @@ +.\" $OpenBSD: spamlogd.8,v 1.1 2004/02/26 07:28:55 beck Exp $ +.\" +.\" Copyright (c) 2004 Bob Beck. All rights reserved. +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd February 16, 2004 +.Dt SPAMLOGD 8 +.Os +.Sh NAME +.Nm spamlogd +.Nd spamd whitelist updating daemon +.Sh SYNOPSIS +.Nm spamlogd +.Op Fl i Ar interface +.Sh DESCRIPTION +.Nm +manipulates the +.Xr spamd 8 +database in +.Pa /var/db/spamd +used for +.Xr spamd 8 +greylisting. +.Nm +updates the +.Pa /var/db/spamd +whitelist entries whenever a connection +to port 25 is logged to the +.Xr pflog 4 +interface. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl i Ar interface +Specify a network interface on which packets must arrive. +The default is to watch for connections logged from any interfaces. +.El +.Pp +It is important to be sure to log any connections to your real +MTA in order for +.Nm +to update the whitelist entries. +An example +.Xr pf.conf 5 +configuration for logging such connections is as follows: +.Bd -literal -offset 4n +$EXT_IF = "fxp0" +$MAILHOSTS = "{129.128.11.10, 129.128.11.43}" +pass in log on $EXT_IF inet proto tcp to $MAILHOSTS \e + port smtp keep state +.Ed +.Sh FILES +/var/db/spamd +.Sh SEE ALSO +.Xr pflog 4 , +.Xr spamd.conf 5 , +.Xr pflogd 8 , +.Xr spamd 8 , +.Xr spamd-setup 8 , +.Xr spamdb 8 , +.Xr tcpdump 8 +.Sh HISTORY +The +.Nm +command +appeared in +.Ox 3.5 . diff --git a/libexec/spamlogd/spamlogd.c b/libexec/spamlogd/spamlogd.c new file mode 100644 index 00000000000..23e6057e7b7 --- /dev/null +++ b/libexec/spamlogd/spamlogd.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2004 Bob Beck. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* watch pf log for mail connections, update whitelist entries. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "grey.h" +#define PATH_TCPDUMP "/usr/sbin/tcpdump" + +extern struct passwd *pw; +extern FILE * grey; +extern int debug; + +size_t whitecount, whitealloc; +char **whitelist; +int pfdev; + +DB *db; +DBT dbk, dbd; +BTREEINFO btreeinfo; + + +/* borrowed from dhartmei.. */ +static int +address_valid_v4(const char *a) +{ + if (!*a) + return (0); + while (*a) + if ((*a >= '0' && *a <= '9') || *a == '.') + a++; + else + return (0); + return (1); +} + +int +dbupdate(char *dbname, char *ip) +{ + struct gdata gd; + time_t now; + int r; + + now = time(NULL); + memset(&btreeinfo, 0, sizeof(btreeinfo)); + db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_BTREE, &btreeinfo); + if (db == NULL) + return(-1); + if (!address_valid_v4(ip)) { + warnx("invalid ip address %s\n", ip); + goto bad; + } + memset(&dbk, 0, sizeof(dbk)); + dbk.size = strlen(ip); + dbk.data = ip; + memset(&dbd, 0, sizeof(dbd)); + /* add or update whitelist entry */ + r = db->get(db, &dbk, &dbd, 0); + if (r == -1) { + warn("db->get failed"); + goto bad; + } + if (r) { + /* new entry */ + memset(&gd, 0, sizeof(gd)); + gd.first = now; + gd.bcount = 1; + gd.pass = now; + gd.expire = now + WHITEEXP; + memset(&dbk, 0, sizeof(dbk)); + dbk.size = strlen(ip); + dbk.data = ip; + memset(&dbd, 0, sizeof(dbd)); + dbd.size = sizeof(gd); + dbd.data = &gd; + r = db->put(db, &dbk, &dbd, 0); + if (r) { + warn("db->put failed"); + goto bad; + } + } else { + if (dbd.size != sizeof(gd)) { + /* whatever this is, it doesn't belong */ + db->del(db, &dbk, 0); + goto bad; + } + memcpy(&gd, dbd.data, sizeof(gd)); + gd.pcount++; + gd.expire = now + WHITEEXP; + memset(&dbk, 0, sizeof(dbk)); + dbk.size = strlen(ip); + dbk.data = ip; + memset(&dbd, 0, sizeof(dbd)); + dbd.size = sizeof(gd); + dbd.data = &gd; + r = db->put(db, &dbk, &dbd, 0); + if (r) { + warn("db->put failed"); + goto bad; + } + } + db->sync(db, 0); + db->close(db); + return (0); + bad: + db->sync(db, 0); + db->close(db); + return(-1); +} + +static int +usage(void) +{ + fprintf(stderr, "usage: spamlogd [-i netif]\n"); + exit(-1); +} + +char *targv[19] = { + "tcpdump", "-l", "-n", "-e", "-i", "pflog0", "-q", + "-t", "inbound", "and", "port", "25", "and", "action", "pass", + NULL, NULL, NULL, NULL +}; + +int +main(int argc, char **argv) +{ + int ch, p[2]; + char *buf, *lbuf; + size_t len; + pid_t pid; + FILE *f; + + while ((ch = getopt(argc, argv, "i:")) != -1) { + switch (ch) { + case 'i': + targv[15] = "and"; + targv[16] = "on"; + targv[17] = optarg; + break; + default: + usage(); + break; + } + } + + if (daemon(1, 1) == -1) + err(1, "daemon"); + if (pipe(p) == -1) + err(1, "pipe"); + switch (pid = fork()) { + case -1: + err(1, "fork"); + case 0: + /* child */ + close(p[0]); + close(STDERR_FILENO); + if (dup2(p[1], STDOUT_FILENO) == -1) { + warn("dup2"); + _exit(1); + } + close(p[1]); + execvp(PATH_TCPDUMP, targv); + warn("exec of %s failed", PATH_TCPDUMP); + _exit(1); + } + + /* parent */ + f = fdopen(p[0], "r"); + if (f == NULL) + err(1, "fdopen"); + lbuf = NULL; + while ((buf = fgetln(f, &len))) { + char *cp; + + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + else { + if ((lbuf = (char *)malloc(len + 1)) == NULL) + err(1, NULL); + memcpy(lbuf, buf, len); + lbuf[len] = '\0'; + buf = lbuf; + } + if ((cp = (strchr(buf, '>'))) != NULL) { + /* XXX replace this grot with an sscanf */ + while (*cp != '.' && cp >= buf) { + *cp = '\0'; + cp--; + } + *cp ='\0'; + while (*cp != ' ' && cp >= buf) + cp--; + cp++; + dbupdate(PATH_SPAMD_DB, cp); + } + if (lbuf != NULL) { + free(lbuf); + lbuf = NULL; + } + } + exit(0); +} -- cgit v1.2.3