GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/smtpd/smtpd/../smtp.c Lines: 0 141 0.0 %
Date: 2017-11-07 Branches: 0 95 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: smtp.c,v 1.156 2017/05/22 13:43:15 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 int smtp_enqueue(void);
50
static int smtp_can_accept(void);
51
static void smtp_setup_listeners(void);
52
static int smtp_sni_callback(SSL *, int *, void *);
53
54
#define	SMTP_FD_RESERVE	5
55
static size_t	sessions;
56
static size_t	maxsessions;
57
58
void
59
smtp_imsg(struct mproc *p, struct imsg *imsg)
60
{
61
	if (p->proc == PROC_LKA) {
62
		switch (imsg->hdr.type) {
63
		case IMSG_SMTP_DNS_PTR:
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_SMTP_TLS_INIT:
69
		case IMSG_SMTP_TLS_VERIFY:
70
			smtp_session_imsg(p, imsg);
71
			return;
72
		}
73
	}
74
75
	if (p->proc == PROC_QUEUE) {
76
		switch (imsg->hdr.type) {
77
		case IMSG_SMTP_MESSAGE_COMMIT:
78
		case IMSG_SMTP_MESSAGE_CREATE:
79
		case IMSG_SMTP_MESSAGE_OPEN:
80
		case IMSG_QUEUE_ENVELOPE_SUBMIT:
81
		case IMSG_QUEUE_ENVELOPE_COMMIT:
82
			smtp_session_imsg(p, imsg);
83
			return;
84
85
		case IMSG_QUEUE_SMTP_SESSION:
86
			m_compose(p, IMSG_QUEUE_SMTP_SESSION, 0, 0,
87
			    smtp_enqueue(), imsg->data,
88
			    imsg->hdr.len - sizeof imsg->hdr);
89
			return;
90
		}
91
	}
92
93
	if (p->proc == PROC_CONTROL) {
94
		switch (imsg->hdr.type) {
95
		case IMSG_CTL_SMTP_SESSION:
96
			m_compose(p, IMSG_CTL_SMTP_SESSION, imsg->hdr.peerid, 0,
97
			    smtp_enqueue(), NULL, 0);
98
			return;
99
100
		case IMSG_CTL_PAUSE_SMTP:
101
			log_debug("debug: smtp: pausing listening sockets");
102
			smtp_pause();
103
			env->sc_flags |= SMTPD_SMTP_PAUSED;
104
			return;
105
106
		case IMSG_CTL_RESUME_SMTP:
107
			log_debug("debug: smtp: resuming listening sockets");
108
			env->sc_flags &= ~SMTPD_SMTP_PAUSED;
109
			smtp_resume();
110
			return;
111
		}
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)) < 0)
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)) == -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
	int			 ret;
257
258
	if (env->sc_flags & SMTPD_SMTP_PAUSED)
259
		fatalx("smtp_session: unexpected client");
260
261
	if (!smtp_can_accept()) {
262
		log_warnx("warn: Disabling incoming SMTP connections: "
263
		    "Client limit reached");
264
		goto pause;
265
	}
266
267
	len = sizeof(ss);
268
	if ((sock = accept(fd, (struct sockaddr *)&ss, &len)) == -1) {
269
		if (errno == ENFILE || errno == EMFILE) {
270
			log_warn("warn: Disabling incoming SMTP connections");
271
			goto pause;
272
		}
273
		if (errno == EINTR || errno == EWOULDBLOCK ||
274
		    errno == ECONNABORTED)
275
			return;
276
		fatal("smtp_accept");
277
	}
278
279
	if (listener->filter[0])
280
		ret = smtpf_session(listener, sock, &ss, NULL);
281
	else
282
		ret = smtp_session(listener, sock, &ss, NULL);
283
284
	if (ret == -1) {
285
		log_warn("warn: Failed to create SMTP session");
286
		close(sock);
287
		return;
288
	}
289
	io_set_nonblocking(sock);
290
291
	sessions++;
292
	stat_increment("smtp.session", 1);
293
	if (listener->ss.ss_family == AF_LOCAL)
294
		stat_increment("smtp.session.local", 1);
295
	if (listener->ss.ss_family == AF_INET)
296
		stat_increment("smtp.session.inet4", 1);
297
	if (listener->ss.ss_family == AF_INET6)
298
		stat_increment("smtp.session.inet6", 1);
299
	return;
300
301
pause:
302
	smtp_pause();
303
	env->sc_flags |= SMTPD_SMTP_DISABLED;
304
	return;
305
}
306
307
static int
308
smtp_can_accept(void)
309
{
310
	if (sessions + 1 == maxsessions)
311
		return 0;
312
	return (getdtablesize() - getdtablecount() - SMTP_FD_RESERVE >= 2);
313
}
314
315
void
316
smtp_collect(void)
317
{
318
	sessions--;
319
	stat_decrement("smtp.session", 1);
320
321
	if (!smtp_can_accept())
322
		return;
323
324
	if (env->sc_flags & SMTPD_SMTP_DISABLED) {
325
		log_warnx("warn: smtp: "
326
		    "fd exhaustion over, re-enabling incoming connections");
327
		env->sc_flags &= ~SMTPD_SMTP_DISABLED;
328
		smtp_resume();
329
	}
330
}
331
332
static int
333
smtp_sni_callback(SSL *ssl, int *ad, void *arg)
334
{
335
	const char		*sn;
336
	void			*ssl_ctx;
337
338
	sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
339
	if (sn == NULL)
340
		return SSL_TLSEXT_ERR_NOACK;
341
	ssl_ctx = dict_get(env->sc_ssl_dict, sn);
342
	if (ssl_ctx == NULL)
343
		return SSL_TLSEXT_ERR_NOACK;
344
	SSL_set_SSL_CTX(ssl, ssl_ctx);
345
	return SSL_TLSEXT_ERR_OK;
346
}