[nginx] Mail: added support for XOAUTH2 and OAUTHBEARER authenti...
Maxim Dounin
mdounin at mdounin.ru
Mon Jun 3 15:16:22 UTC 2024
details: http://freenginx.org/hg/nginx/rev/4538c1ffb0f8
branches:
changeset: 9290:4538c1ffb0f8
user: Maxim Dounin <mdounin at mdounin.ru>
date: Mon Jun 03 18:03:11 2024 +0300
description:
Mail: added support for XOAUTH2 and OAUTHBEARER authentication.
This patch adds support for the OAUTHBEARER SASL mechanism as defined
by RFC 7628, as well as pre-RFC XOAUTH2 SASL mechanism. For both
mechanisms, the "Auth-User" header is set to the client identity
obtained from the initial SASL response sent by the client, and the
"Auth-Pass" header is set to the Bearer token itself.
The auth server may return the "Auth-Error-SASL" header, which is
passed to the client as an additional SASL challenge. It is expected
to contain mechanism-specific error details, base64-encoded. After
the client responds (with an empty SASL response for XAUTH2, or with
"AQ==" dummy response for OAUTHBEARER), the error message from the
"Auth-Status" header is sent.
Based on a patch by Rob Mueller.
diffstat:
src/mail/ngx_mail.h | 35 +++-
src/mail/ngx_mail_auth_http_module.c | 140 +++++++++++++----
src/mail/ngx_mail_handler.c | 282 ++++++++++++++++++++++++++++++++++-
src/mail/ngx_mail_imap_handler.c | 40 ++++
src/mail/ngx_mail_imap_module.c | 8 +-
src/mail/ngx_mail_parse.c | 28 +++
src/mail/ngx_mail_pop3_handler.c | 40 ++++
src/mail/ngx_mail_pop3_module.c | 12 +-
src/mail/ngx_mail_smtp_handler.c | 40 ++++
src/mail/ngx_mail_smtp_module.c | 8 +-
10 files changed, 578 insertions(+), 55 deletions(-)
diffs (945 lines):
diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -141,7 +141,9 @@ typedef enum {
ngx_pop3_auth_login_password,
ngx_pop3_auth_plain,
ngx_pop3_auth_cram_md5,
- ngx_pop3_auth_external
+ ngx_pop3_auth_external,
+ ngx_pop3_auth_xoauth2,
+ ngx_pop3_auth_oauthbearer
} ngx_pop3_state_e;
@@ -152,6 +154,8 @@ typedef enum {
ngx_imap_auth_plain,
ngx_imap_auth_cram_md5,
ngx_imap_auth_external,
+ ngx_imap_auth_xoauth2,
+ ngx_imap_auth_oauthbearer,
ngx_imap_login,
ngx_imap_user,
ngx_imap_passwd
@@ -165,6 +169,8 @@ typedef enum {
ngx_smtp_auth_plain,
ngx_smtp_auth_cram_md5,
ngx_smtp_auth_external,
+ ngx_smtp_auth_xoauth2,
+ ngx_smtp_auth_oauthbearer,
ngx_smtp_helo,
ngx_smtp_helo_xclient,
ngx_smtp_helo_auth,
@@ -212,8 +218,9 @@ typedef struct {
unsigned no_sync_literal:1;
unsigned starttls:1;
unsigned esmtp:1;
- unsigned auth_method:3;
+ unsigned auth_method:4;
unsigned auth_wait:1;
+ unsigned auth_quit:1;
ngx_str_t login;
ngx_str_t passwd;
@@ -229,6 +236,8 @@ typedef struct {
ngx_str_t smtp_from;
ngx_str_t smtp_to;
+ ngx_str_t auth_err;
+
ngx_str_t cmd;
ngx_uint_t command;
@@ -303,15 +312,19 @@ typedef struct {
#define NGX_MAIL_AUTH_APOP 3
#define NGX_MAIL_AUTH_CRAM_MD5 4
#define NGX_MAIL_AUTH_EXTERNAL 5
-#define NGX_MAIL_AUTH_NONE 6
+#define NGX_MAIL_AUTH_XOAUTH2 6
+#define NGX_MAIL_AUTH_OAUTHBEARER 7
+#define NGX_MAIL_AUTH_NONE 8
-#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_EXTERNAL_ENABLED 0x0020
-#define NGX_MAIL_AUTH_NONE_ENABLED 0x0040
+#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_EXTERNAL_ENABLED 0x0020
+#define NGX_MAIL_AUTH_XOAUTH2_ENABLED 0x0040
+#define NGX_MAIL_AUTH_OAUTHBEARER_ENABLED 0x0080
+#define NGX_MAIL_AUTH_NONE_ENABLED 0x0100
#define NGX_MAIL_PARSE_INVALID_COMMAND 20
@@ -399,6 +412,10 @@ ngx_int_t ngx_mail_auth_cram_md5_salt(ng
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_xoauth2(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_uint_t n);
+ngx_int_t ngx_mail_auth_oauthbearer(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);
diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c
--- a/src/mail/ngx_mail_auth_http_module.c
+++ b/src/mail/ngx_mail_auth_http_module.c
@@ -53,6 +53,7 @@ struct ngx_mail_auth_http_ctx_s {
ngx_str_t err;
ngx_str_t errmsg;
ngx_str_t errcode;
+ ngx_str_t errsasl;
time_t sleep;
@@ -67,6 +68,7 @@ static void ngx_mail_auth_http_ignore_st
static void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
ngx_mail_auth_http_ctx_t *ctx);
static void ngx_mail_auth_sleep_handler(ngx_event_t *rev);
+static void ngx_mail_auth_send_error(ngx_mail_session_t *s);
static ngx_int_t ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
ngx_mail_auth_http_ctx_t *ctx);
static void ngx_mail_auth_http_block_read(ngx_event_t *rev);
@@ -152,6 +154,8 @@ static ngx_str_t ngx_mail_auth_http_me
ngx_string("apop"),
ngx_string("cram-md5"),
ngx_string("external"),
+ ngx_string("xoauth2"),
+ ngx_string("oauthbearer"),
ngx_string("none")
};
@@ -677,6 +681,51 @@ ngx_mail_auth_http_process_headers(ngx_m
continue;
}
+ if (len == sizeof("Auth-Error-SASL") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Error-SASL",
+ sizeof("Auth-Error-SASL") - 1)
+ == 0)
+ {
+ if (s->auth_method != NGX_MAIL_AUTH_XOAUTH2
+ && s->auth_method != NGX_MAIL_AUTH_OAUTHBEARER)
+ {
+ continue;
+ }
+
+ len = ctx->header_end - ctx->header_start;
+
+ if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {
+ size = len + sizeof("334 " CRLF) - 1;
+
+ } else {
+ size = len + sizeof("+ " CRLF) - 1;
+ }
+
+ p = ngx_pnalloc(s->connection->pool, size);
+ if (p == NULL) {
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->errsasl.len = size;
+ ctx->errsasl.data = p;
+
+ if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {
+ *p++ = '3'; *p++ = '3'; *p++ = '4'; *p++ = ' ';
+
+ } else {
+ *p++ = '+'; *p++ = ' ';
+ }
+
+ p = ngx_cpymem(p, ctx->header_start, len);
+ *p++ = CR; *p = LF;
+
+ continue;
+ }
+
/* ignore other headers */
continue;
@@ -717,14 +766,15 @@ ngx_mail_auth_http_process_headers(ngx_m
*p++ = CR; *p = LF;
}
- s->out = ctx->err;
+ s->out = ctx->errsasl;
+ s->auth_err = ctx->err;
timer = ctx->sleep;
ngx_destroy_pool(ctx->pool);
if (timer == 0) {
- s->quit = 1;
- ngx_mail_send(s->connection->write);
+ s->auth_quit = 1;
+ ngx_mail_auth_send_error(s);
return;
}
@@ -858,9 +908,8 @@ ngx_mail_auth_http_process_headers(ngx_m
static void
ngx_mail_auth_sleep_handler(ngx_event_t *rev)
{
- ngx_connection_t *c;
- ngx_mail_session_t *s;
- ngx_mail_core_srv_conf_t *cscf;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth sleep handler");
@@ -877,33 +926,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t
return;
}
- cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
-
- rev->handler = cscf->protocol->auth_state;
-
- s->mail_state = 0;
- s->auth_method = NGX_MAIL_AUTH_PLAIN;
- s->tag.len = 0;
-
- c->log->action = "in auth state";
-
- ngx_mail_send(c->write);
-
- if (c->destroyed) {
- return;
- }
-
- ngx_add_timer(rev, cscf->timeout);
-
- if (rev->ready) {
- rev->handler(rev);
- return;
- }
-
- if (ngx_handle_read_event(rev, 0) != NGX_OK) {
- ngx_mail_close_connection(c);
- }
-
+ ngx_mail_auth_send_error(s);
return;
}
@@ -915,6 +938,57 @@ ngx_mail_auth_sleep_handler(ngx_event_t
}
+static void
+ngx_mail_auth_send_error(ngx_mail_session_t *s)
+{
+ ngx_event_t *rev;
+ ngx_connection_t *c;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ c = s->connection;
+ rev = c->read;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ rev->handler = cscf->protocol->auth_state;
+
+ s->auth_method = NGX_MAIL_AUTH_PLAIN;
+
+ c->log->action = "in auth state";
+
+ if (s->out.len == 0) {
+ s->out = s->auth_err;
+ s->quit = s->auth_quit;
+ ngx_str_null(&s->auth_err);
+
+ s->state = 0;
+ s->mail_state = 0;
+ s->tag.len = 0;
+
+ } else {
+ s->auth_err.len -= s->tag.len;
+ s->auth_err.data += s->tag.len;
+ }
+
+ ngx_mail_send(c->write);
+
+ if (c->destroyed) {
+ return;
+ }
+
+ ngx_add_timer(rev, cscf->timeout);
+
+ if (rev->ready) {
+ rev->handler(rev);
+ return;
+ }
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+}
+
+
static ngx_int_t
ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
ngx_mail_auth_http_ctx_t *ctx)
diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -755,6 +755,274 @@ ngx_mail_auth_external(ngx_mail_session_
}
+ngx_int_t
+ngx_mail_auth_xoauth2(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)
+{
+ u_char *p, *last;
+ ngx_str_t *arg, oauth;
+
+ arg = s->args.elts;
+
+ if (s->auth_err.len) {
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth xoauth2 cancel");
+
+ if (s->args.nelts == 1 && arg[0].len == 0) {
+ s->out = s->auth_err;
+ s->quit = s->auth_quit;
+ s->state = 0;
+ s->mail_state = 0;
+ ngx_str_null(&s->auth_err);
+ return NGX_OK;
+ }
+
+ s->quit = s->auth_quit;
+ ngx_str_null(&s->auth_err);
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth xoauth2: \"%V\"", &arg[n]);
+
+ oauth.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
+ if (oauth.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&oauth, &arg[n]) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid base64 encoding in "
+ "AUTH XOAUTH2 command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ /*
+ * https://developers.google.com/gmail/imap/xoauth2-protocol
+ * "user=" {User} "^Aauth=Bearer " {token} "^A^A"
+ */
+
+ p = oauth.data;
+ last = p + oauth.len;
+
+ while (p < last) {
+ if (*p++ == '\1') {
+ s->login.len = p - oauth.data - 1;
+ s->login.data = oauth.data;
+ s->passwd.len = last - p;
+ s->passwd.data = p;
+ break;
+ }
+ }
+
+ if (s->login.len < sizeof("user=") - 1
+ || ngx_strncasecmp(s->login.data, (u_char *) "user=",
+ sizeof("user=") - 1)
+ != 0)
+ {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid login in AUTH XOAUTH2 command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ s->login.len -= sizeof("user=") - 1;
+ s->login.data += sizeof("user=") - 1;
+
+ if (s->passwd.len < sizeof("auth=Bearer ") - 1
+ || ngx_strncasecmp(s->passwd.data, (u_char *) "auth=Bearer ",
+ sizeof("auth=Bearer ") - 1)
+ != 0)
+ {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid token in AUTH XOAUTH2 command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ s->passwd.len -= sizeof("auth=Bearer ") - 1;
+ s->passwd.data += sizeof("auth=Bearer ") - 1;
+
+ if (s->passwd.len < 2
+ || s->passwd.data[s->passwd.len - 2] != '\1'
+ || s->passwd.data[s->passwd.len - 1] != '\1')
+ {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid token in AUTH XOAUTH2 command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ s->passwd.len -= 2;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth xoauth2: \"%V\" \"%V\"", &s->login, &s->passwd);
+
+ s->auth_method = NGX_MAIL_AUTH_XOAUTH2;
+
+ return NGX_DONE;
+}
+
+
+ngx_int_t
+ngx_mail_auth_oauthbearer(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_uint_t n)
+{
+ u_char *p, *d, *last, *prev;
+ ngx_str_t *arg, oauth;
+
+ arg = s->args.elts;
+
+ if (s->auth_err.len) {
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth oauthbearer cancel");
+
+ if (s->args.nelts == 1
+ && ngx_strncmp(arg[0].data, (u_char *) "AQ==", 4) == 0)
+ {
+ s->out = s->auth_err;
+ s->quit = s->auth_quit;
+ s->state = 0;
+ s->mail_state = 0;
+ ngx_str_null(&s->auth_err);
+ return NGX_OK;
+ }
+
+ s->quit = s->auth_quit;
+ ngx_str_null(&s->auth_err);
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth oauthbearer: \"%V\"", &arg[n]);
+
+ oauth.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
+ if (oauth.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&oauth, &arg[n]) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid base64 encoding in "
+ "AUTH OAUTHBEARER command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ /*
+ * RFC 7628
+ * "n,a=user at example.com,^A...^Aauth=Bearer <token>^A^A"
+ */
+
+ p = oauth.data;
+ last = p + oauth.len;
+
+ s->login.len = 0;
+ prev = NULL;
+
+ while (p < last) {
+ if (*p == ',') {
+ if (prev
+ && (size_t) (p - prev) > sizeof("a=") - 1
+ && ngx_strncasecmp(prev, (u_char *) "a=", sizeof("a=") - 1)
+ == 0)
+ {
+ s->login.len = p - prev - (sizeof("a=") - 1);
+ s->login.data = prev + sizeof("a=") - 1;
+ break;
+ }
+
+ p++;
+ prev = p;
+ continue;
+ }
+
+ if (*p == '\1') {
+ break;
+ }
+
+ p++;
+ }
+
+ if (s->login.len == 0) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid login in AUTH OAUTHBEARER command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ s->passwd.len = 0;
+ prev = NULL;
+
+ while (p < last) {
+ if (*p == '\1') {
+ if (prev
+ && (size_t) (p - prev) > sizeof("auth=Bearer ") - 1
+ && ngx_strncasecmp(prev, (u_char *) "auth=Bearer ",
+ sizeof("auth=Bearer ") - 1)
+ == 0)
+ {
+ s->passwd.len = p - prev - (sizeof("auth=Bearer ") - 1);
+ s->passwd.data = prev + sizeof("auth=Bearer ") - 1;
+ break;
+ }
+
+ p++;
+ prev = p;
+ continue;
+ }
+
+ p++;
+ }
+
+ if (s->passwd.len == 0) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid token in AUTH OAUTHBEARER command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ /* decode =2C =3D in login */
+
+ p = s->login.data;
+ d = s->login.data;
+ last = s->login.data + s->login.len;
+
+ while (p < last) {
+ if (*p == '=') {
+
+ /*
+ * login is always followed by other data,
+ * so p[1] and p[2] can be checked directly
+ */
+
+ if (p[1] == '2' && (p[2] == 'C' || p[2] == 'c')) {
+ *d++ = ',';
+
+ } else if (p[1] == '3' && (p[2] == 'D' || p[2] == 'd')) {
+ *d++ = '=';
+
+ } else {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid login in "
+ "AUTH OAUTHBEARER command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ p += 3;
+ continue;
+ }
+
+ *d++ = *p++;
+ }
+
+ s->login.len = d - s->login.data;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth oauthbearer: \"%V\" \"%V\"",
+ &s->login, &s->passwd);
+
+ s->auth_method = NGX_MAIL_AUTH_OAUTHBEARER;
+
+ return NGX_DONE;
+}
+
+
void
ngx_mail_send(ngx_event_t *wev)
{
@@ -919,13 +1187,17 @@ ngx_mail_auth(ngx_mail_session_t *s, ngx
{
s->args.nelts = 0;
- if (s->buffer->pos == s->buffer->last) {
- s->buffer->pos = s->buffer->start;
- s->buffer->last = s->buffer->start;
+ if (s->state) {
+ /* preserve tag */
+ s->arg_start = s->buffer->pos;
+
+ } else {
+ if (s->buffer->pos == s->buffer->last) {
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+ }
}
- s->state = 0;
-
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
diff --git a/src/mail/ngx_mail_imap_handler.c b/src/mail/ngx_mail_imap_handler.c
--- a/src/mail/ngx_mail_imap_handler.c
+++ b/src/mail/ngx_mail_imap_handler.c
@@ -220,6 +220,14 @@ ngx_mail_imap_auth_state(ngx_event_t *re
case ngx_imap_auth_external:
rc = ngx_mail_auth_external(s, c, 0);
break;
+
+ case ngx_imap_auth_xoauth2:
+ rc = ngx_mail_auth_xoauth2(s, c, 0);
+ break;
+
+ case ngx_imap_auth_oauthbearer:
+ rc = ngx_mail_auth_oauthbearer(s, c, 0);
+ break;
}
} else if (rc == NGX_IMAP_NEXT) {
@@ -432,6 +440,38 @@ ngx_mail_imap_authenticate(ngx_mail_sess
s->mail_state = ngx_imap_auth_external;
return NGX_OK;
+
+ case NGX_MAIL_AUTH_XOAUTH2:
+
+ if (!(iscf->auth_methods & NGX_MAIL_AUTH_XOAUTH2_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (s->args.nelts == 2) {
+ s->mail_state = ngx_imap_auth_xoauth2;
+ return ngx_mail_auth_xoauth2(s, c, 1);
+ }
+
+ ngx_str_set(&s->out, imap_plain_next);
+ s->mail_state = ngx_imap_auth_xoauth2;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_OAUTHBEARER:
+
+ if (!(iscf->auth_methods & NGX_MAIL_AUTH_OAUTHBEARER_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (s->args.nelts == 2) {
+ s->mail_state = ngx_imap_auth_oauthbearer;
+ return ngx_mail_auth_oauthbearer(s, c, 1);
+ }
+
+ ngx_str_set(&s->out, imap_plain_next);
+ s->mail_state = ngx_imap_auth_oauthbearer;
+
+ return NGX_OK;
}
return rc;
diff --git a/src/mail/ngx_mail_imap_module.c b/src/mail/ngx_mail_imap_module.c
--- a/src/mail/ngx_mail_imap_module.c
+++ b/src/mail/ngx_mail_imap_module.c
@@ -30,6 +30,8 @@ static ngx_conf_bitmask_t ngx_mail_imap
{ 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("xoauth2"), NGX_MAIL_AUTH_XOAUTH2_ENABLED },
+ { ngx_string("oauthbearer"), NGX_MAIL_AUTH_OAUTHBEARER_ENABLED },
{ ngx_null_string, 0 }
};
@@ -40,6 +42,8 @@ static ngx_str_t ngx_mail_imap_auth_met
ngx_null_string, /* APOP */
ngx_string("AUTH=CRAM-MD5"),
ngx_string("AUTH=EXTERNAL"),
+ ngx_string("AUTH=XOAUTH2"),
+ ngx_string("AUTH=OAUTHBEARER"),
ngx_null_string /* NONE */
};
@@ -182,7 +186,7 @@ ngx_mail_imap_merge_srv_conf(ngx_conf_t
}
for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m < NGX_MAIL_AUTH_NONE_ENABLED;
m <<= 1, i++)
{
if (m & conf->auth_methods) {
@@ -208,7 +212,7 @@ ngx_mail_imap_merge_srv_conf(ngx_conf_t
auth = p;
for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m < NGX_MAIL_AUTH_NONE_ENABLED;
m <<= 1, i++)
{
if (m & conf->auth_methods) {
diff --git a/src/mail/ngx_mail_parse.c b/src/mail/ngx_mail_parse.c
--- a/src/mail/ngx_mail_parse.c
+++ b/src/mail/ngx_mail_parse.c
@@ -953,6 +953,20 @@ ngx_mail_auth_parse(ngx_mail_session_t *
return NGX_MAIL_PARSE_INVALID_COMMAND;
}
+ if (arg[0].len == 7) {
+
+ if (ngx_strncasecmp(arg[0].data, (u_char *) "XOAUTH2", 7) == 0) {
+
+ if (s->args.nelts == 1 || s->args.nelts == 2) {
+ return NGX_MAIL_AUTH_XOAUTH2;
+ }
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
if (arg[0].len == 8) {
if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) {
@@ -976,5 +990,19 @@ ngx_mail_auth_parse(ngx_mail_session_t *
return NGX_MAIL_PARSE_INVALID_COMMAND;
}
+ if (arg[0].len == 11) {
+
+ if (ngx_strncasecmp(arg[0].data, (u_char *) "OAUTHBEARER", 11) == 0) {
+
+ if (s->args.nelts == 1 || s->args.nelts == 2) {
+ return NGX_MAIL_AUTH_OAUTHBEARER;
+ }
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
return NGX_MAIL_PARSE_INVALID_COMMAND;
}
diff --git a/src/mail/ngx_mail_pop3_handler.c b/src/mail/ngx_mail_pop3_handler.c
--- a/src/mail/ngx_mail_pop3_handler.c
+++ b/src/mail/ngx_mail_pop3_handler.c
@@ -260,6 +260,14 @@ ngx_mail_pop3_auth_state(ngx_event_t *re
case ngx_pop3_auth_external:
rc = ngx_mail_auth_external(s, c, 0);
break;
+
+ case ngx_pop3_auth_xoauth2:
+ rc = ngx_mail_auth_xoauth2(s, c, 0);
+ break;
+
+ case ngx_pop3_auth_oauthbearer:
+ rc = ngx_mail_auth_oauthbearer(s, c, 0);
+ break;
}
}
@@ -553,6 +561,38 @@ ngx_mail_pop3_auth(ngx_mail_session_t *s
s->mail_state = ngx_pop3_auth_external;
return NGX_OK;
+
+ case NGX_MAIL_AUTH_XOAUTH2:
+
+ if (!(pscf->auth_methods & NGX_MAIL_AUTH_XOAUTH2_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (s->args.nelts == 2) {
+ s->mail_state = ngx_pop3_auth_xoauth2;
+ return ngx_mail_auth_xoauth2(s, c, 1);
+ }
+
+ ngx_str_set(&s->out, pop3_next);
+ s->mail_state = ngx_pop3_auth_xoauth2;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_OAUTHBEARER:
+
+ if (!(pscf->auth_methods & NGX_MAIL_AUTH_OAUTHBEARER_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (s->args.nelts == 2) {
+ s->mail_state = ngx_pop3_auth_oauthbearer;
+ return ngx_mail_auth_oauthbearer(s, c, 1);
+ }
+
+ ngx_str_set(&s->out, pop3_next);
+ s->mail_state = ngx_pop3_auth_oauthbearer;
+
+ return NGX_OK;
}
return rc;
diff --git a/src/mail/ngx_mail_pop3_module.c b/src/mail/ngx_mail_pop3_module.c
--- a/src/mail/ngx_mail_pop3_module.c
+++ b/src/mail/ngx_mail_pop3_module.c
@@ -30,6 +30,8 @@ static ngx_conf_bitmask_t ngx_mail_pop3
{ 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_string("xoauth2"), NGX_MAIL_AUTH_XOAUTH2_ENABLED },
+ { ngx_string("oauthbearer"), NGX_MAIL_AUTH_OAUTHBEARER_ENABLED },
{ ngx_null_string, 0 }
};
@@ -40,6 +42,8 @@ static ngx_str_t ngx_mail_pop3_auth_met
ngx_null_string, /* APOP */
ngx_string("CRAM-MD5"),
ngx_string("EXTERNAL"),
+ ngx_string("XOAUTH2"),
+ ngx_string("OAUTHBEARER"),
ngx_null_string /* NONE */
};
@@ -183,7 +187,7 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t
size += sizeof("SASL") - 1 + sizeof(CRLF) - 1;
for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m < NGX_MAIL_AUTH_NONE_ENABLED;
m <<= 1, i++)
{
if (ngx_mail_pop3_auth_methods_names[i].len == 0) {
@@ -214,7 +218,7 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t
p = ngx_cpymem(p, "SASL", sizeof("SASL") - 1);
for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m < NGX_MAIL_AUTH_NONE_ENABLED;
m <<= 1, i++)
{
if (ngx_mail_pop3_auth_methods_names[i].len == 0) {
@@ -254,7 +258,7 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t
+ sizeof("." CRLF) - 1;
for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m < NGX_MAIL_AUTH_NONE_ENABLED;
m <<= 1, i++)
{
if (ngx_mail_pop3_auth_methods_names[i].len == 0) {
@@ -279,7 +283,7 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t
sizeof("+OK methods supported:" CRLF) - 1);
for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m < NGX_MAIL_AUTH_NONE_ENABLED;
m <<= 1, i++)
{
if (ngx_mail_pop3_auth_methods_names[i].len == 0) {
diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c
--- a/src/mail/ngx_mail_smtp_handler.c
+++ b/src/mail/ngx_mail_smtp_handler.c
@@ -548,6 +548,14 @@ ngx_mail_smtp_auth_state(ngx_event_t *re
case ngx_smtp_auth_external:
rc = ngx_mail_auth_external(s, c, 0);
break;
+
+ case ngx_smtp_auth_xoauth2:
+ rc = ngx_mail_auth_xoauth2(s, c, 0);
+ break;
+
+ case ngx_smtp_auth_oauthbearer:
+ rc = ngx_mail_auth_oauthbearer(s, c, 0);
+ break;
}
}
@@ -745,6 +753,38 @@ ngx_mail_smtp_auth(ngx_mail_session_t *s
s->mail_state = ngx_smtp_auth_external;
return NGX_OK;
+
+ case NGX_MAIL_AUTH_XOAUTH2:
+
+ if (!(sscf->auth_methods & NGX_MAIL_AUTH_XOAUTH2_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (s->args.nelts == 2) {
+ s->mail_state = ngx_smtp_auth_xoauth2;
+ return ngx_mail_auth_xoauth2(s, c, 1);
+ }
+
+ ngx_str_set(&s->out, smtp_next);
+ s->mail_state = ngx_smtp_auth_xoauth2;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_OAUTHBEARER:
+
+ if (!(sscf->auth_methods & NGX_MAIL_AUTH_OAUTHBEARER_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (s->args.nelts == 2) {
+ s->mail_state = ngx_smtp_auth_oauthbearer;
+ return ngx_mail_auth_oauthbearer(s, c, 1);
+ }
+
+ ngx_str_set(&s->out, smtp_next);
+ s->mail_state = ngx_smtp_auth_oauthbearer;
+
+ return NGX_OK;
}
return rc;
diff --git a/src/mail/ngx_mail_smtp_module.c b/src/mail/ngx_mail_smtp_module.c
--- a/src/mail/ngx_mail_smtp_module.c
+++ b/src/mail/ngx_mail_smtp_module.c
@@ -22,6 +22,8 @@ static ngx_conf_bitmask_t ngx_mail_smtp
{ 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("xoauth2"), NGX_MAIL_AUTH_XOAUTH2_ENABLED },
+ { ngx_string("oauthbearer"), NGX_MAIL_AUTH_OAUTHBEARER_ENABLED },
{ ngx_string("none"), NGX_MAIL_AUTH_NONE_ENABLED },
{ ngx_null_string, 0 }
};
@@ -33,6 +35,8 @@ static ngx_str_t ngx_mail_smtp_auth_met
ngx_null_string, /* APOP */
ngx_string("CRAM-MD5"),
ngx_string("EXTERNAL"),
+ ngx_string("XOAUTH2"),
+ ngx_string("OAUTHBEARER"),
ngx_null_string /* NONE */
};
@@ -210,7 +214,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t
auth_enabled = 0;
for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m < NGX_MAIL_AUTH_NONE_ENABLED;
m <<= 1, i++)
{
if (m & conf->auth_methods) {
@@ -253,7 +257,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t
*p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H';
for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
+ m < NGX_MAIL_AUTH_NONE_ENABLED;
m <<= 1, i++)
{
if (m & conf->auth_methods) {
More information about the nginx-devel
mailing list