--- 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 #include +#include #include #include @@ -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 --- 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 -#if USE_CAPSICUM -#include -#endif #include -#include -#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include #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 #include #include +#include #include #include #include @@ -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 #include +#include #include #include @@ -45,6 +46,7 @@ #include #include #include +#include #include #include @@ -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 #include #include +#include #include #include #include