# HG changeset patch # User Igor Sysoev # Date 1161715111 0 # Node ID da9c1521319d40828ad0eb21b36b14182e06a86f # Parent eef6d9cc45da12a7ef5bf70fd951677be6520719 AUTH PLAIN LOGIN CRAM-MD5 diff --git a/src/imap/ngx_imap.h b/src/imap/ngx_imap.h --- a/src/imap/ngx_imap.h +++ b/src/imap/ngx_imap.h @@ -83,6 +83,8 @@ typedef struct { ngx_str_t pop3_capability; ngx_str_t pop3_starttls_capability; + ngx_str_t pop3_auth_capability; + ngx_str_t imap_capability; ngx_str_t imap_starttls_capability; ngx_str_t imap_starttls_only_capability; @@ -120,7 +122,11 @@ typedef enum { typedef enum { ngx_pop3_start = 0, ngx_pop3_user, - ngx_pop3_passwd + ngx_pop3_passwd, + ngx_pop3_auth_login_username, + ngx_pop3_auth_login_password, + ngx_pop3_auth_plain, + ngx_pop3_auth_cram_md5 } ngx_po3_state_e; @@ -153,7 +159,7 @@ typedef struct { unsigned backslash:1; unsigned no_sync_literal:1; unsigned starttls:1; - unsigned auth_method:1; + unsigned auth_method:2; ngx_str_t login; ngx_str_t passwd; @@ -192,13 +198,14 @@ typedef struct { #define NGX_POP3_NOOP 5 #define NGX_POP3_STLS 6 #define NGX_POP3_APOP 7 -#define NGX_POP3_STAT 8 -#define NGX_POP3_LIST 9 -#define NGX_POP3_RETR 10 -#define NGX_POP3_DELE 11 -#define NGX_POP3_RSET 12 -#define NGX_POP3_TOP 13 -#define NGX_POP3_UIDL 14 +#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 @@ -210,12 +217,14 @@ typedef struct { #define NGX_IMAP_NEXT 6 -#define NGX_IMAP_AUTH_PLAIN 0 -#define NGX_IMAP_AUTH_APOP 1 +#define NGX_IMAP_AUTH_PLAIN 0 +#define NGX_IMAP_AUTH_APOP 1 +#define NGX_IMAP_AUTH_CRAM_MD5 2 -#define NGX_IMAP_AUTH_PLAIN_ENABLED 0x0002 -#define NGX_IMAP_AUTH_APOP_ENABLED 0x0004 +#define NGX_IMAP_AUTH_PLAIN_ENABLED 0x0002 +#define NGX_IMAP_AUTH_APOP_ENABLED 0x0004 +#define NGX_IMAP_AUTH_CRAM_MD5_ENABLED 0x0008 #define NGX_IMAP_PARSE_INVALID_COMMAND 20 diff --git a/src/imap/ngx_imap_auth_http_module.c b/src/imap/ngx_imap_auth_http_module.c --- a/src/imap/ngx_imap_auth_http_module.c +++ b/src/imap/ngx_imap_auth_http_module.c @@ -133,7 +133,9 @@ ngx_module_t ngx_imap_auth_http_module static char *ngx_imap_auth_http_protocol[] = { "pop3", "imap" }; static ngx_str_t ngx_imap_auth_http_method[] = { - ngx_string("plain"), ngx_string("apop") + ngx_string("plain"), + ngx_string("apop"), + ngx_string("cram-md5") }; @@ -1078,7 +1080,7 @@ ngx_imap_auth_http_create_request(ngx_im b->last = ngx_copy(b->last, passwd.data, passwd.len); *b->last++ = CR; *b->last++ = LF; - if (s->salt.len) { + if (s->auth_method != NGX_IMAP_AUTH_PLAIN && s->salt.len) { b->last = ngx_cpymem(b->last, "Auth-Salt: ", sizeof("Auth-Salt: ") - 1); b->last = ngx_copy(b->last, s->salt.data, s->salt.len); diff --git a/src/imap/ngx_imap_core_module.c b/src/imap/ngx_imap_core_module.c --- a/src/imap/ngx_imap_core_module.c +++ b/src/imap/ngx_imap_core_module.c @@ -48,10 +48,27 @@ static ngx_str_t ngx_imap_default_capab static ngx_conf_bitmask_t ngx_imap_auth_methods[] = { { ngx_string("plain"), NGX_IMAP_AUTH_PLAIN_ENABLED }, { ngx_string("apop"), NGX_IMAP_AUTH_APOP_ENABLED }, + { ngx_string("cram-md5"), NGX_IMAP_AUTH_CRAM_MD5_ENABLED }, { ngx_null_string, 0 } }; +static ngx_str_t ngx_pop3_auth_plain_capability = + ngx_string("+OK methods supported:" CRLF + "LOGIN" CRLF + "PLAIN" CRLF + "." CRLF); + + +static ngx_str_t ngx_pop3_auth_cram_md5_capability = + ngx_string("+OK methods supported:" CRLF + "LOGIN" CRLF + "PLAIN" CRLF + "CRAM-MD5" CRLF + "." CRLF); + + + static ngx_command_t ngx_imap_core_commands[] = { { ngx_string("server"), @@ -279,6 +296,13 @@ ngx_imap_core_merge_srv_conf(ngx_conf_t size += c[i].len + sizeof(CRLF) - 1; } + if (conf->auth_methods & NGX_IMAP_AUTH_CRAM_MD5_ENABLED) { + size += sizeof("SASL LOGIN PLAIN CRAM-MD5" CRLF) - 1; + + } else { + size += sizeof("SASL LOGIN PLAIN" CRLF) - 1; + } + p = ngx_palloc(cf->pool, size); if (p == NULL) { return NGX_CONF_ERROR; @@ -295,6 +319,15 @@ ngx_imap_core_merge_srv_conf(ngx_conf_t *p++ = CR; *p++ = LF; } + if (conf->auth_methods & NGX_IMAP_AUTH_CRAM_MD5_ENABLED) { + p = ngx_cpymem(p, "SASL LOGIN PLAIN CRAM-MD5" CRLF, + sizeof("SASL LOGIN PLAIN CRAM-MD5" CRLF) - 1); + + } else { + p = ngx_cpymem(p, "SASL LOGIN PLAIN" CRLF, + sizeof("SASL LOGIN PLAIN" CRLF) - 1); + } + *p++ = '.'; *p++ = CR; *p = LF; @@ -315,6 +348,14 @@ ngx_imap_core_merge_srv_conf(ngx_conf_t *p++ = '.'; *p++ = CR; *p = LF; + if (conf->auth_methods & NGX_IMAP_AUTH_CRAM_MD5_ENABLED) { + conf->pop3_auth_capability = ngx_pop3_auth_cram_md5_capability; + + } else { + conf->pop3_auth_capability = ngx_pop3_auth_plain_capability; + } + + if (conf->imap_capabilities.nelts == 0) { conf->imap_capabilities = prev->imap_capabilities; } diff --git a/src/imap/ngx_imap_handler.c b/src/imap/ngx_imap_handler.c --- a/src/imap/ngx_imap_handler.c +++ b/src/imap/ngx_imap_handler.c @@ -32,6 +32,9 @@ static ngx_str_t internal_server_errors }; static u_char pop3_ok[] = "+OK" CRLF; +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 imap_star[] = "* "; @@ -547,11 +550,9 @@ ngx_imap_auth_state(ngx_event_t *rev) ngx_imap_auth_http_init(s); return; - - } else { - rc = NGX_IMAP_PARSE_INVALID_COMMAND; } + rc = NGX_IMAP_PARSE_INVALID_COMMAND; break; case NGX_IMAP_CAPABILITY: @@ -666,10 +667,10 @@ ngx_imap_auth_state(ngx_event_t *rev) void ngx_pop3_auth_state(ngx_event_t *rev) { - u_char *text; + u_char *text, *p, *last; ssize_t size; ngx_int_t rc; - ngx_str_t *arg; + ngx_str_t *arg, salt, plain; ngx_connection_t *c; ngx_imap_session_t *s; ngx_imap_core_srv_conf_t *cscf; @@ -730,10 +731,10 @@ ngx_pop3_auth_state(ngx_event_t *rev) ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, "pop3 login: \"%V\"", &s->login); - } else { - rc = NGX_IMAP_PARSE_INVALID_COMMAND; + break; } + rc = NGX_IMAP_PARSE_INVALID_COMMAND; break; case NGX_POP3_CAPA: @@ -799,11 +800,78 @@ ngx_pop3_auth_state(ngx_event_t *rev) ngx_imap_auth_http_init(s); return; + } - } else { + rc = NGX_IMAP_PARSE_INVALID_COMMAND; + break; + + case NGX_POP3_AUTH: + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); + + if (s->args.nelts == 0) { + size = cscf->pop3_auth_capability.len; + text = cscf->pop3_auth_capability.data; + break; + } + + if (s->args.nelts != 1) { rc = NGX_IMAP_PARSE_INVALID_COMMAND; + break; } + arg = s->args.elts; + + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + s->arg_start = s->buffer->start; + + if (arg[0].len == 5) { + + if (ngx_strncasecmp(arg[0].data, "LOGIN", 5) == 0) { + s->imap_state = ngx_pop3_auth_login_username; + + size = sizeof(pop3_username) - 1; + text = pop3_username; + + break; + + } else if (ngx_strncasecmp(arg[0].data, "PLAIN", 5) == 0) { + s->imap_state = ngx_pop3_auth_plain; + + size = sizeof(pop3_next) - 1; + text = pop3_next; + + break; + } + + } else if (arg[0].len == 8 + && ngx_strncasecmp(arg[0].data, "CRAM-MD5", 8) == 0) + { + s->imap_state = ngx_pop3_auth_cram_md5; + + text = ngx_palloc(c->pool, + sizeof("+ " CRLF) - 1 + + ngx_base64_encoded_length(s->salt.len)); + if (text == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + text[0] = '+'; text[1]= ' '; + salt.data = &text[2]; + s->salt.len -= 2; + + ngx_encode_base64(&salt, &s->salt); + + s->salt.len += 2; + size = 2 + salt.len; + text[size++] = CR; text[size++] = LF; + + break; + } + + rc = NGX_IMAP_PARSE_INVALID_COMMAND; break; case NGX_POP3_QUIT: @@ -869,11 +937,9 @@ ngx_pop3_auth_state(ngx_event_t *rev) ngx_imap_auth_http_init(s); return; - - } else { - rc = NGX_IMAP_PARSE_INVALID_COMMAND; } + rc = NGX_IMAP_PARSE_INVALID_COMMAND; break; case NGX_POP3_CAPA: @@ -900,6 +966,189 @@ ngx_pop3_auth_state(ngx_event_t *rev) /* suppress warinings */ case ngx_pop3_passwd: break; + + case ngx_pop3_auth_login_username: + arg = s->args.elts; + s->imap_state = ngx_pop3_auth_login_password; + + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + s->arg_start = s->buffer->start; + + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, + "pop3 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_imap_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"); + ngx_imap_session_internal_server_error(s); + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, + "pop3 auth login username: \"%V\"", &s->login); + + size = sizeof(pop3_password) - 1; + text = pop3_password; + + break; + + case ngx_pop3_auth_login_password: + arg = s->args.elts; + +#if (NGX_DEBUG_IMAP_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, + "pop3 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_imap_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"); + ngx_imap_session_internal_server_error(s); + return; + } + +#if (NGX_DEBUG_IMAP_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, + "pop3 auth login password: \"%V\"", &s->passwd); +#endif + + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + + if (rev->timer_set) { + ngx_del_timer(rev); + } + + ngx_imap_auth_http_init(s); + + return; + + case ngx_pop3_auth_plain: + arg = s->args.elts; + +#if (NGX_DEBUG_IMAP_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, + "pop3 auth plain: \"%V\"", &arg[0]); +#endif + + plain.data = ngx_palloc(c->pool, + ngx_base64_decoded_length(arg[0].len)); + if (plain.data == NULL){ + ngx_imap_session_internal_server_error(s); + return; + } + + if (ngx_decode_base64(&plain, &arg[0]) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid base64 encoding " + "in AUTH PLAIN command"); + ngx_imap_session_internal_server_error(s); + return; + } + + p = plain.data; + last = p + plain.len; + + while (p < last && *p++) { /* void */ } + + s->login.data = p; + + while (p < last && *p) { p++; } + + s->login.len = p++ - s->login.data; + s->passwd.data = p; + + while (p < last && *p) { p++; } + + s->passwd.len = p - s->passwd.data; + +#if (NGX_DEBUG_IMAP_PASSWD) + ngx_log_debug2(NGX_LOG_DEBUG_IMAP, c->log, 0, + "pop3 auth plain: \"%V\" \"%V\"", + &s->login, &s->passwd); +#endif + + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + + if (rev->timer_set) { + ngx_del_timer(rev); + } + + ngx_imap_auth_http_init(s); + + return; + + case ngx_pop3_auth_cram_md5: + arg = s->args.elts; + + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, + "pop3 auth cram-md5: \"%V\"", &arg[0]); + + s->login.data = ngx_palloc(c->pool, + ngx_base64_decoded_length(arg[0].len)); + if (s->login.data == NULL){ + ngx_imap_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"); + ngx_imap_session_internal_server_error(s); + return; + } + + 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; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_IMAP, c->log, 0, + "pop3 auth cram-md5: \"%V\" \"%V\"", + &s->login, &s->passwd); + + s->auth_method = NGX_IMAP_AUTH_CRAM_MD5; + + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + + if (rev->timer_set) { + ngx_del_timer(rev); + } + + ngx_imap_auth_http_init(s); + + return; } } diff --git a/src/imap/ngx_imap_parse.c b/src/imap/ngx_imap_parse.c --- a/src/imap/ngx_imap_parse.c +++ b/src/imap/ngx_imap_parse.c @@ -441,6 +441,10 @@ ngx_int_t ngx_pop3_parse_command(ngx_ima { s->command = NGX_POP3_CAPA; + } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H') + { + s->command = NGX_POP3_AUTH; + } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P') { s->command = NGX_POP3_NOOP; @@ -571,8 +575,7 @@ done: s->arg_start = NULL; } - s->state = sw_start; - + s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument; return NGX_OK; invalid: