]> git.kaiwu.me - nginx.git/commitdiff
Mail: support SASL EXTERNAL (RFC 4422).
authorRob N ★ <robn@fastmail.com>
Sat, 8 Oct 2016 07:05:00 +0000 (18:05 +1100)
committerRob N ★ <robn@fastmail.com>
Sat, 8 Oct 2016 07:05:00 +0000 (18:05 +1100)
This is needed to allow TLS client certificate auth to work. With
ssl_verify_client configured, the auth daemon can choose to allow the
connection to proceed based on the certificate data.

This has been tested with Thunderbird for IMAP only. I've not yet found a
client that will do client certificate auth for POP3 or SMTP, and the method is
not really documented anywhere that I can find. That said, its simple enough
that the way I've done is probably right.

src/mail/ngx_mail.h
src/mail/ngx_mail_auth_http_module.c
src/mail/ngx_mail_handler.c
src/mail/ngx_mail_imap_handler.c
src/mail/ngx_mail_imap_module.c
src/mail/ngx_mail_parse.c
src/mail/ngx_mail_pop3_handler.c
src/mail/ngx_mail_pop3_module.c
src/mail/ngx_mail_smtp_handler.c
src/mail/ngx_mail_smtp_module.c

index c30af35c9b4e609674026ae5ee972067ed95715b..6002508200921c13227bb7a838c6b9e7f9438222 100644 (file)
@@ -132,7 +132,8 @@ typedef enum {
     ngx_pop3_auth_login_username,
     ngx_pop3_auth_login_password,
     ngx_pop3_auth_plain,
-    ngx_pop3_auth_cram_md5
+    ngx_pop3_auth_cram_md5,
+    ngx_pop3_auth_external
 } ngx_pop3_state_e;
 
 
@@ -142,6 +143,7 @@ typedef enum {
     ngx_imap_auth_login_password,
     ngx_imap_auth_plain,
     ngx_imap_auth_cram_md5,
+    ngx_imap_auth_external,
     ngx_imap_login,
     ngx_imap_user,
     ngx_imap_passwd
@@ -154,6 +156,7 @@ typedef enum {
     ngx_smtp_auth_login_password,
     ngx_smtp_auth_plain,
     ngx_smtp_auth_cram_md5,
+    ngx_smtp_auth_external,
     ngx_smtp_helo,
     ngx_smtp_helo_xclient,
     ngx_smtp_helo_from,
@@ -285,14 +288,16 @@ typedef struct {
 #define NGX_MAIL_AUTH_LOGIN_USERNAME    2
 #define NGX_MAIL_AUTH_APOP              3
 #define NGX_MAIL_AUTH_CRAM_MD5          4
-#define NGX_MAIL_AUTH_NONE              5
+#define NGX_MAIL_AUTH_EXTERNAL          5
+#define NGX_MAIL_AUTH_NONE              6
 
 
 #define NGX_MAIL_AUTH_PLAIN_ENABLED     0x0002
 #define NGX_MAIL_AUTH_LOGIN_ENABLED     0x0004
 #define NGX_MAIL_AUTH_APOP_ENABLED      0x0008
 #define NGX_MAIL_AUTH_CRAM_MD5_ENABLED  0x0010
-#define NGX_MAIL_AUTH_NONE_ENABLED      0x0020
+#define NGX_MAIL_AUTH_EXTERNAL_ENABLED  0x0020
+#define NGX_MAIL_AUTH_NONE_ENABLED      0x0040
 
 
 #define NGX_MAIL_PARSE_INVALID_COMMAND  20
@@ -377,6 +382,8 @@ ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s,
 ngx_int_t ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s,
     ngx_connection_t *c, char *prefix, size_t len);
 ngx_int_t ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c);
+ngx_int_t ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c,
+    ngx_uint_t n);
 ngx_int_t ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c);
 
 void ngx_mail_send(ngx_event_t *wev);
index a94434a2ffd8e7a9912919dd03a3a3c3cc2454ef..6b57358b4769167831dd1df71851d60a3af235d4 100644 (file)
@@ -151,6 +151,7 @@ static ngx_str_t   ngx_mail_auth_http_method[] = {
     ngx_string("plain"),
     ngx_string("apop"),
     ngx_string("cram-md5"),
+    ngx_string("external"),
     ngx_string("none")
 };
 
