1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2 */
3
4 #include "lib.h"
5 #include "str.h"
6 #include "istream.h"
7 #include "ostream.h"
8 #include "buffer.h"
9 #include "time-util.h"
10 #include "eacces-error.h"
11 #include "home-expand.h"
12 #include "hostpid.h"
13 #include "message-address.h"
14 #include "mail-user.h"
15
16 #include "sieve-settings.h"
17 #include "sieve-extensions.h"
18 #include "sieve-plugins.h"
19
20 #include "sieve-address.h"
21 #include "sieve-script.h"
22 #include "sieve-storage-private.h"
23 #include "sieve-ast.h"
24 #include "sieve-binary.h"
25 #include "sieve-actions.h"
26 #include "sieve-result.h"
27
28 #include "sieve-parser.h"
29 #include "sieve-validator.h"
30 #include "sieve-generator.h"
31 #include "sieve-interpreter.h"
32 #include "sieve-binary-dumper.h"
33
34 #include "sieve.h"
35 #include "sieve-common.h"
36 #include "sieve-limits.h"
37 #include "sieve-error-private.h"
38
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <stdio.h>
44 #include <dirent.h>
45
46 struct event_category event_category_sieve = {
47 .name = "sieve",
48 };
49
50 /*
51 * Main Sieve library interface
52 */
53
54 struct sieve_instance *
sieve_init(const struct sieve_environment * env,const struct sieve_callbacks * callbacks,void * context,bool debug)55 sieve_init(const struct sieve_environment *env,
56 const struct sieve_callbacks *callbacks, void *context, bool debug)
57 {
58 struct sieve_instance *svinst;
59 const char *domain;
60 pool_t pool;
61
62 /* Create Sieve engine instance */
63 pool = pool_alloconly_create("sieve", 8192);
64 svinst = p_new(pool, struct sieve_instance, 1);
65 svinst->pool = pool;
66 svinst->callbacks = callbacks;
67 svinst->context = context;
68 svinst->debug = debug;
69 svinst->base_dir = p_strdup_empty(pool, env->base_dir);
70 svinst->username = p_strdup_empty(pool, env->username);
71 svinst->home_dir = p_strdup_empty(pool, env->home_dir);
72 svinst->temp_dir = p_strdup_empty(pool, env->temp_dir);
73 svinst->flags = env->flags;
74 svinst->env_location = env->location;
75 svinst->delivery_phase = env->delivery_phase;
76
77 svinst->event = event_create(env->event_parent);
78 event_add_category(svinst->event, &event_category_sieve);
79 event_set_forced_debug(svinst->event, debug);
80 event_set_append_log_prefix(svinst->event, "sieve: ");
81 event_add_str(svinst->event, "user", env->username);
82
83 /* Determine domain */
84 if (env->domainname != NULL && *(env->domainname) != '\0')
85 domain = env->domainname;
86 else {
87 /* Fall back to parsing username localpart@domain */
88 domain = svinst->username == NULL ? NULL :
89 strchr(svinst->username, '@');
90 if (domain == NULL || *(domain+1) == '\0') {
91 /* Fall back to parsing hostname host.domain */
92 domain = (env->hostname != NULL ?
93 strchr(env->hostname, '.') : NULL);
94 if (domain == NULL || *(domain+1) == '\0' ||
95 strchr(domain+1, '.') == NULL) {
96 /* Fall back to bare hostname */
97 domain = env->hostname;
98 } else {
99 domain++;
100 }
101 } else {
102 domain++;
103 }
104 }
105 svinst->hostname = p_strdup_empty(pool, env->hostname);
106 svinst->domainname = p_strdup(pool, domain);
107
108 sieve_errors_init(svinst);
109
110 e_debug(svinst->event, "%s version %s initializing",
111 PIGEONHOLE_NAME, PIGEONHOLE_VERSION_FULL);
112
113 /* Read configuration */
114
115 sieve_settings_load(svinst);
116
117 /* Initialize extensions */
118 if (!sieve_extensions_init(svinst)) {
119 sieve_deinit(&svinst);
120 return NULL;
121 }
122
123 /* Initialize storage classes */
124 sieve_storages_init(svinst);
125
126 /* Initialize plugins */
127 sieve_plugins_load(svinst, NULL, NULL);
128
129 /* Configure extensions */
130 sieve_extensions_configure(svinst);
131
132 return svinst;
133 }
134
sieve_deinit(struct sieve_instance ** _svinst)135 void sieve_deinit(struct sieve_instance **_svinst)
136 {
137 struct sieve_instance *svinst = *_svinst;
138
139 sieve_plugins_unload(svinst);
140 sieve_storages_deinit(svinst);
141 sieve_extensions_deinit(svinst);
142 sieve_errors_deinit(svinst);
143
144 event_unref(&svinst->event);
145
146 pool_unref(&(svinst)->pool);
147 *_svinst = NULL;
148 }
149
sieve_set_extensions(struct sieve_instance * svinst,const char * extensions)150 void sieve_set_extensions(struct sieve_instance *svinst, const char *extensions)
151 {
152 sieve_extensions_set_string(svinst, extensions, FALSE, FALSE);
153 }
154
155 const char *
sieve_get_capabilities(struct sieve_instance * svinst,const char * name)156 sieve_get_capabilities(struct sieve_instance *svinst, const char *name)
157 {
158 if (name == NULL || *name == '\0')
159 return sieve_extensions_get_string(svinst);
160
161 return sieve_extension_capabilities_get_string(svinst, name);
162 }
163
sieve_get_event(struct sieve_instance * svinst)164 struct event *sieve_get_event(struct sieve_instance *svinst)
165 {
166 return svinst->event;
167 }
168
169 /*
170 * Low-level compiler functions
171 */
172
173 struct sieve_ast *
sieve_parse(struct sieve_script * script,struct sieve_error_handler * ehandler,enum sieve_error * error_r)174 sieve_parse(struct sieve_script *script, struct sieve_error_handler *ehandler,
175 enum sieve_error *error_r)
176 {
177 struct sieve_parser *parser;
178 struct sieve_ast *ast = NULL;
179
180 /* Parse */
181 parser = sieve_parser_create(script, ehandler, error_r);
182 if (parser == NULL)
183 return NULL;
184
185 if (!sieve_parser_run(parser, &ast))
186 ast = NULL;
187 else
188 sieve_ast_ref(ast);
189
190 sieve_parser_free(&parser);
191
192 if (error_r != NULL) {
193 if (ast == NULL)
194 *error_r = SIEVE_ERROR_NOT_VALID;
195 else
196 *error_r = SIEVE_ERROR_NONE;
197 }
198 return ast;
199 }
200
sieve_validate(struct sieve_ast * ast,struct sieve_error_handler * ehandler,enum sieve_compile_flags flags,enum sieve_error * error_r)201 bool sieve_validate(struct sieve_ast *ast, struct sieve_error_handler *ehandler,
202 enum sieve_compile_flags flags, enum sieve_error *error_r)
203 {
204 bool result = TRUE;
205 struct sieve_validator *validator =
206 sieve_validator_create(ast, ehandler, flags);
207
208 if (!sieve_validator_run(validator))
209 result = FALSE;
210
211 sieve_validator_free(&validator);
212
213 if (error_r != NULL) {
214 if (!result)
215 *error_r = SIEVE_ERROR_NOT_VALID;
216 else
217 *error_r = SIEVE_ERROR_NONE;
218 }
219 return result;
220 }
221
222 static struct sieve_binary *
sieve_generate(struct sieve_ast * ast,struct sieve_error_handler * ehandler,enum sieve_compile_flags flags,enum sieve_error * error_r)223 sieve_generate(struct sieve_ast *ast, struct sieve_error_handler *ehandler,
224 enum sieve_compile_flags flags, enum sieve_error *error_r)
225 {
226 struct sieve_generator *generator =
227 sieve_generator_create(ast, ehandler, flags);
228 struct sieve_binary *sbin = NULL;
229
230 sbin = sieve_generator_run(generator, NULL);
231
232 sieve_generator_free(&generator);
233
234 if (error_r != NULL) {
235 if (sbin == NULL)
236 *error_r = SIEVE_ERROR_NOT_VALID;
237 else
238 *error_r = SIEVE_ERROR_NONE;
239 }
240 return sbin;
241 }
242
243 /*
244 * Sieve compilation
245 */
246
247 struct sieve_binary *
sieve_compile_script(struct sieve_script * script,struct sieve_error_handler * ehandler,enum sieve_compile_flags flags,enum sieve_error * error_r)248 sieve_compile_script(struct sieve_script *script,
249 struct sieve_error_handler *ehandler,
250 enum sieve_compile_flags flags,
251 enum sieve_error *error_r)
252 {
253 struct sieve_ast *ast;
254 struct sieve_binary *sbin;
255 enum sieve_error error, *errorp;
256
257 if (error_r != NULL)
258 errorp = error_r;
259 else
260 errorp = &error;
261 *errorp = SIEVE_ERROR_NONE;
262
263 /* Parse */
264 ast = sieve_parse(script, ehandler, errorp);
265 if (ast == NULL) {
266 switch (*errorp) {
267 case SIEVE_ERROR_NOT_FOUND:
268 if (error_r == NULL) {
269 sieve_error(ehandler, sieve_script_name(script),
270 "script not found");
271 }
272 break;
273 default:
274 sieve_error(ehandler, sieve_script_name(script),
275 "parse failed");
276 }
277 return NULL;
278 }
279
280 /* Validate */
281 if (!sieve_validate(ast, ehandler, flags, errorp)) {
282 sieve_error(ehandler, sieve_script_name(script),
283 "validation failed");
284
285 sieve_ast_unref(&ast);
286 return NULL;
287 }
288
289 /* Generate */
290 sbin = sieve_generate(ast, ehandler, flags, errorp);
291 if (sbin == NULL) {
292 sieve_error(ehandler, sieve_script_name(script),
293 "code generation failed");
294 sieve_ast_unref(&ast);
295 return NULL;
296 }
297
298 /* Cleanup */
299 sieve_ast_unref(&ast);
300 return sbin;
301 }
302
303 struct sieve_binary *
sieve_compile(struct sieve_instance * svinst,const char * script_location,const char * script_name,struct sieve_error_handler * ehandler,enum sieve_compile_flags flags,enum sieve_error * error_r)304 sieve_compile(struct sieve_instance *svinst, const char *script_location,
305 const char *script_name, struct sieve_error_handler *ehandler,
306 enum sieve_compile_flags flags, enum sieve_error *error_r)
307 {
308 struct sieve_script *script;
309 struct sieve_binary *sbin;
310 enum sieve_error error;
311
312 script = sieve_script_create_open(svinst, script_location,
313 script_name, &error);
314 if (script == NULL) {
315 if (error_r != NULL)
316 *error_r = error;
317 switch (error) {
318 case SIEVE_ERROR_NOT_FOUND:
319 sieve_error(ehandler, script_name, "script not found");
320 break;
321 default:
322 sieve_internal_error(ehandler, script_name,
323 "failed to open script");
324 }
325 return NULL;
326 }
327
328 sbin = sieve_compile_script(script, ehandler, flags, error_r);
329 if (sbin != NULL) {
330 e_debug(svinst->event,
331 "Script `%s' from %s successfully compiled",
332 sieve_script_name(script),
333 sieve_script_location(script));
334 }
335
336 sieve_script_unref(&script);
337 return sbin;
338 }
339
340 /*
341 * Sieve runtime
342 */
343
344 static int
sieve_run(struct sieve_binary * sbin,struct sieve_result * result,struct sieve_execute_env * eenv,struct sieve_error_handler * ehandler)345 sieve_run(struct sieve_binary *sbin, struct sieve_result *result,
346 struct sieve_execute_env *eenv, struct sieve_error_handler *ehandler)
347 {
348 struct sieve_interpreter *interp;
349 int ret = 0;
350
351 /* Create the interpreter */
352 interp = sieve_interpreter_create(sbin, NULL, eenv, ehandler);
353 if (interp == NULL)
354 return SIEVE_EXEC_BIN_CORRUPT;
355
356 /* Run the interpreter */
357 ret = sieve_interpreter_run(interp, result);
358
359 /* Free the interpreter */
360 sieve_interpreter_free(&interp);
361
362 return ret;
363 }
364
365 /*
366 * Reading/writing sieve binaries
367 */
368
369 struct sieve_binary *
sieve_load(struct sieve_instance * svinst,const char * bin_path,enum sieve_error * error_r)370 sieve_load(struct sieve_instance *svinst, const char *bin_path,
371 enum sieve_error *error_r)
372 {
373 return sieve_binary_open(svinst, bin_path, NULL, error_r);
374 }
375
376 static struct sieve_binary *
sieve_open_script_real(struct sieve_script * script,struct sieve_error_handler * ehandler,enum sieve_compile_flags flags,enum sieve_error * error_r)377 sieve_open_script_real(struct sieve_script *script,
378 struct sieve_error_handler *ehandler,
379 enum sieve_compile_flags flags,
380 enum sieve_error *error_r)
381 {
382 struct sieve_instance *svinst = sieve_script_svinst(script);
383 struct sieve_resource_usage rusage;
384 struct sieve_binary *sbin;
385 enum sieve_error error;
386 const char *errorstr = NULL;
387 int ret;
388
389 if (error_r == NULL)
390 error_r = &error;
391
392 sieve_resource_usage_init(&rusage);
393
394 /* Try to open the matching binary */
395 sbin = sieve_script_binary_load(script, error_r);
396 if (sbin != NULL) {
397 sieve_binary_get_resource_usage(sbin, &rusage);
398
399 /* Ok, it exists; now let's see if it is up to date */
400 if (!sieve_resource_usage_is_excessive(svinst, &rusage) &&
401 !sieve_binary_up_to_date(sbin, flags)) {
402 /* Not up to date */
403 e_debug(svinst->event,
404 "Script binary %s is not up-to-date",
405 sieve_binary_path(sbin));
406 sieve_binary_close(&sbin);
407 }
408 }
409
410 /* If the binary does not exist or is not up-to-date, we need
411 * to (re-)compile.
412 */
413 if (sbin != NULL) {
414 e_debug(svinst->event,
415 "Script binary %s successfully loaded",
416 sieve_binary_path(sbin));
417 } else {
418 sbin = sieve_compile_script(script, ehandler, flags, error_r);
419 if (sbin == NULL)
420 return NULL;
421
422 e_debug(svinst->event,
423 "Script `%s' from %s successfully compiled",
424 sieve_script_name(script),
425 sieve_script_location(script));
426
427 sieve_binary_set_resource_usage(sbin, &rusage);
428 }
429
430 /* Check whether binary can be executed. */
431 ret = sieve_binary_check_executable(sbin, error_r, &errorstr);
432 if (ret <= 0) {
433 const char *path = sieve_binary_path(sbin);
434
435 if (path != NULL) {
436 e_debug(svinst->event,
437 "Script binary %s cannot be executed",
438 path);
439 } else {
440 e_debug(svinst->event,
441 "Script binary from %s cannot be executed",
442 sieve_binary_source(sbin));
443 }
444 if (ret < 0) {
445 sieve_internal_error(ehandler,
446 sieve_script_name(script),
447 "failed to open script");
448 } else {
449 sieve_error(ehandler, sieve_script_name(script),
450 "%s", errorstr);
451 }
452 sieve_binary_close(&sbin);
453 }
454
455 return sbin;
456 }
457
458 struct sieve_binary *
sieve_open_script(struct sieve_script * script,struct sieve_error_handler * ehandler,enum sieve_compile_flags flags,enum sieve_error * error_r)459 sieve_open_script(struct sieve_script *script,
460 struct sieve_error_handler *ehandler,
461 enum sieve_compile_flags flags, enum sieve_error *error_r)
462 {
463 struct sieve_binary *sbin;
464
465 T_BEGIN {
466 sbin = sieve_open_script_real(script, ehandler, flags, error_r);
467 } T_END;
468
469 return sbin;
470 }
471
472 struct sieve_binary *
sieve_open(struct sieve_instance * svinst,const char * script_location,const char * script_name,struct sieve_error_handler * ehandler,enum sieve_compile_flags flags,enum sieve_error * error_r)473 sieve_open(struct sieve_instance *svinst, const char *script_location,
474 const char *script_name, struct sieve_error_handler *ehandler,
475 enum sieve_compile_flags flags, enum sieve_error *error_r)
476 {
477 struct sieve_script *script;
478 struct sieve_binary *sbin;
479 enum sieve_error error;
480
481 /* First open the scriptfile itself */
482 script = sieve_script_create_open(svinst, script_location,
483 script_name, &error);
484 if (script == NULL) {
485 /* Failed */
486 if (error_r != NULL)
487 *error_r = error;
488 switch (error) {
489 case SIEVE_ERROR_NOT_FOUND:
490 sieve_error(ehandler, script_name, "script not found");
491 break;
492 default:
493 sieve_internal_error(ehandler, script_name,
494 "failed to open script");
495 }
496 return NULL;
497 }
498
499 sbin = sieve_open_script(script, ehandler, flags, error_r);
500
501 /* Drop script reference, if sbin != NULL it holds a reference of its own.
502 * Otherwise the script object is freed here.
503 */
504 sieve_script_unref(&script);
505
506 return sbin;
507 }
508
sieve_get_source(struct sieve_binary * sbin)509 const char *sieve_get_source(struct sieve_binary *sbin)
510 {
511 return sieve_binary_source(sbin);
512 }
513
sieve_is_loaded(struct sieve_binary * sbin)514 bool sieve_is_loaded(struct sieve_binary *sbin)
515 {
516 return sieve_binary_loaded(sbin);
517 }
518
sieve_save_as(struct sieve_binary * sbin,const char * bin_path,bool update,mode_t save_mode,enum sieve_error * error_r)519 int sieve_save_as(struct sieve_binary *sbin, const char *bin_path, bool update,
520 mode_t save_mode, enum sieve_error *error_r)
521 {
522 if (bin_path == NULL)
523 return sieve_save(sbin, update, error_r);
524
525 return sieve_binary_save(sbin, bin_path, update, save_mode, error_r);
526 }
527
sieve_save(struct sieve_binary * sbin,bool update,enum sieve_error * error_r)528 int sieve_save(struct sieve_binary *sbin, bool update,
529 enum sieve_error *error_r)
530 {
531 struct sieve_script *script = sieve_binary_script(sbin);
532
533 if (script == NULL)
534 return sieve_binary_save(sbin, NULL, update, 0600, error_r);
535
536 return sieve_script_binary_save(script, sbin, update, error_r);
537 }
538
sieve_record_resource_usage(struct sieve_binary * sbin,const struct sieve_resource_usage * rusage)539 bool sieve_record_resource_usage(struct sieve_binary *sbin,
540 const struct sieve_resource_usage *rusage)
541 {
542 return sieve_binary_record_resource_usage(sbin, rusage);
543 }
544
sieve_check_executable(struct sieve_binary * sbin,enum sieve_error * error_r,const char ** client_error_r)545 int sieve_check_executable(struct sieve_binary *sbin,
546 enum sieve_error *error_r,
547 const char **client_error_r)
548 {
549 return sieve_binary_check_executable(sbin, error_r, client_error_r);
550 }
551
sieve_close(struct sieve_binary ** _sbin)552 void sieve_close(struct sieve_binary **_sbin)
553 {
554 sieve_binary_close(_sbin);
555 }
556
557 /*
558 * Debugging
559 */
560
sieve_dump(struct sieve_binary * sbin,struct ostream * stream,bool verbose)561 void sieve_dump(struct sieve_binary *sbin, struct ostream *stream, bool verbose)
562 {
563 struct sieve_binary_dumper *dumpr = sieve_binary_dumper_create(sbin);
564
565 sieve_binary_dumper_run(dumpr, stream, verbose);
566
567 sieve_binary_dumper_free(&dumpr);
568 }
569
sieve_hexdump(struct sieve_binary * sbin,struct ostream * stream)570 void sieve_hexdump(struct sieve_binary *sbin, struct ostream *stream)
571 {
572 struct sieve_binary_dumper *dumpr = sieve_binary_dumper_create(sbin);
573
574 sieve_binary_dumper_hexdump(dumpr, stream);
575
576 sieve_binary_dumper_free(&dumpr);
577 }
578
sieve_test(struct sieve_binary * sbin,const struct sieve_message_data * msgdata,const struct sieve_script_env * senv,struct sieve_error_handler * ehandler,struct ostream * stream,enum sieve_execute_flags flags)579 int sieve_test(struct sieve_binary *sbin,
580 const struct sieve_message_data *msgdata,
581 const struct sieve_script_env *senv,
582 struct sieve_error_handler *ehandler, struct ostream *stream,
583 enum sieve_execute_flags flags)
584 {
585 struct sieve_instance *svinst = sieve_binary_svinst(sbin);
586 struct sieve_result *result;
587 struct sieve_execute_env eenv;
588 pool_t pool;
589 int ret;
590
591 pool = pool_alloconly_create("sieve execution", 4096);
592 sieve_execute_init(&eenv, svinst, pool, msgdata, senv, flags);
593
594 /* Create result object */
595 result = sieve_result_create(svinst, pool, &eenv);
596
597 /* Run the script */
598 ret = sieve_run(sbin, result, &eenv, ehandler);
599
600 /* Print result if successful */
601 if (ret > 0) {
602 ret = (sieve_result_print(result, senv, stream, NULL) ?
603 SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE);
604 }
605
606 /* Cleanup */
607 if (result != NULL)
608 sieve_result_unref(&result);
609 sieve_execute_deinit(&eenv);
610 pool_unref(&pool);
611
612 return ret;
613 }
614
615 /*
616 * Script execution
617 */
618
sieve_script_env_init(struct sieve_script_env * senv,struct mail_user * user,const char ** error_r)619 int sieve_script_env_init(struct sieve_script_env *senv, struct mail_user *user,
620 const char **error_r)
621 {
622 const struct message_address *postmaster;
623 const char *error;
624
625 if (!mail_user_get_postmaster_address(user, &postmaster, &error)) {
626 *error_r = t_strdup_printf(
627 "Invalid postmaster_address: %s", error);
628 return -1;
629 }
630
631 i_zero(senv);
632 senv->user = user;
633 senv->postmaster_address = postmaster;
634 return 0;
635 }
636
sieve_execute(struct sieve_binary * sbin,const struct sieve_message_data * msgdata,const struct sieve_script_env * senv,struct sieve_error_handler * exec_ehandler,struct sieve_error_handler * action_ehandler,enum sieve_execute_flags flags)637 int sieve_execute(struct sieve_binary *sbin,
638 const struct sieve_message_data *msgdata,
639 const struct sieve_script_env *senv,
640 struct sieve_error_handler *exec_ehandler,
641 struct sieve_error_handler *action_ehandler,
642 enum sieve_execute_flags flags)
643 {
644 struct sieve_instance *svinst = sieve_binary_svinst(sbin);
645 struct sieve_result *result = NULL;
646 struct sieve_result_execution *rexec;
647 struct sieve_execute_env eenv;
648 pool_t pool;
649 int ret;
650
651 pool = pool_alloconly_create("sieve execution", 4096);
652 sieve_execute_init(&eenv, svinst, pool, msgdata, senv, flags);
653
654 /* Create result object */
655 result = sieve_result_create(svinst, pool, &eenv);
656
657 /* Run the script */
658 ret = sieve_run(sbin, result, &eenv, exec_ehandler);
659
660 rexec = sieve_result_execution_create(result, pool);
661
662 /* Evaluate status and execute the result:
663 Strange situations, e.g. currupt binaries, must be handled by the
664 caller. In that case no implicit keep is attempted, because the
665 situation may be resolved.
666 */
667 ret = sieve_result_execute(rexec, ret, TRUE, action_ehandler, NULL);
668
669 sieve_result_execution_destroy(&rexec);
670
671 /* Cleanup */
672 if (result != NULL)
673 sieve_result_unref(&result);
674 sieve_execute_finish(&eenv, ret);
675 sieve_execute_deinit(&eenv);
676 pool_unref(&pool);
677
678 return ret;
679 }
680
681 /*
682 * Multiscript support
683 */
684
685 struct sieve_multiscript {
686 pool_t pool;
687 struct sieve_execute_env exec_env;
688 struct sieve_result *result;
689 struct sieve_result_execution *rexec;
690 struct event *event;
691
692 int status;
693 bool keep;
694
695 struct ostream *teststream;
696
697 bool active:1;
698 bool discard_handled:1;
699 };
700
701 struct sieve_multiscript *
sieve_multiscript_start_execute(struct sieve_instance * svinst,const struct sieve_message_data * msgdata,const struct sieve_script_env * senv)702 sieve_multiscript_start_execute(struct sieve_instance *svinst,
703 const struct sieve_message_data *msgdata,
704 const struct sieve_script_env *senv)
705 {
706 pool_t pool;
707 struct sieve_result *result;
708 struct sieve_multiscript *mscript;
709
710 pool = pool_alloconly_create("sieve execution", 4096);
711 mscript = p_new(pool, struct sieve_multiscript, 1);
712 mscript->pool = pool;
713 sieve_execute_init(&mscript->exec_env, svinst, pool, msgdata, senv, 0);
714
715 mscript->event = event_create(mscript->exec_env.event);
716 event_set_append_log_prefix(mscript->event, "multi-script: ");
717
718 result = sieve_result_create(svinst, pool, &mscript->exec_env);
719 sieve_result_set_keep_action(result, NULL, NULL);
720 mscript->result = result;
721
722 mscript->rexec = sieve_result_execution_create(result, pool);
723
724 mscript->status = SIEVE_EXEC_OK;
725 mscript->active = TRUE;
726 mscript->keep = TRUE;
727
728 e_debug(mscript->event, "Start execute sequence");
729
730 return mscript;
731 }
732
sieve_multiscript_destroy(struct sieve_multiscript ** _mscript)733 static void sieve_multiscript_destroy(struct sieve_multiscript **_mscript)
734 {
735 struct sieve_multiscript *mscript = *_mscript;
736
737 if (mscript == NULL)
738 return;
739 *_mscript = NULL;
740
741 e_debug(mscript->event, "Destroy");
742
743 event_unref(&mscript->event);
744
745 sieve_result_execution_destroy(&mscript->rexec);
746 sieve_result_unref(&mscript->result);
747 sieve_execute_deinit(&mscript->exec_env);
748 pool_unref(&mscript->pool);
749 }
750
751 struct sieve_multiscript *
sieve_multiscript_start_test(struct sieve_instance * svinst,const struct sieve_message_data * msgdata,const struct sieve_script_env * senv,struct ostream * stream)752 sieve_multiscript_start_test(struct sieve_instance *svinst,
753 const struct sieve_message_data *msgdata,
754 const struct sieve_script_env *senv,
755 struct ostream *stream)
756 {
757 struct sieve_multiscript *mscript =
758 sieve_multiscript_start_execute(svinst, msgdata, senv);
759
760 mscript->teststream = stream;
761
762 return mscript;
763 }
764
765 static void
sieve_multiscript_test(struct sieve_multiscript * mscript)766 sieve_multiscript_test(struct sieve_multiscript *mscript)
767 {
768 const struct sieve_script_env *senv = mscript->exec_env.scriptenv;
769
770 e_debug(mscript->event, "Test result");
771
772 if (mscript->status > 0) {
773 mscript->status =
774 (sieve_result_print(mscript->result, senv,
775 mscript->teststream,
776 &mscript->keep) ?
777 SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE);
778 } else {
779 mscript->keep = TRUE;
780 }
781
782 sieve_result_mark_executed(mscript->result);
783 }
784
785 static void
sieve_multiscript_execute(struct sieve_multiscript * mscript,struct sieve_error_handler * ehandler,enum sieve_execute_flags flags)786 sieve_multiscript_execute(struct sieve_multiscript *mscript,
787 struct sieve_error_handler *ehandler,
788 enum sieve_execute_flags flags)
789 {
790 e_debug(mscript->event, "Execute result");
791
792 mscript->exec_env.flags = flags;
793
794 if (mscript->status > 0) {
795 mscript->status = sieve_result_execute(mscript->rexec,
796 SIEVE_EXEC_OK, FALSE,
797 ehandler,
798 &mscript->keep);
799 }
800 }
801
sieve_multiscript_run(struct sieve_multiscript * mscript,struct sieve_binary * sbin,struct sieve_error_handler * exec_ehandler,struct sieve_error_handler * action_ehandler,enum sieve_execute_flags flags)802 bool sieve_multiscript_run(struct sieve_multiscript *mscript,
803 struct sieve_binary *sbin,
804 struct sieve_error_handler *exec_ehandler,
805 struct sieve_error_handler *action_ehandler,
806 enum sieve_execute_flags flags)
807 {
808 if (!mscript->active) {
809 e_debug(mscript->event, "Sequence ended");
810 return FALSE;
811 }
812
813 e_debug(mscript->event, "Run script `%s'", sieve_binary_source(sbin));
814
815 /* Run the script */
816 mscript->exec_env.flags = flags;
817 mscript->status = sieve_run(sbin, mscript->result, &mscript->exec_env,
818 exec_ehandler);
819
820 if (mscript->status >= 0) {
821 mscript->keep = FALSE;
822
823 if (mscript->teststream != NULL)
824 sieve_multiscript_test(mscript);
825 else {
826 sieve_multiscript_execute(mscript, action_ehandler,
827 flags);
828 }
829 if (!mscript->keep)
830 mscript->active = FALSE;
831 }
832
833 if (!mscript->active || mscript->status <= 0) {
834 e_debug(mscript->event, "Sequence ended");
835 mscript->active = FALSE;
836 return FALSE;
837 }
838
839 e_debug(mscript->event, "Sequence active");
840 return TRUE;
841 }
842
sieve_multiscript_will_discard(struct sieve_multiscript * mscript)843 bool sieve_multiscript_will_discard(struct sieve_multiscript *mscript)
844 {
845 return (!mscript->active && mscript->status == SIEVE_EXEC_OK &&
846 !sieve_result_executed_delivery(mscript->rexec));
847 }
848
sieve_multiscript_run_discard(struct sieve_multiscript * mscript,struct sieve_binary * sbin,struct sieve_error_handler * exec_ehandler,struct sieve_error_handler * action_ehandler,enum sieve_execute_flags flags)849 void sieve_multiscript_run_discard(struct sieve_multiscript *mscript,
850 struct sieve_binary *sbin,
851 struct sieve_error_handler *exec_ehandler,
852 struct sieve_error_handler *action_ehandler,
853 enum sieve_execute_flags flags)
854 {
855 if (!sieve_multiscript_will_discard(mscript)) {
856 e_debug(mscript->event, "Not running discard script");
857 return;
858 }
859 i_assert(!mscript->discard_handled);
860
861 e_debug(mscript->event, "Run discard script `%s'",
862 sieve_binary_source(sbin));
863
864 sieve_result_set_keep_action(mscript->result, NULL, &act_store);
865
866 /* Run the discard script */
867 flags |= SIEVE_EXECUTE_FLAG_DEFER_KEEP;
868 mscript->exec_env.flags = flags;
869 mscript->status = sieve_run(sbin, mscript->result, &mscript->exec_env,
870 exec_ehandler);
871
872 if (mscript->status >= 0) {
873 mscript->keep = FALSE;
874
875 if (mscript->teststream != NULL)
876 sieve_multiscript_test(mscript);
877 else {
878 sieve_multiscript_execute(mscript, action_ehandler,
879 flags);
880 }
881 if (mscript->status == SIEVE_EXEC_FAILURE)
882 mscript->status = SIEVE_EXEC_KEEP_FAILED;
883 mscript->active = FALSE;
884 }
885
886 mscript->discard_handled = TRUE;
887 }
888
sieve_multiscript_status(struct sieve_multiscript * mscript)889 int sieve_multiscript_status(struct sieve_multiscript *mscript)
890 {
891 return mscript->status;
892 }
893
sieve_multiscript_finish(struct sieve_multiscript ** _mscript,struct sieve_error_handler * action_ehandler,enum sieve_execute_flags flags,int status)894 int sieve_multiscript_finish(struct sieve_multiscript **_mscript,
895 struct sieve_error_handler *action_ehandler,
896 enum sieve_execute_flags flags, int status)
897 {
898 struct sieve_multiscript *mscript = *_mscript;
899
900 if (mscript == NULL)
901 return SIEVE_EXEC_OK;
902 *_mscript = NULL;
903
904 switch (status) {
905 case SIEVE_EXEC_OK:
906 status = mscript->status;
907 break;
908 case SIEVE_EXEC_TEMP_FAILURE:
909 break;
910 case SIEVE_EXEC_BIN_CORRUPT:
911 case SIEVE_EXEC_FAILURE:
912 case SIEVE_EXEC_KEEP_FAILED:
913 case SIEVE_EXEC_RESOURCE_LIMIT:
914 if (mscript->status == SIEVE_EXEC_TEMP_FAILURE)
915 status = mscript->status;
916 break;
917 }
918
919 e_debug(mscript->event, "Finishing sequence (status=%s)",
920 sieve_execution_exitcode_to_str(status));
921
922 mscript->exec_env.flags = flags;
923 sieve_result_set_keep_action(mscript->result, NULL, &act_store);
924
925 mscript->keep = FALSE;
926 if (mscript->teststream != NULL)
927 mscript->keep = TRUE;
928 else {
929 status = sieve_result_execute(
930 mscript->rexec, status, TRUE, action_ehandler,
931 &mscript->keep);
932 }
933
934 e_debug(mscript->event, "Sequence finished (status=%s, keep=%s)",
935 sieve_execution_exitcode_to_str(status),
936 (mscript->keep ? "yes" : "no"));
937
938 sieve_execute_finish(&mscript->exec_env, status);
939
940 /* Cleanup */
941 sieve_multiscript_destroy(&mscript);
942
943 return status;
944 }
945
946 /*
947 * Configured Limits
948 */
949
sieve_max_redirects(struct sieve_instance * svinst)950 unsigned int sieve_max_redirects(struct sieve_instance *svinst)
951 {
952 return svinst->max_redirects;
953 }
954
sieve_max_actions(struct sieve_instance * svinst)955 unsigned int sieve_max_actions(struct sieve_instance *svinst)
956 {
957 return svinst->max_actions;
958 }
959
sieve_max_script_size(struct sieve_instance * svinst)960 size_t sieve_max_script_size(struct sieve_instance *svinst)
961 {
962 return svinst->max_script_size;
963 }
964
965 /*
966 * User log
967 */
968
969 const char *
sieve_user_get_log_path(struct sieve_instance * svinst,struct sieve_script * user_script)970 sieve_user_get_log_path(struct sieve_instance *svinst,
971 struct sieve_script *user_script)
972 {
973 const char *log_path = NULL;
974
975 /* Determine user log file path */
976 log_path = sieve_setting_get(svinst, "sieve_user_log");
977 if (log_path == NULL) {
978 const char *path;
979
980 if (user_script == NULL ||
981 (path = sieve_file_script_get_path(user_script)) == NULL) {
982 /* Default */
983 if (svinst->home_dir != NULL) {
984 log_path = t_strconcat(
985 svinst->home_dir, "/.dovecot.sieve.log",
986 NULL);
987 }
988 } else {
989 /* Use script file as a base (legacy behavior) */
990 log_path = t_strconcat(path, ".log", NULL);
991 }
992 } else if (svinst->home_dir != NULL) {
993 /* Expand home dir if necessary */
994 if (log_path[0] == '~') {
995 log_path = home_expand_tilde(log_path,
996 svinst->home_dir);
997 } else if (log_path[0] != '/') {
998 log_path = t_strconcat(svinst->home_dir, "/",
999 log_path, NULL);
1000 }
1001 }
1002 return log_path;
1003 }
1004
1005 /*
1006 * Script trace log
1007 */
1008
1009 struct sieve_trace_log {
1010 struct ostream *output;
1011 };
1012
sieve_trace_log_create(struct sieve_instance * svinst,const char * path,struct sieve_trace_log ** trace_log_r)1013 int sieve_trace_log_create(struct sieve_instance *svinst, const char *path,
1014 struct sieve_trace_log **trace_log_r)
1015 {
1016 struct sieve_trace_log *trace_log;
1017 struct ostream *output;
1018 int fd;
1019
1020 *trace_log_r = NULL;
1021
1022 if (path == NULL)
1023 output = o_stream_create_fd(1, 0);
1024 else {
1025 fd = open(path, O_CREAT | O_APPEND | O_WRONLY, 0600);
1026 if (fd == -1) {
1027 e_error(svinst->event, "trace: "
1028 "creat(%s) failed: %m", path);
1029 return -1;
1030 }
1031 output = o_stream_create_fd_autoclose(&fd, 0);
1032 o_stream_set_name(output, path);
1033 }
1034
1035 trace_log = i_new(struct sieve_trace_log, 1);
1036 trace_log->output = output;
1037
1038 *trace_log_r = trace_log;
1039 return 0;
1040 }
1041
sieve_trace_log_create_dir(struct sieve_instance * svinst,const char * dir,struct sieve_trace_log ** trace_log_r)1042 int sieve_trace_log_create_dir(struct sieve_instance *svinst, const char *dir,
1043 struct sieve_trace_log **trace_log_r)
1044 {
1045 static unsigned int counter = 0;
1046 const char *timestamp, *prefix;
1047 struct stat st;
1048
1049 *trace_log_r = NULL;
1050
1051 if (stat(dir, &st) < 0) {
1052 if (errno != ENOENT && errno != EACCES) {
1053 e_error(svinst->event, "trace: "
1054 "stat(%s) failed: %m", dir);
1055 }
1056 return -1;
1057 }
1058
1059 timestamp = t_strflocaltime("%Y%m%d-%H%M%S", ioloop_time);
1060
1061 counter++;
1062
1063 prefix = t_strdup_printf("%s/%s.%s.%u.trace",
1064 dir, timestamp, my_pid, counter);
1065 return sieve_trace_log_create(svinst, prefix, trace_log_r);
1066 }
1067
sieve_trace_log_open(struct sieve_instance * svinst,struct sieve_trace_log ** trace_log_r)1068 int sieve_trace_log_open(struct sieve_instance *svinst,
1069 struct sieve_trace_log **trace_log_r)
1070 {
1071 const char *trace_dir =
1072 sieve_setting_get(svinst, "sieve_trace_dir");
1073
1074 *trace_log_r = NULL;
1075 if (trace_dir == NULL)
1076 return -1;
1077
1078 if (svinst->home_dir != NULL) {
1079 /* Expand home dir if necessary */
1080 if (trace_dir[0] == '~') {
1081 trace_dir = home_expand_tilde(trace_dir,
1082 svinst->home_dir);
1083 } else if (trace_dir[0] != '/') {
1084 trace_dir = t_strconcat(svinst->home_dir, "/",
1085 trace_dir, NULL);
1086 }
1087 }
1088
1089 return sieve_trace_log_create_dir(svinst, trace_dir, trace_log_r);
1090 }
1091
sieve_trace_log_write_line(struct sieve_trace_log * trace_log,const string_t * line)1092 void sieve_trace_log_write_line(struct sieve_trace_log *trace_log,
1093 const string_t *line)
1094 {
1095 struct const_iovec iov[2];
1096
1097 if (line == NULL) {
1098 o_stream_nsend_str(trace_log->output, "\n");
1099 return;
1100 }
1101
1102 memset(iov, 0, sizeof(iov));
1103 iov[0].iov_base = str_data(line);
1104 iov[0].iov_len = str_len(line);
1105 iov[1].iov_base = "\n";
1106 iov[1].iov_len = 1;
1107 o_stream_nsendv(trace_log->output, iov, 2);
1108 }
1109
sieve_trace_log_printf(struct sieve_trace_log * trace_log,const char * fmt,...)1110 void sieve_trace_log_printf(struct sieve_trace_log *trace_log,
1111 const char *fmt, ...)
1112 {
1113 va_list args;
1114
1115 va_start(args, fmt);
1116 T_BEGIN {
1117 o_stream_nsend_str(trace_log->output,
1118 t_strdup_vprintf(fmt, args));
1119 } T_END;
1120 va_end(args);
1121 }
1122
sieve_trace_log_free(struct sieve_trace_log ** _trace_log)1123 void sieve_trace_log_free(struct sieve_trace_log **_trace_log)
1124 {
1125 struct sieve_trace_log *trace_log = *_trace_log;
1126
1127 *_trace_log = NULL;
1128
1129 if (o_stream_finish(trace_log->output) < 0) {
1130 i_error("write(%s) failed: %s",
1131 o_stream_get_name(trace_log->output),
1132 o_stream_get_error(trace_log->output));
1133 }
1134 o_stream_destroy(&trace_log->output);
1135 i_free(trace_log);
1136 }
1137
sieve_trace_config_get(struct sieve_instance * svinst,struct sieve_trace_config * tr_config)1138 int sieve_trace_config_get(struct sieve_instance *svinst,
1139 struct sieve_trace_config *tr_config)
1140 {
1141 const char *tr_level =
1142 sieve_setting_get(svinst, "sieve_trace_level");
1143 bool tr_debug, tr_addresses;
1144
1145 i_zero(tr_config);
1146
1147 if (tr_level == NULL || *tr_level == '\0' ||
1148 strcasecmp(tr_level, "none") == 0)
1149 return -1;
1150
1151 if (strcasecmp(tr_level, "actions") == 0)
1152 tr_config->level = SIEVE_TRLVL_ACTIONS;
1153 else if (strcasecmp(tr_level, "commands") == 0)
1154 tr_config->level = SIEVE_TRLVL_COMMANDS;
1155 else if (strcasecmp(tr_level, "tests") == 0)
1156 tr_config->level = SIEVE_TRLVL_TESTS;
1157 else if (strcasecmp(tr_level, "matching") == 0)
1158 tr_config->level = SIEVE_TRLVL_MATCHING;
1159 else {
1160 e_error(svinst->event, "Unknown trace level: %s", tr_level);
1161 return -1;
1162 }
1163
1164 tr_debug = FALSE;
1165 (void)sieve_setting_get_bool_value(svinst, "sieve_trace_debug",
1166 &tr_debug);
1167 tr_addresses = FALSE;
1168 (void)sieve_setting_get_bool_value(svinst, "sieve_trace_addresses",
1169 &tr_addresses);
1170
1171 if (tr_debug)
1172 tr_config->flags |= SIEVE_TRFLG_DEBUG;
1173 if (tr_addresses)
1174 tr_config->flags |= SIEVE_TRFLG_ADDRESSES;
1175 return 0;
1176 }
1177
1178 /*
1179 * Execution exit codes
1180 */
1181
sieve_execution_exitcode_to_str(int code)1182 const char *sieve_execution_exitcode_to_str(int code)
1183 {
1184 switch (code) {
1185 case SIEVE_EXEC_OK:
1186 return "ok";
1187 case SIEVE_EXEC_FAILURE:
1188 return "failure";
1189 case SIEVE_EXEC_TEMP_FAILURE:
1190 return "temporary_failure";
1191 case SIEVE_EXEC_BIN_CORRUPT:
1192 return "binary_corrupt";
1193 case SIEVE_EXEC_KEEP_FAILED:
1194 return "keep_failed";
1195 case SIEVE_EXEC_RESOURCE_LIMIT:
1196 return "resource_limit";
1197 }
1198 i_unreached();
1199 }
1200
1201 /*
1202 * User e-mail address
1203 */
1204
sieve_get_user_email(struct sieve_instance * svinst)1205 const struct smtp_address *sieve_get_user_email(struct sieve_instance *svinst)
1206 {
1207 struct smtp_address *address;
1208 const char *username = svinst->username;
1209
1210 if (svinst->user_email_implicit != NULL)
1211 return svinst->user_email_implicit;
1212 if (svinst->user_email != NULL)
1213 return svinst->user_email;
1214
1215 if (smtp_address_parse_mailbox(svinst->pool, username, 0,
1216 &address, NULL) >= 0) {
1217 svinst->user_email_implicit = address;
1218 return svinst->user_email_implicit;
1219 }
1220
1221 if (svinst->domainname != NULL) {
1222 svinst->user_email_implicit = smtp_address_create(
1223 svinst->pool, username, svinst->domainname);
1224 return svinst->user_email_implicit;
1225 }
1226 return NULL;
1227 }
1228
1229 /*
1230 * Postmaster address
1231 */
1232
1233 const struct message_address *
sieve_get_postmaster(const struct sieve_script_env * senv)1234 sieve_get_postmaster(const struct sieve_script_env *senv)
1235 {
1236 i_assert(senv->postmaster_address != NULL);
1237 return senv->postmaster_address;
1238 }
1239
1240 const struct smtp_address *
sieve_get_postmaster_smtp(const struct sieve_script_env * senv)1241 sieve_get_postmaster_smtp(const struct sieve_script_env *senv)
1242 {
1243 struct smtp_address *addr;
1244 int ret;
1245
1246 ret = smtp_address_create_from_msg_temp(
1247 sieve_get_postmaster(senv), &addr);
1248 i_assert(ret >= 0);
1249 return addr;
1250 }
1251
sieve_get_postmaster_address(const struct sieve_script_env * senv)1252 const char *sieve_get_postmaster_address(const struct sieve_script_env *senv)
1253 {
1254 const struct message_address *postmaster =
1255 sieve_get_postmaster(senv);
1256 string_t *addr = t_str_new(256);
1257
1258 message_address_write(addr, postmaster);
1259 return str_c(addr);
1260 }
1261
1262 /*
1263 * Resource usage
1264 */
1265
sieve_resource_usage_init(struct sieve_resource_usage * rusage_r)1266 void sieve_resource_usage_init(struct sieve_resource_usage *rusage_r)
1267 {
1268 i_zero(rusage_r);
1269 }
1270
sieve_resource_usage_add(struct sieve_resource_usage * dst,const struct sieve_resource_usage * src)1271 void sieve_resource_usage_add(struct sieve_resource_usage *dst,
1272 const struct sieve_resource_usage *src)
1273 {
1274 if ((UINT_MAX - dst->cpu_time_msecs) < src->cpu_time_msecs)
1275 dst->cpu_time_msecs = UINT_MAX;
1276 else
1277 dst->cpu_time_msecs += src->cpu_time_msecs;
1278 }
1279
sieve_resource_usage_is_high(struct sieve_instance * svinst ATTR_UNUSED,const struct sieve_resource_usage * rusage)1280 bool sieve_resource_usage_is_high(struct sieve_instance *svinst ATTR_UNUSED,
1281 const struct sieve_resource_usage *rusage)
1282 {
1283 return (rusage->cpu_time_msecs > SIEVE_HIGH_CPU_TIME_MSECS);
1284 }
1285
sieve_resource_usage_is_excessive(struct sieve_instance * svinst,const struct sieve_resource_usage * rusage)1286 bool sieve_resource_usage_is_excessive(
1287 struct sieve_instance *svinst,
1288 const struct sieve_resource_usage *rusage)
1289 {
1290 i_assert(svinst->max_cpu_time_secs <= (UINT_MAX / 1000));
1291 if (svinst->max_cpu_time_secs == 0)
1292 return FALSE;
1293 return (rusage->cpu_time_msecs > (svinst->max_cpu_time_secs * 1000));
1294 }
1295
1296 const char *
sieve_resource_usage_get_summary(const struct sieve_resource_usage * rusage)1297 sieve_resource_usage_get_summary(const struct sieve_resource_usage *rusage)
1298 {
1299 if (rusage->cpu_time_msecs == 0)
1300 return "no usage recorded";
1301
1302 return t_strdup_printf("cpu time = %u ms", rusage->cpu_time_msecs);
1303 }
1304