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