1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2
3 #include "submission-common.h"
4 #include "str.h"
5 #include "istream.h"
6 #include "istream-concat.h"
7 #include "istream-seekable.h"
8 #include "mail-storage.h"
9 #include "imap-url.h"
10 #include "imap-msgpart.h"
11 #include "imap-msgpart-url.h"
12 #include "imap-urlauth.h"
13 #include "imap-urlauth-fetch.h"
14
15 #include "submission-recipient.h"
16 #include "submission-commands.h"
17 #include "submission-backend-relay.h"
18
19 /*
20 * EHLO, HELO commands
21 */
22
23 static void
submission_helo_reply_add_extra(struct client * client,struct smtp_server_reply * reply)24 submission_helo_reply_add_extra(struct client *client,
25 struct smtp_server_reply *reply)
26 {
27 const struct client_extra_capability *cap;
28
29 if (!array_is_created(&client->extra_capabilities))
30 return;
31
32 array_foreach(&client->extra_capabilities, cap) {
33 if (cap->params == NULL) {
34 smtp_server_reply_ehlo_add(reply, cap->capability);
35 } else {
36 smtp_server_reply_ehlo_add_param(reply, cap->capability,
37 "%s", cap->params);
38 }
39 }
40 }
41
submission_helo_reply_submit(struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_helo * data)42 void submission_helo_reply_submit(struct smtp_server_cmd_ctx *cmd,
43 struct smtp_server_cmd_helo *data)
44 {
45 struct client *client = smtp_server_connection_get_context(cmd->conn);
46 enum smtp_capability backend_caps = client->backend_capabilities;
47 struct smtp_server_reply *reply;
48 uoff_t cap_size;
49
50 reply = smtp_server_reply_create_ehlo(cmd->cmd);
51 if (!data->helo.old_smtp) {
52 string_t *burl_params = t_str_new(256);
53
54 str_append(burl_params, "imap");
55 if (*client->set->imap_urlauth_host == '\0' ||
56 strcmp(client->set->imap_urlauth_host,
57 URL_HOST_ALLOW_ANY) == 0) {
58 str_printfa(burl_params, " imap://%s",
59 client->set->hostname);
60 } else {
61 str_printfa(burl_params, " imap://%s",
62 client->set->imap_urlauth_host);
63 }
64 if (client->set->imap_urlauth_port != 143) {
65 str_printfa(burl_params, ":%u",
66 client->set->imap_urlauth_port);
67 }
68
69 if ((backend_caps & SMTP_CAPABILITY_8BITMIME) != 0)
70 smtp_server_reply_ehlo_add(reply, "8BITMIME");
71 smtp_server_reply_ehlo_add(reply, "AUTH");
72 if ((backend_caps & SMTP_CAPABILITY_BINARYMIME) != 0 &&
73 (backend_caps & SMTP_CAPABILITY_CHUNKING) != 0)
74 smtp_server_reply_ehlo_add(reply, "BINARYMIME");
75 smtp_server_reply_ehlo_add_param(reply,
76 "BURL", "%s", str_c(burl_params));
77 smtp_server_reply_ehlo_add(reply, "CHUNKING");
78 if ((backend_caps & SMTP_CAPABILITY_DSN) != 0)
79 smtp_server_reply_ehlo_add(reply, "DSN");
80 smtp_server_reply_ehlo_add(reply,
81 "ENHANCEDSTATUSCODES");
82 smtp_server_reply_ehlo_add(reply,
83 "PIPELINING");
84
85 cap_size = client_get_max_mail_size(client);
86 if (cap_size > 0) {
87 smtp_server_reply_ehlo_add_param(reply,
88 "SIZE", "%"PRIuUOFF_T, cap_size);
89 } else {
90 smtp_server_reply_ehlo_add(reply, "SIZE");
91 }
92 if ((backend_caps & SMTP_CAPABILITY_VRFY) != 0)
93 smtp_server_reply_ehlo_add(reply, "VRFY");
94
95 submission_helo_reply_add_extra(client, reply);
96 }
97 smtp_server_reply_submit(reply);
98 }
99
cmd_helo(void * conn_ctx,struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_helo * data)100 int cmd_helo(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
101 struct smtp_server_cmd_helo *data)
102 {
103 struct client *client = conn_ctx;
104
105 if (!data->first ||
106 smtp_server_connection_get_state(client->conn, NULL)
107 >= SMTP_SERVER_STATE_READY)
108 return client->v.cmd_helo(client, cmd, data);
109
110 /* respond right away */
111 submission_helo_reply_submit(cmd, data);
112 return 1;
113 }
114
client_default_cmd_helo(struct client * client,struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_helo * data)115 int client_default_cmd_helo(struct client *client,
116 struct smtp_server_cmd_ctx *cmd,
117 struct smtp_server_cmd_helo *data)
118 {
119 return submission_backend_cmd_helo(client->backend_default, cmd, data);
120 }
121
122
123 /*
124 * MAIL command
125 */
126
cmd_mail(void * conn_ctx,struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_mail * data)127 int cmd_mail(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
128 struct smtp_server_cmd_mail *data)
129 {
130 struct client *client = conn_ctx;
131
132 client->state.backend = client->backend_default;
133
134 return client->v.cmd_mail(client, cmd, data);
135 }
136
client_default_cmd_mail(struct client * client,struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_mail * data)137 int client_default_cmd_mail(struct client *client,
138 struct smtp_server_cmd_ctx *cmd,
139 struct smtp_server_cmd_mail *data)
140 {
141 if (client->user->anonymous && !client->state.anonymous_allowed) {
142 /* NOTE: may need to allow anonymous BURL access in the future,
143 but while that is not supported, deny all anonymous access
144 explicitly. */
145 smtp_server_reply(cmd, 554, "5.7.1",
146 "Access denied (anonymous user)");
147 return -1;
148 }
149
150 return submission_backend_cmd_mail(client->state.backend, cmd, data);
151 }
152
153 /*
154 * RCPT command
155 */
156
cmd_rcpt(void * conn_ctx,struct smtp_server_cmd_ctx * cmd,struct smtp_server_recipient * rcpt)157 int cmd_rcpt(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
158 struct smtp_server_recipient *rcpt)
159 {
160 struct client *client = conn_ctx;
161 struct submission_recipient *srcpt;
162
163 srcpt = submission_recipient_create(client, rcpt);
164
165 return client->v.cmd_rcpt(client, cmd, srcpt);
166 }
167
client_default_cmd_rcpt(struct client * client ATTR_UNUSED,struct smtp_server_cmd_ctx * cmd,struct submission_recipient * srcpt)168 int client_default_cmd_rcpt(struct client *client ATTR_UNUSED,
169 struct smtp_server_cmd_ctx *cmd,
170 struct submission_recipient *srcpt)
171 {
172 if (client->user->anonymous && !srcpt->anonymous_allowed) {
173 /* NOTE: may need to allow anonymous BURL access in the future,
174 but while that is not supported, deny all anonymous access
175 explicitly. */
176 smtp_server_recipient_reply(
177 srcpt->rcpt, 554, "5.7.1",
178 "Access denied (anonymous user)");
179 return -1;
180 }
181
182 return submission_backend_cmd_rcpt(srcpt->backend, cmd, srcpt);
183 }
184
185 /*
186 * RSET command
187 */
188
cmd_rset(void * conn_ctx,struct smtp_server_cmd_ctx * cmd)189 int cmd_rset(void *conn_ctx, struct smtp_server_cmd_ctx *cmd)
190 {
191 struct client *client = conn_ctx;
192
193 return client->v.cmd_rset(client, cmd);
194 }
195
client_default_cmd_rset(struct client * client,struct smtp_server_cmd_ctx * cmd)196 int client_default_cmd_rset(struct client *client,
197 struct smtp_server_cmd_ctx *cmd)
198 {
199 struct submission_backend *backend = client->state.backend;
200
201 if (backend == NULL)
202 backend = client->backend_default;
203
204 /* all backends will also be notified through trans_free(), but that
205 doesn't allow changing the RSET command response. */
206 return submission_backend_cmd_rset(backend, cmd);
207 }
208
209 /*
210 * DATA/BDAT commands
211 */
212
cmd_data_continue(void * conn_ctx,struct smtp_server_cmd_ctx * cmd,struct smtp_server_transaction * trans)213 int cmd_data_continue(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
214 struct smtp_server_transaction *trans)
215 {
216 struct client *client = conn_ctx;
217 struct istream *data_input = client->state.data_input;
218 uoff_t data_size;
219 struct istream *inputs[3];
220 string_t *added_headers;
221 const unsigned char *data;
222 size_t size;
223 int ret;
224
225 while ((ret = i_stream_read_more(data_input, &data, &size)) > 0) {
226 i_stream_skip(data_input, size);
227 if (!smtp_server_cmd_data_check_size(cmd))
228 return -1;
229 }
230
231 if (ret == 0)
232 return 0;
233 if (ret < 0 && data_input->stream_errno != 0)
234 return -1;
235
236 /* Done reading DATA stream; remove it from state and continue with
237 local variable. */
238 client->state.data_input = NULL;
239
240 /* Current data stream position is the data size */
241 client->state.data_size = data_input->v_offset;
242
243 /* prepend our own headers */
244 added_headers = t_str_new(200);
245 smtp_server_transaction_write_trace_record(
246 added_headers, trans, SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_FINAL);
247
248 i_stream_seek(data_input, 0);
249 inputs[0] = i_stream_create_copy_from_data(
250 str_data(added_headers), str_len(added_headers));
251 inputs[1] = data_input;
252 inputs[2] = NULL;
253
254 data_input = i_stream_create_concat(inputs);
255 i_stream_set_name(data_input, "<submission DATA>");
256 data_size = client->state.data_size + str_len(added_headers);
257
258 i_stream_unref(&inputs[0]);
259 i_stream_unref(&inputs[1]);
260
261 ret = client->v.cmd_data(client, cmd, trans, data_input, data_size);
262
263 i_stream_unref(&data_input);
264 return ret;
265 }
266
cmd_data_begin(void * conn_ctx,struct smtp_server_cmd_ctx * cmd ATTR_UNUSED,struct smtp_server_transaction * trans ATTR_UNUSED,struct istream * data_input)267 int cmd_data_begin(void *conn_ctx,
268 struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
269 struct smtp_server_transaction *trans ATTR_UNUSED,
270 struct istream *data_input)
271 {
272 struct client *client = conn_ctx;
273 struct istream *inputs[2];
274 string_t *path;
275
276 if (client->user->anonymous && !client->state.anonymous_allowed) {
277 smtp_server_reply(cmd, 554, "5.7.1",
278 "Access denied (anonymous user)");
279 return -1;
280 }
281
282 inputs[0] = data_input;
283 inputs[1] = NULL;
284
285 path = t_str_new(256);
286 mail_user_set_get_temp_prefix(path, client->user->set);
287 client->state.data_input = i_stream_create_seekable_path(inputs,
288 SUBMISSION_MAIL_DATA_MAX_INMEMORY_SIZE, str_c(path));
289 return 0;
290 }
291
client_default_cmd_data(struct client * client,struct smtp_server_cmd_ctx * cmd,struct smtp_server_transaction * trans,struct istream * data_input,uoff_t data_size)292 int client_default_cmd_data(struct client *client,
293 struct smtp_server_cmd_ctx *cmd,
294 struct smtp_server_transaction *trans,
295 struct istream *data_input, uoff_t data_size)
296 {
297 return submission_backends_cmd_data(client, cmd, trans,
298 data_input, data_size);
299 }
300
301 /*
302 * BURL command
303 */
304
305 /* FIXME: RFC 4468
306 If the URL argument to BURL refers to binary data, then the submit server
307 MAY refuse the command or down convert as described in Binary SMTP.
308 */
309
310 struct cmd_burl_context {
311 struct client *client;
312 struct smtp_server_cmd_ctx *cmd;
313
314 struct imap_urlauth_fetch *urlauth_fetch;
315 struct imap_msgpart_url *url_fetch;
316
317 bool chunk_last:1;
318 };
319
320 static void
cmd_burl_destroy(struct smtp_server_cmd_ctx * cmd ATTR_UNUSED,struct cmd_burl_context * burl_cmd)321 cmd_burl_destroy(struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
322 struct cmd_burl_context *burl_cmd)
323 {
324 if (burl_cmd->urlauth_fetch != NULL)
325 imap_urlauth_fetch_deinit(&burl_cmd->urlauth_fetch);
326 if (burl_cmd->url_fetch != NULL)
327 imap_msgpart_url_free(&burl_cmd->url_fetch);
328 }
329
330 static int
cmd_burl_fetch_cb(struct imap_urlauth_fetch_reply * reply,bool last,void * context)331 cmd_burl_fetch_cb(struct imap_urlauth_fetch_reply *reply,
332 bool last, void *context)
333 {
334 struct cmd_burl_context *burl_cmd = context;
335 struct smtp_server_cmd_ctx *cmd = burl_cmd->cmd;
336 int ret;
337
338 i_assert(last);
339
340 if (reply == NULL) {
341 /* fatal failure */
342 // FIXME: make this an internal error
343 smtp_server_reply(cmd, 554, "5.6.6",
344 "IMAP URLAUTH resolution failed");
345 return -1;
346 }
347 if (!reply->succeeded) {
348 /* URL fetch failed */
349 if (reply->error != NULL) {
350 smtp_server_reply(cmd, 554, "5.6.6",
351 "IMAP URLAUTH resolution failed: %s",
352 reply->error);
353 } else {
354 smtp_server_reply(cmd, 554, "5.6.6",
355 "IMAP URLAUTH resolution failed");
356 }
357 return 1;
358 }
359
360 /* URL fetch succeeded */
361 ret = smtp_server_connection_data_chunk_add(cmd,
362 reply->input, reply->size, burl_cmd->chunk_last, FALSE);
363 if (ret < 0)
364 return -1;
365
366 /* Command is likely not yet complete at this point, so return 0 */
367 return 0;
368 }
369
370 static int
cmd_burl_fetch_trusted(struct cmd_burl_context * burl_cmd,struct imap_url * imap_url)371 cmd_burl_fetch_trusted(struct cmd_burl_context *burl_cmd,
372 struct imap_url *imap_url)
373 {
374 struct smtp_server_cmd_ctx *cmd = burl_cmd->cmd;
375 struct client *client = burl_cmd->client;
376 const char *host_name = client->set->imap_urlauth_host;
377 in_port_t host_port = client->set->imap_urlauth_port;
378 struct imap_msgpart_open_result result;
379 const char *error;
380
381 /* validate host */
382 if (imap_url->host.name == NULL ||
383 (strcmp(host_name, URL_HOST_ALLOW_ANY) != 0 &&
384 strcmp(imap_url->host.name, host_name) != 0)) {
385 smtp_server_reply(cmd, 554, "5.6.6",
386 "IMAP URL resolution failed: "
387 "Inappropriate or missing host name");
388 return -1;
389 }
390
391 /* validate port */
392 if ((imap_url->port == 0 && host_port != 143) ||
393 (imap_url->port != 0 && host_port != imap_url->port)) {
394 smtp_server_reply(cmd, 554, "5.6.6",
395 "IMAP URL resolution failed: "
396 "Inappropriate server port");
397 return -1;
398 }
399
400 /* retrieve URL */
401 if (imap_msgpart_url_create
402 (client->user, imap_url, &burl_cmd->url_fetch, &error) < 0) {
403 smtp_server_reply(cmd, 554, "5.6.6",
404 "IMAP URL resolution failed: %s", error);
405 return -1;
406 }
407 if (imap_msgpart_url_read_part(burl_cmd->url_fetch,
408 &result, &error) <= 0) {
409 smtp_server_reply(cmd, 554, "5.6.6",
410 "IMAP URL resolution failed: %s", error);
411 return -1;
412 }
413
414 return smtp_server_connection_data_chunk_add(cmd,
415 result.input, result.size, burl_cmd->chunk_last, FALSE);
416 }
417
418 static int
cmd_burl_fetch(struct cmd_burl_context * burl_cmd,const char * url,struct imap_url * imap_url)419 cmd_burl_fetch(struct cmd_burl_context *burl_cmd, const char *url,
420 struct imap_url *imap_url)
421 {
422 struct smtp_server_cmd_ctx *cmd = burl_cmd->cmd;
423 struct client *client = burl_cmd->client;
424
425 if (client->urlauth_ctx == NULL) {
426 /* RFC5248, Section 2.4:
427
428 554 5.7.14 Trust relationship required
429
430 The submission server requires a configured trust
431 relationship with a third-party server in order to access
432 the message content. This value replaces the prior use of
433 X.7.8 for this error condition, thereby updating [RFC4468].
434 */
435 smtp_server_reply(cmd, 554, "5.7.14",
436 "No IMAP URLAUTH access available");
437 return -1;
438 }
439
440 /* urlauth */
441 burl_cmd->urlauth_fetch =
442 imap_urlauth_fetch_init(client->urlauth_ctx,
443 cmd_burl_fetch_cb, burl_cmd);
444 if (imap_urlauth_fetch_url_parsed(burl_cmd->urlauth_fetch,
445 url, imap_url, IMAP_URLAUTH_FETCH_FLAG_BODY) == 0) {
446 /* wait for URL fetch */
447 return 0;
448 }
449 return 1;
450 }
451
cmd_burl(struct smtp_server_cmd_ctx * cmd,const char * params)452 void cmd_burl(struct smtp_server_cmd_ctx *cmd, const char *params)
453 {
454 struct smtp_server_connection *conn = cmd->conn;
455 struct client *client = smtp_server_connection_get_context(conn);
456 struct cmd_burl_context *burl_cmd;
457 const char *const *argv;
458 enum imap_url_parse_flags url_parse_flags =
459 IMAP_URL_PARSE_ALLOW_URLAUTH;
460 struct imap_url *imap_url;
461 const char *url, *error;
462 bool chunk_last = FALSE;
463 int ret = 1;
464
465 smtp_server_connection_data_chunk_init(cmd);
466
467 /* burl-cmd = "BURL" SP absolute-URI [SP end-marker] CRLF
468 end-marker = "LAST"
469 */
470 argv = t_strsplit(params, " ");
471 url = argv[0];
472 if (url == NULL) {
473 smtp_server_reply(cmd, 501, "5.5.4",
474 "Missing chunk URL parameter");
475 ret = -1;
476 } else if (imap_url_parse(url, NULL, url_parse_flags,
477 &imap_url, &error) < 0) {
478 smtp_server_reply(cmd, 501, "5.5.4",
479 "Invalid chunk URL: %s", error);
480 ret = -1;
481 } else if (argv[1] != NULL) {
482 if (strcasecmp(argv[1], "LAST") != 0) {
483 smtp_server_reply(cmd, 501, "5.5.4",
484 "Invalid end marker parameter");
485 ret = -1;
486 } else if (argv[2] != NULL) {
487 smtp_server_reply(cmd, 501, "5.5.4",
488 "Invalid parameters");
489 ret = -1;
490 } else {
491 chunk_last = TRUE;
492 }
493 }
494
495 if (ret < 0 || !smtp_server_connection_data_check_state(cmd))
496 return;
497
498 if (client->user->anonymous) {
499 smtp_server_reply(cmd, 554, "5.7.1",
500 "Access denied (anonymous user)");
501 return;
502 }
503
504 burl_cmd = p_new(cmd->pool, struct cmd_burl_context, 1);
505 burl_cmd->client = client;
506 burl_cmd->cmd = cmd;
507 burl_cmd->chunk_last = chunk_last;
508
509 smtp_server_command_add_hook(cmd->cmd, SMTP_SERVER_COMMAND_HOOK_DESTROY,
510 cmd_burl_destroy, burl_cmd);
511
512 if (imap_url->uauth_rumpurl == NULL) {
513 /* direct local url */
514 ret = cmd_burl_fetch_trusted(burl_cmd, imap_url);
515 } else {
516 ret = cmd_burl_fetch(burl_cmd, url, imap_url);
517 }
518
519 if (ret == 0 && chunk_last)
520 smtp_server_command_input_lock(cmd);
521 }
522
523 /*
524 * VRFY command
525 */
526
cmd_vrfy(void * conn_ctx,struct smtp_server_cmd_ctx * cmd,const char * param)527 int cmd_vrfy(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
528 const char *param)
529 {
530 struct client *client = conn_ctx;
531
532 if (client->user->anonymous) {
533 smtp_server_reply(cmd, 550, "5.7.1",
534 "Access denied (anonymous user)");
535 return -1;
536 }
537
538 return client->v.cmd_vrfy(client, cmd, param);
539 }
540
client_default_cmd_vrfy(struct client * client,struct smtp_server_cmd_ctx * cmd,const char * param)541 int client_default_cmd_vrfy(struct client *client,
542 struct smtp_server_cmd_ctx *cmd, const char *param)
543 {
544 return submission_backend_cmd_vrfy(client->backend_default, cmd, param);
545 }
546
547 /*
548 * NOOP command
549 */
550
cmd_noop(void * conn_ctx,struct smtp_server_cmd_ctx * cmd)551 int cmd_noop(void *conn_ctx, struct smtp_server_cmd_ctx *cmd)
552 {
553 struct client *client = conn_ctx;
554
555 return client->v.cmd_noop(client, cmd);
556 }
557
client_default_cmd_noop(struct client * client,struct smtp_server_cmd_ctx * cmd)558 int client_default_cmd_noop(struct client *client,
559 struct smtp_server_cmd_ctx *cmd)
560 {
561 return submission_backend_cmd_noop(client->backend_default, cmd);
562 }
563
564 /*
565 * QUIT command
566 */
567
568 struct cmd_quit_context {
569 struct client *client;
570
571 struct smtp_server_cmd_ctx *cmd;
572 };
573
cmd_quit_finish(struct cmd_quit_context * quit_cmd)574 static void cmd_quit_finish(struct cmd_quit_context *quit_cmd)
575 {
576 struct client *client = quit_cmd->client;
577 struct smtp_server_cmd_ctx *cmd = quit_cmd->cmd;
578
579 timeout_remove(&client->to_quit);
580 smtp_server_reply_quit(cmd);
581 }
582
583 static void
cmd_quit_next(struct smtp_server_cmd_ctx * cmd ATTR_UNUSED,struct cmd_quit_context * quit_cmd)584 cmd_quit_next(struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
585 struct cmd_quit_context *quit_cmd)
586 {
587 struct client *client = quit_cmd->client;
588
589 /* give backend a brief interval to generate a quit reply */
590 client->to_quit = timeout_add(SUBMISSION_MAX_WAIT_QUIT_REPLY_MSECS,
591 cmd_quit_finish, quit_cmd);
592 }
593
cmd_quit(void * conn_ctx,struct smtp_server_cmd_ctx * cmd)594 int cmd_quit(void *conn_ctx, struct smtp_server_cmd_ctx *cmd)
595 {
596 struct client *client = conn_ctx;
597 struct cmd_quit_context *quit_cmd;
598
599 quit_cmd = p_new(cmd->pool, struct cmd_quit_context, 1);
600 quit_cmd->client = client;
601 quit_cmd->cmd = cmd;
602
603 smtp_server_command_add_hook(cmd->cmd, SMTP_SERVER_COMMAND_HOOK_NEXT,
604 cmd_quit_next, quit_cmd);
605
606 return client->v.cmd_quit(client, cmd);
607 }
608
client_default_cmd_quit(struct client * client,struct smtp_server_cmd_ctx * cmd)609 int client_default_cmd_quit(struct client *client,
610 struct smtp_server_cmd_ctx *cmd)
611 {
612 return submission_backend_cmd_quit(client->backend_default, cmd);
613 }
614
615
616