1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2  */
3 
4 #include "lib.h"
5 #include "str.h"
6 #include "array.h"
7 #include "home-expand.h"
8 #include "var-expand.h"
9 #include "eacces-error.h"
10 #include "smtp-address.h"
11 #include "smtp-submit.h"
12 #include "mail-storage.h"
13 #include "mail-deliver.h"
14 #include "mail-user.h"
15 #include "mail-duplicate.h"
16 #include "smtp-submit.h"
17 #include "mail-send.h"
18 #include "iostream-ssl.h"
19 #include "lda-settings.h"
20 
21 #include "sieve.h"
22 #include "sieve-script.h"
23 #include "sieve-storage.h"
24 
25 #include "lda-sieve-plugin.h"
26 
27 #include <sys/stat.h>
28 #include <dirent.h>
29 
30 /*
31  * Configuration
32  */
33 
34 #define LDA_SIEVE_DEFAULT_LOCATION "~/.dovecot.sieve"
35 
36 #define LDA_SIEVE_MAX_USER_ERRORS 30
37 
38 /*
39  * Global variables
40  */
41 
42 static deliver_mail_func_t *next_deliver_mail;
43 
44 /*
45  * Settings handling
46  */
47 
48 static const char *
lda_sieve_get_setting(void * context,const char * identifier)49 lda_sieve_get_setting(void *context, const char *identifier)
50 {
51 	struct mail_deliver_context *mdctx =
52 		(struct mail_deliver_context *)context;
53 	const char *value = NULL;
54 
55 	if (mdctx == NULL)
56 		return NULL;
57 
58 	if (mdctx->rcpt_user == NULL ||
59 	    (value = mail_user_plugin_getenv(
60 		mdctx->rcpt_user, identifier)) == NULL) {
61 		if (strcmp(identifier, "recipient_delimiter") == 0)
62 			value = mdctx->set->recipient_delimiter;
63 	}
64 
65 	return value;
66 }
67 
68 static const struct sieve_callbacks lda_sieve_callbacks = {
69 	NULL,
70 	lda_sieve_get_setting
71 };
72 
73 /*
74  * Mail transmission
75  */
76 
77 static void *
lda_sieve_smtp_start(const struct sieve_script_env * senv,const struct smtp_address * mail_from)78 lda_sieve_smtp_start(const struct sieve_script_env *senv,
79 		     const struct smtp_address *mail_from)
80 {
81 	struct mail_deliver_context *dctx =
82 		(struct mail_deliver_context *)senv->script_context;
83 	struct mail_user *user = dctx->rcpt_user;
84 	struct ssl_iostream_settings ssl_set;
85 	struct smtp_submit_input submit_input;
86 
87 	i_zero(&ssl_set);
88 	mail_user_init_ssl_client_settings(user, &ssl_set);
89 
90 	i_zero(&submit_input);
91 	submit_input.ssl = &ssl_set;
92 
93 	return (void *)smtp_submit_init_simple(&submit_input, dctx->smtp_set,
94 					       mail_from);
95 }
96 
97 static void
lda_sieve_smtp_add_rcpt(const struct sieve_script_env * senv ATTR_UNUSED,void * handle,const struct smtp_address * rcpt_to)98 lda_sieve_smtp_add_rcpt(const struct sieve_script_env *senv ATTR_UNUSED,
99 			void *handle, const struct smtp_address *rcpt_to)
100 {
101 	struct smtp_submit *smtp_submit = (struct smtp_submit *) handle;
102 
103 	smtp_submit_add_rcpt(smtp_submit, rcpt_to);
104 }
105 
106 static struct ostream *
lda_sieve_smtp_send(const struct sieve_script_env * senv ATTR_UNUSED,void * handle)107 lda_sieve_smtp_send(const struct sieve_script_env *senv ATTR_UNUSED,
108 		    void *handle)
109 {
110 	struct smtp_submit *smtp_submit = (struct smtp_submit *) handle;
111 
112 	return smtp_submit_send(smtp_submit);
113 }
114 
115 static void
lda_sieve_smtp_abort(const struct sieve_script_env * senv ATTR_UNUSED,void * handle)116 lda_sieve_smtp_abort(const struct sieve_script_env *senv ATTR_UNUSED,
117 		     void *handle)
118 {
119 	struct smtp_submit *smtp_submit = (struct smtp_submit *) handle;
120 
121 	smtp_submit_deinit(&smtp_submit);
122 }
123 
124 static int
lda_sieve_smtp_finish(const struct sieve_script_env * senv ATTR_UNUSED,void * handle,const char ** error_r)125 lda_sieve_smtp_finish(const struct sieve_script_env *senv ATTR_UNUSED,
126 		      void *handle, const char **error_r)
127 {
128 	struct smtp_submit *smtp_submit = (struct smtp_submit *) handle;
129 	int ret;
130 
131 	ret = smtp_submit_run(smtp_submit, error_r);
132 	smtp_submit_deinit(&smtp_submit);
133 	return ret;
134 }
135 
136 static int
lda_sieve_reject_mail(const struct sieve_script_env * senv,const struct smtp_address * recipient,const char * reason)137 lda_sieve_reject_mail(const struct sieve_script_env *senv,
138 		      const struct smtp_address *recipient,
139 		      const char *reason)
140 {
141 	struct mail_deliver_context *dctx =
142 		(struct mail_deliver_context *)senv->script_context;
143 
144 	return mail_send_rejection(dctx, recipient, reason);
145 }
146 
147 /*
148  * Duplicate checking
149  */
150 
151 static void *
lda_sieve_duplicate_transaction_begin(const struct sieve_script_env * senv)152 lda_sieve_duplicate_transaction_begin(const struct sieve_script_env *senv)
153 {
154 	struct mail_deliver_context *dctx =
155 		(struct mail_deliver_context *)senv->script_context;
156 
157 	return mail_duplicate_transaction_begin(dctx->dup_db);
158 }
159 
lda_sieve_duplicate_transaction_commit(void ** _dup_trans)160 static void lda_sieve_duplicate_transaction_commit(void **_dup_trans)
161 {
162 	struct mail_duplicate_transaction *dup_trans = *_dup_trans;
163 
164 	*_dup_trans = NULL;
165 	mail_duplicate_transaction_commit(&dup_trans);
166 }
167 
lda_sieve_duplicate_transaction_rollback(void ** _dup_trans)168 static void lda_sieve_duplicate_transaction_rollback(void **_dup_trans)
169 {
170 	struct mail_duplicate_transaction *dup_trans = *_dup_trans;
171 
172 	*_dup_trans = NULL;
173 	mail_duplicate_transaction_rollback(&dup_trans);
174 }
175 
176 static enum sieve_duplicate_check_result
lda_sieve_duplicate_check(void * _dup_trans,const struct sieve_script_env * senv,const void * id,size_t id_size)177 lda_sieve_duplicate_check(void *_dup_trans, const struct sieve_script_env *senv,
178 			  const void *id, size_t id_size)
179 {
180 	struct mail_duplicate_transaction *dup_trans = _dup_trans;
181 
182 	switch (mail_duplicate_check(dup_trans, id, id_size,
183 				     senv->user->username)) {
184 	case MAIL_DUPLICATE_CHECK_RESULT_EXISTS:
185 		return SIEVE_DUPLICATE_CHECK_RESULT_EXISTS;
186 	case MAIL_DUPLICATE_CHECK_RESULT_NOT_FOUND:
187 		return SIEVE_DUPLICATE_CHECK_RESULT_NOT_FOUND;
188 	case MAIL_DUPLICATE_CHECK_RESULT_DEADLOCK:
189 	case MAIL_DUPLICATE_CHECK_RESULT_LOCK_TIMEOUT:
190 		return SIEVE_DUPLICATE_CHECK_RESULT_TEMP_FAILURE;
191 	case MAIL_DUPLICATE_CHECK_RESULT_IO_ERROR:
192 	case MAIL_DUPLICATE_CHECK_RESULT_TOO_MANY_LOCKS:
193 		break;
194 	}
195 	return SIEVE_DUPLICATE_CHECK_RESULT_FAILURE;
196 }
197 
198 static void
lda_sieve_duplicate_mark(void * _dup_trans,const struct sieve_script_env * senv,const void * id,size_t id_size,time_t time)199 lda_sieve_duplicate_mark(void *_dup_trans, const struct sieve_script_env *senv,
200 			 const void *id, size_t id_size, time_t time)
201 {
202 	struct mail_duplicate_transaction *dup_trans = _dup_trans;
203 
204 	mail_duplicate_mark(dup_trans, id, id_size, senv->user->username, time);
205 }
206 
207 /*
208  * Result logging
209  */
210 
211 static const char *
lda_sieve_result_amend_log_message(const struct sieve_script_env * senv,enum log_type log_type ATTR_UNUSED,const char * message)212 lda_sieve_result_amend_log_message(const struct sieve_script_env *senv,
213 				   enum log_type log_type ATTR_UNUSED,
214 				   const char *message)
215 {
216 	struct mail_deliver_context *mdctx = senv->script_context;
217 	const struct var_expand_table *table;
218 	string_t *str;
219 	const char *error;
220 
221 	table = mail_deliver_ctx_get_log_var_expand_table(mdctx, message);
222 
223 	str = t_str_new(256);
224 	if (var_expand(str, mdctx->set->deliver_log_format,
225 		       table, &error) <= 0) {
226 		e_error(mdctx->event,
227 			"Failed to expand deliver_log_format=%s: %s",
228 			mdctx->set->deliver_log_format, error);
229 	}
230 	return str_c(str);
231 }
232 
233 /*
234  * Plugin implementation
235  */
236 
237 struct lda_sieve_run_context {
238 	struct sieve_instance *svinst;
239 
240 	struct mail_deliver_context *mdctx;
241 	const char *home_dir;
242 
243 	struct sieve_script **scripts;
244 	unsigned int script_count;
245 
246 	struct sieve_script *user_script;
247 	struct sieve_script *main_script;
248 	struct sieve_script *discard_script;
249 
250 	const struct sieve_message_data *msgdata;
251 	const struct sieve_script_env *scriptenv;
252 
253 	struct sieve_error_handler *user_ehandler;
254 	struct sieve_error_handler *master_ehandler;
255 	struct sieve_error_handler *action_ehandler;
256 	const char *userlog;
257 };
258 
259 static int
lda_sieve_get_personal_storage(struct sieve_instance * svinst,struct mail_user * user,struct sieve_storage ** storage_r,enum sieve_error * error_r)260 lda_sieve_get_personal_storage(struct sieve_instance *svinst,
261 			       struct mail_user *user,
262 			       struct sieve_storage **storage_r,
263 			       enum sieve_error *error_r)
264 {
265 	*storage_r = sieve_storage_create_main(svinst, user, 0, error_r);
266 	if (*storage_r == NULL) {
267 		switch (*error_r) {
268 		case SIEVE_ERROR_NOT_POSSIBLE:
269 		case SIEVE_ERROR_NOT_FOUND:
270 			break;
271 		case SIEVE_ERROR_TEMP_FAILURE:
272 			e_error(sieve_get_event(svinst),
273 				"Failed to access user's personal storage "
274 				"(temporary failure)");
275 			return -1;
276 		default:
277 			e_error(sieve_get_event(svinst),
278 				"Failed to access user's personal storage");
279 			break;
280 		}
281 		return 0;
282 	}
283 	return 1;
284 }
285 
286 static int
lda_sieve_multiscript_get_scripts(struct sieve_instance * svinst,const char * label,const char * location,ARRAY_TYPE (sieve_script)* scripts,enum sieve_error * error_r)287 lda_sieve_multiscript_get_scripts(struct sieve_instance *svinst,
288 				  const char *label, const char *location,
289 				  ARRAY_TYPE(sieve_script) *scripts,
290 				  enum sieve_error *error_r)
291 {
292 	struct sieve_script_sequence *seq;
293 	struct sieve_script *script;
294 	bool finished = FALSE;
295 	int ret = 1;
296 
297 	seq = sieve_script_sequence_create(svinst, location, error_r);
298 	if (seq == NULL)
299 		return (*error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1);
300 
301 	while (ret > 0 && !finished) {
302 		script = sieve_script_sequence_next(seq, error_r);
303 		if (script == NULL) {
304 			switch (*error_r) {
305 			case SIEVE_ERROR_NONE:
306 				finished = TRUE;
307 				break;
308 			case SIEVE_ERROR_TEMP_FAILURE:
309 				e_error(sieve_get_event(svinst),
310 					"Failed to access %s script from `%s' "
311 					"(temporary failure)",
312 					label, location);
313 				ret = -1;
314 			default:
315 				break;
316 			}
317 			continue;
318 		}
319 
320 		array_append(scripts, &script, 1);
321 	}
322 
323 	sieve_script_sequence_free(&seq);
324 	return ret;
325 }
326 
327 static void
lda_sieve_binary_save(struct lda_sieve_run_context * srctx,struct sieve_binary * sbin,struct sieve_script * script)328 lda_sieve_binary_save(struct lda_sieve_run_context *srctx,
329 		      struct sieve_binary *sbin, struct sieve_script *script)
330 {
331 	enum sieve_error error;
332 
333 	/* Save binary when compiled */
334 	if (sieve_save(sbin, FALSE, &error) < 0 &&
335 	    error == SIEVE_ERROR_NO_PERMISSION &&
336 	    script != srctx->user_script) {
337 		/* Cannot save binary for global script */
338 		e_error(sieve_get_event(srctx->svinst),
339 			"The LDA Sieve plugin does not have permission "
340 			"to save global Sieve script binaries; "
341 			"global Sieve scripts like `%s' need to be "
342 			"pre-compiled using the sievec tool",
343 			sieve_script_location(script));
344 	}
345 }
346 
347 static struct
lda_sieve_open(struct lda_sieve_run_context * srctx,struct sieve_script * script,enum sieve_compile_flags cpflags,bool recompile,enum sieve_error * error_r)348 sieve_binary *lda_sieve_open(struct lda_sieve_run_context *srctx,
349 			     struct sieve_script *script,
350 			     enum sieve_compile_flags cpflags, bool recompile,
351 			     enum sieve_error *error_r)
352 {
353 	struct sieve_instance *svinst = srctx->svinst;
354 	struct sieve_error_handler *ehandler;
355 	struct sieve_binary *sbin;
356 	const char *compile_name = "compile";
357 
358 	if (recompile) {
359 		/* Warn */
360 		e_warning(sieve_get_event(svinst),
361 			  "Encountered corrupt binary: re-compiling script %s",
362 			  sieve_script_location(script));
363 		compile_name = "re-compile";
364 	} else {
365 		e_debug(sieve_get_event(svinst),
366 			"Loading script %s", sieve_script_location(script));
367 	}
368 
369 	if (script == srctx->user_script)
370 		ehandler = srctx->user_ehandler;
371 	else
372 		ehandler = srctx->master_ehandler;
373 
374 	sieve_error_handler_reset(ehandler);
375 
376 	if (recompile)
377 		sbin = sieve_compile_script(script, ehandler, cpflags, error_r);
378 	else
379 		sbin = sieve_open_script(script, ehandler, cpflags, error_r);
380 
381 	/* Load or compile the sieve script */
382 	if (sbin == NULL) {
383 		switch (*error_r) {
384 		/* Script not found */
385 		case SIEVE_ERROR_NOT_FOUND:
386 			e_debug(sieve_get_event(svinst),
387 				"Script `%s' is missing for %s",
388 				sieve_script_location(script),
389 				compile_name);
390 			break;
391 		/* Temporary failure */
392 		case SIEVE_ERROR_TEMP_FAILURE:
393 			e_error(sieve_get_event(svinst),
394 				"Failed to open script `%s' for %s "
395 				"(temporary failure)",
396 				sieve_script_location(script), compile_name);
397 			break;
398 		/* Compile failed */
399 		case SIEVE_ERROR_NOT_VALID:
400 			if (script == srctx->user_script &&
401 			    srctx->userlog != NULL ) {
402 				e_info(sieve_get_event(svinst),
403 				       "Failed to %s script `%s' "
404 				       "(view user logfile `%s' for more information)",
405 				       compile_name,
406 				       sieve_script_location(script),
407 				       srctx->userlog);
408 				break;
409 			}
410 			e_error(sieve_get_event(svinst),
411 				"Failed to %s script `%s'",
412 				compile_name, sieve_script_location(script));
413 			break;
414 		/* Cumulative resource limit exceeded */
415 		case SIEVE_ERROR_RESOURCE_LIMIT:
416 			e_error(sieve_get_event(svinst),
417 				"Failed to open script `%s' for %s "
418 				"(cumulative resource limit exceeded)",
419 				sieve_script_location(script), compile_name);
420 			break;
421 		/* Something else */
422 		default:
423 			e_error(sieve_get_event(svinst),
424 				"Failed to open script `%s' for %s",
425 				sieve_script_location(script), compile_name);
426 			break;
427 		}
428 
429 		return NULL;
430 	}
431 
432 	if (!recompile)
433 		lda_sieve_binary_save(srctx, sbin, script);
434 	return sbin;
435 }
436 
437 static int
lda_sieve_handle_exec_status(struct lda_sieve_run_context * srctx,struct sieve_script * script,int status)438 lda_sieve_handle_exec_status(struct lda_sieve_run_context *srctx,
439 			     struct sieve_script *script, int status)
440 {
441 	struct sieve_instance *svinst = srctx->svinst;
442 	struct mail_deliver_context *mdctx = srctx->mdctx;
443 	struct sieve_exec_status *estatus = srctx->scriptenv->exec_status;
444 	const char *userlog_notice = "";
445 	enum log_type log_level, user_log_level;
446 	enum mail_error mail_error = MAIL_ERROR_NONE;
447 	int ret;
448 
449 	log_level = user_log_level = LOG_TYPE_ERROR;
450 
451 	if (estatus != NULL && estatus->last_storage != NULL &&
452 	    estatus->store_failed) {
453 		mail_storage_get_last_error(estatus->last_storage, &mail_error);
454 
455 		/* Don't bother administrator too much with benign errors */
456 		if (mail_error == MAIL_ERROR_NOQUOTA) {
457 			log_level = LOG_TYPE_INFO;
458 			user_log_level = LOG_TYPE_INFO;
459 		}
460 	}
461 
462 	if (script == srctx->user_script && srctx->userlog != NULL) {
463 		userlog_notice = t_strdup_printf(
464 			" (user logfile %s may reveal additional details)",
465 			srctx->userlog);
466 		user_log_level = LOG_TYPE_INFO;
467 	}
468 
469 	switch (status) {
470 	case SIEVE_EXEC_FAILURE:
471 		e_log(sieve_get_event(svinst), user_log_level,
472 		      "Execution of script %s failed, "
473 		      "but implicit keep was successful%s",
474 		      sieve_script_location(script), userlog_notice);
475 		ret = 1;
476 		break;
477 	case SIEVE_EXEC_TEMP_FAILURE:
478 		e_log(sieve_get_event(svinst), log_level,
479 		      "Execution of script %s was aborted due to temporary failure%s",
480 		      sieve_script_location(script), userlog_notice);
481 		if (mail_error != MAIL_ERROR_TEMP &&
482 		    mdctx->tempfail_error == NULL) {
483 			mdctx->tempfail_error =
484 				"Execution of Sieve filters was aborted due to temporary failure";
485 		}
486 		ret = -1;
487 		break;
488 	case SIEVE_EXEC_BIN_CORRUPT:
489 		e_error(sieve_get_event(svinst),
490 			"!!BUG!!: Binary compiled from %s is still corrupt; "
491 			"bailing out and reverting to default delivery",
492 			sieve_script_location(script));
493 		ret = -1;
494 		break;
495 	case SIEVE_EXEC_RESOURCE_LIMIT:
496 		e_error(sieve_get_event(svinst),
497 			"Execution of script %s was aborted "
498 			"due to excessive resource usage",
499 			sieve_script_location(script));
500 		ret = -1;
501 		break;
502 	case SIEVE_EXEC_KEEP_FAILED:
503 		e_log(sieve_get_event(svinst), log_level,
504 		      "Execution of script %s failed with unsuccessful implicit keep%s",
505 		      sieve_script_location(script), userlog_notice);
506 		ret = -1;
507 		break;
508 	default:
509 		ret = status > 0 ? 1 : -1;
510 		break;
511 	}
512 
513 	return ret;
514 }
515 
516 static int
lda_sieve_execute_script(struct lda_sieve_run_context * srctx,struct sieve_multiscript * mscript,struct sieve_script * script,unsigned int index,bool discard_script,enum sieve_error * error_r)517 lda_sieve_execute_script(struct lda_sieve_run_context *srctx,
518 			 struct sieve_multiscript *mscript,
519 			 struct sieve_script *script,
520 			 unsigned int index, bool discard_script,
521 			 enum sieve_error *error_r)
522 {
523 	struct sieve_instance *svinst = srctx->svinst;
524 	struct sieve_error_handler *exec_ehandler;
525 	struct sieve_binary *sbin = NULL;
526 	enum sieve_compile_flags cpflags = 0;
527 	enum sieve_execute_flags exflags = SIEVE_EXECUTE_FLAG_LOG_RESULT;
528 	struct sieve_resource_usage *rusage =
529 		&srctx->scriptenv->exec_status->resource_usage;
530 	bool user_script;
531 	int mstatus, ret;
532 
533 	*error_r = SIEVE_ERROR_NONE;
534 
535 	user_script = (script == srctx->user_script);
536 
537 	sieve_resource_usage_init(rusage);
538 	if (user_script) {
539 		cpflags |= SIEVE_COMPILE_FLAG_NOGLOBAL;
540 		exflags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
541 		exec_ehandler = srctx->user_ehandler;
542 	} else {
543 		exec_ehandler = srctx->master_ehandler;
544 	}
545 
546 	/* Open */
547 
548 	if (!discard_script) {
549 		e_debug(sieve_get_event(svinst),
550 			"Opening script %d of %d from `%s'",
551 			index, srctx->script_count,
552 			sieve_script_location(script));
553 	} else {
554 		e_debug(sieve_get_event(svinst),
555 			"Opening discard script from `%s'",
556 			sieve_script_location(script));
557 	}
558 
559 	sbin = lda_sieve_open(srctx, script, cpflags, FALSE, error_r);
560 	if (sbin == NULL)
561 		return 0;
562 
563 	/* Execute */
564 
565 	e_debug(sieve_get_event(svinst),
566 		"Executing script from `%s'",
567 		sieve_get_source(sbin));
568 
569 	if (!discard_script) {
570 		ret = (sieve_multiscript_run(mscript, sbin, exec_ehandler,
571 					     exec_ehandler, exflags) ? 1 : 0);
572 	} else {
573 		sieve_multiscript_run_discard(mscript, sbin, exec_ehandler,
574 					      exec_ehandler, exflags);
575 		ret = 0;
576 	}
577 
578 	mstatus = sieve_multiscript_status(mscript);
579 	if (ret == 0 && mstatus == SIEVE_EXEC_BIN_CORRUPT &&
580 	    sieve_is_loaded(sbin)) {
581 		/* Close corrupt script */
582 
583 		sieve_close(&sbin);
584 
585 		/* Recompile */
586 
587 		sbin = lda_sieve_open(srctx, script, cpflags, TRUE,
588 				      error_r);
589 		if (sbin == NULL)
590 			return 0;
591 
592 		/* Execute again */
593 
594 		if (!discard_script) {
595 			ret = (sieve_multiscript_run(
596 				mscript, sbin, exec_ehandler,
597 				exec_ehandler, exflags) ? 1 : 0);
598 		} else {
599 			sieve_multiscript_run_discard(
600 				mscript, sbin, exec_ehandler,
601 				exec_ehandler, exflags);
602 		}
603 
604 		/* Save new version */
605 
606 		mstatus = sieve_multiscript_status(mscript);
607 		if (mstatus != SIEVE_EXEC_BIN_CORRUPT)
608 			lda_sieve_binary_save(srctx, sbin, script);
609 	}
610 	if (ret == 0 && mstatus == SIEVE_EXEC_RESOURCE_LIMIT)
611 		ret = -1;
612 
613 	if (user_script)
614 		(void)sieve_record_resource_usage(sbin, rusage);
615 
616 	sieve_close(&sbin);
617 
618 	return ret;
619 }
620 
lda_sieve_execute_scripts(struct lda_sieve_run_context * srctx)621 static int lda_sieve_execute_scripts(struct lda_sieve_run_context *srctx)
622 {
623 	struct sieve_instance *svinst = srctx->svinst;
624 	struct sieve_multiscript *mscript;
625 	struct sieve_error_handler *exec_ehandler;
626 	struct sieve_script *script, *last_script = NULL;
627 	enum sieve_execute_flags exflags = SIEVE_EXECUTE_FLAG_LOG_RESULT;
628 	bool discard_script;
629 	enum sieve_error error;
630 	unsigned int i;
631 	int ret;
632 
633 	i_assert(srctx->script_count > 0);
634 
635 	/* Start execution */
636 
637 	mscript = sieve_multiscript_start_execute(svinst, srctx->msgdata,
638 						  srctx->scriptenv);
639 
640 	/* Execute scripts */
641 
642 	i = 0;
643 	discard_script = FALSE;
644 	error = SIEVE_ERROR_NONE;
645 	for (;;) {
646 		if (!discard_script) {
647 			/* normal script sequence */
648 			i_assert(i < srctx->script_count);
649 			script = srctx->scripts[i];
650 			i++;
651 		} else {
652 			/* discard script */
653 			script = srctx->discard_script;
654 		}
655 
656 		i_assert(script != NULL);
657 		last_script = script;
658 
659 		ret = lda_sieve_execute_script(srctx, mscript, script, i,
660 					       discard_script, &error);
661 		if (ret < 0)
662 			break;
663 		if (error == SIEVE_ERROR_NOT_FOUND) {
664 			/* skip scripts which finally turn out not to exist */
665 			ret = 1;
666 		}
667 
668 		if (discard_script) {
669 			/* Executed discard script, which is always final */
670 			break;
671 		} else if (ret > 0) {
672 			/* The "keep" action is applied; execute next script */
673 			i_assert(i <= srctx->script_count);
674 			if (i == srctx->script_count) {
675 				/* End of normal script sequence */
676 				break;
677 			}
678 		} else if (error != SIEVE_ERROR_NONE) {
679 			break;
680 		} else if (sieve_multiscript_will_discard(mscript) &&
681 			   srctx->discard_script != NULL) {
682 			/* Mail is set to be discarded, but we have a discard script. */
683 			discard_script = TRUE;
684 		} else {
685 			break;
686 		}
687 	}
688 
689 	/* Finish execution */
690 	exec_ehandler = (srctx->user_ehandler != NULL ?
691 			 srctx->user_ehandler : srctx->master_ehandler);
692 	ret = sieve_multiscript_finish(&mscript, exec_ehandler, exflags,
693 				       (error == SIEVE_ERROR_TEMP_FAILURE ?
694 					SIEVE_EXEC_TEMP_FAILURE :
695 					SIEVE_EXEC_OK));
696 
697 	/* Don't log additional messages about compile failure */
698 	if (error != SIEVE_ERROR_NONE && ret == SIEVE_EXEC_FAILURE) {
699 		e_info(sieve_get_event(svinst),
700 		       "Aborted script execution sequence with successful implicit keep");
701 		return 1;
702 	}
703 
704 	return lda_sieve_handle_exec_status(srctx, last_script, ret);
705 }
706 
lda_sieve_find_scripts(struct lda_sieve_run_context * srctx)707 static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
708 {
709 	struct mail_deliver_context *mdctx = srctx->mdctx;
710 	struct sieve_instance *svinst = srctx->svinst;
711 	struct sieve_storage *main_storage;
712 	const char *sieve_before, *sieve_after, *sieve_discard;
713 	const char *setting_name;
714 	enum sieve_error error;
715 	ARRAY_TYPE(sieve_script) script_sequence;
716 	struct sieve_script *const *scripts;
717 	unsigned int after_index, count, i;
718 	int ret = 1;
719 
720 	/* Find the personal script to execute */
721 
722 	ret = lda_sieve_get_personal_storage(svinst, mdctx->rcpt_user,
723 					     &main_storage, &error);
724 	if (ret == 0 && error == SIEVE_ERROR_NOT_POSSIBLE)
725 		return 0;
726 	if (ret > 0) {
727 		srctx->main_script =
728 			sieve_storage_active_script_open(main_storage, &error);
729 
730 		if (srctx->main_script == NULL) {
731 			switch (error) {
732 			case SIEVE_ERROR_NOT_FOUND:
733 				e_debug(sieve_get_event(svinst),
734 					"User has no active script in storage `%s'",
735 					sieve_storage_location(main_storage));
736 				break;
737 			case SIEVE_ERROR_TEMP_FAILURE:
738 				e_error(sieve_get_event(svinst),
739 					"Failed to access active Sieve script in user storage `%s' "
740 					"(temporary failure)",
741 					sieve_storage_location(main_storage));
742 				ret = -1;
743 				break;
744 			default:
745 				e_error(sieve_get_event(svinst),
746 					"Failed to access active Sieve script in user storage `%s'",
747 					sieve_storage_location(main_storage));
748 				break;
749 			}
750 		} else if (!sieve_script_is_default(srctx->main_script)) {
751 			srctx->user_script = srctx->main_script;
752 		}
753 		sieve_storage_unref(&main_storage);
754 	}
755 
756 	if (ret >= 0 && srctx->main_script == NULL) {
757 		e_debug(sieve_get_event(svinst),
758 			"User has no personal script");
759 	}
760 
761 	/* Compose script array */
762 
763 	t_array_init(&script_sequence, 16);
764 
765 	/* before */
766 	if (ret >= 0) {
767 		i = 2;
768 		setting_name = "sieve_before";
769 		sieve_before = mail_user_plugin_getenv(
770 			mdctx->rcpt_user, setting_name);
771 		while (ret >= 0 &&
772 		       sieve_before != NULL && *sieve_before != '\0') {
773 			ret = lda_sieve_multiscript_get_scripts(
774 				svinst, setting_name, sieve_before,
775 				&script_sequence, &error);
776 			if (ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE) {
777 				ret = -1;
778 				break;
779 			} else if (ret == 0) {
780 				e_debug(sieve_get_event(svinst),
781 					"Location for %s not found: %s",
782 					setting_name, sieve_before);
783 			}
784 			ret = 0;
785 			setting_name = t_strdup_printf("sieve_before%u", i++);
786 			sieve_before = mail_user_plugin_getenv(
787 				mdctx->rcpt_user, setting_name);
788 		}
789 
790 		if (ret >= 0) {
791 			scripts = array_get(&script_sequence, &count);
792 			for (i = 0; i < count; i ++) {
793 				e_debug(sieve_get_event(svinst),
794 					"Executed before user's personal Sieve script(%d): %s",
795 					i+1, sieve_script_location(scripts[i]));
796 			}
797 		}
798 	}
799 
800 	/* main */
801 	if (srctx->main_script != NULL) {
802 		array_append(&script_sequence, &srctx->main_script, 1);
803 
804 		if (ret >= 0) {
805 			e_debug(sieve_get_event(svinst),
806 				"Using the following location for user's Sieve script: %s",
807 				sieve_script_location(srctx->main_script));
808 		}
809 	}
810 
811 	after_index = array_count(&script_sequence);
812 
813 	/* after */
814 	if (ret >= 0) {
815 		i = 2;
816 		setting_name = "sieve_after";
817 		sieve_after = mail_user_plugin_getenv(mdctx->rcpt_user, setting_name);
818 		while (sieve_after != NULL && *sieve_after != '\0') {
819 			ret = lda_sieve_multiscript_get_scripts(
820 				svinst, setting_name, sieve_after,
821 				&script_sequence, &error);
822 			if (ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE) {
823 				ret = -1;
824 				break;
825 			} else if (ret == 0) {
826 				e_debug(sieve_get_event(svinst),
827 					"Location for %s not found: %s",
828 					setting_name, sieve_after);
829 			}
830 			ret = 0;
831 			setting_name = t_strdup_printf("sieve_after%u", i++);
832 			sieve_after = mail_user_plugin_getenv(
833 				mdctx->rcpt_user, setting_name);
834 		}
835 
836 		if (ret >= 0) {
837 			scripts = array_get(&script_sequence, &count);
838 			for ( i = after_index; i < count; i ++ ) {
839 				e_debug(sieve_get_event(svinst),
840 					"executed after user's Sieve script(%d): %s",
841 					i+1, sieve_script_location(scripts[i]));
842 			}
843 		}
844 	}
845 
846 	/* discard */
847 	sieve_discard = mail_user_plugin_getenv(
848 		mdctx->rcpt_user, "sieve_discard");
849 	if (sieve_discard != NULL && *sieve_discard != '\0') {
850 		srctx->discard_script = sieve_script_create_open(
851 			svinst, sieve_discard, NULL, &error);
852 		if (srctx->discard_script == NULL) {
853 			switch (error) {
854 			case SIEVE_ERROR_NOT_FOUND:
855 				e_debug(sieve_get_event(svinst),
856 					"Location for sieve_discard not found: %s",
857 					sieve_discard);
858 				break;
859 			case SIEVE_ERROR_TEMP_FAILURE:
860 				ret = -1;
861 				break;
862 			default:
863 				break;
864 			}
865 		}
866 	}
867 
868 	if (ret < 0) {
869 		mdctx->tempfail_error =
870 			"Temporarily unable to access necessary Sieve scripts";
871 	}
872 	srctx->scripts =
873 		array_get_modifiable(&script_sequence, &srctx->script_count);
874 	return ret;
875 }
876 
877 static void
lda_sieve_free_scripts(struct lda_sieve_run_context * srctx)878 lda_sieve_free_scripts(struct lda_sieve_run_context *srctx)
879 {
880 	unsigned int i;
881 
882 	for (i = 0; i < srctx->script_count; i++)
883 		sieve_script_unref(&srctx->scripts[i]);
884 	if (srctx->discard_script != NULL)
885 		sieve_script_unref(&srctx->discard_script);
886 }
887 
888 static void
lda_sieve_init_trace_log(struct lda_sieve_run_context * srctx,const struct smtp_address * mail_from,struct sieve_trace_config * trace_config_r,struct sieve_trace_log ** trace_log_r)889 lda_sieve_init_trace_log(struct lda_sieve_run_context *srctx,
890 			 const struct smtp_address *mail_from,
891 			 struct sieve_trace_config *trace_config_r,
892 			 struct sieve_trace_log **trace_log_r)
893 {
894 	struct mail_deliver_context *mdctx = srctx->mdctx;
895 	struct sieve_instance *svinst = srctx->svinst;
896 	struct sieve_trace_log *trace_log = NULL;
897 
898 	if (sieve_trace_config_get(svinst, trace_config_r) < 0 ||
899 	    sieve_trace_log_open(svinst, &trace_log) < 0) {
900 		i_zero(trace_config_r);
901 		*trace_log_r = NULL;
902 		return;
903 	}
904 
905 	/* Write header for trace file */
906 	sieve_trace_log_printf(trace_log,
907 		"Sieve trace log for message delivery:\n"
908 		"\n"
909 		"  Username: %s\n", mdctx->rcpt_user->username);
910 	if (mdctx->rcpt_user->session_id != NULL) {
911 		sieve_trace_log_printf(trace_log,
912 			"  Session ID: %s\n",
913 			mdctx->rcpt_user->session_id);
914 	}
915 	sieve_trace_log_printf(trace_log,
916 		"  Sender: %s\n"
917 		"  Final recipient: %s\n"
918 		"  Default mailbox: %s\n\n",
919 		smtp_address_encode_path(mail_from),
920 		smtp_address_encode_path(mdctx->rcpt_to),
921 		(mdctx->rcpt_default_mailbox != NULL ?
922 		 mdctx->rcpt_default_mailbox : "INBOX"));
923 
924 	*trace_log_r = trace_log;
925 }
926 
927 static int
lda_sieve_execute(struct lda_sieve_run_context * srctx,struct mail_storage ** storage_r)928 lda_sieve_execute(struct lda_sieve_run_context *srctx,
929 		  struct mail_storage **storage_r)
930 {
931 	struct mail_deliver_context *mdctx = srctx->mdctx;
932 	struct sieve_instance *svinst = srctx->svinst;
933 	struct sieve_message_data msgdata;
934 	struct sieve_script_env scriptenv;
935 	struct sieve_exec_status estatus;
936 	struct sieve_trace_config trace_config;
937 	const struct smtp_address *mail_from;
938 	struct sieve_trace_log *trace_log;
939 	const char *error;
940 	int ret;
941 
942 	/* Check whether there are any scripts to execute at all */
943 
944 	if (srctx->script_count == 0) {
945 		e_debug(sieve_get_event(svinst),
946 			"No scripts to execute: "
947 			"reverting to default delivery.");
948 
949 		/* No error, but no delivery by this plugin either. A return
950 		   value of <= 0 for a deliver plugin is is considered a
951 		   failure. In deliver itself, saved_mail and tried_default_save
952 		   remain unset, meaning that deliver will then attempt the
953 		   default delivery. We return 0 to signify the lack of a real
954 		   error.
955 		 */
956 		return 0;
957 	}
958 
959 	/* Initialize user error handler */
960 
961 	if (srctx->user_script != NULL) {
962 		const char *log_path =
963 			sieve_user_get_log_path(svinst, srctx->user_script);
964 
965 		if (log_path != NULL) {
966 			srctx->userlog = log_path;
967 			srctx->user_ehandler = sieve_logfile_ehandler_create(
968 				svinst, srctx->userlog,
969 				LDA_SIEVE_MAX_USER_ERRORS);
970 		}
971 	}
972 
973 	/* Determine return address */
974 
975 	mail_from = mail_deliver_get_return_address(mdctx);
976 
977 	/* Initialize trace logging */
978 
979 	lda_sieve_init_trace_log(srctx, mail_from, &trace_config, &trace_log);
980 
981 	/* Collect necessary message data */
982 
983 	i_zero(&msgdata);
984 
985 	msgdata.mail = mdctx->src_mail;
986 	msgdata.auth_user = mdctx->rcpt_user->username;
987 	msgdata.envelope.mail_from = mail_from;
988 	msgdata.envelope.mail_params = &mdctx->mail_params;
989 	msgdata.envelope.rcpt_to = mdctx->rcpt_to;
990 	msgdata.envelope.rcpt_params = &mdctx->rcpt_params;
991 	(void)mail_get_message_id(msgdata.mail, &msgdata.id);
992 
993 	srctx->msgdata = &msgdata;
994 
995 	/* Compose script execution environment */
996 
997 	if (sieve_script_env_init(&scriptenv, mdctx->rcpt_user, &error) < 0) {
998 		e_error(sieve_get_event(svinst),
999 			"Failed to initialize script execution: %s", error);
1000 		if (trace_log != NULL)
1001 			sieve_trace_log_free(&trace_log);
1002 		return -1;
1003 	}
1004 
1005 	scriptenv.default_mailbox = mdctx->rcpt_default_mailbox;
1006 	scriptenv.mailbox_autocreate = mdctx->set->lda_mailbox_autocreate;
1007 	scriptenv.mailbox_autosubscribe = mdctx->set->lda_mailbox_autosubscribe;
1008 	scriptenv.smtp_start = lda_sieve_smtp_start;
1009 	scriptenv.smtp_add_rcpt = lda_sieve_smtp_add_rcpt;
1010 	scriptenv.smtp_send = lda_sieve_smtp_send;
1011 	scriptenv.smtp_abort = lda_sieve_smtp_abort;
1012 	scriptenv.smtp_finish = lda_sieve_smtp_finish;
1013 	scriptenv.duplicate_transaction_begin =
1014 		lda_sieve_duplicate_transaction_begin;
1015 	scriptenv.duplicate_transaction_commit =
1016 		lda_sieve_duplicate_transaction_commit;
1017 	scriptenv.duplicate_transaction_rollback =
1018 		lda_sieve_duplicate_transaction_rollback;
1019 	scriptenv.duplicate_mark = lda_sieve_duplicate_mark;
1020 	scriptenv.duplicate_check = lda_sieve_duplicate_check;
1021 	scriptenv.reject_mail = lda_sieve_reject_mail;
1022 	scriptenv.result_amend_log_message = lda_sieve_result_amend_log_message;
1023 	scriptenv.script_context = (void *) mdctx;
1024 	scriptenv.trace_log = trace_log;
1025 	scriptenv.trace_config = trace_config;
1026 
1027 	i_zero(&estatus);
1028 	scriptenv.exec_status = &estatus;
1029 
1030 	srctx->scriptenv = &scriptenv;
1031 
1032 	/* Execute script(s) */
1033 
1034 	ret = lda_sieve_execute_scripts(srctx);
1035 
1036 	/* Record status */
1037 
1038 	mdctx->tried_default_save = estatus.tried_default_save;
1039 	*storage_r = estatus.last_storage;
1040 
1041 	if (trace_log != NULL)
1042 		sieve_trace_log_free(&trace_log);
1043 
1044 	return ret;
1045 }
1046 
1047 static int
lda_sieve_deliver_mail(struct mail_deliver_context * mdctx,struct mail_storage ** storage_r)1048 lda_sieve_deliver_mail(struct mail_deliver_context *mdctx,
1049 		       struct mail_storage **storage_r)
1050 {
1051 	struct lda_sieve_run_context srctx;
1052 	const struct mail_storage_settings *mail_set =
1053 		mail_user_set_get_storage_set(mdctx->rcpt_user);
1054 	bool debug = mdctx->rcpt_user->mail_debug;
1055 	struct sieve_environment svenv;
1056 	int ret = 0;
1057 
1058 	/* Initialize run context */
1059 
1060 	i_zero(&srctx);
1061 	srctx.mdctx = mdctx;
1062 	(void)mail_user_get_home(mdctx->rcpt_user, &srctx.home_dir);
1063 
1064 	/* Initialize Sieve engine */
1065 
1066 	memset((void*)&svenv, 0, sizeof(svenv));
1067 	svenv.username = mdctx->rcpt_user->username;
1068 	svenv.home_dir = srctx.home_dir;
1069 	svenv.hostname = mail_set->hostname;
1070 	svenv.base_dir = mdctx->rcpt_user->set->base_dir;
1071 	svenv.temp_dir = mdctx->rcpt_user->set->mail_temp_dir;
1072 	svenv.event_parent = mdctx->event;
1073 	svenv.flags = SIEVE_FLAG_HOME_RELATIVE;
1074 	svenv.location = SIEVE_ENV_LOCATION_MDA;
1075 	svenv.delivery_phase = SIEVE_DELIVERY_PHASE_DURING;
1076 
1077 	srctx.svinst = sieve_init(&svenv, &lda_sieve_callbacks, mdctx, debug);
1078 
1079 	/* Initialize master error handler */
1080 
1081 	srctx.master_ehandler = sieve_master_ehandler_create(srctx.svinst, 0);
1082 
1083 	sieve_error_handler_accept_infolog(srctx.master_ehandler, TRUE);
1084 	sieve_error_handler_accept_debuglog(srctx.master_ehandler, debug);
1085 
1086 	*storage_r = NULL;
1087 
1088 	/* Find Sieve scripts and run them */
1089 
1090 	T_BEGIN {
1091 		if (lda_sieve_find_scripts(&srctx) < 0)
1092 			ret = -1;
1093 		else if (srctx.scripts == NULL)
1094 			ret = 0;
1095 		else
1096 			ret = lda_sieve_execute(&srctx, storage_r);
1097 
1098 		lda_sieve_free_scripts(&srctx);
1099 	} T_END;
1100 
1101 	/* Clean up */
1102 
1103 	if (srctx.user_ehandler != NULL)
1104 		sieve_error_handler_unref(&srctx.user_ehandler);
1105 	sieve_error_handler_unref(&srctx.master_ehandler);
1106 	sieve_deinit(&srctx.svinst);
1107 
1108 	return ret;
1109 }
1110 
1111 /*
1112  * Plugin interface
1113  */
1114 
1115 const char *sieve_plugin_version = DOVECOT_ABI_VERSION;
1116 const char sieve_plugin_binary_dependency[] = "lda lmtp";
1117 
sieve_plugin_init(void)1118 void sieve_plugin_init(void)
1119 {
1120 	/* Hook into the delivery process */
1121 	next_deliver_mail = mail_deliver_hook_set(lda_sieve_deliver_mail);
1122 }
1123 
sieve_plugin_deinit(void)1124 void sieve_plugin_deinit(void)
1125 {
1126 	/* Remove hook */
1127 	mail_deliver_hook_set(next_deliver_mail);
1128 }
1129