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

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

diff --git a/src/plugins/imapsieve/imap-sieve.c b/src/plugins/imapsieve/imap-sieve.c
index 3ed19ea8f..63383bfaf 100644
--- a/src/plugins/imapsieve/imap-sieve.c
+++ b/src/plugins/imapsieve/imap-sieve.c
@@ -282,6 +282,8 @@ struct imap_sieve_run_script {
 
 	/* Binary corrupt after recompile; don't recompile again */
 	bool binary_corrupt:1;
+	/* Resource usage exceeded */
+	bool rusage_exceeded:1;
 };
 
 struct imap_sieve_run {
@@ -563,6 +565,13 @@ imap_sieve_run_open_script(struct imap_sieve_run *isrun,
 				"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),
@@ -668,10 +677,12 @@ imap_sieve_run_scripts(struct imap_sieve_run *isrun,
 	struct sieve_instance *svinst = isieve->svinst;
 	struct imap_sieve_run_script *scripts = isrun->scripts;
 	unsigned int count = isrun->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;
@@ -696,6 +707,12 @@ imap_sieve_run_scripts(struct imap_sieve_run *isrun,
 		user_script = (script == isrun->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;
@@ -762,6 +779,12 @@ imap_sieve_run_scripts(struct imap_sieve_run *isrun,
 			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 */
@@ -771,6 +794,12 @@ imap_sieve_run_scripts(struct imap_sieve_run *isrun,
 		    isrun->user_ehandler : isieve->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);
@@ -784,6 +813,8 @@ imap_sieve_run_scripts(struct imap_sieve_run *isrun,
 		return 1;
 	}
 
+	if (last_script == NULL && ret == SIEVE_EXEC_OK)
+		return 0;
 	return imap_sieve_handle_exec_status(isrun, last_script, ret,
 					     scriptenv->exec_status, fatal_r);
 }
-- 
GitLab