1a9e8641dSBaptiste Daroussin /* 2e56bad4aSBaptiste Daroussin * Copyright (c) 2008-2014, Simon Schubert <2@0x2c.org>. 3a9e8641dSBaptiste Daroussin * Copyright (c) 2008 The DragonFly Project. All rights reserved. 4a9e8641dSBaptiste Daroussin * 5a9e8641dSBaptiste Daroussin * This code is derived from software contributed to The DragonFly Project 6a9e8641dSBaptiste Daroussin * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg, 7a9e8641dSBaptiste Daroussin * Germany. 8a9e8641dSBaptiste Daroussin * 9a9e8641dSBaptiste Daroussin * Redistribution and use in source and binary forms, with or without 10a9e8641dSBaptiste Daroussin * modification, are permitted provided that the following conditions 11a9e8641dSBaptiste Daroussin * are met: 12a9e8641dSBaptiste Daroussin * 13a9e8641dSBaptiste Daroussin * 1. Redistributions of source code must retain the above copyright 14a9e8641dSBaptiste Daroussin * notice, this list of conditions and the following disclaimer. 15a9e8641dSBaptiste Daroussin * 2. Redistributions in binary form must reproduce the above copyright 16a9e8641dSBaptiste Daroussin * notice, this list of conditions and the following disclaimer in 17a9e8641dSBaptiste Daroussin * the documentation and/or other materials provided with the 18a9e8641dSBaptiste Daroussin * distribution. 19a9e8641dSBaptiste Daroussin * 3. Neither the name of The DragonFly Project nor the names of its 20a9e8641dSBaptiste Daroussin * contributors may be used to endorse or promote products derived 21a9e8641dSBaptiste Daroussin * from this software without specific, prior written permission. 22a9e8641dSBaptiste Daroussin * 23a9e8641dSBaptiste Daroussin * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24a9e8641dSBaptiste Daroussin * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25a9e8641dSBaptiste Daroussin * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26a9e8641dSBaptiste Daroussin * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27a9e8641dSBaptiste Daroussin * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28a9e8641dSBaptiste Daroussin * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 29a9e8641dSBaptiste Daroussin * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30a9e8641dSBaptiste Daroussin * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31a9e8641dSBaptiste Daroussin * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32a9e8641dSBaptiste Daroussin * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33a9e8641dSBaptiste Daroussin * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34a9e8641dSBaptiste Daroussin * SUCH DAMAGE. 35a9e8641dSBaptiste Daroussin */ 36a9e8641dSBaptiste Daroussin 37a9e8641dSBaptiste Daroussin #include "dfcompat.h" 38a9e8641dSBaptiste Daroussin 39a9e8641dSBaptiste Daroussin #include <sys/param.h> 40a9e8641dSBaptiste Daroussin #include <sys/queue.h> 41a9e8641dSBaptiste Daroussin #include <sys/stat.h> 42a9e8641dSBaptiste Daroussin #include <sys/types.h> 43a9e8641dSBaptiste Daroussin #include <sys/socket.h> 44a9e8641dSBaptiste Daroussin #include <netinet/in.h> 45a9e8641dSBaptiste Daroussin #include <arpa/inet.h> 46a9e8641dSBaptiste Daroussin 47a9e8641dSBaptiste Daroussin #include <openssl/ssl.h> 48a9e8641dSBaptiste Daroussin #include <openssl/err.h> 49a9e8641dSBaptiste Daroussin 50a9e8641dSBaptiste Daroussin #include <ctype.h> 51a9e8641dSBaptiste Daroussin #include <err.h> 52a9e8641dSBaptiste Daroussin #include <errno.h> 53a9e8641dSBaptiste Daroussin #include <netdb.h> 54a9e8641dSBaptiste Daroussin #include <setjmp.h> 55a9e8641dSBaptiste Daroussin #include <signal.h> 56a9e8641dSBaptiste Daroussin #include <syslog.h> 57a9e8641dSBaptiste Daroussin #include <unistd.h> 58a9e8641dSBaptiste Daroussin 59a9e8641dSBaptiste Daroussin #include "dma.h" 60a9e8641dSBaptiste Daroussin 61a9e8641dSBaptiste Daroussin char neterr[ERRMSG_SIZE]; 62a9e8641dSBaptiste Daroussin 63a9e8641dSBaptiste Daroussin char * 64a9e8641dSBaptiste Daroussin ssl_errstr(void) 65a9e8641dSBaptiste Daroussin { 66a9e8641dSBaptiste Daroussin long oerr, nerr; 67a9e8641dSBaptiste Daroussin 68a9e8641dSBaptiste Daroussin oerr = 0; 69a9e8641dSBaptiste Daroussin while ((nerr = ERR_get_error()) != 0) 70a9e8641dSBaptiste Daroussin oerr = nerr; 71a9e8641dSBaptiste Daroussin 72a9e8641dSBaptiste Daroussin return (ERR_error_string(oerr, NULL)); 73a9e8641dSBaptiste Daroussin } 74a9e8641dSBaptiste Daroussin 75a9e8641dSBaptiste Daroussin ssize_t 76a9e8641dSBaptiste Daroussin send_remote_command(int fd, const char* fmt, ...) 77a9e8641dSBaptiste Daroussin { 78a9e8641dSBaptiste Daroussin va_list va; 79a9e8641dSBaptiste Daroussin char cmd[4096]; 80a9e8641dSBaptiste Daroussin size_t len, pos; 81a9e8641dSBaptiste Daroussin int s; 82a9e8641dSBaptiste Daroussin ssize_t n; 83a9e8641dSBaptiste Daroussin 84a9e8641dSBaptiste Daroussin va_start(va, fmt); 85a9e8641dSBaptiste Daroussin s = vsnprintf(cmd, sizeof(cmd) - 2, fmt, va); 86a9e8641dSBaptiste Daroussin va_end(va); 87a9e8641dSBaptiste Daroussin if (s == sizeof(cmd) - 2 || s < 0) { 88a9e8641dSBaptiste Daroussin strcpy(neterr, "Internal error: oversized command string"); 89a9e8641dSBaptiste Daroussin return (-1); 90a9e8641dSBaptiste Daroussin } 91a9e8641dSBaptiste Daroussin 92a9e8641dSBaptiste Daroussin /* We *know* there are at least two more bytes available */ 93a9e8641dSBaptiste Daroussin strcat(cmd, "\r\n"); 94a9e8641dSBaptiste Daroussin len = strlen(cmd); 95a9e8641dSBaptiste Daroussin 96a9e8641dSBaptiste Daroussin if (((config.features & SECURETRANS) != 0) && 97a9e8641dSBaptiste Daroussin ((config.features & NOSSL) == 0)) { 98a9e8641dSBaptiste Daroussin while ((s = SSL_write(config.ssl, (const char*)cmd, len)) <= 0) { 99a9e8641dSBaptiste Daroussin s = SSL_get_error(config.ssl, s); 100a9e8641dSBaptiste Daroussin if (s != SSL_ERROR_WANT_READ && 101a9e8641dSBaptiste Daroussin s != SSL_ERROR_WANT_WRITE) { 102a9e8641dSBaptiste Daroussin strncpy(neterr, ssl_errstr(), sizeof(neterr)); 103a9e8641dSBaptiste Daroussin return (-1); 104a9e8641dSBaptiste Daroussin } 105a9e8641dSBaptiste Daroussin } 106a9e8641dSBaptiste Daroussin } 107a9e8641dSBaptiste Daroussin else { 108a9e8641dSBaptiste Daroussin pos = 0; 109a9e8641dSBaptiste Daroussin while (pos < len) { 110a9e8641dSBaptiste Daroussin n = write(fd, cmd + pos, len - pos); 111a9e8641dSBaptiste Daroussin if (n < 0) 112a9e8641dSBaptiste Daroussin return (-1); 113a9e8641dSBaptiste Daroussin pos += n; 114a9e8641dSBaptiste Daroussin } 115a9e8641dSBaptiste Daroussin } 116a9e8641dSBaptiste Daroussin 117a9e8641dSBaptiste Daroussin return (len); 118a9e8641dSBaptiste Daroussin } 119a9e8641dSBaptiste Daroussin 120a9e8641dSBaptiste Daroussin int 121a9e8641dSBaptiste Daroussin read_remote(int fd, int extbufsize, char *extbuf) 122a9e8641dSBaptiste Daroussin { 123a9e8641dSBaptiste Daroussin ssize_t rlen = 0; 124a9e8641dSBaptiste Daroussin size_t pos, len, copysize; 125a9e8641dSBaptiste Daroussin char buff[BUF_SIZE]; 126a9e8641dSBaptiste Daroussin int done = 0, status = 0, status_running = 0, extbufpos = 0; 127a9e8641dSBaptiste Daroussin enum { parse_status, parse_spacedash, parse_rest } parsestate; 128a9e8641dSBaptiste Daroussin 129a9e8641dSBaptiste Daroussin if (do_timeout(CON_TIMEOUT, 1) != 0) { 130a9e8641dSBaptiste Daroussin snprintf(neterr, sizeof(neterr), "Timeout reached"); 131a9e8641dSBaptiste Daroussin return (-1); 132a9e8641dSBaptiste Daroussin } 133a9e8641dSBaptiste Daroussin 134a9e8641dSBaptiste Daroussin /* 135a9e8641dSBaptiste Daroussin * Remote reading code from femail.c written by Henning Brauer of 136a9e8641dSBaptiste Daroussin * OpenBSD and released under a BSD style license. 137a9e8641dSBaptiste Daroussin */ 138a9e8641dSBaptiste Daroussin len = 0; 139a9e8641dSBaptiste Daroussin pos = 0; 140a9e8641dSBaptiste Daroussin parsestate = parse_status; 141a9e8641dSBaptiste Daroussin neterr[0] = 0; 142a9e8641dSBaptiste Daroussin while (!(done && parsestate == parse_status)) { 143a9e8641dSBaptiste Daroussin rlen = 0; 144a9e8641dSBaptiste Daroussin if (pos == 0 || 145a9e8641dSBaptiste Daroussin (pos > 0 && memchr(buff + pos, '\n', len - pos) == NULL)) { 146a9e8641dSBaptiste Daroussin memmove(buff, buff + pos, len - pos); 147a9e8641dSBaptiste Daroussin len -= pos; 148a9e8641dSBaptiste Daroussin pos = 0; 149a9e8641dSBaptiste Daroussin if (((config.features & SECURETRANS) != 0) && 150a9e8641dSBaptiste Daroussin (config.features & NOSSL) == 0) { 151a9e8641dSBaptiste Daroussin if ((rlen = SSL_read(config.ssl, buff + len, sizeof(buff) - len)) == -1) { 152a9e8641dSBaptiste Daroussin strncpy(neterr, ssl_errstr(), sizeof(neterr)); 153a9e8641dSBaptiste Daroussin goto error; 154a9e8641dSBaptiste Daroussin } 155a9e8641dSBaptiste Daroussin } else { 156a9e8641dSBaptiste Daroussin if ((rlen = read(fd, buff + len, sizeof(buff) - len)) == -1) { 157a9e8641dSBaptiste Daroussin strncpy(neterr, strerror(errno), sizeof(neterr)); 158a9e8641dSBaptiste Daroussin goto error; 159a9e8641dSBaptiste Daroussin } 160a9e8641dSBaptiste Daroussin } 161a9e8641dSBaptiste Daroussin len += rlen; 162a9e8641dSBaptiste Daroussin 163a9e8641dSBaptiste Daroussin copysize = sizeof(neterr) - strlen(neterr) - 1; 164a9e8641dSBaptiste Daroussin if (copysize > len) 165a9e8641dSBaptiste Daroussin copysize = len; 166a9e8641dSBaptiste Daroussin strncat(neterr, buff, copysize); 167a9e8641dSBaptiste Daroussin } 168a9e8641dSBaptiste Daroussin /* 169a9e8641dSBaptiste Daroussin * If there is an external buffer with a size bigger than zero 170a9e8641dSBaptiste Daroussin * and as long as there is space in the external buffer and 171a9e8641dSBaptiste Daroussin * there are new characters read from the mailserver 172a9e8641dSBaptiste Daroussin * copy them to the external buffer 173a9e8641dSBaptiste Daroussin */ 174a9e8641dSBaptiste Daroussin if (extbufpos <= (extbufsize - 1) && rlen > 0 && extbufsize > 0 && extbuf != NULL) { 175a9e8641dSBaptiste Daroussin /* do not write over the bounds of the buffer */ 176a9e8641dSBaptiste Daroussin if(extbufpos + rlen > (extbufsize - 1)) { 177a9e8641dSBaptiste Daroussin rlen = extbufsize - extbufpos; 178a9e8641dSBaptiste Daroussin } 179a9e8641dSBaptiste Daroussin memcpy(extbuf + extbufpos, buff + len - rlen, rlen); 180a9e8641dSBaptiste Daroussin extbufpos += rlen; 181a9e8641dSBaptiste Daroussin } 182a9e8641dSBaptiste Daroussin 183a9e8641dSBaptiste Daroussin if (pos == len) 184a9e8641dSBaptiste Daroussin continue; 185a9e8641dSBaptiste Daroussin 186a9e8641dSBaptiste Daroussin switch (parsestate) { 187a9e8641dSBaptiste Daroussin case parse_status: 188a9e8641dSBaptiste Daroussin for (; pos < len; pos++) { 189a9e8641dSBaptiste Daroussin if (isdigit(buff[pos])) { 190a9e8641dSBaptiste Daroussin status_running = status_running * 10 + (buff[pos] - '0'); 191a9e8641dSBaptiste Daroussin } else { 192a9e8641dSBaptiste Daroussin status = status_running; 193a9e8641dSBaptiste Daroussin status_running = 0; 194a9e8641dSBaptiste Daroussin parsestate = parse_spacedash; 195a9e8641dSBaptiste Daroussin break; 196a9e8641dSBaptiste Daroussin } 197a9e8641dSBaptiste Daroussin } 198a9e8641dSBaptiste Daroussin continue; 199a9e8641dSBaptiste Daroussin 200a9e8641dSBaptiste Daroussin case parse_spacedash: 201a9e8641dSBaptiste Daroussin switch (buff[pos]) { 202a9e8641dSBaptiste Daroussin case ' ': 203a9e8641dSBaptiste Daroussin done = 1; 204a9e8641dSBaptiste Daroussin break; 205a9e8641dSBaptiste Daroussin 206a9e8641dSBaptiste Daroussin case '-': 207a9e8641dSBaptiste Daroussin /* ignore */ 208a9e8641dSBaptiste Daroussin /* XXX read capabilities */ 209a9e8641dSBaptiste Daroussin break; 210a9e8641dSBaptiste Daroussin 211a9e8641dSBaptiste Daroussin default: 212a9e8641dSBaptiste Daroussin strcpy(neterr, "invalid syntax in reply from server"); 213a9e8641dSBaptiste Daroussin goto error; 214a9e8641dSBaptiste Daroussin } 215a9e8641dSBaptiste Daroussin 216a9e8641dSBaptiste Daroussin pos++; 217a9e8641dSBaptiste Daroussin parsestate = parse_rest; 218a9e8641dSBaptiste Daroussin continue; 219a9e8641dSBaptiste Daroussin 220a9e8641dSBaptiste Daroussin case parse_rest: 221a9e8641dSBaptiste Daroussin /* skip up to \n */ 222a9e8641dSBaptiste Daroussin for (; pos < len; pos++) { 223a9e8641dSBaptiste Daroussin if (buff[pos] == '\n') { 224a9e8641dSBaptiste Daroussin pos++; 225a9e8641dSBaptiste Daroussin parsestate = parse_status; 226a9e8641dSBaptiste Daroussin break; 227a9e8641dSBaptiste Daroussin } 228a9e8641dSBaptiste Daroussin } 229a9e8641dSBaptiste Daroussin } 230a9e8641dSBaptiste Daroussin 231a9e8641dSBaptiste Daroussin } 232a9e8641dSBaptiste Daroussin 233a9e8641dSBaptiste Daroussin do_timeout(0, 0); 234a9e8641dSBaptiste Daroussin 235a9e8641dSBaptiste Daroussin /* chop off trailing newlines */ 236a9e8641dSBaptiste Daroussin while (neterr[0] != 0 && strchr("\r\n", neterr[strlen(neterr) - 1]) != 0) 237a9e8641dSBaptiste Daroussin neterr[strlen(neterr) - 1] = 0; 238a9e8641dSBaptiste Daroussin 239a9e8641dSBaptiste Daroussin return (status/100); 240a9e8641dSBaptiste Daroussin 241a9e8641dSBaptiste Daroussin error: 242a9e8641dSBaptiste Daroussin do_timeout(0, 0); 243a9e8641dSBaptiste Daroussin return (-1); 244a9e8641dSBaptiste Daroussin } 245a9e8641dSBaptiste Daroussin 246a9e8641dSBaptiste Daroussin /* 247a9e8641dSBaptiste Daroussin * Handle SMTP authentication 248a9e8641dSBaptiste Daroussin */ 249a9e8641dSBaptiste Daroussin static int 250a9e8641dSBaptiste Daroussin smtp_login(int fd, char *login, char* password) 251a9e8641dSBaptiste Daroussin { 252a9e8641dSBaptiste Daroussin char *temp; 253a9e8641dSBaptiste Daroussin int len, res = 0; 254a9e8641dSBaptiste Daroussin 255a9e8641dSBaptiste Daroussin res = smtp_auth_md5(fd, login, password); 256a9e8641dSBaptiste Daroussin if (res == 0) { 257a9e8641dSBaptiste Daroussin return (0); 258a9e8641dSBaptiste Daroussin } else if (res == -2) { 259a9e8641dSBaptiste Daroussin /* 260a9e8641dSBaptiste Daroussin * If the return code is -2, then then the login attempt failed, 261a9e8641dSBaptiste Daroussin * do not try other login mechanisms 262a9e8641dSBaptiste Daroussin */ 263a9e8641dSBaptiste Daroussin return (1); 264a9e8641dSBaptiste Daroussin } 265a9e8641dSBaptiste Daroussin 266a9e8641dSBaptiste Daroussin if ((config.features & INSECURE) != 0 || 267a9e8641dSBaptiste Daroussin (config.features & SECURETRANS) != 0) { 268a9e8641dSBaptiste Daroussin /* Send AUTH command according to RFC 2554 */ 269a9e8641dSBaptiste Daroussin send_remote_command(fd, "AUTH LOGIN"); 270a9e8641dSBaptiste Daroussin if (read_remote(fd, 0, NULL) != 3) { 271a9e8641dSBaptiste Daroussin syslog(LOG_NOTICE, "remote delivery deferred:" 272a9e8641dSBaptiste Daroussin " AUTH login not available: %s", 273a9e8641dSBaptiste Daroussin neterr); 274a9e8641dSBaptiste Daroussin return (1); 275a9e8641dSBaptiste Daroussin } 276a9e8641dSBaptiste Daroussin 277a9e8641dSBaptiste Daroussin len = base64_encode(login, strlen(login), &temp); 278a9e8641dSBaptiste Daroussin if (len < 0) { 279a9e8641dSBaptiste Daroussin encerr: 280a9e8641dSBaptiste Daroussin syslog(LOG_ERR, "can not encode auth reply: %m"); 281a9e8641dSBaptiste Daroussin return (1); 282a9e8641dSBaptiste Daroussin } 283a9e8641dSBaptiste Daroussin 284a9e8641dSBaptiste Daroussin send_remote_command(fd, "%s", temp); 285a9e8641dSBaptiste Daroussin free(temp); 286a9e8641dSBaptiste Daroussin res = read_remote(fd, 0, NULL); 287a9e8641dSBaptiste Daroussin if (res != 3) { 288a9e8641dSBaptiste Daroussin syslog(LOG_NOTICE, "remote delivery %s: AUTH login failed: %s", 289a9e8641dSBaptiste Daroussin res == 5 ? "failed" : "deferred", neterr); 290a9e8641dSBaptiste Daroussin return (res == 5 ? -1 : 1); 291a9e8641dSBaptiste Daroussin } 292a9e8641dSBaptiste Daroussin 293a9e8641dSBaptiste Daroussin len = base64_encode(password, strlen(password), &temp); 294a9e8641dSBaptiste Daroussin if (len < 0) 295a9e8641dSBaptiste Daroussin goto encerr; 296a9e8641dSBaptiste Daroussin 297a9e8641dSBaptiste Daroussin send_remote_command(fd, "%s", temp); 298a9e8641dSBaptiste Daroussin free(temp); 299a9e8641dSBaptiste Daroussin res = read_remote(fd, 0, NULL); 300a9e8641dSBaptiste Daroussin if (res != 2) { 301a9e8641dSBaptiste Daroussin syslog(LOG_NOTICE, "remote delivery %s: Authentication failed: %s", 302a9e8641dSBaptiste Daroussin res == 5 ? "failed" : "deferred", neterr); 303a9e8641dSBaptiste Daroussin return (res == 5 ? -1 : 1); 304a9e8641dSBaptiste Daroussin } 305a9e8641dSBaptiste Daroussin } else { 306a9e8641dSBaptiste Daroussin syslog(LOG_WARNING, "non-encrypted SMTP login is disabled in config, so skipping it. "); 307a9e8641dSBaptiste Daroussin return (1); 308a9e8641dSBaptiste Daroussin } 309a9e8641dSBaptiste Daroussin 310a9e8641dSBaptiste Daroussin return (0); 311a9e8641dSBaptiste Daroussin } 312a9e8641dSBaptiste Daroussin 313a9e8641dSBaptiste Daroussin static int 314a9e8641dSBaptiste Daroussin open_connection(struct mx_hostentry *h) 315a9e8641dSBaptiste Daroussin { 316a9e8641dSBaptiste Daroussin int fd; 317a9e8641dSBaptiste Daroussin 318a9e8641dSBaptiste Daroussin syslog(LOG_INFO, "trying remote delivery to %s [%s] pref %d", 319a9e8641dSBaptiste Daroussin h->host, h->addr, h->pref); 320a9e8641dSBaptiste Daroussin 321a9e8641dSBaptiste Daroussin fd = socket(h->ai.ai_family, h->ai.ai_socktype, h->ai.ai_protocol); 322a9e8641dSBaptiste Daroussin if (fd < 0) { 323a9e8641dSBaptiste Daroussin syslog(LOG_INFO, "socket for %s [%s] failed: %m", 324a9e8641dSBaptiste Daroussin h->host, h->addr); 325a9e8641dSBaptiste Daroussin return (-1); 326a9e8641dSBaptiste Daroussin } 327a9e8641dSBaptiste Daroussin 328a9e8641dSBaptiste Daroussin if (connect(fd, (struct sockaddr *)&h->sa, h->ai.ai_addrlen) < 0) { 329a9e8641dSBaptiste Daroussin syslog(LOG_INFO, "connect to %s [%s] failed: %m", 330a9e8641dSBaptiste Daroussin h->host, h->addr); 331a9e8641dSBaptiste Daroussin close(fd); 332a9e8641dSBaptiste Daroussin return (-1); 333a9e8641dSBaptiste Daroussin } 334a9e8641dSBaptiste Daroussin 335a9e8641dSBaptiste Daroussin return (fd); 336a9e8641dSBaptiste Daroussin } 337a9e8641dSBaptiste Daroussin 338a9e8641dSBaptiste Daroussin static void 339a9e8641dSBaptiste Daroussin close_connection(int fd) 340a9e8641dSBaptiste Daroussin { 341a9e8641dSBaptiste Daroussin if (config.ssl != NULL) { 342a9e8641dSBaptiste Daroussin if (((config.features & SECURETRANS) != 0) && 343a9e8641dSBaptiste Daroussin ((config.features & NOSSL) == 0)) 344a9e8641dSBaptiste Daroussin SSL_shutdown(config.ssl); 345a9e8641dSBaptiste Daroussin SSL_free(config.ssl); 346a9e8641dSBaptiste Daroussin } 347a9e8641dSBaptiste Daroussin 348a9e8641dSBaptiste Daroussin close(fd); 349a9e8641dSBaptiste Daroussin } 350a9e8641dSBaptiste Daroussin 351a9e8641dSBaptiste Daroussin static int 352a9e8641dSBaptiste Daroussin deliver_to_host(struct qitem *it, struct mx_hostentry *host) 353a9e8641dSBaptiste Daroussin { 354a9e8641dSBaptiste Daroussin struct authuser *a; 355a9e8641dSBaptiste Daroussin char line[1000]; 356a9e8641dSBaptiste Daroussin size_t linelen; 357a9e8641dSBaptiste Daroussin int fd, error = 0, do_auth = 0, res = 0; 358a9e8641dSBaptiste Daroussin 359a9e8641dSBaptiste Daroussin if (fseek(it->mailf, 0, SEEK_SET) != 0) { 360a9e8641dSBaptiste Daroussin snprintf(errmsg, sizeof(errmsg), "can not seek: %s", strerror(errno)); 361a9e8641dSBaptiste Daroussin return (-1); 362a9e8641dSBaptiste Daroussin } 363a9e8641dSBaptiste Daroussin 364a9e8641dSBaptiste Daroussin fd = open_connection(host); 365a9e8641dSBaptiste Daroussin if (fd < 0) 366a9e8641dSBaptiste Daroussin return (1); 367a9e8641dSBaptiste Daroussin 368a9e8641dSBaptiste Daroussin #define READ_REMOTE_CHECK(c, exp) \ 369a9e8641dSBaptiste Daroussin res = read_remote(fd, 0, NULL); \ 370a9e8641dSBaptiste Daroussin if (res == 5) { \ 371a9e8641dSBaptiste Daroussin syslog(LOG_ERR, "remote delivery to %s [%s] failed after %s: %s", \ 372a9e8641dSBaptiste Daroussin host->host, host->addr, c, neterr); \ 373a9e8641dSBaptiste Daroussin snprintf(errmsg, sizeof(errmsg), "%s [%s] did not like our %s:\n%s", \ 374a9e8641dSBaptiste Daroussin host->host, host->addr, c, neterr); \ 375*2382c29eSBaptiste Daroussin error = -1; \ 376*2382c29eSBaptiste Daroussin goto out; \ 377a9e8641dSBaptiste Daroussin } else if (res != exp) { \ 378a9e8641dSBaptiste Daroussin syslog(LOG_NOTICE, "remote delivery deferred: %s [%s] failed after %s: %s", \ 379a9e8641dSBaptiste Daroussin host->host, host->addr, c, neterr); \ 380*2382c29eSBaptiste Daroussin error = 1; \ 381*2382c29eSBaptiste Daroussin goto out; \ 382a9e8641dSBaptiste Daroussin } 383a9e8641dSBaptiste Daroussin 384a9e8641dSBaptiste Daroussin /* Check first reply from remote host */ 385a9e8641dSBaptiste Daroussin if ((config.features & SECURETRANS) == 0 || 386a9e8641dSBaptiste Daroussin (config.features & STARTTLS) != 0) { 387a9e8641dSBaptiste Daroussin config.features |= NOSSL; 388a9e8641dSBaptiste Daroussin READ_REMOTE_CHECK("connect", 2); 389a9e8641dSBaptiste Daroussin 390a9e8641dSBaptiste Daroussin config.features &= ~NOSSL; 391a9e8641dSBaptiste Daroussin } 392a9e8641dSBaptiste Daroussin 393a9e8641dSBaptiste Daroussin if ((config.features & SECURETRANS) != 0) { 394a9e8641dSBaptiste Daroussin error = smtp_init_crypto(fd, config.features); 395a9e8641dSBaptiste Daroussin if (error == 0) 396a9e8641dSBaptiste Daroussin syslog(LOG_DEBUG, "SSL initialization successful"); 397a9e8641dSBaptiste Daroussin else 398a9e8641dSBaptiste Daroussin goto out; 399a9e8641dSBaptiste Daroussin 400a9e8641dSBaptiste Daroussin if ((config.features & STARTTLS) == 0) 401a9e8641dSBaptiste Daroussin READ_REMOTE_CHECK("connect", 2); 402a9e8641dSBaptiste Daroussin } 403a9e8641dSBaptiste Daroussin 404a9e8641dSBaptiste Daroussin /* XXX allow HELO fallback */ 405a9e8641dSBaptiste Daroussin /* XXX record ESMTP keywords */ 406a9e8641dSBaptiste Daroussin send_remote_command(fd, "EHLO %s", hostname()); 407a9e8641dSBaptiste Daroussin READ_REMOTE_CHECK("EHLO", 2); 408a9e8641dSBaptiste Daroussin 409a9e8641dSBaptiste Daroussin /* 410a9e8641dSBaptiste Daroussin * Use SMTP authentication if the user defined an entry for the remote 411a9e8641dSBaptiste Daroussin * or smarthost 412a9e8641dSBaptiste Daroussin */ 413a9e8641dSBaptiste Daroussin SLIST_FOREACH(a, &authusers, next) { 414a9e8641dSBaptiste Daroussin if (strcmp(a->host, host->host) == 0) { 415a9e8641dSBaptiste Daroussin do_auth = 1; 416a9e8641dSBaptiste Daroussin break; 417a9e8641dSBaptiste Daroussin } 418a9e8641dSBaptiste Daroussin } 419a9e8641dSBaptiste Daroussin 420a9e8641dSBaptiste Daroussin if (do_auth == 1) { 421a9e8641dSBaptiste Daroussin /* 422a9e8641dSBaptiste Daroussin * Check if the user wants plain text login without using 423a9e8641dSBaptiste Daroussin * encryption. 424a9e8641dSBaptiste Daroussin */ 425a9e8641dSBaptiste Daroussin syslog(LOG_INFO, "using SMTP authentication for user %s", a->login); 426a9e8641dSBaptiste Daroussin error = smtp_login(fd, a->login, a->password); 427a9e8641dSBaptiste Daroussin if (error < 0) { 428a9e8641dSBaptiste Daroussin syslog(LOG_ERR, "remote delivery failed:" 429a9e8641dSBaptiste Daroussin " SMTP login failed: %m"); 430a9e8641dSBaptiste Daroussin snprintf(errmsg, sizeof(errmsg), "SMTP login to %s failed", host->host); 431*2382c29eSBaptiste Daroussin error = -1; 432*2382c29eSBaptiste Daroussin goto out; 433a9e8641dSBaptiste Daroussin } 434a9e8641dSBaptiste Daroussin /* SMTP login is not available, so try without */ 435a9e8641dSBaptiste Daroussin else if (error > 0) { 436a9e8641dSBaptiste Daroussin syslog(LOG_WARNING, "SMTP login not available. Trying without."); 437a9e8641dSBaptiste Daroussin } 438a9e8641dSBaptiste Daroussin } 439a9e8641dSBaptiste Daroussin 440a9e8641dSBaptiste Daroussin /* XXX send ESMTP ENVID, RET (FULL/HDRS) and 8BITMIME */ 441a9e8641dSBaptiste Daroussin send_remote_command(fd, "MAIL FROM:<%s>", it->sender); 442a9e8641dSBaptiste Daroussin READ_REMOTE_CHECK("MAIL FROM", 2); 443a9e8641dSBaptiste Daroussin 444a9e8641dSBaptiste Daroussin /* XXX send ESMTP ORCPT */ 445a9e8641dSBaptiste Daroussin send_remote_command(fd, "RCPT TO:<%s>", it->addr); 446a9e8641dSBaptiste Daroussin READ_REMOTE_CHECK("RCPT TO", 2); 447a9e8641dSBaptiste Daroussin 448a9e8641dSBaptiste Daroussin send_remote_command(fd, "DATA"); 449a9e8641dSBaptiste Daroussin READ_REMOTE_CHECK("DATA", 3); 450a9e8641dSBaptiste Daroussin 451a9e8641dSBaptiste Daroussin error = 0; 452a9e8641dSBaptiste Daroussin while (!feof(it->mailf)) { 453a9e8641dSBaptiste Daroussin if (fgets(line, sizeof(line), it->mailf) == NULL) 454a9e8641dSBaptiste Daroussin break; 455a9e8641dSBaptiste Daroussin linelen = strlen(line); 456a9e8641dSBaptiste Daroussin if (linelen == 0 || line[linelen - 1] != '\n') { 457a9e8641dSBaptiste Daroussin syslog(LOG_CRIT, "remote delivery failed: corrupted queue file"); 458a9e8641dSBaptiste Daroussin snprintf(errmsg, sizeof(errmsg), "corrupted queue file"); 459a9e8641dSBaptiste Daroussin error = -1; 460a9e8641dSBaptiste Daroussin goto out; 461a9e8641dSBaptiste Daroussin } 462a9e8641dSBaptiste Daroussin 463a9e8641dSBaptiste Daroussin /* Remove trailing \n's and escape leading dots */ 464a9e8641dSBaptiste Daroussin trim_line(line); 465a9e8641dSBaptiste Daroussin 466a9e8641dSBaptiste Daroussin /* 467a9e8641dSBaptiste Daroussin * If the first character is a dot, we escape it so the line 468a9e8641dSBaptiste Daroussin * length increases 469a9e8641dSBaptiste Daroussin */ 470a9e8641dSBaptiste Daroussin if (line[0] == '.') 471a9e8641dSBaptiste Daroussin linelen++; 472a9e8641dSBaptiste Daroussin 473a9e8641dSBaptiste Daroussin if (send_remote_command(fd, "%s", line) != (ssize_t)linelen+1) { 474a9e8641dSBaptiste Daroussin syslog(LOG_NOTICE, "remote delivery deferred: write error"); 475a9e8641dSBaptiste Daroussin error = 1; 476a9e8641dSBaptiste Daroussin goto out; 477a9e8641dSBaptiste Daroussin } 478a9e8641dSBaptiste Daroussin } 479a9e8641dSBaptiste Daroussin 480a9e8641dSBaptiste Daroussin send_remote_command(fd, "."); 481a9e8641dSBaptiste Daroussin READ_REMOTE_CHECK("final DATA", 2); 482a9e8641dSBaptiste Daroussin 483a9e8641dSBaptiste Daroussin send_remote_command(fd, "QUIT"); 484a9e8641dSBaptiste Daroussin if (read_remote(fd, 0, NULL) != 2) 485a9e8641dSBaptiste Daroussin syslog(LOG_INFO, "remote delivery succeeded but QUIT failed: %s", neterr); 486a9e8641dSBaptiste Daroussin out: 487a9e8641dSBaptiste Daroussin 488a9e8641dSBaptiste Daroussin close_connection(fd); 489a9e8641dSBaptiste Daroussin return (error); 490a9e8641dSBaptiste Daroussin } 491a9e8641dSBaptiste Daroussin 492a9e8641dSBaptiste Daroussin int 493a9e8641dSBaptiste Daroussin deliver_remote(struct qitem *it) 494a9e8641dSBaptiste Daroussin { 495a9e8641dSBaptiste Daroussin struct mx_hostentry *hosts, *h; 496a9e8641dSBaptiste Daroussin const char *host; 497a9e8641dSBaptiste Daroussin int port; 498a9e8641dSBaptiste Daroussin int error = 1, smarthost = 0; 499a9e8641dSBaptiste Daroussin 500a9e8641dSBaptiste Daroussin port = SMTP_PORT; 501a9e8641dSBaptiste Daroussin 502a9e8641dSBaptiste Daroussin /* Smarthost support? */ 503a9e8641dSBaptiste Daroussin if (config.smarthost != NULL) { 504a9e8641dSBaptiste Daroussin host = config.smarthost; 505a9e8641dSBaptiste Daroussin port = config.port; 506a9e8641dSBaptiste Daroussin syslog(LOG_INFO, "using smarthost (%s:%i)", host, port); 507a9e8641dSBaptiste Daroussin smarthost = 1; 508a9e8641dSBaptiste Daroussin } else { 509a9e8641dSBaptiste Daroussin host = strrchr(it->addr, '@'); 510a9e8641dSBaptiste Daroussin /* Should not happen */ 511a9e8641dSBaptiste Daroussin if (host == NULL) { 512a9e8641dSBaptiste Daroussin snprintf(errmsg, sizeof(errmsg), "Internal error: badly formed address %s", 513a9e8641dSBaptiste Daroussin it->addr); 514a9e8641dSBaptiste Daroussin return(-1); 515a9e8641dSBaptiste Daroussin } else { 516a9e8641dSBaptiste Daroussin /* Step over the @ */ 517a9e8641dSBaptiste Daroussin host++; 518a9e8641dSBaptiste Daroussin } 519a9e8641dSBaptiste Daroussin } 520a9e8641dSBaptiste Daroussin 521a9e8641dSBaptiste Daroussin error = dns_get_mx_list(host, port, &hosts, smarthost); 522a9e8641dSBaptiste Daroussin if (error) { 523a9e8641dSBaptiste Daroussin snprintf(errmsg, sizeof(errmsg), "DNS lookup failure: host %s not found", host); 524a9e8641dSBaptiste Daroussin syslog(LOG_NOTICE, "remote delivery %s: DNS lookup failure: host %s not found", 525a9e8641dSBaptiste Daroussin error < 0 ? "failed" : "deferred", 526a9e8641dSBaptiste Daroussin host); 527a9e8641dSBaptiste Daroussin return (error); 528a9e8641dSBaptiste Daroussin } 529a9e8641dSBaptiste Daroussin 530a9e8641dSBaptiste Daroussin for (h = hosts; *h->host != 0; h++) { 531a9e8641dSBaptiste Daroussin switch (deliver_to_host(it, h)) { 532a9e8641dSBaptiste Daroussin case 0: 533a9e8641dSBaptiste Daroussin /* success */ 534a9e8641dSBaptiste Daroussin error = 0; 535a9e8641dSBaptiste Daroussin goto out; 536a9e8641dSBaptiste Daroussin case 1: 537a9e8641dSBaptiste Daroussin /* temp failure */ 538a9e8641dSBaptiste Daroussin error = 1; 539a9e8641dSBaptiste Daroussin break; 540a9e8641dSBaptiste Daroussin default: 541a9e8641dSBaptiste Daroussin /* perm failure */ 542a9e8641dSBaptiste Daroussin error = -1; 543a9e8641dSBaptiste Daroussin goto out; 544a9e8641dSBaptiste Daroussin } 545a9e8641dSBaptiste Daroussin } 546a9e8641dSBaptiste Daroussin out: 547a9e8641dSBaptiste Daroussin free(hosts); 548a9e8641dSBaptiste Daroussin 549a9e8641dSBaptiste Daroussin return (error); 550a9e8641dSBaptiste Daroussin } 551