summaryrefslogtreecommitdiffstats
path: root/libexec/spamd
diff options
context:
space:
mode:
authorhenning <henning@openbsd.org>2015-02-07 10:45:19 +0000
committerhenning <henning@openbsd.org>2015-02-07 10:45:19 +0000
commita2913c44cb7f5c198ff8f2f723e9cc379cd5785c (patch)
treef5342197b5df1ba2e4396c42e0d92f66e2059ab6 /libexec/spamd
parentAdd support for interface-mtu (option 26). (diff)
downloadwireguard-openbsd-a2913c44cb7f5c198ff8f2f723e9cc379cd5785c.tar.xz
wireguard-openbsd-a2913c44cb7f5c198ff8f2f723e9cc379cd5785c.zip
add STARTTLS support, using the shiny libtls.
Rationale: when you publish DANE records for certificate pinning, you MUST offer TLS on the indicated service. Not offering TLS is verboten since that would re-open the door for a MitM. This is obviously fundamentally incompatible with having spamd in front of your mailservers - spamd kinda is a MitM here, but intentional and utterly valid. DANE is desirable because it allows one to not have to trust the broken SSL CA model, and, depending on the mode chosen, even show the SSL cert mafia the middle finger by not needing them at all. ok reyk jsing bob
Diffstat (limited to 'libexec/spamd')
-rw-r--r--libexec/spamd/Makefile6
-rw-r--r--libexec/spamd/spamd.812
-rw-r--r--libexec/spamd/spamd.c134
3 files changed, 135 insertions, 17 deletions
diff --git a/libexec/spamd/Makefile b/libexec/spamd/Makefile
index 01c94b30b34..2994861fb62 100644
--- a/libexec/spamd/Makefile
+++ b/libexec/spamd/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.10 2013/08/21 16:13:29 millert Exp $
+# $OpenBSD: Makefile,v 1.11 2015/02/07 10:45:19 henning Exp $
PROG= spamd
SRCS= spamd.c sdl.c gdcopy.c grey.c sync.c
@@ -6,7 +6,7 @@ MAN= spamd.8
CFLAGS+= -Wall -Wstrict-prototypes
-LDADD+= -lcrypto
-DPADD+= ${LIBCRYPTO}
+LDADD+= -ltls -lssl -lcrypto
+DPADD+= ${LIBTLS} ${LIBSSL} ${LIBCRYPTO}
.include <bsd.prog.mk>
diff --git a/libexec/spamd/spamd.8 b/libexec/spamd/spamd.8
index 9ff89f64e09..c811eb454ff 100644
--- a/libexec/spamd/spamd.8
+++ b/libexec/spamd/spamd.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: spamd.8,v 1.123 2014/11/22 18:15:41 deraadt Exp $
+.\" $OpenBSD: spamd.8,v 1.124 2015/02/07 10:45:19 henning 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: November 22 2014 $
+.Dd $Mdocdate: February 7 2015 $
.Dt SPAMD 8
.Os
.Sh NAME
@@ -33,12 +33,14 @@
.Bk -words
.Op Fl 45bdv
.Op Fl B Ar maxblack
+.Op Fl C Ar file
.Op Fl c Ar maxcon
.Oo
.Fl G
.Ar passtime : Ns Ar greyexp : Ns Ar whiteexp
.Oc
.Op Fl h Ar hostname
+.Op Fl K Ar file
.Op Fl l Ar address
.Op Fl M Ar address
.Op Fl n Ar name
@@ -130,6 +132,9 @@ When this value is exceeded new blacklisted connections will not be stuttered
at.
.It Fl b
Run in blacklist-only mode.
+.It Fl C Ar file
+Load the certificate for TLS from the given
+.Ar file .
.It Fl c Ar maxcon
The maximum number of concurrent connections to allow.
.Ar maxcon
@@ -156,6 +161,9 @@ and
to 864 (hours, approximately 36 days).
.It Fl h Ar hostname
The hostname that is reported in the SMTP banner.
+.It Fl K Ar file
+Load the private key for TLS from the given
+.Ar file .
.It Fl l Ar address
Specify the local address to which
.Nm
diff --git a/libexec/spamd/spamd.c b/libexec/spamd/spamd.c
index ea6a06f15d4..52a25e03417 100644
--- a/libexec/spamd/spamd.c
+++ b/libexec/spamd/spamd.c
@@ -1,6 +1,7 @@
-/* $OpenBSD: spamd.c,v 1.122 2015/01/16 06:39:50 deraadt Exp $ */
+/* $OpenBSD: spamd.c,v 1.123 2015/02/07 10:45:19 henning Exp $ */
/*
+ * Copyright (c) 2015 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2002-2007 Bob Beck. All rights reserved.
* Copyright (c) 2002 Theo de Raadt. All rights reserved.
*
@@ -22,6 +23,7 @@
#include <sys/sysctl.h>
#include <sys/resource.h>
#include <sys/signal.h>
+#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -37,6 +39,7 @@
#include <syslog.h>
#include <unistd.h>
#include <limits.h>
+#include <tls.h>
#include <netdb.h>
@@ -58,6 +61,7 @@ struct con {
char caddr[32];
char helo[MAX_MAIL], mail[MAX_MAIL], rcpt[MAX_MAIL];
struct sdlist **blacklists;
+ struct tls *cctx;
/*
* we will do stuttering by changing these to time_t's of
@@ -102,6 +106,7 @@ char *loglists(struct con *);
void getcaddr(struct con *);
void gethelo(char *, size_t, char *);
int read_configline(FILE *);
+void spamd_tls_init(char *, char *);
char hostname[HOST_NAME_MAX+1];
struct syslog_data sdata = SYSLOG_DATA_INIT;
@@ -119,6 +124,8 @@ struct passwd *pw;
pid_t jail_pid = -1;
u_short cfg_port;
u_short sync_port;
+struct tls_config *tlscfg;
+struct tls *tlsctx;
extern struct sdlist *blacklists;
extern int pfdev;
@@ -157,10 +164,10 @@ usage(void)
extern char *__progname;
fprintf(stderr,
- "usage: %s [-45bdv] [-B maxblack] [-c maxcon] "
+ "usage: %s [-45bdv] [-B maxblack] [-C file] [-c maxcon] "
"[-G passtime:greyexp:whiteexp]\n"
- "\t[-h hostname] [-l address] [-M address] [-n name] [-p port]\n"
- "\t[-S secs] [-s secs] "
+ "\t[-h hostname] [-K file] [-l address] [-M address] [-n name]\n"
+ "\t[-p port] [-S secs] [-s secs] "
"[-w window] [-Y synctarget] [-y synclisten]\n",
__progname);
@@ -419,6 +426,32 @@ read_configline(FILE *config)
return (0);
}
+void
+spamd_tls_init(char *keyfile, char *certfile)
+{
+ if (keyfile == NULL && certfile == NULL)
+ return;
+ if (keyfile == NULL || certfile == NULL)
+ errx(1, "need key and certificate for TLS");
+
+ if (tls_init() != 0)
+ errx(1, "failed to initialise tls");
+ if ((tlscfg = tls_config_new()) == NULL)
+ errx(1, "failed to get tls config");
+ if ((tlsctx = tls_server()) == NULL)
+ errx(1, "failed to get tls server");
+ /* might need user-specified ciphers, tls_config_set_ciphers */
+
+ if (tls_config_set_cert_file(tlscfg, certfile) != 0)
+ err(1, "could not load certificate %s", certfile);
+ if (tls_config_set_key_file(tlscfg, keyfile) != 0)
+ err(1, "could not load key %s", keyfile);
+ if (tls_configure(tlsctx, tlscfg) != 0)
+ errx(1, "failed to configure TLS - %s", tls_error(tlsctx));
+
+ /* set hostname to cert's CN unless explicitely given? */
+}
+
int
append_error_string(struct con *cp, size_t off, char *fmt, int af, void *ia)
{
@@ -675,6 +708,7 @@ initcon(struct con *cp, int fd, struct sockaddr *sa)
if (grow_obuf(cp, 0) == NULL)
err(1, "malloc");
cp->fd = fd;
+ cp->cctx = NULL;
if (len > sizeof(cp->ss))
errx(1, "sockaddr size");
if (sa->sa_family != AF_INET)
@@ -718,7 +752,11 @@ closecon(struct con *cp)
{
time_t tt;
- close(cp->fd);
+ if (cp->cctx) {
+ tls_close(cp->cctx);
+ tls_free(cp->cctx);
+ } else
+ close(cp->fd);
slowdowntill = 0;
time(&tt);
@@ -797,9 +835,16 @@ nextstate(struct con *cp)
snprintf(cp->obuf, cp->osize,
"501 helo requires domain name.\r\n");
} else {
- snprintf(cp->obuf, cp->osize,
- "250 Hello, spam sender. "
- "Pleased to be wasting your time.\r\n");
+ if (tlsctx != NULL && cp->blacklists == NULL &&
+ match(cp->ibuf, "EHLO")) {
+ snprintf(cp->obuf, cp->osize,
+ "250 STARTTLS\r\n");
+ nextstate = 7;
+ } else {
+ snprintf(cp->obuf, cp->osize,
+ "250 Hello, spam sender. Pleased "
+ "to be wasting your time.\r\n");
+ }
}
cp->op = cp->obuf;
cp->ol = strlen(cp->op);
@@ -810,6 +855,7 @@ nextstate(struct con *cp)
}
goto mail;
case 2:
+ tlsinitdone:
/* sent 250 Hello, wait for input */
cp->ip = cp->ibuf;
cp->il = sizeof(cp->ibuf) - 1;
@@ -885,6 +931,39 @@ nextstate(struct con *cp)
cp->state = 5;
cp->r = t;
break;
+ case 7:
+ /* sent 250 STARTTLS, wait for input */
+ cp->ip = cp->ibuf;
+ cp->il = sizeof(cp->ibuf) - 1;
+ cp->laststate = cp->state;
+ cp->state = 8;
+ cp->r = t;
+ break;
+ case 8:
+ if (tlsctx != NULL && cp->blacklists == NULL &&
+ cp->cctx == NULL && match(cp->ibuf, "STARTTLS")) {
+ snprintf(cp->obuf, cp->osize,
+ "220 glad you want to burn more CPU cycles on "
+ "your spam\r\n");
+ cp->op = cp->obuf;
+ cp->ol = strlen(cp->op);
+ cp->laststate = cp->state;
+ cp->state = 9;
+ cp->w = t + cp->stutter;
+ break;
+ }
+ goto mail;
+ case 9:
+ if (tls_accept_socket(tlsctx, &cp->cctx, cp->fd) == -1) {
+ snprintf(cp->obuf, cp->osize,
+ "500 STARTTLS failed\r\n");
+ cp->op = cp->obuf;
+ cp->ol = strlen(cp->op);
+ cp->laststate = cp->state;
+ cp->state = 98;
+ goto done;
+ }
+ goto tlsinitdone;
case 50:
spam:
@@ -993,7 +1072,14 @@ handler(struct con *cp)
int n;
if (cp->r) {
- n = read(cp->fd, cp->ip, cp->il);
+ if (cp->cctx) {
+ size_t outlen;
+ n = -1;
+ if (tls_read(cp->cctx, cp->ip, cp->il, &outlen) == 0)
+ n = outlen;
+ } else
+ n = read(cp->fd, cp->ip, cp->il);
+
if (n == 0)
closecon(cp);
else if (n == -1) {
@@ -1023,6 +1109,7 @@ void
handlew(struct con *cp, int one)
{
int n;
+ size_t outlen;
/* kill stutter on greylisted connections after initial delay */
if (cp->stutter && greylist && cp->blacklists == NULL &&
@@ -1032,7 +1119,13 @@ handlew(struct con *cp, int one)
if (cp->w) {
if (*cp->op == '\n' && !cp->sr) {
/* insert \r before \n */
- n = write(cp->fd, "\r", 1);
+ if (cp->cctx) {
+ n = -1;
+ if (tls_write(cp->cctx, "\r", 1, &outlen) == 0)
+ n = outlen;
+ } else
+ n = write(cp->fd, "\r", 1);
+
if (n == 0) {
closecon(cp);
goto handled;
@@ -1047,7 +1140,14 @@ handlew(struct con *cp, int one)
cp->sr = 1;
else
cp->sr = 0;
- n = write(cp->fd, cp->op, (one && cp->stutter) ? 1 : cp->ol);
+ if (cp->cctx) {
+ n = -1;
+ if (tls_write(cp->cctx, cp->op, cp->ol, &outlen) == 0)
+ n = outlen;
+ } else
+ n = write(cp->fd, cp->op,
+ (one && cp->stutter) ? 1 : cp->ol);
+
if (n == 0)
closecon(cp);
else if (n == -1) {
@@ -1100,6 +1200,8 @@ main(int argc, char *argv[])
const char *errstr;
char *sync_iface = NULL;
char *sync_baddr = NULL;
+ char *tlskeyfile = NULL;
+ char *tlscertfile = NULL;
tzset();
openlog_r("spamd", LOG_PID | LOG_NDELAY, LOG_DAEMON, &sdata);
@@ -1122,7 +1224,7 @@ main(int argc, char *argv[])
if (maxblack > maxfiles)
maxblack = maxfiles;
while ((ch =
- getopt(argc, argv, "45l:c:B:p:bdG:h:s:S:M:n:vw:y:Y:")) != -1) {
+ getopt(argc, argv, "45l:c:B:p:bdG:h:s:S:M:n:vw:y:Y:C:K:")) != -1) {
switch (ch) {
case '4':
nreply = "450";
@@ -1212,6 +1314,12 @@ main(int argc, char *argv[])
sync_baddr = optarg;
syncrecv++;
break;
+ case 'C':
+ tlscertfile = optarg;
+ break;
+ case 'K':
+ tlskeyfile = optarg;
+ break;
default:
usage();
break;
@@ -1360,6 +1468,8 @@ main(int argc, char *argv[])
}
jail:
+ spamd_tls_init(tlskeyfile, tlscertfile);
+
if (chroot("/var/empty") == -1 || chdir("/") == -1) {
syslog(LOG_ERR, "cannot chdir to /var/empty.");
exit(1);