xref: /openbsd-src/usr.sbin/smtpd/smtp.c (revision 24bb5fcea3ed904bc467217bdaadb5dfc618d5bf)
1 /*	$OpenBSD: smtp.c,v 1.171 2021/06/14 17:58:16 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 <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 
30 static void smtp_setup_events(void);
31 static void smtp_pause(void);
32 static void smtp_resume(void);
33 static void smtp_accept(int, short, void *);
34 static void smtp_dropped(struct listener *, int, const struct sockaddr_storage *);
35 static int smtp_enqueue(void);
36 static int smtp_can_accept(void);
37 static void smtp_setup_listeners(void);
38 static void smtp_setup_listener_tls(struct listener *);
39 
40 int
41 proxy_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 
48 static 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  */
54 void tls_config_use_fake_private_key(struct tls_config *config);
55 
56 #define	SMTP_FD_RESERVE	5
57 static size_t	sessions;
58 static size_t	maxsessions;
59 
60 void
61 smtp_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 
107 void
108 smtp_postfork(void)
109 {
110 	smtp_setup_listeners();
111 }
112 
113 void
114 smtp_postprivdrop(void)
115 {
116 }
117 
118 void
119 smtp_configure(void)
120 {
121 	smtp_setup_events();
122 }
123 
124 static void
125 smtp_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 
151 static void
152 smtp_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 		fatal("%s", tls_config_error(config));
170 
171 	if (l->tls_protocols) {
172 		if (tls_config_parse_protocols(&protos, l->tls_protocols) == -1)
173 			fatal("failed to parse protocols \"%s\"",
174 			    l->tls_protocols);
175 		if (tls_config_set_protocols(config, protos) == -1)
176 			fatal("%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 		fatal("tls_config_set_dheparams");
185 
186 	tls_config_use_fake_private_key(config);
187 	for (i = 0; i < l->pkicount; i++) {
188 		pki = l->pki[i];
189 		if (i == 0) {
190 			if (tls_config_set_keypair_mem(config, pki->pki_cert,
191 			    pki->pki_cert_len, NULL, 0) == -1)
192 				fatal("tls_config_set_keypair_mem");
193 		} else {
194 			if (tls_config_add_keypair_mem(config, pki->pki_cert,
195 			    pki->pki_cert_len, NULL, 0) == -1)
196 				fatal("tls_config_add_keypair_mem");
197 		}
198 	}
199 	free(l->pki);
200 	l->pkicount = 0;
201 
202 	if (l->ca_name[0]) {
203 		ca = dict_get(env->sc_ca_dict, l->ca_name);
204 		if (tls_config_set_ca_mem(config, ca->ca_cert, ca->ca_cert_len)
205 		    == -1)
206 			fatal("tls_config_set_ca_mem");
207 	}
208 	else if (tls_config_set_ca_file(config, tls_default_ca_cert_file())
209 	    == -1)
210 		fatal("tls_config_set_ca_file");
211 
212 	if (l->flags & F_TLS_VERIFY)
213 		tls_config_verify_client(config);
214 
215 	l->tls = tls_server();
216 	if (l->tls == NULL)
217 		fatal("tls_server");
218 	if (tls_configure(l->tls, config) == -1) {
219 		fatal("tls_configure: %s", tls_error(l->tls));
220 	}
221 	tls_config_free(config);
222 }
223 
224 
225 static void
226 smtp_setup_events(void)
227 {
228 	struct listener *l;
229 
230 	TAILQ_FOREACH(l, env->sc_listeners, entry) {
231 		log_debug("debug: smtp: listen on %s port %d flags 0x%01x",
232 		    ss_to_text(&l->ss), ntohs(l->port), l->flags);
233 
234 		io_set_nonblocking(l->fd);
235 		if (listen(l->fd, SMTPD_BACKLOG) == -1)
236 			fatal("listen");
237 		event_set(&l->ev, l->fd, EV_READ|EV_PERSIST, smtp_accept, l);
238 
239 		if (!(env->sc_flags & SMTPD_SMTP_PAUSED))
240 			event_add(&l->ev, NULL);
241 	}
242 
243 	purge_config(PURGE_PKI_KEYS);
244 
245 	maxsessions = (getdtablesize() - getdtablecount()) / 2 - SMTP_FD_RESERVE;
246 	log_debug("debug: smtp: will accept at most %zu clients", maxsessions);
247 }
248 
249 static void
250 smtp_pause(void)
251 {
252 	struct listener *l;
253 
254 	if (env->sc_flags & (SMTPD_SMTP_DISABLED|SMTPD_SMTP_PAUSED))
255 		return;
256 
257 	TAILQ_FOREACH(l, env->sc_listeners, entry)
258 		event_del(&l->ev);
259 }
260 
261 static void
262 smtp_resume(void)
263 {
264 	struct listener *l;
265 
266 	if (env->sc_flags & (SMTPD_SMTP_DISABLED|SMTPD_SMTP_PAUSED))
267 		return;
268 
269 	TAILQ_FOREACH(l, env->sc_listeners, entry)
270 		event_add(&l->ev, NULL);
271 }
272 
273 static int
274 smtp_enqueue(void)
275 {
276 	struct listener	*listener = env->sc_sock_listener;
277 	int		 fd[2];
278 
279 	/*
280 	 * Some enqueue requests buffered in IMSG may still arrive even after
281 	 * call to smtp_pause() because enqueue listener is not a real socket
282 	 * and thus cannot be paused properly.
283 	 */
284 	if (env->sc_flags & SMTPD_SMTP_PAUSED)
285 		return (-1);
286 
287 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd))
288 		return (-1);
289 
290 	if ((smtp_session(listener, fd[0], &listener->ss, env->sc_hostname, NULL)) == -1) {
291 		close(fd[0]);
292 		close(fd[1]);
293 		return (-1);
294 	}
295 
296 	sessions++;
297 	stat_increment("smtp.session", 1);
298 	stat_increment("smtp.session.local", 1);
299 
300 	return (fd[1]);
301 }
302 
303 static void
304 smtp_accept(int fd, short event, void *p)
305 {
306 	struct listener		*listener = p;
307 	struct sockaddr_storage	 ss;
308 	socklen_t		 len;
309 	int			 sock;
310 
311 	if (env->sc_flags & SMTPD_SMTP_PAUSED)
312 		fatalx("smtp_session: unexpected client");
313 
314 	if (!smtp_can_accept()) {
315 		log_warnx("warn: Disabling incoming SMTP connections: "
316 		    "Client limit reached");
317 		goto pause;
318 	}
319 
320 	len = sizeof(ss);
321 	if ((sock = accept(fd, (struct sockaddr *)&ss, &len)) == -1) {
322 		if (errno == ENFILE || errno == EMFILE) {
323 			log_warn("warn: Disabling incoming SMTP connections");
324 			goto pause;
325 		}
326 		if (errno == EINTR || errno == EWOULDBLOCK ||
327 		    errno == ECONNABORTED)
328 			return;
329 		fatal("smtp_accept");
330 	}
331 
332 	if (listener->flags & F_PROXY) {
333 		io_set_nonblocking(sock);
334 		if (proxy_session(listener, sock, &ss,
335 			smtp_accepted, smtp_dropped) == -1) {
336 			close(sock);
337 			return;
338 		}
339 		return;
340 	}
341 
342 	smtp_accepted(listener, sock, &ss, NULL);
343 	return;
344 
345 pause:
346 	smtp_pause();
347 	env->sc_flags |= SMTPD_SMTP_DISABLED;
348 	return;
349 }
350 
351 static int
352 smtp_can_accept(void)
353 {
354 	if (sessions + 1 == maxsessions)
355 		return 0;
356 	return (getdtablesize() - getdtablecount() - SMTP_FD_RESERVE >= 2);
357 }
358 
359 void
360 smtp_collect(void)
361 {
362 	sessions--;
363 	stat_decrement("smtp.session", 1);
364 
365 	if (!smtp_can_accept())
366 		return;
367 
368 	if (env->sc_flags & SMTPD_SMTP_DISABLED) {
369 		log_warnx("warn: smtp: "
370 		    "fd exhaustion over, re-enabling incoming connections");
371 		env->sc_flags &= ~SMTPD_SMTP_DISABLED;
372 		smtp_resume();
373 	}
374 }
375 
376 static void
377 smtp_accepted(struct listener *listener, int sock, const struct sockaddr_storage *ss, struct io *io)
378 {
379 	int     ret;
380 
381 	ret = smtp_session(listener, sock, ss, NULL, io);
382 	if (ret == -1) {
383 		log_warn("warn: Failed to create SMTP session");
384 		close(sock);
385 		return;
386 	}
387 	io_set_nonblocking(sock);
388 
389 	sessions++;
390 	stat_increment("smtp.session", 1);
391 	if (listener->ss.ss_family == AF_LOCAL)
392 		stat_increment("smtp.session.local", 1);
393 	if (listener->ss.ss_family == AF_INET)
394 		stat_increment("smtp.session.inet4", 1);
395 	if (listener->ss.ss_family == AF_INET6)
396 		stat_increment("smtp.session.inet6", 1);
397 }
398 
399 static void
400 smtp_dropped(struct listener *listener, int sock, const struct sockaddr_storage *ss)
401 {
402 	close(sock);
403 	sessions--;
404 }
405