1 |
|
|
/* $OpenBSD: mta_session.c,v 1.98 2017/05/24 21:27:32 gilles Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> |
5 |
|
|
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> |
6 |
|
|
* Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> |
7 |
|
|
* Copyright (c) 2012 Eric Faurot <eric@openbsd.org> |
8 |
|
|
* |
9 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
10 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
11 |
|
|
* copyright notice and this permission notice appear in all copies. |
12 |
|
|
* |
13 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
14 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
15 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
16 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
17 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
18 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
19 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
20 |
|
|
*/ |
21 |
|
|
|
22 |
|
|
#include <sys/types.h> |
23 |
|
|
#include <sys/queue.h> |
24 |
|
|
#include <sys/tree.h> |
25 |
|
|
#include <sys/socket.h> |
26 |
|
|
#include <sys/uio.h> |
27 |
|
|
|
28 |
|
|
#include <ctype.h> |
29 |
|
|
#include <err.h> |
30 |
|
|
#include <errno.h> |
31 |
|
|
#include <event.h> |
32 |
|
|
#include <imsg.h> |
33 |
|
|
#include <inttypes.h> |
34 |
|
|
#include <netdb.h> |
35 |
|
|
#include <openssl/ssl.h> |
36 |
|
|
#include <pwd.h> |
37 |
|
|
#include <resolv.h> |
38 |
|
|
#include <limits.h> |
39 |
|
|
#include <signal.h> |
40 |
|
|
#include <stdio.h> |
41 |
|
|
#include <stdlib.h> |
42 |
|
|
#include <string.h> |
43 |
|
|
#include <time.h> |
44 |
|
|
#include <unistd.h> |
45 |
|
|
|
46 |
|
|
#include "smtpd.h" |
47 |
|
|
#include "log.h" |
48 |
|
|
#include "ssl.h" |
49 |
|
|
|
50 |
|
|
#define MAX_TRYBEFOREDISABLE 10 |
51 |
|
|
|
52 |
|
|
#define MTA_HIWAT 65535 |
53 |
|
|
|
54 |
|
|
enum mta_state { |
55 |
|
|
MTA_INIT, |
56 |
|
|
MTA_BANNER, |
57 |
|
|
MTA_EHLO, |
58 |
|
|
MTA_HELO, |
59 |
|
|
MTA_LHLO, |
60 |
|
|
MTA_STARTTLS, |
61 |
|
|
MTA_AUTH, |
62 |
|
|
MTA_AUTH_PLAIN, |
63 |
|
|
MTA_AUTH_LOGIN, |
64 |
|
|
MTA_AUTH_LOGIN_USER, |
65 |
|
|
MTA_AUTH_LOGIN_PASS, |
66 |
|
|
MTA_READY, |
67 |
|
|
MTA_MAIL, |
68 |
|
|
MTA_RCPT, |
69 |
|
|
MTA_DATA, |
70 |
|
|
MTA_BODY, |
71 |
|
|
MTA_EOM, |
72 |
|
|
MTA_LMTP_EOM, |
73 |
|
|
MTA_RSET, |
74 |
|
|
MTA_QUIT, |
75 |
|
|
}; |
76 |
|
|
|
77 |
|
|
#define MTA_FORCE_ANYSSL 0x0001 |
78 |
|
|
#define MTA_FORCE_SMTPS 0x0002 |
79 |
|
|
#define MTA_FORCE_TLS 0x0004 |
80 |
|
|
#define MTA_FORCE_PLAIN 0x0008 |
81 |
|
|
#define MTA_WANT_SECURE 0x0010 |
82 |
|
|
#define MTA_USE_AUTH 0x0020 |
83 |
|
|
#define MTA_USE_CERT 0x0040 |
84 |
|
|
#define MTA_DOWNGRADE_PLAIN 0x0080 |
85 |
|
|
|
86 |
|
|
#define MTA_TLS_TRIED 0x0080 |
87 |
|
|
|
88 |
|
|
#define MTA_TLS 0x0100 |
89 |
|
|
#define MTA_VERIFIED 0x0200 |
90 |
|
|
|
91 |
|
|
#define MTA_FREE 0x0400 |
92 |
|
|
#define MTA_LMTP 0x0800 |
93 |
|
|
#define MTA_WAIT 0x1000 |
94 |
|
|
#define MTA_HANGON 0x2000 |
95 |
|
|
#define MTA_RECONN 0x4000 |
96 |
|
|
|
97 |
|
|
#define MTA_EXT_STARTTLS 0x01 |
98 |
|
|
#define MTA_EXT_PIPELINING 0x02 |
99 |
|
|
#define MTA_EXT_AUTH 0x04 |
100 |
|
|
#define MTA_EXT_AUTH_PLAIN 0x08 |
101 |
|
|
#define MTA_EXT_AUTH_LOGIN 0x10 |
102 |
|
|
|
103 |
|
|
struct mta_session { |
104 |
|
|
uint64_t id; |
105 |
|
|
struct mta_relay *relay; |
106 |
|
|
struct mta_route *route; |
107 |
|
|
char *helo; |
108 |
|
|
|
109 |
|
|
int flags; |
110 |
|
|
|
111 |
|
|
int attempt; |
112 |
|
|
int use_smtps; |
113 |
|
|
int use_starttls; |
114 |
|
|
int use_smtp_tls; |
115 |
|
|
int ready; |
116 |
|
|
|
117 |
|
|
struct event ev; |
118 |
|
|
struct io *io; |
119 |
|
|
int ext; |
120 |
|
|
|
121 |
|
|
size_t msgtried; |
122 |
|
|
size_t msgcount; |
123 |
|
|
size_t rcptcount; |
124 |
|
|
int hangon; |
125 |
|
|
|
126 |
|
|
enum mta_state state; |
127 |
|
|
struct mta_task *task; |
128 |
|
|
struct mta_envelope *currevp; |
129 |
|
|
FILE *datafp; |
130 |
|
|
|
131 |
|
|
size_t failures; |
132 |
|
|
|
133 |
|
|
char replybuf[2048]; |
134 |
|
|
}; |
135 |
|
|
|
136 |
|
|
static void mta_session_init(void); |
137 |
|
|
static void mta_start(int fd, short ev, void *arg); |
138 |
|
|
static void mta_io(struct io *, int, void *); |
139 |
|
|
static void mta_free(struct mta_session *); |
140 |
|
|
static void mta_on_ptr(void *, void *, void *); |
141 |
|
|
static void mta_on_timeout(struct runq *, void *); |
142 |
|
|
static void mta_connect(struct mta_session *); |
143 |
|
|
static void mta_enter_state(struct mta_session *, int); |
144 |
|
|
static void mta_flush_task(struct mta_session *, int, const char *, size_t, int); |
145 |
|
|
static void mta_error(struct mta_session *, const char *, ...); |
146 |
|
|
static void mta_send(struct mta_session *, char *, ...); |
147 |
|
|
static ssize_t mta_queue_data(struct mta_session *); |
148 |
|
|
static void mta_response(struct mta_session *, char *); |
149 |
|
|
static const char * mta_strstate(int); |
150 |
|
|
static void mta_start_tls(struct mta_session *); |
151 |
|
|
static int mta_verify_certificate(struct mta_session *); |
152 |
|
|
static void mta_tls_verified(struct mta_session *); |
153 |
|
|
static struct mta_session *mta_tree_pop(struct tree *, uint64_t); |
154 |
|
|
static const char * dsn_strret(enum dsn_ret); |
155 |
|
|
static const char * dsn_strnotify(uint8_t); |
156 |
|
|
|
157 |
|
|
void mta_hoststat_update(const char *, const char *); |
158 |
|
|
void mta_hoststat_reschedule(const char *); |
159 |
|
|
void mta_hoststat_cache(const char *, uint64_t); |
160 |
|
|
void mta_hoststat_uncache(const char *, uint64_t); |
161 |
|
|
|
162 |
|
|
static struct tree wait_helo; |
163 |
|
|
static struct tree wait_ptr; |
164 |
|
|
static struct tree wait_fd; |
165 |
|
|
static struct tree wait_ssl_init; |
166 |
|
|
static struct tree wait_ssl_verify; |
167 |
|
|
|
168 |
|
|
static struct runq *hangon; |
169 |
|
|
|
170 |
|
|
static void |
171 |
|
|
mta_session_init(void) |
172 |
|
|
{ |
173 |
|
|
static int init = 0; |
174 |
|
|
|
175 |
|
|
if (!init) { |
176 |
|
|
tree_init(&wait_helo); |
177 |
|
|
tree_init(&wait_ptr); |
178 |
|
|
tree_init(&wait_fd); |
179 |
|
|
tree_init(&wait_ssl_init); |
180 |
|
|
tree_init(&wait_ssl_verify); |
181 |
|
|
runq_init(&hangon, mta_on_timeout); |
182 |
|
|
init = 1; |
183 |
|
|
} |
184 |
|
|
} |
185 |
|
|
|
186 |
|
|
void |
187 |
|
|
mta_session(struct mta_relay *relay, struct mta_route *route) |
188 |
|
|
{ |
189 |
|
|
struct mta_session *s; |
190 |
|
|
struct timeval tv; |
191 |
|
|
|
192 |
|
|
mta_session_init(); |
193 |
|
|
|
194 |
|
|
s = xcalloc(1, sizeof *s, "mta_session"); |
195 |
|
|
s->id = generate_uid(); |
196 |
|
|
s->relay = relay; |
197 |
|
|
s->route = route; |
198 |
|
|
|
199 |
|
|
if (relay->flags & RELAY_SSL && relay->flags & RELAY_AUTH) |
200 |
|
|
s->flags |= MTA_USE_AUTH; |
201 |
|
|
if (relay->pki_name) |
202 |
|
|
s->flags |= MTA_USE_CERT; |
203 |
|
|
if (relay->flags & RELAY_LMTP) |
204 |
|
|
s->flags |= MTA_LMTP; |
205 |
|
|
switch (relay->flags & (RELAY_SSL|RELAY_TLS_OPTIONAL)) { |
206 |
|
|
case RELAY_SSL: |
207 |
|
|
s->flags |= MTA_FORCE_ANYSSL; |
208 |
|
|
s->flags |= MTA_WANT_SECURE; |
209 |
|
|
break; |
210 |
|
|
case RELAY_SMTPS: |
211 |
|
|
s->flags |= MTA_FORCE_SMTPS; |
212 |
|
|
s->flags |= MTA_WANT_SECURE; |
213 |
|
|
break; |
214 |
|
|
case RELAY_STARTTLS: |
215 |
|
|
s->flags |= MTA_FORCE_TLS; |
216 |
|
|
s->flags |= MTA_WANT_SECURE; |
217 |
|
|
break; |
218 |
|
|
case RELAY_TLS_OPTIONAL: |
219 |
|
|
/* do not force anything, try tls then smtp */ |
220 |
|
|
break; |
221 |
|
|
default: |
222 |
|
|
s->flags |= MTA_FORCE_PLAIN; |
223 |
|
|
} |
224 |
|
|
|
225 |
|
|
if (relay->flags & RELAY_BACKUP) |
226 |
|
|
s->flags &= ~MTA_FORCE_PLAIN; |
227 |
|
|
|
228 |
|
|
log_debug("debug: mta: %p: spawned for relay %s", s, |
229 |
|
|
mta_relay_to_text(relay)); |
230 |
|
|
stat_increment("mta.session", 1); |
231 |
|
|
|
232 |
|
|
if (route->dst->ptrname || route->dst->lastptrquery) { |
233 |
|
|
/* We want to delay the connection since to always notify |
234 |
|
|
* the relay asynchronously. |
235 |
|
|
*/ |
236 |
|
|
tv.tv_sec = 0; |
237 |
|
|
tv.tv_usec = 0; |
238 |
|
|
evtimer_set(&s->ev, mta_start, s); |
239 |
|
|
evtimer_add(&s->ev, &tv); |
240 |
|
|
} else if (waitq_wait(&route->dst->ptrname, mta_on_ptr, s)) { |
241 |
|
|
m_create(p_lka, IMSG_MTA_DNS_PTR, 0, 0, -1); |
242 |
|
|
m_add_id(p_lka, s->id); |
243 |
|
|
m_add_sockaddr(p_lka, s->route->dst->sa); |
244 |
|
|
m_close(p_lka); |
245 |
|
|
tree_xset(&wait_ptr, s->id, s); |
246 |
|
|
s->flags |= MTA_WAIT; |
247 |
|
|
} |
248 |
|
|
} |
249 |
|
|
|
250 |
|
|
void |
251 |
|
|
mta_session_imsg(struct mproc *p, struct imsg *imsg) |
252 |
|
|
{ |
253 |
|
|
struct ca_vrfy_resp_msg *resp_ca_vrfy; |
254 |
|
|
struct ca_cert_resp_msg *resp_ca_cert; |
255 |
|
|
struct mta_session *s; |
256 |
|
|
struct mta_host *h; |
257 |
|
|
struct msg m; |
258 |
|
|
uint64_t reqid; |
259 |
|
|
const char *name; |
260 |
|
|
void *ssl; |
261 |
|
|
int dnserror, status; |
262 |
|
|
|
263 |
|
|
switch (imsg->hdr.type) { |
264 |
|
|
|
265 |
|
|
case IMSG_MTA_OPEN_MESSAGE: |
266 |
|
|
m_msg(&m, imsg); |
267 |
|
|
m_get_id(&m, &reqid); |
268 |
|
|
m_end(&m); |
269 |
|
|
|
270 |
|
|
s = mta_tree_pop(&wait_fd, reqid); |
271 |
|
|
if (s == NULL) { |
272 |
|
|
if (imsg->fd != -1) |
273 |
|
|
close(imsg->fd); |
274 |
|
|
return; |
275 |
|
|
} |
276 |
|
|
|
277 |
|
|
if (imsg->fd == -1) { |
278 |
|
|
log_debug("debug: mta: failed to obtain msg fd"); |
279 |
|
|
mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, |
280 |
|
|
"Could not get message fd", 0, 0); |
281 |
|
|
mta_enter_state(s, MTA_READY); |
282 |
|
|
return; |
283 |
|
|
} |
284 |
|
|
|
285 |
|
|
s->datafp = fdopen(imsg->fd, "r"); |
286 |
|
|
if (s->datafp == NULL) |
287 |
|
|
fatal("mta: fdopen"); |
288 |
|
|
|
289 |
|
|
mta_enter_state(s, MTA_MAIL); |
290 |
|
|
return; |
291 |
|
|
|
292 |
|
|
case IMSG_MTA_DNS_PTR: |
293 |
|
|
m_msg(&m, imsg); |
294 |
|
|
m_get_id(&m, &reqid); |
295 |
|
|
m_get_int(&m, &dnserror); |
296 |
|
|
if (dnserror) |
297 |
|
|
name = NULL; |
298 |
|
|
else |
299 |
|
|
m_get_string(&m, &name); |
300 |
|
|
m_end(&m); |
301 |
|
|
s = mta_tree_pop(&wait_ptr, reqid); |
302 |
|
|
if (s == NULL) |
303 |
|
|
return; |
304 |
|
|
|
305 |
|
|
h = s->route->dst; |
306 |
|
|
h->lastptrquery = time(NULL); |
307 |
|
|
if (name) |
308 |
|
|
h->ptrname = xstrdup(name, "mta: ptr"); |
309 |
|
|
waitq_run(&h->ptrname, h->ptrname); |
310 |
|
|
return; |
311 |
|
|
|
312 |
|
|
case IMSG_MTA_TLS_INIT: |
313 |
|
|
resp_ca_cert = imsg->data; |
314 |
|
|
s = mta_tree_pop(&wait_ssl_init, resp_ca_cert->reqid); |
315 |
|
|
if (s == NULL) |
316 |
|
|
return; |
317 |
|
|
|
318 |
|
|
if (resp_ca_cert->status == CA_FAIL) { |
319 |
|
|
if (s->relay->pki_name) { |
320 |
|
|
log_info("%016"PRIx64" mta " |
321 |
|
|
"event=closing reason=ca-failure", |
322 |
|
|
s->id); |
323 |
|
|
mta_free(s); |
324 |
|
|
return; |
325 |
|
|
} |
326 |
|
|
else { |
327 |
|
|
ssl = ssl_mta_init(NULL, NULL, 0, env->sc_tls_ciphers); |
328 |
|
|
if (ssl == NULL) |
329 |
|
|
fatal("mta: ssl_mta_init"); |
330 |
|
|
io_start_tls(s->io, ssl); |
331 |
|
|
return; |
332 |
|
|
} |
333 |
|
|
} |
334 |
|
|
|
335 |
|
|
resp_ca_cert = xmemdup(imsg->data, sizeof *resp_ca_cert, "mta:ca_cert"); |
336 |
|
|
resp_ca_cert->cert = xstrdup((char *)imsg->data + |
337 |
|
|
sizeof *resp_ca_cert, "mta:ca_cert"); |
338 |
|
|
ssl = ssl_mta_init(resp_ca_cert->name, |
339 |
|
|
resp_ca_cert->cert, resp_ca_cert->cert_len, env->sc_tls_ciphers); |
340 |
|
|
if (ssl == NULL) |
341 |
|
|
fatal("mta: ssl_mta_init"); |
342 |
|
|
io_start_tls(s->io, ssl); |
343 |
|
|
|
344 |
|
|
freezero(resp_ca_cert->cert, resp_ca_cert->cert_len); |
345 |
|
|
free(resp_ca_cert); |
346 |
|
|
return; |
347 |
|
|
|
348 |
|
|
case IMSG_MTA_TLS_VERIFY: |
349 |
|
|
resp_ca_vrfy = imsg->data; |
350 |
|
|
s = mta_tree_pop(&wait_ssl_verify, resp_ca_vrfy->reqid); |
351 |
|
|
if (s == NULL) |
352 |
|
|
return; |
353 |
|
|
|
354 |
|
|
if (resp_ca_vrfy->status == CA_OK) |
355 |
|
|
s->flags |= MTA_VERIFIED; |
356 |
|
|
else if (s->relay->flags & F_TLS_VERIFY) { |
357 |
|
|
errno = 0; |
358 |
|
|
mta_error(s, "SSL certificate check failed"); |
359 |
|
|
mta_free(s); |
360 |
|
|
return; |
361 |
|
|
} |
362 |
|
|
|
363 |
|
|
mta_tls_verified(s); |
364 |
|
|
io_resume(s->io, IO_IN); |
365 |
|
|
return; |
366 |
|
|
|
367 |
|
|
case IMSG_MTA_LOOKUP_HELO: |
368 |
|
|
m_msg(&m, imsg); |
369 |
|
|
m_get_id(&m, &reqid); |
370 |
|
|
m_get_int(&m, &status); |
371 |
|
|
if (status == LKA_OK) |
372 |
|
|
m_get_string(&m, &name); |
373 |
|
|
m_end(&m); |
374 |
|
|
|
375 |
|
|
s = mta_tree_pop(&wait_helo, reqid); |
376 |
|
|
if (s == NULL) |
377 |
|
|
return; |
378 |
|
|
|
379 |
|
|
if (status == LKA_OK) { |
380 |
|
|
s->helo = xstrdup(name, "mta_session_imsg"); |
381 |
|
|
mta_connect(s); |
382 |
|
|
} else { |
383 |
|
|
mta_source_error(s->relay, s->route, |
384 |
|
|
"Failed to retrieve helo string"); |
385 |
|
|
mta_free(s); |
386 |
|
|
} |
387 |
|
|
return; |
388 |
|
|
|
389 |
|
|
default: |
390 |
|
|
errx(1, "mta_session_imsg: unexpected %s imsg", |
391 |
|
|
imsg_to_str(imsg->hdr.type)); |
392 |
|
|
} |
393 |
|
|
} |
394 |
|
|
|
395 |
|
|
static struct mta_session * |
396 |
|
|
mta_tree_pop(struct tree *wait, uint64_t reqid) |
397 |
|
|
{ |
398 |
|
|
struct mta_session *s; |
399 |
|
|
|
400 |
|
|
s = tree_xpop(wait, reqid); |
401 |
|
|
if (s->flags & MTA_FREE) { |
402 |
|
|
log_debug("debug: mta: %p: zombie session", s); |
403 |
|
|
mta_free(s); |
404 |
|
|
return (NULL); |
405 |
|
|
} |
406 |
|
|
s->flags &= ~MTA_WAIT; |
407 |
|
|
|
408 |
|
|
return (s); |
409 |
|
|
} |
410 |
|
|
|
411 |
|
|
static void |
412 |
|
|
mta_free(struct mta_session *s) |
413 |
|
|
{ |
414 |
|
|
struct mta_relay *relay; |
415 |
|
|
struct mta_route *route; |
416 |
|
|
|
417 |
|
|
log_debug("debug: mta: %p: session done", s); |
418 |
|
|
|
419 |
|
|
if (s->ready) |
420 |
|
|
s->relay->nconn_ready -= 1; |
421 |
|
|
|
422 |
|
|
if (s->flags & MTA_HANGON) { |
423 |
|
|
log_debug("debug: mta: %p: cancelling hangon timer", s); |
424 |
|
|
runq_cancel(hangon, NULL, s); |
425 |
|
|
} |
426 |
|
|
|
427 |
|
|
if (s->io) |
428 |
|
|
io_free(s->io); |
429 |
|
|
|
430 |
|
|
if (s->task) |
431 |
|
|
fatalx("current task should have been deleted already"); |
432 |
|
|
if (s->datafp) |
433 |
|
|
fclose(s->datafp); |
434 |
|
|
free(s->helo); |
435 |
|
|
|
436 |
|
|
relay = s->relay; |
437 |
|
|
route = s->route; |
438 |
|
|
free(s); |
439 |
|
|
stat_decrement("mta.session", 1); |
440 |
|
|
mta_route_collect(relay, route); |
441 |
|
|
} |
442 |
|
|
|
443 |
|
|
static void |
444 |
|
|
mta_on_timeout(struct runq *runq, void *arg) |
445 |
|
|
{ |
446 |
|
|
struct mta_session *s = arg; |
447 |
|
|
|
448 |
|
|
log_debug("mta: timeout for session hangon"); |
449 |
|
|
|
450 |
|
|
s->flags &= ~MTA_HANGON; |
451 |
|
|
s->hangon++; |
452 |
|
|
|
453 |
|
|
mta_enter_state(s, MTA_READY); |
454 |
|
|
} |
455 |
|
|
|
456 |
|
|
static void |
457 |
|
|
mta_on_ptr(void *tag, void *arg, void *data) |
458 |
|
|
{ |
459 |
|
|
struct mta_session *s = arg; |
460 |
|
|
|
461 |
|
|
mta_connect(s); |
462 |
|
|
} |
463 |
|
|
|
464 |
|
|
static void |
465 |
|
|
mta_start(int fd, short ev, void *arg) |
466 |
|
|
{ |
467 |
|
|
struct mta_session *s = arg; |
468 |
|
|
|
469 |
|
|
mta_connect(s); |
470 |
|
|
} |
471 |
|
|
|
472 |
|
|
static void |
473 |
|
|
mta_connect(struct mta_session *s) |
474 |
|
|
{ |
475 |
|
|
struct sockaddr_storage ss; |
476 |
|
|
struct sockaddr *sa; |
477 |
|
|
int portno; |
478 |
|
|
const char *schema = "smtp+tls://"; |
479 |
|
|
|
480 |
|
|
if (s->helo == NULL) { |
481 |
|
|
if (s->relay->helotable && s->route->src->sa) { |
482 |
|
|
m_create(p_lka, IMSG_MTA_LOOKUP_HELO, 0, 0, -1); |
483 |
|
|
m_add_id(p_lka, s->id); |
484 |
|
|
m_add_string(p_lka, s->relay->helotable); |
485 |
|
|
m_add_sockaddr(p_lka, s->route->src->sa); |
486 |
|
|
m_close(p_lka); |
487 |
|
|
tree_xset(&wait_helo, s->id, s); |
488 |
|
|
s->flags |= MTA_WAIT; |
489 |
|
|
return; |
490 |
|
|
} |
491 |
|
|
else if (s->relay->heloname) |
492 |
|
|
s->helo = xstrdup(s->relay->heloname, "mta_connect"); |
493 |
|
|
else |
494 |
|
|
s->helo = xstrdup(env->sc_hostname, "mta_connect"); |
495 |
|
|
} |
496 |
|
|
|
497 |
|
|
if (s->io) { |
498 |
|
|
io_free(s->io); |
499 |
|
|
s->io = NULL; |
500 |
|
|
} |
501 |
|
|
|
502 |
|
|
s->use_smtps = s->use_starttls = s->use_smtp_tls = 0; |
503 |
|
|
|
504 |
|
|
switch (s->attempt) { |
505 |
|
|
case 0: |
506 |
|
|
if (s->flags & MTA_FORCE_SMTPS) |
507 |
|
|
s->use_smtps = 1; /* smtps */ |
508 |
|
|
else if (s->flags & (MTA_FORCE_TLS|MTA_FORCE_ANYSSL)) |
509 |
|
|
s->use_starttls = 1; /* tls, tls+smtps */ |
510 |
|
|
else if (!(s->flags & MTA_FORCE_PLAIN)) |
511 |
|
|
s->use_smtp_tls = 1; |
512 |
|
|
break; |
513 |
|
|
case 1: |
514 |
|
|
if (s->flags & MTA_FORCE_ANYSSL) { |
515 |
|
|
s->use_smtps = 1; /* tls+smtps */ |
516 |
|
|
break; |
517 |
|
|
} |
518 |
|
|
else if (s->flags & MTA_DOWNGRADE_PLAIN) { |
519 |
|
|
/* smtp+tls, with tls failure */ |
520 |
|
|
break; |
521 |
|
|
} |
522 |
|
|
default: |
523 |
|
|
mta_free(s); |
524 |
|
|
return; |
525 |
|
|
} |
526 |
|
|
portno = s->use_smtps ? 465 : 25; |
527 |
|
|
|
528 |
|
|
/* Override with relay-specified port */ |
529 |
|
|
if (s->relay->port) |
530 |
|
|
portno = s->relay->port; |
531 |
|
|
|
532 |
|
|
memmove(&ss, s->route->dst->sa, s->route->dst->sa->sa_len); |
533 |
|
|
sa = (struct sockaddr *)&ss; |
534 |
|
|
|
535 |
|
|
if (sa->sa_family == AF_INET) |
536 |
|
|
((struct sockaddr_in *)sa)->sin_port = htons(portno); |
537 |
|
|
else if (sa->sa_family == AF_INET6) |
538 |
|
|
((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); |
539 |
|
|
|
540 |
|
|
s->attempt += 1; |
541 |
|
|
if (s->use_smtp_tls) |
542 |
|
|
schema = "smtp+tls://"; |
543 |
|
|
else if (s->use_starttls) |
544 |
|
|
schema = "tls://"; |
545 |
|
|
else if (s->use_smtps) |
546 |
|
|
schema = "smtps://"; |
547 |
|
|
else if (s->flags & MTA_LMTP) |
548 |
|
|
schema = "lmtp://"; |
549 |
|
|
else |
550 |
|
|
schema = "smtp://"; |
551 |
|
|
|
552 |
|
|
log_info("%016"PRIx64" mta " |
553 |
|
|
"event=connecting address=%s%s:%d host=%s", |
554 |
|
|
s->id, schema, sa_to_text(s->route->dst->sa), |
555 |
|
|
portno, s->route->dst->ptrname); |
556 |
|
|
|
557 |
|
|
mta_enter_state(s, MTA_INIT); |
558 |
|
|
s->io = io_new(); |
559 |
|
|
io_set_callback(s->io, mta_io, s); |
560 |
|
|
io_set_timeout(s->io, 300000); |
561 |
|
|
if (io_connect(s->io, sa, s->route->src->sa) == -1) { |
562 |
|
|
/* |
563 |
|
|
* This error is most likely a "no route", |
564 |
|
|
* so there is no need to try again. |
565 |
|
|
*/ |
566 |
|
|
log_debug("debug: mta: io_connect failed: %s", io_error(s->io)); |
567 |
|
|
if (errno == EADDRNOTAVAIL) |
568 |
|
|
mta_source_error(s->relay, s->route, io_error(s->io)); |
569 |
|
|
else |
570 |
|
|
mta_error(s, "Connection failed: %s", io_error(s->io)); |
571 |
|
|
mta_free(s); |
572 |
|
|
} |
573 |
|
|
} |
574 |
|
|
|
575 |
|
|
static void |
576 |
|
|
mta_enter_state(struct mta_session *s, int newstate) |
577 |
|
|
{ |
578 |
|
|
struct mta_envelope *e; |
579 |
|
|
size_t envid_sz; |
580 |
|
|
int oldstate; |
581 |
|
|
ssize_t q; |
582 |
|
|
char ibuf[LINE_MAX]; |
583 |
|
|
char obuf[LINE_MAX]; |
584 |
|
|
int offset; |
585 |
|
|
|
586 |
|
|
again: |
587 |
|
|
oldstate = s->state; |
588 |
|
|
|
589 |
|
|
log_trace(TRACE_MTA, "mta: %p: %s -> %s", s, |
590 |
|
|
mta_strstate(oldstate), |
591 |
|
|
mta_strstate(newstate)); |
592 |
|
|
|
593 |
|
|
s->state = newstate; |
594 |
|
|
|
595 |
|
|
memset(s->replybuf, 0, sizeof s->replybuf); |
596 |
|
|
|
597 |
|
|
/* don't try this at home! */ |
598 |
|
|
#define mta_enter_state(_s, _st) do { newstate = _st; goto again; } while (0) |
599 |
|
|
|
600 |
|
|
switch (s->state) { |
601 |
|
|
case MTA_INIT: |
602 |
|
|
case MTA_BANNER: |
603 |
|
|
break; |
604 |
|
|
|
605 |
|
|
case MTA_EHLO: |
606 |
|
|
s->ext = 0; |
607 |
|
|
mta_send(s, "EHLO %s", s->helo); |
608 |
|
|
break; |
609 |
|
|
|
610 |
|
|
case MTA_HELO: |
611 |
|
|
s->ext = 0; |
612 |
|
|
mta_send(s, "HELO %s", s->helo); |
613 |
|
|
break; |
614 |
|
|
|
615 |
|
|
case MTA_LHLO: |
616 |
|
|
s->ext = 0; |
617 |
|
|
mta_send(s, "LHLO %s", s->helo); |
618 |
|
|
break; |
619 |
|
|
|
620 |
|
|
case MTA_STARTTLS: |
621 |
|
|
if (s->flags & MTA_DOWNGRADE_PLAIN) |
622 |
|
|
mta_enter_state(s, MTA_AUTH); |
623 |
|
|
if (s->flags & MTA_TLS) /* already started */ |
624 |
|
|
mta_enter_state(s, MTA_AUTH); |
625 |
|
|
else if ((s->ext & MTA_EXT_STARTTLS) == 0) { |
626 |
|
|
if (s->flags & MTA_FORCE_TLS || s->flags & MTA_WANT_SECURE) { |
627 |
|
|
mta_error(s, "TLS required but not supported by remote host"); |
628 |
|
|
s->flags |= MTA_RECONN; |
629 |
|
|
} |
630 |
|
|
else |
631 |
|
|
/* server doesn't support starttls, do not use it */ |
632 |
|
|
mta_enter_state(s, MTA_AUTH); |
633 |
|
|
} |
634 |
|
|
else |
635 |
|
|
mta_send(s, "STARTTLS"); |
636 |
|
|
break; |
637 |
|
|
|
638 |
|
|
case MTA_AUTH: |
639 |
|
|
if (s->relay->secret && s->flags & MTA_TLS) { |
640 |
|
|
if (s->ext & MTA_EXT_AUTH) { |
641 |
|
|
if (s->ext & MTA_EXT_AUTH_PLAIN) { |
642 |
|
|
mta_enter_state(s, MTA_AUTH_PLAIN); |
643 |
|
|
break; |
644 |
|
|
} |
645 |
|
|
if (s->ext & MTA_EXT_AUTH_LOGIN) { |
646 |
|
|
mta_enter_state(s, MTA_AUTH_LOGIN); |
647 |
|
|
break; |
648 |
|
|
} |
649 |
|
|
log_debug("debug: mta: %p: no supported AUTH method on session", s); |
650 |
|
|
mta_error(s, "no supported AUTH method"); |
651 |
|
|
} |
652 |
|
|
else { |
653 |
|
|
log_debug("debug: mta: %p: AUTH not advertised on session", s); |
654 |
|
|
mta_error(s, "AUTH not advertised"); |
655 |
|
|
} |
656 |
|
|
} |
657 |
|
|
else if (s->relay->secret) { |
658 |
|
|
log_debug("debug: mta: %p: not using AUTH on non-TLS " |
659 |
|
|
"session", s); |
660 |
|
|
mta_error(s, "Refuse to AUTH over unsecure channel"); |
661 |
|
|
mta_connect(s); |
662 |
|
|
} else { |
663 |
|
|
mta_enter_state(s, MTA_READY); |
664 |
|
|
} |
665 |
|
|
break; |
666 |
|
|
|
667 |
|
|
case MTA_AUTH_PLAIN: |
668 |
|
|
mta_send(s, "AUTH PLAIN %s", s->relay->secret); |
669 |
|
|
break; |
670 |
|
|
|
671 |
|
|
case MTA_AUTH_LOGIN: |
672 |
|
|
mta_send(s, "AUTH LOGIN"); |
673 |
|
|
break; |
674 |
|
|
|
675 |
|
|
case MTA_AUTH_LOGIN_USER: |
676 |
|
|
memset(ibuf, 0, sizeof ibuf); |
677 |
|
|
if (base64_decode(s->relay->secret, (unsigned char *)ibuf, |
678 |
|
|
sizeof(ibuf)-1) == -1) { |
679 |
|
|
log_debug("debug: mta: %p: credentials too large on session", s); |
680 |
|
|
mta_error(s, "Credentials too large"); |
681 |
|
|
break; |
682 |
|
|
} |
683 |
|
|
|
684 |
|
|
memset(obuf, 0, sizeof obuf); |
685 |
|
|
base64_encode((unsigned char *)ibuf + 1, strlen(ibuf + 1), obuf, sizeof obuf); |
686 |
|
|
mta_send(s, "%s", obuf); |
687 |
|
|
|
688 |
|
|
memset(ibuf, 0, sizeof ibuf); |
689 |
|
|
memset(obuf, 0, sizeof obuf); |
690 |
|
|
break; |
691 |
|
|
|
692 |
|
|
case MTA_AUTH_LOGIN_PASS: |
693 |
|
|
memset(ibuf, 0, sizeof ibuf); |
694 |
|
|
if (base64_decode(s->relay->secret, (unsigned char *)ibuf, |
695 |
|
|
sizeof(ibuf)-1) == -1) { |
696 |
|
|
log_debug("debug: mta: %p: credentials too large on session", s); |
697 |
|
|
mta_error(s, "Credentials too large"); |
698 |
|
|
break; |
699 |
|
|
} |
700 |
|
|
|
701 |
|
|
offset = strlen(ibuf+1)+2; |
702 |
|
|
memset(obuf, 0, sizeof obuf); |
703 |
|
|
base64_encode((unsigned char *)ibuf + offset, strlen(ibuf + offset), obuf, sizeof obuf); |
704 |
|
|
mta_send(s, "%s", obuf); |
705 |
|
|
|
706 |
|
|
memset(ibuf, 0, sizeof ibuf); |
707 |
|
|
memset(obuf, 0, sizeof obuf); |
708 |
|
|
break; |
709 |
|
|
|
710 |
|
|
case MTA_READY: |
711 |
|
|
/* Ready to send a new mail */ |
712 |
|
|
if (s->ready == 0) { |
713 |
|
|
s->ready = 1; |
714 |
|
|
s->relay->nconn_ready += 1; |
715 |
|
|
mta_route_ok(s->relay, s->route); |
716 |
|
|
} |
717 |
|
|
|
718 |
|
|
if (s->msgtried >= MAX_TRYBEFOREDISABLE) { |
719 |
|
|
log_info("%016"PRIx64" mta event=host-rejects-all-mails", |
720 |
|
|
s->id); |
721 |
|
|
mta_route_down(s->relay, s->route); |
722 |
|
|
mta_enter_state(s, MTA_QUIT); |
723 |
|
|
break; |
724 |
|
|
} |
725 |
|
|
|
726 |
|
|
if (s->msgcount >= s->relay->limits->max_mail_per_session) { |
727 |
|
|
log_debug("debug: mta: " |
728 |
|
|
"%p: cannot send more message to relay %s", s, |
729 |
|
|
mta_relay_to_text(s->relay)); |
730 |
|
|
mta_enter_state(s, MTA_QUIT); |
731 |
|
|
break; |
732 |
|
|
} |
733 |
|
|
|
734 |
|
|
/* |
735 |
|
|
* When downgrading from opportunistic TLS, clear flag and |
736 |
|
|
* possibly reuse the same task (forbidden in other cases). |
737 |
|
|
*/ |
738 |
|
|
if (s->flags & MTA_DOWNGRADE_PLAIN) |
739 |
|
|
s->flags &= ~MTA_DOWNGRADE_PLAIN; |
740 |
|
|
else if (s->task) |
741 |
|
|
fatalx("task should be NULL at this point"); |
742 |
|
|
|
743 |
|
|
if (s->task == NULL) |
744 |
|
|
s->task = mta_route_next_task(s->relay, s->route); |
745 |
|
|
if (s->task == NULL) { |
746 |
|
|
log_debug("debug: mta: %p: no task for relay %s", |
747 |
|
|
s, mta_relay_to_text(s->relay)); |
748 |
|
|
|
749 |
|
|
if (s->relay->nconn > 1 || |
750 |
|
|
s->hangon >= s->relay->limits->sessdelay_keepalive) { |
751 |
|
|
mta_enter_state(s, MTA_QUIT); |
752 |
|
|
break; |
753 |
|
|
} |
754 |
|
|
|
755 |
|
|
log_debug("mta: debug: last connection: hanging on for %llds", |
756 |
|
|
(long long)(s->relay->limits->sessdelay_keepalive - |
757 |
|
|
s->hangon)); |
758 |
|
|
s->flags |= MTA_HANGON; |
759 |
|
|
runq_schedule(hangon, time(NULL) + 1, NULL, s); |
760 |
|
|
break; |
761 |
|
|
} |
762 |
|
|
|
763 |
|
|
log_debug("debug: mta: %p: handling next task for relay %s", s, |
764 |
|
|
mta_relay_to_text(s->relay)); |
765 |
|
|
|
766 |
|
|
stat_increment("mta.task.running", 1); |
767 |
|
|
|
768 |
|
|
m_create(p_queue, IMSG_MTA_OPEN_MESSAGE, 0, 0, -1); |
769 |
|
|
m_add_id(p_queue, s->id); |
770 |
|
|
m_add_msgid(p_queue, s->task->msgid); |
771 |
|
|
m_close(p_queue); |
772 |
|
|
|
773 |
|
|
tree_xset(&wait_fd, s->id, s); |
774 |
|
|
s->flags |= MTA_WAIT; |
775 |
|
|
break; |
776 |
|
|
|
777 |
|
|
case MTA_MAIL: |
778 |
|
|
s->currevp = TAILQ_FIRST(&s->task->envelopes); |
779 |
|
|
|
780 |
|
|
e = s->currevp; |
781 |
|
|
s->hangon = 0; |
782 |
|
|
s->msgtried++; |
783 |
|
|
envid_sz = strlen(e->dsn_envid); |
784 |
|
|
if (s->ext & MTA_EXT_DSN) { |
785 |
|
|
mta_send(s, "MAIL FROM:<%s>%s%s%s%s", |
786 |
|
|
s->task->sender, |
787 |
|
|
e->dsn_ret ? " RET=" : "", |
788 |
|
|
e->dsn_ret ? dsn_strret(e->dsn_ret) : "", |
789 |
|
|
envid_sz ? " ENVID=" : "", |
790 |
|
|
envid_sz ? e->dsn_envid : ""); |
791 |
|
|
} else |
792 |
|
|
mta_send(s, "MAIL FROM:<%s>", s->task->sender); |
793 |
|
|
break; |
794 |
|
|
|
795 |
|
|
case MTA_RCPT: |
796 |
|
|
if (s->currevp == NULL) |
797 |
|
|
s->currevp = TAILQ_FIRST(&s->task->envelopes); |
798 |
|
|
|
799 |
|
|
e = s->currevp; |
800 |
|
|
if (s->ext & MTA_EXT_DSN) { |
801 |
|
|
mta_send(s, "RCPT TO:<%s>%s%s%s%s", |
802 |
|
|
e->dest, |
803 |
|
|
e->dsn_notify ? " NOTIFY=" : "", |
804 |
|
|
e->dsn_notify ? dsn_strnotify(e->dsn_notify) : "", |
805 |
|
|
e->dsn_orcpt ? " ORCPT=" : "", |
806 |
|
|
e->dsn_orcpt ? e->dsn_orcpt : ""); |
807 |
|
|
} else |
808 |
|
|
mta_send(s, "RCPT TO:<%s>", e->dest); |
809 |
|
|
|
810 |
|
|
s->rcptcount++; |
811 |
|
|
break; |
812 |
|
|
|
813 |
|
|
case MTA_DATA: |
814 |
|
|
fseek(s->datafp, 0, SEEK_SET); |
815 |
|
|
mta_send(s, "DATA"); |
816 |
|
|
break; |
817 |
|
|
|
818 |
|
|
case MTA_BODY: |
819 |
|
|
if (s->datafp == NULL) { |
820 |
|
|
log_trace(TRACE_MTA, "mta: %p: end-of-file", s); |
821 |
|
|
mta_enter_state(s, MTA_EOM); |
822 |
|
|
break; |
823 |
|
|
} |
824 |
|
|
|
825 |
|
|
if ((q = mta_queue_data(s)) == -1) { |
826 |
|
|
s->flags |= MTA_FREE; |
827 |
|
|
break; |
828 |
|
|
} |
829 |
|
|
if (q == 0) { |
830 |
|
|
mta_enter_state(s, MTA_BODY); |
831 |
|
|
break; |
832 |
|
|
} |
833 |
|
|
|
834 |
|
|
log_trace(TRACE_MTA, "mta: %p: >>> [...%zd bytes...]", s, q); |
835 |
|
|
break; |
836 |
|
|
|
837 |
|
|
case MTA_EOM: |
838 |
|
|
mta_send(s, "."); |
839 |
|
|
break; |
840 |
|
|
|
841 |
|
|
case MTA_LMTP_EOM: |
842 |
|
|
/* LMTP reports status of each delivery, so enable read */ |
843 |
|
|
io_set_read(s->io); |
844 |
|
|
break; |
845 |
|
|
|
846 |
|
|
case MTA_RSET: |
847 |
|
|
if (s->datafp) { |
848 |
|
|
fclose(s->datafp); |
849 |
|
|
s->datafp = NULL; |
850 |
|
|
} |
851 |
|
|
mta_send(s, "RSET"); |
852 |
|
|
break; |
853 |
|
|
|
854 |
|
|
case MTA_QUIT: |
855 |
|
|
mta_send(s, "QUIT"); |
856 |
|
|
break; |
857 |
|
|
|
858 |
|
|
default: |
859 |
|
|
fatalx("mta_enter_state: unknown state"); |
860 |
|
|
} |
861 |
|
|
#undef mta_enter_state |
862 |
|
|
} |
863 |
|
|
|
864 |
|
|
/* |
865 |
|
|
* Handle a response to an SMTP command |
866 |
|
|
*/ |
867 |
|
|
static void |
868 |
|
|
mta_response(struct mta_session *s, char *line) |
869 |
|
|
{ |
870 |
|
|
struct mta_envelope *e; |
871 |
|
|
struct sockaddr_storage ss; |
872 |
|
|
struct sockaddr *sa; |
873 |
|
|
const char *domain; |
874 |
|
|
socklen_t sa_len; |
875 |
|
|
char buf[LINE_MAX]; |
876 |
|
|
int delivery; |
877 |
|
|
|
878 |
|
|
switch (s->state) { |
879 |
|
|
|
880 |
|
|
case MTA_BANNER: |
881 |
|
|
if (line[0] != '2') { |
882 |
|
|
mta_error(s, "BANNER rejected: %s", line); |
883 |
|
|
s->flags |= MTA_FREE; |
884 |
|
|
return; |
885 |
|
|
} |
886 |
|
|
if (s->flags & MTA_LMTP) |
887 |
|
|
mta_enter_state(s, MTA_LHLO); |
888 |
|
|
else |
889 |
|
|
mta_enter_state(s, MTA_EHLO); |
890 |
|
|
break; |
891 |
|
|
|
892 |
|
|
case MTA_EHLO: |
893 |
|
|
if (line[0] != '2') { |
894 |
|
|
/* rejected at ehlo state */ |
895 |
|
|
if ((s->flags & MTA_USE_AUTH) || |
896 |
|
|
(s->flags & MTA_WANT_SECURE)) { |
897 |
|
|
mta_error(s, "EHLO rejected: %s", line); |
898 |
|
|
s->flags |= MTA_FREE; |
899 |
|
|
return; |
900 |
|
|
} |
901 |
|
|
mta_enter_state(s, MTA_HELO); |
902 |
|
|
return; |
903 |
|
|
} |
904 |
|
|
if (!(s->flags & MTA_FORCE_PLAIN)) |
905 |
|
|
mta_enter_state(s, MTA_STARTTLS); |
906 |
|
|
else |
907 |
|
|
mta_enter_state(s, MTA_READY); |
908 |
|
|
break; |
909 |
|
|
|
910 |
|
|
case MTA_HELO: |
911 |
|
|
if (line[0] != '2') { |
912 |
|
|
mta_error(s, "HELO rejected: %s", line); |
913 |
|
|
s->flags |= MTA_FREE; |
914 |
|
|
return; |
915 |
|
|
} |
916 |
|
|
mta_enter_state(s, MTA_READY); |
917 |
|
|
break; |
918 |
|
|
|
919 |
|
|
case MTA_LHLO: |
920 |
|
|
if (line[0] != '2') { |
921 |
|
|
mta_error(s, "LHLO rejected: %s", line); |
922 |
|
|
s->flags |= MTA_FREE; |
923 |
|
|
return; |
924 |
|
|
} |
925 |
|
|
mta_enter_state(s, MTA_READY); |
926 |
|
|
break; |
927 |
|
|
|
928 |
|
|
case MTA_STARTTLS: |
929 |
|
|
if (line[0] != '2') { |
930 |
|
|
if (!(s->flags & MTA_WANT_SECURE)) { |
931 |
|
|
mta_enter_state(s, MTA_AUTH); |
932 |
|
|
return; |
933 |
|
|
} |
934 |
|
|
/* XXX mark that the MX doesn't support STARTTLS */ |
935 |
|
|
mta_error(s, "STARTTLS rejected: %s", line); |
936 |
|
|
s->flags |= MTA_FREE; |
937 |
|
|
return; |
938 |
|
|
} |
939 |
|
|
|
940 |
|
|
mta_start_tls(s); |
941 |
|
|
break; |
942 |
|
|
|
943 |
|
|
case MTA_AUTH_PLAIN: |
944 |
|
|
if (line[0] != '2') { |
945 |
|
|
mta_error(s, "AUTH rejected: %s", line); |
946 |
|
|
s->flags |= MTA_FREE; |
947 |
|
|
return; |
948 |
|
|
} |
949 |
|
|
mta_enter_state(s, MTA_READY); |
950 |
|
|
break; |
951 |
|
|
|
952 |
|
|
case MTA_AUTH_LOGIN: |
953 |
|
|
if (strncmp(line, "334 ", 4) != 0) { |
954 |
|
|
mta_error(s, "AUTH rejected: %s", line); |
955 |
|
|
s->flags |= MTA_FREE; |
956 |
|
|
return; |
957 |
|
|
} |
958 |
|
|
mta_enter_state(s, MTA_AUTH_LOGIN_USER); |
959 |
|
|
break; |
960 |
|
|
|
961 |
|
|
case MTA_AUTH_LOGIN_USER: |
962 |
|
|
if (strncmp(line, "334 ", 4) != 0) { |
963 |
|
|
mta_error(s, "AUTH rejected: %s", line); |
964 |
|
|
s->flags |= MTA_FREE; |
965 |
|
|
return; |
966 |
|
|
} |
967 |
|
|
mta_enter_state(s, MTA_AUTH_LOGIN_PASS); |
968 |
|
|
break; |
969 |
|
|
|
970 |
|
|
case MTA_AUTH_LOGIN_PASS: |
971 |
|
|
if (line[0] != '2') { |
972 |
|
|
mta_error(s, "AUTH rejected: %s", line); |
973 |
|
|
s->flags |= MTA_FREE; |
974 |
|
|
return; |
975 |
|
|
} |
976 |
|
|
mta_enter_state(s, MTA_READY); |
977 |
|
|
break; |
978 |
|
|
|
979 |
|
|
case MTA_MAIL: |
980 |
|
|
if (line[0] != '2') { |
981 |
|
|
if (line[0] == '5') |
982 |
|
|
delivery = IMSG_MTA_DELIVERY_PERMFAIL; |
983 |
|
|
else |
984 |
|
|
delivery = IMSG_MTA_DELIVERY_TEMPFAIL; |
985 |
|
|
mta_flush_task(s, delivery, line, 0, 0); |
986 |
|
|
mta_enter_state(s, MTA_RSET); |
987 |
|
|
return; |
988 |
|
|
} |
989 |
|
|
mta_enter_state(s, MTA_RCPT); |
990 |
|
|
break; |
991 |
|
|
|
992 |
|
|
case MTA_RCPT: |
993 |
|
|
e = s->currevp; |
994 |
|
|
|
995 |
|
|
/* remove envelope from hosttat cache if there */ |
996 |
|
|
if ((domain = strchr(e->dest, '@')) != NULL) { |
997 |
|
|
domain++; |
998 |
|
|
mta_hoststat_uncache(domain, e->id); |
999 |
|
|
} |
1000 |
|
|
|
1001 |
|
|
s->currevp = TAILQ_NEXT(s->currevp, entry); |
1002 |
|
|
if (line[0] == '2') { |
1003 |
|
|
s->failures = 0; |
1004 |
|
|
/* |
1005 |
|
|
* this host is up, reschedule envelopes that |
1006 |
|
|
* were cached for reschedule. |
1007 |
|
|
*/ |
1008 |
|
|
if (domain) |
1009 |
|
|
mta_hoststat_reschedule(domain); |
1010 |
|
|
} |
1011 |
|
|
else { |
1012 |
|
|
if (line[0] == '5') |
1013 |
|
|
delivery = IMSG_MTA_DELIVERY_PERMFAIL; |
1014 |
|
|
else |
1015 |
|
|
delivery = IMSG_MTA_DELIVERY_TEMPFAIL; |
1016 |
|
|
s->failures++; |
1017 |
|
|
|
1018 |
|
|
/* remove failed envelope from task list */ |
1019 |
|
|
TAILQ_REMOVE(&s->task->envelopes, e, entry); |
1020 |
|
|
stat_decrement("mta.envelope", 1); |
1021 |
|
|
|
1022 |
|
|
/* log right away */ |
1023 |
|
|
(void)snprintf(buf, sizeof(buf), "%s", |
1024 |
|
|
mta_host_to_text(s->route->dst)); |
1025 |
|
|
|
1026 |
|
|
e->session = s->id; |
1027 |
|
|
/* XXX */ |
1028 |
|
|
/* |
1029 |
|
|
* getsockname() can only fail with ENOBUFS here |
1030 |
|
|
* best effort, don't log source ... |
1031 |
|
|
*/ |
1032 |
|
|
sa_len = sizeof(ss); |
1033 |
|
|
sa = (struct sockaddr *)&ss; |
1034 |
|
|
if (getsockname(io_fileno(s->io), sa, &sa_len) < 0) |
1035 |
|
|
mta_delivery_log(e, NULL, buf, delivery, line); |
1036 |
|
|
else |
1037 |
|
|
mta_delivery_log(e, sa_to_text(sa), |
1038 |
|
|
buf, delivery, line); |
1039 |
|
|
|
1040 |
|
|
if (domain) |
1041 |
|
|
mta_hoststat_update(domain, e->status); |
1042 |
|
|
mta_delivery_notify(e); |
1043 |
|
|
|
1044 |
|
|
if (s->relay->limits->max_failures_per_session && |
1045 |
|
|
s->failures == s->relay->limits->max_failures_per_session) { |
1046 |
|
|
mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, |
1047 |
|
|
"Too many consecutive errors, closing connection", 0, 1); |
1048 |
|
|
mta_enter_state(s, MTA_QUIT); |
1049 |
|
|
break; |
1050 |
|
|
} |
1051 |
|
|
|
1052 |
|
|
/* |
1053 |
|
|
* if no more envelopes, flush failed queue |
1054 |
|
|
*/ |
1055 |
|
|
if (TAILQ_EMPTY(&s->task->envelopes)) { |
1056 |
|
|
mta_flush_task(s, IMSG_MTA_DELIVERY_OK, |
1057 |
|
|
"No envelope", 0, 0); |
1058 |
|
|
mta_enter_state(s, MTA_RSET); |
1059 |
|
|
break; |
1060 |
|
|
} |
1061 |
|
|
} |
1062 |
|
|
|
1063 |
|
|
if (s->currevp == NULL) |
1064 |
|
|
mta_enter_state(s, MTA_DATA); |
1065 |
|
|
else |
1066 |
|
|
mta_enter_state(s, MTA_RCPT); |
1067 |
|
|
break; |
1068 |
|
|
|
1069 |
|
|
case MTA_DATA: |
1070 |
|
|
if (line[0] == '2' || line[0] == '3') { |
1071 |
|
|
mta_enter_state(s, MTA_BODY); |
1072 |
|
|
break; |
1073 |
|
|
} |
1074 |
|
|
if (line[0] == '5') |
1075 |
|
|
delivery = IMSG_MTA_DELIVERY_PERMFAIL; |
1076 |
|
|
else |
1077 |
|
|
delivery = IMSG_MTA_DELIVERY_TEMPFAIL; |
1078 |
|
|
mta_flush_task(s, delivery, line, 0, 0); |
1079 |
|
|
mta_enter_state(s, MTA_RSET); |
1080 |
|
|
break; |
1081 |
|
|
|
1082 |
|
|
case MTA_LMTP_EOM: |
1083 |
|
|
case MTA_EOM: |
1084 |
|
|
if (line[0] == '2') { |
1085 |
|
|
delivery = IMSG_MTA_DELIVERY_OK; |
1086 |
|
|
s->msgtried = 0; |
1087 |
|
|
s->msgcount++; |
1088 |
|
|
} |
1089 |
|
|
else if (line[0] == '5') |
1090 |
|
|
delivery = IMSG_MTA_DELIVERY_PERMFAIL; |
1091 |
|
|
else |
1092 |
|
|
delivery = IMSG_MTA_DELIVERY_TEMPFAIL; |
1093 |
|
|
mta_flush_task(s, delivery, line, (s->flags & MTA_LMTP) ? 1 : 0, 0); |
1094 |
|
|
if (s->task) { |
1095 |
|
|
s->rcptcount--; |
1096 |
|
|
mta_enter_state(s, MTA_LMTP_EOM); |
1097 |
|
|
} else { |
1098 |
|
|
s->rcptcount = 0; |
1099 |
|
|
if (s->relay->limits->sessdelay_transaction) { |
1100 |
|
|
log_debug("debug: mta: waiting for %llds before next transaction", |
1101 |
|
|
(long long int)s->relay->limits->sessdelay_transaction); |
1102 |
|
|
s->hangon = s->relay->limits->sessdelay_transaction -1; |
1103 |
|
|
s->flags |= MTA_HANGON; |
1104 |
|
|
runq_schedule(hangon, time(NULL) |
1105 |
|
|
+ s->relay->limits->sessdelay_transaction, |
1106 |
|
|
NULL, s); |
1107 |
|
|
} |
1108 |
|
|
else |
1109 |
|
|
mta_enter_state(s, MTA_READY); |
1110 |
|
|
} |
1111 |
|
|
break; |
1112 |
|
|
|
1113 |
|
|
case MTA_RSET: |
1114 |
|
|
s->rcptcount = 0; |
1115 |
|
|
if (s->relay->limits->sessdelay_transaction) { |
1116 |
|
|
log_debug("debug: mta: waiting for %llds after reset", |
1117 |
|
|
(long long int)s->relay->limits->sessdelay_transaction); |
1118 |
|
|
s->hangon = s->relay->limits->sessdelay_transaction -1; |
1119 |
|
|
s->flags |= MTA_HANGON; |
1120 |
|
|
runq_schedule(hangon, time(NULL) |
1121 |
|
|
+ s->relay->limits->sessdelay_transaction, |
1122 |
|
|
NULL, s); |
1123 |
|
|
} |
1124 |
|
|
else |
1125 |
|
|
mta_enter_state(s, MTA_READY); |
1126 |
|
|
break; |
1127 |
|
|
|
1128 |
|
|
default: |
1129 |
|
|
fatalx("mta_response() bad state"); |
1130 |
|
|
} |
1131 |
|
|
} |
1132 |
|
|
|
1133 |
|
|
static void |
1134 |
|
|
mta_io(struct io *io, int evt, void *arg) |
1135 |
|
|
{ |
1136 |
|
|
struct mta_session *s = arg; |
1137 |
|
|
char *line, *msg, *p; |
1138 |
|
|
size_t len; |
1139 |
|
|
const char *error; |
1140 |
|
|
int cont; |
1141 |
|
|
|
1142 |
|
|
log_trace(TRACE_IO, "mta: %p: %s %s", s, io_strevent(evt), |
1143 |
|
|
io_strio(io)); |
1144 |
|
|
|
1145 |
|
|
switch (evt) { |
1146 |
|
|
|
1147 |
|
|
case IO_CONNECTED: |
1148 |
|
|
log_info("%016"PRIx64" mta event=connected", s->id); |
1149 |
|
|
|
1150 |
|
|
if (s->use_smtps) { |
1151 |
|
|
io_set_write(io); |
1152 |
|
|
mta_start_tls(s); |
1153 |
|
|
} |
1154 |
|
|
else { |
1155 |
|
|
mta_enter_state(s, MTA_BANNER); |
1156 |
|
|
io_set_read(io); |
1157 |
|
|
} |
1158 |
|
|
break; |
1159 |
|
|
|
1160 |
|
|
case IO_TLSREADY: |
1161 |
|
|
log_info("%016"PRIx64" mta event=starttls ciphers=%s", |
1162 |
|
|
s->id, ssl_to_text(io_ssl(s->io))); |
1163 |
|
|
s->flags |= MTA_TLS; |
1164 |
|
|
|
1165 |
|
|
if (mta_verify_certificate(s)) { |
1166 |
|
|
io_pause(s->io, IO_IN); |
1167 |
|
|
break; |
1168 |
|
|
} |
1169 |
|
|
|
1170 |
|
|
mta_tls_verified(s); |
1171 |
|
|
break; |
1172 |
|
|
|
1173 |
|
|
case IO_DATAIN: |
1174 |
|
|
nextline: |
1175 |
|
|
line = io_getline(s->io, &len); |
1176 |
|
|
if (line == NULL) { |
1177 |
|
|
if (io_datalen(s->io) >= LINE_MAX) { |
1178 |
|
|
mta_error(s, "Input too long"); |
1179 |
|
|
mta_free(s); |
1180 |
|
|
} |
1181 |
|
|
return; |
1182 |
|
|
} |
1183 |
|
|
|
1184 |
|
|
log_trace(TRACE_MTA, "mta: %p: <<< %s", s, line); |
1185 |
|
|
|
1186 |
|
|
if ((error = parse_smtp_response(line, len, &msg, &cont))) { |
1187 |
|
|
mta_error(s, "Bad response: %s", error); |
1188 |
|
|
mta_free(s); |
1189 |
|
|
return; |
1190 |
|
|
} |
1191 |
|
|
|
1192 |
|
|
/* read extensions */ |
1193 |
|
|
if (s->state == MTA_EHLO) { |
1194 |
|
|
if (strcmp(msg, "STARTTLS") == 0) |
1195 |
|
|
s->ext |= MTA_EXT_STARTTLS; |
1196 |
|
|
else if (strncmp(msg, "AUTH ", 5) == 0) { |
1197 |
|
|
s->ext |= MTA_EXT_AUTH; |
1198 |
|
|
if ((p = strstr(msg, " PLAIN")) && |
1199 |
|
|
(*(p+6) == '\0' || *(p+6) == ' ')) |
1200 |
|
|
s->ext |= MTA_EXT_AUTH_PLAIN; |
1201 |
|
|
if ((p = strstr(msg, " LOGIN")) && |
1202 |
|
|
(*(p+6) == '\0' || *(p+6) == ' ')) |
1203 |
|
|
s->ext |= MTA_EXT_AUTH_LOGIN; |
1204 |
|
|
} |
1205 |
|
|
else if (strcmp(msg, "PIPELINING") == 0) |
1206 |
|
|
s->ext |= MTA_EXT_PIPELINING; |
1207 |
|
|
else if (strcmp(msg, "DSN") == 0) |
1208 |
|
|
s->ext |= MTA_EXT_DSN; |
1209 |
|
|
} |
1210 |
|
|
|
1211 |
|
|
/* continuation reply, we parse out the repeating statuses and ESC */ |
1212 |
|
|
if (cont) { |
1213 |
|
|
if (s->replybuf[0] == '\0') |
1214 |
|
|
(void)strlcat(s->replybuf, line, sizeof s->replybuf); |
1215 |
|
|
else { |
1216 |
|
|
line = line + 4; |
1217 |
|
|
if (isdigit((int)*line) && *(line + 1) == '.' && |
1218 |
|
|
isdigit((int)*line+2) && *(line + 3) == '.' && |
1219 |
|
|
isdigit((int)*line+4) && isspace((int)*(line + 5))) |
1220 |
|
|
(void)strlcat(s->replybuf, line+5, sizeof s->replybuf); |
1221 |
|
|
else |
1222 |
|
|
(void)strlcat(s->replybuf, line, sizeof s->replybuf); |
1223 |
|
|
} |
1224 |
|
|
goto nextline; |
1225 |
|
|
} |
1226 |
|
|
|
1227 |
|
|
/* last line of a reply, check if we're on a continuation to parse out status and ESC. |
1228 |
|
|
* if we overflow reply buffer or are not on continuation, log entire last line. |
1229 |
|
|
*/ |
1230 |
|
|
if (s->replybuf[0] != '\0') { |
1231 |
|
|
p = line + 4; |
1232 |
|
|
if (isdigit((int)*p) && *(p + 1) == '.' && |
1233 |
|
|
isdigit((int)*p+2) && *(p + 3) == '.' && |
1234 |
|
|
isdigit((int)*p+4) && isspace((int)*(p + 5))) |
1235 |
|
|
p += 5; |
1236 |
|
|
if (strlcat(s->replybuf, p, sizeof s->replybuf) >= sizeof s->replybuf) |
1237 |
|
|
(void)strlcpy(s->replybuf, line, sizeof s->replybuf); |
1238 |
|
|
} |
1239 |
|
|
else |
1240 |
|
|
(void)strlcpy(s->replybuf, line, sizeof s->replybuf); |
1241 |
|
|
|
1242 |
|
|
if (s->state == MTA_QUIT) { |
1243 |
|
|
log_info("%016"PRIx64" mta event=closed reason=quit messages=%zu", |
1244 |
|
|
s->id, s->msgcount); |
1245 |
|
|
mta_free(s); |
1246 |
|
|
return; |
1247 |
|
|
} |
1248 |
|
|
io_set_write(io); |
1249 |
|
|
mta_response(s, s->replybuf); |
1250 |
|
|
if (s->flags & MTA_FREE) { |
1251 |
|
|
mta_free(s); |
1252 |
|
|
return; |
1253 |
|
|
} |
1254 |
|
|
if (s->flags & MTA_RECONN) { |
1255 |
|
|
s->flags &= ~MTA_RECONN; |
1256 |
|
|
mta_connect(s); |
1257 |
|
|
return; |
1258 |
|
|
} |
1259 |
|
|
|
1260 |
|
|
if (io_datalen(s->io)) { |
1261 |
|
|
log_debug("debug: mta: remaining data in input buffer"); |
1262 |
|
|
mta_error(s, "Remote host sent too much data"); |
1263 |
|
|
if (s->flags & MTA_WAIT) |
1264 |
|
|
s->flags |= MTA_FREE; |
1265 |
|
|
else |
1266 |
|
|
mta_free(s); |
1267 |
|
|
} |
1268 |
|
|
break; |
1269 |
|
|
|
1270 |
|
|
case IO_LOWAT: |
1271 |
|
|
if (s->state == MTA_BODY) { |
1272 |
|
|
mta_enter_state(s, MTA_BODY); |
1273 |
|
|
if (s->flags & MTA_FREE) { |
1274 |
|
|
mta_free(s); |
1275 |
|
|
return; |
1276 |
|
|
} |
1277 |
|
|
} |
1278 |
|
|
|
1279 |
|
|
if (io_queued(s->io) == 0) |
1280 |
|
|
io_set_read(io); |
1281 |
|
|
break; |
1282 |
|
|
|
1283 |
|
|
case IO_TIMEOUT: |
1284 |
|
|
log_debug("debug: mta: %p: connection timeout", s); |
1285 |
|
|
mta_error(s, "Connection timeout"); |
1286 |
|
|
if (!s->ready) |
1287 |
|
|
mta_connect(s); |
1288 |
|
|
else |
1289 |
|
|
mta_free(s); |
1290 |
|
|
break; |
1291 |
|
|
|
1292 |
|
|
case IO_ERROR: |
1293 |
|
|
log_debug("debug: mta: %p: IO error: %s", s, io_error(io)); |
1294 |
|
|
if (!s->ready) { |
1295 |
|
|
mta_error(s, "IO Error: %s", io_error(io)); |
1296 |
|
|
mta_connect(s); |
1297 |
|
|
break; |
1298 |
|
|
} |
1299 |
|
|
else if (!(s->flags & (MTA_FORCE_TLS|MTA_FORCE_SMTPS|MTA_FORCE_ANYSSL))) { |
1300 |
|
|
/* error in non-strict SSL negotiation, downgrade to plain */ |
1301 |
|
|
if (s->flags & MTA_TLS) { |
1302 |
|
|
log_info("smtp-out: Error on session %016"PRIx64 |
1303 |
|
|
": opportunistic TLS failed, " |
1304 |
|
|
"downgrading to plain", s->id); |
1305 |
|
|
s->flags &= ~MTA_TLS; |
1306 |
|
|
s->flags |= MTA_DOWNGRADE_PLAIN; |
1307 |
|
|
mta_connect(s); |
1308 |
|
|
break; |
1309 |
|
|
} |
1310 |
|
|
} |
1311 |
|
|
mta_error(s, "IO Error: %s", io_error(io)); |
1312 |
|
|
mta_free(s); |
1313 |
|
|
break; |
1314 |
|
|
|
1315 |
|
|
case IO_TLSERROR: |
1316 |
|
|
log_debug("debug: mta: %p: TLS IO error: %s", s, io_error(io)); |
1317 |
|
|
if (!(s->flags & (MTA_FORCE_TLS|MTA_FORCE_SMTPS|MTA_FORCE_ANYSSL))) { |
1318 |
|
|
/* error in non-strict SSL negotiation, downgrade to plain */ |
1319 |
|
|
log_info("smtp-out: TLS Error on session %016"PRIx64 |
1320 |
|
|
": TLS failed, " |
1321 |
|
|
"downgrading to plain", s->id); |
1322 |
|
|
s->flags &= ~MTA_TLS; |
1323 |
|
|
s->flags |= MTA_DOWNGRADE_PLAIN; |
1324 |
|
|
mta_connect(s); |
1325 |
|
|
break; |
1326 |
|
|
} |
1327 |
|
|
mta_error(s, "IO Error: %s", io_error(io)); |
1328 |
|
|
mta_free(s); |
1329 |
|
|
break; |
1330 |
|
|
|
1331 |
|
|
case IO_DISCONNECTED: |
1332 |
|
|
log_debug("debug: mta: %p: disconnected in state %s", |
1333 |
|
|
s, mta_strstate(s->state)); |
1334 |
|
|
mta_error(s, "Connection closed unexpectedly"); |
1335 |
|
|
if (!s->ready) |
1336 |
|
|
mta_connect(s); |
1337 |
|
|
else |
1338 |
|
|
mta_free(s); |
1339 |
|
|
break; |
1340 |
|
|
|
1341 |
|
|
default: |
1342 |
|
|
fatalx("mta_io() bad event"); |
1343 |
|
|
} |
1344 |
|
|
} |
1345 |
|
|
|
1346 |
|
|
static void |
1347 |
|
|
mta_send(struct mta_session *s, char *fmt, ...) |
1348 |
|
|
{ |
1349 |
|
|
va_list ap; |
1350 |
|
|
char *p; |
1351 |
|
|
int len; |
1352 |
|
|
|
1353 |
|
|
va_start(ap, fmt); |
1354 |
|
|
if ((len = vasprintf(&p, fmt, ap)) == -1) |
1355 |
|
|
fatal("mta: vasprintf"); |
1356 |
|
|
va_end(ap); |
1357 |
|
|
|
1358 |
|
|
log_trace(TRACE_MTA, "mta: %p: >>> %s", s, p); |
1359 |
|
|
|
1360 |
|
|
io_xprintf(s->io, "%s\r\n", p); |
1361 |
|
|
|
1362 |
|
|
free(p); |
1363 |
|
|
} |
1364 |
|
|
|
1365 |
|
|
/* |
1366 |
|
|
* Queue some data into the input buffer |
1367 |
|
|
*/ |
1368 |
|
|
static ssize_t |
1369 |
|
|
mta_queue_data(struct mta_session *s) |
1370 |
|
|
{ |
1371 |
|
|
char *ln = NULL; |
1372 |
|
|
size_t sz = 0, q; |
1373 |
|
|
ssize_t len; |
1374 |
|
|
|
1375 |
|
|
q = io_queued(s->io); |
1376 |
|
|
|
1377 |
|
|
while (io_queued(s->io) < MTA_HIWAT) { |
1378 |
|
|
if ((len = getline(&ln, &sz, s->datafp)) == -1) |
1379 |
|
|
break; |
1380 |
|
|
if (ln[len - 1] == '\n') |
1381 |
|
|
ln[len - 1] = '\0'; |
1382 |
|
|
io_xprintf(s->io, "%s%s\r\n", *ln == '.' ? "." : "", ln); |
1383 |
|
|
} |
1384 |
|
|
|
1385 |
|
|
free(ln); |
1386 |
|
|
if (ferror(s->datafp)) { |
1387 |
|
|
mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, |
1388 |
|
|
"Error reading content file", 0, 0); |
1389 |
|
|
return (-1); |
1390 |
|
|
} |
1391 |
|
|
|
1392 |
|
|
if (feof(s->datafp)) { |
1393 |
|
|
fclose(s->datafp); |
1394 |
|
|
s->datafp = NULL; |
1395 |
|
|
} |
1396 |
|
|
|
1397 |
|
|
return (io_queued(s->io) - q); |
1398 |
|
|
} |
1399 |
|
|
|
1400 |
|
|
static void |
1401 |
|
|
mta_flush_task(struct mta_session *s, int delivery, const char *error, size_t count, |
1402 |
|
|
int cache) |
1403 |
|
|
{ |
1404 |
|
|
struct mta_envelope *e; |
1405 |
|
|
char relay[LINE_MAX]; |
1406 |
|
|
size_t n; |
1407 |
|
|
struct sockaddr_storage ss; |
1408 |
|
|
struct sockaddr *sa; |
1409 |
|
|
socklen_t sa_len; |
1410 |
|
|
const char *domain; |
1411 |
|
|
|
1412 |
|
|
(void)snprintf(relay, sizeof relay, "%s", mta_host_to_text(s->route->dst)); |
1413 |
|
|
n = 0; |
1414 |
|
|
while ((e = TAILQ_FIRST(&s->task->envelopes))) { |
1415 |
|
|
|
1416 |
|
|
if (count && n == count) { |
1417 |
|
|
stat_decrement("mta.envelope", n); |
1418 |
|
|
return; |
1419 |
|
|
} |
1420 |
|
|
|
1421 |
|
|
TAILQ_REMOVE(&s->task->envelopes, e, entry); |
1422 |
|
|
|
1423 |
|
|
/* we're about to log, associate session to envelope */ |
1424 |
|
|
e->session = s->id; |
1425 |
|
|
e->ext = s->ext; |
1426 |
|
|
|
1427 |
|
|
/* XXX */ |
1428 |
|
|
/* |
1429 |
|
|
* getsockname() can only fail with ENOBUFS here |
1430 |
|
|
* best effort, don't log source ... |
1431 |
|
|
*/ |
1432 |
|
|
sa = (struct sockaddr *)&ss; |
1433 |
|
|
sa_len = sizeof(ss); |
1434 |
|
|
if (getsockname(io_fileno(s->io), sa, &sa_len) < 0) |
1435 |
|
|
mta_delivery_log(e, NULL, relay, delivery, error); |
1436 |
|
|
else |
1437 |
|
|
mta_delivery_log(e, sa_to_text(sa), |
1438 |
|
|
relay, delivery, error); |
1439 |
|
|
|
1440 |
|
|
mta_delivery_notify(e); |
1441 |
|
|
|
1442 |
|
|
domain = strchr(e->dest, '@'); |
1443 |
|
|
if (domain) { |
1444 |
|
|
domain++; |
1445 |
|
|
mta_hoststat_update(domain, error); |
1446 |
|
|
if (cache) |
1447 |
|
|
mta_hoststat_cache(domain, e->id); |
1448 |
|
|
} |
1449 |
|
|
|
1450 |
|
|
n++; |
1451 |
|
|
} |
1452 |
|
|
|
1453 |
|
|
free(s->task->sender); |
1454 |
|
|
free(s->task); |
1455 |
|
|
s->task = NULL; |
1456 |
|
|
|
1457 |
|
|
if (s->datafp) { |
1458 |
|
|
fclose(s->datafp); |
1459 |
|
|
s->datafp = NULL; |
1460 |
|
|
} |
1461 |
|
|
|
1462 |
|
|
stat_decrement("mta.envelope", n); |
1463 |
|
|
stat_decrement("mta.task.running", 1); |
1464 |
|
|
stat_decrement("mta.task", 1); |
1465 |
|
|
} |
1466 |
|
|
|
1467 |
|
|
static void |
1468 |
|
|
mta_error(struct mta_session *s, const char *fmt, ...) |
1469 |
|
|
{ |
1470 |
|
|
va_list ap; |
1471 |
|
|
char *error; |
1472 |
|
|
int len; |
1473 |
|
|
|
1474 |
|
|
va_start(ap, fmt); |
1475 |
|
|
if ((len = vasprintf(&error, fmt, ap)) == -1) |
1476 |
|
|
fatal("mta: vasprintf"); |
1477 |
|
|
va_end(ap); |
1478 |
|
|
|
1479 |
|
|
if (s->msgcount) |
1480 |
|
|
log_info("smtp-out: Error on session %016"PRIx64 |
1481 |
|
|
" after %zu message%s sent: %s", s->id, s->msgcount, |
1482 |
|
|
(s->msgcount > 1) ? "s" : "", error); |
1483 |
|
|
else |
1484 |
|
|
log_info("%016"PRIx64" mta event=error reason=%s", |
1485 |
|
|
s->id, error); |
1486 |
|
|
|
1487 |
|
|
/* |
1488 |
|
|
* If not connected yet, and the error is not local, just ignore it |
1489 |
|
|
* and try to reconnect. |
1490 |
|
|
*/ |
1491 |
|
|
if (s->state == MTA_INIT && |
1492 |
|
|
(errno == ETIMEDOUT || errno == ECONNREFUSED)) { |
1493 |
|
|
log_debug("debug: mta: not reporting route error yet"); |
1494 |
|
|
free(error); |
1495 |
|
|
return; |
1496 |
|
|
} |
1497 |
|
|
|
1498 |
|
|
mta_route_error(s->relay, s->route); |
1499 |
|
|
|
1500 |
|
|
if (s->task) |
1501 |
|
|
mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, error, 0, 0); |
1502 |
|
|
|
1503 |
|
|
free(error); |
1504 |
|
|
} |
1505 |
|
|
|
1506 |
|
|
static void |
1507 |
|
|
mta_start_tls(struct mta_session *s) |
1508 |
|
|
{ |
1509 |
|
|
struct ca_cert_req_msg req_ca_cert; |
1510 |
|
|
const char *certname; |
1511 |
|
|
|
1512 |
|
|
if (s->relay->pki_name) { |
1513 |
|
|
certname = s->relay->pki_name; |
1514 |
|
|
req_ca_cert.fallback = 0; |
1515 |
|
|
} |
1516 |
|
|
else { |
1517 |
|
|
certname = s->helo; |
1518 |
|
|
req_ca_cert.fallback = 1; |
1519 |
|
|
} |
1520 |
|
|
|
1521 |
|
|
req_ca_cert.reqid = s->id; |
1522 |
|
|
(void)strlcpy(req_ca_cert.name, certname, sizeof req_ca_cert.name); |
1523 |
|
|
m_compose(p_lka, IMSG_MTA_TLS_INIT, 0, 0, -1, |
1524 |
|
|
&req_ca_cert, sizeof(req_ca_cert)); |
1525 |
|
|
tree_xset(&wait_ssl_init, s->id, s); |
1526 |
|
|
s->flags |= MTA_WAIT; |
1527 |
|
|
return; |
1528 |
|
|
} |
1529 |
|
|
|
1530 |
|
|
static int |
1531 |
|
|
mta_verify_certificate(struct mta_session *s) |
1532 |
|
|
{ |
1533 |
|
|
#define MAX_CERTS 16 |
1534 |
|
|
#define MAX_CERT_LEN (MAX_IMSGSIZE - (IMSG_HEADER_SIZE + sizeof(req_ca_vrfy))) |
1535 |
|
|
struct ca_vrfy_req_msg req_ca_vrfy; |
1536 |
|
|
struct iovec iov[2]; |
1537 |
|
|
X509 *x; |
1538 |
|
|
STACK_OF(X509) *xchain; |
1539 |
|
|
const char *name; |
1540 |
|
|
unsigned char *cert_der[MAX_CERTS]; |
1541 |
|
|
int cert_len[MAX_CERTS]; |
1542 |
|
|
int i, cert_count, res; |
1543 |
|
|
|
1544 |
|
|
res = 0; |
1545 |
|
|
memset(cert_der, 0, sizeof(cert_der)); |
1546 |
|
|
memset(&req_ca_vrfy, 0, sizeof req_ca_vrfy); |
1547 |
|
|
|
1548 |
|
|
/* Send the client certificate */ |
1549 |
|
|
if (s->relay->ca_name) { |
1550 |
|
|
name = s->relay->ca_name; |
1551 |
|
|
req_ca_vrfy.fallback = 0; |
1552 |
|
|
} |
1553 |
|
|
else { |
1554 |
|
|
name = s->helo; |
1555 |
|
|
req_ca_vrfy.fallback = 1; |
1556 |
|
|
} |
1557 |
|
|
if (strlcpy(req_ca_vrfy.name, name, sizeof req_ca_vrfy.name) |
1558 |
|
|
>= sizeof req_ca_vrfy.name) |
1559 |
|
|
return 0; |
1560 |
|
|
|
1561 |
|
|
x = SSL_get_peer_certificate(io_ssl(s->io)); |
1562 |
|
|
if (x == NULL) |
1563 |
|
|
return 0; |
1564 |
|
|
xchain = SSL_get_peer_cert_chain(io_ssl(s->io)); |
1565 |
|
|
|
1566 |
|
|
/* |
1567 |
|
|
* Client provided a certificate and possibly a certificate chain. |
1568 |
|
|
* SMTP can't verify because it does not have the information that |
1569 |
|
|
* it needs, instead it will pass the certificate and chain to the |
1570 |
|
|
* lookup process and wait for a reply. |
1571 |
|
|
* |
1572 |
|
|
*/ |
1573 |
|
|
|
1574 |
|
|
cert_len[0] = i2d_X509(x, &cert_der[0]); |
1575 |
|
|
X509_free(x); |
1576 |
|
|
|
1577 |
|
|
if (cert_len[0] < 0) { |
1578 |
|
|
log_warnx("warn: failed to encode certificate"); |
1579 |
|
|
goto end; |
1580 |
|
|
} |
1581 |
|
|
log_debug("debug: certificate 0: len=%d", cert_len[0]); |
1582 |
|
|
if (cert_len[0] > (int)MAX_CERT_LEN) { |
1583 |
|
|
log_warnx("warn: certificate too long"); |
1584 |
|
|
goto end; |
1585 |
|
|
} |
1586 |
|
|
|
1587 |
|
|
if (xchain) { |
1588 |
|
|
cert_count = sk_X509_num(xchain); |
1589 |
|
|
log_debug("debug: certificate chain len: %d", cert_count); |
1590 |
|
|
if (cert_count >= MAX_CERTS) { |
1591 |
|
|
log_warnx("warn: certificate chain too long"); |
1592 |
|
|
goto end; |
1593 |
|
|
} |
1594 |
|
|
} |
1595 |
|
|
else |
1596 |
|
|
cert_count = 0; |
1597 |
|
|
|
1598 |
|
|
for (i = 0; i < cert_count; ++i) { |
1599 |
|
|
x = sk_X509_value(xchain, i); |
1600 |
|
|
cert_len[i+1] = i2d_X509(x, &cert_der[i+1]); |
1601 |
|
|
if (cert_len[i+1] < 0) { |
1602 |
|
|
log_warnx("warn: failed to encode certificate"); |
1603 |
|
|
goto end; |
1604 |
|
|
} |
1605 |
|
|
log_debug("debug: certificate %i: len=%d", i+1, cert_len[i+1]); |
1606 |
|
|
if (cert_len[i+1] > (int)MAX_CERT_LEN) { |
1607 |
|
|
log_warnx("warn: certificate too long"); |
1608 |
|
|
goto end; |
1609 |
|
|
} |
1610 |
|
|
} |
1611 |
|
|
|
1612 |
|
|
tree_xset(&wait_ssl_verify, s->id, s); |
1613 |
|
|
s->flags |= MTA_WAIT; |
1614 |
|
|
|
1615 |
|
|
/* Send the client certificate */ |
1616 |
|
|
req_ca_vrfy.reqid = s->id; |
1617 |
|
|
req_ca_vrfy.cert_len = cert_len[0]; |
1618 |
|
|
req_ca_vrfy.n_chain = cert_count; |
1619 |
|
|
iov[0].iov_base = &req_ca_vrfy; |
1620 |
|
|
iov[0].iov_len = sizeof(req_ca_vrfy); |
1621 |
|
|
iov[1].iov_base = cert_der[0]; |
1622 |
|
|
iov[1].iov_len = cert_len[0]; |
1623 |
|
|
m_composev(p_lka, IMSG_MTA_TLS_VERIFY_CERT, 0, 0, -1, |
1624 |
|
|
iov, nitems(iov)); |
1625 |
|
|
|
1626 |
|
|
memset(&req_ca_vrfy, 0, sizeof req_ca_vrfy); |
1627 |
|
|
req_ca_vrfy.reqid = s->id; |
1628 |
|
|
|
1629 |
|
|
/* Send the chain, one cert at a time */ |
1630 |
|
|
for (i = 0; i < cert_count; ++i) { |
1631 |
|
|
req_ca_vrfy.cert_len = cert_len[i+1]; |
1632 |
|
|
iov[1].iov_base = cert_der[i+1]; |
1633 |
|
|
iov[1].iov_len = cert_len[i+1]; |
1634 |
|
|
m_composev(p_lka, IMSG_MTA_TLS_VERIFY_CHAIN, 0, 0, -1, |
1635 |
|
|
iov, nitems(iov)); |
1636 |
|
|
} |
1637 |
|
|
|
1638 |
|
|
/* Tell lookup process that it can start verifying, we're done */ |
1639 |
|
|
memset(&req_ca_vrfy, 0, sizeof req_ca_vrfy); |
1640 |
|
|
req_ca_vrfy.reqid = s->id; |
1641 |
|
|
m_compose(p_lka, IMSG_MTA_TLS_VERIFY, 0, 0, -1, |
1642 |
|
|
&req_ca_vrfy, sizeof req_ca_vrfy); |
1643 |
|
|
|
1644 |
|
|
res = 1; |
1645 |
|
|
|
1646 |
|
|
end: |
1647 |
|
|
for (i = 0; i < MAX_CERTS; ++i) |
1648 |
|
|
free(cert_der[i]); |
1649 |
|
|
|
1650 |
|
|
return res; |
1651 |
|
|
} |
1652 |
|
|
|
1653 |
|
|
static void |
1654 |
|
|
mta_tls_verified(struct mta_session *s) |
1655 |
|
|
{ |
1656 |
|
|
X509 *x; |
1657 |
|
|
|
1658 |
|
|
x = SSL_get_peer_certificate(io_ssl(s->io)); |
1659 |
|
|
if (x) { |
1660 |
|
|
log_info("smtp-out: Server certificate verification %s " |
1661 |
|
|
"on session %016"PRIx64, |
1662 |
|
|
(s->flags & MTA_VERIFIED) ? "succeeded" : "failed", |
1663 |
|
|
s->id); |
1664 |
|
|
X509_free(x); |
1665 |
|
|
} |
1666 |
|
|
|
1667 |
|
|
if (s->use_smtps) { |
1668 |
|
|
mta_enter_state(s, MTA_BANNER); |
1669 |
|
|
io_set_read(s->io); |
1670 |
|
|
} |
1671 |
|
|
else |
1672 |
|
|
mta_enter_state(s, MTA_EHLO); |
1673 |
|
|
} |
1674 |
|
|
|
1675 |
|
|
static const char * |
1676 |
|
|
dsn_strret(enum dsn_ret ret) |
1677 |
|
|
{ |
1678 |
|
|
if (ret == DSN_RETHDRS) |
1679 |
|
|
return "HDRS"; |
1680 |
|
|
else if (ret == DSN_RETFULL) |
1681 |
|
|
return "FULL"; |
1682 |
|
|
else { |
1683 |
|
|
log_debug("mta: invalid ret %d", ret); |
1684 |
|
|
return "???"; |
1685 |
|
|
} |
1686 |
|
|
} |
1687 |
|
|
|
1688 |
|
|
static const char * |
1689 |
|
|
dsn_strnotify(uint8_t arg) |
1690 |
|
|
{ |
1691 |
|
|
static char buf[32]; |
1692 |
|
|
size_t sz; |
1693 |
|
|
|
1694 |
|
|
buf[0] = '\0'; |
1695 |
|
|
if (arg & DSN_SUCCESS) |
1696 |
|
|
(void)strlcat(buf, "SUCCESS,", sizeof(buf)); |
1697 |
|
|
|
1698 |
|
|
if (arg & DSN_FAILURE) |
1699 |
|
|
(void)strlcat(buf, "FAILURE,", sizeof(buf)); |
1700 |
|
|
|
1701 |
|
|
if (arg & DSN_DELAY) |
1702 |
|
|
(void)strlcat(buf, "DELAY,", sizeof(buf)); |
1703 |
|
|
|
1704 |
|
|
if (arg & DSN_NEVER) |
1705 |
|
|
(void)strlcat(buf, "NEVER,", sizeof(buf)); |
1706 |
|
|
|
1707 |
|
|
/* trim trailing comma */ |
1708 |
|
|
sz = strlen(buf); |
1709 |
|
|
if (sz) |
1710 |
|
|
buf[sz - 1] = '\0'; |
1711 |
|
|
|
1712 |
|
|
return (buf); |
1713 |
|
|
} |
1714 |
|
|
|
1715 |
|
|
#define CASE(x) case x : return #x |
1716 |
|
|
|
1717 |
|
|
static const char * |
1718 |
|
|
mta_strstate(int state) |
1719 |
|
|
{ |
1720 |
|
|
switch (state) { |
1721 |
|
|
CASE(MTA_INIT); |
1722 |
|
|
CASE(MTA_BANNER); |
1723 |
|
|
CASE(MTA_EHLO); |
1724 |
|
|
CASE(MTA_HELO); |
1725 |
|
|
CASE(MTA_STARTTLS); |
1726 |
|
|
CASE(MTA_AUTH); |
1727 |
|
|
CASE(MTA_AUTH_PLAIN); |
1728 |
|
|
CASE(MTA_AUTH_LOGIN); |
1729 |
|
|
CASE(MTA_AUTH_LOGIN_USER); |
1730 |
|
|
CASE(MTA_AUTH_LOGIN_PASS); |
1731 |
|
|
CASE(MTA_READY); |
1732 |
|
|
CASE(MTA_MAIL); |
1733 |
|
|
CASE(MTA_RCPT); |
1734 |
|
|
CASE(MTA_DATA); |
1735 |
|
|
CASE(MTA_BODY); |
1736 |
|
|
CASE(MTA_EOM); |
1737 |
|
|
CASE(MTA_LMTP_EOM); |
1738 |
|
|
CASE(MTA_RSET); |
1739 |
|
|
CASE(MTA_QUIT); |
1740 |
|
|
default: |
1741 |
|
|
return "MTA_???"; |
1742 |
|
|
} |
1743 |
|
|
} |