1 /* $OpenBSD: smtp.c,v 1.166 2019/08/10 16:07:01 gilles 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 void smtp_dropped(struct listener *, int, const struct sockaddr_storage *); 50 static int smtp_enqueue(void); 51 static int smtp_can_accept(void); 52 static void smtp_setup_listeners(void); 53 static int smtp_sni_callback(SSL *, int *, void *); 54 55 int 56 proxy_session(struct listener *listener, int sock, 57 const struct sockaddr_storage *ss, 58 void (*accepted)(struct listener *, int, 59 const struct sockaddr_storage *, struct io *), 60 void (*dropped)(struct listener *, int, 61 const struct sockaddr_storage *)); 62 63 static void smtp_accepted(struct listener *, int, const struct sockaddr_storage *, struct io *); 64 65 66 #define SMTP_FD_RESERVE 5 67 static size_t sessions; 68 static size_t maxsessions; 69 70 void 71 smtp_imsg(struct mproc *p, struct imsg *imsg) 72 { 73 switch (imsg->hdr.type) { 74 case IMSG_SMTP_CHECK_SENDER: 75 case IMSG_SMTP_EXPAND_RCPT: 76 case IMSG_SMTP_LOOKUP_HELO: 77 case IMSG_SMTP_AUTHENTICATE: 78 case IMSG_FILTER_SMTP_PROTOCOL: 79 case IMSG_FILTER_SMTP_DATA_BEGIN: 80 smtp_session_imsg(p, imsg); 81 return; 82 83 case IMSG_SMTP_MESSAGE_COMMIT: 84 case IMSG_SMTP_MESSAGE_CREATE: 85 case IMSG_SMTP_MESSAGE_OPEN: 86 case IMSG_QUEUE_ENVELOPE_SUBMIT: 87 case IMSG_QUEUE_ENVELOPE_COMMIT: 88 smtp_session_imsg(p, imsg); 89 return; 90 91 case IMSG_QUEUE_SMTP_SESSION: 92 m_compose(p, IMSG_QUEUE_SMTP_SESSION, 0, 0, smtp_enqueue(), 93 imsg->data, imsg->hdr.len - sizeof imsg->hdr); 94 return; 95 96 case IMSG_CTL_SMTP_SESSION: 97 m_compose(p, IMSG_CTL_SMTP_SESSION, imsg->hdr.peerid, 0, 98 smtp_enqueue(), NULL, 0); 99 return; 100 101 case IMSG_CTL_PAUSE_SMTP: 102 log_debug("debug: smtp: pausing listening sockets"); 103 smtp_pause(); 104 env->sc_flags |= SMTPD_SMTP_PAUSED; 105 return; 106 107 case IMSG_CTL_RESUME_SMTP: 108 log_debug("debug: smtp: resuming listening sockets"); 109 env->sc_flags &= ~SMTPD_SMTP_PAUSED; 110 smtp_resume(); 111 return; 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)) == -1) 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, NULL)) == -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 (listener->flags & F_PROXY) { 279 io_set_nonblocking(sock); 280 if (proxy_session(listener, sock, &ss, 281 smtp_accepted, smtp_dropped) == -1) { 282 close(sock); 283 return; 284 } 285 return; 286 } 287 288 smtp_accepted(listener, sock, &ss, NULL); 289 return; 290 291 pause: 292 smtp_pause(); 293 env->sc_flags |= SMTPD_SMTP_DISABLED; 294 return; 295 } 296 297 static int 298 smtp_can_accept(void) 299 { 300 if (sessions + 1 == maxsessions) 301 return 0; 302 return (getdtablesize() - getdtablecount() - SMTP_FD_RESERVE >= 2); 303 } 304 305 void 306 smtp_collect(void) 307 { 308 sessions--; 309 stat_decrement("smtp.session", 1); 310 311 if (!smtp_can_accept()) 312 return; 313 314 if (env->sc_flags & SMTPD_SMTP_DISABLED) { 315 log_warnx("warn: smtp: " 316 "fd exhaustion over, re-enabling incoming connections"); 317 env->sc_flags &= ~SMTPD_SMTP_DISABLED; 318 smtp_resume(); 319 } 320 } 321 322 static int 323 smtp_sni_callback(SSL *ssl, int *ad, void *arg) 324 { 325 const char *sn; 326 void *ssl_ctx; 327 328 sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); 329 if (sn == NULL) 330 return SSL_TLSEXT_ERR_NOACK; 331 ssl_ctx = dict_get(env->sc_ssl_dict, sn); 332 if (ssl_ctx == NULL) 333 return SSL_TLSEXT_ERR_NOACK; 334 SSL_set_SSL_CTX(ssl, ssl_ctx); 335 return SSL_TLSEXT_ERR_OK; 336 } 337 338 static void 339 smtp_accepted(struct listener *listener, int sock, const struct sockaddr_storage *ss, struct io *io) 340 { 341 int ret; 342 343 ret = smtp_session(listener, sock, ss, NULL, io); 344 if (ret == -1) { 345 log_warn("warn: Failed to create SMTP session"); 346 close(sock); 347 return; 348 } 349 io_set_nonblocking(sock); 350 351 sessions++; 352 stat_increment("smtp.session", 1); 353 if (listener->ss.ss_family == AF_LOCAL) 354 stat_increment("smtp.session.local", 1); 355 if (listener->ss.ss_family == AF_INET) 356 stat_increment("smtp.session.inet4", 1); 357 if (listener->ss.ss_family == AF_INET6) 358 stat_increment("smtp.session.inet6", 1); 359 } 360 361 static void 362 smtp_dropped(struct listener *listener, int sock, const struct sockaddr_storage *ss) 363 { 364 close(sock); 365 sessions--; 366 } 367