dma: import snapshot 2021-07-10

This commit is contained in:
Baptiste Daroussin 2021-09-22 11:09:27 +02:00
commit fbe95b885f
16 changed files with 379 additions and 130 deletions

View file

@ -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

View file

@ -1 +1 @@
v0.11
v0.13

View file

@ -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)

View file

@ -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);

View file

@ -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);
}

View file

@ -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 user@example.com .
This value will be overridden when the
.Sq MASQUERADE
config file setting or the
.Fl f
flag is used.
.El

View file

@ -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()");

View file

@ -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)

View file

@ -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)));

View file

@ -271,11 +271,6 @@ err:
*he = hosts;
return (err);
free(ans);
if (hosts != NULL)
free(hosts);
return (err);
}
#if defined(TESTING)

0
contrib/dma/get-version.sh Executable file → Normal file
View file

View file

@ -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

View file

@ -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 */

View file

@ -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);
// 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);
}
}
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);
}
// 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) {
len = base64_encode(login, strlen(login), &temp);
if (len < 0) {
encerr:
syslog(LOG_ERR, "can not encode auth reply: %m");
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);
}
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);
}
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);
}
} 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);
return -1;
}
/*
* Use SMTP authentication if the user defined an entry for the remote
@ -424,7 +540,7 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host)
* encryption.
*/
syslog(LOG_INFO, "using SMTP authentication for user %s", a->login);
error = smtp_login(fd, a->login, a->password);
error = smtp_login(fd, a->login, a->password, &features);
if (error < 0) {
syslog(LOG_ERR, "remote delivery failed:"
" SMTP login failed: %m");
@ -443,8 +559,17 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host)
READ_REMOTE_CHECK("MAIL FROM", 2);
/* XXX send ESMTP ORCPT */
send_remote_command(fd, "RCPT TO:<%s>", it->addr);
READ_REMOTE_CHECK("RCPT TO", 2);
if ((addrtmp = strdup(it->addr)) == NULL) {
syslog(LOG_CRIT, "remote delivery deferred: unable to allocate memory");
error = 1;
goto out;
}
to_addr = strtok(addrtmp, ",");
while (to_addr != NULL) {
send_remote_command(fd, "RCPT TO:<%s>", to_addr);
READ_REMOTE_CHECK("RCPT TO", 2);
to_addr = strtok(NULL, ",");
}
send_remote_command(fd, "DATA");
READ_REMOTE_CHECK("DATA", 3);
@ -486,6 +611,7 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host)
syslog(LOG_INFO, "remote delivery succeeded but QUIT failed: %s", neterr);
out:
free(addrtmp);
close_connection(fd);
return (error);
}

View file

@ -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>

View file

@ -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>
@ -98,6 +99,25 @@ hostname(void)
}
local:
snprintf(name, sizeof(name), "%s", systemhostname());
initialized = 1;
return (name);
}
const char *
systemhostname(void)
{
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 255
#endif
static char name[HOST_NAME_MAX+1];
static int initialized = 0;
char *s;
if (initialized)
return (name);
if (gethostname(name, sizeof(name)) != 0)
*name = 0;
/*