1 /* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "ioloop.h"
5 #include "array.h"
6 #include "str.h"
7 #include "istream.h"
8 #include "ostream.h"
9 #include "iostream-temp.h"
10 #include "iostream-ssl.h"
11 #include "master-service.h"
12 #include "program-client.h"
13 #include "smtp-client.h"
14 #include "smtp-client-connection.h"
15 #include "smtp-client-transaction.h"
16 #include "smtp-submit.h"
17 
18 #include <unistd.h>
19 #include <sys/wait.h>
20 #include <sysexits.h>
21 #include <signal.h>
22 
23 #define DEFAULT_SUBMISSION_PORT 25
24 
25 static struct event_category event_category_smtp_submit = {
26 	.name = "smtp-submit"
27 };
28 
29 struct smtp_submit_session {
30 	pool_t pool;
31 	struct smtp_submit_settings set;
32 	struct ssl_iostream_settings ssl_set;
33 	struct event *event;
34 	bool allow_root:1;
35 };
36 
37 struct smtp_submit {
38 	pool_t pool;
39 
40 	struct smtp_submit_session *session;
41 	struct event *event;
42 
43 	struct ostream *output;
44 	struct istream *input;
45 
46 	struct smtp_address *mail_from;
47 	ARRAY_TYPE(smtp_address) rcpt_to;
48 
49 	struct timeout *to_error;
50 	int status;
51 	const char *error;
52 
53 	struct program_client *prg_client;
54 	struct smtp_client *smtp_client;
55 	struct smtp_client_transaction *smtp_trans;
56 
57 	smtp_submit_callback_t *callback;
58 	void *context;
59 
60 	bool simple:1;
61 };
62 
63 struct smtp_submit_session *
smtp_submit_session_init(const struct smtp_submit_input * input,const struct smtp_submit_settings * set)64 smtp_submit_session_init(const struct smtp_submit_input *input,
65 			 const struct smtp_submit_settings *set)
66 {
67 	struct smtp_submit_session *session;
68 	pool_t pool;
69 
70 	pool = pool_alloconly_create("smtp submit session", 128);
71 	session = p_new(pool, struct smtp_submit_session, 1);
72 	session->pool = pool;
73 
74 	session->set = *set;
75 	session->set.hostname =
76 		p_strdup_empty(pool, set->hostname);
77 	session->set.submission_host =
78 		p_strdup_empty(pool, set->submission_host);
79 	session->set.sendmail_path =
80 		p_strdup_empty(pool, set->sendmail_path);
81 	session->set.submission_ssl =
82 		p_strdup_empty(pool, set->submission_ssl);
83 
84 	if (input->ssl != NULL) {
85 		ssl_iostream_settings_init_from(pool, &session->ssl_set,
86 						input->ssl);
87 	}
88 	session->allow_root = input->allow_root;
89 
90 	session->event = event_create(input->event_parent);
91 	event_add_category(session->event, &event_category_smtp_submit);
92 
93 	return session;
94 }
95 
smtp_submit_session_deinit(struct smtp_submit_session ** _session)96 void smtp_submit_session_deinit(struct smtp_submit_session **_session)
97 {
98 	struct smtp_submit_session *session = *_session;
99 
100 	*_session = NULL;
101 
102 	event_unref(&session->event);
103 	pool_unref(&session->pool);
104 }
105 
106 struct smtp_submit *
smtp_submit_init(struct smtp_submit_session * session,const struct smtp_address * mail_from)107 smtp_submit_init(struct smtp_submit_session *session,
108 		 const struct smtp_address *mail_from)
109 {
110 	struct smtp_submit *subm;
111 	pool_t pool;
112 
113 	pool = pool_alloconly_create("smtp submit", 256);
114 	subm = p_new(pool, struct smtp_submit, 1);
115 	subm->session = session;
116 	subm->pool = pool;
117 
118 	subm->mail_from = smtp_address_clone(pool, mail_from);;
119 	p_array_init(&subm->rcpt_to, pool, 2);
120 
121 	subm->event = event_create(session->event);
122 	event_add_str(subm->event, "mail_from",
123 		      smtp_address_encode(subm->mail_from));
124 
125 	return subm;
126 }
127 
128 struct smtp_submit *
smtp_submit_init_simple(const struct smtp_submit_input * input,const struct smtp_submit_settings * set,const struct smtp_address * mail_from)129 smtp_submit_init_simple(const struct smtp_submit_input *input,
130 			const struct smtp_submit_settings *set,
131 			const struct smtp_address *mail_from)
132 {
133 	struct smtp_submit_session *session;
134 	struct smtp_submit *subm;
135 
136 	session = smtp_submit_session_init(input, set);
137 	subm = smtp_submit_init(session, mail_from);
138 	subm->simple = TRUE;
139 	return subm;
140 }
141 
smtp_submit_deinit(struct smtp_submit ** _subm)142 void smtp_submit_deinit(struct smtp_submit **_subm)
143 {
144 	struct smtp_submit *subm = *_subm;
145 
146 	*_subm = NULL;
147 
148 	if (subm->output != NULL)
149 		o_stream_destroy(&subm->output);
150 	if (subm->input != NULL)
151 		i_stream_destroy(&subm->input);
152 
153 	if (subm->prg_client != NULL)
154 		program_client_destroy(&subm->prg_client);
155 	if (subm->smtp_trans != NULL)
156 		smtp_client_transaction_destroy(&subm->smtp_trans);
157 	if (subm->smtp_client != NULL)
158 		smtp_client_deinit(&subm->smtp_client);
159 
160 	timeout_remove(&subm->to_error);
161 
162 	if (subm->simple)
163 		 smtp_submit_session_deinit(&subm->session);
164 	event_unref(&subm->event);
165 	pool_unref(&subm->pool);
166 }
167 
smtp_submit_add_rcpt(struct smtp_submit * subm,const struct smtp_address * rcpt_to)168 void smtp_submit_add_rcpt(struct smtp_submit *subm,
169 			  const struct smtp_address *rcpt_to)
170 {
171 	struct smtp_address *rcpt;
172 
173 	i_assert(subm->output == NULL);
174 	i_assert(!smtp_address_isnull(rcpt_to));
175 
176 	rcpt = smtp_address_clone(subm->pool, rcpt_to);
177 	array_push_back(&subm->rcpt_to, &rcpt);
178 }
179 
smtp_submit_send(struct smtp_submit * subm)180 struct ostream *smtp_submit_send(struct smtp_submit *subm)
181 {
182 	i_assert(subm->output == NULL);
183 	i_assert(array_count(&subm->rcpt_to) > 0);
184 
185 	event_add_int(subm->event, "recipients", array_count(&subm->rcpt_to));
186 
187 	subm->output = iostream_temp_create
188 		(t_strconcat("/tmp/dovecot.",
189 			master_service_get_name(master_service), NULL), 0);
190 	o_stream_set_no_error_handling(subm->output, TRUE);
191 	return subm->output;
192 }
193 
194 static void
smtp_submit_callback(struct smtp_submit * subm,int status,const char * error)195 smtp_submit_callback(struct smtp_submit *subm, int status,
196 		     const char *error)
197 {
198 	struct smtp_submit_result result;
199 	smtp_submit_callback_t *callback;
200 
201 	timeout_remove(&subm->to_error);
202 
203 	struct event_passthrough *e =
204 		event_create_passthrough(subm->event)->
205 		set_name("smtp_submit_finished");
206 	if (status > 0)
207 		e_debug(e->event(), "Sent message successfully");
208 	else {
209 		e->add_str("error", error);
210 		e_debug(e->event(), "Failed to send message: %s", error);
211 	}
212 
213 	i_zero(&result);
214 	result.status = status;
215 	result.error = error;
216 
217 	callback = subm->callback;
218 	subm->callback = NULL;
219 	callback(&result, subm->context);
220 }
221 
222 static void
smtp_submit_delayed_error_callback(struct smtp_submit * subm)223 smtp_submit_delayed_error_callback(struct smtp_submit *subm)
224 {
225 	smtp_submit_callback(subm, -1, subm->error);
226 }
227 
228 static void
smtp_submit_delayed_error(struct smtp_submit * subm,const char * error)229 smtp_submit_delayed_error(struct smtp_submit *subm,
230 			  const char *error)
231 {
232 	subm->status = -1;
233 	subm->error = p_strdup(subm->pool, error);
234 	subm->to_error = timeout_add_short(0,
235 		smtp_submit_delayed_error_callback, subm);
236 }
237 
238 static void
smtp_submit_error(struct smtp_submit * subm,int status,const char * error)239 smtp_submit_error(struct smtp_submit *subm,
240 		  int status, const char *error)
241 {
242 	const struct smtp_submit_settings *set = &subm->session->set;
243 	i_assert(status <= 0);
244 	if (subm->error != NULL)
245 		return;
246 
247 	subm->status = status;
248 	subm->error = p_strdup_printf(subm->pool,
249 		"smtp(%s): %s",
250 		set->submission_host, error);
251 }
252 
253 static void
smtp_submit_success(struct smtp_submit * subm)254 smtp_submit_success(struct smtp_submit *subm)
255 {
256 	if (subm->error != NULL)
257 		return;
258 	subm->status = 1;
259 }
260 
261 static void
smtp_submit_send_host_finished(struct smtp_submit * subm)262 smtp_submit_send_host_finished(struct smtp_submit *subm)
263 {
264 	i_assert(subm->status > 0 || subm->error != NULL);
265 	smtp_submit_callback(subm, subm->status, subm->error);
266 	subm->smtp_trans = NULL;
267 }
268 
269 static bool
reply_is_temp_fail(const struct smtp_reply * reply)270 reply_is_temp_fail(const struct smtp_reply *reply)
271 {
272 	return (smtp_reply_is_temp_fail(reply) ||
273 		!smtp_reply_is_remote(reply));
274 }
275 
276 static void
rcpt_to_callback(const struct smtp_reply * reply,struct smtp_submit * subm)277 rcpt_to_callback(const struct smtp_reply *reply,
278 		 struct smtp_submit *subm)
279 {
280 	if (!smtp_reply_is_success(reply)) {
281 		smtp_submit_error(subm,
282 			(reply_is_temp_fail(reply) ? -1 : 0),
283 			t_strdup_printf("RCPT TO failed: %s",
284 				smtp_reply_log(reply)));
285 	}
286 }
287 
288 static void
data_callback(const struct smtp_reply * reply,struct smtp_submit * subm)289 data_callback(const struct smtp_reply *reply,
290 	      struct smtp_submit *subm)
291 {
292 	if (!smtp_reply_is_success(reply)) {
293 		smtp_submit_error(subm,
294 			(reply_is_temp_fail(reply) ? -1 : 0),
295 			t_strdup_printf("DATA failed: %s",
296 				smtp_reply_log(reply)));
297 		return;
298 	}
299 
300 	smtp_submit_success(subm);
301 }
302 
303 static void
data_dummy_callback(const struct smtp_reply * reply ATTR_UNUSED,struct smtp_submit * subm ATTR_UNUSED)304 data_dummy_callback(const struct smtp_reply *reply ATTR_UNUSED,
305 		    struct smtp_submit *subm ATTR_UNUSED)
306 {
307 	/* nothing */
308 }
309 
310 static void
smtp_submit_send_host(struct smtp_submit * subm)311 smtp_submit_send_host(struct smtp_submit *subm)
312 {
313 	const struct smtp_submit_settings *set = &subm->session->set;
314 	struct smtp_client_settings smtp_set;
315 	struct smtp_client *smtp_client;
316 	struct smtp_client_connection *smtp_conn;
317 	struct smtp_client_transaction *smtp_trans;
318 	enum smtp_client_connection_ssl_mode ssl_mode;
319 	struct smtp_address *rcpt;
320 	const char *host;
321 	in_port_t port;
322 
323 	if (net_str2hostport(set->submission_host,
324 			     DEFAULT_SUBMISSION_PORT, &host, &port) < 0) {
325 		smtp_submit_delayed_error(subm, t_strdup_printf(
326 			"Invalid submission_host: %s", host));
327 		return;
328 	}
329 
330 	i_zero(&smtp_set);
331 	smtp_set.my_hostname = set->hostname;
332 	smtp_set.connect_timeout_msecs = set->submission_timeout*1000;
333 	smtp_set.command_timeout_msecs = set->submission_timeout*1000;
334 	smtp_set.debug = set->mail_debug;
335 	smtp_set.ssl = &subm->session->ssl_set;
336 	smtp_set.event_parent = subm->event;
337 
338 	ssl_mode = SMTP_CLIENT_SSL_MODE_NONE;
339 	if (set->submission_ssl != NULL) {
340 		if (strcasecmp(set->submission_ssl, "smtps") == 0 ||
341 			strcasecmp(set->submission_ssl, "submissions") == 0)
342 			ssl_mode = SMTP_CLIENT_SSL_MODE_IMMEDIATE;
343 		else if (strcasecmp(set->submission_ssl, "starttls") == 0)
344 			ssl_mode = SMTP_CLIENT_SSL_MODE_STARTTLS;
345 	}
346 
347 	smtp_client = smtp_client_init(&smtp_set);
348 	smtp_conn = smtp_client_connection_create(smtp_client,
349 		  SMTP_PROTOCOL_SMTP, host, port, ssl_mode, NULL);
350 
351 	smtp_trans = smtp_client_transaction_create(smtp_conn,
352 		subm->mail_from, NULL, 0, smtp_submit_send_host_finished, subm);
353 	smtp_client_connection_unref(&smtp_conn);
354 
355 	array_foreach_elem(&subm->rcpt_to, rcpt) {
356 		smtp_client_transaction_add_rcpt(smtp_trans,
357 			rcpt, NULL, rcpt_to_callback, data_dummy_callback, subm);
358 	}
359 
360 	subm->smtp_client = smtp_client;
361 	subm->smtp_trans = smtp_trans;
362 
363 	smtp_client_transaction_send
364 		(smtp_trans, subm->input, data_callback, subm);
365 	i_stream_unref(&subm->input);
366 }
367 
368 static void
smtp_submit_sendmail_callback(enum program_client_exit_status status,struct smtp_submit * subm)369 smtp_submit_sendmail_callback(enum program_client_exit_status status,
370 			      struct smtp_submit *subm)
371 {
372 	if (status == PROGRAM_CLIENT_EXIT_STATUS_INTERNAL_FAILURE) {
373 		smtp_submit_callback(subm, -1,
374 			"Failed to execute sendmail");
375 		return;
376 	}
377 	if (status == PROGRAM_CLIENT_EXIT_STATUS_FAILURE) {
378 		smtp_submit_callback(subm, -1,
379 			"Sendmail program returned error");
380 		return;
381 	}
382 
383 	smtp_submit_callback(subm, 1, NULL);
384 }
385 
386 static void
smtp_submit_send_sendmail(struct smtp_submit * subm)387 smtp_submit_send_sendmail(struct smtp_submit *subm)
388 {
389 	const struct smtp_submit_settings *set = &subm->session->set;
390 	const char *const *sendmail_args, *sendmail_bin, *str;
391 	ARRAY_TYPE(const_string) args;
392 	struct smtp_address *rcpt;
393 	unsigned int i;
394 	struct program_client_settings pc_set;
395 	struct program_client *pc;
396 
397 	sendmail_args = t_strsplit(set->sendmail_path, " ");
398 	t_array_init(&args, 16);
399 	i_assert(sendmail_args[0] != NULL);
400 	sendmail_bin = sendmail_args[0];
401 	for (i = 1; sendmail_args[i] != NULL; i++)
402 		array_push_back(&args, &sendmail_args[i]);
403 
404 	str = "-i"; array_push_back(&args, &str); /* ignore dots */
405 	str = "-f"; array_push_back(&args, &str);
406 	str = !smtp_address_isnull(subm->mail_from) ?
407 		smtp_address_encode(subm->mail_from) : "<>";
408 	array_push_back(&args, &str);
409 
410 	str = "--"; array_push_back(&args, &str);
411 	array_foreach_elem(&subm->rcpt_to, rcpt) {
412 		const char *rcpt_encoded = smtp_address_encode(rcpt);
413 		array_push_back(&args, &rcpt_encoded);
414 	}
415 	array_append_zero(&args);
416 
417 	i_zero(&pc_set);
418 	pc_set.client_connect_timeout_msecs = set->submission_timeout * 1000;
419 	pc_set.input_idle_timeout_msecs = set->submission_timeout * 1000;
420 	pc_set.debug = set->mail_debug;
421 	pc_set.event = subm->event;
422 	pc_set.allow_root = subm->session->allow_root;
423 	restrict_access_init(&pc_set.restrict_set);
424 
425 	pc = program_client_local_create
426 		(sendmail_bin, array_front(&args), &pc_set);
427 
428 	program_client_set_input(pc, subm->input);
429 	i_stream_unref(&subm->input);
430 
431 	subm->prg_client = pc;
432 
433 	program_client_run_async(pc, smtp_submit_sendmail_callback, subm);
434 }
435 
436 struct smtp_submit_run_context {
437 	int status;
438 	char *error;
439 };
440 
441 static void
smtp_submit_run_callback(const struct smtp_submit_result * result,struct smtp_submit_run_context * rctx)442 smtp_submit_run_callback(const struct smtp_submit_result *result,
443 			 struct smtp_submit_run_context *rctx)
444 {
445 	rctx->error = i_strdup(result->error);
446 	rctx->status = result->status;
447 	io_loop_stop(current_ioloop);
448 }
449 
smtp_submit_run(struct smtp_submit * subm,const char ** error_r)450 int smtp_submit_run(struct smtp_submit *subm,
451 		    const char **error_r)
452 {
453 	struct smtp_submit_run_context rctx;
454 	struct ioloop *ioloop;
455 
456 	ioloop = io_loop_create();
457 	io_loop_set_running(ioloop);
458 
459 	i_zero(&rctx);
460 	smtp_submit_run_async(subm,
461 		smtp_submit_run_callback, &rctx);
462 
463 	if (io_loop_is_running(ioloop))
464 		io_loop_run(ioloop);
465 
466 	io_loop_destroy(&ioloop);
467 
468 	if (rctx.error == NULL)
469 		*error_r = NULL;
470 	else {
471 		*error_r = t_strdup(rctx.error);
472 		i_free(rctx.error);
473 	}
474 
475 	return rctx.status;
476 }
477 
478 #undef smtp_submit_run_async
smtp_submit_run_async(struct smtp_submit * subm,smtp_submit_callback_t * callback,void * context)479 void smtp_submit_run_async(struct smtp_submit *subm,
480 			   smtp_submit_callback_t *callback, void *context)
481 {
482 	const struct smtp_submit_settings *set = &subm->session->set;
483 	uoff_t data_size;
484 
485 	subm->callback = callback;
486 	subm->context = context;
487 
488 	/* the mail has been written to a file. now actually send it. */
489 	subm->input = iostream_temp_finish
490 		(&subm->output, IO_BLOCK_SIZE);
491 
492 	if (i_stream_get_size(subm->input, TRUE, &data_size) > 0)
493 		event_add_int(subm->event, "data_size", data_size);
494 
495 	struct event_passthrough *e =
496 		event_create_passthrough(subm->event)->
497 		set_name("smtp_submit_started");
498 	e_debug(e->event(), "Started sending message");
499 
500 	if (set->submission_host != NULL) {
501 		smtp_submit_send_host(subm);
502 	} else {
503 		smtp_submit_send_sendmail(subm);
504 	}
505 }
506