index 901bb8f15f5ba8a4d30d63c4a5fb64b4467be568..9d4ef561b799af1404122b7bfd93a9c15b7576cc 100644 (file)
@@ -612,6 +612,40 @@ ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c)
 }
 
 
+ngx_int_t
+ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c,
+    ngx_uint_t n)
+{
+    ngx_str_t  *arg, external;
+
+    arg = s->args.elts;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "mail auth external: \"%V\"", &arg[n]);
+
+    external.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
+    if (external.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (ngx_decode_base64(&external, &arg[n]) != NGX_OK) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+            "client sent invalid base64 encoding in AUTH EXTERNAL command");
+        return NGX_MAIL_PARSE_INVALID_COMMAND;
+    }
+
+    s->login.len = external.len;
+    s->login.data = external.data;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "mail auth external: \"%V\"", &s->login);
+
+    s->auth_method = NGX_MAIL_AUTH_EXTERNAL;
+
+    return NGX_DONE;
+}
+
+
 void
 ngx_mail_send(ngx_event_t *wev)
 {
index 57e2fb77daa8c635b9afc30a7563d389450da56f..1c54457853e80f60e556e6073eb72148d67f1e86 100644 (file)
@@ -222,6 +222,10 @@ ngx_mail_imap_auth_state(ngx_event_t *rev)
         case ngx_imap_auth_cram_md5:
             rc = ngx_mail_auth_cram_md5(s, c);
             break;
+
+        case ngx_imap_auth_external:
+            rc = ngx_mail_auth_external(s, c, 0);
+            break;
         }
 
     } else if (rc == NGX_IMAP_NEXT) {
@@ -399,6 +403,13 @@ ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c)
         }
 
         return NGX_ERROR;
+
+    case NGX_MAIL_AUTH_EXTERNAL:
+
+        ngx_str_set(&s->out, imap_username);
+        s->mail_state = ngx_imap_auth_external;
+
+        return NGX_OK;
     }
 
     return rc;
index d281070fb3fd4fc19c3a27e03d7126790b7adbb2..1f187fdeebc51681d63d02f1db46f93ed27cf431 100644 (file)
@@ -29,6 +29,7 @@ static ngx_conf_bitmask_t  ngx_mail_imap_auth_methods[] = {
     { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
     { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },
     { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
+    { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED },
     { ngx_null_string, 0 }
 };
 
@@ -38,6 +39,7 @@ static ngx_str_t  ngx_mail_imap_auth_methods_names[] = {
     ngx_string("AUTH=LOGIN"),
     ngx_null_string,  /* APOP */
     ngx_string("AUTH=CRAM-MD5"),
+    ngx_string("AUTH=EXTERNAL"),
     ngx_null_string   /* NONE */
 };
 
@@ -179,7 +181,7 @@ ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
     }
 
     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
-         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
          m <<= 1, i++)
     {
         if (m & conf->auth_methods) {
@@ -205,7 +207,7 @@ ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
     auth = p;
 
     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
-         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
          m <<= 1, i++)
     {
         if (m & conf->auth_methods) {
index b158f5a0fbaa77175983bfaf21378cbf90d24cf7..2c2cdffa1179e8862b501a5deed4191a855f5954 100644 (file)
@@ -905,13 +905,27 @@ ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)
 
     if (arg[0].len == 8) {
 
-        if (s->args.nelts != 1) {
-            return NGX_MAIL_PARSE_INVALID_COMMAND;
-        }
-
         if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) {
+
+            if (s->args.nelts != 1) {
+                return NGX_MAIL_PARSE_INVALID_COMMAND;
+            }
+
             return NGX_MAIL_AUTH_CRAM_MD5;
         }
+
+        if (ngx_strncasecmp(arg[0].data, (u_char *) "EXTERNAL", 8) == 0) {
+
+            if (s->args.nelts == 1) {
+                return NGX_MAIL_AUTH_EXTERNAL;
+            }
+
+            if (s->args.nelts == 2) {
+                return ngx_mail_auth_external(s, c, 1);
+            }
+        }
+
+        return NGX_MAIL_PARSE_INVALID_COMMAND;
     }
 
     return NGX_MAIL_PARSE_INVALID_COMMAND;
index 51bc257a5951f27f5a108dd096c58d87f6319c90..a2d565804f96e90fd2ebd755c62bf993eba69a7d 100644 (file)
@@ -240,6 +240,10 @@ ngx_mail_pop3_auth_state(ngx_event_t *rev)
         case ngx_pop3_auth_cram_md5:
             rc = ngx_mail_auth_cram_md5(s, c);
             break;
+
+        case ngx_pop3_auth_external:
+            rc = ngx_mail_auth_external(s, c, 0);
+            break;
         }
     }
 
