aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBaptiste Daroussin <bapt@FreeBSD.org>2021-09-22 08:52:34 +0000
committerBaptiste Daroussin <bapt@FreeBSD.org>2021-09-22 08:52:34 +0000
commit16a84834c2798d54c29d4794ef87472c9afd943e (patch)
treedc7032d812564b4f1083ec6158553d6cd5627b39
parent598ad972718c41f5438bf7654286bbce15c9cea1 (diff)
downloadsrc-16a84834c2798d54c29d4794ef87472c9afd943e.tar.gz
src-16a84834c2798d54c29d4794ef87472c9afd943e.zip
dma: import git snapshot 2021-07-10vendor/dma/2021-07-10
-rw-r--r--Makefile2
-rw-r--r--VERSION2
-rw-r--r--conf.c20
-rw-r--r--crypto.c51
-rw-r--r--dfcompat.c6
-rw-r--r--dma-mbox-create.c1
-rw-r--r--dma.825
-rw-r--r--dma.c40
-rw-r--r--dma.conf6
-rw-r--r--dma.h17
-rw-r--r--dns.c5
-rw-r--r--[-rwxr-xr-x]get-version.sh0
-rw-r--r--local.c6
-rw-r--r--mail.c24
-rw-r--r--net.c285
-rw-r--r--spool.c3
-rw-r--r--util.c21
17 files changed, 380 insertions, 134 deletions
diff --git a/Makefile b/Makefile
index aed2ef7246cf..8cae5b28f98b 100644
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,7 @@ CC?= gcc
CFLAGS?= -O -pipe
LDADD?= -lssl -lcrypto -lresolv
-CFLAGS+= -Wall -DDMA_VERSION='"${version}"' -DLIBEXEC_PATH='"${LIBEXEC}"' -DCONF_PATH='"${CONFDIR}"'
+CFLAGS+= -Wall -Wno-format-truncation -DDMA_VERSION='"${version}"' -DLIBEXEC_PATH='"${LIBEXEC}"' -DCONF_PATH='"${CONFDIR}"'
INSTALL?= install -p
CHGRP?= chgrp
diff --git a/VERSION b/VERSION
index 5416288bc5ef..3c58828758cd 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v0.11
+v0.13
diff --git a/conf.c b/conf.c
index 3e3c7d3d8bd2..0c88bb193013 100644
--- a/conf.c
+++ b/conf.c
@@ -218,10 +218,26 @@ parse_conf(const char *config_path)
config.masquerade_user = user;
} else if (strcmp(word, "STARTTLS") == 0 && data == NULL)
config.features |= STARTTLS;
- else if (strcmp(word, "OPPORTUNISTIC_TLS") == 0 && data == NULL)
+ else if (strcmp(word, "FINGERPRINT") == 0) {
+ if (strlen(data) != SHA256_DIGEST_LENGTH * 2) {
+ errlogx(EX_CONFIG, "invalid sha256 fingerprint length");
+ }
+ unsigned char *fingerprint = malloc(SHA256_DIGEST_LENGTH);
+ if (fingerprint == NULL) {
+ errlogx(EX_CONFIG, "fingerprint allocation failed");
+ }
+ unsigned int i;
+ for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
+ if(sscanf(data + 2 * i, "%02hhx", &fingerprint[i]) != 1) {
+ errlogx(EX_CONFIG, "failed to read fingerprint");
+ }
+ }
+ free(data);
+ config.fingerprint = fingerprint;
+ } else if (strcmp(word, "OPPORTUNISTIC_TLS") == 0 && data == NULL)
config.features |= TLS_OPP;
else if (strcmp(word, "SECURETRANSFER") == 0 && data == NULL)
- config.features |= SECURETRANS;
+ config.features |= SECURETRANSFER;
else if (strcmp(word, "DEFER") == 0 && data == NULL)
config.features |= DEFER;
else if (strcmp(word, "INSECURE") == 0 && data == NULL)
diff --git a/crypto.c b/crypto.c
index 897b55bfdcfc..368238b9d632 100644
--- a/crypto.c
+++ b/crypto.c
@@ -40,6 +40,8 @@
#include <openssl/pem.h>
#include <openssl/rand.h>
+#include <strings.h>
+#include <string.h>
#include <syslog.h>
#include "dma.h"
@@ -76,8 +78,31 @@ init_cert_file(SSL_CTX *ctx, const char *path)
return (0);
}
+static int
+verify_server_fingerprint(const X509 *cert)
+{
+ unsigned char fingerprint[EVP_MAX_MD_SIZE] = {0};
+ unsigned int fingerprint_len = 0;
+ if(!X509_digest(cert, EVP_sha256(), fingerprint, &fingerprint_len)) {
+ syslog(LOG_WARNING, "failed to load fingerprint of server certicate: %s",
+ ssl_errstr());
+ return (1);
+ }
+ if(fingerprint_len != SHA256_DIGEST_LENGTH) {
+ syslog(LOG_WARNING, "sha256 fingerprint has unexpected length of %d bytes",
+ fingerprint_len);
+ return (1);
+ }
+ if(memcmp(fingerprint, config.fingerprint, SHA256_DIGEST_LENGTH) != 0) {
+ syslog(LOG_WARNING, "fingerprints do not match");
+ return (1);
+ }
+ syslog(LOG_DEBUG, "successfully verified server certificate fingerprint");
+ return (0);
+}
+
int
-smtp_init_crypto(int fd, int feature)
+smtp_init_crypto(int fd, int feature, struct smtp_features* features)
{
SSL_CTX *ctx = NULL;
#if (OPENSSL_VERSION_NUMBER >= 0x00909000L)
@@ -93,7 +118,12 @@ smtp_init_crypto(int fd, int feature)
SSL_library_init();
SSL_load_error_strings();
- meth = TLSv1_client_method();
+ // Allow any possible version
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ meth = TLS_client_method();
+#else
+ meth = SSLv23_client_method();
+#endif
ctx = SSL_CTX_new(meth);
if (ctx == NULL) {
@@ -113,13 +143,12 @@ smtp_init_crypto(int fd, int feature)
/*
* If the user wants STARTTLS, we have to send EHLO here
*/
- if (((feature & SECURETRANS) != 0) &&
+ if (((feature & SECURETRANSFER) != 0) &&
(feature & STARTTLS) != 0) {
/* TLS init phase, disable SSL_write */
config.features |= NOSSL;
- send_remote_command(fd, "EHLO %s", hostname());
- if (read_remote(fd, 0, NULL) == 2) {
+ if (perform_server_greeting(fd, features) == 0) {
send_remote_command(fd, "STARTTLS");
if (read_remote(fd, 0, NULL) != 2) {
if ((feature & TLS_OPP) == 0) {
@@ -130,7 +159,12 @@ smtp_init_crypto(int fd, int feature)
return (0);
}
}
+ } else {
+ syslog(LOG_ERR, "remote delivery deferred: could not perform server greeting: %s",
+ neterr);
+ return (1);
}
+
/* End of TLS init phase, enable SSL_write/read */
config.features &= ~NOSSL;
}
@@ -155,7 +189,7 @@ smtp_init_crypto(int fd, int feature)
/* Open SSL connection */
error = SSL_connect(config.ssl);
- if (error < 0) {
+ if (error != 1) {
syslog(LOG_ERR, "remote delivery deferred: SSL handshake failed fatally: %s",
ssl_errstr());
return (1);
@@ -166,6 +200,11 @@ smtp_init_crypto(int fd, int feature)
if (cert == NULL) {
syslog(LOG_WARNING, "remote delivery deferred: Peer did not provide certificate: %s",
ssl_errstr());
+ return (1);
+ }
+ if(config.fingerprint != NULL && verify_server_fingerprint(cert)) {
+ X509_free(cert);
+ return (1);
}
X509_free(cert);
diff --git a/dfcompat.c b/dfcompat.c
index 014fa88b8d49..d4ecc1d74ae9 100644
--- a/dfcompat.c
+++ b/dfcompat.c
@@ -16,7 +16,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $
- * $FreeBSD$
+ * $FreeBSD: src/lib/libc/string/strlcpy.c,v 1.10 2008/10/19 10:11:35 delphij Exp $
* $DragonFly: src/lib/libc/string/strlcpy.c,v 1.4 2005/09/18 16:32:34 asmodai Exp $
*/
@@ -85,7 +85,7 @@ strlcpy(char *dst, const char *src, size_t siz)
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD$
+ * $FreeBSD: src/lib/libc/stdlib/reallocf.c,v 1.3 1999/08/28 00:01:37 peter Exp $
* $DragonFly: src/lib/libc/stdlib/reallocf.c,v 1.2 2003/06/17 04:26:46 dillon Exp $
*/
#include <stdlib.h>
@@ -96,7 +96,7 @@ reallocf(void *ptr, size_t size)
void *nptr;
nptr = realloc(ptr, size);
- if (!nptr && ptr)
+ if (!nptr && ptr && size != 0)
free(ptr);
return (nptr);
}
diff --git a/dma-mbox-create.c b/dma-mbox-create.c
index 80088601f2d7..37011e0355d5 100644
--- a/dma-mbox-create.c
+++ b/dma-mbox-create.c
@@ -47,6 +47,7 @@
#include <paths.h>
#include <pwd.h>
#include <stdio.h>
+#include <string.h>
#include <syslog.h>
#include <unistd.h>
diff --git a/dma.8 b/dma.8
index 76ab174fe590..a906f75447f2 100644
--- a/dma.8
+++ b/dma.8
@@ -82,15 +82,18 @@ config file setting below.
.Pp
All other
.Ar mode Ns
-s are are ignored.
+s are ignored.
.It Fl D
Don't run in the background.
Useful for debugging.
.It Fl f Ar sender
Set sender address (envelope-from) to
.Ar sender .
-This overrides the value of the environment variable
-.Ev EMAIL .
+This overrides the value of the
+.Ev EMAIL
+environment variable, but is overridden by the
+.Sq MASQUERADE
+config file setting.
.It Fl i
Ignore dots alone on lines by themselves in incoming messages.
This should be set if you are reading data from a file.
@@ -213,7 +216,7 @@ Just stick with the default.
Path to the
.Sq auth.conf
file.
-.It Ic SECURETRANS Xo
+.It Ic SECURETRANSFER Xo
(boolean, default=commented)
.Xc
Uncomment if you want TLS/SSL secured transfer.
@@ -222,7 +225,12 @@ Uncomment if you want TLS/SSL secured transfer.
.Xc
Uncomment if you want to use STARTTLS.
Only useful together with
-.Sq SECURETRANS .
+.Sq SECURETRANSFER .
+.It Ic FINGERPRINT Xo
+(string, default=empty)
+.Xc
+Pin the server certificate by specifying its SHA256 fingerprint.
+Only makes sense if you use a smarthost.
.It Ic OPPORTUNISTIC_TLS Xo
(boolean, default=commented)
.Xc
@@ -234,7 +242,7 @@ the outside mail exchangers; in opportunistic TLS mode, the connection will
be encrypted if the remote server supports STARTTLS, but an unencrypted
delivery will still be made if the negotiation fails.
Only useful together with
-.Sq SECURETRANS
+.Sq SECURETRANSFER
and
.Sq STARTTLS .
.It Ic CERTFILE Xo
@@ -283,7 +291,7 @@ as the hostname.
Masquerade the envelope-from addresses with this address/hostname.
Use this setting if mails are not accepted by destination mail servers
because your sender domain is invalid.
-This setting is overridden by the
+This setting overrides the
.Fl f
flag and the
.Ev EMAIL
@@ -308,6 +316,7 @@ will send all mails as
.Ql Sm off Va username @percolator .
.Sm on
.It Ic NULLCLIENT Xo
+(boolean, default=commented)
.Xc
Bypass aliases and local delivery, and instead forward all mails to
the defined
@@ -328,6 +337,8 @@ Used to set the sender address (envelope-from).
Use a plain address, in the form of
.Li user@example.com .
This value will be overridden when the
+.Sq MASQUERADE
+config file setting or the
.Fl f
flag is used.
.El
diff --git a/dma.c b/dma.c
index e643e0f4ac9b..ae0018a243d2 100644
--- a/dma.c
+++ b/dma.c
@@ -85,6 +85,7 @@ struct config config = {
.mailname = NULL,
.masquerade_host = NULL,
.masquerade_user = NULL,
+ .fingerprint = NULL,
};
@@ -100,15 +101,14 @@ set_from(struct queue *queue, const char *osender)
const char *addr;
char *sender;
- if (osender) {
+ if (config.masquerade_user) {
+ addr = config.masquerade_user;
+ } else if (osender) {
addr = osender;
} else if (getenv("EMAIL") != NULL) {
addr = getenv("EMAIL");
} else {
- if (config.masquerade_user)
- addr = config.masquerade_user;
- else
- addr = username;
+ addr = username;
}
if (!strchr(addr, '@')) {
@@ -331,8 +331,8 @@ retry:
switch (error) {
case 0:
- delqueue(it);
syslog(LOG_INFO, "<%s> delivery successful", it->addr);
+ delqueue(it);
exit(EX_OK);
case 1:
@@ -422,9 +422,10 @@ main(int argc, char **argv)
{
struct sigaction act;
char *sender = NULL;
+ char *own_name = NULL;
struct queue queue;
int i, ch;
- int nodot = 0, showq = 0, queue_only = 0;
+ int nodot = 0, showq = 0, queue_only = 0, newaliases = 0;
int recp_from_header = 0;
set_username();
@@ -458,19 +459,17 @@ main(int argc, char **argv)
bzero(&queue, sizeof(queue));
LIST_INIT(&queue.queue);
- if (strcmp(basename(argv[0]), "mailq") == 0) {
+ own_name = basename(argv[0]);
+
+ if (strcmp(own_name, "mailq") == 0) {
argv++; argc--;
showq = 1;
if (argc != 0)
errx(EX_USAGE, "invalid arguments");
goto skipopts;
- } else if (strcmp(argv[0], "newaliases") == 0) {
- logident_base = "dma";
- setlogident(NULL);
-
- if (read_aliases() != 0)
- errx(EX_SOFTWARE, "could not parse aliases file `%s'", config.aliases);
- exit(EX_OK);
+ } else if (strcmp(own_name, "newaliases") == 0) {
+ newaliases = 1;
+ goto skipopts;
}
opterr = 0;
@@ -481,7 +480,7 @@ main(int argc, char **argv)
if (optarg[0] == 'c' || optarg[0] == 'm') {
break;
}
- /* else FALLTRHOUGH */
+ /* Else FALLTHROUGH */
case 'b':
/* -bX is being ignored, except for -bp */
if (optarg[0] == 'p') {
@@ -491,7 +490,7 @@ main(int argc, char **argv)
queue_only = 1;
break;
}
- /* else FALLTRHOUGH */
+ /* Else FALLTHROUGH */
case 'D':
daemonize = 0;
break;
@@ -511,7 +510,7 @@ main(int argc, char **argv)
/* -oX is being ignored, except for -oi */
if (optarg[0] != 'i')
break;
- /* else FALLTRHOUGH */
+ /* Else FALLTHROUGH */
case 'O':
break;
case 'i':
@@ -545,7 +544,7 @@ main(int argc, char **argv)
doqueue = 1;
break;
}
- /* FALLTHROUGH */
+ /* Else FALLTHROUGH */
default:
fprintf(stderr, "invalid argument: `-%c'\n", optopt);
@@ -596,6 +595,9 @@ skipopts:
if (read_aliases() != 0)
errlog(EX_SOFTWARE, "could not parse aliases file `%s'", config.aliases);
+ if (newaliases)
+ return(0);
+
if ((sender = set_from(&queue, sender)) == NULL)
errlog(EX_SOFTWARE, NULL);
diff --git a/dma.conf b/dma.conf
index 1cc2bf5bc843..fa95fc1a0c22 100644
--- a/dma.conf
+++ b/dma.conf
@@ -18,13 +18,17 @@
# SMTP authentication
#AUTHPATH /etc/dma/auth.conf
-# Uncomment if yout want TLS/SSL support
+# Uncomment if you want TLS/SSL support
#SECURETRANSFER
# Uncomment if you want STARTTLS support (only used in combination with
# SECURETRANSFER)
#STARTTLS
+# Pin the server certificate by specifying its SHA256 fingerprint.
+# Only makes sense if you use a smarthost.
+#FINGERPRINT 1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF
+
# Uncomment if you have specified STARTTLS above and it should be allowed
# to fail ("opportunistic TLS", use an encrypted connection when available
# but allow an unencrypted one to servers that do not support it)
diff --git a/dma.h b/dma.h
index 593417617d3d..9e7f6cd2c431 100644
--- a/dma.h
+++ b/dma.h
@@ -51,6 +51,7 @@
#define BUF_SIZE 2048
#define ERRMSG_SIZE 1024
#define USERNAME_SIZE 50
+#define EHLO_RESPONSE_SIZE BUF_SIZE
#define MIN_RETRY 300 /* 5 minutes */
#define MAX_RETRY (3*60*60) /* retry at least every 3 hours */
#define MAX_TIMEOUT (5*24*60*60) /* give up after 5 days */
@@ -62,7 +63,7 @@
#define CON_TIMEOUT (5*60) /* Connection timeout per RFC5321 */
#define STARTTLS 0x002 /* StartTLS support */
-#define SECURETRANS 0x004 /* SSL/TLS in general */
+#define SECURETRANSFER 0x004 /* SSL/TLS in general */
#define NOSSL 0x008 /* Do not use SSL */
#define DEFER 0x010 /* Defer mails */
#define INSECURE 0x020 /* Allow plain login w/o encryption */
@@ -137,6 +138,7 @@ struct config {
const char *mailname;
const char *masquerade_host;
const char *masquerade_user;
+ const unsigned char *fingerprint;
/* XXX does not belong into config */
SSL *ssl;
@@ -160,6 +162,15 @@ struct mx_hostentry {
struct sockaddr_storage sa;
};
+struct smtp_auth_mechanisms {
+ int cram_md5;
+ int login;
+};
+
+struct smtp_features {
+ struct smtp_auth_mechanisms auth;
+ int starttls;
+};
/* global variables */
extern struct aliases aliases;
@@ -187,7 +198,7 @@ void parse_authfile(const char *);
/* crypto.c */
void hmac_md5(unsigned char *, int, unsigned char *, int, unsigned char *);
int smtp_auth_md5(int, char *, char *);
-int smtp_init_crypto(int, int);
+int smtp_init_crypto(int, int, struct smtp_features*);
/* dns.c */
int dns_get_mx_list(const char *, int, struct mx_hostentry **, int);
@@ -196,6 +207,7 @@ int dns_get_mx_list(const char *, int, struct mx_hostentry **, int);
char *ssl_errstr(void);
int read_remote(int, int, char *);
ssize_t send_remote_command(int, const char*, ...) __attribute__((__nonnull__(2), __format__ (__printf__, 2, 3)));
+int perform_server_greeting(int, struct smtp_features*);
int deliver_remote(struct qitem *);
/* base64.c */
@@ -227,6 +239,7 @@ int readmail(struct queue *, int, int);
/* util.c */
const char *hostname(void);
+const char *systemhostname(void);
void setlogident(const char *, ...) __attribute__((__format__ (__printf__, 1, 2)));
void errlog(int, const char *, ...) __attribute__((__format__ (__printf__, 2, 3)));
void errlogx(int, const char *, ...) __attribute__((__format__ (__printf__, 2, 3)));
diff --git a/dns.c b/dns.c
index bd28c4db724c..449e6b463caa 100644
--- a/dns.c
+++ b/dns.c
@@ -271,11 +271,6 @@ err:
*he = hosts;
return (err);
-
- free(ans);
- if (hosts != NULL)
- free(hosts);
- return (err);
}
#if defined(TESTING)
diff --git a/get-version.sh b/get-version.sh
index d9691ac37c95..d9691ac37c95 100755..100644
--- a/get-version.sh
+++ b/get-version.sh
diff --git a/local.c b/local.c
index 94e217928983..2c3483ea0380 100644
--- a/local.c
+++ b/local.c
@@ -44,6 +44,8 @@
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
+#include <strings.h>
+#include <string.h>
#include <syslog.h>
#include <unistd.h>
@@ -80,7 +82,7 @@ create_mbox(const char *name)
for (i = 3; i <= maxfd; ++i)
close(i);
- execl(LIBEXEC_PATH "/dma-mbox-create", "dma-mbox-create", name, NULL);
+ execl(LIBEXEC_PATH "/dma-mbox-create", "dma-mbox-create", name, (char *)NULL);
syslog(LOG_ERR, "cannot execute "LIBEXEC_PATH"/dma-mbox-create: %m");
exit(EX_SOFTWARE);
@@ -218,7 +220,7 @@ retry:
/*
* mboxro processing:
* - escape lines that start with "From " with a > sign.
- * - be reversable by escaping lines that contain an arbitrary
+ * - be reversible by escaping lines that contain an arbitrary
* number of > signs, followed by "From ", i.e. />*From / in regexp.
* - strict mbox processing only requires escaping after empty lines,
* yet most MUAs seem to relax this requirement and will treat any
diff --git a/mail.c b/mail.c
index a6d11fcae685..40fb5db44489 100644
--- a/mail.c
+++ b/mail.c
@@ -36,6 +36,8 @@
#include <errno.h>
#include <inttypes.h>
#include <signal.h>
+#include <strings.h>
+#include <string.h>
#include <syslog.h>
#include <unistd.h>
@@ -70,7 +72,7 @@ bounce(struct qitem *it, const char *reason)
error = fprintf(bounceq.mailf,
"Received: from MAILER-DAEMON\n"
"\tid %s\n"
- "\tby %s (%s);\n"
+ "\tby %s (%s on %s);\n"
"\t%s\n"
"X-Original-To: <%s>\n"
"From: MAILER-DAEMON <>\n"
@@ -88,7 +90,7 @@ bounce(struct qitem *it, const char *reason)
"%s\n"
"\n",
bounceq.id,
- hostname(), VERSION,
+ hostname(), VERSION, systemhostname(),
rfc822date(),
it->addr,
it->sender,
@@ -188,8 +190,7 @@ again:
switch (*s) {
case ' ':
case '\t':
- s++;
- /* continue */
+ ps->state = MAIN;
break;
default:
@@ -198,6 +199,7 @@ again:
goto newaddr;
return (0);
}
+ break;
case QUIT:
return (0);
@@ -352,6 +354,7 @@ readmail(struct queue *queue, int nodot, int recp_from_header)
int had_from = 0;
int had_messagid = 0;
int had_date = 0;
+ int had_first_line = 0;
int had_last_line = 0;
int nocopy = 0;
@@ -361,12 +364,12 @@ readmail(struct queue *queue, int nodot, int recp_from_header)
"Received: from %s (uid %d)\n"
"\t(envelope-from %s)\n"
"\tid %s\n"
- "\tby %s (%s);\n"
+ "\tby %s (%s on %s);\n"
"\t%s\n",
username, useruid,
queue->sender,
queue->id,
- hostname(), VERSION,
+ hostname(), VERSION, systemhostname(),
rfc822date());
if ((ssize_t)error < 0)
return (-1);
@@ -389,6 +392,15 @@ readmail(struct queue *queue, int nodot, int recp_from_header)
line[linelen + 1] = 0;
had_last_line = 1;
}
+ if (!had_first_line) {
+ /*
+ * Ignore a leading RFC-976 From_ or >From_ line mistakenly
+ * inserted by some programs.
+ */
+ if (strprefixcmp(line, "From ") == 0 || strprefixcmp(line, ">From ") == 0)
+ continue;
+ had_first_line = 1;
+ }
if (!had_headers) {
/*
* Unless this is a continuation, switch of
diff --git a/net.c b/net.c
index 47ee92849421..e8e2634a9386 100644
--- a/net.c
+++ b/net.c
@@ -53,6 +53,8 @@
#include <netdb.h>
#include <setjmp.h>
#include <signal.h>
+#include <strings.h>
+#include <string.h>
#include <syslog.h>
#include <unistd.h>
@@ -93,13 +95,13 @@ send_remote_command(int fd, const char* fmt, ...)
strcat(cmd, "\r\n");
len = strlen(cmd);
- if (((config.features & SECURETRANS) != 0) &&
+ if (((config.features & SECURETRANSFER) != 0) &&
((config.features & NOSSL) == 0)) {
while ((s = SSL_write(config.ssl, (const char*)cmd, len)) <= 0) {
s = SSL_get_error(config.ssl, s);
if (s != SSL_ERROR_WANT_READ &&
s != SSL_ERROR_WANT_WRITE) {
- strncpy(neterr, ssl_errstr(), sizeof(neterr));
+ strlcpy(neterr, ssl_errstr(), sizeof(neterr));
return (-1);
}
}
@@ -146,15 +148,15 @@ read_remote(int fd, int extbufsize, char *extbuf)
memmove(buff, buff + pos, len - pos);
len -= pos;
pos = 0;
- if (((config.features & SECURETRANS) != 0) &&
+ if (((config.features & SECURETRANSFER) != 0) &&
(config.features & NOSSL) == 0) {
if ((rlen = SSL_read(config.ssl, buff + len, sizeof(buff) - len)) == -1) {
- strncpy(neterr, ssl_errstr(), sizeof(neterr));
+ strlcpy(neterr, ssl_errstr(), sizeof(neterr));
goto error;
}
} else {
if ((rlen = read(fd, buff + len, sizeof(buff) - len)) == -1) {
- strncpy(neterr, strerror(errno), sizeof(neterr));
+ strlcpy(neterr, strerror(errno), sizeof(neterr));
goto error;
}
}
@@ -247,64 +249,70 @@ error:
* Handle SMTP authentication
*/
static int
-smtp_login(int fd, char *login, char* password)
+smtp_login(int fd, char *login, char* password, const struct smtp_features* features)
{
char *temp;
int len, res = 0;
- res = smtp_auth_md5(fd, login, password);
- if (res == 0) {
- return (0);
- } else if (res == -2) {
- /*
- * If the return code is -2, then then the login attempt failed,
- * do not try other login mechanisms
- */
- return (1);
- }
-
- if ((config.features & INSECURE) != 0 ||
- (config.features & SECURETRANS) != 0) {
- /* Send AUTH command according to RFC 2554 */
- send_remote_command(fd, "AUTH LOGIN");
- if (read_remote(fd, 0, NULL) != 3) {
- syslog(LOG_NOTICE, "remote delivery deferred:"
- " AUTH login not available: %s",
- neterr);
+ // CRAM-MD5
+ if (features->auth.cram_md5) {
+ res = smtp_auth_md5(fd, login, password);
+ if (res == 0) {
+ return (0);
+ } else if (res == -2) {
+ /*
+ * If the return code is -2, then then the login attempt failed,
+ * do not try other login mechanisms
+ */
return (1);
}
+ }
- len = base64_encode(login, strlen(login), &temp);
- if (len < 0) {
+ // LOGIN
+ if (features->auth.login) {
+ if ((config.features & INSECURE) != 0 ||
+ (config.features & SECURETRANSFER) != 0) {
+ /* Send AUTH command according to RFC 2554 */
+ send_remote_command(fd, "AUTH LOGIN");
+ if (read_remote(fd, 0, NULL) != 3) {
+ syslog(LOG_NOTICE, "remote delivery deferred:"
+ " AUTH login not available: %s",
+ neterr);
+ return (1);
+ }
+
+ len = base64_encode(login, strlen(login), &temp);
+ if (len < 0) {
encerr:
- syslog(LOG_ERR, "can not encode auth reply: %m");
- return (1);
- }
+ syslog(LOG_ERR, "can not encode auth reply: %m");
+ return (1);
+ }
- send_remote_command(fd, "%s", temp);
- free(temp);
- res = read_remote(fd, 0, NULL);
- if (res != 3) {
- syslog(LOG_NOTICE, "remote delivery %s: AUTH login failed: %s",
- res == 5 ? "failed" : "deferred", neterr);
- return (res == 5 ? -1 : 1);
- }
+ send_remote_command(fd, "%s", temp);
+ free(temp);
+ res = read_remote(fd, 0, NULL);
+ if (res != 3) {
+ syslog(LOG_NOTICE, "remote delivery %s: AUTH login failed: %s",
+ res == 5 ? "failed" : "deferred", neterr);
+ return (res == 5 ? -1 : 1);
+ }
- len = base64_encode(password, strlen(password), &temp);
- if (len < 0)
- goto encerr;
-
- send_remote_command(fd, "%s", temp);
- free(temp);
- res = read_remote(fd, 0, NULL);
- if (res != 2) {
- syslog(LOG_NOTICE, "remote delivery %s: Authentication failed: %s",
- res == 5 ? "failed" : "deferred", neterr);
- return (res == 5 ? -1 : 1);
+ len = base64_encode(password, strlen(password), &temp);
+ if (len < 0)
+ goto encerr;
+
+ send_remote_command(fd, "%s", temp);
+ free(temp);
+ res = read_remote(fd, 0, NULL);
+ if (res != 2) {
+ syslog(LOG_NOTICE, "remote delivery %s: Authentication failed: %s",
+ res == 5 ? "failed" : "deferred", neterr);
+ return (res == 5 ? -1 : 1);
+ }
+ } else {
+ syslog(LOG_WARNING, "non-encrypted SMTP login is disabled in config, so skipping it. ");
+ return (1);
}
- } else {
- syslog(LOG_WARNING, "non-encrypted SMTP login is disabled in config, so skipping it. ");
- return (1);
}
return (0);
@@ -339,7 +347,7 @@ static void
close_connection(int fd)
{
if (config.ssl != NULL) {
- if (((config.features & SECURETRANS) != 0) &&
+ if (((config.features & SECURETRANSFER) != 0) &&
((config.features & NOSSL) == 0))
SSL_shutdown(config.ssl);
SSL_free(config.ssl);
@@ -348,11 +356,116 @@ close_connection(int fd)
close(fd);
}
+static void parse_auth_line(char* line, struct smtp_auth_mechanisms* auth) {
+ // Skip the auth prefix
+ line += strlen("AUTH ");
+
+ char* method = strtok(line, " ");
+ while (method) {
+ if (strcmp(method, "CRAM-MD5") == 0)
+ auth->cram_md5 = 1;
+
+ else if (strcmp(method, "LOGIN") == 0)
+ auth->login = 1;
+
+ method = strtok(NULL, " ");
+ }
+}
+
+int perform_server_greeting(int fd, struct smtp_features* features) {
+ /*
+ Send EHLO
+ XXX allow HELO fallback
+ */
+ send_remote_command(fd, "EHLO %s", hostname());
+
+ char buffer[EHLO_RESPONSE_SIZE];
+ memset(buffer, 0, sizeof(buffer));
+
+ int res = read_remote(fd, sizeof(buffer) - 1, buffer);
+
+ // Got an unexpected response
+ if (res != 2)
+ return -1;
+
+ // Reset all features
+ memset(features, 0, sizeof(*features));
+
+ // Run through the buffer line by line
+ char linebuffer[EHLO_RESPONSE_SIZE];
+ char* p = buffer;
+
+ while (*p) {
+ char* line = linebuffer;
+ while (*p && *p != '\n') {
+ *line++ = *p++;
+ }
+
+ // p should never point to NULL after the loop
+ // above unless we reached the end of the buffer.
+ // In that case we will raise an error.
+ if (!*p) {
+ return -1;
+ }
+
+ // Otherwise p points to the newline character which
+ // we will skip.
+ p++;
+
+ // Terminte the string (and remove the carriage-return character)
+ *--line = '\0';
+ line = linebuffer;
+
+ // End main loop for empty lines
+ if (*line == '\0')
+ break;
+
+ // Process the line
+ // - Must start with 250, followed by dash or space
+ // - We won't check for the correct usage of space and dash because
+ // that is already done in read_remote().
+ if ((strncmp(line, "250-", 4) != 0) && (strncmp(line, "250 ", 4) != 0)) {
+ syslog(LOG_ERR, "Invalid line: %s\n", line);
+ return -1;
+ }
+
+ // Skip the prefix
+ line += 4;
+
+ // Check for STARTTLS
+ if (strcmp(line, "STARTTLS") == 0)
+ features->starttls = 1;
+
+ // Parse authentication mechanisms
+ else if (strncmp(line, "AUTH ", 5) == 0)
+ parse_auth_line(line, &features->auth);
+ }
+
+ syslog(LOG_DEBUG, "Server greeting successfully completed");
+
+ // STARTTLS
+ if (features->starttls)
+ syslog(LOG_DEBUG, " Server supports STARTTLS");
+ else
+ syslog(LOG_DEBUG, " Server does not support STARTTLS");
+
+ // Authentication
+ if (features->auth.cram_md5) {
+ syslog(LOG_DEBUG, " Server supports CRAM-MD5 authentication");
+ }
+ if (features->auth.login) {
+ syslog(LOG_DEBUG, " Server supports LOGIN authentication");
+ }
+
+ return 0;
+}
+
static int
deliver_to_host(struct qitem *it, struct mx_hostentry *host)
{
struct authuser *a;
- char line[1000];
+ struct smtp_features features;
+ char line[1000], *addrtmp = NULL, *to_addr;
size_t linelen;
int fd, error = 0, do_auth = 0, res = 0;
@@ -365,24 +478,26 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host)
if (fd < 0)
return (1);
-#define READ_REMOTE_CHECK(c, exp) \
- res = read_remote(fd, 0, NULL); \
- if (res == 5) { \
- syslog(LOG_ERR, "remote delivery to %s [%s] failed after %s: %s", \
- host->host, host->addr, c, neterr); \
- snprintf(errmsg, sizeof(errmsg), "%s [%s] did not like our %s:\n%s", \
- host->host, host->addr, c, neterr); \
- error = -1; \
- goto out; \
- } else if (res != exp) { \
- syslog(LOG_NOTICE, "remote delivery deferred: %s [%s] failed after %s: %s", \
- host->host, host->addr, c, neterr); \
- error = 1; \
- goto out; \
- }
+#define READ_REMOTE_CHECK(c, exp) \
+ do { \
+ res = read_remote(fd, 0, NULL); \
+ if (res == 5) { \
+ syslog(LOG_ERR, "remote delivery to %s [%s] failed after %s: %s", \
+ host->host, host->addr, c, neterr); \
+ snprintf(errmsg, sizeof(errmsg), "%s [%s] did not like our %s:\n%s", \
+ host->host, host->addr, c, neterr); \
+ error = -1; \
+ goto out; \
+ } else if (res != exp) { \
+ syslog(LOG_NOTICE, "remote delivery deferred: %s [%s] failed after %s: %s", \
+ host->host, host->addr, c, neterr); \
+ error = 1; \
+ goto out; \
+ } \
+ } while (0)
/* Check first reply from remote host */
- if ((config.features & SECURETRANS) == 0 ||
+ if ((config.features & SECURETRANSFER) == 0 ||
(config.features & STARTTLS) != 0) {
config.features |= NOSSL;
READ_REMOTE_CHECK("connect", 2);
@@ -390,8 +505,8 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host)
config.features &= ~NOSSL;
}
- if ((config.features & SECURETRANS) != 0) {
- error = smtp_init_crypto(fd, config.features);
+ if ((config.features & SECURETRANSFER) != 0) {
+ error = smtp_init_crypto(fd, config.features, &features);
if (error == 0)
syslog(LOG_DEBUG, "SSL initialization successful");
else
@@ -401,10 +516,12 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host)
READ_REMOTE_CHECK("connect", 2);
}
- /* XXX allow HELO fallback */
- /* XXX record ESMTP keywords */
- send_remote_command(fd, "EHLO %s", hostname());
- READ_REMOTE_CHECK("EHLO", 2);
+ // Say EHLO
+ if (perform_server_greeting(fd, &features) != 0) {
+ syslog(LOG_ERR, "Could not perform server greeting at %s [%s]: %s",
+ host->host, host->addr, neterr);
+ return -1;
+ }
/*
* Use SMTP authentication if the user defined an entry for the remote
@@ -423,7 +540,7 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host)
* encryption.
*/
syslog(LOG_INFO, "using SMTP authentication for user %s", a->login);
- error = smtp_login(fd, a->login, a->password);
+ error = smtp_login(fd, a->login, a->password, &features);
if (error < 0) {
syslog(LOG_ERR, "remote delivery failed:"
" SMTP login failed: %m");
@@ -442,8 +559,17 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host)
READ_REMOTE_CHECK("MAIL FROM", 2);
/* XXX send ESMTP ORCPT */
- send_remote_command(fd, "RCPT TO:<%s>", it->addr);
- READ_REMOTE_CHECK("RCPT TO", 2);
+ if ((addrtmp = strdup(it->addr)) == NULL) {
+ syslog(LOG_CRIT, "remote delivery deferred: unable to allocate memory");
+ error = 1;
+ goto out;
+ }
+ to_addr = strtok(addrtmp, ",");
+ while (to_addr != NULL) {
+ send_remote_command(fd, "RCPT TO:<%s>", to_addr);
+ READ_REMOTE_CHECK("RCPT TO", 2);
+ to_addr = strtok(NULL, ",");
+ }
send_remote_command(fd, "DATA");
READ_REMOTE_CHECK("DATA", 3);
@@ -485,6 +611,7 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host)
syslog(LOG_INFO, "remote delivery succeeded but QUIT failed: %s", neterr);
out:
+ free(addrtmp);
close_connection(fd);
return (error);
}
diff --git a/spool.c b/spool.c
index e9c9c4355ab4..1cdce7578a4f 100644
--- a/spool.c
+++ b/spool.c
@@ -37,6 +37,7 @@
#include <sys/file.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <ctype.h>
#include <dirent.h>
@@ -45,6 +46,8 @@
#include <fcntl.h>
#include <inttypes.h>
#include <unistd.h>
+#include <strings.h>
+#include <string.h>
#include <syslog.h>
#include "dma.h"
diff --git a/util.c b/util.c
index d528f313d091..d41abe8a55e7 100644
--- a/util.c
+++ b/util.c
@@ -44,6 +44,8 @@
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
+#include <strings.h>
+#include <string.h>
#include <syslog.h>
#include <unistd.h>
@@ -97,6 +99,25 @@ hostname(void)
}
local:
+ snprintf(name, sizeof(name), "%s", systemhostname());
+
+ initialized = 1;
+ return (name);
+}
+
+const char *
+systemhostname(void)
+{
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+ static char name[HOST_NAME_MAX+1];
+ static int initialized = 0;
+ char *s;
+
+ if (initialized)
+ return (name);
+
if (gethostname(name, sizeof(name)) != 0)
*name = 0;
/*