[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#991119: unblock: postsrsd/1.10-2



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package postsrsd

[ Reason ]
Security fix for CVE-2021-35525.

[ Impact ]
Package is vulnerable to a potential DoS attack.

[ Tests ]
Tests from upstream backported, testsuite from upstream passes, manually tested 
functionality.

[ Risks ]
Fix is a one-to-one backport from upstream, modulus formatting changes.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]
N/A

unblock postsrsd/1.10-2
diff -Nru postsrsd-1.10/debian/changelog postsrsd-1.10/debian/changelog
--- postsrsd-1.10/debian/changelog	2020-12-02 22:36:36.000000000 +0100
+++ postsrsd-1.10/debian/changelog	2021-07-14 21:21:11.000000000 +0200
@@ -1,4 +1,12 @@
-postsrsd (1.10-1) UNRELEASED; urgency=medium
+postsrsd (1.10-2) UNRELEASED; urgency=medium
+
+  * Fix CVE-2021-35525: potential DoS when Postfix sends certain long data
+    fields such as multiple concatenated email addresses. Fix backported from
+    upstream commit 077be98d8c8. (Closes: #990439)
+
+ -- Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>  Wed, 14 Jul 2021 21:21:11 +0200
+
+postsrsd (1.10-1) unstable; urgency=medium
 
   * New upstream release (Closes: #975633)
   * Drop patches integrated upstream
diff -Nru postsrsd-1.10/debian/patches/0002-SECURITY-Fix-DoS-on-overly-long-input-from-Postfix.patch postsrsd-1.10/debian/patches/0002-SECURITY-Fix-DoS-on-overly-long-input-from-Postfix.patch
--- postsrsd-1.10/debian/patches/0002-SECURITY-Fix-DoS-on-overly-long-input-from-Postfix.patch	1970-01-01 01:00:00.000000000 +0100
+++ postsrsd-1.10/debian/patches/0002-SECURITY-Fix-DoS-on-overly-long-input-from-Postfix.patch	2021-07-14 21:21:11.000000000 +0200
@@ -0,0 +1,211 @@
+From: =?utf-8?q?Timo_R=C3=B6hling?= <timo@gaussglocke.de>
+Date: Sun, 21 Mar 2021 15:27:55 +0100
+Subject: SECURITY: Fix DoS on overly long input from Postfix
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+Thanks to Mateusz Jończyk who reported this issue and gave valuable
+feedback for its resolution.
+
+PostSRSd would hang on an overly long GET request, because the
+fread()/fwrite() logic in the subprocess would get confused by the
+remaining input line in its buffer.
+
+Theoretically, this error should never occur, as Postfix is supposed to
+send valid email addresses only, which are shorter than the buffer, even
+assuming every single character is percent-encoded. However, Postfix
+sometimes does seem to send malformed request with multiple concatenated
+email addresses. I'm not sure if there's a reliable way to trigger this
+condition by an external attacker, but it is a security bug in PostSRSd
+nevertheless.
+
+Fixes CVE-2021-35525.
+
+Origin: https://github.com/roehling/postsrsd/commit/077be98d8c8a9847e4ae0c7dc09e7474cbe27db2
+Forwarded: not-needed
+Last-Update: 2021-07-14
+---
+ postsrsd.c              | 52 ++++++++++++++++++++++++++++++-------------------
+ run_postsrsd_tests.bats | 40 +++++++++++++++++++++++++++++++++----
+ 2 files changed, 68 insertions(+), 24 deletions(-)
+
+diff --git a/postsrsd.c b/postsrsd.c
+index c009d8f..5ebf7f6 100644
+--- a/postsrsd.c
++++ b/postsrsd.c
+@@ -518,9 +518,9 @@ int main (int argc, char **argv)
+     fds[sc].events = POLLIN;
+   }
+   while(TRUE) {
+     int conn;
+-    FILE *fp;
++    FILE *fp_read, *fp_write;
+     char linebuf[1024], *line;
+     char keybuf[1024], *key;
+ 
+     if (poll(fds, socket_count, 1000) < 0) {
+@@ -540,41 +540,53 @@ int main (int argc, char **argv)
+           int i;
+           // close listen sockets so that we don't stop the main daemon process from restarting
+           for (i = 0; i < socket_count; ++i) close (sockets[i]);
+ 
+-          fp = fdopen(conn, "r+");
+-          if (fp == NULL) exit(EXIT_FAILURE);
+-          fds[0].fd = conn;
+-          fds[0].events = POLLIN;
+-          if (poll(fds, 1, timeout * 1000) <= 0) return EXIT_FAILURE;
+-          line = fgets(linebuf, sizeof(linebuf), fp);
+-          while (line) {
+-            fseek (fp, 0, SEEK_CUR); /* Workaround for Solaris */
++          /* create separate input/output streams */
++          fp_read = fdopen(conn, "r");
++          if (fp_read == NULL)
++            return EXIT_FAILURE;
++          fp_write = fdopen(dup(conn), "w");
++          if (fp_write == NULL) return EXIT_FAILURE;
++          errno = 0;
++          alarm(timeout);
++          if (errno != 0)
++              return EXIT_FAILURE;
++          while ((line = fgets(linebuf, sizeof(linebuf), fp_read))) {
+             char* token;
++            alarm(0);
++            if (strlen(line) >= sizeof(linebuf) - 1) {
++              fprintf(fp_write, "500 Invalid request\n");
++              fflush(fp_write);
++              return EXIT_FAILURE;
++            }
+             token = strtok(line, " \r\n");
+             if (token == NULL || strcmp(token, "get") != 0) {
+-              fprintf (fp, "500 Invalid request\n");
+-              fflush (fp);
++              fprintf (fp_write, "500 Invalid request\n");
++              fflush (fp_write);
+               return EXIT_FAILURE;
+             }
+             token = strtok(NULL, "\r\n");
+             if (!token) {
+-              fprintf (fp, "500 Invalid request\n");
+-              fflush (fp);
++              fprintf (fp_write, "500 Invalid request\n");
++              fflush (fp_write);
+               return EXIT_FAILURE;
+             }
+             key = url_decode(keybuf, sizeof(keybuf), token);
+             if (!key) {
+-              fprintf (fp, "500 Invalid request\n");
+-              fflush(fp);
++              fprintf (fp_write, "500 Invalid request\n");
++              fflush(fp_write);
+               return EXIT_FAILURE;
+             }
+-            handler[sc](srs, fp, key, domain, excludes);
+-            fflush (fp);
+-            if (poll(fds, 1, timeout * 1000) <= 0) break;
+-            line = fgets(linebuf, sizeof(linebuf), fp);
++            handler[sc](srs, fp_write, key, domain, excludes);
++            fflush (fp_write);
++            errno = 0;
++            alarm(timeout);
++            if (errno != 0)
++              return EXIT_FAILURE;
+           }
+-          fclose (fp);
++          fclose (fp_write);
++          fclose (fp_read);
+           return EXIT_SUCCESS;
+         }
+         close (conn);
+       }
+diff --git a/run_postsrsd_tests.bats b/run_postsrsd_tests.bats
+index f4b04bb..3d52a50 100755
+--- a/run_postsrsd_tests.bats
++++ b/run_postsrsd_tests.bats
+@@ -2,9 +2,9 @@
+ # vim: filetype=bash:
+ 
+ if [ ! -x "$POSTSRSD" ]
+ then
+-	for builddir in . build* obj*
++	for builddir in . build* obj* _build*
+ 	do
+ 		if [ -x "${builddir}/postsrsd" ]
+ 		then
+ 			POSTSRSD="${builddir}/postsrsd"
+@@ -14,9 +14,9 @@ then
+ fi
+ if [ ! -x "$POSTSRSD" ]
+ then
+ 	cat>&2 <<- EOF
+-	cannot find postsrsd executable (looked in ., build*, obj*)
++	cannot find postsrsd executable (looked in ., build*, obj*, _build*)
+ 	please build the executable first, or set the POSTSRSD
+ 	environment variable if it is in a different location.
+ 
+ 	EOF
+@@ -25,14 +25,21 @@ fi
+ 
+ LANG=C.UTF-8
+ 
+ 
++fillchar()
++{
++        local count="$1"
++        local char="$2"
++        eval 'printf "'"$char"'%.0s" {1..'"$count"'}'
++}
++
+ start_postsrsd_at()
+ {
+ 	echo 'tops3cr3t' > "$BATS_TMPDIR/postsrsd.secret"
+ 	local faketime="$1"
+ 	shift
+-	faketime "${faketime}" ${POSTSRSD} -D -f 10001 -r 10002 -p "$BATS_TMPDIR/postsrsd.pid" -s "$BATS_TMPDIR/postsrsd.secret" -d example.com "$@"
++	faketime "${faketime}" ${POSTSRSD} -D -t1 -f 10001 -r 10002 -p "$BATS_TMPDIR/postsrsd.pid" -s "$BATS_TMPDIR/postsrsd.secret" -d example.com "$@"
+ }
+ 
+ stop_postsrsd()
+ {
+@@ -158,9 +165,9 @@ teardown()
+ 	read<&9 line
+ 	[[ "$line" =~ ^"500 Domain excluded" ]]
+ }
+ 
+-@test "SRS invalid requests" {
++@test "Malformed or invalid requests" {
+ 	start_postsrsd_at "2020-01-01 00:01:00 UTC"
+ 	exec 9<>/dev/tcp/127.0.0.1/10001
+ 	echo>&9 "get"
+ 	read<&9 line
+@@ -172,5 +179,30 @@ teardown()
+ 	exec 9<>/dev/tcp/127.0.0.1/10001
+ 	echo>&9 "get encoding%error@otherdomain.com"
+ 	read<&9 line
+ 	[[ "$line" =~ ^500 ]]
++	exec 9<>/dev/tcp/127.0.0.1/10001
++	# Try to overflow the input buffer
++	echo>&9 "get too_long@`fillchar 1024 a`.com"
++	read<&9 line
++	[[ "$line" =~ ^500 ]]
++}
++
++@test "Pipelining multiple requests" {
++	start_postsrsd_at "2020-01-01 00:01:00 UTC"
++	exec 9<>/dev/tcp/127.0.0.1/10001
++	# Send two requests at once and see if PostSRSd answers both
++	echo>&9 -e "get test@domain1.com\nget test@domain2.com"
++	read<&9 line
++	[[ "$line" =~ ^200 ]]
++	read<&9 line
++	[[ "$line" =~ ^200 ]]
++}
++
++@test "Session timeout" {
++	start_postsrsd_at "2020-01-01 00:01:00 UTC"
++	exec 9<>/dev/tcp/127.0.0.1/10001
++	# Wait until PostSRSd disconnects due to inactivity
++	sleep 2
++	echo >&9 "get test@example.com"
++	! read <&9 line
+ }
diff -Nru postsrsd-1.10/debian/patches/series postsrsd-1.10/debian/patches/series
--- postsrsd-1.10/debian/patches/series	2020-12-02 22:36:36.000000000 +0100
+++ postsrsd-1.10/debian/patches/series	2021-07-14 21:21:11.000000000 +0200
@@ -1 +1,2 @@
 0001-Run-as-postsrsd-user-by-default.patch
+0002-SECURITY-Fix-DoS-on-overly-long-input-from-Postfix.patch

Reply to: