summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);