From f9ebcd4556a2f1dee342d41ab330ed2181c077c4 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan.bosch@open-xchange.com>
Date: Mon, 7 Dec 2020 23:41:00 +0100
Subject: [PATCH] plugins: imap-filter-sieve: Implement cumulative resource
 limits on script execution.

---
 .../imap-filter-sieve/imap-filter-sieve.c     | 33 ++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/src/plugins/imap-filter-sieve/imap-filter-sieve.c b/src/plugins/imap-filter-sieve/imap-filter-sieve.c
index 26369541f..be339ccef 100644
--- a/src/plugins/imap-filter-sieve/imap-filter-sieve.c
+++ b/src/plugins/imap-filter-sieve/imap-filter-sieve.c
@@ -34,6 +34,8 @@ struct imap_filter_sieve_script {
 
 	/* Binary corrupt after recompile; don't recompile again */
 	bool binary_corrupt:1;
+	/* Resource usage exceeded */
+	bool rusage_exceeded:1;
 };
 
 struct imap_filter_sieve_user {
@@ -389,6 +391,13 @@ imap_sieve_filter_open_script(struct imap_filter_sieve_context *sctx,
 				"Failed to %s script `%s'",
 				compile_name, sieve_script_location(script));
 			break;
+		/* Cumulative resource limit exceeded */
+		case SIEVE_ERROR_RESOURCE_LIMIT:
+			e_error(sieve_get_event(svinst),
+				"Failed to open script `%s' for %s "
+				"(cumulative resource limit exceeded)",
+				sieve_script_location(script), compile_name);
+			break;
 		/* Something else */
 		default:
 			e_error(sieve_get_event(svinst),
@@ -764,10 +773,12 @@ imap_sieve_filter_run_scripts(struct imap_filter_sieve_context *sctx,
 	struct sieve_instance *svinst = ifsuser->svinst;
 	struct imap_filter_sieve_script *scripts = sctx->scripts;
 	unsigned int count = sctx->scripts_count;
+	struct sieve_resource_usage *rusage =
+		&scriptenv->exec_status->resource_usage;
 	struct sieve_multiscript *mscript;
 	struct sieve_error_handler *ehandler;
 	struct sieve_script *last_script = NULL;
-	bool user_script = FALSE, more = TRUE;
+	bool user_script = FALSE, more = TRUE, rusage_exceeded = FALSE;
 	enum sieve_compile_flags cpflags;
 	enum sieve_execute_flags exflags;
 	enum sieve_error compile_error = SIEVE_ERROR_NONE;
@@ -798,6 +809,12 @@ imap_sieve_filter_run_scripts(struct imap_filter_sieve_context *sctx,
 		user_script = (script == sctx->user_script);
 		last_script = script;
 
+		if (scripts[i].rusage_exceeded) {
+			rusage_exceeded = TRUE;
+			break;
+		}
+
+		sieve_resource_usage_init(rusage);
 		if (user_script) {
 			cpflags |= SIEVE_COMPILE_FLAG_NOGLOBAL;
 			exflags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
@@ -842,6 +859,12 @@ imap_sieve_filter_run_scripts(struct imap_filter_sieve_context *sctx,
 			else if (more)
 				(void)sieve_save(sbin, FALSE, NULL);
 		}
+
+		if (user_script && !sieve_record_resource_usage(sbin, rusage)) {
+			rusage_exceeded = ((i + 1) < count && more);
+			scripts[i].rusage_exceeded = TRUE;
+			break;
+		}
 	}
 
 	/* Finish execution */
@@ -850,6 +873,12 @@ imap_sieve_filter_run_scripts(struct imap_filter_sieve_context *sctx,
 		user_ehandler : ifsuser->master_ehandler);
 	if (compile_error == SIEVE_ERROR_TEMP_FAILURE) {
 		ret = sieve_multiscript_tempfail(&mscript, ehandler, exflags);
+	} else if (rusage_exceeded) {
+		i_assert(last_script != NULL);
+		(void)sieve_multiscript_tempfail(&mscript, ehandler, exflags);
+		sieve_error(ehandler, sieve_script_name(last_script),
+			    "cumulative resource usage limit exceeded");
+		ret = SIEVE_EXEC_RESOURCE_LIMIT;
 	} else {
 		ret = sieve_multiscript_finish(&mscript, ehandler, exflags,
 					       NULL);
@@ -864,6 +893,8 @@ imap_sieve_filter_run_scripts(struct imap_filter_sieve_context *sctx,
 		return 0;
 	}
 
+	if (last_script == NULL && ret == SIEVE_EXEC_OK)
+		return 0;
 	i_assert(last_script != NULL); /* at least one script is executed */
 	return imap_sieve_filter_handle_exec_status(sctx, last_script, ret,
 						    scriptenv->exec_status,
-- 
GitLab