summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libexec/spamd/grey.c329
-rw-r--r--libexec/spamlogd/spamlogd.c10
-rw-r--r--usr.sbin/spamdb/spamdb.c22
3 files changed, 278 insertions, 83 deletions
diff --git a/libexec/spamd/grey.c b/libexec/spamd/grey.c
index e1ed65dca20..c60937b0dab 100644
--- a/libexec/spamd/grey.c
+++ b/libexec/spamd/grey.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: grey.c,v 1.23 2006/12/07 21:10:41 otto Exp $ */
+/* $OpenBSD: grey.c,v 1.24 2007/01/04 21:41:37 beck Exp $ */
/*
- * Copyright (c) 2004,2005 Bob Beck. All rights reserved.
+ * Copyright (c) 2004-2006 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
@@ -63,6 +63,20 @@ pid_t db_pid = -1;
int pfdev;
int spamdconf;
+struct db_change {
+ SLIST_ENTRY(db_change) entry;
+ char * key;
+ void * data;
+ size_t dsiz;
+ int act;
+};
+
+#define DBC_ADD 1
+#define DBC_DEL 2
+
+/* db pending changes list */
+SLIST_HEAD(, db_change) db_changes = SLIST_HEAD_INITIALIZER(db_changes);
+
static char *pargv[11]= {
"pfctl", "-p", "/dev/pf", "-q", "-t",
"spamd-white", "-T", "replace", "-f" "-", NULL
@@ -95,7 +109,8 @@ configure_spamd(char **addrs, int count, FILE *sdc)
for (i = 0; i < count; i++)
fprintf(sdc, "%s/32;", addrs[i]);
fprintf(sdc, "\n");
- fflush(sdc);
+ if (fflush(sdc) == EOF)
+ syslog_r(LOG_DEBUG, &sdata, "configure_spamd: fflush failed (%m)");
return(0);
}
@@ -269,11 +284,91 @@ addtrapaddr(char *addr)
return(0);
}
+static int
+queue_change(char *key, char *data, size_t dsiz, int act) {
+ struct db_change *dbc;
+
+ if ((dbc = malloc(sizeof(*dbc))) == NULL) {
+ syslog_r(LOG_DEBUG, &sdata, "malloc failed (queue change)");
+ return(-1);
+ }
+ if ((dbc->key = strdup(key)) == NULL) {
+ syslog_r(LOG_DEBUG, &sdata, "malloc failed (queue change)");
+ free(dbc);
+ return(-1);
+ }
+ if ((dbc->data = malloc(dsiz)) == NULL) {
+ syslog_r(LOG_DEBUG, &sdata, "malloc failed (queue change)");
+ free(dbc->key);
+ free(dbc);
+ return(-1);
+ }
+ memcpy(dbc->data, data, dsiz);
+ dbc->dsiz = dsiz;
+ dbc->act = act;
+ syslog_r(LOG_DEBUG, &sdata,
+ "queueing %s of %s", ((act == DBC_ADD) ? "add" : "deletion"),
+ dbc->key);
+ SLIST_INSERT_HEAD(&db_changes, dbc, entry);
+ return(0);
+}
+
+static int
+do_changes(DB *db) {
+ DBT dbk, dbd;
+ struct db_change *dbc;
+ int ret = 0;
+
+ while (!SLIST_EMPTY(&db_changes)) {
+ dbc = SLIST_FIRST(&db_changes);
+ switch(dbc->act) {
+ case DBC_ADD:
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(dbc->key);
+ dbk.data = dbc->key;
+ memset(&dbd, 0, sizeof(dbd));
+ dbd.size = dbc->dsiz;
+ dbd.data = dbc->data;
+ if (db->put(db, &dbk, &dbd, 0)) {
+ db->sync(db, 0);
+ syslog_r(LOG_ERR, &sdata,
+ "can't add %s to spamd db (%m)", dbc->key);
+ ret = -1;
+ }
+ db->sync(db, 0);
+ break;
+ case DBC_DEL:
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(dbc->key);
+ dbk.data = dbc->key;
+ if (db->del(db, &dbk, 0)) {
+ syslog_r(LOG_ERR, &sdata,
+ "can't delete %s from spamd db (%m)",
+ dbc->key);
+ ret = -1;
+ }
+ break;
+ default:
+ syslog_r(LOG_ERR, &sdata, "Unrecognized db change");
+ ret = -1;
+ }
+ free(dbc->key);
+ dbc->key = NULL;
+ free(dbc->data);
+ dbc->data = NULL;
+ dbc->act = 0;
+ dbc->dsiz = 0;
+ SLIST_REMOVE_HEAD(&db_changes, entry);
+
+ }
+ return(ret);
+}
+
int
greyscan(char *dbname)
{
- BTREEINFO btreeinfo;
+ HASHINFO hashinfo;
DBT dbk, dbd;
DB *db;
struct gdata gd;
@@ -283,9 +378,8 @@ greyscan(char *dbname)
time_t now = time(NULL);
/* walk db, expire, and whitelist */
-
- memset(&btreeinfo, 0, sizeof(btreeinfo));
- db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_BTREE, &btreeinfo);
+ memset(&hashinfo, 0, sizeof(hashinfo));
+ db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo);
if (db == NULL) {
syslog_r(LOG_INFO, &sdata, "dbopen failed (%m)");
return(-1);
@@ -295,6 +389,7 @@ greyscan(char *dbname)
for (r = db->seq(db, &dbk, &dbd, R_FIRST); !r;
r = db->seq(db, &dbk, &dbd, R_NEXT)) {
if ((dbk.size < 1) || dbd.size != sizeof(struct gdata)) {
+ syslog_r(LOG_ERR, &sdata, "bogus entry in spamd database");
goto bad;
}
if (asiz < dbk.size + 1) {
@@ -311,21 +406,13 @@ greyscan(char *dbname)
memcpy(&gd, dbd.data, sizeof(gd));
if (gd.expire <= now && gd.pcount != -2) {
/* get rid of entry */
- if (debug)
- fprintf(stderr, "deleting %s\n", a);
- if (db->del(db, &dbk, 0)) {
+ if (queue_change(a, NULL, 0, DBC_DEL) == -1)
goto bad;
- }
- db->sync(db, 0);
- } else if (gd.pcount == -1) {
+ } else if (gd.pcount == -1) {
/* this is a greytrap hit */
if ((addtrapaddr(a) == -1) &&
- db->del(db, &dbk, 0)) {
- db->sync(db, 0);
+ (queue_change(a, NULL, 0, DBC_DEL) == -1))
goto bad;
- }
- if (debug)
- fprintf(stderr, "trapped %s\n", a);
} else if (gd.pcount >= 0 && gd.pass <= now) {
int tuple = 0;
char *cp;
@@ -335,33 +422,34 @@ greyscan(char *dbname)
* add address to whitelist
* add an address-keyed entry to db
*/
+
cp = strchr(a, '\n');
if (cp != NULL) {
tuple = 1;
*cp = '\0';
}
- if ((addwhiteaddr(a) == -1) && db->del(db, &dbk, 0)) {
- db->sync(db, 0);
- goto bad;
+
+ if (addwhiteaddr(a) == -1) {
+ if (cp != NULL)
+ *cp = '\n';
+ if (queue_change(a, NULL, 0, DBC_DEL) == -1)
+ goto bad;
}
+
if (tuple) {
- if (db->del(db, &dbk, 0)) {
- db->sync(db, 0);
+ if (cp != NULL)
+ *cp = '\n';
+ if (queue_change(a, NULL, 0, DBC_DEL) == -1)
goto bad;
- }
+ if (cp != NULL)
+ *cp = '\0';
/* re-add entry, keyed only by ip */
- memset(&dbk, 0, sizeof(dbk));
- dbk.size = strlen(a);
- dbk.data = a;
- memset(&dbd, 0, sizeof(dbd));
gd.expire = now + whiteexp;
- dbd.size = sizeof(gd);
+ dbd.size = sizeof(gd);
dbd.data = &gd;
- if (db->put(db, &dbk, &dbd, 0)) {
- db->sync(db, 0);
+ if (queue_change(a, (void *) &gd, sizeof(gd),
+ DBC_ADD) == -1)
goto bad;
- }
- db->sync(db, 0);
syslog_r(LOG_DEBUG, &sdata,
"whitelisting %s in %s", a, dbname);
@@ -370,6 +458,7 @@ greyscan(char *dbname)
fprintf(stderr, "whitelisted %s\n", a);
}
}
+ (void) do_changes(db);
db->close(db);
db = NULL;
configure_pf(whitelist, whitecount);
@@ -382,6 +471,7 @@ greyscan(char *dbname)
asiz = 0;
return(0);
bad:
+ (void) do_changes(db);
db->close(db);
db = NULL;
freeaddrlists();
@@ -394,7 +484,7 @@ greyscan(char *dbname)
int
greyupdate(char *dbname, char *ip, char *from, char *to)
{
- BTREEINFO btreeinfo;
+ HASHINFO hashinfo;
DBT dbk, dbd;
DB *db;
char *key = NULL;
@@ -407,8 +497,8 @@ greyupdate(char *dbname, char *ip, char *from, char *to)
now = time(NULL);
/* open with lock, find record, update, close, unlock */
- memset(&btreeinfo, 0, sizeof(btreeinfo));
- db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_BTREE, &btreeinfo);
+ memset(&hashinfo, 0, sizeof(hashinfo));
+ db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo);
if (db == NULL)
return(-1);
if (asprintf(&key, "%s\n%s\n%s", ip, from, to) == -1)
@@ -575,60 +665,158 @@ greyreader(void)
void
greyscanner(void)
{
- int i;
-
for (;;) {
- sleep(DB_SCAN_INTERVAL);
- i = greyscan(PATH_SPAMD_DB);
- if (i == -1)
+ if (greyscan(PATH_SPAMD_DB) == -1)
syslog_r(LOG_NOTICE, &sdata, "scan of %s failed",
PATH_SPAMD_DB);
+ sleep(DB_SCAN_INTERVAL);
}
/* NOTREACHED */
}
-int
-greywatcher(void)
+static void
+drop_privs(void)
{
- int i;
- struct sigaction sa;
+ /*
+ * lose root, continue as non-root user
+ */
+ if (pw) {
+ setgroups(1, &pw->pw_gid);
+ setegid(pw->pw_gid);
+ setgid(pw->pw_gid);
+ seteuid(pw->pw_uid);
+ setuid(pw->pw_uid);
+ }
+}
- pfdev = open("/dev/pf", O_RDWR);
- if (pfdev == -1) {
- syslog_r(LOG_ERR, &sdata, "open of /dev/pf failed (%m)");
+static void
+convert_spamd_db(void)
+{
+ char sfn[] = "/var/db/spamd.XXXXXXXXX";
+ int r, fd = -1;
+ DB *db1, *db2;
+ BTREEINFO btreeinfo;
+ HASHINFO hashinfo;
+ DBT dbk, dbd;
+
+ /* try to open the db as a BTREE */
+ memset(&btreeinfo, 0, sizeof(btreeinfo));
+ db1 = dbopen(PATH_SPAMD_DB, O_EXLOCK|O_RDWR, 0600, DB_BTREE,
+ &btreeinfo);
+ if (db1 == NULL) {
+ syslog_r(LOG_ERR, &sdata,
+ "corrupt db in %s, remove and restart", PATH_SPAMD_DB);
exit(1);
}
+
+ if ((fd = mkstemp(sfn)) == -1) {
+ syslog_r(LOG_ERR, &sdata,
+ "can't convert %s: mkstemp failed (%m)", PATH_SPAMD_DB);
+ exit(1);
+
+ }
+ memset(&hashinfo, 0, sizeof(hashinfo));
+ db2 = dbopen(sfn, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo);
+ if (db2 == NULL) {
+ unlink(sfn);
+ syslog_r(LOG_ERR, &sdata,
+ "can't convert %s: can't dbopen %s (%m)", PATH_SPAMD_DB,
+ sfn);
+ }
- /* check to see if /var/db/spamd exists, if not, create it */
- if ((i = open(PATH_SPAMD_DB, O_RDWR, 0)) == -1 && errno == ENOENT) {
- i = open(PATH_SPAMD_DB, O_RDWR|O_CREAT, 0644);
- if (i == -1) {
- syslog_r(LOG_ERR, &sdata, "create %s failed (%m)",
- PATH_SPAMD_DB);
- exit(1);
+ memset(&dbk, 0, sizeof(dbk));
+ memset(&dbd, 0, sizeof(dbd));
+ for (r = db1->seq(db1, &dbk, &dbd, R_FIRST); !r;
+ r = db1->seq(db1, &dbk, &dbd, R_NEXT)) {
+ if (db2->put(db2, &dbk, &dbd, 0)) {
+ db2->sync(db2, 0);
+ db2->close(db2);
+ db1->close(db1);
+ unlink(sfn);
+ syslog_r(LOG_ERR, &sdata,
+ "can't convert %s - remove and restart",
+ PATH_SPAMD_DB);
+ exit(1);
+ }
}
- /* if we are dropping privs, chown to that user */
- if (pw && (fchown(i, pw->pw_uid, pw->pw_gid) == -1)) {
- syslog_r(LOG_ERR, &sdata, "chown %s failed (%m)",
+ db2->sync(db2, 0);
+ db2->close(db2);
+ db1->sync(db1, 0);
+ db1->close(db1);
+ rename(sfn, PATH_SPAMD_DB);
+ close(fd);
+ /* if we are dropping privs, chown to that user */
+ if (pw && (chown(PATH_SPAMD_DB, pw->pw_uid, pw->pw_gid) == -1)) {
+ syslog_r(LOG_ERR, &sdata,
+ "chown %s failed (%m)", PATH_SPAMD_DB);
+ exit(1);
+ }
+}
+
+static void
+check_spamd_db(void)
+{
+ HASHINFO hashinfo;
+ int i = -1;
+ DB *db;
+
+ /* check to see if /var/db/spamd exists, if not, create it */
+ memset(&hashinfo, 0, sizeof(hashinfo));
+ db = dbopen(PATH_SPAMD_DB, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo);
+
+ if (db == NULL) {
+ switch (errno) {
+ case ENOENT:
+ i = open(PATH_SPAMD_DB, O_RDWR|O_CREAT, 0644);
+ if (i == -1) {
+ syslog_r(LOG_ERR, &sdata,
+ "create %s failed (%m)", PATH_SPAMD_DB);
+ exit(1);
+ }
+ /* if we are dropping privs, chown to that user */
+ if (pw && (fchown(i, pw->pw_uid, pw->pw_gid) == -1)) {
+ syslog_r(LOG_ERR, &sdata,
+ "chown %s failed (%m)", PATH_SPAMD_DB);
+ exit(1);
+ }
+ close(i);
+ drop_privs();
+ return;
+ break;
+ case EFTYPE:
+ /*
+ * db may be old BTREE instead of HASH, attempt to
+ * convert.
+ */
+ convert_spamd_db();
+ drop_privs();
+ return;
+ break;
+ default:
+ syslog_r(LOG_ERR, &sdata, "open of %s failed (%m)",
PATH_SPAMD_DB);
exit(1);
}
}
- if (i != -1)
- close(i);
+ db->sync(db, 0);
+ db->close(db);
+ drop_privs();
+}
- /*
- * lose root, continue as non-root user
- * XXX Should not be _spamd - as it currently is.
- */
- if (pw) {
- setgroups(1, &pw->pw_gid);
- setegid(pw->pw_gid);
- setgid(pw->pw_gid);
- seteuid(pw->pw_uid);
- setuid(pw->pw_uid);
+
+int
+greywatcher(void)
+{
+ struct sigaction sa;
+
+ pfdev = open("/dev/pf", O_RDWR);
+ if (pfdev == -1) {
+ syslog_r(LOG_ERR, &sdata, "open of /dev/pf failed (%m)");
+ exit(1);
}
+ check_spamd_db();
+
db_pid = fork();
switch(db_pid) {
case -1:
@@ -645,6 +833,7 @@ greywatcher(void)
/* NOTREACHED */
_exit(1);
}
+
/*
* parent, scans db periodically for changes and updates
* pf whitelist table accordingly.
diff --git a/libexec/spamlogd/spamlogd.c b/libexec/spamlogd/spamlogd.c
index 21d31c98a87..5e1f316df77 100644
--- a/libexec/spamlogd/spamlogd.c
+++ b/libexec/spamlogd/spamlogd.c
@@ -1,9 +1,9 @@
-/* $OpenBSD: spamlogd.c,v 1.14 2006/11/03 19:39:33 henning Exp $ */
+/* $OpenBSD: spamlogd.c,v 1.15 2007/01/04 21:41:37 beck Exp $ */
/*
* Copyright (c) 2006 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2006 Berk D. Demir.
- * Copyright (c) 2004 Bob Beck.
+ * Copyright (c) 2004-2007 Bob Beck.
* Copyright (c) 2001 Theo de Raadt.
* Copyright (c) 2001 Can Erkin Acar.
* All rights reserved
@@ -187,7 +187,7 @@ logpkt_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
int
dbupdate(char *dbname, char *ip)
{
- BTREEINFO btreeinfo;
+ HASHINFO hashinfo;
DBT dbk, dbd;
DB *db;
struct gdata gd;
@@ -196,8 +196,8 @@ dbupdate(char *dbname, char *ip)
struct in_addr ia;
now = time(NULL);
- memset(&btreeinfo, 0, sizeof(btreeinfo));
- db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_BTREE, &btreeinfo);
+ memset(&hashinfo, 0, sizeof(hashinfo));
+ db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo);
if (db == NULL) {
logmsg(LOG_ERR, "Can not open db %s: %s", dbname,
strerror(errno));
diff --git a/usr.sbin/spamdb/spamdb.c b/usr.sbin/spamdb/spamdb.c
index e4028f3fd23..82648231b4b 100644
--- a/usr.sbin/spamdb/spamdb.c
+++ b/usr.sbin/spamdb/spamdb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: spamdb.c,v 1.18 2006/12/09 20:04:27 jmc Exp $ */
+/* $OpenBSD: spamdb.c,v 1.19 2007/01/04 21:41:37 beck Exp $ */
/*
* Copyright (c) 2004 Bob Beck. All rights reserved.
@@ -29,6 +29,7 @@
#include <time.h>
#include <netdb.h>
#include <ctype.h>
+#include <errno.h>
#include "grey.h"
@@ -252,7 +253,7 @@ int
main(int argc, char **argv)
{
int i, ch, action = 0, type = WHITE, r = 0;
- BTREEINFO btreeinfo;
+ HASHINFO hashinfo;
DB *db;
while ((ch = getopt(argc, argv, "adtT")) != -1) {
@@ -277,12 +278,17 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
- memset(&btreeinfo, 0, sizeof(btreeinfo));
- btreeinfo.cachesize = 8192 * 128;
- db = dbopen(PATH_SPAMD_DB, O_EXLOCK|O_RDWR, 0600, DB_BTREE,
- &btreeinfo);
- if (db == NULL)
- err(1, "cannot open %s for writing", PATH_SPAMD_DB);
+ memset(&hashinfo, 0, sizeof(hashinfo));
+ db = dbopen(PATH_SPAMD_DB, O_EXLOCK|O_RDWR, 0600, DB_HASH,
+ &hashinfo);
+ if (db == NULL) {
+ if (errno == EFTYPE)
+ err(1,
+ "%s is old, run current spamd to convert it",
+ PATH_SPAMD_DB);
+ else
+ err(1, "cannot open %s for writing", PATH_SPAMD_DB);
+ }
switch (action) {
case 0: