xref: /openbsd-src/usr.sbin/smtpd/smtp.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
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