[PATCH] Update mail parsing to be per protocol (imap/pop/smtp)

Kunal Pariani kpariani at zimbra.com
Wed Sep 17 20:49:50 UTC 2014


Hello,
This patch adds separate mail parsing states per protocol (imap/pop/smtp), specific error codes for parsing/auth/login failures. Also includes some minor bug fixes in the mail parsing code. Requesting to include this patch as Zimbra has been using this since nginx 0.5.37, so it's quite heavily tested.

Thanks
-Kunal

----- Original Message -----
From: "Kunal Pariani" <kpariani at zimbra.com>
To: "nginx-devel" <nginx-devel at nginx.org>
Sent: Wednesday, September 17, 2014 1:45:14 PM
Subject: [PATCH] Update mail parsing to be per protocol (imap/pop/smtp)

# HG changeset patch
# User Kunal Pariani <kpariani at zimbra.com>
# Date 1410986530 18000
#      Wed Sep 17 15:42:10 2014 -0500
# Node ID dfc11aa2a98e697a562b2aee092a77e3dc13f55a
# Parent  65988e6762de9c2c98f5f028fddecf45bc05a913
Update mail parsing to be per protocol (imap/pop/smtp)

diff -r 65988e6762de -r dfc11aa2a98e src/mail/ngx_mail.h
--- a/src/mail/ngx_mail.h	Wed Sep 17 15:24:16 2014 -0500
+++ b/src/mail/ngx_mail.h	Wed Sep 17 15:42:10 2014 -0500
@@ -248,6 +248,7 @@ typedef struct {
     u_char                 *arg_start;
     u_char                 *arg_end;
     ngx_uint_t              literal_len;
+    ngx_uint_t              eargs;          /* expected #args for command */
 } ngx_mail_session_t;
 
 
