IMAP AUTHENTICATE

patch by Maxim Dounin
This commit is contained in:
Igor Sysoev 2007-07-20 19:38:08 +00:00
parent 8241fb2998
commit 5d06f9b142
4 changed files with 442 additions and 119 deletions

View file

@ -100,6 +100,7 @@ typedef struct {
ngx_str_t smtp_greeting;
ngx_uint_t pop3_auth_methods;
ngx_uint_t imap_auth_methods;
ngx_uint_t smtp_auth_methods;
ngx_array_t pop3_capabilities;
@ -134,6 +135,10 @@ typedef enum {
typedef enum {
ngx_imap_start = 0,
ngx_imap_auth_login_username,
ngx_imap_auth_login_password,
ngx_imap_auth_plain,
ngx_imap_auth_cram_md5,
ngx_imap_login,
ngx_imap_user,
ngx_imap_passwd
@ -216,45 +221,47 @@ typedef struct {
} ngx_mail_log_ctx_t;
#define NGX_POP3_USER 1
#define NGX_POP3_PASS 2
#define NGX_POP3_CAPA 3
#define NGX_POP3_QUIT 4
#define NGX_POP3_NOOP 5
#define NGX_POP3_STLS 6
#define NGX_POP3_APOP 7
#define NGX_POP3_AUTH 8
#define NGX_POP3_STAT 9
#define NGX_POP3_LIST 10
#define NGX_POP3_RETR 11
#define NGX_POP3_DELE 12
#define NGX_POP3_RSET 13
#define NGX_POP3_TOP 14
#define NGX_POP3_UIDL 15
#define NGX_POP3_USER 1
#define NGX_POP3_PASS 2
#define NGX_POP3_CAPA 3
#define NGX_POP3_QUIT 4
#define NGX_POP3_NOOP 5
#define NGX_POP3_STLS 6
#define NGX_POP3_APOP 7
#define NGX_POP3_AUTH 8
#define NGX_POP3_STAT 9
#define NGX_POP3_LIST 10
#define NGX_POP3_RETR 11
#define NGX_POP3_DELE 12
#define NGX_POP3_RSET 13
#define NGX_POP3_TOP 14
#define NGX_POP3_UIDL 15
#define NGX_IMAP_LOGIN 1
#define NGX_IMAP_LOGOUT 2
#define NGX_IMAP_CAPABILITY 3
#define NGX_IMAP_NOOP 4
#define NGX_IMAP_STARTTLS 5
#define NGX_IMAP_LOGIN 1
#define NGX_IMAP_LOGOUT 2
#define NGX_IMAP_CAPABILITY 3
#define NGX_IMAP_NOOP 4
#define NGX_IMAP_STARTTLS 5
#define NGX_IMAP_NEXT 6
#define NGX_IMAP_NEXT 6
#define NGX_IMAP_AUTHENTICATE 7
#define NGX_SMTP_HELO 1
#define NGX_SMTP_EHLO 2
#define NGX_SMTP_AUTH 3
#define NGX_SMTP_QUIT 4
#define NGX_SMTP_NOOP 5
#define NGX_SMTP_MAIL 6
#define NGX_SMTP_RSET 7
#define NGX_SMTP_RCPT 8
#define NGX_SMTP_DATA 9
#define NGX_SMTP_VRFY 10
#define NGX_SMTP_EXPN 11
#define NGX_SMTP_HELP 12
#define NGX_SMTP_STARTTLS 13
#define NGX_SMTP_HELO 1
#define NGX_SMTP_EHLO 2
#define NGX_SMTP_AUTH 3
#define NGX_SMTP_QUIT 4
#define NGX_SMTP_NOOP 5
#define NGX_SMTP_MAIL 6
#define NGX_SMTP_RSET 7
#define NGX_SMTP_RCPT 8
#define NGX_SMTP_DATA 9
#define NGX_SMTP_VRFY 10
#define NGX_SMTP_EXPN 11
#define NGX_SMTP_HELP 12
#define NGX_SMTP_STARTTLS 13
#define NGX_MAIL_AUTH_PLAIN 0

View file

@ -54,6 +54,14 @@ static ngx_conf_bitmask_t ngx_pop3_auth_methods[] = {
};
static ngx_conf_bitmask_t ngx_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_null_string, 0 }
};
static ngx_conf_bitmask_t ngx_smtp_auth_methods[] = {
{ ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
{ ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },
@ -62,6 +70,14 @@ static ngx_conf_bitmask_t ngx_smtp_auth_methods[] = {
};
static ngx_str_t ngx_imap_auth_methods_names[] = {
ngx_string("AUTH=PLAIN"),
ngx_string("AUTH=LOGIN"),
ngx_null_string, /* APOP */
ngx_string("AUTH=CRAM-MD5")
};
static ngx_str_t ngx_smtp_auth_methods_names[] = {
ngx_string("PLAIN"),
ngx_string("LOGIN"),
@ -172,6 +188,13 @@ static ngx_command_t ngx_mail_core_commands[] = {
offsetof(ngx_mail_core_srv_conf_t, pop3_auth_methods),
&ngx_pop3_auth_methods },
{ ngx_string("imap_auth"),
NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_MAIL_SRV_CONF_OFFSET,
offsetof(ngx_mail_core_srv_conf_t, imap_auth_methods),
&ngx_imap_auth_methods },
{ ngx_string("smtp_auth"),
NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
@ -297,6 +320,11 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
(NGX_CONF_BITMASK_SET
|NGX_MAIL_AUTH_PLAIN_ENABLED));
ngx_conf_merge_bitmask_value(conf->imap_auth_methods,
prev->imap_auth_methods,
(NGX_CONF_BITMASK_SET
|NGX_MAIL_AUTH_PLAIN_ENABLED));
ngx_conf_merge_bitmask_value(conf->smtp_auth_methods,
prev->smtp_auth_methods,
(NGX_CONF_BITMASK_SET
@ -463,6 +491,15 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
size += 1 + c[i].len;
}
for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
m <<= 1, i++)
{
if (m & conf->imap_auth_methods) {
size += 1 + ngx_imap_auth_methods_names[i].len;
}
}
p = ngx_palloc(cf->pool, size);
if (p == NULL) {
return NGX_CONF_ERROR;
@ -478,6 +515,19 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
p = ngx_cpymem(p, c[i].data, c[i].len);
}
auth = p;
for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
m <<= 1, i++)
{
if (m & conf->imap_auth_methods) {
*p++ = ' ';
p = ngx_cpymem(p, ngx_imap_auth_methods_names[i].data,
ngx_imap_auth_methods_names[i].len);
}
}
*p++ = CR; *p = LF;
@ -497,7 +547,8 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
*p++ = CR; *p = LF;
size += sizeof(" LOGINDISABLED") - 1;
size = (auth - conf->imap_capability.data) + sizeof(CRLF) - 1
+ sizeof(" STARTTLS LOGINDISABLED") - 1;
p = ngx_palloc(cf->pool, size);
if (p == NULL) {
@ -507,9 +558,10 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
conf->imap_starttls_only_capability.len = size;
conf->imap_starttls_only_capability.data = p;
p = ngx_cpymem(p, conf->imap_starttls_capability.data,
conf->imap_starttls_capability.len - (sizeof(CRLF) - 1));
p = ngx_cpymem(p, " LOGINDISABLED", sizeof(" LOGINDISABLED") - 1);
p = ngx_cpymem(p, conf->imap_capability.data,
auth - conf->imap_capability.data);
p = ngx_cpymem(p, " STARTTLS LOGINDISABLED",
sizeof(" STARTTLS LOGINDISABLED") - 1);
*p++ = CR; *p = LF;

View file

@ -280,6 +280,9 @@ ngx_mail_init_session(ngx_connection_t *c)
&& (cscf->pop3_auth_methods
& (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED)))
|| (s->protocol == NGX_MAIL_IMAP_PROTOCOL
&& (cscf->imap_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED))
|| (s->protocol == NGX_MAIL_SMTP_PROTOCOL
&& (cscf->smtp_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)))
{
@ -985,7 +988,7 @@ ngx_imap_auth_state(ngx_event_t *rev)
{
u_char *p, *last, *text, *dst, *src, *end;
ssize_t text_len, last_len;
ngx_str_t *arg;
ngx_str_t *arg, salt;
ngx_int_t rc;
ngx_uint_t tag, i;
ngx_connection_t *c;
@ -1055,113 +1058,342 @@ ngx_imap_auth_state(ngx_event_t *rev)
s->backslash = 0;
}
switch (s->command) {
switch (s->mail_state) {
case NGX_IMAP_LOGIN:
case ngx_imap_start:
switch (s->command) {
case NGX_IMAP_LOGIN:
#if (NGX_MAIL_SSL)
if (c->ssl == NULL) {
sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
if (c->ssl == NULL) {
sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
}
}
#endif
arg = s->args.elts;
if (s->args.nelts == 2 && arg[0].len) {
s->login.len = arg[0].len;
s->login.data = ngx_palloc(c->pool, s->login.len);
if (s->login.data == NULL) {
ngx_mail_session_internal_server_error(s);
return;
}
ngx_memcpy(s->login.data, arg[0].data, s->login.len);
s->passwd.len = arg[1].len;
s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
if (s->passwd.data == NULL) {
ngx_mail_session_internal_server_error(s);
return;
}
ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
#if (NGX_DEBUG_MAIL_PASSWD)
ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
"imap login:\"%V\" passwd:\"%V\"",
&s->login, &s->passwd);
#else
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
"imap login:\"%V\"", &s->login);
#endif
ngx_mail_do_auth(s);
return;
}
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
case NGX_IMAP_AUTHENTICATE:
#if (NGX_MAIL_SSL)
if (c->ssl == NULL) {
sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
}
}
#endif
if (s->args.nelts != 1) {
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
}
}
arg = s->args.elts;
if (arg[0].len == 5) {
if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5)
== 0)
{
s->mail_state = ngx_imap_auth_login_username;
last_len = sizeof(pop3_username) - 1;
last = pop3_username;
tag = 0;
break;
} else if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN",
5)
== 0)
{
s->mail_state = ngx_imap_auth_plain;
last_len = sizeof(pop3_next) - 1;
last = pop3_next;
tag = 0;
break;
}
} else if (arg[0].len == 8
&& ngx_strncasecmp(arg[0].data,
(u_char *) "CRAM-MD5", 8)
== 0)
{
cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
if (!(cscf->imap_auth_methods
& NGX_MAIL_AUTH_CRAM_MD5_ENABLED)
|| s->args.nelts != 1)
{
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
}
s->mail_state = ngx_imap_auth_cram_md5;
last = ngx_palloc(c->pool,
sizeof("+ " CRLF) - 1
+ ngx_base64_encoded_length(s->salt.len));
if (last == NULL) {
ngx_mail_session_internal_server_error(s);
return;
}
last[0] = '+'; last[1]= ' ';
salt.data = &last[2];
s->salt.len -= 2;
ngx_encode_base64(&salt, &s->salt);
s->salt.len += 2;
last_len = 2 + salt.len;
last[last_len++] = CR; last[last_len++] = LF;
tag = 0;
break;
}
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
case NGX_IMAP_CAPABILITY:
cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
#if (NGX_MAIL_SSL)
if (c->ssl == NULL) {
sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
text_len = cscf->imap_starttls_capability.len;
text = cscf->imap_starttls_capability.data;
break;
}
if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
text_len = cscf->imap_starttls_only_capability.len;
text = cscf->imap_starttls_only_capability.data;
break;
}
}
#endif
text_len = cscf->imap_capability.len;
text = cscf->imap_capability.data;
break;
case NGX_IMAP_LOGOUT:
s->quit = 1;
text = imap_bye;
text_len = sizeof(imap_bye) - 1;
break;
case NGX_IMAP_NOOP:
break;
#if (NGX_MAIL_SSL)
case NGX_IMAP_STARTTLS:
if (c->ssl == NULL) {
sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
if (sslcf->starttls) {
c->read->handler = ngx_mail_starttls_handler;
break;
}
}
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
#endif
default:
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
}
break;
case ngx_imap_auth_login_username:
arg = s->args.elts;
s->mail_state = ngx_imap_auth_login_password;
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
"imap auth login username: \"%V\"", &arg[0]);
s->login.data = ngx_palloc(c->pool,
ngx_base64_decoded_length(arg[0].len));
if (s->login.data == NULL){
ngx_mail_session_internal_server_error(s);
return;
}
if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid base64 encoding "
"in AUTH LOGIN command");
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
}
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
"imap auth login username: \"%V\"", &s->login);
last_len = sizeof(pop3_password) - 1;
last = pop3_password;
tag = 0;
break;
case ngx_imap_auth_login_password:
arg = s->args.elts;
if (s->args.nelts == 2 && arg[0].len) {
s->login.len = arg[0].len;
s->login.data = ngx_palloc(c->pool, s->login.len);
if (s->login.data == NULL) {
ngx_mail_session_internal_server_error(s);
return;
}
ngx_memcpy(s->login.data, arg[0].data, s->login.len);
s->passwd.len = arg[1].len;
s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
if (s->passwd.data == NULL) {
ngx_mail_session_internal_server_error(s);
return;
}
ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
#if (NGX_DEBUG_MAIL_PASSWD)
ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
"imap login:\"%V\" passwd:\"%V\"",
&s->login, &s->passwd);
#else
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
"imap login:\"%V\"", &s->login);
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
"imap auth login password: \"%V\"", &arg[0]);
#endif
s->passwd.data = ngx_palloc(c->pool,
ngx_base64_decoded_length(arg[0].len));
if (s->passwd.data == NULL){
ngx_mail_session_internal_server_error(s);
return;
}
if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid base64 encoding "
"in AUTH LOGIN command");
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
}
#if (NGX_DEBUG_MAIL_PASSWD)
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
"imap auth login password: \"%V\"", &s->passwd);
#endif
ngx_mail_do_auth(s);
return;
case ngx_imap_auth_plain:
arg = s->args.elts;
rc = ngx_mail_decode_auth_plain(s, &arg[0]);
if (rc == NGX_OK) {
ngx_mail_do_auth(s);
return;
}
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
case NGX_IMAP_CAPABILITY:
cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
#if (NGX_MAIL_SSL)
if (c->ssl == NULL) {
sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
text_len = cscf->imap_starttls_capability.len;
text = cscf->imap_starttls_capability.data;
break;
}
if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
text_len = cscf->imap_starttls_only_capability.len;
text = cscf->imap_starttls_only_capability.data;
break;
}
if (rc == NGX_ERROR) {
ngx_mail_session_internal_server_error(s);
return;
}
#endif
text_len = cscf->imap_capability.len;
text = cscf->imap_capability.data;
/* rc == NGX_MAIL_PARSE_INVALID_COMMAND */
break;
case NGX_IMAP_LOGOUT:
s->quit = 1;
text = imap_bye;
text_len = sizeof(imap_bye) - 1;
break;
case ngx_imap_auth_cram_md5:
arg = s->args.elts;
case NGX_IMAP_NOOP:
break;
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
"imap auth cram-md5: \"%V\"", &arg[0]);
#if (NGX_MAIL_SSL)
s->login.data = ngx_palloc(c->pool,
ngx_base64_decoded_length(arg[0].len));
if (s->login.data == NULL){
ngx_mail_session_internal_server_error(s);
return;
}
case NGX_IMAP_STARTTLS:
if (c->ssl == NULL) {
sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
if (sslcf->starttls) {
c->read->handler = ngx_mail_starttls_handler;
if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid base64 encoding "
"in AUTH CRAM-MD5 command");
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
}
p = s->login.data;
last = p + s->login.len;
while (p < last) {
if (*p++ == ' ') {
s->login.len = p - s->login.data - 1;
s->passwd.len = last - p;
s->passwd.data = p;
break;
}
}
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
#endif
if (s->passwd.len != 32) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid CRAM-MD5 hash "
"in AUTH CRAM-MD5 command");
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
}
default:
rc = NGX_MAIL_PARSE_INVALID_COMMAND;
break;
ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
"imap auth cram-md5: \"%V\" \"%V\"",
&s->login, &s->passwd);
s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;
ngx_mail_do_auth(s);
return;
}
} else if (rc == NGX_IMAP_NEXT) {
@ -1171,6 +1403,8 @@ ngx_imap_auth_state(ngx_event_t *rev)
}
if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) {
s->mail_state = ngx_imap_start;
s->state = 0;
last = imap_invalid_command;
last_len = sizeof(imap_invalid_command) - 1;
}
@ -1209,9 +1443,18 @@ ngx_imap_auth_state(ngx_event_t *rev)
if (rc != NGX_IMAP_NEXT) {
s->args.nelts = 0;
s->buffer->pos = s->buffer->start;
s->buffer->last = s->buffer->start;
s->tag.len = 0;
if (s->state) {
/* preserve tag */
s->arg_start = s->buffer->start + s->tag.len;
s->buffer->pos = s->arg_start;
s->buffer->last = s->arg_start;
} else {
s->buffer->pos = s->buffer->start;
s->buffer->last = s->buffer->start;
s->tag.len = 0;
}
}
ngx_mail_send(c->write);

View file

@ -354,6 +354,27 @@ ngx_int_t ngx_imap_parse_command(ngx_mail_session_t *s)
}
break;
case 12:
if ((c[0] == 'A'|| c[0] == 'a')
&& (c[1] == 'U'|| c[1] == 'u')
&& (c[2] == 'T'|| c[2] == 't')
&& (c[3] == 'H'|| c[3] == 'h')
&& (c[4] == 'E'|| c[4] == 'e')
&& (c[5] == 'N'|| c[5] == 'n')
&& (c[6] == 'T'|| c[6] == 't')
&& (c[7] == 'I'|| c[7] == 'i')
&& (c[8] == 'C'|| c[8] == 'c')
&& (c[9] == 'A'|| c[9] == 'a')
&& (c[10] == 'T'|| c[10] == 't')
&& (c[11] == 'E'|| c[11] == 'e'))
{
s->command = NGX_IMAP_AUTHENTICATE;
} else {
goto invalid;
}
break;
default:
goto invalid;
}
@ -573,7 +594,7 @@ done:
s->literal_len = 0;
}
s->state = sw_start;
s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;
return NGX_OK;