The branch stable/13 has been updated by emaste:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=74c7755fe9726c7ebdfc9b04f9caee5fbf93246b

commit 74c7755fe9726c7ebdfc9b04f9caee5fbf93246b
Author:     Baptiste Daroussin <[email protected]>
AuthorDate: 2021-09-22 09:09:27 +0000
Commit:     Ed Maste <[email protected]>
CommitDate: 2022-01-30 17:54:56 +0000

    dma: import snapshot 2021-07-10
    
    (cherry picked from commit fbe95b885f3431b1d8003545b32e8ffa88f2d16b)
---
 contrib/dma/Makefile       |   2 +-
 contrib/dma/VERSION        |   2 +-
 contrib/dma/conf.c         |  20 +++-
 contrib/dma/crypto.c       |  43 ++++++-
 contrib/dma/dfcompat.c     |   6 +-
 contrib/dma/dma.8          |  17 ++-
 contrib/dma/dma.c          |  38 +++---
 contrib/dma/dma.conf       |   6 +-
 contrib/dma/dma.h          |  17 ++-
 contrib/dma/dns.c          |   5 -
 contrib/dma/get-version.sh |   0
 contrib/dma/local.c        |   5 +-
 contrib/dma/mail.c         |  40 +++++--
 contrib/dma/net.c          | 284 ++++++++++++++++++++++++++++++++-------------
 contrib/dma/spool.c        |   2 +
 contrib/dma/util.c         |  20 ++++
 16 files changed, 378 insertions(+), 129 deletions(-)

diff --git a/contrib/dma/Makefile b/contrib/dma/Makefile
index aed2ef7246cf..8cae5b28f98b 100644
--- a/contrib/dma/Makefile
+++ b/contrib/dma/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/contrib/dma/VERSION b/contrib/dma/VERSION
index 5416288bc5ef..3c58828758cd 100644
--- a/contrib/dma/VERSION
+++ b/contrib/dma/VERSION
@@ -1 +1 @@
-v0.11
+v0.13
diff --git a/contrib/dma/conf.c b/contrib/dma/conf.c
index b8a6a2e2cbd7..13cfac7a6de4 100644
--- a/contrib/dma/conf.c
+++ b/contrib/dma/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/contrib/dma/crypto.c b/contrib/dma/crypto.c
index 37b255c18310..368238b9d632 100644
--- a/contrib/dma/crypto.c
+++ b/contrib/dma/crypto.c
@@ -40,6 +40,7 @@
 #include <openssl/pem.h>
 #include <openssl/rand.h>
 
+#include <strings.h>
 #include <string.h>
 #include <syslog.h>
 
@@ -77,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)
@@ -119,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) {
@@ -136,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;
        }
@@ -161,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);
@@ -172,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/contrib/dma/dfcompat.c b/contrib/dma/dfcompat.c
index 014fa88b8d49..d4ecc1d74ae9 100644
--- a/contrib/dma/dfcompat.c
+++ b/contrib/dma/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/contrib/dma/dma.8 b/contrib/dma/dma.8
index cadf899e50fc..e0f5e79ff47d 100644
--- a/contrib/dma/dma.8
+++ b/contrib/dma/dma.8
@@ -89,8 +89,11 @@ 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.
@@ -223,6 +226,11 @@ Uncomment if you want TLS/SSL secured transfer.
 Uncomment if you want to use STARTTLS.
 Only useful together with
 .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
@@ -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
@@ -309,6 +317,7 @@ will send all mails as
 .Ql 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
@@ -329,6 +338,8 @@ Used to set the sender address (envelope-from).
 Use a plain address, in the form of
 .Li [email protected] .
 This value will be overridden when the
+.Sq MASQUERADE
+config file setting or the
 .Fl f
 flag is used.
 .El
diff --git a/contrib/dma/dma.c b/contrib/dma/dma.c
index b553c0fa0eef..72115ae2b55e 100644
--- a/contrib/dma/dma.c
+++ b/contrib/dma/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, '@')) {
@@ -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("%s", logident_base);
-
-               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, "set_from()");
 
diff --git a/contrib/dma/dma.conf b/contrib/dma/dma.conf
index 1cc2bf5bc843..fa95fc1a0c22 100644
--- a/contrib/dma/dma.conf
+++ b/contrib/dma/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/contrib/dma/dma.h b/contrib/dma/dma.h
index 593417617d3d..9e7f6cd2c431 100644
--- a/contrib/dma/dma.h
+++ b/contrib/dma/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/contrib/dma/dns.c b/contrib/dma/dns.c
index bd28c4db724c..449e6b463caa 100644
--- a/contrib/dma/dns.c
+++ b/contrib/dma/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/contrib/dma/get-version.sh b/contrib/dma/get-version.sh
old mode 100755
new mode 100644
diff --git a/contrib/dma/local.c b/contrib/dma/local.c
index b6c4180fc5dc..2c3483ea0380 100644
--- a/contrib/dma/local.c
+++ b/contrib/dma/local.c
@@ -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>
@@ -81,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);
 
@@ -219,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/contrib/dma/mail.c b/contrib/dma/mail.c
index ad928272e1a1..48c8ee6d4dd2 100644
--- a/contrib/dma/mail.c
+++ b/contrib/dma/mail.c
@@ -36,6 +36,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <signal.h>
+#include <strings.h>
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
@@ -73,7 +74,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"
@@ -91,7 +92,7 @@ bounce(struct qitem *it, const char *reason)
                "%s\n"
                "\n",
                bounceq.id,
-               hostname(), VERSION,
+               hostname(), VERSION, systemhostname(),
                rfc822date(),
                it->addr,
                it->sender,
@@ -191,8 +192,7 @@ again:
                switch (*s) {
                case ' ':
                case '\t':
-                       s++;
-                       /* continue */
+                       ps->state = MAIN;
                        break;
 
                default:
@@ -201,6 +201,7 @@ again:
                                goto newaddr;
                        return (0);
                }
+               break;
 
        case QUIT:
                return (0);
@@ -383,6 +384,8 @@ 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;
        int ret = -1;
 
@@ -392,12 +395,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);
@@ -406,7 +409,30 @@ readmail(struct queue *queue, int nodot, int 
recp_from_header)
                newline[0] = '\0';
                if ((linelen = getline(&line, &linecap, stdin)) <= 0)
                        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 */
diff --git a/contrib/dma/net.c b/contrib/dma/net.c
index 7953370500d8..e8e2634a9386 100644
--- a/contrib/dma/net.c
+++ b/contrib/dma/net.c
@@ -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>
@@ -94,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);
                        }
                }
@@ -147,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;
                                }
                        }
@@ -248,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);
@@ -340,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);
@@ -349,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;
 
@@ -366,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);
@@ -391,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
@@ -402,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);
*** 100 LINES SKIPPED ***

Reply via email to