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