From 0f849a0c7750c78fae43b22e6fdc50f4867c3474 Mon Sep 17 00:00:00 2001 From: reyk Date: Mon, 18 May 2015 16:04:21 +0000 Subject: [PATCH] Change spamd to use divert-to instead of rdr-to. divert-to has many advantages over rdr-to for proxies. For example, it is much easier to use, requires less code, does not depend on /dev/pf, works in-band without the asynchronous lookup (DIOCNATLOOK ioctl), saves us from additional port allocations by the rdr/NAT code, and even avoids potential collisions and race conditions that could theoretically happen with the lookup. Heads up: users will have to update their spamd PF rules from rdr-to to divert-to. spamd now also listens to 127.0.0.1 instead of "any" (0.0.0.0) by default which should be fine with most setups but has to be considered for some special configurations. Based on a diff is almost two years old but got delayed several times ... beck@: "now is the time to get it in" :) Tested by many With help from okan@ OK okan@ beck@ millert@ --- etc/examples/pf.conf | 6 ++-- libexec/spamd/grey.c | 82 +------------------------------------------ libexec/spamd/sdl.c | 4 +-- libexec/spamd/spamd.8 | 20 +++++------ libexec/spamd/spamd.c | 17 +++------ 5 files changed, 21 insertions(+), 108 deletions(-) diff --git a/etc/examples/pf.conf b/etc/examples/pf.conf index 6cb9c9258c7..2dd043aaca6 100644 --- a/etc/examples/pf.conf +++ b/etc/examples/pf.conf @@ -1,4 +1,4 @@ -# $OpenBSD: pf.conf,v 1.1 2014/07/16 12:46:16 deraadt Exp $ +# $OpenBSD: pf.conf,v 1.2 2015/05/18 16:04:21 reyk Exp $ # # See pf.conf(5) for syntax and examples. # Remember to set net.inet.ip.forwarding=1 and/or net.inet6.ip6.forwarding=1 @@ -22,8 +22,8 @@ pass # establish keep-state # rules for spamd(8) #table persist #table persist file "/etc/mail/nospamd" -#pass in on egress proto tcp from any to any port smtp \ -# rdr-to 127.0.0.1 port spamd +#pass in on egress inet proto tcp from any to any port smtp \ +# divert-to 127.0.0.1 port spamd #pass in on egress proto tcp from to any port smtp #pass in log on egress proto tcp from to any port smtp #pass out log on egress proto tcp to any port smtp diff --git a/libexec/spamd/grey.c b/libexec/spamd/grey.c index 71b9f35651d..e91c437b9a9 100644 --- a/libexec/spamd/grey.c +++ b/libexec/spamd/grey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: grey.c,v 1.58 2015/01/13 21:42:59 millert Exp $ */ +/* $OpenBSD: grey.c,v 1.59 2015/05/18 16:04:21 reyk Exp $ */ /* * Copyright (c) 2004-2006 Bob Beck. All rights reserved. @@ -53,14 +53,8 @@ extern int syncsend; /* From netinet/in.h, but only _KERNEL_ gets them. */ #define satosin(sa) ((struct sockaddr_in *)(sa)) #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) -int server_lookup4(struct sockaddr_in *, struct sockaddr_in *, - struct sockaddr_in *); -int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *, - struct sockaddr_in6 *); void configure_spamd(char **, u_int, FILE *); -int server_lookup(struct sockaddr *, struct sockaddr *, - struct sockaddr *); int configure_pf(char **, int); char *dequotetolower(const char *); void readsuffixlists(void); @@ -153,80 +147,6 @@ configure_spamd(char **addrs, u_int count, FILE *sdc) syslog_r(LOG_DEBUG, &sdata, "configure_spamd: fflush failed (%m)"); } - -/* Stolen from ftp-proxy */ -int -server_lookup(struct sockaddr *client, struct sockaddr *proxy, - struct sockaddr *server) -{ - if (client->sa_family == AF_INET) - return (server_lookup4(satosin(client), satosin(proxy), - satosin(server))); - - if (client->sa_family == AF_INET6) - return (server_lookup6(satosin6(client), satosin6(proxy), - satosin6(server))); - - errno = EPROTONOSUPPORT; - return (-1); -} - -int -server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy, - struct sockaddr_in *server) -{ - struct pfioc_natlook pnl; - - memset(&pnl, 0, sizeof pnl); - pnl.direction = PF_OUT; - pnl.af = AF_INET; - pnl.proto = IPPROTO_TCP; - memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4); - memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4); - pnl.sport = client->sin_port; - pnl.dport = proxy->sin_port; - - if (ioctl(pfdev, DIOCNATLOOK, &pnl) == -1) - return (-1); - - memset(server, 0, sizeof(struct sockaddr_in)); - server->sin_len = sizeof(struct sockaddr_in); - server->sin_family = AF_INET; - memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4, - sizeof server->sin_addr.s_addr); - server->sin_port = pnl.rdport; - - return (0); -} - -int -server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy, - struct sockaddr_in6 *server) -{ - struct pfioc_natlook pnl; - - memset(&pnl, 0, sizeof pnl); - pnl.direction = PF_OUT; - pnl.af = AF_INET6; - pnl.proto = IPPROTO_TCP; - memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6); - memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6); - pnl.sport = client->sin6_port; - pnl.dport = proxy->sin6_port; - - if (ioctl(pfdev, DIOCNATLOOK, &pnl) == -1) - return (-1); - - memset(server, 0, sizeof(struct sockaddr_in6)); - server->sin6_len = sizeof(struct sockaddr_in6); - server->sin6_family = AF_INET6; - memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6, - sizeof server->sin6_addr); - server->sin6_port = pnl.rdport; - - return (0); -} - int configure_pf(char **addrs, int count) { diff --git a/libexec/spamd/sdl.c b/libexec/spamd/sdl.c index ae2826dab3a..2fbf36eac03 100644 --- a/libexec/spamd/sdl.c +++ b/libexec/spamd/sdl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdl.c,v 1.21 2015/01/13 21:42:59 millert Exp $ */ +/* $OpenBSD: sdl.c,v 1.22 2015/05/18 16:04:21 reyk Exp $ */ /* * Copyright (c) 2003-2007 Bob Beck. All rights reserved. @@ -23,7 +23,7 @@ * someone is on. Spamd gets the connecting address, and looks it up * against all lists to determine what deferral messages to feed back * to the connecting machine. - The redirection to spamd will happen - * from pf in the kernel, first macth will rdr to us. Spamd (along with + * from pf in the kernel, first match will divert to us. Spamd (along with * setup) must keep track of *all* matches, so as to tell someone all the * lists that they are on. */ diff --git a/libexec/spamd/spamd.8 b/libexec/spamd/spamd.8 index 6380e256055..5144e500326 100644 --- a/libexec/spamd/spamd.8 +++ b/libexec/spamd/spamd.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: spamd.8,v 1.127 2015/04/14 17:29:06 deraadt Exp $ +.\" $OpenBSD: spamd.8,v 1.128 2015/05/18 16:04:21 reyk Exp $ .\" .\" Copyright (c) 2002 Theo de Raadt. All rights reserved. .\" @@ -22,7 +22,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: April 14 2015 $ +.Dd $Mdocdate: May 18 2015 $ .Dt SPAMD 8 .Os .Sh NAME @@ -58,7 +58,7 @@ receiving machine. considers sending hosts to be of three types: .Pp .Em blacklisted -hosts are redirected to +hosts are diverted to .Nm and .Em tarpitted @@ -75,7 +75,7 @@ such as .Xr smtpd 8 . .Pp .Em greylisted -hosts are redirected to +hosts are diverted to .Nm , but .Nm @@ -168,7 +168,7 @@ is to .Xr bind 2 . By default .Nm -listens on all local addresses. +listens on the localhost address 127.0.0.1. .It Fl M Ar address Specify a local IP address which is listed as a low priority MX record, used to identify and trap hosts that connect to MX hosts out of order. @@ -180,7 +180,7 @@ The SMTP version banner that is reported upon initial connection. .It Fl p Ar port Specify a different port number from the default port that .Nm -should listen for redirected SMTP connections on. +should listen for diverted SMTP connections on. The default port is found by looking for the named service .Dq spamd using @@ -273,7 +273,7 @@ table, allowing connections to pass to the real MTA. Any addresses not found in -are redirected to +are diverted to .Nm . .Pp An example @@ -288,7 +288,7 @@ to the SMTP agent (thus bypassing table \*(Ltspamd-white\*(Gt persist table \*(Ltnospamd\*(Gt persist file "/etc/mail/nospamd" pass in on egress proto tcp from any to any port smtp \e - rdr-to 127.0.0.1 port spamd + divert-to 127.0.0.1 port spamd pass in on egress proto tcp from \*(Ltnospamd\*(Gt to any port smtp pass in log on egress proto tcp from \*(Ltspamd-white\*(Gt to any port smtp pass out log on egress proto tcp to any port smtp @@ -467,7 +467,7 @@ However when running in blacklist-only mode, a slightly modified .Xr pf.conf 5 ruleset is required, -redirecting any addresses found in the +diverting any addresses found in the table to .Nm . @@ -476,7 +476,7 @@ are passed to the real MTA. .Bd -literal -offset 4n table \*(Ltspamd\*(Gt persist pass in on egress proto tcp from \*(Ltspamd\*(Gt to any port smtp \e - rdr-to 127.0.0.1 port spamd + divert-to 127.0.0.1 port spamd .Ed .Pp Addresses can be loaded into the diff --git a/libexec/spamd/spamd.c b/libexec/spamd/spamd.c index 3f4933cf3ec..a43aaf7f831 100644 --- a/libexec/spamd/spamd.c +++ b/libexec/spamd/spamd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: spamd.c,v 1.127 2015/04/18 18:28:37 deraadt Exp $ */ +/* $OpenBSD: spamd.c,v 1.128 2015/05/18 16:04:21 reyk Exp $ */ /* * Copyright (c) 2015 Henning Brauer @@ -48,9 +48,6 @@ #include "grey.h" #include "sync.h" -extern int server_lookup(struct sockaddr *, struct sockaddr *, - struct sockaddr *); - struct con { struct pollfd *pfd; int state; @@ -648,23 +645,19 @@ setlog(char *p, size_t len, char *f) } /* - * Get address client connected to, by doing a DIOCNATLOOK call. - * Uses server_lookup code from ftp-proxy. + * Get address client connected to, by doing a getsockname call. + * Must not be used with a NAT'ed connection (use divert-to instead of rdr-to). */ void getcaddr(struct con *cp) { - struct sockaddr_storage spamd_end; - struct sockaddr *sep = (struct sockaddr *) &spamd_end; struct sockaddr_storage original_destination; struct sockaddr *odp = (struct sockaddr *) &original_destination; socklen_t len = sizeof(struct sockaddr_storage); int error; cp->caddr[0] = '\0'; - if (getsockname(cp->pfd->fd, sep, &len) == -1) - return; - if (server_lookup((struct sockaddr *)&cp->ss, sep, odp) != 0) + if (getsockname(cp->pfd->fd, odp, &len) == -1) return; error = getnameinfo(odp, odp->sa_len, cp->caddr, sizeof(cp->caddr), NULL, 0, NI_NUMERICHOST); @@ -1389,7 +1382,7 @@ main(int argc, char *argv[]) if (inet_pton(AF_INET, bind_address, &sin.sin_addr) != 1) err(1, "inet_pton"); } else - sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sin.sin_family = AF_INET; sin.sin_port = htons(port); -- 2.20.1