@@ -494,6 +498,13 @@ ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c)
         }
 
         return NGX_ERROR;
+
+    case NGX_MAIL_AUTH_EXTERNAL:
+
+        ngx_str_set(&s->out, pop3_username);
+        s->mail_state = ngx_pop3_auth_external;
+
+        return NGX_OK;
     }
 
     return rc;
index efd298fcd74f4d41f8ad20d55c0517c1639f22d6..bd60e0a6733156967704f919522be0cc90831a5d 100644 (file)
@@ -29,6 +29,7 @@ static ngx_conf_bitmask_t  ngx_mail_pop3_auth_methods[] = {
     { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
     { ngx_string("apop"), NGX_MAIL_AUTH_APOP_ENABLED },
     { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
+    { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED },
     { ngx_null_string, 0 }
 };
 
@@ -38,6 +39,7 @@ static ngx_str_t  ngx_mail_pop3_auth_methods_names[] = {
     ngx_string("LOGIN"),
     ngx_null_string,  /* APOP */
     ngx_string("CRAM-MD5"),
+    ngx_string("EXTERNAL"),
     ngx_null_string   /* NONE */
 };
 
@@ -180,7 +182,7 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
     size += sizeof("SASL") - 1 + sizeof(CRLF) - 1;
 
     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
-         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
          m <<= 1, i++)
     {
         if (m & conf->auth_methods) {
@@ -207,7 +209,7 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
     p = ngx_cpymem(p, "SASL", sizeof("SASL") - 1);
 
     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
-         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
          m <<= 1, i++)
     {
         if (m & conf->auth_methods) {
@@ -243,7 +245,7 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
            + sizeof("." CRLF) - 1;
 
     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
-         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
          m <<= 1, i++)
     {
         if (m & conf->auth_methods) {
@@ -264,7 +266,7 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
                    sizeof("+OK methods supported:" CRLF) - 1);
 
     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
-         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
          m <<= 1, i++)
     {
         if (m & conf->auth_methods) {
index 81cc75f2442f6c7ed4239977e82be7b2dcd381c6..47756c31156fbdaa05e52759802fefd50ba39920 100644 (file)
@@ -485,6 +485,10 @@ ngx_mail_smtp_auth_state(ngx_event_t *rev)
         case ngx_smtp_auth_cram_md5:
             rc = ngx_mail_auth_cram_md5(s, c);
             break;
+
+        case ngx_smtp_auth_external:
+            rc = ngx_mail_auth_external(s, c, 0);
+            break;
         }
     }
 
@@ -652,6 +656,13 @@ ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c)
         }
 
         return NGX_ERROR;
+
+    case NGX_MAIL_AUTH_EXTERNAL:
+
+        ngx_str_set(&s->out, smtp_username);
+        s->mail_state = ngx_smtp_auth_external;
+
+        return NGX_OK;
     }
 
     return rc;
index d5bb51cc23bb966d4c6252fa7394bf515454aad0..f03bd907e9b440e3e7b9320f9cd32d6263f3eabf 100644 (file)
@@ -21,6 +21,7 @@ static ngx_conf_bitmask_t  ngx_mail_smtp_auth_methods[] = {
     { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
     { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },
     { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
+    { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED },
     { ngx_string("none"), NGX_MAIL_AUTH_NONE_ENABLED },
     { ngx_null_string, 0 }
 };
@@ -31,6 +32,7 @@ static ngx_str_t  ngx_mail_smtp_auth_methods_names[] = {
     ngx_string("LOGIN"),
     ngx_null_string,  /* APOP */
     ngx_string("CRAM-MD5"),
+    ngx_string("EXTERNAL"),
     ngx_null_string   /* NONE */
 };
 
@@ -207,7 +209,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
     auth_enabled = 0;
 
     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
-         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
          m <<= 1, i++)
     {
         if (m & conf->auth_methods) {
@@ -250,7 +252,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
         *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H';
 
         for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
-             m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+             m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
              m <<= 1, i++)
         {
             if (m & conf->auth_methods) {