diff options
author | Bob Beck <beck@cvs.openbsd.org> | 2004-02-26 07:28:56 +0000 |
---|---|---|
committer | Bob Beck <beck@cvs.openbsd.org> | 2004-02-26 07:28:56 +0000 |
commit | c879576d51d8099395bf9286c8e939a3c6484839 (patch) | |
tree | 873df009cb315b2bdd3132a80971aee555209401 /usr.sbin | |
parent | 68ca8b3e82a4a5094091027feddcb51262302e73 (diff) |
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@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/spamdb/Makefile | 9 | ||||
-rw-r--r-- | usr.sbin/spamdb/spamdb.8 | 61 | ||||
-rw-r--r-- | usr.sbin/spamdb/spamdb.c | 263 |
4 files changed, 335 insertions, 2 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 16973d56da7..3b1811ffc41 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.104 2004/02/23 19:55:35 tedu Exp $ +# $OpenBSD: Makefile,v 1.105 2004/02/26 07:28:55 beck Exp $ # not yet done: catman @@ -15,7 +15,7 @@ SUBDIR= ac accton adduser amd arp authpf \ procmap pstat pwd_mkdb quot quotaon rarpd rbootd \ rdconfig rdate repquota rmt \ rpc.bootparamd rpc.lockd rwhod \ - sa sensorsd sesd sliplogin slstats spppcontrol spray \ + sa sensorsd sesd sliplogin slstats spamdb spppcontrol spray \ syslogc syslogd tcpdump timed tokenadm tokeninit traceroute trpt \ usbdevs user vipw vnconfig wsmoused zdump zic diff --git a/usr.sbin/spamdb/Makefile b/usr.sbin/spamdb/Makefile new file mode 100644 index 00000000000..1574b921827 --- /dev/null +++ b/usr.sbin/spamdb/Makefile @@ -0,0 +1,9 @@ +# $OpenBSD: Makefile,v 1.1 2004/02/26 07:28:55 beck Exp $ + +PROG= spamdb +SRCS= spamdb.c +MAN= spamdb.8 + +CFLAGS+= -Wall -Wstrict-prototypes -ansi -I ${.CURDIR}/../../libexec/spamd + +.include <bsd.prog.mk> diff --git a/usr.sbin/spamdb/spamdb.8 b/usr.sbin/spamdb/spamdb.8 new file mode 100644 index 00000000000..85d676ef119 --- /dev/null +++ b/usr.sbin/spamdb/spamdb.8 @@ -0,0 +1,61 @@ +.\" $OpenBSD: spamdb.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 SPAMDB 8 +.Os +.Sh NAME +.Nm spamdb +.Nd spamd database tool +.Sh SYNOPSIS +.Nm spamdb +.Bk -words +.Op Fl a Ar ip +.Op Fl d Ar ip +.Ek +.Sh DESCRIPTION +.Nm +manipulates the spamd database in +.Pa /var/db/spamd +used for +.Xr spamd 8 +greylisting. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl a Ar ip +Add or update a whitelist entry for IP address "ip". +Updates time last seen to now. +.It Fl d Ar ip +Delete a whitelist entry for IP address "ip". +.El +.Pp +If invoked without any arguments, +.Nm +lists the contents of the database +in a text format. +.Sh FILES +/var/db/spamd +.Sh SEE ALSO +.Xr spamd.conf 5 , +.Xr spamd 8 , +.Xr spamd-setup 8 +.Sh HISTORY +The +.Nm +command +appeared in +.Ox 3.5 . diff --git a/usr.sbin/spamdb/spamdb.c b/usr.sbin/spamdb/spamdb.c new file mode 100644 index 00000000000..930b131f06a --- /dev/null +++ b/usr.sbin/spamdb/spamdb.c @@ -0,0 +1,263 @@ +/* + * 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. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/fcntl.h> +#include <sys/wait.h> +#include <net/if.h> +#include <netinet/in.h> +#include <net/pfvar.h> +#include <arpa/inet.h> +#include <db.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "grey.h" +#define PATH_SPAMD_DB "/var/db/spamd" + +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, int add) +{ + 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)); + if (!add) { + /* remove whitelist entry */ + r = db->get(db, &dbk, &dbd, 0); + if (r == -1) { + warn("db->get failed"); + goto bad; + } + if (r) { + warnx("no entry for %s", ip); + goto bad; + } else if (db->del(db, &dbk, 0)) { + warn("db->del failed"); + goto bad; + } + } else { + /* 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); +} + + + +int +dblist(char *dbname) +{ + struct gdata gd; + int r; + + /* walk db, list in text format */ + memset(&btreeinfo, 0, sizeof(btreeinfo)); + db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_BTREE, &btreeinfo); + if (db == NULL) + err(1, "dbopen"); + memset(&dbk, 0, sizeof(dbk)); + memset(&dbd, 0, sizeof(dbd)); + for (r = db->seq(db, &dbk, &dbd, R_FIRST); !r; + r = db->seq(db, &dbk, &dbd, R_NEXT)) { + char *a, *cp; + + if ((dbk.size < 1) || dbd.size != sizeof(struct gdata)) { + db->close(db); + errx(1, "bogus size db entry - bad db file?"); + } + memcpy(&gd, dbd.data, sizeof(gd)); + a = malloc(dbk.size + 1); + if (a == NULL) + err(1, "malloc"); + memcpy(a, dbk.data, dbk.size); + a[dbk.size]='\0'; + cp = strchr(a, '\n'); + if (cp == NULL) + /* this is a whitelist entry */ + printf("WHITE:%s:%d:%d:%d:%d:%d\n", a, gd.first, + gd.pass, gd.expire, gd.bcount, gd.pcount); + else { + char *from, *to; + + /* greylist entry */ + *cp = '\0'; + from = cp + 1; + to = strchr(from, '\n'); + if (to == NULL) { + warnx("No from part in grey key %s", a); + goto bad; + } + *to = '\0'; + to++; + printf("GREY:%s:%s:%s:%d:%d:%d:%d:%d\n", + a, from, to, gd.first, gd.pass, gd.expire, + gd.bcount, gd.pcount); + } + } + db->sync(db, 0); + db->close(db); + return(0); + bad: + db->sync(db, 0); + db->close(db); + errx(1, "incorrect db format entry"); + /* NOTREACHED */ + return(-1); +} + + +static int +usage(void) +{ + fprintf(stderr, "usage: spamdb [-a ip] [-d ip]\n"); + exit(-1); +} + +int +main(int argc, char **argv) +{ + int ch, action = 0; + char *ip = NULL; + + while ((ch = getopt(argc, argv, "a:d:")) != -1) { + switch (ch) { + case 'a': + action = 1; + ip = optarg; + break; + case 'd': + action = 2; + ip = optarg; + break; + default: + usage(); + break; + } + } + + switch (action) { + case 0: + dblist("/var/db/spamd"); + break; + case 1: + dbupdate("/var/db/spamd", ip, 1); + break; + case 2: + dbupdate("/var/db/spamd", ip, 0); + break; + default: + errx(-1, "bad action"); + } + return(0); +} |