1f67bedddSMatthias Schmidt /* 2f67bedddSMatthias Schmidt * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3f67bedddSMatthias Schmidt * 4f67bedddSMatthias Schmidt * This code is derived from software contributed to The DragonFly Project 5f67bedddSMatthias Schmidt * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg, 6f67bedddSMatthias Schmidt * Germany. 7f67bedddSMatthias Schmidt * 8f67bedddSMatthias Schmidt * Redistribution and use in source and binary forms, with or without 9f67bedddSMatthias Schmidt * modification, are permitted provided that the following conditions 10f67bedddSMatthias Schmidt * are met: 11f67bedddSMatthias Schmidt * 12f67bedddSMatthias Schmidt * 1. Redistributions of source code must retain the above copyright 13f67bedddSMatthias Schmidt * notice, this list of conditions and the following disclaimer. 14f67bedddSMatthias Schmidt * 2. Redistributions in binary form must reproduce the above copyright 15f67bedddSMatthias Schmidt * notice, this list of conditions and the following disclaimer in 16f67bedddSMatthias Schmidt * the documentation and/or other materials provided with the 17f67bedddSMatthias Schmidt * distribution. 18f67bedddSMatthias Schmidt * 3. Neither the name of The DragonFly Project nor the names of its 19f67bedddSMatthias Schmidt * contributors may be used to endorse or promote products derived 20f67bedddSMatthias Schmidt * from this software without specific, prior written permission. 21f67bedddSMatthias Schmidt * 22f67bedddSMatthias Schmidt * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23f67bedddSMatthias Schmidt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24f67bedddSMatthias Schmidt * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25f67bedddSMatthias Schmidt * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26f67bedddSMatthias Schmidt * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27f67bedddSMatthias Schmidt * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 28f67bedddSMatthias Schmidt * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29f67bedddSMatthias Schmidt * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30f67bedddSMatthias Schmidt * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31f67bedddSMatthias Schmidt * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32f67bedddSMatthias Schmidt * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33f67bedddSMatthias Schmidt * SUCH DAMAGE. 34f67bedddSMatthias Schmidt */ 35f67bedddSMatthias Schmidt 36c8b07ee5SSascha Wildner #include "dfcompat.h" 37c8b07ee5SSascha Wildner 38f67bedddSMatthias Schmidt #include <sys/param.h> 39f67bedddSMatthias Schmidt #include <sys/queue.h> 40f67bedddSMatthias Schmidt #include <sys/stat.h> 41f67bedddSMatthias Schmidt #include <sys/types.h> 42f67bedddSMatthias Schmidt #include <sys/socket.h> 43f67bedddSMatthias Schmidt #include <netinet/in.h> 44f67bedddSMatthias Schmidt #include <arpa/inet.h> 45f67bedddSMatthias Schmidt 46f67bedddSMatthias Schmidt #include <openssl/ssl.h> 47e2c88018SSimon Schubert #include <openssl/err.h> 48f67bedddSMatthias Schmidt 49c8b07ee5SSascha Wildner #include <ctype.h> 50bc7baf1dSSascha Wildner #include <err.h> 51a5a8a1a4SSimon Schubert #include <errno.h> 52f67bedddSMatthias Schmidt #include <netdb.h> 53f67bedddSMatthias Schmidt #include <setjmp.h> 54f67bedddSMatthias Schmidt #include <signal.h> 55f67bedddSMatthias Schmidt #include <syslog.h> 56f67bedddSMatthias Schmidt #include <unistd.h> 57f67bedddSMatthias Schmidt 58f67bedddSMatthias Schmidt #include "dma.h" 59f67bedddSMatthias Schmidt 60c8b07ee5SSascha Wildner char neterr[ERRMSG_SIZE]; 61f67bedddSMatthias Schmidt 62e2c88018SSimon Schubert char * 63e2c88018SSimon Schubert ssl_errstr(void) 64e2c88018SSimon Schubert { 65e2c88018SSimon Schubert long oerr, nerr; 66e2c88018SSimon Schubert 67e2c88018SSimon Schubert oerr = 0; 68e2c88018SSimon Schubert while ((nerr = ERR_get_error()) != 0) 69e2c88018SSimon Schubert oerr = nerr; 70e2c88018SSimon Schubert 71e2c88018SSimon Schubert return (ERR_error_string(oerr, NULL)); 72e2c88018SSimon Schubert } 73e2c88018SSimon Schubert 74f67bedddSMatthias Schmidt ssize_t 75f67bedddSMatthias Schmidt send_remote_command(int fd, const char* fmt, ...) 76f67bedddSMatthias Schmidt { 77f67bedddSMatthias Schmidt va_list va; 78f67bedddSMatthias Schmidt char cmd[4096]; 792922fd2bSSimon Schubert size_t len, pos; 802922fd2bSSimon Schubert int s; 812922fd2bSSimon Schubert ssize_t n; 82f67bedddSMatthias Schmidt 83f67bedddSMatthias Schmidt va_start(va, fmt); 842922fd2bSSimon Schubert s = vsnprintf(cmd, sizeof(cmd) - 2, fmt, va); 852922fd2bSSimon Schubert va_end(va); 86e2c88018SSimon Schubert if (s == sizeof(cmd) - 2 || s < 0) { 87e2c88018SSimon Schubert strcpy(neterr, "Internal error: oversized command string"); 88e2c88018SSimon Schubert return (-1); 89e2c88018SSimon Schubert } 90e2c88018SSimon Schubert 912922fd2bSSimon Schubert /* We *know* there are at least two more bytes available */ 922922fd2bSSimon Schubert strcat(cmd, "\r\n"); 932922fd2bSSimon Schubert len = strlen(cmd); 94f67bedddSMatthias Schmidt 95ca259d14SSimon Schubert if (((config.features & SECURETRANS) != 0) && 96ca259d14SSimon Schubert ((config.features & NOSSL) == 0)) { 97ca259d14SSimon Schubert while ((s = SSL_write(config.ssl, (const char*)cmd, len)) <= 0) { 98ca259d14SSimon Schubert s = SSL_get_error(config.ssl, s); 992922fd2bSSimon Schubert if (s != SSL_ERROR_WANT_READ && 100e2c88018SSimon Schubert s != SSL_ERROR_WANT_WRITE) { 101e2c88018SSimon Schubert strncpy(neterr, ssl_errstr(), sizeof(neterr)); 1022922fd2bSSimon Schubert return (-1); 1032922fd2bSSimon Schubert } 104f67bedddSMatthias Schmidt } 105e2c88018SSimon Schubert } 106f67bedddSMatthias Schmidt else { 1072922fd2bSSimon Schubert pos = 0; 1082922fd2bSSimon Schubert while (pos < len) { 1092922fd2bSSimon Schubert n = write(fd, cmd + pos, len - pos); 1102922fd2bSSimon Schubert if (n < 0) 1112922fd2bSSimon Schubert return (-1); 1122922fd2bSSimon Schubert pos += n; 113f67bedddSMatthias Schmidt } 1142922fd2bSSimon Schubert } 115f67bedddSMatthias Schmidt 1162922fd2bSSimon Schubert return (len); 117f67bedddSMatthias Schmidt } 118f67bedddSMatthias Schmidt 1196ef9fe01SMatthias Schmidt int 1207b68d8aeSMatthias Schmidt read_remote(int fd, int extbufsize, char *extbuf) 121f67bedddSMatthias Schmidt { 1226ef9fe01SMatthias Schmidt ssize_t rlen = 0; 123c8b07ee5SSascha Wildner size_t pos, len, copysize; 1246ef9fe01SMatthias Schmidt char buff[BUF_SIZE]; 125c8b07ee5SSascha Wildner int done = 0, status = 0, status_running = 0, extbufpos = 0; 126c8b07ee5SSascha Wildner enum { parse_status, parse_spacedash, parse_rest } parsestate; 127f67bedddSMatthias Schmidt 128c8b07ee5SSascha Wildner if (do_timeout(CON_TIMEOUT, 1) != 0) { 129a5a8a1a4SSimon Schubert snprintf(neterr, sizeof(neterr), "Timeout reached"); 130e2c88018SSimon Schubert return (-1); 131f67bedddSMatthias Schmidt } 132f67bedddSMatthias Schmidt 133f67bedddSMatthias Schmidt /* 1346ef9fe01SMatthias Schmidt * Remote reading code from femail.c written by Henning Brauer of 1356ef9fe01SMatthias Schmidt * OpenBSD and released under a BSD style license. 136f67bedddSMatthias Schmidt */ 137c8b07ee5SSascha Wildner len = 0; 138c8b07ee5SSascha Wildner pos = 0; 139c8b07ee5SSascha Wildner parsestate = parse_status; 140c8b07ee5SSascha Wildner neterr[0] = 0; 141c8b07ee5SSascha Wildner while (!(done && parsestate == parse_status)) { 1427b68d8aeSMatthias Schmidt rlen = 0; 1436ef9fe01SMatthias Schmidt if (pos == 0 || 1446ef9fe01SMatthias Schmidt (pos > 0 && memchr(buff + pos, '\n', len - pos) == NULL)) { 1456ef9fe01SMatthias Schmidt memmove(buff, buff + pos, len - pos); 1466ef9fe01SMatthias Schmidt len -= pos; 1476ef9fe01SMatthias Schmidt pos = 0; 148ca259d14SSimon Schubert if (((config.features & SECURETRANS) != 0) && 149ca259d14SSimon Schubert (config.features & NOSSL) == 0) { 150c8b07ee5SSascha Wildner if ((rlen = SSL_read(config.ssl, buff + len, sizeof(buff) - len)) == -1) { 151e2c88018SSimon Schubert strncpy(neterr, ssl_errstr(), sizeof(neterr)); 152c8b07ee5SSascha Wildner goto error; 153e2c88018SSimon Schubert } 1546ef9fe01SMatthias Schmidt } else { 155e2c88018SSimon Schubert if ((rlen = read(fd, buff + len, sizeof(buff) - len)) == -1) { 156e2c88018SSimon Schubert strncpy(neterr, strerror(errno), sizeof(neterr)); 157c8b07ee5SSascha Wildner goto error; 158e2c88018SSimon Schubert } 1596ef9fe01SMatthias Schmidt } 1606ef9fe01SMatthias Schmidt len += rlen; 161c8b07ee5SSascha Wildner 162c8b07ee5SSascha Wildner copysize = sizeof(neterr) - strlen(neterr) - 1; 163c8b07ee5SSascha Wildner if (copysize > len) 164c8b07ee5SSascha Wildner copysize = len; 165c8b07ee5SSascha Wildner strncat(neterr, buff, copysize); 1666ef9fe01SMatthias Schmidt } 1677b68d8aeSMatthias Schmidt /* 1687b68d8aeSMatthias Schmidt * If there is an external buffer with a size bigger than zero 1697b68d8aeSMatthias Schmidt * and as long as there is space in the external buffer and 1707b68d8aeSMatthias Schmidt * there are new characters read from the mailserver 1717b68d8aeSMatthias Schmidt * copy them to the external buffer 1727b68d8aeSMatthias Schmidt */ 173c8b07ee5SSascha Wildner if (extbufpos <= (extbufsize - 1) && rlen > 0 && extbufsize > 0 && extbuf != NULL) { 1747b68d8aeSMatthias Schmidt /* do not write over the bounds of the buffer */ 1757b68d8aeSMatthias Schmidt if(extbufpos + rlen > (extbufsize - 1)) { 1767b68d8aeSMatthias Schmidt rlen = extbufsize - extbufpos; 1777b68d8aeSMatthias Schmidt } 1787b68d8aeSMatthias Schmidt memcpy(extbuf + extbufpos, buff + len - rlen, rlen); 1797b68d8aeSMatthias Schmidt extbufpos += rlen; 1807b68d8aeSMatthias Schmidt } 181f67bedddSMatthias Schmidt 1826ef9fe01SMatthias Schmidt if (pos == len) 183c8b07ee5SSascha Wildner continue; 1846ef9fe01SMatthias Schmidt 185c8b07ee5SSascha Wildner switch (parsestate) { 186c8b07ee5SSascha Wildner case parse_status: 187c8b07ee5SSascha Wildner for (; pos < len; pos++) { 188c8b07ee5SSascha Wildner if (isdigit(buff[pos])) { 189c8b07ee5SSascha Wildner status_running = status_running * 10 + (buff[pos] - '0'); 190c8b07ee5SSascha Wildner } else { 191c8b07ee5SSascha Wildner status = status_running; 192c8b07ee5SSascha Wildner status_running = 0; 193c8b07ee5SSascha Wildner parsestate = parse_spacedash; 194c8b07ee5SSascha Wildner break; 195c8b07ee5SSascha Wildner } 196c8b07ee5SSascha Wildner } 197c8b07ee5SSascha Wildner continue; 198c8b07ee5SSascha Wildner 199c8b07ee5SSascha Wildner case parse_spacedash: 200c8b07ee5SSascha Wildner switch (buff[pos]) { 201c8b07ee5SSascha Wildner case ' ': 2026ef9fe01SMatthias Schmidt done = 1; 203c8b07ee5SSascha Wildner break; 204c8b07ee5SSascha Wildner 205c8b07ee5SSascha Wildner case '-': 206c8b07ee5SSascha Wildner /* ignore */ 207c8b07ee5SSascha Wildner /* XXX read capabilities */ 208c8b07ee5SSascha Wildner break; 209c8b07ee5SSascha Wildner 210c8b07ee5SSascha Wildner default: 211e2c88018SSimon Schubert strcpy(neterr, "invalid syntax in reply from server"); 212c8b07ee5SSascha Wildner goto error; 213e2c88018SSimon Schubert } 2146ef9fe01SMatthias Schmidt 215c8b07ee5SSascha Wildner pos++; 216c8b07ee5SSascha Wildner parsestate = parse_rest; 217c8b07ee5SSascha Wildner continue; 218c8b07ee5SSascha Wildner 219c8b07ee5SSascha Wildner case parse_rest: 2206ef9fe01SMatthias Schmidt /* skip up to \n */ 221c8b07ee5SSascha Wildner for (; pos < len; pos++) { 222c8b07ee5SSascha Wildner if (buff[pos] == '\n') { 223c8b07ee5SSascha Wildner pos++; 224c8b07ee5SSascha Wildner parsestate = parse_status; 225c8b07ee5SSascha Wildner break; 226c8b07ee5SSascha Wildner } 227c8b07ee5SSascha Wildner } 228c8b07ee5SSascha Wildner } 2296ef9fe01SMatthias Schmidt 2306ef9fe01SMatthias Schmidt } 231f67bedddSMatthias Schmidt 232c8b07ee5SSascha Wildner do_timeout(0, 0); 233c8b07ee5SSascha Wildner 234c8b07ee5SSascha Wildner /* chop off trailing newlines */ 235c8b07ee5SSascha Wildner while (neterr[0] != 0 && strchr("\r\n", neterr[strlen(neterr) - 1]) != 0) 236c8b07ee5SSascha Wildner neterr[strlen(neterr) - 1] = 0; 237c8b07ee5SSascha Wildner 2386ef9fe01SMatthias Schmidt return (status/100); 239c8b07ee5SSascha Wildner 240c8b07ee5SSascha Wildner error: 241c8b07ee5SSascha Wildner do_timeout(0, 0); 242c8b07ee5SSascha Wildner return (-1); 243f67bedddSMatthias Schmidt } 244f67bedddSMatthias Schmidt 245f67bedddSMatthias Schmidt /* 246f67bedddSMatthias Schmidt * Handle SMTP authentication 247f67bedddSMatthias Schmidt */ 248f67bedddSMatthias Schmidt static int 249405f48eeSSimon Schubert smtp_login(int fd, char *login, char* password) 250f67bedddSMatthias Schmidt { 251f67bedddSMatthias Schmidt char *temp; 2526ef9fe01SMatthias Schmidt int len, res = 0; 253f67bedddSMatthias Schmidt 254405f48eeSSimon Schubert res = smtp_auth_md5(fd, login, password); 2557b68d8aeSMatthias Schmidt if (res == 0) { 2567b68d8aeSMatthias Schmidt return (0); 2577b68d8aeSMatthias Schmidt } else if (res == -2) { 2587b68d8aeSMatthias Schmidt /* 2597b68d8aeSMatthias Schmidt * If the return code is -2, then then the login attempt failed, 2607b68d8aeSMatthias Schmidt * do not try other login mechanisms 2617b68d8aeSMatthias Schmidt */ 2625d7fe8bbSSimon Schubert return (1); 2637b68d8aeSMatthias Schmidt } 2647b68d8aeSMatthias Schmidt 265ca259d14SSimon Schubert if ((config.features & INSECURE) != 0 || 266ca259d14SSimon Schubert (config.features & SECURETRANS) != 0) { 267f67bedddSMatthias Schmidt /* Send AUTH command according to RFC 2554 */ 268f67bedddSMatthias Schmidt send_remote_command(fd, "AUTH LOGIN"); 2697b68d8aeSMatthias Schmidt if (read_remote(fd, 0, NULL) != 3) { 270405f48eeSSimon Schubert syslog(LOG_NOTICE, "remote delivery deferred:" 271a5a8a1a4SSimon Schubert " AUTH login not available: %s", 272405f48eeSSimon Schubert neterr); 273f67bedddSMatthias Schmidt return (1); 274f67bedddSMatthias Schmidt } 275f67bedddSMatthias Schmidt 276f67bedddSMatthias Schmidt len = base64_encode(login, strlen(login), &temp); 2775d7fe8bbSSimon Schubert if (len < 0) { 2785d7fe8bbSSimon Schubert encerr: 279405f48eeSSimon Schubert syslog(LOG_ERR, "can not encode auth reply: %m"); 2805d7fe8bbSSimon Schubert return (1); 2815d7fe8bbSSimon Schubert } 282f67bedddSMatthias Schmidt 283f67bedddSMatthias Schmidt send_remote_command(fd, "%s", temp); 2845d7fe8bbSSimon Schubert free(temp); 285e2c88018SSimon Schubert res = read_remote(fd, 0, NULL); 286e2c88018SSimon Schubert if (res != 3) { 287405f48eeSSimon Schubert syslog(LOG_NOTICE, "remote delivery %s: AUTH login failed: %s", 288405f48eeSSimon Schubert res == 5 ? "failed" : "deferred", neterr); 289e2c88018SSimon Schubert return (res == 5 ? -1 : 1); 290f67bedddSMatthias Schmidt } 291f67bedddSMatthias Schmidt 292f67bedddSMatthias Schmidt len = base64_encode(password, strlen(password), &temp); 2935d7fe8bbSSimon Schubert if (len < 0) 2945d7fe8bbSSimon Schubert goto encerr; 295f67bedddSMatthias Schmidt 296f67bedddSMatthias Schmidt send_remote_command(fd, "%s", temp); 2975d7fe8bbSSimon Schubert free(temp); 2987b68d8aeSMatthias Schmidt res = read_remote(fd, 0, NULL); 299e2c88018SSimon Schubert if (res != 2) { 300405f48eeSSimon Schubert syslog(LOG_NOTICE, "remote delivery %s: Authentication failed: %s", 301405f48eeSSimon Schubert res == 5 ? "failed" : "deferred", neterr); 302e2c88018SSimon Schubert return (res == 5 ? -1 : 1); 303f67bedddSMatthias Schmidt } 3047b68d8aeSMatthias Schmidt } else { 305405f48eeSSimon Schubert syslog(LOG_WARNING, "non-encrypted SMTP login is disabled in config, so skipping it. "); 3067b68d8aeSMatthias Schmidt return (1); 3077b68d8aeSMatthias Schmidt } 308f67bedddSMatthias Schmidt 309f67bedddSMatthias Schmidt return (0); 310f67bedddSMatthias Schmidt } 311f67bedddSMatthias Schmidt 312f67bedddSMatthias Schmidt static int 3133021968aSSimon Schubert open_connection(struct mx_hostentry *h) 314f67bedddSMatthias Schmidt { 3153021968aSSimon Schubert int fd; 316f67bedddSMatthias Schmidt 3173021968aSSimon Schubert syslog(LOG_INFO, "trying remote delivery to %s [%s] pref %d", 3183021968aSSimon Schubert h->host, h->addr, h->pref); 319f67bedddSMatthias Schmidt 3203021968aSSimon Schubert fd = socket(h->ai.ai_family, h->ai.ai_socktype, h->ai.ai_protocol); 3213021968aSSimon Schubert if (fd < 0) { 3223021968aSSimon Schubert syslog(LOG_INFO, "socket for %s [%s] failed: %m", 3233021968aSSimon Schubert h->host, h->addr); 324f67bedddSMatthias Schmidt return (-1); 325f67bedddSMatthias Schmidt } 3263021968aSSimon Schubert 327725a35c6SSimon Schubert if (connect(fd, (struct sockaddr *)&h->sa, h->ai.ai_addrlen) < 0) { 3283021968aSSimon Schubert syslog(LOG_INFO, "connect to %s [%s] failed: %m", 3293021968aSSimon Schubert h->host, h->addr); 330f67bedddSMatthias Schmidt close(fd); 331f67bedddSMatthias Schmidt return (-1); 332f67bedddSMatthias Schmidt } 3333021968aSSimon Schubert 334f67bedddSMatthias Schmidt return (fd); 335f67bedddSMatthias Schmidt } 336f67bedddSMatthias Schmidt 33710eeb0dfSSimon Schubert static void 33810eeb0dfSSimon Schubert close_connection(int fd) 33910eeb0dfSSimon Schubert { 340de30bd07SMatthias Schmidt if (config.ssl != NULL) { 341ca259d14SSimon Schubert if (((config.features & SECURETRANS) != 0) && 342ca259d14SSimon Schubert ((config.features & NOSSL) == 0)) 343ca259d14SSimon Schubert SSL_shutdown(config.ssl); 344ca259d14SSimon Schubert SSL_free(config.ssl); 345de30bd07SMatthias Schmidt } 34610eeb0dfSSimon Schubert 34710eeb0dfSSimon Schubert close(fd); 34810eeb0dfSSimon Schubert } 34910eeb0dfSSimon Schubert 3503021968aSSimon Schubert static int 351c8b07ee5SSascha Wildner deliver_to_host(struct qitem *it, struct mx_hostentry *host) 352f67bedddSMatthias Schmidt { 353f67bedddSMatthias Schmidt struct authuser *a; 3543021968aSSimon Schubert char line[1000]; 355f67bedddSMatthias Schmidt size_t linelen; 3563021968aSSimon Schubert int fd, error = 0, do_auth = 0, res = 0; 357f67bedddSMatthias Schmidt 358ebffba26SSimon Schubert if (fseek(it->mailf, 0, SEEK_SET) != 0) { 359c8b07ee5SSascha Wildner snprintf(errmsg, sizeof(errmsg), "can not seek: %s", strerror(errno)); 3607d35694eSSimon Schubert return (-1); 3617d35694eSSimon Schubert } 3627d35694eSSimon Schubert 363405f48eeSSimon Schubert fd = open_connection(host); 364f67bedddSMatthias Schmidt if (fd < 0) 365f67bedddSMatthias Schmidt return (1); 366f67bedddSMatthias Schmidt 3673021968aSSimon Schubert #define READ_REMOTE_CHECK(c, exp) \ 3683021968aSSimon Schubert res = read_remote(fd, 0, NULL); \ 3693021968aSSimon Schubert if (res == 5) { \ 3703021968aSSimon Schubert syslog(LOG_ERR, "remote delivery to %s [%s] failed after %s: %s", \ 3713021968aSSimon Schubert host->host, host->addr, c, neterr); \ 372c8b07ee5SSascha Wildner snprintf(errmsg, sizeof(errmsg), "%s [%s] did not like our %s:\n%s", \ 3733021968aSSimon Schubert host->host, host->addr, c, neterr); \ 3743021968aSSimon Schubert return (-1); \ 3753021968aSSimon Schubert } else if (res != exp) { \ 3763021968aSSimon Schubert syslog(LOG_NOTICE, "remote delivery deferred: %s [%s] failed after %s: %s", \ 3773021968aSSimon Schubert host->host, host->addr, c, neterr); \ 3783021968aSSimon Schubert return (1); \ 3793021968aSSimon Schubert } 3803021968aSSimon Schubert 381b8b7d065SSimon Schubert /* Check first reply from remote host */ 382c8b07ee5SSascha Wildner if ((config.features & SECURETRANS) == 0 || 383c8b07ee5SSascha Wildner (config.features & STARTTLS) != 0) { 384b8b7d065SSimon Schubert config.features |= NOSSL; 385b8b7d065SSimon Schubert READ_REMOTE_CHECK("connect", 2); 386b8b7d065SSimon Schubert 387b8b7d065SSimon Schubert config.features &= ~NOSSL; 388c8b07ee5SSascha Wildner } 389b8b7d065SSimon Schubert 390b8b7d065SSimon Schubert if ((config.features & SECURETRANS) != 0) { 391b8b7d065SSimon Schubert error = smtp_init_crypto(fd, config.features); 392655c0566SSimon Schubert if (error == 0) 393b8b7d065SSimon Schubert syslog(LOG_DEBUG, "SSL initialization successful"); 394b8b7d065SSimon Schubert else 395b8b7d065SSimon Schubert goto out; 396c8b07ee5SSascha Wildner 397c8b07ee5SSascha Wildner if ((config.features & STARTTLS) == 0) 398c8b07ee5SSascha Wildner READ_REMOTE_CHECK("connect", 2); 399b8b7d065SSimon Schubert } 400b8b7d065SSimon Schubert 401c21e2cfdSSimon Schubert /* XXX allow HELO fallback */ 402c21e2cfdSSimon Schubert /* XXX record ESMTP keywords */ 403f67bedddSMatthias Schmidt send_remote_command(fd, "EHLO %s", hostname()); 4043021968aSSimon Schubert READ_REMOTE_CHECK("EHLO", 2); 405f67bedddSMatthias Schmidt 406f67bedddSMatthias Schmidt /* 407f67bedddSMatthias Schmidt * Use SMTP authentication if the user defined an entry for the remote 408f67bedddSMatthias Schmidt * or smarthost 409f67bedddSMatthias Schmidt */ 410f67bedddSMatthias Schmidt SLIST_FOREACH(a, &authusers, next) { 4113021968aSSimon Schubert if (strcmp(a->host, host->host) == 0) { 412f67bedddSMatthias Schmidt do_auth = 1; 413f67bedddSMatthias Schmidt break; 414f67bedddSMatthias Schmidt } 415f67bedddSMatthias Schmidt } 416f67bedddSMatthias Schmidt 417f67bedddSMatthias Schmidt if (do_auth == 1) { 418b558d098SMatthias Schmidt /* 419b558d098SMatthias Schmidt * Check if the user wants plain text login without using 420b558d098SMatthias Schmidt * encryption. 421b558d098SMatthias Schmidt */ 422ca259d14SSimon Schubert syslog(LOG_INFO, "using SMTP authentication for user %s", a->login); 423405f48eeSSimon Schubert error = smtp_login(fd, a->login, a->password); 424f67bedddSMatthias Schmidt if (error < 0) { 425405f48eeSSimon Schubert syslog(LOG_ERR, "remote delivery failed:" 426405f48eeSSimon Schubert " SMTP login failed: %m"); 427c8b07ee5SSascha Wildner snprintf(errmsg, sizeof(errmsg), "SMTP login to %s failed", host->host); 428f67bedddSMatthias Schmidt return (-1); 429f67bedddSMatthias Schmidt } 430f67bedddSMatthias Schmidt /* SMTP login is not available, so try without */ 431405f48eeSSimon Schubert else if (error > 0) { 432405f48eeSSimon Schubert syslog(LOG_WARNING, "SMTP login not available. Trying without."); 433405f48eeSSimon Schubert } 434b558d098SMatthias Schmidt } 435f67bedddSMatthias Schmidt 436c21e2cfdSSimon Schubert /* XXX send ESMTP ENVID, RET (FULL/HDRS) and 8BITMIME */ 4377dfd2fd8SSimon Schubert send_remote_command(fd, "MAIL FROM:<%s>", it->sender); 4387dfd2fd8SSimon Schubert READ_REMOTE_CHECK("MAIL FROM", 2); 4397dfd2fd8SSimon Schubert 440c21e2cfdSSimon Schubert /* XXX send ESMTP ORCPT */ 441f67bedddSMatthias Schmidt send_remote_command(fd, "RCPT TO:<%s>", it->addr); 4427dfd2fd8SSimon Schubert READ_REMOTE_CHECK("RCPT TO", 2); 443f67bedddSMatthias Schmidt 444f67bedddSMatthias Schmidt send_remote_command(fd, "DATA"); 4457dfd2fd8SSimon Schubert READ_REMOTE_CHECK("DATA", 3); 446f67bedddSMatthias Schmidt 4470caaabf6SSimon Schubert error = 0; 448f4e61a9fSSimon 'corecode' Schubert while (!feof(it->mailf)) { 449f4e61a9fSSimon 'corecode' Schubert if (fgets(line, sizeof(line), it->mailf) == NULL) 450f67bedddSMatthias Schmidt break; 451f67bedddSMatthias Schmidt linelen = strlen(line); 452f67bedddSMatthias Schmidt if (linelen == 0 || line[linelen - 1] != '\n') { 453405f48eeSSimon Schubert syslog(LOG_CRIT, "remote delivery failed: corrupted queue file"); 454c8b07ee5SSascha Wildner snprintf(errmsg, sizeof(errmsg), "corrupted queue file"); 455f67bedddSMatthias Schmidt error = -1; 456f67bedddSMatthias Schmidt goto out; 457f67bedddSMatthias Schmidt } 458f67bedddSMatthias Schmidt 459f67bedddSMatthias Schmidt /* Remove trailing \n's and escape leading dots */ 460f67bedddSMatthias Schmidt trim_line(line); 461f67bedddSMatthias Schmidt 462f67bedddSMatthias Schmidt /* 463f67bedddSMatthias Schmidt * If the first character is a dot, we escape it so the line 464f67bedddSMatthias Schmidt * length increases 465f67bedddSMatthias Schmidt */ 466f67bedddSMatthias Schmidt if (line[0] == '.') 467f67bedddSMatthias Schmidt linelen++; 468f67bedddSMatthias Schmidt 469f67bedddSMatthias Schmidt if (send_remote_command(fd, "%s", line) != (ssize_t)linelen+1) { 470405f48eeSSimon Schubert syslog(LOG_NOTICE, "remote delivery deferred: write error"); 471f67bedddSMatthias Schmidt error = 1; 472f67bedddSMatthias Schmidt goto out; 473f67bedddSMatthias Schmidt } 474f67bedddSMatthias Schmidt } 475f67bedddSMatthias Schmidt 476f67bedddSMatthias Schmidt send_remote_command(fd, "."); 4777dfd2fd8SSimon Schubert READ_REMOTE_CHECK("final DATA", 2); 478f67bedddSMatthias Schmidt 479f67bedddSMatthias Schmidt send_remote_command(fd, "QUIT"); 4806cfc247dSSimon Schubert if (read_remote(fd, 0, NULL) != 2) 481405f48eeSSimon Schubert syslog(LOG_INFO, "remote delivery succeeded but QUIT failed: %s", neterr); 482f67bedddSMatthias Schmidt out: 483f67bedddSMatthias Schmidt 48410eeb0dfSSimon Schubert close_connection(fd); 485f67bedddSMatthias Schmidt return (error); 486f67bedddSMatthias Schmidt } 487f67bedddSMatthias Schmidt 4883021968aSSimon Schubert int 489c8b07ee5SSascha Wildner deliver_remote(struct qitem *it) 4903021968aSSimon Schubert { 4913021968aSSimon Schubert struct mx_hostentry *hosts, *h; 492ca259d14SSimon Schubert const char *host; 4933021968aSSimon Schubert int port; 4943021968aSSimon Schubert int error = 1, smarthost = 0; 4953021968aSSimon Schubert 496*14dfb991SJoris Giovannangeli port = SMTP_PORT; 497*14dfb991SJoris Giovannangeli 498*14dfb991SJoris Giovannangeli /* Smarthost support? */ 499*14dfb991SJoris Giovannangeli if (config.smarthost != NULL) { 500*14dfb991SJoris Giovannangeli host = config.smarthost; 501*14dfb991SJoris Giovannangeli port = config.port; 502*14dfb991SJoris Giovannangeli syslog(LOG_INFO, "using smarthost (%s:%i)", host, port); 503*14dfb991SJoris Giovannangeli smarthost = 1; 504*14dfb991SJoris Giovannangeli } else { 5053021968aSSimon Schubert host = strrchr(it->addr, '@'); 5063021968aSSimon Schubert /* Should not happen */ 5073021968aSSimon Schubert if (host == NULL) { 508c8b07ee5SSascha Wildner snprintf(errmsg, sizeof(errmsg), "Internal error: badly formed address %s", 5093021968aSSimon Schubert it->addr); 5103021968aSSimon Schubert return(-1); 5113021968aSSimon Schubert } else { 5123021968aSSimon Schubert /* Step over the @ */ 5133021968aSSimon Schubert host++; 5143021968aSSimon Schubert } 5153021968aSSimon Schubert } 5163021968aSSimon Schubert 5173021968aSSimon Schubert error = dns_get_mx_list(host, port, &hosts, smarthost); 5183021968aSSimon Schubert if (error) { 519*14dfb991SJoris Giovannangeli snprintf(errmsg, sizeof(errmsg), "DNS lookup failure: host %s not found", host); 520*14dfb991SJoris Giovannangeli syslog(LOG_NOTICE, "remote delivery %s: DNS lookup failure: host %s not found", 5213021968aSSimon Schubert error < 0 ? "failed" : "deferred", 5223021968aSSimon Schubert host); 5233021968aSSimon Schubert return (error); 5243021968aSSimon Schubert } 5253021968aSSimon Schubert 5263021968aSSimon Schubert for (h = hosts; *h->host != 0; h++) { 527c8b07ee5SSascha Wildner switch (deliver_to_host(it, h)) { 5283021968aSSimon Schubert case 0: 5293021968aSSimon Schubert /* success */ 5303021968aSSimon Schubert error = 0; 5313021968aSSimon Schubert goto out; 5323021968aSSimon Schubert case 1: 5333021968aSSimon Schubert /* temp failure */ 5343021968aSSimon Schubert error = 1; 5353021968aSSimon Schubert break; 5363021968aSSimon Schubert default: 5373021968aSSimon Schubert /* perm failure */ 5383021968aSSimon Schubert error = -1; 5393021968aSSimon Schubert goto out; 5403021968aSSimon Schubert } 5413021968aSSimon Schubert } 5423021968aSSimon Schubert out: 5433021968aSSimon Schubert free(hosts); 5443021968aSSimon Schubert 5453021968aSSimon Schubert return (error); 5463021968aSSimon Schubert } 547