@@ -315,7 +316,12 @@ typedef struct {
 #define NGX_MAIL_AUTH_NONE_ENABLED      0x0020
 
 
-#define NGX_MAIL_PARSE_INVALID_COMMAND  20
+#define NGX_MAIL_PARSE_INVALID_COMMAND   20
+#define NGX_MAIL_PARSE_INVALID_AUTH_MECH 30
+#define NGX_MAIL_AUTH_ABORT              40
+#define NGX_MAIL_AUTH_ARGUMENT           50
+#define NGX_MAIL_AUTH_FAILED             60
+#define NGX_MAIL_LOGIN_FAILED            70
 
 
 typedef void (*ngx_mail_init_session_pt)(ngx_mail_session_t *s,
@@ -399,6 +405,13 @@ ngx_int_t ngx_mail_auth_parse(ngx_mail_s
 
 void ngx_mail_send(ngx_event_t *wev);
 ngx_int_t ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c);
+void ngx_mail_set_imap_parse_state_start(ngx_mail_session_t * s);
+void ngx_mail_set_pop3_parse_state_start(ngx_mail_session_t * s);
+void ngx_mail_set_smtp_parse_state_start(ngx_mail_session_t * s);
+void ngx_mail_set_imap_parse_state_argument(ngx_mail_session_t * s);
+void ngx_mail_set_pop3_parse_state_argument(ngx_mail_session_t * s);
+void ngx_mail_set_smtp_parse_state_argument(ngx_mail_session_t * s);
+void ngx_mail_reset_parse_buffer(ngx_mail_session_t * s);
 void ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c);
 void ngx_mail_close_connection(ngx_connection_t *c);
 void ngx_mail_session_internal_server_error(ngx_mail_session_t *s);
diff -r 65988e6762de -r dfc11aa2a98e src/mail/ngx_mail_imap_handler.c
--- a/src/mail/ngx_mail_imap_handler.c	Wed Sep 17 15:24:16 2014 -0500
+++ b/src/mail/ngx_mail_imap_handler.c	Wed Sep 17 15:42:10 2014 -0500
@@ -21,7 +21,6 @@ static ngx_int_t ngx_mail_imap_capabilit
 static ngx_int_t ngx_mail_imap_starttls(ngx_mail_session_t *s,
     ngx_connection_t *c);
 
-
 static u_char  imap_greeting[] = "* OK IMAP4 ready" CRLF;
 static u_char  imap_star[] = "* ";
 static u_char  imap_ok[] = "OK completed" CRLF;
@@ -31,7 +30,11 @@ static u_char  imap_username[] = "+ VXNl
 static u_char  imap_password[] = "+ UGFzc3dvcmQ6" CRLF;
 static u_char  imap_bye[] = "* BYE" CRLF;
 static u_char  imap_invalid_command[] = "BAD invalid command" CRLF;
-
+static u_char  imap_unsupported_mech[] = "NO mechanism not supported" CRLF;
+static u_char  imap_nocleartext[] = "NO cleartext logins disabled" CRLF;
+static u_char  imap_authaborted[] = "BAD AUTHENTICATE aborted" CRLF;
+static u_char  imap_login_failed[] = "NO LOGIN failed" CRLF;
+static u_char  imap_authenticate_failed[] = "NO AUTHENTICATE failed" CRLF;
 
 void
 ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
@@ -235,15 +238,74 @@ ngx_mail_imap_auth_state(ngx_event_t *re
         ngx_mail_auth(s, c);
         return;
 
+    case NGX_OK:
+        ngx_mail_set_imap_parse_state_start(s);
+        s->arg_start = NULL;
+        ngx_mail_reset_parse_buffer(s);
+        break;
+
+    case NGX_MAIL_AUTH_ABORT:
+        ngx_str_set(&s->out, imap_authaborted);
+        s->mail_state = ngx_imap_start;
+        ngx_mail_set_imap_parse_state_start(s);
+        s->arg_start = NULL;
+        ngx_mail_reset_parse_buffer(s);
+        break;
+
     case NGX_ERROR:
         ngx_mail_session_internal_server_error(s);
         return;
 
+    case NGX_MAIL_AUTH_FAILED:
+        ngx_str_set(&s->out, imap_authenticate_failed);
+        s->mail_state = ngx_imap_start;
+        ngx_mail_set_imap_parse_state_start(s);
+        s->arg_start = NULL;
+        ngx_mail_reset_parse_buffer(s);
+        break;
+
+    case NGX_MAIL_LOGIN_FAILED:
+        ngx_str_set(&s->out, imap_login_failed);
+        s->mail_state = ngx_imap_start;
+        ngx_mail_set_imap_parse_state_start(s);
+        s->arg_start = NULL;
+        ngx_mail_reset_parse_buffer(s);
+        break;
+
+    case NGX_MAIL_PARSE_INVALID_AUTH_MECH:
+        ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, c->log, 0,
+                "unsupported IMAP auth mechanism");
+        ngx_str_set(&s->out, imap_unsupported_mech);
+        s->mail_state = ngx_imap_start;
+        ngx_mail_set_imap_parse_state_start(s);
+        s->arg_start = NULL;
+        ngx_mail_reset_parse_buffer(s);
+        break;
+
     case NGX_MAIL_PARSE_INVALID_COMMAND:
-        s->state = 0;
         ngx_str_set(&s->out, imap_invalid_command);
         s->mail_state = ngx_imap_start;
+        ngx_mail_set_imap_parse_state_start(s);
+        s->arg_start = NULL;
+        ngx_mail_reset_parse_buffer(s);
         break;
+
+    case NGX_MAIL_AUTH_ARGUMENT:
+        ngx_mail_set_imap_parse_state_argument(s);
+    	/* preserve tag, since tag's memory is allocated in buffer, need to set the
+    	 * buffer pos after tag
+    	 */
+        s->arg_start = s->buffer->start + s->tag.len;
+        s->buffer->pos = s->arg_start;
+        s->buffer->last = s->arg_start;
+        tag = 0; /* just output s->out */
+        break;
+
+    case NGX_IMAP_NEXT:
+        /* do nothing, preserve all the state, including s->state, s->mail_state,
+         * s->buffer, s->arg_start
+         */
+    	break;
     }
 
     if (tag) {
@@ -300,6 +362,7 @@ ngx_mail_imap_login(ngx_mail_session_t *
 
 #if (NGX_MAIL_SSL)
     if (ngx_mail_starttls_only(s, c)) {
+        ngx_str_set(&s->text, imap_nocleartext);
         return NGX_MAIL_PARSE_INVALID_COMMAND;
     }
 #endif
@@ -342,7 +405,7 @@ ngx_mail_imap_login(ngx_mail_session_t *
 static ngx_int_t
 ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c)
 {
-    ngx_int_t                  rc;
+    ngx_int_t                  rc, res;
     ngx_mail_core_srv_conf_t  *cscf;
     ngx_mail_imap_srv_conf_t  *iscf;
 
@@ -353,36 +416,48 @@ ngx_mail_imap_authenticate(ngx_mail_sess
 #endif
 
     rc = ngx_mail_auth_parse(s, c);
+    iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);
 
     switch (rc) {
 
     case NGX_MAIL_AUTH_LOGIN:
 
+        if (!(iscf->auth_methods & NGX_MAIL_AUTH_LOGIN_ENABLED)) {
+            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
+        }
         ngx_str_set(&s->out, imap_username);
         s->mail_state = ngx_imap_auth_login_username;
 
-        return NGX_OK;
+        return NGX_MAIL_AUTH_ARGUMENT;
 
     case NGX_MAIL_AUTH_LOGIN_USERNAME:
 
-        ngx_str_set(&s->out, imap_password);
-        s->mail_state = ngx_imap_auth_login_password;
-
-        return ngx_mail_auth_login_username(s, c, 1);
+        if (!(iscf->auth_methods & NGX_MAIL_AUTH_LOGIN_ENABLED)) {
+            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
+        }
+        res = ngx_mail_auth_login_username(s, c, 1);
+        if (res == NGX_MAIL_AUTH_ARGUMENT) {
+            ngx_str_set(&s->out, imap_password);
+            s->mail_state = ngx_imap_auth_login_password;
+            return NGX_MAIL_AUTH_ARGUMENT;
+        } else {
+            return res;
+        }
 
     case NGX_MAIL_AUTH_PLAIN:
 
+        if (!(iscf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED)) {
+            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
+        }
         ngx_str_set(&s->out, imap_plain_next);
         s->mail_state = ngx_imap_auth_plain;
 
-        return NGX_OK;
+        return NGX_MAIL_AUTH_ARGUMENT;
 
     case NGX_MAIL_AUTH_CRAM_MD5:
 
-        iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);
-
         if (!(iscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
-            return NGX_MAIL_PARSE_INVALID_COMMAND;
+            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
         }
 
         if (s->salt.data == NULL) {
diff -r 65988e6762de -r dfc11aa2a98e src/mail/ngx_mail_parse.c
--- a/src/mail/ngx_mail_parse.c	Wed Sep 17 15:24:16 2014 -0500
+++ b/src/mail/ngx_mail_parse.c	Wed Sep 17 15:42:10 2014 -0500
@@ -13,20 +13,73 @@
 #include <ngx_mail_imap_module.h>
 #include <ngx_mail_smtp_module.h>
 
+typedef enum {
+    swi_start = 0,
+    swi_spaces_before_command,
+    swi_command,
+    swi_spaces_before_argument,
+    swi_argument,
+    swi_backslash,
+    swi_literal,
+    swi_no_sync_literal_argument,
+    swi_start_literal_argument,
+    swi_literal_argument,
+    swi_end_literal_argument,
+    swi_almost_done,
+} ngx_imap_parse_state_e;
+
+typedef enum {
+    swp_start = 0,
+    swp_spaces_before_argument,
+    swp_argument,
+    swp_almost_done
+} ngx_pop3_parse_state_e;
+
+typedef enum {
+    sws_start = 0,
+    sws_command,
+    sws_spaces_before_argument,
+    sws_argument,
+    sws_almost_done,
+    sws_invalid
+} ngx_smtp_parse_state_e;
+
+inline void ngx_mail_set_imap_parse_state_start(ngx_mail_session_t * s) {
+    s->state = swi_start;
+}
+
+inline void ngx_mail_set_pop3_parse_state_start(ngx_mail_session_t * s) {
+    s->state = swp_start;
+}
+
+inline void ngx_mail_set_smtp_parse_state_start(ngx_mail_session_t * s) {
+    s->state = sws_start;
+}
+
+inline void ngx_mail_set_imap_parse_state_argument(ngx_mail_session_t * s) {
+    s->state = swi_argument;
+}
+
+inline void ngx_mail_set_pop3_parse_state_argument(ngx_mail_session_t * s) {
+    s->state = swp_argument;
+}
+
+inline void ngx_mail_set_smtp_parse_state_argument(ngx_mail_session_t * s) {
+    s->state = sws_argument;
+}
+
+inline void ngx_mail_reset_parse_buffer(ngx_mail_session_t * s) {
+    s->buffer->pos = s->buffer->start;
+    s->buffer->last = s->buffer->start;
+}
 
 ngx_int_t
 ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
 {
     u_char      ch, *p, *c, c0, c1, c2, c3;
     ngx_str_t  *arg;
-    enum {
-        sw_start = 0,
-        sw_spaces_before_argument,
-        sw_argument,
-        sw_almost_done
-    } state;
 
-    state = s->state;
+    ngx_pop3_parse_state_e state = s->state;
 
     for (p = s->buffer->pos; p < s->buffer->last; p++) {
         ch = *p;
@@ -34,7 +87,7 @@ ngx_mail_pop3_parse_command(ngx_mail_ses
         switch (state) {
 
         /* POP3 command */
-        case sw_start:
+        case swp_start:
             if (ch == ' ' || ch == CR || ch == LF) {
                 c = s->buffer->start;
 
@@ -87,10 +140,10 @@ ngx_mail_pop3_parse_command(ngx_mail_ses
 
                 switch (ch) {
                 case ' ':
-                    state = sw_spaces_before_argument;
+                    state = swp_spaces_before_argument;
                     break;
                 case CR:
-                    state = sw_almost_done;
+                    state = swp_almost_done;
                     break;
                 case LF:
                     goto done;
@@ -104,12 +157,12 @@ ngx_mail_pop3_parse_command(ngx_mail_ses
 
             break;
 
-        case sw_spaces_before_argument:
+        case swp_spaces_before_argument:
             switch (ch) {
             case ' ':
                 break;
             case CR:
-                state = sw_almost_done;
+                state = swp_almost_done;
                 s->arg_end = p;
                 break;
             case LF:
@@ -117,7 +170,7 @@ ngx_mail_pop3_parse_command(ngx_mail_ses
                 goto done;
             default:
                 if (s->args.nelts <= 2) {
-                    state = sw_argument;
+                    state = swp_argument;
                     s->arg_start = p;
                     break;
                 }
@@ -125,7 +178,7 @@ ngx_mail_pop3_parse_command(ngx_mail_ses
             }
             break;
 
-        case sw_argument:
+        case swp_argument:
             switch (ch) {
 
             case ' ':
@@ -155,10 +208,10 @@ ngx_mail_pop3_parse_command(ngx_mail_ses
 
                 switch (ch) {
                 case ' ':
-                    state = sw_spaces_before_argument;
+                    state = swp_spaces_before_argument;
                     break;
                 case CR:
-                    state = sw_almost_done;
+                    state = swp_almost_done;
                     break;
                 case LF:
                     goto done;
@@ -170,7 +223,7 @@ ngx_mail_pop3_parse_command(ngx_mail_ses
             }
             break;
 
-        case sw_almost_done:
+        case swp_almost_done:
             switch (ch) {
             case LF:
                 goto done;
@@ -199,13 +252,11 @@ done:
         s->arg_start = NULL;
     }
 
-    s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;
-
     return NGX_OK;
 
 invalid:
 
-    s->state = sw_start;
+    s->state = swp_start;
     s->arg_start = NULL;
 
     return NGX_MAIL_PARSE_INVALID_COMMAND;
@@ -217,22 +268,8 @@ ngx_mail_imap_parse_command(ngx_mail_ses
 {
     u_char      ch, *p, *c;
     ngx_str_t  *arg;
-    enum {
-        sw_start = 0,
-        sw_spaces_before_command,
-        sw_command,
-        sw_spaces_before_argument,
-        sw_argument,
-        sw_backslash,
-        sw_literal,
-        sw_no_sync_literal_argument,
-        sw_start_literal_argument,
-        sw_literal_argument,
-        sw_end_literal_argument,
-        sw_almost_done
-    } state;
-
-    state = s->state;
+    p = NULL;
+    ngx_imap_parse_state_e state = s->state;
 
     for (p = s->buffer->pos; p < s->buffer->last; p++) {
         ch = *p;
@@ -240,40 +277,41 @@ ngx_mail_imap_parse_command(ngx_mail_ses
         switch (state) {
 
         /* IMAP tag */
-        case sw_start:
+        case swi_start:
             switch (ch) {
             case ' ':
-                s->tag.len = p - s->buffer->start + 1;
+                s->tag.len = p - s->buffer->start;
                 s->tag.data = s->buffer->start;
-                state = sw_spaces_before_command;
+                state = swi_spaces_before_command;
+                s->eargs = 0;
                 break;
-            case CR:
-                s->state = sw_start;
-                return NGX_MAIL_PARSE_INVALID_COMMAND;
-            case LF:
-                s->state = sw_start;
-                return NGX_MAIL_PARSE_INVALID_COMMAND;
+            case CR: /* walk through */
+            case LF: /* walk through */
+            case '\x0':
+            	s->tag.len = p - s->buffer->start;
+            	s->tag.data = s->buffer->start;
+            	s->state = swi_start;
+            	return NGX_MAIL_PARSE_INVALID_COMMAND;
             }
             break;
 
-        case sw_spaces_before_command:
+        case swi_spaces_before_command:
             switch (ch) {
             case ' ':
                 break;
-            case CR:
-                s->state = sw_start;
-                return NGX_MAIL_PARSE_INVALID_COMMAND;
-            case LF:
-                s->state = sw_start;
-                return NGX_MAIL_PARSE_INVALID_COMMAND;
+            case CR: /* walk through */
+            case LF: /* walk through */
+            case '\x0':
+            	s->state = swi_start;
+            	return NGX_MAIL_PARSE_INVALID_COMMAND;
             default:
                 s->cmd_start = p;
-                state = sw_command;
+                state = swi_command;
                 break;
             }
             break;
 
-        case sw_command:
+        case swi_command:
             if (ch == ' ' || ch == CR || ch == LF) {
 
                 c = s->cmd_start;
@@ -287,7 +325,7 @@ ngx_mail_imap_parse_command(ngx_mail_ses
                         && (c[3] == 'P'|| c[3] == 'p'))
                     {
                         s->command = NGX_IMAP_NOOP;
-
+                        s->eargs = 0;
                     } else {
                         goto invalid;
                     }
@@ -301,7 +339,7 @@ ngx_mail_imap_parse_command(ngx_mail_ses
                         && (c[4] == 'N'|| c[4] == 'n'))
                     {
                         s->command = NGX_IMAP_LOGIN;
-
+                        s->eargs = 2;
                     } else {
                         goto invalid;
                     }
@@ -316,7 +354,7 @@ ngx_mail_imap_parse_command(ngx_mail_ses
                         && (c[5] == 'T'|| c[5] == 't'))
                     {
                         s->command = NGX_IMAP_LOGOUT;
-
+                        s->eargs = 0;
                     } else {
                         goto invalid;
                     }
@@ -334,7 +372,7 @@ ngx_mail_imap_parse_command(ngx_mail_ses
                         && (c[7] == 'S'|| c[7] == 's'))
                     {
                         s->command = NGX_IMAP_STARTTLS;
-
+                        s->eargs = 0;
                     } else {
                         goto invalid;
                     }
@@ -354,7 +392,7 @@ ngx_mail_imap_parse_command(ngx_mail_ses
                         && (c[9] == 'Y'|| c[9] == 'y'))
                     {
                         s->command = NGX_IMAP_CAPABILITY;
-
+                        s->eargs = 0;
                     } else {
                         goto invalid;
                     }
@@ -374,8 +412,12 @@ ngx_mail_imap_parse_command(ngx_mail_ses
                         && (c[10] == 'T'|| c[10] == 't')
                         && (c[11] == 'E'|| c[11] == 'e'))
                     {
-                        s->command = NGX_IMAP_AUTHENTICATE;
-
+                        if (ch != ' ') {
+                            goto invalid;
+                        } else {
+                            s->command = NGX_IMAP_AUTHENTICATE;
+                            s->eargs = 1;
+                        }
                     } else {
                         goto invalid;
                     }
@@ -387,10 +429,13 @@ ngx_mail_imap_parse_command(ngx_mail_ses
 
                 switch (ch) {
                 case ' ':
-                    state = sw_spaces_before_argument;
+                    if (s->command == NGX_IMAP_CAPABILITY) {
+                        goto invalid;
+                    }
+                    state = swi_spaces_before_argument;
                     break;
                 case CR:
-                    state = sw_almost_done;
+                    state = swi_almost_done;
                     break;
                 case LF:
                     goto done;
@@ -404,42 +449,48 @@ ngx_mail_imap_parse_command(ngx_mail_ses
 
             break;
 
-        case sw_spaces_before_argument:
+        case swi_spaces_before_argument:
             switch (ch) {
             case ' ':
                 break;
             case CR:
-                state = sw_almost_done;
+                if (s->args.nelts == 0) {
+                    goto invalid; /* no argument */
+                }
+                state = swi_almost_done;
                 s->arg_end = p;
                 break;
             case LF:
+                if ( s->args.nelts == 0) {
+                    goto invalid; /* no argument */
+                }
                 s->arg_end = p;
                 goto done;
             case '"':
-                if (s->args.nelts <= 2) {
+                if (s->args.nelts <= s->eargs) {
                     s->quoted = 1;
                     s->arg_start = p + 1;
-                    state = sw_argument;
+                    state = swi_argument;
                     break;
                 }
                 goto invalid;
             case '{':
-                if (s->args.nelts <= 2) {
-                    state = sw_literal;
+                if (s->args.nelts <= s->eargs) {
+                    state = swi_literal;
                     break;
                 }
                 goto invalid;
             default:
-                if (s->args.nelts <= 2) {
+                if (s->args.nelts <= s->eargs) {
                     s->arg_start = p;
-                    state = sw_argument;
+                    state = swi_argument;
                     break;
                 }
                 goto invalid;
             }
             break;
 
-        case sw_argument:
+        case swi_argument:
             if (ch == ' ' && s->quoted) {
                 break;
             }
@@ -465,10 +516,10 @@ ngx_mail_imap_parse_command(ngx_mail_ses
                 switch (ch) {
                 case '"':
                 case ' ':
-                    state = sw_spaces_before_argument;
+                    state = swi_spaces_before_argument;
                     break;
                 case CR:
-                    state = sw_almost_done;
+                    state = swi_almost_done;
                     break;
                 case LF:
                     goto done;
@@ -477,46 +528,52 @@ ngx_mail_imap_parse_command(ngx_mail_ses
             case '\\':
                 if (s->quoted) {
                     s->backslash = 1;
-                    state = sw_backslash;
+                    state = swi_backslash;
                 }
                 break;
             }
             break;
 
-        case sw_backslash:
+        case swi_backslash:
             switch (ch) {
             case CR:
             case LF:
                 goto invalid;
+            case '\\':
+            case '"':
+            	/* (RFC3501)
+            	   a backslash may only escape another backslash, or a double quote */
+                state = swi_argument;
+                break;
             default:
-                state = sw_argument;
+                goto invalid;
             }
             break;
 
-        case sw_literal:
+        case swi_literal:
             if (ch >= '0' && ch <= '9') {
                 s->literal_len = s->literal_len * 10 + (ch - '0');
                 break;
             }
             if (ch == '}') {
-                state = sw_start_literal_argument;
+                state = swi_start_literal_argument;
                 break;
             }
             if (ch == '+') {
-                state = sw_no_sync_literal_argument;
+                state = swi_no_sync_literal_argument;
                 break;
             }
             goto invalid;
 
-        case sw_no_sync_literal_argument:
+        case swi_no_sync_literal_argument:
             if (ch == '}') {
                 s->no_sync_literal = 1;
-                state = sw_start_literal_argument;
+                state = swi_start_literal_argument;
                 break;
             }
             goto invalid;
 
-        case sw_start_literal_argument:
+        case swi_start_literal_argument:
             switch (ch) {
             case CR:
                 break;
@@ -524,10 +581,10 @@ ngx_mail_imap_parse_command(ngx_mail_ses
                 s->buffer->pos = p + 1;
                 s->arg_start = p + 1;
                 if (s->no_sync_literal == 0) {
-                    s->state = sw_literal_argument;
+                    s->state = swi_literal_argument;
                     return NGX_IMAP_NEXT;
                 }
-                state = sw_literal_argument;
+                state = swi_literal_argument;
                 s->no_sync_literal = 0;
                 break;
             default:
@@ -535,7 +592,7 @@ ngx_mail_imap_parse_command(ngx_mail_ses
             }
             break;
 
-        case sw_literal_argument:
+        case swi_literal_argument:
             if (s->literal_len && --s->literal_len) {
                 break;
             }
@@ -547,36 +604,39 @@ ngx_mail_imap_parse_command(ngx_mail_ses
             arg->len = p + 1 - s->arg_start;
             arg->data = s->arg_start;
             s->arg_start = NULL;
-            state = sw_end_literal_argument;
+            state = swi_end_literal_argument;
 
             break;
 
-        case sw_end_literal_argument:
+        case swi_end_literal_argument:
             switch (ch) {
             case '{':
-                if (s->args.nelts <= 2) {
-                    state = sw_literal;
+                if (s->args.nelts <= s->eargs) {
+                    state = swi_literal;
                     break;
                 }
                 goto invalid;
             case CR:
-                state = sw_almost_done;
+                state = swi_almost_done;
                 break;
             case LF:
                 goto done;
             default:
-                state = sw_spaces_before_argument;
+                state = swi_spaces_before_argument;
                 break;
             }
             break;
 
-        case sw_almost_done:
+        case swi_almost_done:
             switch (ch) {
             case LF:
                 goto done;
             default:
                 goto invalid;
             }
+
+        default:
+        	break; /* for avoid warning only */
         }
     }
 
@@ -604,13 +664,11 @@ done:
         s->literal_len = 0;
     }
 
-    s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;
-
     return NGX_OK;
 
 invalid:
 
-    s->state = sw_start;
+    s->state = swi_start;
     s->quoted = 0;
     s->no_sync_literal = 0;
     s->literal_len = 0;
@@ -624,16 +682,8 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
 {
     u_char      ch, *p, *c, c0, c1, c2, c3;
     ngx_str_t  *arg;
-    enum {
-        sw_start = 0,
-        sw_command,
-        sw_invalid,
-        sw_spaces_before_argument,
-        sw_argument,
-        sw_almost_done
-    } state;
 
-    state = s->state;
+    ngx_smtp_parse_state_e state = s->state;
 
     for (p = s->buffer->pos; p < s->buffer->last; p++) {
         ch = *p;
@@ -641,13 +691,13 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
         switch (state) {
 
         /* SMTP command */
-        case sw_start:
+        case sws_start:
             s->cmd_start = p;
-            state = sw_command;
+            state = sws_command;
 
             /* fall through */
 
-        case sw_command:
+        case sws_command:
             if (ch == ' ' || ch == CR || ch == LF) {
                 c = s->cmd_start;
 
@@ -732,10 +782,10 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
 
                 switch (ch) {
                 case ' ':
-                    state = sw_spaces_before_argument;
+                    state = sws_spaces_before_argument;
                     break;
                 case CR:
-                    state = sw_almost_done;
+                    state = sws_almost_done;
                     break;
                 case LF:
                     goto done;
@@ -749,15 +799,15 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
 
             break;
 
-        case sw_invalid:
+        case sws_invalid:
             goto invalid;
 
-        case sw_spaces_before_argument:
+        case sws_spaces_before_argument:
             switch (ch) {
             case ' ':
                 break;
             case CR:
-                state = sw_almost_done;
+                state = sws_almost_done;
                 s->arg_end = p;
                 break;
             case LF:
@@ -765,7 +815,7 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
                 goto done;
             default:
                 if (s->args.nelts <= 10) {
-                    state = sw_argument;
+                    state = sws_argument;
                     s->arg_start = p;
                     break;
                 }
@@ -773,7 +823,7 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
             }
             break;
 
-        case sw_argument:
+        case sws_argument:
             switch (ch) {
             case ' ':
             case CR:
@@ -788,10 +838,10 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
 
                 switch (ch) {
                 case ' ':
-                    state = sw_spaces_before_argument;
+                    state = sws_spaces_before_argument;
                     break;
                 case CR:
-                    state = sw_almost_done;
+                    state = sws_almost_done;
                     break;
                 case LF:
                     goto done;
@@ -803,7 +853,7 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
             }
             break;
 
-        case sw_almost_done:
+        case sws_almost_done:
             switch (ch) {
             case LF:
                 goto done;
@@ -832,20 +882,18 @@ done:
         s->arg_start = NULL;
     }
 
-    s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;
-
     return NGX_OK;
 
 invalid:
 
-    s->state = sw_invalid;
+    s->state = sws_invalid;
     s->arg_start = NULL;
 
     /* skip invalid command till LF */
 
     for (p = s->buffer->pos; p < s->buffer->last; p++) {
         if (*p == LF) {
-            s->state = sw_start;
+            s->state = sws_start;
             p++;
             break;
         }
@@ -882,7 +930,7 @@ ngx_mail_auth_parse(ngx_mail_session_t *
                 return NGX_MAIL_AUTH_LOGIN;
             }
 
-            if (s->args.nelts == 2) {
+            if (s->args.nelts == 2) { /* initial response */
                 return NGX_MAIL_AUTH_LOGIN_USERNAME;
             }
 
@@ -895,7 +943,7 @@ ngx_mail_auth_parse(ngx_mail_session_t *
                 return NGX_MAIL_AUTH_PLAIN;
             }
 
-            if (s->args.nelts == 2) {
+            if (s->args.nelts == 2) { /* initial response */
                 return ngx_mail_auth_plain(s, c, 1);
             }
         }
@@ -914,5 +962,5 @@ ngx_mail_auth_parse(ngx_mail_session_t *
         }
     }
 
-    return NGX_MAIL_PARSE_INVALID_COMMAND;
+    return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
 }
diff -r 65988e6762de -r dfc11aa2a98e src/mail/ngx_mail_pop3_handler.c
--- a/src/mail/ngx_mail_pop3_handler.c	Wed Sep 17 15:24:16 2014 -0500
+++ b/src/mail/ngx_mail_pop3_handler.c	Wed Sep 17 15:42:10 2014 -0500
@@ -27,7 +27,11 @@ static u_char  pop3_next[] = "+ " CRLF;
 static u_char  pop3_username[] = "+ VXNlcm5hbWU6" CRLF;
 static u_char  pop3_password[] = "+ UGFzc3dvcmQ6" CRLF;
 static u_char  pop3_invalid_command[] = "-ERR invalid command" CRLF;
-
+static u_char  pop3_unsupported_mech[] = "-ERR mechanism not supported" CRLF;
+static u_char  pop3_nocleartext[] = "-ERR cleartext logins disabled" CRLF;
+static u_char  pop3_authaborted[] = "-ERR authentication aborted" CRLF;
+static u_char  pop3_login_failed[] = "-ERR invalid username/password" CRLF;
+static u_char  pop3_auth_failed[] = "-ERR line is too long" CRLF;
 
 void
 ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
@@ -109,6 +113,7 @@ ngx_mail_pop3_init_protocol(ngx_event_t 
         }
     }
 
+    ngx_mail_set_pop3_parse_state_start(s);
     s->mail_state = ngx_pop3_start;
     c->read->handler = ngx_mail_pop3_auth_state;
 
@@ -249,30 +254,63 @@ ngx_mail_pop3_auth_state(ngx_event_t *re
         ngx_mail_auth(s, c);
         return;
 
+    case NGX_OK:
+        s->arg_start = NULL;
+        ngx_mail_set_pop3_parse_state_start(s);
+        break;
+
+    case NGX_MAIL_AUTH_ABORT:
+        ngx_str_set(&s->out, pop3_authaborted);
+        s->mail_state = ngx_pop3_start;
+        s->arg_start = NULL;
+        ngx_mail_set_pop3_parse_state_start(s);
+        break;
+
     case NGX_ERROR:
         ngx_mail_session_internal_server_error(s);
         return;
 
+    case NGX_MAIL_LOGIN_FAILED:
+        ngx_str_set(&s->out, pop3_login_failed);
+        s->mail_state = ngx_pop3_start;
+        s->arg_start = NULL;
+        ngx_mail_set_pop3_parse_state_start(s);
+        break;
+
+    case NGX_MAIL_AUTH_FAILED:
+        ngx_str_set(&s->out, pop3_auth_failed);
+        s->mail_state = ngx_pop3_start;
+        s->arg_start = NULL;
+        ngx_mail_set_pop3_parse_state_start(s);
+        break;
+
+    case NGX_MAIL_PARSE_INVALID_AUTH_MECH:
+        ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, c->log, 0,
+                "unsupported POP auth mechanism");
+        ngx_str_set(&s->out, pop3_unsupported_mech);
+        s->mail_state = ngx_pop3_start;
+        s->arg_start = NULL;
+        ngx_mail_set_pop3_parse_state_start(s);
+        break;
+
     case NGX_MAIL_PARSE_INVALID_COMMAND:
+        ngx_str_set(&s->out, pop3_invalid_command);
         s->mail_state = ngx_pop3_start;
-        s->state = 0;
+        s->arg_start = NULL;
+        ngx_mail_set_pop3_parse_state_start(s);
+        break;
 
-        ngx_str_set(&s->out, pop3_invalid_command);
+    case NGX_MAIL_AUTH_ARGUMENT:
+        s->arg_start = s->buffer->start;
+        ngx_mail_set_pop3_parse_state_argument(s);
+        break;
 
-        /* fall through */
+    }
 
-    case NGX_OK:
+    s->args.nelts = 0;
+    ngx_mail_reset_parse_buffer(s);
 
-        s->args.nelts = 0;
-        s->buffer->pos = s->buffer->start;
-        s->buffer->last = s->buffer->start;
-
-        if (s->state) {
-            s->arg_start = s->buffer->start;
-        }
-
-        ngx_mail_send(c->write);
-    }
+    ngx_mail_send(c->write);
 }
 
 static ngx_int_t
@@ -282,6 +320,7 @@ ngx_mail_pop3_user(ngx_mail_session_t *s
 
 #if (NGX_MAIL_SSL)
     if (ngx_mail_starttls_only(s, c)) {
+        ngx_str_set(&s->out, pop3_nocleartext);
         return NGX_MAIL_PARSE_INVALID_COMMAND;
     }
 #endif
@@ -451,10 +490,7 @@ ngx_mail_pop3_auth(ngx_mail_session_t *s
     pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
 
     if (s->args.nelts == 0) {
-        s->out = pscf->auth_capability;
-        s->state = 0;
-
-        return NGX_OK;
+        return NGX_MAIL_PARSE_INVALID_COMMAND;
     }
 
     rc = ngx_mail_auth_parse(s, c);
@@ -463,6 +499,9 @@ ngx_mail_pop3_auth(ngx_mail_session_t *s
 
     case NGX_MAIL_AUTH_LOGIN:
 
+        if (!(pscf->auth_methods & NGX_MAIL_AUTH_LOGIN_ENABLED)) {
+            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
+        }
         ngx_str_set(&s->out, pop3_username);
         s->mail_state = ngx_pop3_auth_login_username;
 
@@ -470,6 +509,9 @@ ngx_mail_pop3_auth(ngx_mail_session_t *s
 
     case NGX_MAIL_AUTH_LOGIN_USERNAME:
 
+        if (!(pscf->auth_methods & NGX_MAIL_AUTH_LOGIN_ENABLED)) {
+            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
+        }
         ngx_str_set(&s->out, pop3_password);
         s->mail_state = ngx_pop3_auth_login_password;
 
@@ -477,6 +519,9 @@ ngx_mail_pop3_auth(ngx_mail_session_t *s
 
     case NGX_MAIL_AUTH_PLAIN:
 
+        if (!(pscf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED)) {
+            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
+        }
         ngx_str_set(&s->out, pop3_next);
         s->mail_state = ngx_pop3_auth_plain;
 
@@ -494,6 +539,9 @@ ngx_mail_pop3_auth(ngx_mail_session_t *s
         }
 
         return NGX_ERROR;
+
+    default:
+        break;
     }
 
     return rc;

_______________________________________________
nginx-devel mailing list
nginx-devel at nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel



More information about the nginx-devel mailing list