From 3e459432e3518ab3c159955ad2b8d35de17c572c Mon Sep 17 00:00:00 2001 From: Nick Winterer Date: Mon, 12 Jan 2026 21:52:08 -0800 Subject: [PATCH 1/3] Add support for AUTH PLAIN --- dma.h | 1 + net.c | 77 ++++++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/dma.h b/dma.h index 41588eb..2cd9980 100644 --- a/dma.h +++ b/dma.h @@ -166,6 +166,7 @@ struct mx_hostentry { struct smtp_auth_mechanisms { int cram_md5; int login; + int plain; }; struct smtp_features { diff --git a/net.c b/net.c index 81b304b..dc5661a 100644 --- a/net.c +++ b/net.c @@ -281,44 +281,67 @@ smtp_login(int fd, char *login, char* password, const struct smtp_features* feat } } - // LOGIN - if (features->auth.login) { + // LOGIN or PLAIN + if (features->auth.login || features->auth.plain) { if ((config.features & INSECURE) != 0 || (config.features & SECURETRANSFER) != 0) { /* Send AUTH command according to RFC 2554 */ - send_remote_command(fd, "AUTH LOGIN"); + const char *auth_mechanism = features->auth.login ? "LOGIN" : "PLAIN"; + send_remote_command(fd, "AUTH %s", auth_mechanism); if (read_remote(fd, 0, NULL) != 3) { syslog(LOG_NOTICE, "remote delivery deferred:" - " AUTH login not available: %s", - neterr); + " AUTH %s not available: %s", + auth_mechanism, neterr); return (1); } - len = base64_encode(login, strlen(login), &temp); - if (len < 0) { + if (features->auth.login) { + // LOGIN mechanism + 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); + } else if (features->auth.plain) { + // PLAIN mechanism + size_t buflen = strlen(login) + strlen(password) + 3; + char *plainbuf = malloc(buflen); + if (plainbuf == NULL) { + syslog(LOG_ERR, "remote delivery deferred: unable to allocate memory"); + return (1); + } + + snprintf(plainbuf, buflen, "%c%s%c%s", '\0', login, '\0', password); - len = base64_encode(password, strlen(password), &temp); - if (len < 0) - goto encerr; + len = base64_encode(plainbuf, buflen, &temp); + free(plainbuf); + if (len < 0) + goto encerr; + + send_remote_command(fd, "%s", temp); + free(temp); + } - 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", + syslog(LOG_NOTICE, "remote delivery %s: AUTH PLAIN failed: %s", res == 5 ? "failed" : "deferred", neterr); return (res == 5 ? -1 : 1); } @@ -381,6 +404,9 @@ static void parse_auth_line(char* line, struct smtp_auth_mechanisms* auth) { else if (strcmp(method, "LOGIN") == 0) auth->login = 1; + else if (strcmp(method, "PLAIN") == 0) + auth->plain = 1; + method = strtok(NULL, " "); } } @@ -469,6 +495,9 @@ int perform_server_greeting(int fd, struct smtp_features* features) { if (features->auth.login) { syslog(LOG_DEBUG, " Server supports LOGIN authentication"); } + if (features->auth.plain) { + syslog(LOG_DEBUG, " Server supports PLAIN authentication"); + } return 0; } From dc6bbc5d3bb0fa514966c01d977bd61ef6416d8f Mon Sep 17 00:00:00 2001 From: Nick Winterer Date: Wed, 14 Jan 2026 13:16:53 -0800 Subject: [PATCH 2/3] Add clearer syslog output when server doesn't support any suitable auth mechanisms --- net.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/net.c b/net.c index dc5661a..f9567c7 100644 --- a/net.c +++ b/net.c @@ -581,18 +581,23 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host) * Check if the user wants plain text login without using * encryption. */ - syslog(LOG_INFO, "using SMTP authentication for user %s", a->login); - error = smtp_login(fd, a->login, a->password, &features); - if (error < 0) { - syslog(LOG_ERR, "remote delivery failed:" - " SMTP login failed: %m"); - snprintf(errmsg, sizeof(errmsg), "SMTP login to %s failed", host->host); - error = -1; - goto out; - } - /* SMTP login is not available, so try without */ - else if (error > 0) { - syslog(LOG_WARNING, "SMTP login not available. Trying without."); + if (features.auth.cram_md5 || features.auth.login || features.auth.plain) { + syslog(LOG_INFO, "using SMTP authentication for user %s", a->login); + error = smtp_login(fd, a->login, a->password, &features); + if (error < 0) { + syslog(LOG_ERR, "remote delivery failed:" + " SMTP login failed: %m"); + snprintf(errmsg, sizeof(errmsg), "SMTP login to %s failed", host->host); + error = -1; + goto out; + } + /* SMTP login is not available, so try without */ + else if (error > 0) { + syslog(LOG_WARNING, "SMTP login not available. Trying without."); + } + } else { + syslog(LOG_ERR, "No supported AUTH mechanisms in common with server." + " Skipping authentication."); } } From 34ec96f4874c426479ddf80d11ddd3a51a1fe395 Mon Sep 17 00:00:00 2001 From: Nick Winterer Date: Fri, 13 Feb 2026 12:44:43 -0800 Subject: [PATCH 3/3] fixup! Add support for AUTH PLAIN --- crypto.c | 6 +- net.c | 192 ++++++++++++++++++++++++++++++++----------------------- 2 files changed, 114 insertions(+), 84 deletions(-) diff --git a/crypto.c b/crypto.c index 94d96d0..0176846 100644 --- a/crypto.c +++ b/crypto.c @@ -383,7 +383,7 @@ smtp_auth_md5(int fd, char *login, char *password) " AUTH cram-md5 not available: %s", neterr); /* if cram-md5 is not available */ free(temp); - return (-1); + return (1); } /* skip 3 char status + 1 char space */ @@ -405,7 +405,7 @@ smtp_auth_md5(int fd, char *login, char *password) len = base64_encode(buffer, strlen(buffer), &temp); if (len < 0) { syslog(LOG_ERR, "can not encode auth reply: %m"); - return (-1); + return (1); } /* send answer */ @@ -414,7 +414,7 @@ smtp_auth_md5(int fd, char *login, char *password) if (read_remote(fd, 0, NULL) != 2) { syslog(LOG_WARNING, "remote delivery deferred:" " AUTH cram-md5 failed: %s", neterr); - return (-2); + return (-1); } return (0); diff --git a/net.c b/net.c index f9567c7..1c0f351 100644 --- a/net.c +++ b/net.c @@ -258,100 +258,132 @@ read_remote(int fd, int extbufsize, char *extbuf) return (-1); } +static int +smtp_auth_login(int fd, char *login, char* password) +{ + char *temp; + int len, res = 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); + } + + 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); + } + + return 0; +} + +static int +smtp_auth_plain(int fd, char *login, char* password) +{ + char *temp, *authstr; + int len, res = 0; + + /* Send AUTH command according to RFC 2554 */ + send_remote_command(fd, "AUTH PLAIN"); + if (read_remote(fd, 0, NULL) != 3) { + syslog(LOG_NOTICE, "remote delivery deferred:" + " AUTH plain not available: %s", + neterr); + return (1); + } + + len = asprintf(&authstr, "%c%s%c%s", '\0', login, '\0', password); + if (len < 0) { + syslog(LOG_ERR, "can not format auth reply: %m"); + return (1); + } + + len = base64_encode(authstr, len, &temp); + free(authstr); + if (len < 0) { + 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 != 2) { + syslog(LOG_NOTICE, "remote delivery %s: Authentication failed: %s", + res == 5 ? "failed" : "deferred", neterr); + return (res == 5 ? -1 : 1); + } + + return 0; +} + /* * Handle SMTP authentication */ static int -smtp_login(int fd, char *login, char* password, const struct smtp_features* features) +smtp_auth(int fd, char *login, char* password, const struct smtp_features* features) { - char *temp; - int len, res = 0; + int res = 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 (res != 1) { + return res; } } - // LOGIN or PLAIN - if (features->auth.login || features->auth.plain) { - if ((config.features & INSECURE) != 0 || + if ((config.features & INSECURE) != 0 || (config.features & SECURETRANSFER) != 0) { - /* Send AUTH command according to RFC 2554 */ - const char *auth_mechanism = features->auth.login ? "LOGIN" : "PLAIN"; - send_remote_command(fd, "AUTH %s", auth_mechanism); - if (read_remote(fd, 0, NULL) != 3) { - syslog(LOG_NOTICE, "remote delivery deferred:" - " AUTH %s not available: %s", - auth_mechanism, neterr); - return (1); - } - if (features->auth.login) { - // LOGIN mechanism - len = base64_encode(login, strlen(login), &temp); - if (len < 0) { -encerr: - 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); - } else if (features->auth.plain) { - // PLAIN mechanism - size_t buflen = strlen(login) + strlen(password) + 3; - char *plainbuf = malloc(buflen); - if (plainbuf == NULL) { - syslog(LOG_ERR, "remote delivery deferred: unable to allocate memory"); - return (1); - } - - snprintf(plainbuf, buflen, "%c%s%c%s", '\0', login, '\0', password); - - len = base64_encode(plainbuf, buflen, &temp); - free(plainbuf); - if (len < 0) - goto encerr; - - send_remote_command(fd, "%s", temp); - free(temp); + // LOGIN + if (features->auth.login) { + res = smtp_auth_login(fd, login, password); + if (res != 1) { + return res; } + } - res = read_remote(fd, 0, NULL); - if (res != 2) { - syslog(LOG_NOTICE, "remote delivery %s: AUTH PLAIN failed: %s", - res == 5 ? "failed" : "deferred", neterr); - return (res == 5 ? -1 : 1); + // PLAIN + if (features->auth.plain) { + res = smtp_auth_plain(fd, login, password); + if (res != 1) { + return res; } - } else { - syslog(LOG_WARNING, "non-encrypted SMTP login is disabled in config, so skipping it. "); - return (1); - } + } + } else if (features->auth.login || features->auth.plain) { + syslog(LOG_WARNING, "non-encrypted SMTP authentication is disabled in config, so skipping it. "); + return (1); } - return (0); + return res; } static int @@ -583,17 +615,15 @@ deliver_to_host(struct qitem *it, struct mx_hostentry *host) */ if (features.auth.cram_md5 || features.auth.login || features.auth.plain) { syslog(LOG_INFO, "using SMTP authentication for user %s", a->login); - error = smtp_login(fd, a->login, a->password, &features); + error = smtp_auth(fd, a->login, a->password, &features); if (error < 0) { - syslog(LOG_ERR, "remote delivery failed:" - " SMTP login failed: %m"); - snprintf(errmsg, sizeof(errmsg), "SMTP login to %s failed", host->host); + snprintf(errmsg, sizeof(errmsg), "SMTP auth to %s failed", host->host); error = -1; goto out; } - /* SMTP login is not available, so try without */ + /* SMTP auth is not available, so try without */ else if (error > 0) { - syslog(LOG_WARNING, "SMTP login not available. Trying without."); + syslog(LOG_WARNING, "SMTP auth not available. Trying without."); } } else { syslog(LOG_ERR, "No supported AUTH mechanisms in common with server."