Bug#879007: cyrus-imapd does not accept new connections

Thadeu Lima de Souza Cascardo cascardo at debian.org
Wed Oct 25 22:09:06 UTC 2017


Source: cyrus-imapd
Followup-For: Bug #879007

Here is a debdiff for a backport of the referred commit, also applied to
upstream 2.5.11. I have started running it on my own server (stretch),
and it seems to fix the problem.


-- System Information:
Debian Release: buster/sid
  APT prefers unstable-debug
  APT policy: (500, 'unstable-debug'), (500, 'unstable')
Architecture: amd64 (x86_64)
Foreign Architectures: i386, armhf

Kernel: Linux 4.13.0-1-amd64 (SMP w/4 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=pt_BR.UTF-8 (charmap=UTF-8), LANGUAGE=en_US:en (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
-------------- next part --------------
diff -Nru cyrus-imapd-2.5.10/debian/changelog cyrus-imapd-2.5.10/debian/changelog
--- cyrus-imapd-2.5.10/debian/changelog	2016-12-07 08:23:20.000000000 -0200
+++ cyrus-imapd-2.5.10/debian/changelog	2017-10-25 15:03:52.000000000 -0200
@@ -1,3 +1,9 @@
+cyrus-imapd (2.5.10-3+cascardo1) unstable; urgency=medium
+
+  * imapd.c: imapoptions: implement idle timeout (Closes: #879007)
+
+ -- Thadeu Lima de Souza Cascardo <cascardo at debian.org>  Wed, 25 Oct 2017 15:03:52 -0200
+
 cyrus-imapd (2.5.10-3) unstable; urgency=medium
 
   * Rely on default tls_ciphers and tls_versions configurations
diff -Nru cyrus-imapd-2.5.10/debian/patches/0019-imapoptions-implement-idle-timeout.patch cyrus-imapd-2.5.10/debian/patches/0019-imapoptions-implement-idle-timeout.patch
--- cyrus-imapd-2.5.10/debian/patches/0019-imapoptions-implement-idle-timeout.patch	1969-12-31 21:00:00.000000000 -0300
+++ cyrus-imapd-2.5.10/debian/patches/0019-imapoptions-implement-idle-timeout.patch	2017-10-25 15:02:53.000000000 -0200
@@ -0,0 +1,177 @@
+commit 865199dce1291c612c5b5f3f5957b7800ba863b9
+Author: Philipp Gesang <philipp.gesang at intra2net.com>
+Date:   Wed Sep 21 17:26:40 2016 +0200
+
+    imapd.c: imapoptions: implement idle timeout
+    
+    Use the value of the configuration variable "timeout" as an upper
+    limit in minutes for idle connections. To allow further
+    customization, add a new configuration option "imapidletimeout"
+    which, if greater than zero, will be used instead. The value
+    defaults to zero (not set).
+    
+    RFC 2177 recommends that a client re-issue the IDLE command at
+    least every 29 minutes if it wishes to continue, otherwise the
+    server is free to treat the client as disconnected.
+    
+    The rationale is that sometimes connections aren't properly
+    reset. Currently, a connection is not collected if it was in IDLE
+    state at that point. If this happens repeatedly, imapd keeps
+    accumulating dead connections which can cause DOS. This patch
+    solves the problem by forcing imapd to stop idling after
+    exceeding the configured timeout.
+
+diff --git a/imap/imapd.c b/imap/imapd.c
+index ea218964c..90621b808 100644
+--- a/imap/imapd.c
++++ b/imap/imapd.c
+@@ -58,6 +58,9 @@
+ #include <sys/wait.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
++#include <time.h>
++#include <stdbool.h>
++#include <errno.h>
+ 
+ #include <sasl/sasl.h>
+ 
+@@ -2858,6 +2861,25 @@ static void cmd_id(char *tag)
+     imapd_id.did_id = 1;
+ }
+ 
++static bool deadline_exceeded(const struct timespec *d)
++{
++    struct timespec now;
++
++    if (d->tv_sec <= 0) {
++	/* No deadline configured */
++	return false;
++    }
++
++    errno = 0;
++    if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) {
++	syslog(LOG_ERR, "clock_gettime (%d %m): error reading clock", errno);
++	return false;
++    }
++
++    return now.tv_sec > d->tv_sec ||
++	    (now.tv_sec == d->tv_sec && now.tv_nsec > d->tv_nsec);
++}
++
+ /*
+  * Perform an IDLE command
+  */
+@@ -2867,6 +2889,26 @@ static void cmd_idle(char *tag)
+     int flags;
+     static struct buf arg;
+     static int idle_period = -1;
++    static time_t idle_timeout = -1;
++    struct timespec deadline = { 0, 0 };
++
++    if (idle_timeout == -1) {
++	idle_timeout = config_getint(IMAPOPT_IMAPIDLETIMEOUT);
++	if (idle_timeout <= 0) {
++	    idle_timeout = config_getint(IMAPOPT_TIMEOUT);
++	}
++	idle_timeout *= 60; /* unit is minutes */
++    }
++
++    if (idle_timeout > 0) {
++	errno = 0;
++	if (clock_gettime(CLOCK_MONOTONIC, &deadline) == -1) {
++	    syslog(LOG_ERR, "clock_gettime (%d %m): error reading clock",
++		errno);
++	} else {
++	    deadline.tv_sec += idle_timeout;
++	}
++    }
+ 
+     if (!backend_current) {  /* Local mailbox */
+ 
+@@ -2883,6 +2925,13 @@ static void cmd_idle(char *tag)
+ 
+ 	index_release(imapd_index);
+ 	while ((flags = idle_wait(imapd_in->fd))) {
++	    if (deadline_exceeded(&deadline)) {
++		syslog(LOG_DEBUG, "timeout for user '%s' while idling",
++		    imapd_userid);
++		shut_down(0);
++		break;
++	    }
++
+ 	    if (flags & IDLE_INPUT) {
+ 		/* Get continuation data */
+ 		c = getword(imapd_in, &arg);
+@@ -2915,7 +2964,8 @@ static void cmd_idle(char *tag)
+ 	idle_stop(index_mboxname(imapd_index));
+     }
+     else {  /* Remote mailbox */
+-	int done = 0, shutdown = 0;
++	int done = 0;
++	enum { shutdown_skip, shutdown_bye, shutdown_silent } shutdown = shutdown_skip;
+ 	char buf[2048];
+ 
+ 	/* get polling period */
+@@ -2947,6 +2997,14 @@ static void cmd_idle(char *tag)
+ 
+ 	/* Pipe updates to client while waiting for end of command */
+ 	while (!done) {
++	    if (deadline_exceeded(&deadline)) {
++		syslog(LOG_DEBUG,
++		    "timeout for user '%s' while idling on remote mailbox",
++		    imapd_userid);
++		shutdown = shutdown_silent;
++		goto done;
++	    }
++
+ 	    /* Flush any buffered output */
+ 	    prot_flush(imapd_out);
+ 
+@@ -2955,7 +3013,8 @@ static void cmd_idle(char *tag)
+ 		(shutdown_file(buf, sizeof(buf)) ||
+ 		 (imapd_userid && 
+ 		  userdeny(imapd_userid, config_ident, buf, sizeof(buf))))) {
+-		shutdown = done = 1;
++		done = 1;
++		shutdown = shutdown_bye;
+ 		goto done;
+ 	    }
+ 
+@@ -2979,12 +3038,20 @@ static void cmd_idle(char *tag)
+ 	    pipe_until_tag(backend_current, tag, 0);
+ 	}
+ 
+-	if (shutdown) {
++	switch (shutdown) {
++	case shutdown_bye:
++	    ;
+ 	    char *p;
+ 
+ 	    for (p = buf; *p == '['; p++); /* can't have [ be first char */
+ 	    prot_printf(imapd_out, "* BYE [ALERT] %s\r\n", p);
++	    /* fallthrough */
++	case shutdown_silent:
+ 	    shut_down(0);
++	    break;
++	case shutdown_skip:
++	default:
++	    break;
+ 	}
+     }
+ 
+diff --git a/lib/imapoptions b/lib/imapoptions
+index dd78d19d1..e4fc3ae88 100644
+--- a/lib/imapoptions
++++ b/lib/imapoptions
+@@ -1871,6 +1871,11 @@ product version in the capabilities */
+ /* The length of the IMAP server's inactivity autologout timer,
+    in minutes.  The minimum value is 30, the default. */
+ 
++{ "imapidletimeout", 0, INT }
++/* Timeout for idling clients (RFC 2177) in minutes. If set to
++   zero (the default) or less, the value of "timeout" will be
++   used instead. */
++
+ { "tls_ca_file", "DEFAULT", STRING }
+ /* Deprecated in favor of \fItls_client_ca_file\fR. */
+ 
diff -Nru cyrus-imapd-2.5.10/debian/patches/series cyrus-imapd-2.5.10/debian/patches/series
--- cyrus-imapd-2.5.10/debian/patches/series	2016-12-07 08:23:20.000000000 -0200
+++ cyrus-imapd-2.5.10/debian/patches/series	2017-10-25 15:03:17.000000000 -0200
@@ -16,3 +16,4 @@
 0016-Use-UnicodeData.txt-from-system.patch
 0017-libisieve-has-to-be-noinst_LTLIBRARY-for-PIC-code-to.patch
 0018-Replace-struct-sched_param-with-struct-caldav_sched_.patch
+0019-imapoptions-implement-idle-timeout.patch


More information about the Pkg-Cyrus-imapd-Debian-devel mailing list