aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgilles <gilles@poolp.org>2016-07-06 17:32:37 +0200
committergilles <gilles@poolp.org>2016-07-06 17:32:37 +0200
commit0f08a78d655a165c2dc29f201c6fc1e270ca3a1d (patch)
treea3e17b7b6d87cf85a16ea2937f2698a8cc1b3f58
parentremove all experimental filters from -extras (diff)
downloadOpenSMTPD-extras-0f08a78d655a165c2dc29f201c6fc1e270ca3a1d.tar.xz
OpenSMTPD-extras-0f08a78d655a165c2dc29f201c6fc1e270ca3a1d.zip
experimental filter-dnsbl
-rw-r--r--configure.ac13
-rw-r--r--extras/filters/Makefile.am4
-rw-r--r--extras/filters/filter-dnsbl/Makefile.am14
-rw-r--r--extras/filters/filter-dnsbl/filter-dnsbl.8103
-rw-r--r--extras/filters/filter-dnsbl/filter_dnsbl.c187
5 files changed, 321 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index ea33729..5f394b7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -996,6 +996,19 @@ AC_SUBST([pkglibexecdir])
# individual modules
#
+HAVE_FILTER_DNSBL=no
+AC_ARG_WITH([filter-dnsbl],
+ [ --with-filter-dnsbl Enable filter dnsbl],
+ [
+ if test "x$withval" != "xno" ; then
+ AC_DEFINE([HAVE_FILTER_DNSBL], [1],
+ [Define if you have filter dnsbl])
+ HAVE_FILTER_DNSBL=yes
+ fi
+ ]
+)
+AM_CONDITIONAL([HAVE_FILTER_DNSBL], [test $HAVE_FILTER_DNSBL = yes])
+
HAVE_FILTER_MONKEY=no
AC_ARG_WITH([filter-monkey],
[ --with-filter-monkey Enable filter monkey],
diff --git a/extras/filters/Makefile.am b/extras/filters/Makefile.am
index eb670bb..7c7240b 100644
--- a/extras/filters/Makefile.am
+++ b/extras/filters/Makefile.am
@@ -1,5 +1,9 @@
SUBDIRS =
+if HAVE_FILTER_DNSBL
+SUBDIRS += filter-dnsbl
+endif
+
if HAVE_FILTER_MONKEY
SUBDIRS += filter-monkey
endif
diff --git a/extras/filters/filter-dnsbl/Makefile.am b/extras/filters/filter-dnsbl/Makefile.am
new file mode 100644
index 0000000..1db4695
--- /dev/null
+++ b/extras/filters/filter-dnsbl/Makefile.am
@@ -0,0 +1,14 @@
+include $(top_srcdir)/mk/paths.mk
+include $(top_srcdir)/mk/filter.mk
+include $(top_srcdir)/mk/experimental.mk
+
+pkglibexec_PROGRAMS = filter-dnsbl
+
+filter_dnsbl_SOURCES = $(SRCS)
+filter_dnsbl_SOURCES += filter_dnsbl.c
+
+man_MANS = filter-dnsbl.8
+
+if !NO_LIBASR
+LDADD += -lasr
+endif
diff --git a/extras/filters/filter-dnsbl/filter-dnsbl.8 b/extras/filters/filter-dnsbl/filter-dnsbl.8
new file mode 100644
index 0000000..254a5db
--- /dev/null
+++ b/extras/filters/filter-dnsbl/filter-dnsbl.8
@@ -0,0 +1,103 @@
+.\"
+.\" Copyright (c) 2015, 2016 Joerg Jung <jung@openbsd.org>
+.\"
+.\" 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 $Mdocdate: May 16 2016 $
+.Dt FILTER-DNSBL 8
+.Os
+.Sh NAME
+.Nm filter-dnsbl
+.Nd smtpd filter for DNSBL checks
+.Sh SYNOPSIS
+.Nm
+.Op Fl Cdv
+.Op Fl c Ar path
+.Op Fl h Ar host
+.Op Fl w Ar host
+.Sh DESCRIPTION
+.Nm
+is a filter for
+.Xr smtpd 8
+which can be used to check new connections against a
+DNS-based Blackhole List (DNSBL) and optional a DNS-based Whitelist (DNSWL).
+.Pp
+The options are as follows:
+.Bl -tag -width "-c path"
+.It Fl C
+No
+.Xr chroot 2
+mode, if this option is specified,
+.Nm
+will not run in a chroot.
+.It Fl c Ar path
+Set the chroot
+.Ar path .
+.Pp
+The default
+.Ar path
+is
+.Pa /var/empty
+(compile option).
+.It Fl d
+Debug mode, if this option is specified,
+.Nm
+will run in the foreground and log to
+.Em stderr .
+.It Fl h Ar host
+Set the DNSBL
+.Ar host
+to be queried.
+.Pp
+The default
+.Ar host
+is dnsbl.sorbs.net.
+.It Fl v
+Produce more verbose output.
+.It Fl w Ar host
+Set the DNSWL
+.Ar host
+to be queried.
+.Pp
+The default is to not query a DNSWL
+.Ar host .
+.El
+.Pp
+.Nm
+runs by default in a chroot.
+In order to make DNS queries working, a valid
+.Pa resolv.conf
+is required within the chroot.
+.Pp
+The debug and verbose options given with the
+.Xr smtpd 8
+invocation are intially passed to
+.Nm .
+.Pp
+The remote IP address of new connections is used in a DNS query against the
+DNSBL and optional DNSWL.
+If the remote address is listed in the DNSBL, the connection is rejected,
+otherwise accepted.
+If the optional DNSWL host is given, it is queried first and if the remote
+address is listed the connection is accepted (without checking the DNSBL).
+.Sh SEE ALSO
+.Xr chroot 2 ,
+.Xr filter_api 3 ,
+.Xr smtpd.conf 5 ,
+.Xr smtpd 8
+.Sh HISTORY
+The first version of
+.Nm
+was written in 2013.
+.Sh AUTHORS
+.An Eric Faurot Aq Mt eric@openbsd.org
diff --git a/extras/filters/filter-dnsbl/filter_dnsbl.c b/extras/filters/filter-dnsbl/filter_dnsbl.c
new file mode 100644
index 0000000..6835538
--- /dev/null
+++ b/extras/filters/filter-dnsbl/filter_dnsbl.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
+ * Copyright (c) 2016 Joerg Jung <jung@openbsd.org>
+ *
+ * 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 "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <asr.h>
+
+#include <smtpd-api.h>
+
+struct dnsbl {
+ uint64_t id;
+ struct asr_query *aq_bl, *aq_wl;
+};
+
+static const char *dnsbl_host = "dnsbl.sorbs.net", *dnswl_host;
+
+static void
+dnsbl_event_black(struct asr_result *ar, void *p)
+{
+ struct dnsbl *dl = p;
+
+ if (ar->ar_addrinfo)
+ freeaddrinfo(ar->ar_addrinfo);
+ if (ar->ar_gai_errno == EAI_NODATA || ar->ar_gai_errno == EAI_NONAME) {
+ log_info("info: event_black: ACCEPT address");
+ filter_api_accept(dl->id);
+ } else if (ar->ar_gai_errno) {
+ log_warnx("warn: session %016"PRIx64": event_black: getaddrinfo '%s'", dl->id, gai_strerror(ar->ar_gai_errno));
+ filter_api_reject_code(dl->id, FILTER_FAIL, 451, "4.7.1 DNSBL filter failed");
+ } else {
+ log_warnx("warn: session %016"PRIx64": event_black: REJECT address", dl->id);
+ filter_api_reject_code(dl->id, FILTER_CLOSE, 554, "5.7.1 Address in DNSBL");
+ }
+ free(dl);
+}
+
+static void
+dnsbl_event_white(struct asr_result *ar, void *p)
+{
+ struct dnsbl *dl = p;
+
+ if (ar->ar_addrinfo)
+ freeaddrinfo(ar->ar_addrinfo);
+ if (ar->ar_gai_errno == EAI_NODATA || ar->ar_gai_errno == EAI_NONAME) {
+ log_debug("debug: event_white: address not in DNSWL");
+ event_asr_run(dl->aq_bl, dnsbl_event_black, dl);
+ } else if (ar->ar_gai_errno) {
+ log_warnx("warn: session %016"PRIx64": event_white: getaddrinfo '%s'", dl->id, gai_strerror(ar->ar_gai_errno));
+ filter_api_reject_code(dl->id, FILTER_FAIL, 451, "4.7.1 DNSBL filter failed");
+ asr_abort(dl->aq_bl);
+ free(dl);
+ } else {
+ log_info("info: event_white: ACCEPT address");
+ filter_api_accept(dl->id);
+ asr_abort(dl->aq_bl);
+ free(dl);
+ }
+}
+
+static struct asr_query *
+dnsbl_query(in_addr_t ia, const char *h)
+{
+ struct asr_query *aq;
+ struct addrinfo hints;
+ char buf[512];
+
+ ia = ntohl(ia);
+ if (snprintf(buf, sizeof(buf), "%d.%d.%d.%d.%s.",
+ ia & 0xff, (ia >> 8) & 0xff, (ia >> 16) & 0xff, (ia >> 24) & 0xff, h) >= sizeof(buf)) {
+ log_warnx("warn: query: host name too long: %s", buf);
+ return NULL;
+ }
+ log_debug("debug: query: checking %s", buf);
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if (!(aq = getaddrinfo_async(buf, NULL, &hints, NULL))) {
+ log_warn("warn: query: getaddrinfo_async");
+ return NULL;
+ }
+ return aq;
+}
+
+static int
+dnsbl_on_connect(uint64_t id, struct filter_connect *conn)
+{
+ struct dnsbl *dl;
+ in_addr_t ia;
+
+ if (conn->remote.ss_family != AF_INET)
+ return filter_api_accept(id);
+ ia = ((const struct sockaddr_in *)&conn->remote)->sin_addr.s_addr;
+ dl = xcalloc(1, sizeof(struct dnsbl), "on_connect");
+ dl->id = id;
+ if (!(dl->aq_bl = dnsbl_query(ia, dnsbl_host)) || (dnswl_host &&
+ !(dl->aq_wl = dnsbl_query(ia, dnswl_host)))) {
+ free(dl);
+ return filter_api_reject_code(id, FILTER_FAIL, 451, "4.7.1 DNSBL filter failed");
+ }
+ dnswl_host ? event_asr_run(dl->aq_wl, dnsbl_event_white, dl) :
+ event_asr_run(dl->aq_bl, dnsbl_event_black, dl);
+ return 1;
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch, C = 0, d = 0, v = 0;
+ char *c = NULL, *h = NULL, *w = NULL;
+
+ log_init(1);
+
+ while ((ch = getopt(argc, argv, "Cc:dh:vw:")) != -1) {
+ switch (ch) {
+ case 'C':
+ C = 1;
+ break;
+ case 'c':
+ c = optarg;
+ break;
+ case 'd':
+ d = 1;
+ break;
+ case 'h':
+ h = optarg;
+ break;
+ case 'v':
+ v |= TRACE_DEBUG;
+ break;
+ case 'w':
+ w = optarg;
+ break;
+ default:
+ log_warnx("warn: bad option");
+ return 1;
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (c)
+ c = strip(c);
+ if (h)
+ dnsbl_host = strip(h);
+ if (w)
+ dnswl_host = strip(w);
+
+ log_init(d);
+ log_verbose(v);
+
+ log_debug("debug: starting...");
+
+ filter_api_on_connect(dnsbl_on_connect);
+ if (c)
+ filter_api_set_chroot(c); /* getaddrinfo requires resolv.conf */
+ if (C)
+ filter_api_no_chroot();
+
+ filter_api_loop();
+ log_debug("debug: exiting");
+
+ return 1;
+}