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