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
30static void smtp_setup_events(void);
31static void smtp_pause(void);
32static void smtp_resume(void);
33static void smtp_accept(int, short, void *);
34static void smtp_dropped(struct listener *, int, const struct sockaddr_storage *);
35static int smtp_enqueue(void);
36static int smtp_can_accept(void);
37static void smtp_setup_listeners(void);
38static void smtp_setup_listener_tls(struct listener *);
39
40int
41proxy_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
48static 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 */
54void tls_config_use_fake_private_key(struct tls_config *config);
55
56#define	SMTP_FD_RESERVE	5
57static size_t	sessions;
58static size_t	maxsessions;
59
60void
61smtp_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
107void
108smtp_postfork(void)
109{
110	smtp_setup_listeners();
111}
112
113void
114smtp_postprivdrop(void)
115{
116}
117
118void
119smtp_configure(void)
120{
121	smtp_setup_events();
122}
123
124static void
125smtp_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
151static void
152smtp_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
229static void
230smtp_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
253static void
254smtp_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
265static void
266smtp_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
277static int
278smtp_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
307static void
308smtp_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
349pause:
350	smtp_pause();
351	env->sc_flags |= SMTPD_SMTP_DISABLED;
352	return;
353}
354
355static int
356smtp_can_accept(void)
357{
358	if (sessions + 1 == maxsessions)
359		return 0;
360	return (getdtablesize() - getdtablecount() - SMTP_FD_RESERVE >= 2);
361}
362
363void
364smtp_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
380static void
381smtp_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
403static void
404smtp_dropped(struct listener *listener, int sock, const struct sockaddr_storage *ss)
405{
406	close(sock);
407	sessions--;
408}
409