1 /* $OpenBSD: smtp.c,v 1.155 2016/03/25 15:06:58 krw Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> 5 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 6 * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/types.h> 22 #include <sys/queue.h> 23 #include <sys/tree.h> 24 #include <sys/socket.h> 25 26 #include <err.h> 27 #include <errno.h> 28 #include <event.h> 29 #include <imsg.h> 30 #include <netdb.h> 31 #include <pwd.h> 32 #include <signal.h> 33 #include <limits.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include <openssl/ssl.h> 40 41 #include "smtpd.h" 42 #include "log.h" 43 #include "ssl.h" 44 45 static void smtp_setup_events(void); 46 static void smtp_pause(void); 47 static void smtp_resume(void); 48 static void smtp_accept(int, short, void *); 49 static int smtp_enqueue(void); 50 static int smtp_can_accept(void); 51 static void smtp_setup_listeners(void); 52 static int smtp_sni_callback(SSL *, int *, void *); 53 54 #define SMTP_FD_RESERVE 5 55 static size_t sessions; 56 static size_t maxsessions; 57 58 void 59 smtp_imsg(struct mproc *p, struct imsg *imsg) 60 { 61 if (p->proc == PROC_LKA) { 62 switch (imsg->hdr.type) { 63 case IMSG_SMTP_DNS_PTR: 64 case IMSG_SMTP_CHECK_SENDER: 65 case IMSG_SMTP_EXPAND_RCPT: 66 case IMSG_SMTP_LOOKUP_HELO: 67 case IMSG_SMTP_AUTHENTICATE: 68 case IMSG_SMTP_TLS_INIT: 69 case IMSG_SMTP_TLS_VERIFY: 70 smtp_session_imsg(p, imsg); 71 return; 72 } 73 } 74 75 if (p->proc == PROC_QUEUE) { 76 switch (imsg->hdr.type) { 77 case IMSG_SMTP_MESSAGE_COMMIT: 78 case IMSG_SMTP_MESSAGE_CREATE: 79 case IMSG_SMTP_MESSAGE_OPEN: 80 case IMSG_QUEUE_ENVELOPE_SUBMIT: 81 case IMSG_QUEUE_ENVELOPE_COMMIT: 82 smtp_session_imsg(p, imsg); 83 return; 84 85 case IMSG_QUEUE_SMTP_SESSION: 86 m_compose(p, IMSG_QUEUE_SMTP_SESSION, 0, 0, 87 smtp_enqueue(), imsg->data, 88 imsg->hdr.len - sizeof imsg->hdr); 89 return; 90 } 91 } 92 93 if (p->proc == PROC_CONTROL) { 94 switch (imsg->hdr.type) { 95 case IMSG_CTL_SMTP_SESSION: 96 m_compose(p, IMSG_CTL_SMTP_SESSION, imsg->hdr.peerid, 0, 97 smtp_enqueue(), NULL, 0); 98 return; 99 100 case IMSG_CTL_PAUSE_SMTP: 101 log_debug("debug: smtp: pausing listening sockets"); 102 smtp_pause(); 103 env->sc_flags |= SMTPD_SMTP_PAUSED; 104 return; 105 106 case IMSG_CTL_RESUME_SMTP: 107 log_debug("debug: smtp: resuming listening sockets"); 108 env->sc_flags &= ~SMTPD_SMTP_PAUSED; 109 smtp_resume(); 110 return; 111 } 112 } 113 114 errx(1, "smtp_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); 115 } 116 117 void 118 smtp_postfork(void) 119 { 120 smtp_setup_listeners(); 121 } 122 123 void 124 smtp_postprivdrop(void) 125 { 126 } 127 128 void 129 smtp_configure(void) 130 { 131 smtp_setup_events(); 132 } 133 134 static void 135 smtp_setup_listeners(void) 136 { 137 struct listener *l; 138 int opt; 139 140 TAILQ_FOREACH(l, env->sc_listeners, entry) { 141 if ((l->fd = socket(l->ss.ss_family, SOCK_STREAM, 0)) == -1) { 142 if (errno == EAFNOSUPPORT) { 143 log_warn("smtpd: socket"); 144 continue; 145 } 146 fatal("smtpd: socket"); 147 } 148 opt = 1; 149 if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, &opt, 150 sizeof(opt)) < 0) 151 fatal("smtpd: setsockopt"); 152 if (bind(l->fd, (struct sockaddr *)&l->ss, l->ss.ss_len) == -1) 153 fatal("smtpd: bind"); 154 } 155 } 156 157 static void 158 smtp_setup_events(void) 159 { 160 struct listener *l; 161 struct pki *pki; 162 SSL_CTX *ssl_ctx; 163 void *iter; 164 const char *k; 165 166 TAILQ_FOREACH(l, env->sc_listeners, entry) { 167 log_debug("debug: smtp: listen on %s port %d flags 0x%01x" 168 " pki \"%s\"" 169 " ca \"%s\"", ss_to_text(&l->ss), ntohs(l->port), 170 l->flags, l->pki_name, l->ca_name); 171 172 io_set_nonblocking(l->fd); 173 if (listen(l->fd, SMTPD_BACKLOG) == -1) 174 fatal("listen"); 175 event_set(&l->ev, l->fd, EV_READ|EV_PERSIST, smtp_accept, l); 176 177 if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) 178 event_add(&l->ev, NULL); 179 } 180 181 iter = NULL; 182 while (dict_iter(env->sc_pki_dict, &iter, &k, (void **)&pki)) { 183 if (!ssl_setup((SSL_CTX **)&ssl_ctx, pki, smtp_sni_callback, 184 env->sc_tls_ciphers)) 185 fatal("smtp_setup_events: ssl_setup failure"); 186 dict_xset(env->sc_ssl_dict, k, ssl_ctx); 187 } 188 189 purge_config(PURGE_PKI_KEYS); 190 191 maxsessions = (getdtablesize() - getdtablecount()) / 2 - SMTP_FD_RESERVE; 192 log_debug("debug: smtp: will accept at most %zu clients", maxsessions); 193 } 194 195 static void 196 smtp_pause(void) 197 { 198 struct listener *l; 199 200 if (env->sc_flags & (SMTPD_SMTP_DISABLED|SMTPD_SMTP_PAUSED)) 201 return; 202 203 TAILQ_FOREACH(l, env->sc_listeners, entry) 204 event_del(&l->ev); 205 } 206 207 static void 208 smtp_resume(void) 209 { 210 struct listener *l; 211 212 if (env->sc_flags & (SMTPD_SMTP_DISABLED|SMTPD_SMTP_PAUSED)) 213 return; 214 215 TAILQ_FOREACH(l, env->sc_listeners, entry) 216 event_add(&l->ev, NULL); 217 } 218 219 static int 220 smtp_enqueue(void) 221 { 222 struct listener *listener = env->sc_sock_listener; 223 int fd[2]; 224 225 /* 226 * Some enqueue requests buffered in IMSG may still arrive even after 227 * call to smtp_pause() because enqueue listener is not a real socket 228 * and thus cannot be paused properly. 229 */ 230 if (env->sc_flags & SMTPD_SMTP_PAUSED) 231 return (-1); 232 233 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd)) 234 return (-1); 235 236 if ((smtp_session(listener, fd[0], &listener->ss, env->sc_hostname)) == -1) { 237 close(fd[0]); 238 close(fd[1]); 239 return (-1); 240 } 241 242 sessions++; 243 stat_increment("smtp.session", 1); 244 stat_increment("smtp.session.local", 1); 245 246 return (fd[1]); 247 } 248 249 static void 250 smtp_accept(int fd, short event, void *p) 251 { 252 struct listener *listener = p; 253 struct sockaddr_storage ss; 254 socklen_t len; 255 int sock; 256 257 if (env->sc_flags & SMTPD_SMTP_PAUSED) 258 fatalx("smtp_session: unexpected client"); 259 260 if (!smtp_can_accept()) { 261 log_warnx("warn: Disabling incoming SMTP connections: " 262 "Client limit reached"); 263 goto pause; 264 } 265 266 len = sizeof(ss); 267 if ((sock = accept(fd, (struct sockaddr *)&ss, &len)) == -1) { 268 if (errno == ENFILE || errno == EMFILE) { 269 log_warn("warn: Disabling incoming SMTP connections"); 270 goto pause; 271 } 272 if (errno == EINTR || errno == EWOULDBLOCK || 273 errno == ECONNABORTED) 274 return; 275 fatal("smtp_accept"); 276 } 277 278 if (smtp_session(listener, sock, &ss, NULL) == -1) { 279 log_warn("warn: Failed to create SMTP session"); 280 close(sock); 281 return; 282 } 283 io_set_nonblocking(sock); 284 285 sessions++; 286 stat_increment("smtp.session", 1); 287 if (listener->ss.ss_family == AF_LOCAL) 288 stat_increment("smtp.session.local", 1); 289 if (listener->ss.ss_family == AF_INET) 290 stat_increment("smtp.session.inet4", 1); 291 if (listener->ss.ss_family == AF_INET6) 292 stat_increment("smtp.session.inet6", 1); 293 return; 294 295 pause: 296 smtp_pause(); 297 env->sc_flags |= SMTPD_SMTP_DISABLED; 298 return; 299 } 300 301 static int 302 smtp_can_accept(void) 303 { 304 if (sessions + 1 == maxsessions) 305 return 0; 306 return (getdtablesize() - getdtablecount() - SMTP_FD_RESERVE >= 2); 307 } 308 309 void 310 smtp_collect(void) 311 { 312 sessions--; 313 stat_decrement("smtp.session", 1); 314 315 if (!smtp_can_accept()) 316 return; 317 318 if (env->sc_flags & SMTPD_SMTP_DISABLED) { 319 log_warnx("warn: smtp: " 320 "fd exhaustion over, re-enabling incoming connections"); 321 env->sc_flags &= ~SMTPD_SMTP_DISABLED; 322 smtp_resume(); 323 } 324 } 325 326 static int 327 smtp_sni_callback(SSL *ssl, int *ad, void *arg) 328 { 329 const char *sn; 330 void *ssl_ctx; 331 332 sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); 333 if (sn == NULL) 334 return SSL_TLSEXT_ERR_NOACK; 335 ssl_ctx = dict_get(env->sc_ssl_dict, sn); 336 if (ssl_ctx == NULL) 337 return SSL_TLSEXT_ERR_NOACK; 338 SSL_set_SSL_CTX(ssl, ssl_ctx); 339 return SSL_TLSEXT_ERR_OK; 340 } 341