1/* $OpenBSD: smtp.c,v 1.174 2023/05/16 17:48:52 op 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 <errno.h> 22#include <stdlib.h> 23#include <tls.h> 24#include <unistd.h> 25 26#include "smtpd.h" 27#include "log.h" 28#include "ssl.h" 29 30static void smtp_setup_events(void); 31static void smtp_pause(void); 32static void smtp_resume(void); 33static void smtp_accept(int, short, void *); 34static void smtp_dropped(struct listener *, int, const struct sockaddr_storage *); 35static int smtp_enqueue(void); 36static int smtp_can_accept(void); 37static void smtp_setup_listeners(void); 38static void smtp_setup_listener_tls(struct listener *); 39 40int 41proxy_session(struct listener *listener, int sock, 42 const struct sockaddr_storage *ss, 43 void (*accepted)(struct listener *, int, 44 const struct sockaddr_storage *, struct io *), 45 void (*dropped)(struct listener *, int, 46 const struct sockaddr_storage *)); 47 48static void smtp_accepted(struct listener *, int, const struct sockaddr_storage *, struct io *); 49 50/* 51 * This function are not publicy exported because it is a hack until libtls 52 * has a proper privsep setup 53 */ 54void tls_config_use_fake_private_key(struct tls_config *config); 55 56#define SMTP_FD_RESERVE 5 57static size_t sessions; 58static size_t maxsessions; 59 60void 61smtp_imsg(struct mproc *p, struct imsg *imsg) 62{ 63 switch (imsg->hdr.type) { 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_FILTER_SMTP_PROTOCOL: 69 case IMSG_FILTER_SMTP_DATA_BEGIN: 70 smtp_session_imsg(p, imsg); 71 return; 72 73 case IMSG_SMTP_MESSAGE_COMMIT: 74 case IMSG_SMTP_MESSAGE_CREATE: 75 case IMSG_SMTP_MESSAGE_OPEN: 76 case IMSG_QUEUE_ENVELOPE_SUBMIT: 77 case IMSG_QUEUE_ENVELOPE_COMMIT: 78 smtp_session_imsg(p, imsg); 79 return; 80 81 case IMSG_QUEUE_SMTP_SESSION: 82 m_compose(p, IMSG_QUEUE_SMTP_SESSION, 0, 0, smtp_enqueue(), 83 imsg->data, imsg->hdr.len - sizeof imsg->hdr); 84 return; 85 86 case IMSG_CTL_SMTP_SESSION: 87 m_compose(p, IMSG_CTL_SMTP_SESSION, imsg->hdr.peerid, 0, 88 smtp_enqueue(), NULL, 0); 89 return; 90 91 case IMSG_CTL_PAUSE_SMTP: 92 log_debug("debug: smtp: pausing listening sockets"); 93 smtp_pause(); 94 env->sc_flags |= SMTPD_SMTP_PAUSED; 95 return; 96 97 case IMSG_CTL_RESUME_SMTP: 98 log_debug("debug: smtp: resuming listening sockets"); 99 env->sc_flags &= ~SMTPD_SMTP_PAUSED; 100 smtp_resume(); 101 return; 102 } 103 104 fatalx("smtp_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); 105} 106 107void 108smtp_postfork(void) 109{ 110 smtp_setup_listeners(); 111} 112 113void 114smtp_postprivdrop(void) 115{ 116} 117 118void 119smtp_configure(void) 120{ 121 smtp_setup_events(); 122} 123 124static void 125smtp_setup_listeners(void) 126{ 127 struct listener *l; 128 int opt; 129 130 TAILQ_FOREACH(l, env->sc_listeners, entry) { 131 if ((l->fd = socket(l->ss.ss_family, SOCK_STREAM, 0)) == -1) { 132 if (errno == EAFNOSUPPORT) { 133 log_warn("smtpd: socket"); 134 continue; 135 } 136 fatal("smtpd: socket"); 137 } 138 139 if (l->flags & F_SSL) 140 smtp_setup_listener_tls(l); 141 142 opt = 1; 143 if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, &opt, 144 sizeof(opt)) == -1) 145 fatal("smtpd: setsockopt"); 146 if (bind(l->fd, (struct sockaddr *)&l->ss, l->ss.ss_len) == -1) 147 fatal("smtpd: bind"); 148 } 149} 150 151static void 152smtp_setup_listener_tls(struct listener *l) 153{ 154 static const char *dheparams[] = { "none", "auto", "legacy" }; 155 struct tls_config *config; 156 const char *ciphers; 157 uint32_t protos; 158 struct pki *pki; 159 struct ca *ca; 160 int i; 161 162 if ((config = tls_config_new()) == NULL) 163 fatal("smtpd: tls_config_new"); 164 165 ciphers = env->sc_tls_ciphers; 166 if (l->tls_ciphers) 167 ciphers = l->tls_ciphers; 168 if (ciphers && tls_config_set_ciphers(config, ciphers) == -1) 169 fatalx("%s", tls_config_error(config)); 170 171 if (l->tls_protocols) { 172 if (tls_config_parse_protocols(&protos, l->tls_protocols) == -1) 173 fatalx("failed to parse protocols \"%s\"", 174 l->tls_protocols); 175 if (tls_config_set_protocols(config, protos) == -1) 176 fatalx("%s", tls_config_error(config)); 177 } 178 179 pki = l->pki[0]; 180 if (pki == NULL) 181 fatal("no pki defined"); 182 183 if (tls_config_set_dheparams(config, dheparams[pki->pki_dhe]) == -1) 184 fatalx("tls_config_set_dheparams: %s", 185 tls_config_error(config)); 186 187 tls_config_use_fake_private_key(config); 188 for (i = 0; i < l->pkicount; i++) { 189 pki = l->pki[i]; 190 if (i == 0) { 191 if (tls_config_set_keypair_mem(config, pki->pki_cert, 192 pki->pki_cert_len, NULL, 0) == -1) 193 fatalx("tls_config_set_keypair_mem: %s", 194 tls_config_error(config)); 195 } else { 196 if (tls_config_add_keypair_mem(config, pki->pki_cert, 197 pki->pki_cert_len, NULL, 0) == -1) 198 fatalx("tls_config_add_keypair_mem: %s", 199 tls_config_error(config)); 200 } 201 } 202 free(l->pki); 203 l->pkicount = 0; 204 205 if (l->ca_name[0]) { 206 ca = dict_get(env->sc_ca_dict, l->ca_name); 207 if (tls_config_set_ca_mem(config, ca->ca_cert, ca->ca_cert_len) 208 == -1) 209 fatalx("tls_config_set_ca_mem: %s", 210 tls_config_error(config)); 211 } 212 else if (tls_config_set_ca_file(config, tls_default_ca_cert_file()) 213 == -1) 214 fatal("tls_config_set_ca_file"); 215 216 if (l->flags & F_TLS_VERIFY) 217 tls_config_verify_client(config); 218 219 l->tls = tls_server(); 220 if (l->tls == NULL) 221 fatal("tls_server"); 222 if (tls_configure(l->tls, config) == -1) { 223 fatalx("tls_configure: %s", tls_error(l->tls)); 224 } 225 tls_config_free(config); 226} 227 228 229static void 230smtp_setup_events(void) 231{ 232 struct listener *l; 233 234 TAILQ_FOREACH(l, env->sc_listeners, entry) { 235 log_debug("debug: smtp: listen on %s port %d flags 0x%01x", 236 ss_to_text(&l->ss), ntohs(l->port), l->flags); 237 238 io_set_nonblocking(l->fd); 239 if (listen(l->fd, SMTPD_BACKLOG) == -1) 240 fatal("listen"); 241 event_set(&l->ev, l->fd, EV_READ|EV_PERSIST, smtp_accept, l); 242 243 if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) 244 event_add(&l->ev, NULL); 245 } 246 247 purge_config(PURGE_PKI_KEYS); 248 249 maxsessions = (getdtablesize() - getdtablecount()) / 2 - SMTP_FD_RESERVE; 250 log_debug("debug: smtp: will accept at most %zu clients", maxsessions); 251} 252 253static void 254smtp_pause(void) 255{ 256 struct listener *l; 257 258 if (env->sc_flags & (SMTPD_SMTP_DISABLED|SMTPD_SMTP_PAUSED)) 259 return; 260 261 TAILQ_FOREACH(l, env->sc_listeners, entry) 262 event_del(&l->ev); 263} 264 265static void 266smtp_resume(void) 267{ 268 struct listener *l; 269 270 if (env->sc_flags & (SMTPD_SMTP_DISABLED|SMTPD_SMTP_PAUSED)) 271 return; 272 273 TAILQ_FOREACH(l, env->sc_listeners, entry) 274 event_add(&l->ev, NULL); 275} 276 277static int 278smtp_enqueue(void) 279{ 280 struct listener *listener = env->sc_sock_listener; 281 int fd[2]; 282 283 /* 284 * Some enqueue requests buffered in IMSG may still arrive even after 285 * call to smtp_pause() because enqueue listener is not a real socket 286 * and thus cannot be paused properly. 287 */ 288 if (env->sc_flags & SMTPD_SMTP_PAUSED) 289 return (-1); 290 291 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd)) 292 return (-1); 293 294 if ((smtp_session(listener, fd[0], &listener->ss, env->sc_hostname, NULL)) == -1) { 295 close(fd[0]); 296 close(fd[1]); 297 return (-1); 298 } 299 300 sessions++; 301 stat_increment("smtp.session", 1); 302 stat_increment("smtp.session.local", 1); 303 304 return (fd[1]); 305} 306 307static void 308smtp_accept(int fd, short event, void *p) 309{ 310 struct listener *listener = p; 311 struct sockaddr_storage ss; 312 socklen_t len; 313 int sock; 314 315 if (env->sc_flags & SMTPD_SMTP_PAUSED) 316 fatalx("smtp_session: unexpected client"); 317 318 if (!smtp_can_accept()) { 319 log_warnx("warn: Disabling incoming SMTP connections: " 320 "Client limit reached"); 321 goto pause; 322 } 323 324 len = sizeof(ss); 325 if ((sock = accept(fd, (struct sockaddr *)&ss, &len)) == -1) { 326 if (errno == ENFILE || errno == EMFILE) { 327 log_warn("warn: Disabling incoming SMTP connections"); 328 goto pause; 329 } 330 if (errno == EINTR || errno == EWOULDBLOCK || 331 errno == ECONNABORTED) 332 return; 333 fatal("smtp_accept"); 334 } 335 336 if (listener->flags & F_PROXY) { 337 io_set_nonblocking(sock); 338 if (proxy_session(listener, sock, &ss, 339 smtp_accepted, smtp_dropped) == -1) { 340 close(sock); 341 return; 342 } 343 return; 344 } 345 346 smtp_accepted(listener, sock, &ss, NULL); 347 return; 348 349pause: 350 smtp_pause(); 351 env->sc_flags |= SMTPD_SMTP_DISABLED; 352 return; 353} 354 355static int 356smtp_can_accept(void) 357{ 358 if (sessions + 1 == maxsessions) 359 return 0; 360 return (getdtablesize() - getdtablecount() - SMTP_FD_RESERVE >= 2); 361} 362 363void 364smtp_collect(void) 365{ 366 sessions--; 367 stat_decrement("smtp.session", 1); 368 369 if (!smtp_can_accept()) 370 return; 371 372 if (env->sc_flags & SMTPD_SMTP_DISABLED) { 373 log_warnx("warn: smtp: " 374 "fd exhaustion over, re-enabling incoming connections"); 375 env->sc_flags &= ~SMTPD_SMTP_DISABLED; 376 smtp_resume(); 377 } 378} 379 380static void 381smtp_accepted(struct listener *listener, int sock, const struct sockaddr_storage *ss, struct io *io) 382{ 383 int ret; 384 385 ret = smtp_session(listener, sock, ss, NULL, io); 386 if (ret == -1) { 387 log_warn("warn: Failed to create SMTP session"); 388 close(sock); 389 return; 390 } 391 io_set_nonblocking(sock); 392 393 sessions++; 394 stat_increment("smtp.session", 1); 395 if (listener->ss.ss_family == AF_LOCAL) 396 stat_increment("smtp.session.local", 1); 397 if (listener->ss.ss_family == AF_INET) 398 stat_increment("smtp.session.inet4", 1); 399 if (listener->ss.ss_family == AF_INET6) 400 stat_increment("smtp.session.inet6", 1); 401} 402 403static void 404smtp_dropped(struct listener *listener, int sock, const struct sockaddr_storage *ss) 405{ 406 close(sock); 407 sessions--; 408} 409