imap deadlock bug in 0.7.65

Maxim Dounin mdounin at mdounin.ru
Mon Apr 26 04:45:09 MSD 2010


Hello!

On Fri, Apr 23, 2010 at 06:46:02AM +0400, Maxim Dounin wrote:

> Hello!
> 
> On Thu, Apr 22, 2010 at 06:12:57PM -0700, Alan Batie wrote:
> 
> > 
> > At line 729 of ngx_mail_proxy_module.c, there is this check for how much
> > data was received from an imap server response:
> > 
> >     if (b->last - b->pos < 5) {
> >         return NGX_AGAIN;
> >     }
> > 
> > Our zimbra server, oddly enough, running nginx itself, returns "+ \r\n"
> > in response to the initial phase of a login.  As this is only 4
> > characters, nginx goes back for more, only there isn't any more coming,
> > resulting in a timeout.  Changing 5 to 4 fixes the problem, though
> > probably a "MIN_IMAP_RESPONSE" define would probably be better.
> 
> As far as I understand RFC 3501 the only situation where "+ " CRLF 
> form is permitted is server challenage in AUTHENTICATE command.  
> And nginx doesn't use AUTHENTICATE command while talking to backends, 
> it uses LOGIN command instead.
> 
> Could you please provide something like tcpdump -xXs0 of such 
> a connection and some more details about your backend?
> 
> I'm ok with relaxing the above check, just curious.

Just for the record: Alan pointed off-list that he uses Zimbra 6 
as a backend.  Looking int source code indeed reveals that Zimbra 
uses nginx 0.5.30 with local patches which causes it to return 
"+ " CRLF as literal continuation response.

I believe the following patch would be appropriate:

--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -726,7 +726,12 @@ ngx_mail_proxy_read_response(ngx_mail_se

     b->last += n;

-    if (b->last - b->pos < 5) {
+    /*
+     * check up to 4 chars unconditionally
+     * minimal expected reply is "+ " CRLF
+     */
+
+    if (b->last - b->pos < 4) {
         return NGX_AGAIN;
     }


Alternatively, the attached one may be used.  It bumps limit to 2 
(just CRLF) and adds appropriate length checking to individual reply 
checks.

Maxim Dounin
-------------- next part --------------
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1272242475 -14400
# Node ID 54a94faecee92b6638a35b3dafe401efc461c647
# Parent  19b134bf21c0e34a17590df5d83813cbe62735be
Mail: only wait for CRLF from backend.

Backend reply shorter that one we expect (at least 3 chars + CRLF) currently
results in timeout.  Wait only for CRLF and reject too short replies instead.

diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c
--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -726,7 +726,7 @@ ngx_mail_proxy_read_response(ngx_mail_se
 
     b->last += n;
 
-    if (b->last - b->pos < 5) {
+    if (b->last - b->pos < 2) {
         return NGX_AGAIN;
     }
 
@@ -743,11 +743,12 @@ ngx_mail_proxy_read_response(ngx_mail_se
     }
 
     p = b->pos;
+    n = b->last - b->pos - 2;
 
     switch (s->protocol) {
 
     case NGX_MAIL_POP3_PROTOCOL:
-        if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {
+        if (n > 2 && p[0] == '+' && p[1] == 'O' && p[2] == 'K') {
             return NGX_OK;
         }
         break;
@@ -756,7 +757,9 @@ ngx_mail_proxy_read_response(ngx_mail_se
         switch (state) {
 
         case ngx_imap_start:
-            if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {
+            if (n > 3 && p[0] == '*' && p[1] == ' ' && p[2] == 'O'
+                && p[3] == 'K')
+            {
                 return NGX_OK;
             }
             break;
@@ -781,6 +784,10 @@ ngx_mail_proxy_read_response(ngx_mail_se
         break;
 
     default: /* NGX_MAIL_SMTP_PROTOCOL */
+        if (n < 3) {
+            break;
+        }
+
         switch (state) {
 
         case ngx_smtp_start:


More information about the nginx mailing list