xref: /openbsd/usr.sbin/smtpd/smtp.c (revision 62e04d05)
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 
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
smtp_imsg(struct mproc * p,struct imsg * imsg)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
smtp_postfork(void)108 smtp_postfork(void)
109 {
110 	smtp_setup_listeners();
111 }
112 
113 void
smtp_postprivdrop(void)114 smtp_postprivdrop(void)
115 {
116 }
117 
118 void
smtp_configure(void)119 smtp_configure(void)
120 {
121 	smtp_setup_events();
122 }
123 
124 static void
smtp_setup_listeners(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
smtp_setup_listener_tls(struct listener * l)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 		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 
229 static void
smtp_setup_events(void)230 smtp_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 
253 static void
smtp_pause(void)254 smtp_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 
265 static void
smtp_resume(void)266 smtp_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 
277 static int
smtp_enqueue(void)278 smtp_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 
307 static void
smtp_accept(int fd,short event,void * p)308 smtp_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 
349 pause:
350 	smtp_pause();
351 	env->sc_flags |= SMTPD_SMTP_DISABLED;
352 	return;
353 }
354 
355 static int
smtp_can_accept(void)356 smtp_can_accept(void)
357 {
358 	if (sessions + 1 == maxsessions)
359 		return 0;
360 	return (getdtablesize() - getdtablecount() - SMTP_FD_RESERVE >= 2);
361 }
362 
363 void
smtp_collect(void)364 smtp_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 
380 static void
smtp_accepted(struct listener * listener,int sock,const struct sockaddr_storage * ss,struct io * io)381 smtp_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 
403 static void
smtp_dropped(struct listener * listener,int sock,const struct sockaddr_storage * ss)404 smtp_dropped(struct listener *listener, int sock, const struct sockaddr_storage *ss)
405 {
406 	close(sock);
407 	sessions--;
408 }
409