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