1 /* $OpenBSD: smtp.c,v 1.139 2014/07/08 14:38:17 eric 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 <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 38 #include <openssl/ssl.h> 39 40 #include "smtpd.h" 41 #include "log.h" 42 #include "ssl.h" 43 44 static void smtp_setup_events(void); 45 static void smtp_pause(void); 46 static void smtp_resume(void); 47 static void smtp_accept(int, short, void *); 48 static int smtp_enqueue(uid_t *); 49 static int smtp_can_accept(void); 50 static void smtp_setup_listeners(void); 51 52 #define SMTP_FD_RESERVE 5 53 static size_t sessions; 54 55 void 56 smtp_imsg(struct mproc *p, struct imsg *imsg) 57 { 58 if (p->proc == PROC_LKA) { 59 switch (imsg->hdr.type) { 60 case IMSG_SMTP_DNS_PTR: 61 case IMSG_SMTP_EXPAND_RCPT: 62 case IMSG_SMTP_LOOKUP_HELO: 63 case IMSG_SMTP_AUTHENTICATE: 64 case IMSG_SMTP_SSL_INIT: 65 case IMSG_SMTP_SSL_VERIFY: 66 smtp_session_imsg(p, imsg); 67 return; 68 } 69 } 70 71 if (p->proc == PROC_QUEUE) { 72 switch (imsg->hdr.type) { 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, 83 smtp_enqueue(NULL), imsg->data, 84 imsg->hdr.len - sizeof imsg->hdr); 85 return; 86 } 87 } 88 89 if (p->proc == PROC_CONTROL) { 90 switch (imsg->hdr.type) { 91 case IMSG_CTL_SMTP_SESSION: 92 m_compose(p, IMSG_CTL_SMTP_SESSION, imsg->hdr.peerid, 0, 93 smtp_enqueue(imsg->data), NULL, 0); 94 return; 95 96 case IMSG_CTL_PAUSE_SMTP: 97 log_debug("debug: smtp: pausing listening sockets"); 98 smtp_pause(); 99 env->sc_flags |= SMTPD_SMTP_PAUSED; 100 return; 101 102 case IMSG_CTL_RESUME_SMTP: 103 log_debug("debug: smtp: resuming listening sockets"); 104 env->sc_flags &= ~SMTPD_SMTP_PAUSED; 105 smtp_resume(); 106 return; 107 } 108 } 109 110 errx(1, "smtp_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); 111 } 112 113 void 114 smtp_postfork(void) 115 { 116 smtp_setup_listeners(); 117 } 118 119 void 120 smtp_postprivdrop(void) 121 { 122 } 123 124 void 125 smtp_configure(void) 126 { 127 smtp_setup_events(); 128 } 129 130 static void 131 smtp_setup_listeners(void) 132 { 133 struct listener *l; 134 int opt; 135 136 TAILQ_FOREACH(l, env->sc_listeners, entry) { 137 if ((l->fd = socket(l->ss.ss_family, SOCK_STREAM, 0)) == -1) { 138 if (errno == EAFNOSUPPORT) { 139 log_warn("smtpd: socket"); 140 continue; 141 } 142 fatal("smtpd: socket"); 143 } 144 opt = 1; 145 if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, &opt, 146 sizeof(opt)) < 0) 147 fatal("smtpd: setsockopt"); 148 if (bind(l->fd, (struct sockaddr *)&l->ss, l->ss.ss_len) == -1) 149 fatal("smtpd: bind"); 150 } 151 } 152 153 static void 154 smtp_setup_events(void) 155 { 156 struct listener *l; 157 struct pki *pki; 158 SSL_CTX *ssl_ctx; 159 void *iter; 160 const char *k; 161 162 TAILQ_FOREACH(l, env->sc_listeners, entry) { 163 log_debug("debug: smtp: listen on %s port %d flags 0x%01x" 164 " pki \"%s\"", ss_to_text(&l->ss), ntohs(l->port), 165 l->flags, l->pki_name); 166 167 session_socket_blockmode(l->fd, BM_NONBLOCK); 168 if (listen(l->fd, SMTPD_BACKLOG) == -1) 169 fatal("listen"); 170 event_set(&l->ev, l->fd, EV_READ|EV_PERSIST, smtp_accept, l); 171 172 if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) 173 event_add(&l->ev, NULL); 174 } 175 176 iter = NULL; 177 while (dict_iter(env->sc_pki_dict, &iter, &k, (void **)&pki)) { 178 if (! ssl_setup((SSL_CTX **)&ssl_ctx, pki)) 179 fatal("smtp_setup_events: ssl_setup failure"); 180 dict_xset(env->sc_ssl_dict, k, ssl_ctx); 181 } 182 183 purge_config(PURGE_PKI_KEYS); 184 185 log_debug("debug: smtp: will accept at most %d clients", 186 (getdtablesize() - getdtablecount())/2 - SMTP_FD_RESERVE); 187 } 188 189 static void 190 smtp_pause(void) 191 { 192 struct listener *l; 193 194 if (env->sc_flags & (SMTPD_SMTP_DISABLED|SMTPD_SMTP_PAUSED)) 195 return; 196 197 TAILQ_FOREACH(l, env->sc_listeners, entry) 198 event_del(&l->ev); 199 } 200 201 static void 202 smtp_resume(void) 203 { 204 struct listener *l; 205 206 if (env->sc_flags & (SMTPD_SMTP_DISABLED|SMTPD_SMTP_PAUSED)) 207 return; 208 209 TAILQ_FOREACH(l, env->sc_listeners, entry) 210 event_add(&l->ev, NULL); 211 } 212 213 static int 214 smtp_enqueue(uid_t *euid) 215 { 216 static struct listener local, *listener = NULL; 217 char buf[SMTPD_MAXHOSTNAMELEN], *hostname; 218 int fd[2]; 219 220 if (listener == NULL) { 221 listener = &local; 222 (void)strlcpy(listener->tag, "local", sizeof(listener->tag)); 223 listener->ss.ss_family = AF_LOCAL; 224 listener->ss.ss_len = sizeof(struct sockaddr *); 225 (void)strlcpy(listener->hostname, "localhost", 226 sizeof(listener->hostname)); 227 } 228 229 /* 230 * Some enqueue requests buffered in IMSG may still arrive even after 231 * call to smtp_pause() because enqueue listener is not a real socket 232 * and thus cannot be paused properly. 233 */ 234 if (env->sc_flags & SMTPD_SMTP_PAUSED) 235 return (-1); 236 237 /* XXX dont' fatal here */ 238 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd)) 239 fatal("socketpair"); 240 241 hostname = "localhost"; 242 if (euid) { 243 (void)snprintf(buf, sizeof(buf), "%d@localhost", *euid); 244 hostname = buf; 245 } 246 247 if ((smtp_session(listener, fd[0], &listener->ss, hostname)) == -1) { 248 close(fd[0]); 249 close(fd[1]); 250 return (-1); 251 } 252 253 sessions++; 254 stat_increment("smtp.session", 1); 255 stat_increment("smtp.session.local", 1); 256 257 return (fd[1]); 258 } 259 260 static void 261 smtp_accept(int fd, short event, void *p) 262 { 263 struct listener *listener = p; 264 struct sockaddr_storage ss; 265 socklen_t len; 266 int sock; 267 268 if (env->sc_flags & SMTPD_SMTP_PAUSED) 269 fatalx("smtp_session: unexpected client"); 270 271 if (! smtp_can_accept()) { 272 log_warnx("warn: Disabling incoming SMTP connections: " 273 "Client limit reached"); 274 goto pause; 275 } 276 277 len = sizeof(ss); 278 if ((sock = accept(fd, (struct sockaddr *)&ss, &len)) == -1) { 279 if (errno == ENFILE || errno == EMFILE) { 280 log_warn("warn: Disabling incoming SMTP connections"); 281 goto pause; 282 } 283 if (errno == EINTR || errno == EWOULDBLOCK || 284 errno == ECONNABORTED) 285 return; 286 fatal("smtp_accept"); 287 } 288 289 if (smtp_session(listener, sock, &ss, NULL) == -1) { 290 log_warn("warn: Failed to create SMTP session"); 291 close(sock); 292 return; 293 } 294 io_set_blocking(sock, 0); 295 296 sessions++; 297 stat_increment("smtp.session", 1); 298 if (listener->ss.ss_family == AF_LOCAL) 299 stat_increment("smtp.session.local", 1); 300 if (listener->ss.ss_family == AF_INET) 301 stat_increment("smtp.session.inet4", 1); 302 if (listener->ss.ss_family == AF_INET6) 303 stat_increment("smtp.session.inet6", 1); 304 return; 305 306 pause: 307 smtp_pause(); 308 env->sc_flags |= SMTPD_SMTP_DISABLED; 309 return; 310 } 311 312 static int 313 smtp_can_accept(void) 314 { 315 size_t max; 316 317 max = (getdtablesize() - getdtablecount()) / 2 - SMTP_FD_RESERVE; 318 319 return (sessions < max); 320 } 321 322 void 323 smtp_collect(void) 324 { 325 sessions--; 326 stat_decrement("smtp.session", 1); 327 328 if (!smtp_can_accept()) 329 return; 330 331 if (env->sc_flags & SMTPD_SMTP_DISABLED) { 332 log_warnx("warn: smtp: " 333 "fd exhaustion over, re-enabling incoming connections"); 334 env->sc_flags &= ~SMTPD_SMTP_DISABLED; 335 smtp_resume(); 336 } 337 } 338