Skip to content

Instantly share code, notes, and snippets.

@jpmens
Last active March 6, 2020 10:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpmens/ef3a407212bd7d0295da1c9a7169f5c3 to your computer and use it in GitHub Desktop.
Save jpmens/ef3a407212bd7d0295da1c9a7169f5c3 to your computer and use it in GitHub Desktop.
dma in FreeBSD 12.1-RELEASE vs. dma from github

I'm seeing authentication failures and strange messages in /var/log/maillog during dma transfers; this doesn't make sense as my server is offering LOGIN PLAIN.

I obtained a copy of https://github.com/corecode/dma and build cd bsd; make and the result is different (better).

Haven't yet compared sources.

for f in freebsd/contrib/dma/*.?; do diff -u $f ../d/dma/$(basename $f); done|less
--- freebsd/contrib/dma/conf.c 2020-03-06 11:33:23.000000000 +0100
+++ ../d/dma/conf.c 2020-03-06 11:30:13.000000000 +0100
@@ -121,7 +121,7 @@
au = calloc(1, sizeof(*au));
if (au == NULL)
- errlog(EX_OSERR, "calloc()");
+ errlog(EX_OSERR, NULL);
data = strdup(line);
au->login = strsep(&data, "|");
@@ -218,7 +218,22 @@
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");
+ }
+ for (unsigned int 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;
--- freebsd/contrib/dma/crypto.c 2020-03-06 11:33:23.000000000 +0100
+++ ../d/dma/crypto.c 2020-03-06 11:30:13.000000000 +0100
@@ -40,6 +40,7 @@
#include <openssl/pem.h>
#include <openssl/rand.h>
+#include <strings.h>
#include <string.h>
#include <syslog.h>
@@ -78,7 +79,30 @@
}
int
-smtp_init_crypto(int fd, int feature)
+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, struct smtp_features* features)
{
SSL_CTX *ctx = NULL;
#if (OPENSSL_VERSION_NUMBER >= 0x00909000L)
@@ -124,8 +148,7 @@
/* 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) {
@@ -137,6 +160,7 @@
}
}
}
+
/* End of TLS init phase, enable SSL_write/read */
config.features &= ~NOSSL;
}
@@ -161,7 +185,7 @@
/* 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);
@@ -172,6 +196,11 @@
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);
--- freebsd/contrib/dma/dfcompat.c 2020-03-06 11:33:23.000000000 +0100
+++ ../d/dma/dfcompat.c 2020-03-06 11:30:13.000000000 +0100
@@ -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 @@
* 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>
--- freebsd/contrib/dma/dma-mbox-create.c 2020-03-06 11:33:23.000000000 +0100
+++ ../d/dma/dma-mbox-create.c 2020-03-06 11:30:13.000000000 +0100
@@ -38,18 +38,9 @@
* user-supplied information. Keep the root window as small as possible.
*/
-#ifdef __FreeBSD__
-#define USE_CAPSICUM 1
-#endif
-
#include <sys/param.h>
-#if USE_CAPSICUM
-#include <sys/capsicum.h>
-#endif
#include <sys/stat.h>
-#include <capsicum_helpers.h>
-#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
@@ -94,9 +85,6 @@
int
main(int argc, char **argv)
{
-#if USE_CAPSICUM
- cap_rights_t rights;
-#endif
const char *user;
struct passwd *pw;
struct group *gr;
@@ -104,10 +92,7 @@
gid_t mail_gid;
int f, maildirfd;
- /*
- * Open log fd now for capability sandbox.
- */
- openlog("dma-mbox-create", LOG_NDELAY, LOG_MAIL);
+ openlog("dma-mbox-create", 0, LOG_MAIL);
errno = 0;
gr = getgrnam(DMA_GROUP);
@@ -149,28 +134,6 @@
if (maildirfd < 0)
logfail(EX_NOINPUT, "cannot open maildir %s", _PATH_MAILDIR);
- /*
- * Cache NLS data, for strerror, for err(3), before entering capability
- * mode.
- */
- caph_cache_catpages();
-
- /*
- * Cache local time before entering Capsicum capability sandbox.
- */
- caph_cache_tzdata();
-
-#if USE_CAPSICUM
- cap_rights_init(&rights, CAP_CREATE, CAP_FCHMOD, CAP_FCHOWN,
- CAP_LOOKUP, CAP_READ);
- if (cap_rights_limit(maildirfd, &rights) < 0 && errno != ENOSYS)
- err(EX_OSERR, "can't limit maildirfd rights");
-
- /* Enter Capsicum capability sandbox */
- if (caph_enter() < 0)
- err(EX_OSERR, "cap_enter");
-#endif
-
user_uid = pw->pw_uid;
f = openat(maildirfd, user, O_RDONLY|O_CREAT|O_NOFOLLOW, 0600);
--- freebsd/contrib/dma/dma.8 2020-03-06 11:33:23.000000000 +0100
+++ ../d/dma/dma.8 2020-03-06 11:30:13.000000000 +0100
@@ -30,7 +30,7 @@
.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd May 20, 2017
+.Dd February 13, 2014
.Dt DMA 8
.Os
.Sh NAME
@@ -74,7 +74,7 @@
.It Fl bp
List all mails currently stored in the mail queue.
.It Fl bq
-Queue the mail, but do not attempt to deliver it.
+Queue the mail, but don't attempt to deliver it.
See also the
.Sq DEFER
config file setting below.
@@ -82,15 +82,18 @@
.Pp
All other
.Ar mode Ns
-s are ignored.
+s are are ignored.
.It Fl D
-Do not run in the background.
+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.
@@ -198,7 +201,7 @@
.Ql *
can be used to create a catch-all alias, which gets used if no other
matching alias is found.
-Use the catch-all alias only if you do not want any local mail to be
+Use the catch-all alias only if you don't want any local mail to be
delivered.
.It Ic SPOOLDIR Xo
(string, default=/var/spool/dma)
@@ -213,7 +216,7 @@
Path to the
.Sq auth.conf
file.
-.It Ic SECURETRANSFER Xo
+.It Ic SECURETRANS Xo
(boolean, default=commented)
.Xc
Uncomment if you want TLS/SSL secured transfer.
@@ -222,7 +225,10 @@
.Xc
Uncomment if you want to use STARTTLS.
Only useful together with
-.Sq SECURETRANSFER .
+.Sq SECURETRANS .
+.It Ic FINGERPRINT Xo
+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 +240,7 @@
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 SECURETRANSFER
+.Sq SECURETRANS
and
.Sq STARTTLS .
.It Ic CERTFILE Xo
@@ -283,7 +289,7 @@
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
@@ -305,8 +311,7 @@
setting it to
.Ql percolator
will send all mails as
-.Sm off
-.Ql Va username @percolator .
+.Ql Sm off Va username @percolator .
.Sm on
.It Ic NULLCLIENT Xo
.Xc
@@ -329,6 +334,8 @@
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
--- freebsd/contrib/dma/dma.c 2020-03-06 11:33:23.000000000 +0100
+++ ../d/dma/dma.c 2020-03-06 11:30:13.000000000 +0100
@@ -85,6 +85,7 @@
.mailname = NULL,
.masquerade_host = NULL,
.masquerade_user = NULL,
+ .fingerprint = NULL,
};
@@ -100,15 +101,14 @@
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 @@
switch (error) {
case 0:
- syslog(LOG_INFO, "<%s> delivery successful", it->addr);
delqueue(it);
+ syslog(LOG_INFO, "<%s> delivery successful", it->addr);
exit(EX_OK);
case 1:
@@ -466,7 +466,7 @@
goto skipopts;
} else if (strcmp(argv[0], "newaliases") == 0) {
logident_base = "dma";
- setlogident("%s", logident_base);
+ setlogident(NULL);
if (read_aliases() != 0)
errx(EX_SOFTWARE, "could not parse aliases file `%s'", config.aliases);
@@ -565,7 +565,7 @@
skipopts:
if (logident_base == NULL)
logident_base = "dma";
- setlogident("%s", logident_base);
+ setlogident(NULL);
act.sa_handler = sighup_handler;
act.sa_flags = 0;
@@ -597,7 +597,7 @@
errlog(EX_SOFTWARE, "could not parse aliases file `%s'", config.aliases);
if ((sender = set_from(&queue, sender)) == NULL)
- errlog(EX_SOFTWARE, "set_from()");
+ errlog(EX_SOFTWARE, NULL);
if (newspoolf(&queue) != 0)
errlog(EX_CANTCREAT, "can not create temp file in `%s'", config.spooldir);
--- freebsd/contrib/dma/dma.h 2020-03-06 11:33:23.000000000 +0100
+++ ../d/dma/dma.h 2020-03-06 11:30:13.000000000 +0100
@@ -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 */
@@ -137,6 +138,7 @@
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 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 @@
/* 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 @@
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 */
--- freebsd/contrib/dma/local.c 2020-03-06 11:33:23.000000000 +0100
+++ ../d/dma/local.c 2020-03-06 11:30:13.000000000 +0100
@@ -44,6 +44,7 @@
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
+#include <strings.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
@@ -219,7 +220,7 @@
/*
* 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
--- freebsd/contrib/dma/mail.c 2020-03-06 11:33:23.000000000 +0100
+++ ../d/dma/mail.c 2020-03-06 11:30:13.000000000 +0100
@@ -36,14 +36,13 @@
#include <errno.h>
#include <inttypes.h>
#include <signal.h>
+#include <strings.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "dma.h"
-#define MAX_LINE_RFC822 1000
-
void
bounce(struct qitem *it, const char *reason)
{
@@ -336,7 +335,7 @@
ps->pos = 0;
addr = strdup(ps->addr);
if (addr == NULL)
- errlog(EX_SOFTWARE, "strdup");
+ errlog(EX_SOFTWARE, NULL);
if (add_recp(queue, addr, EXPAND_WILDCARD) != 0)
errlogx(EX_DATAERR, "invalid recipient `%s'", addr);
@@ -344,47 +343,20 @@
goto again;
}
-static int
-writeline(struct queue *queue, const char *line, ssize_t linelen)
-{
- ssize_t len;
-
- while (linelen > 0) {
- len = linelen;
- if (linelen > MAX_LINE_RFC822) {
- len = MAX_LINE_RFC822 - 10;
- }
-
- if (fwrite(line, len, 1, queue->mailf) != 1)
- return (-1);
-
- if (linelen <= MAX_LINE_RFC822)
- break;
-
- if (fwrite("\n", 1, 1, queue->mailf) != 1)
- return (-1);
-
- line += MAX_LINE_RFC822 - 10;
- linelen = strlen(line);
- }
- return (0);
-}
-
int
readmail(struct queue *queue, int nodot, int recp_from_header)
{
struct parse_state parse_state;
- char *line = NULL;
- ssize_t linelen;
- size_t linecap = 0;
- char newline[MAX_LINE_RFC822];
+ char line[1000]; /* by RFC2822 */
+ size_t linelen;
size_t error;
int had_headers = 0;
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;
- int ret = -1;
parse_state.state = NONE;
@@ -403,17 +375,33 @@
return (-1);
while (!feof(stdin)) {
- newline[0] = '\0';
- if ((linelen = getline(&line, &linecap, stdin)) <= 0)
+ if (fgets(line, sizeof(line) - 1, stdin) == NULL)
break;
-
+ if (had_last_line)
+ errlogx(EX_DATAERR, "bad mail input format:"
+ " from %s (uid %d) (envelope-from %s)",
+ username, useruid, queue->sender);
+ linelen = strlen(line);
+ if (linelen == 0 || line[linelen - 1] != '\n') {
+ /*
+ * This line did not end with a newline character.
+ * If we fix it, it better be the last line of
+ * the file.
+ */
+ line[linelen] = '\n';
+ 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) {
- if (linelen > MAX_LINE_RFC822) {
- /* XXX also split headers */
- errlogx(EX_DATAERR, "bad mail input format:"
- " from %s (uid %d) (envelope-from %s)",
- username, useruid, queue->sender);
- }
/*
* Unless this is a continuation, switch of
* the Bcc: nocopy flag.
@@ -454,39 +442,31 @@
while (!had_date || !had_messagid || !had_from) {
if (!had_date) {
had_date = 1;
- snprintf(newline, sizeof(newline), "Date: %s\n", rfc822date());
+ snprintf(line, sizeof(line), "Date: %s\n", rfc822date());
} else if (!had_messagid) {
/* XXX msgid, assign earlier and log? */
had_messagid = 1;
- snprintf(newline, sizeof(newline), "Message-Id: <%"PRIxMAX".%s.%"PRIxMAX"@%s>\n",
+ snprintf(line, sizeof(line), "Message-Id: <%"PRIxMAX".%s.%"PRIxMAX"@%s>\n",
(uintmax_t)time(NULL),
queue->id,
(uintmax_t)random(),
hostname());
} else if (!had_from) {
had_from = 1;
- snprintf(newline, sizeof(newline), "From: <%s>\n", queue->sender);
+ snprintf(line, sizeof(line), "From: <%s>\n", queue->sender);
}
- if (fwrite(newline, strlen(newline), 1, queue->mailf) != 1)
- goto fail;
+ if (fwrite(line, strlen(line), 1, queue->mailf) != 1)
+ return (-1);
}
- strlcpy(newline, "\n", sizeof(newline));
+ strcpy(line, "\n");
}
if (!nodot && linelen == 2 && line[0] == '.')
break;
if (!nocopy) {
- if (newline[0]) {
- if (fwrite(newline, strlen(newline), 1, queue->mailf) != 1)
- goto fail;
- } else {
- if (writeline(queue, line, linelen) != 0)
- goto fail;
- }
+ if (fwrite(line, strlen(line), 1, queue->mailf) != 1)
+ return (-1);
}
}
- ret = 0;
-fail:
- free(line);
- return (ret);
+ return (0);
}
--- freebsd/contrib/dma/net.c 2020-03-06 11:33:23.000000000 +0100
+++ ../d/dma/net.c 2020-03-06 11:30:13.000000000 +0100
@@ -53,6 +53,7 @@
#include <netdb.h>
#include <setjmp.h>
#include <signal.h>
+#include <strings.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
@@ -100,7 +101,7 @@
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);
}
}
@@ -150,12 +151,12 @@
if (((config.features & SECURETRANS) != 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;
}
}
@@ -248,64 +249,70 @@
* 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 & 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);
+ 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);
@@ -349,11 +356,116 @@
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;
@@ -366,21 +478,23 @@
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 ||
@@ -392,7 +506,7 @@
}
if ((config.features & SECURETRANS) != 0) {
- error = smtp_init_crypto(fd, config.features);
+ error = smtp_init_crypto(fd, config.features, &features);
if (error == 0)
syslog(LOG_DEBUG, "SSL initialization successful");
else
@@ -402,10 +516,12 @@
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
@@ -424,7 +540,7 @@
* 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");
@@ -443,8 +559,17 @@
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);
@@ -486,6 +611,7 @@
syslog(LOG_INFO, "remote delivery succeeded but QUIT failed: %s", neterr);
out:
+ free(addrtmp);
close_connection(fd);
return (error);
}
--- freebsd/contrib/dma/spool.c 2020-03-06 11:33:23.000000000 +0100
+++ ../d/dma/spool.c 2020-03-06 11:30:13.000000000 +0100
@@ -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,7 @@
#include <fcntl.h>
#include <inttypes.h>
#include <unistd.h>
+#include <strings.h>
#include <string.h>
#include <syslog.h>
@@ -417,7 +419,7 @@
return (0);
/* Did the flush file get touched within the last period seconds? */
- if (st.st_mtim.tv_sec + (int)period >= now.tv_sec)
+ if (st.st_mtim.tv_sec + period >= now.tv_sec)
return (1);
else
return (0);
--- freebsd/contrib/dma/util.c 2020-03-06 11:33:23.000000000 +0100
+++ ../d/dma/util.c 2020-03-06 11:30:13.000000000 +0100
@@ -44,6 +44,7 @@
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
+#include <strings.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
[root@gs /tmp/dma/bsd]# echo hi | ./dma/dma -D jp@example
dma[10528][56272]: new mail from user=root uid=65534 envelope_from=<root@shuttle.ww.example>
dma[10528][56272]: mail to=<jp@example> queued as 10528.800a5d0a0
dma[10528.800a5d0a0][56272]: <jp@example> trying delivery
dma[10528.800a5d0a0][56272]: using smarthost (mail.relay:587)
dma[10528.800a5d0a0][56272]: trying remote delivery to mail.relay [ipv6] pref 0
dma[10528.800a5d0a0][56272]: connect to mail.relay [ipv6] failed: No route to host
dma[10528.800a5d0a0][56272]: trying remote delivery to mail.relay [ip4] pref 0
dma[10528.800a5d0a0][56272]: using SMTP authentication for user jp@example
dma[10528.800a5d0a0][56272]: <jp@example> delivery successful
[root@gs /etc/dma]# echo hi | /usr/libexec/dma -D jp@example
dma[1052a][56283]: new mail from user=root uid=26 envelope_from=<root@shuttle.ww.example>
dma[1052a][56283]: mail to=<jp@example> queued as 1052a.800a5d0a0
dma[1052a.800a5d0a0][56283]: <jp@example> trying delivery
dma[1052a.800a5d0a0][56283]: using smarthost (mail.relay:587)
dma[1052a.800a5d0a0][56283]: trying remote delivery to mail.relay [ipv6] pref 0
dma[1052a.800a5d0a0][56283]: connect to mail.relay [ipv6] failed: No route to host
dma[1052a.800a5d0a0][56283]: trying remote delivery to mail.relay [ip4] pref 0
dma[1052a.800a5d0a0][56283]: using SMTP authentication for user jp@example
dma[1052a.800a5d0a0][56283]: remote delivery deferred: AUTH cram-md5 failed: 535 Refused. Authentication failed. Rate limit hit or SMTP disabled.
dma[1052a.800a5d0a0][56283]: SMTP login not available. Trying without.
dma[1052a.800a5d0a0][56283]: <jp@example> delivery successful
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment