1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2 */
3
4 #include "lib.h"
5 #include "ioloop.h"
6 #include "str-sanitize.h"
7 #include "strfuncs.h"
8 #include "istream.h"
9 #include "istream-header-filter.h"
10 #include "ostream.h"
11 #include "mail-storage.h"
12
13 #include "rfc2822.h"
14
15 #include "sieve-common.h"
16 #include "sieve-limits.h"
17 #include "sieve-address.h"
18 #include "sieve-commands.h"
19 #include "sieve-code.h"
20 #include "sieve-message.h"
21 #include "sieve-actions.h"
22 #include "sieve-validator.h"
23 #include "sieve-generator.h"
24 #include "sieve-interpreter.h"
25 #include "sieve-code-dumper.h"
26 #include "sieve-result.h"
27 #include "sieve-smtp.h"
28 #include "sieve-message.h"
29
30 #include <stdio.h>
31
32 /*
33 * Redirect command
34 *
35 * Syntax
36 * redirect <address: string>
37 */
38
39 static bool
40 cmd_redirect_validate(struct sieve_validator *validator,
41 struct sieve_command *cmd);
42 static bool
43 cmd_redirect_generate(const struct sieve_codegen_env *cgenv,
44 struct sieve_command *cmd);
45
46 const struct sieve_command_def cmd_redirect = {
47 .identifier = "redirect",
48 .type = SCT_COMMAND,
49 .positional_args = 1,
50 .subtests = 0,
51 .block_allowed = FALSE,
52 .block_required = FALSE,
53 .validate = cmd_redirect_validate,
54 .generate = cmd_redirect_generate
55 };
56
57 /*
58 * Redirect operation
59 */
60
61 static bool
62 cmd_redirect_operation_dump(const struct sieve_dumptime_env *denv,
63 sieve_size_t *address);
64 static int
65 cmd_redirect_operation_execute(const struct sieve_runtime_env *renv,
66 sieve_size_t *address);
67
68 const struct sieve_operation_def cmd_redirect_operation = {
69 .mnemonic = "REDIRECT",
70 .code = SIEVE_OPERATION_REDIRECT,
71 .dump = cmd_redirect_operation_dump,
72 .execute = cmd_redirect_operation_execute
73 };
74
75 /*
76 * Redirect action
77 */
78
79 static bool
80 act_redirect_equals(const struct sieve_script_env *senv,
81 const struct sieve_action *act1,
82 const struct sieve_action *act2);
83 static int
84 act_redirect_check_duplicate(const struct sieve_runtime_env *renv,
85 const struct sieve_action *act,
86 const struct sieve_action *act_other);
87 static void
88 act_redirect_print(const struct sieve_action *action,
89 const struct sieve_result_print_env *rpenv, bool *keep);
90
91 static int
92 act_redirect_start(const struct sieve_action_exec_env *aenv, void **tr_context);
93 static int
94 act_redirect_execute(const struct sieve_action_exec_env *aenv, void *tr_context,
95 bool *keep);
96 static int
97 act_redirect_commit(const struct sieve_action_exec_env *aenv, void *tr_context);
98
99 const struct sieve_action_def act_redirect = {
100 .name = "redirect",
101 .flags = SIEVE_ACTFLAG_TRIES_DELIVER,
102 .equals = act_redirect_equals,
103 .check_duplicate = act_redirect_check_duplicate,
104 .print = act_redirect_print,
105 .start = act_redirect_start,
106 .execute = act_redirect_execute,
107 .commit = act_redirect_commit,
108 };
109
110 /*
111 * Validation
112 */
113
114 static bool
cmd_redirect_validate(struct sieve_validator * validator,struct sieve_command * cmd)115 cmd_redirect_validate(struct sieve_validator *validator,
116 struct sieve_command *cmd)
117 {
118 struct sieve_instance *svinst = sieve_validator_svinst(validator);
119 struct sieve_ast_argument *arg = cmd->first_positional;
120
121 /* Check and activate address argument */
122
123 if (!sieve_validate_positional_argument(validator, cmd, arg, "address",
124 1, SAAT_STRING))
125 return FALSE;
126
127 if (!sieve_validator_argument_activate(validator, cmd, arg, FALSE))
128 return FALSE;
129
130 /* We can only assess the validity of the outgoing address when it is
131 * a string literal. For runtime-generated strings this needs to be
132 * done at runtime.
133 */
134 if (sieve_argument_is_string_literal(arg)) {
135 string_t *raw_address = sieve_ast_argument_str(arg);
136 const char *error;
137 bool result;
138
139 T_BEGIN {
140 /* Parse the address */
141 result = sieve_address_validate_str(raw_address, &error);
142 if (!result) {
143 sieve_argument_validate_error(
144 validator, arg,
145 "specified redirect address '%s' is invalid: %s",
146 str_sanitize(str_c(raw_address),128),
147 error);
148 }
149 } T_END;
150
151 return result;
152 }
153
154 if (svinst->max_redirects == 0) {
155 sieve_command_validate_error(validator, cmd,
156 "local policy prohibits the use of a redirect action");
157 return FALSE;
158 }
159 return TRUE;
160 }
161
162 /*
163 * Code generation
164 */
165
166 static bool
cmd_redirect_generate(const struct sieve_codegen_env * cgenv,struct sieve_command * cmd)167 cmd_redirect_generate(const struct sieve_codegen_env *cgenv,
168 struct sieve_command *cmd)
169 {
170 sieve_operation_emit(cgenv->sblock, NULL, &cmd_redirect_operation);
171
172 /* Generate arguments */
173 return sieve_generate_arguments(cgenv, cmd, NULL);
174 }
175
176 /*
177 * Code dump
178 */
179
180 static bool
cmd_redirect_operation_dump(const struct sieve_dumptime_env * denv,sieve_size_t * address)181 cmd_redirect_operation_dump(const struct sieve_dumptime_env *denv,
182 sieve_size_t *address)
183 {
184 sieve_code_dumpf(denv, "REDIRECT");
185 sieve_code_descend(denv);
186
187 if (sieve_action_opr_optional_dump(denv, address, NULL) != 0)
188 return FALSE;
189
190 return sieve_opr_string_dump(denv, address, "address");
191 }
192
193 /*
194 * Code execution
195 */
196
197 static int
cmd_redirect_operation_execute(const struct sieve_runtime_env * renv,sieve_size_t * address)198 cmd_redirect_operation_execute(const struct sieve_runtime_env *renv,
199 sieve_size_t *address)
200 {
201 const struct sieve_execute_env *eenv = renv->exec_env;
202 struct sieve_instance *svinst = eenv->svinst;
203 struct sieve_side_effects_list *slist = NULL;
204 string_t *redirect;
205 const struct smtp_address *to_address;
206 const char *error;
207 int ret;
208
209 /*
210 * Read data
211 */
212
213 /* Optional operands (side effects only) */
214 if (sieve_action_opr_optional_read(renv, address, NULL,
215 &ret, &slist) != 0)
216 return ret;
217
218 /* Read the address */
219 if ((ret = sieve_opr_string_read(renv, address, "address",
220 &redirect)) <= 0)
221 return ret;
222
223 /*
224 * Perform operation
225 */
226
227 /* Parse the address */
228 to_address = sieve_address_parse_str(redirect, &error);
229 if (to_address == NULL) {
230 sieve_runtime_error(renv, NULL,
231 "specified redirect address '%s' is invalid: %s",
232 str_sanitize(str_c(redirect),128), error);
233 return SIEVE_EXEC_FAILURE;
234 }
235
236 if (svinst->max_redirects == 0) {
237 sieve_runtime_error(renv, NULL,
238 "local policy prohibits the use of a redirect action");
239 return SIEVE_EXEC_FAILURE;
240 }
241
242 if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS)) {
243 sieve_runtime_trace(renv, 0, "redirect action");
244 sieve_runtime_trace_descend(renv);
245 sieve_runtime_trace(renv, 0, "forward message to address %s",
246 smtp_address_encode_path(to_address));
247 }
248
249 /* Add redirect action to the result */
250
251 return sieve_act_redirect_add_to_result(renv, "redirect", slist,
252 to_address);
253 }
254
255 /*
256 * Action implementation
257 */
258
259 struct act_redirect_transaction {
260 const char *msg_id, *new_msg_id;
261 const char *dupeid;
262
263 bool skip_redirect:1;
264 };
265
266 static bool
act_redirect_equals(const struct sieve_script_env * senv ATTR_UNUSED,const struct sieve_action * act1,const struct sieve_action * act2)267 act_redirect_equals(const struct sieve_script_env *senv ATTR_UNUSED,
268 const struct sieve_action *act1,
269 const struct sieve_action *act2)
270 {
271 struct act_redirect_context *rd_ctx1 =
272 (struct act_redirect_context *)act1->context;
273 struct act_redirect_context *rd_ctx2 =
274 (struct act_redirect_context *)act2->context;
275
276 /* Address is already normalized */
277 return (smtp_address_equals(rd_ctx1->to_address, rd_ctx2->to_address));
278 }
279
280 static int
act_redirect_check_duplicate(const struct sieve_runtime_env * renv,const struct sieve_action * act,const struct sieve_action * act_other)281 act_redirect_check_duplicate(const struct sieve_runtime_env *renv,
282 const struct sieve_action *act,
283 const struct sieve_action *act_other)
284 {
285 const struct sieve_execute_env *eenv = renv->exec_env;
286
287 return (act_redirect_equals(eenv->scriptenv, act, act_other) ? 1 : 0);
288 }
289
290 static void
act_redirect_print(const struct sieve_action * action,const struct sieve_result_print_env * rpenv,bool * keep)291 act_redirect_print(const struct sieve_action *action,
292 const struct sieve_result_print_env *rpenv, bool *keep)
293 {
294 struct act_redirect_context *ctx =
295 (struct act_redirect_context *)action->context;
296
297 sieve_result_action_printf(rpenv, "redirect message to: %s",
298 smtp_address_encode_path(ctx->to_address));
299 *keep = FALSE;
300 }
301
302 static int
act_redirect_send(const struct sieve_action_exec_env * aenv,struct mail * mail,struct act_redirect_context * ctx,const char * new_msg_id)303 act_redirect_send(const struct sieve_action_exec_env *aenv, struct mail *mail,
304 struct act_redirect_context *ctx, const char *new_msg_id)
305 ATTR_NULL(4)
306 {
307 static const char *hide_headers[] = { "Return-Path" };
308 const struct sieve_execute_env *eenv = aenv->exec_env;
309 struct sieve_instance *svinst = eenv->svinst;
310 struct sieve_message_context *msgctx = aenv->msgctx;
311 const struct sieve_script_env *senv = eenv->scriptenv;
312 struct sieve_address_source env_from = svinst->redirect_from;
313 struct istream *input;
314 struct ostream *output;
315 const struct smtp_address *sender;
316 const char *error;
317 struct sieve_smtp_context *sctx;
318 int ret;
319
320 /* Just to be sure */
321 if (!sieve_smtp_available(senv)) {
322 sieve_result_global_warning(aenv, "no means to send mail");
323 return SIEVE_EXEC_FAILURE;
324 }
325
326 if (mail_get_stream(mail, NULL, NULL, &input) < 0) {
327 return sieve_result_mail_error(aenv, mail,
328 "failed to read input message");
329 }
330
331 /* Determine which sender to use
332
333 From RFC 5228, Section 4.2:
334
335 The envelope sender address on the outgoing message is chosen by the
336 sieve implementation. It MAY be copied from the message being
337 processed. However, if the message being processed has an empty
338 envelope sender address the outgoing message MUST also have an empty
339 envelope sender address. This last requirement is imposed to prevent
340 loops in the case where a message is redirected to an invalid address
341 when then returns a delivery status notification that also ends up
342 being redirected to the same invalid address.
343 */
344 if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) {
345 /* Envelope available */
346 sender = sieve_message_get_sender(msgctx);
347 if (sender != NULL &&
348 sieve_address_source_get_address(&env_from, svinst, senv,
349 msgctx, eenv->flags,
350 &sender) < 0)
351 sender = NULL;
352 } else {
353 /* No envelope available */
354 ret = sieve_address_source_get_address(&env_from, svinst, senv,
355 msgctx, eenv->flags,
356 &sender);
357 if (ret < 0)
358 sender = NULL;
359 else if (ret == 0)
360 sender = svinst->user_email;
361 }
362
363 /* Open SMTP transport */
364 sctx = sieve_smtp_start_single(senv, ctx->to_address, sender, &output);
365
366 /* Remove unwanted headers */
367 input = i_stream_create_header_filter(
368 input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR,
369 hide_headers, N_ELEMENTS(hide_headers),
370 *null_header_filter_callback, (void *)NULL);
371
372 T_BEGIN {
373 string_t *hdr = t_str_new(256);
374 const struct smtp_address *user_email;
375
376 /* Prepend sieve headers (should not affect signatures) */
377 rfc2822_header_append(hdr, "X-Sieve", SIEVE_IMPLEMENTATION,
378 FALSE, NULL);
379 if (svinst->user_email == NULL &&
380 (eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0)
381 user_email = sieve_message_get_final_recipient(msgctx);
382 else
383 user_email = sieve_get_user_email(svinst);
384 if (user_email != NULL) {
385 rfc2822_header_append(hdr, "X-Sieve-Redirected-From",
386 smtp_address_encode(user_email),
387 FALSE, NULL);
388 }
389
390 /* Add new Message-ID if message doesn't have one */
391 if (new_msg_id != NULL)
392 rfc2822_header_write(hdr, "Message-ID", new_msg_id);
393
394 o_stream_nsend(output, str_data(hdr), str_len(hdr));
395 } T_END;
396
397 o_stream_nsend_istream(output, input);
398
399 if (input->stream_errno != 0) {
400 sieve_result_critical(aenv, "failed to read input message",
401 "read(%s) failed: %s",
402 i_stream_get_name(input),
403 i_stream_get_error(input));
404 i_stream_unref(&input);
405 sieve_smtp_abort(sctx);
406 return SIEVE_EXEC_TEMP_FAILURE;
407 }
408 i_stream_unref(&input);
409
410 /* Close SMTP transport */
411 if ((ret = sieve_smtp_finish(sctx, &error)) <= 0) {
412 if (ret < 0) {
413 sieve_result_global_error(
414 aenv, "failed to redirect message to <%s>: %s "
415 "(temporary failure)",
416 smtp_address_encode(ctx->to_address),
417 str_sanitize(error, 512));
418 return SIEVE_EXEC_TEMP_FAILURE;
419 }
420
421 sieve_result_global_log_error(
422 aenv, "failed to redirect message to <%s>: %s "
423 "(permanent failure)",
424 smtp_address_encode(ctx->to_address),
425 str_sanitize(error, 512));
426 return SIEVE_EXEC_FAILURE;
427 }
428
429 return SIEVE_EXEC_OK;
430 }
431
432 static int
act_redirect_get_duplicate_id(struct act_redirect_context * ctx,const struct sieve_action_exec_env * aenv,const char * msg_id,const char ** dupeid_r)433 act_redirect_get_duplicate_id(struct act_redirect_context *ctx,
434 const struct sieve_action_exec_env *aenv,
435 const char *msg_id, const char **dupeid_r)
436 {
437 const struct sieve_execute_env *eenv = aenv->exec_env;
438 struct sieve_message_context *msgctx = aenv->msgctx;
439 const struct sieve_message_data *msgdata = eenv->msgdata;
440 struct mail *mail = msgdata->mail;
441 const struct smtp_address *recipient;
442 const char *resent_id = NULL, *list_id = NULL;
443
444 /* Read identifying headers */
445 if (mail_get_first_header(mail, "resent-message-id", &resent_id) < 0) {
446 return sieve_result_mail_error(
447 aenv, mail,
448 "failed to read header field `resent-message-id'");
449 }
450 if (resent_id == NULL &&
451 mail_get_first_header(mail, "resent-from", &resent_id) < 0) {
452 return sieve_result_mail_error(
453 aenv, mail,
454 "failed to read header field `resent-from'");
455 }
456 if (mail_get_first_header(mail, "list-id", &list_id) < 0) {
457 return sieve_result_mail_error(
458 aenv, mail,
459 "failed to read header field `list-id'");
460 }
461
462 if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0)
463 recipient = sieve_message_get_orig_recipient(msgctx);
464 else
465 recipient = sieve_get_user_email(eenv->svinst);
466
467 pool_t pool = sieve_result_pool(aenv->result);
468
469 /* Base the duplicate ID on:
470 - the message id
471 - the recipient running this Sieve script
472 - redirect target address
473 - if this message is resent: the message-id or from-address of
474 the original message
475 - if the message came through a mailing list: the mailinglist ID
476 */
477 *dupeid_r = p_strdup_printf(
478 pool, "%s-%s-%s-%s-%s", msg_id,
479 (recipient != NULL ? smtp_address_encode(recipient) : ""),
480 smtp_address_encode(ctx->to_address),
481 (resent_id != NULL ? resent_id : ""),
482 (list_id != NULL ? list_id : ""));
483 return SIEVE_EXEC_OK;
484 }
485
486 static int
act_redirect_check_loop_header(const struct sieve_action_exec_env * aenv,struct mail * mail,bool * loop_detected_r)487 act_redirect_check_loop_header(const struct sieve_action_exec_env *aenv,
488 struct mail *mail, bool *loop_detected_r)
489 {
490 const struct sieve_execute_env *eenv = aenv->exec_env;
491 struct sieve_message_context *msgctx = aenv->msgctx;
492 const char *const *headers;
493 const char *recipient, *user_email;
494 const struct smtp_address *addr;
495 int ret;
496
497 *loop_detected_r = FALSE;
498
499 ret = mail_get_headers(mail, "x-sieve-redirected-from", &headers);
500 if (ret < 0) {
501 return sieve_result_mail_error(
502 aenv, mail, "failed to read header field "
503 "`x-sieve-redirected-from'");
504 }
505
506 if (ret == 0)
507 return SIEVE_EXEC_OK;
508
509 recipient = user_email = NULL;
510 if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) {
511 addr = sieve_message_get_final_recipient(msgctx);
512 if (addr != NULL)
513 recipient = smtp_address_encode(addr);
514 }
515 addr = sieve_get_user_email(eenv->svinst);
516 if (addr != NULL)
517 user_email = smtp_address_encode(addr);
518
519 while (*headers != NULL) {
520 const char *header = t_str_trim(*headers, " \t\r\n");
521 if (recipient != NULL && strcmp(header, recipient) == 0) {
522 *loop_detected_r = TRUE;
523 break;
524 }
525 if (user_email != NULL && strcmp(header, user_email) == 0) {
526 *loop_detected_r = TRUE;
527 break;
528 }
529 headers++;
530 }
531
532 return SIEVE_EXEC_OK;
533 }
534
535 static int
act_redirect_start(const struct sieve_action_exec_env * aenv,void ** tr_context)536 act_redirect_start(const struct sieve_action_exec_env *aenv, void **tr_context)
537 {
538 struct act_redirect_transaction *trans;
539 pool_t pool = sieve_result_pool(aenv->result);
540
541 /* Create transaction context */
542 trans = p_new(pool, struct act_redirect_transaction, 1);
543 *tr_context = trans;
544
545 return SIEVE_EXEC_OK;
546 }
547
548 static int
act_redirect_execute(const struct sieve_action_exec_env * aenv,void * tr_context,bool * keep)549 act_redirect_execute(const struct sieve_action_exec_env *aenv,
550 void *tr_context, bool *keep)
551 {
552 const struct sieve_action *action = aenv->action;
553 const struct sieve_execute_env *eenv = aenv->exec_env;
554 struct sieve_instance *svinst = eenv->svinst;
555 struct act_redirect_context *ctx =
556 (struct act_redirect_context *)action->context;
557 struct act_redirect_transaction *trans = tr_context;
558 struct sieve_message_context *msgctx = aenv->msgctx;
559 struct mail *mail = (action->mail != NULL ?
560 action->mail : sieve_message_get_mail(msgctx));
561 const struct sieve_message_data *msgdata = eenv->msgdata;
562 bool duplicate, loop_detected = FALSE;
563 int ret;
564
565 /*
566 * Prevent mail loops
567 */
568
569 /* Create Message-ID for the message if it has none */
570 trans->msg_id = msgdata->id;
571 if (trans->msg_id == NULL) {
572 pool_t pool = sieve_result_pool(aenv->result);
573 trans->msg_id = trans->new_msg_id =
574 p_strdup(pool, sieve_message_get_new_id(svinst));
575 }
576
577 /* Create ID for duplicate database lookup */
578 ret = act_redirect_get_duplicate_id(ctx, aenv, trans->msg_id,
579 &trans->dupeid);
580 if (ret != SIEVE_EXEC_OK)
581 return ret;
582 i_assert(trans->dupeid != NULL);
583
584 /* Check whether we've seen this message before */
585 ret = sieve_action_duplicate_check(aenv, trans->dupeid,
586 strlen(trans->dupeid),
587 &duplicate);
588 if (ret < SIEVE_EXEC_OK) {
589 sieve_result_critical(
590 aenv, "failed to check for duplicate forward",
591 "failed to check for duplicate forward to <%s>%s",
592 smtp_address_encode(ctx->to_address),
593 (ret == SIEVE_EXEC_TEMP_FAILURE ?
594 " (temporaty failure)" : ""));
595 return ret;
596 }
597 if (duplicate) {
598 sieve_result_global_log(
599 aenv, "discarded duplicate forward to <%s>",
600 smtp_address_encode(ctx->to_address));
601 trans->skip_redirect = TRUE;
602 return SIEVE_EXEC_OK;
603 }
604
605 /* Check whether we've seen this message before based on added headers
606 */
607 ret = act_redirect_check_loop_header(aenv, mail, &loop_detected);
608 if (ret != SIEVE_EXEC_OK)
609 return ret;
610 if (loop_detected) {
611 sieve_result_global_log(
612 aenv, "not forwarding message to <%s>: "
613 "the `x-sieve-redirected-from' header indicates a mail loop",
614 smtp_address_encode(ctx->to_address));
615 trans->skip_redirect = TRUE;
616 return SIEVE_EXEC_OK;
617 }
618
619 /* Cancel implicit keep */
620 *keep = FALSE;
621
622 return SIEVE_EXEC_OK;
623 }
624
625 static int
act_redirect_commit(const struct sieve_action_exec_env * aenv,void * tr_context)626 act_redirect_commit(const struct sieve_action_exec_env *aenv, void *tr_context)
627 {
628 const struct sieve_action *action = aenv->action;
629 const struct sieve_execute_env *eenv = aenv->exec_env;
630 struct sieve_instance *svinst = eenv->svinst;
631 struct act_redirect_context *ctx =
632 (struct act_redirect_context *)action->context;
633 struct sieve_message_context *msgctx = aenv->msgctx;
634 struct mail *mail = (action->mail != NULL ?
635 action->mail : sieve_message_get_mail(msgctx));
636 struct act_redirect_transaction *trans = tr_context;
637 int ret;
638
639 if (trans->skip_redirect)
640 return SIEVE_EXEC_OK;
641
642 /*
643 * Try to forward the message
644 */
645
646 ret = act_redirect_send(aenv, mail, ctx, trans->new_msg_id);
647 if (ret == SIEVE_EXEC_OK) {
648 /* Mark this message id as forwarded to the specified
649 destination */
650 sieve_action_duplicate_mark(
651 aenv, trans->dupeid, strlen(trans->dupeid),
652 ioloop_time + svinst->redirect_duplicate_period);
653
654 eenv->exec_status->significant_action_executed = TRUE;
655
656 struct event_passthrough *e =
657 sieve_action_create_finish_event(aenv)->
658 add_str("redirect_target",
659 smtp_address_encode(ctx->to_address));
660
661 sieve_result_event_log(aenv, e->event(),
662 "forwarded to <%s>",
663 smtp_address_encode(ctx->to_address));
664
665 /* Indicate that message was successfully forwarded */
666 eenv->exec_status->message_forwarded = TRUE;
667
668 return SIEVE_EXEC_OK;
669 }
670
671 return ret;
672 }
673