# HG changeset patch # User Igor Sysoev # Date 1190318400 -14400 # Node ID 1c519aff5c0cc50bbc6db813f93767d6cf4569bc # Parent 9a32ae248b7a1332a699f1a64886a99063669cb4 nginx 0.6.12 *) Change: mail proxy was split on three modules: pop3, imap and smtp. *) Feature: the --without-mail_pop3_module, --without-mail_imap_module, and --without-mail_smtp_module configuration parameters. *) Feature: the "smtp_greeting_delay" and "smtp_client_buffer" directives of the ngx_mail_smtp_module. *) Bugfix: the trailing wildcards did not work; bug appeared in 0.6.9. *) Bugfix: nginx could not start on Solaris if the shared PCRE library located in non-standard place was used. *) Bugfix: the "proxy_hide_header" and "fastcgi_hide_header" directives did not hide response header lines whose name was longer than 32 characters. Thanks to Manlio Perillo. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,40 @@ +Changes with nginx 0.6.12 21 Sep 2007 + + *) Change: mail proxy was split on three modules: pop3, imap and smtp. + + *) Feature: the --without-mail_pop3_module, --without-mail_imap_module, + and --without-mail_smtp_module configuration parameters. + + *) Feature: the "smtp_greeting_delay" and "smtp_client_buffer" + directives of the ngx_mail_smtp_module. + + *) Bugfix: the trailing wildcards did not work; bug appeared in 0.6.9. + + *) Bugfix: nginx could not start on Solaris if the shared PCRE library + located in non-standard place was used. + + *) Bugfix: the "proxy_hide_header" and "fastcgi_hide_header" directives + did not hide response header lines whose name was longer than 32 + characters. + Thanks to Manlio Perillo. + + +Changes with nginx 0.6.11 11 Sep 2007 + + *) Bugfix: active connection counter always increased if mail proxy was + used. + + *) Bugfix: if backend returned response header only using non-buffered + proxy, then nginx closed backend connection on timeout. + + *) Bugfix: nginx did not support several "Connection" request header + lines. + + *) Bugfix: if the "max_fails" was set for upstream server, then after + first failure server weight was always one; bug appeared in 0.6.6. + + Changes with nginx 0.6.10 03 Sep 2007 *) Feature: the "open_file_cache", "open_file_cache_retest", and diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,43 @@ +Изменения в nginx 0.6.12 21.09.2007 + + *) Изменение: почтовый прокси-сервер разделён на три модуля: pop3, imap + и smtp. + + *) Добавление: параметры конфигурации --without-mail_pop3_module, + --without-mail_imap_module и --without-mail_smtp_module. + + *) Добавление: директивы smtp_greeting_delay и smtp_client_buffer + модуля ngx_mail_smtp_module. + + *) Исправление: wildcard в конце имени сервера не работали; ошибка + появилась в 0.6.9. + + *) Исправление: при использовании разделяемой библиотеки PCRE, + расположенной в нестандартном месте, nginx не запускался на Solaris. + + *) Исправление: директивы proxy_hide_header и fastcgi_hide_header не + скрывали строки заголовка ответа с именем больше 32 символов. + Спасибо Manlio Perillo. + + +Изменения в nginx 0.6.11 11.09.2007 + + *) Исправление: счётчик активных соединений всегда рос при + использовании почтового прокси-сервера. + + *) Исправление: если бэкенд возвращал только заголовок ответа при + небуферизированном проксировании, то nginx закрывал соединение с + бэкендом по таймауту. + + *) Исправление: nginx не поддерживал несколько строк "Connection" в + заголовке запроса. + + *) Исправление: если в сервере апстрима был задан max_fails, то после + первой же неудачной попытки вес сервера навсегда становился равным + одному; ошибка появилась в 0.6.6. + + Изменения в nginx 0.6.10 03.09.2007 *) Добавление: директивы open_file_cache, open_file_cache_retest и diff --git a/auto/lib/pcre/conf b/auto/lib/pcre/conf --- a/auto/lib/pcre/conf +++ b/auto/lib/pcre/conf @@ -77,7 +77,6 @@ if [ $PCRE != NONE ]; then CORE_DEPS="$CORE_DEPS $PCRE/pcre.h" LINK_DEPS="$LINK_DEPS $PCRE/.libs/libpcre.a" CORE_LIBS="$CORE_LIBS $PCRE/.libs/libpcre.a" - #CORE_LIBS="$CORE_LIBS -L $PCRE/.libs -lpcre" ;; esac @@ -111,7 +110,13 @@ else ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path="/usr/local/include" - ngx_feature_libs="-L /usr/local/lib -lpcre" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lpcre" + else + ngx_feature_libs="-L/usr/local/lib -lpcre" + fi + ngx_feature_test="pcre *re; re = pcre_compile(NULL, 0, NULL, 0, NULL)" . auto/feature @@ -160,7 +165,13 @@ else ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path="/usr/pkg/include" - ngx_feature_libs="-L /usr/pkg/lib -lpcre" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lpcre" + else + ngx_feature_libs="-L/usr/pkg/lib -lpcre" + fi + ngx_feature_test="pcre *re; re = pcre_compile(NULL, 0, NULL, 0, NULL)" . auto/feature @@ -185,7 +196,13 @@ else ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path="/opt/local/include" - ngx_feature_libs="-L/opt/local/lib -lpcre" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lpcre" + else + ngx_feature_libs="-L/opt/local/lib -lpcre" + fi + ngx_feature_test="pcre *re; re = pcre_compile(NULL, 0, NULL, 0, NULL)" . auto/feature diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -308,8 +308,6 @@ fi if [ $MAIL_SSL = YES ]; then - MAIL_DEPS="$MAIL_DEPS $MAIL_SSL_DEPS" - MAIL_SRCS="$MAIL_SRCS $MAIL_SSL_SRCS" have=NGX_MAIL_SSL . auto/have USE_OPENSSL=YES fi @@ -341,6 +339,26 @@ if [ $MAIL = YES ]; then if [ $MAIL_SSL = YES ]; then modules="$modules $MAIL_SSL_MODULE" + MAIL_DEPS="$MAIL_DEPS $MAIL_SSL_DEPS" + MAIL_SRCS="$MAIL_SRCS $MAIL_SSL_SRCS" + fi + + if [ $MAIL_POP3 = YES ]; then + modules="$modules $MAIL_POP3_MODULE" + MAIL_DEPS="$MAIL_DEPS $MAIL_POP3_DEPS" + MAIL_SRCS="$MAIL_SRCS $MAIL_POP3_SRCS" + fi + + if [ $MAIL_IMAP = YES ]; then + modules="$modules $MAIL_IMAP_MODULE" + MAIL_DEPS="$MAIL_DEPS $MAIL_IMAP_DEPS" + MAIL_SRCS="$MAIL_SRCS $MAIL_IMAP_SRCS" + fi + + if [ $MAIL_SMTP = YES ]; then + modules="$modules $MAIL_SMTP_MODULE" + MAIL_DEPS="$MAIL_DEPS $MAIL_SMTP_DEPS" + MAIL_SRCS="$MAIL_SRCS $MAIL_SMTP_SRCS" fi modules="$modules $MAIL_AUTH_HTTP_MODULE" diff --git a/auto/options b/auto/options --- a/auto/options +++ b/auto/options @@ -23,6 +23,8 @@ NGX_CC_OPT= NGX_LD_OPT= CPU=NO +NGX_RPATH=NO + NGX_TEST_BUILD_DEVPOLL=NO NGX_TEST_BUILD_EVENTPORT=NO NGX_TEST_BUILD_EPOLL=NO @@ -81,6 +83,9 @@ HTTP_STUB_STATUS=NO MAIL=NO MAIL_SSL=NO +MAIL_POP3=YES +MAIL_IMAP=YES +MAIL_SMTP=YES NGX_ADDONS= @@ -190,6 +195,9 @@ do # STUB --with-imap) MAIL=YES ;; --with-imap_ssl_module) MAIL_SSL=YES ;; + --without-mail_pop3_module) MAIL_POP3=NO ;; + --without-mail_imap_module) MAIL_IMAP=NO ;; + --without-mail_smtp_module) MAIL_SMTP=NO ;; --add-module=*) NGX_ADDONS="$NGX_ADDONS $value" ;; diff --git a/auto/os/solaris b/auto/os/solaris --- a/auto/os/solaris +++ b/auto/os/solaris @@ -9,6 +9,8 @@ CORE_DEPS="$UNIX_DEPS $SOLARIS_DEPS" CORE_SRCS="$UNIX_SRCS $SOLARIS_SRCS " CORE_LIBS="$CORE_LIBS -lsocket -lnsl -lrt" +NGX_RPATH=YES + # Solaris's make does not support a blank line between target and rules ngx_spacer= diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -425,6 +425,21 @@ MAIL_SRCS="src/mail/ngx_mail.c \ src/mail/ngx_mail_handler.c \ src/mail/ngx_mail_parse.c" +MAIL_POP3_MODULE="ngx_mail_pop3_module" +MAIL_POP3_DEPS="src/mail/ngx_mail_pop3_module.h" +MAIL_POP3_SRCS="src/mail/ngx_mail_pop3_module.c \ + src/mail/ngx_mail_pop3_handler.c" + +MAIL_IMAP_MODULE="ngx_mail_imap_module" +MAIL_IMAP_DEPS="src/mail/ngx_mail_imap_module.h" +MAIL_IMAP_SRCS="src/mail/ngx_mail_imap_module.c \ + src/mail/ngx_mail_imap_handler.c" + +MAIL_SMTP_MODULE="ngx_mail_smtp_module" +MAIL_SMTP_DEPS="src/mail/ngx_mail_smtp_module.h" +MAIL_SMTP_SRCS="src/mail/ngx_mail_smtp_module.c \ + src/mail/ngx_mail_smtp_handler.c" + MAIL_SSL_MODULE="ngx_mail_ssl_module" MAIL_SSL_DEPS="src/mail/ngx_mail_ssl_module.h" MAIL_SSL_SRCS="src/mail/ngx_mail_ssl_module.c" diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.6.11" +#define NGINX_VERSION "0.6.12" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_hash.c b/src/core/ngx_hash.c --- a/src/core/ngx_hash.c +++ b/src/core/ngx_hash.c @@ -896,7 +896,7 @@ wildcard: return NGX_ERROR; } - ngx_cpystrn(p, key->data, last - 1); + ngx_cpystrn(p, key->data, last); hwc = &ha->dns_wc_tail; keys = &ha->dns_wc_tail_hash[k]; diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c --- a/src/event/ngx_event_connect.c +++ b/src/event/ngx_event_connect.c @@ -14,7 +14,7 @@ ngx_int_t ngx_event_connect_peer(ngx_peer_connection_t *pc) { int rc; - u_int event; + ngx_int_t event; ngx_err_t err; ngx_uint_t level; ngx_socket_t s; diff --git a/src/http/modules/ngx_http_empty_gif_module.c b/src/http/modules/ngx_http_empty_gif_module.c --- a/src/http/modules/ngx_http_empty_gif_module.c +++ b/src/http/modules/ngx_http_empty_gif_module.c @@ -128,11 +128,7 @@ ngx_http_empty_gif_handler(ngx_http_requ if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; - rc = ngx_http_send_header(r); - - if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { - return rc; - } + return ngx_http_send_header(r); } b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -1135,7 +1135,7 @@ ngx_http_fastcgi_process_header(ngx_http } else { for (i = 0; i < h->key.len; i++) { - h->lowcase_key[i] = ngx_tolower(h->lowcase_key[i]); + h->lowcase_key[i] = ngx_tolower(h->key.data[i]); } } diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -553,7 +553,7 @@ ngx_http_proxy_create_request(ngx_http_r } else { unparsed_uri = 0; - if (r->quoted_uri) { + if (r->quoted_uri || r->internal) { escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len, r->uri.len - loc_len, NGX_ESCAPE_URI); } @@ -1141,7 +1141,7 @@ ngx_http_proxy_process_header(ngx_http_r } else { for (i = 0; i < h->key.len; i++) { - h->lowcase_key[i] = ngx_tolower(h->lowcase_key[i]); + h->lowcase_key[i] = ngx_tolower(h->key.data[i]); } } diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm --- a/src/http/modules/perl/nginx.pm +++ b/src/http/modules/perl/nginx.pm @@ -47,7 +47,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.6.11'; +our $VERSION = '0.6.12'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -553,8 +553,8 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma if (in_addr[a].default_server) { ngx_log_error(NGX_LOG_ERR, cf->log, 0, - "the duplicate default server in %V:%d", - &lscf[l].file_name, lscf[l].line); + "the duplicate default server in %s:%ui", + &lscf[l].file_name, lscf[l].line); return NGX_CONF_ERROR; } diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -2627,7 +2627,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx ls->family = AF_INET; ls->addr = u.addr.in_addr; ls->port = u.port; - ls->file_name = cf->conf_file->file.name; + ls->file_name = cf->conf_file->file.name.data; ls->line = cf->conf_file->line; ls->conf.backlog = NGX_LISTEN_BACKLOG; ls->conf.rcvbuf = -1; diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -38,8 +38,8 @@ typedef struct { in_port_t port; int family; - ngx_str_t file_name; - ngx_int_t line; + u_char *file_name; + ngx_uint_t line; ngx_http_listen_conf_t conf; } ngx_http_listen_t; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -21,6 +21,8 @@ static ngx_int_t ngx_http_process_header ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); @@ -71,7 +73,7 @@ ngx_http_header_t ngx_http_headers_in[] ngx_http_process_unique_header_line }, { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection), - ngx_http_process_unique_header_line }, + ngx_http_process_connection }, { ngx_string("If-Modified-Since"), offsetof(ngx_http_headers_in_t, if_modified_since), @@ -1199,6 +1201,21 @@ ngx_http_process_unique_header_line(ngx_ static ngx_int_t +ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + if (ngx_strstr(h->value.data, "close")) { + r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; + + } else if (ngx_strstr(h->value.data, "keep-alive")) { + r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { @@ -1294,7 +1311,7 @@ ngx_http_process_request_header(ngx_http return NGX_ERROR; } - if (r->method & (NGX_HTTP_TRACE)) { + if (r->method & NGX_HTTP_TRACE) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent TRACE method"); ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); @@ -1317,26 +1334,11 @@ ngx_http_process_request_header(ngx_http return NGX_ERROR; } - if (r->headers_in.connection) { - if (r->headers_in.connection->value.len == 5 - && ngx_strcasecmp(r->headers_in.connection->value.data, - (u_char *) "close") - == 0) - { - r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; - - } else if (r->headers_in.connection->value.len == 10 - && ngx_strcasecmp(r->headers_in.connection->value.data, - (u_char *) "keep-alive") - == 0) - { - r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE; - - if (r->headers_in.keep_alive) { - r->headers_in.keep_alive_n = - ngx_atotm(r->headers_in.keep_alive->value.data, - r->headers_in.keep_alive->value.len); - } + if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) { + if (r->headers_in.keep_alive) { + r->headers_in.keep_alive_n = + ngx_atotm(r->headers_in.keep_alive->value.data, + r->headers_in.keep_alive->value.len); } } diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -1290,6 +1290,7 @@ ngx_http_upstream_test_connect(ngx_conne if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { if (c->write->pending_eof) { + c->log->action = "connecting to upstream"; (void) ngx_connection_error(c, c->write->kq_errno, "kevent() reported that connect() failed"); return NGX_ERROR; @@ -1313,6 +1314,7 @@ ngx_http_upstream_test_connect(ngx_conne } if (err) { + c->log->action = "connecting to upstream"; (void) ngx_connection_error(c, err, "connect() failed"); return NGX_ERROR; } @@ -1490,6 +1492,11 @@ ngx_http_upstream_send_response(ngx_http ngx_http_upstream_finalize_request(r, u, 0); return; } + + if (u->peer.connection->read->ready) { + ngx_http_upstream_process_non_buffered_body( + u->peer.connection->read); + } } return; @@ -3216,7 +3223,7 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng ngx_log_error(NGX_LOG_WARN, cf->log, 0, "upstream \"%V\" may not have port %d in %s:%ui", &u->host, uscfp[i]->port, - uscfp[i]->file_name.data, uscfp[i]->line); + uscfp[i]->file_name, uscfp[i]->line); return NULL; } @@ -3240,7 +3247,7 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng uscf->flags = flags; uscf->host = u->host; - uscf->file_name = cf->conf_file->file.name; + uscf->file_name = cf->conf_file->file.name.data; uscf->line = cf->conf_file->line; uscf->port = u->port; uscf->default_port = u->default_port; diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -92,7 +92,7 @@ struct ngx_http_upstream_srv_conf_s { ngx_uint_t flags; ngx_str_t host; - ngx_str_t file_name; + u_char *file_name; ngx_uint_t line; in_port_t port; in_port_t default_port; diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c --- a/src/http/ngx_http_upstream_round_robin.c +++ b/src/http/ngx_http_upstream_round_robin.c @@ -136,7 +136,7 @@ ngx_http_upstream_init_round_robin(ngx_c if (us->port == 0 && us->default_port == 0) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "no port in upstream \"%V\" in %s:%ui", - &us->host, us->file_name.data, us->line); + &us->host, us->file_name, us->line); return NGX_ERROR; } @@ -149,7 +149,7 @@ ngx_http_upstream_init_round_robin(ngx_c if (u.err) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "%s in upstream \"%V\" in %s:%ui", - u.err, &us->host, us->file_name.data, us->line); + u.err, &us->host, us->file_name, us->line); } return NGX_ERROR; @@ -498,7 +498,7 @@ ngx_http_upstream_get_peer(ngx_http_upst } if (peer[n].current_weight * 1000 / peer[i].current_weight - >= peer[n].weight * 1000 / peer[i].weight) + > peer[n].weight * 1000 / peer[i].weight) { return n; } diff --git a/src/mail/ngx_mail.c b/src/mail/ngx_mail.c --- a/src/mail/ngx_mail.c +++ b/src/mail/ngx_mail.c @@ -185,6 +185,8 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma /* init mail{} main_conf's */ + cf->ctx = ctx; + if (module->init_main_conf) { rv = module->init_main_conf(cf, ctx->main_conf[mi]); if (rv != NGX_CONF_OK) { @@ -197,6 +199,8 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma /* merge the server{}s' srv_conf's */ + cf->ctx = cscfp[s]->ctx; + if (module->merge_srv_conf) { rv = module->merge_srv_conf(cf, ctx->srv_conf[mi], @@ -209,8 +213,6 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma } } - /* mail{}'s cf->ctx was needed while the configuration merging */ - *cf = pcf; 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 @@ -73,55 +73,27 @@ typedef struct { #define NGX_MAIL_IMAP_PROTOCOL 1 #define NGX_MAIL_SMTP_PROTOCOL 2 -typedef struct { - ngx_msec_t timeout; + +typedef struct ngx_mail_protocol_s ngx_mail_protocol_t; + - size_t imap_client_buffer_size; +typedef struct { + ngx_mail_protocol_t *protocol; - ngx_uint_t protocol; + ngx_msec_t timeout; ngx_flag_t so_keepalive; - ngx_str_t pop3_capability; - ngx_str_t pop3_starttls_capability; - ngx_str_t pop3_starttls_only_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; + ngx_str_t server_name; - ngx_str_t smtp_capability; - ngx_str_t smtp_starttls_capability; - ngx_str_t smtp_starttls_only_capability; - - ngx_str_t server_name; - ngx_str_t smtp_server_name; - 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; - ngx_array_t imap_capabilities; - ngx_array_t smtp_capabilities; + u_char *file_name; + ngx_int_t line; /* server ctx */ ngx_mail_conf_ctx_t *ctx; } ngx_mail_core_srv_conf_t; -typedef struct { - void *(*create_main_conf)(ngx_conf_t *cf); - char *(*init_main_conf)(ngx_conf_t *cf, void *conf); - - void *(*create_srv_conf)(ngx_conf_t *cf); - char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, - void *conf); -} ngx_mail_module_t; - - typedef enum { ngx_pop3_start = 0, ngx_pop3_user, @@ -179,9 +151,9 @@ typedef struct { ngx_uint_t mail_state; + unsigned protocol:3; unsigned blocked:1; unsigned quit:1; - unsigned protocol:2; unsigned quoted:1; unsigned backslash:1; unsigned no_sync_literal:1; @@ -196,6 +168,7 @@ typedef struct { ngx_str_t salt; ngx_str_t tag; ngx_str_t tagged_line; + ngx_str_t text; ngx_str_t *addr_text; ngx_str_t smtp_helo; @@ -205,7 +178,7 @@ typedef struct { ngx_uint_t login_attempt; - /* used to parse IMAP/POP3/SMTP command */ + /* used to parse POP3/IMAP/SMTP command */ ngx_uint_t state; u_char *cmd_start; @@ -279,10 +252,43 @@ typedef struct { #define NGX_MAIL_PARSE_INVALID_COMMAND 20 -#define NGX_MAIL_MODULE 0x4C49414D /* "MAIL" */ +typedef void (*ngx_mail_init_session_pt)(ngx_mail_session_t *s, + ngx_connection_t *c); +typedef void (*ngx_mail_init_protocol_pt)(ngx_event_t *rev); +typedef void (*ngx_mail_auth_state_pt)(ngx_event_t *rev); +typedef ngx_int_t (*ngx_mail_parse_command_pt)(ngx_mail_session_t *s); + + +struct ngx_mail_protocol_s { + ngx_str_t name; + in_port_t port[4]; + ngx_uint_t type; + + ngx_mail_init_session_pt init_session; + ngx_mail_init_protocol_pt init_protocol; + ngx_mail_parse_command_pt parse_command; + ngx_mail_auth_state_pt auth_state; -#define NGX_MAIL_MAIN_CONF 0x02000000 -#define NGX_MAIL_SRV_CONF 0x04000000 + ngx_str_t internal_server_error; +}; + + +typedef struct { + ngx_mail_protocol_t *protocol; + + void *(*create_main_conf)(ngx_conf_t *cf); + char *(*init_main_conf)(ngx_conf_t *cf, void *conf); + + void *(*create_srv_conf)(ngx_conf_t *cf); + char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, + void *conf); +} ngx_mail_module_t; + + +#define NGX_MAIL_MODULE 0x4C49414D /* "MAIL" */ + +#define NGX_MAIL_MAIN_CONF 0x02000000 +#define NGX_MAIL_SRV_CONF 0x04000000 #define NGX_MAIL_MAIN_CONF_OFFSET offsetof(ngx_mail_conf_ctx_t, main_conf) @@ -304,17 +310,36 @@ typedef struct { ((ngx_mail_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index] +#if (NGX_MAIL_SSL) +void ngx_mail_starttls_handler(ngx_event_t *rev); +ngx_int_t ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c); +#endif + + void ngx_mail_init_connection(ngx_connection_t *c); + +ngx_int_t ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_mail_core_srv_conf_t *cscf); +ngx_int_t ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_uint_t n); +ngx_int_t ngx_mail_auth_login_username(ngx_mail_session_t *s, + ngx_connection_t *c); +ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s, + ngx_connection_t *c); +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_parse(ngx_mail_session_t *s, ngx_connection_t *c); + void ngx_mail_send(ngx_event_t *wev); -void ngx_pop3_auth_state(ngx_event_t *rev); -void ngx_imap_auth_state(ngx_event_t *rev); -void ngx_smtp_auth_state(ngx_event_t *rev); +ngx_int_t ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c); +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); +u_char *ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len); -ngx_int_t ngx_pop3_parse_command(ngx_mail_session_t *s); -ngx_int_t ngx_imap_parse_command(ngx_mail_session_t *s); -ngx_int_t ngx_smtp_parse_command(ngx_mail_session_t *s); + +char *ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); /* STUB */ 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 @@ -111,6 +111,8 @@ static ngx_command_t ngx_mail_auth_http static ngx_mail_module_t ngx_mail_auth_http_module_ctx = { + NULL, /* protocol */ + NULL, /* create main configuration */ NULL, /* init main configuration */ @@ -135,7 +137,6 @@ ngx_module_t ngx_mail_auth_http_module }; -static char *ngx_mail_auth_http_protocol[] = { "pop3", "imap", "smtp" }; static ngx_str_t ngx_mail_auth_http_method[] = { ngx_string("plain"), ngx_string("plain"), @@ -145,6 +146,7 @@ static ngx_str_t ngx_mail_auth_http_me static ngx_str_t ngx_mail_smtp_errcode = ngx_string("535 5.7.0"); + void ngx_mail_auth_http_init(ngx_mail_session_t *s) { @@ -239,7 +241,7 @@ ngx_mail_auth_http_write_handler(ngx_eve if (wev->timedout) { ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT, "auth http server %V timed out", ctx->peer.name); - ngx_close_connection(ctx->peer.connection); + ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); return; @@ -250,7 +252,7 @@ ngx_mail_auth_http_write_handler(ngx_eve n = ngx_send(c, ctx->request->pos, size); if (n == NGX_ERROR) { - ngx_close_connection(ctx->peer.connection); + ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); return; @@ -267,7 +269,7 @@ ngx_mail_auth_http_write_handler(ngx_eve } if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { - ngx_close_connection(ctx->peer.connection); + ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); } @@ -302,7 +304,7 @@ ngx_mail_auth_http_read_handler(ngx_even if (rev->timedout) { ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, "auth http server %V timed out", ctx->peer.name); - ngx_close_connection(ctx->peer.connection); + ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); return; @@ -311,7 +313,7 @@ ngx_mail_auth_http_read_handler(ngx_even if (ctx->response == NULL) { ctx->response = ngx_create_temp_buf(ctx->pool, 1024); if (ctx->response == NULL) { - ngx_close_connection(ctx->peer.connection); + ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); return; @@ -333,7 +335,7 @@ ngx_mail_auth_http_read_handler(ngx_even return; } - ngx_close_connection(ctx->peer.connection); + ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); } @@ -749,7 +751,8 @@ ngx_mail_auth_http_process_headers(ngx_m return; } - if (s->passwd.data == NULL && s->protocol != NGX_MAIL_SMTP_PROTOCOL) + if (s->passwd.data == NULL + && s->protocol != NGX_MAIL_SMTP_PROTOCOL) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "auth http server %V did not send password", @@ -868,45 +871,30 @@ ngx_mail_auth_sleep_handler(ngx_event_t return; } - switch (s->protocol) { - - case NGX_MAIL_POP3_PROTOCOL: - s->mail_state = ngx_pop3_start; - s->connection->read->handler = ngx_pop3_auth_state; - break; + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - case NGX_MAIL_IMAP_PROTOCOL: - s->mail_state = ngx_imap_start; - s->connection->read->handler = ngx_imap_auth_state; - break; + rev->handler = cscf->protocol->auth_state; - default: /* NGX_MAIL_SMTP_PROTOCOL */ - s->mail_state = ngx_smtp_start; - s->connection->read->handler = ngx_smtp_auth_state; - break; - } - + s->mail_state = 0; s->auth_method = NGX_MAIL_AUTH_PLAIN; c->log->action = "in auth state"; - ngx_mail_send(s->connection->write); + ngx_mail_send(c->write); if (c->destroyed) { return; } - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - ngx_add_timer(rev, cscf->timeout); if (rev->ready) { - s->connection->read->handler(rev); + rev->handler(rev); return; } if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_mail_close_connection(s->connection); + ngx_mail_close_connection(c); } return; @@ -914,7 +902,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t if (rev->active) { if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_mail_close_connection(s->connection); + ngx_mail_close_connection(c); } } } @@ -1147,9 +1135,10 @@ static ngx_buf_t * ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool, ngx_mail_auth_http_conf_t *ahcf) { - size_t len; - ngx_buf_t *b; - ngx_str_t login, passwd; + size_t len; + ngx_buf_t *b; + ngx_str_t login, passwd; + ngx_mail_core_srv_conf_t *cscf; if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) { return NULL; @@ -1159,6 +1148,8 @@ ngx_mail_auth_http_create_request(ngx_ma return NULL; } + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1 + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1 + sizeof("Auth-Method: ") - 1 @@ -1167,7 +1158,8 @@ ngx_mail_auth_http_create_request(ngx_ma + sizeof("Auth-User: ") - 1 + login.len + sizeof(CRLF) - 1 + sizeof("Auth-Pass: ") - 1 + passwd.len + sizeof(CRLF) - 1 + sizeof("Auth-Salt: ") - 1 + s->salt.len - + sizeof("Auth-Protocol: imap" CRLF) - 1 + + sizeof("Auth-Protocol: ") - 1 + cscf->protocol->name.len + + sizeof(CRLF) - 1 + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN + sizeof(CRLF) - 1 + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len @@ -1214,8 +1206,8 @@ ngx_mail_auth_http_create_request(ngx_ma b->last = ngx_cpymem(b->last, "Auth-Protocol: ", sizeof("Auth-Protocol: ") - 1); - b->last = ngx_cpymem(b->last, ngx_mail_auth_http_protocol[s->protocol], - sizeof("imap") - 1); + b->last = ngx_cpymem(b->last, cscf->protocol->name.data, + cscf->protocol->name.len); *b->last++ = CR; *b->last++ = LF; b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF, diff --git a/src/mail/ngx_mail_core_module.c b/src/mail/ngx_mail_core_module.c --- a/src/mail/ngx_mail_core_module.c +++ b/src/mail/ngx_mail_core_module.c @@ -18,90 +18,10 @@ static char *ngx_mail_core_server(ngx_co void *conf); static char *ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static char *ngx_mail_core_capability(ngx_conf_t *cf, ngx_command_t *cmd, +static char *ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static ngx_conf_enum_t ngx_mail_core_procotol[] = { - { ngx_string("pop3"), NGX_MAIL_POP3_PROTOCOL }, - { ngx_string("imap"), NGX_MAIL_IMAP_PROTOCOL }, - { ngx_string("smtp"), NGX_MAIL_SMTP_PROTOCOL }, - { ngx_null_string, 0 } -}; - - -static ngx_str_t ngx_pop3_default_capabilities[] = { - ngx_string("TOP"), - ngx_string("USER"), - ngx_string("UIDL"), - ngx_null_string -}; - - -static ngx_str_t ngx_imap_default_capabilities[] = { - ngx_string("IMAP4"), - ngx_string("IMAP4rev1"), - ngx_string("UIDPLUS"), - ngx_null_string -}; - - -static ngx_conf_bitmask_t ngx_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_null_string, 0 } -}; - - -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 }, - { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED }, - { ngx_null_string, 0 } -}; - - -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"), - ngx_null_string, /* APOP */ - ngx_string("CRAM-MD5") -}; - - -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_mail_core_commands[] = { { ngx_string("server"), @@ -114,22 +34,15 @@ static ngx_command_t ngx_mail_core_comm { ngx_string("listen"), NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12, ngx_mail_core_listen, - 0, + NGX_MAIL_SRV_CONF_OFFSET, 0, NULL }, { ngx_string("protocol"), NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_enum_slot, + ngx_mail_core_protocol, NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, protocol), - &ngx_mail_core_procotol }, - - { ngx_string("imap_client_buffer"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, imap_client_buffer_size), + 0, NULL }, { ngx_string("so_keepalive"), @@ -146,27 +59,6 @@ static ngx_command_t ngx_mail_core_comm offsetof(ngx_mail_core_srv_conf_t, timeout), NULL }, - { ngx_string("pop3_capabilities"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, - ngx_mail_core_capability, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, pop3_capabilities), - NULL }, - - { ngx_string("imap_capabilities"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, - ngx_mail_core_capability, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, imap_capabilities), - NULL }, - - { ngx_string("smtp_capabilities"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, - ngx_mail_core_capability, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, smtp_capabilities), - NULL }, - { ngx_string("server_name"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -174,39 +66,13 @@ static ngx_command_t ngx_mail_core_comm offsetof(ngx_mail_core_srv_conf_t, server_name), NULL }, - { ngx_string("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, pop3_auth_methods), - &ngx_pop3_auth_methods }, - - { ngx_string("pop3_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, 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, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, smtp_auth_methods), - &ngx_smtp_auth_methods }, - ngx_null_command }; static ngx_mail_module_t ngx_mail_core_module_ctx = { + NULL, /* protocol */ + ngx_mail_core_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ @@ -268,29 +134,15 @@ ngx_mail_core_create_srv_conf(ngx_conf_t return NULL; } - cscf->imap_client_buffer_size = NGX_CONF_UNSET_SIZE; - cscf->protocol = NGX_CONF_UNSET_UINT; + /* + * set by ngx_pcalloc(): + * + * cscf->protocol = NULL; + */ + cscf->timeout = NGX_CONF_UNSET_MSEC; cscf->so_keepalive = NGX_CONF_UNSET; - if (ngx_array_init(&cscf->pop3_capabilities, cf->pool, 4, sizeof(ngx_str_t)) - != NGX_OK) - { - return NULL; - } - - if (ngx_array_init(&cscf->imap_capabilities, cf->pool, 4, sizeof(ngx_str_t)) - != NGX_OK) - { - return NULL; - } - - if (ngx_array_init(&cscf->smtp_capabilities, cf->pool, 4, sizeof(ngx_str_t)) - != NGX_OK) - { - return NULL; - } - return cscf; } @@ -301,35 +153,9 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t ngx_mail_core_srv_conf_t *prev = parent; ngx_mail_core_srv_conf_t *conf = child; - u_char *p, *auth; - size_t size, stls_only_size; - ngx_str_t *c, *d; - ngx_uint_t i, m; - - ngx_conf_merge_size_value(conf->imap_client_buffer_size, - prev->imap_client_buffer_size, - (size_t) ngx_pagesize); ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); - ngx_conf_merge_uint_value(conf->protocol, prev->protocol, - NGX_MAIL_IMAP_PROTOCOL); - ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0); - - ngx_conf_merge_bitmask_value(conf->pop3_auth_methods, - prev->pop3_auth_methods, - (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 - |NGX_MAIL_AUTH_PLAIN_ENABLED - |NGX_MAIL_AUTH_LOGIN_ENABLED)); + ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0); ngx_conf_merge_str_value(conf->server_name, prev->server_name, ""); @@ -343,351 +169,21 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t if (gethostname((char *) conf->server_name.data, NGX_MAXHOSTNAMELEN) == -1) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, - "gethostname() failed"); + ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, + "gethostname() failed"); return NGX_CONF_ERROR; } conf->server_name.len = ngx_strlen(conf->server_name.data); } - - if (conf->pop3_capabilities.nelts == 0) { - conf->pop3_capabilities = prev->pop3_capabilities; - } - - if (conf->pop3_capabilities.nelts == 0) { - - for (d = ngx_pop3_default_capabilities; d->len; d++) { - c = ngx_array_push(&conf->pop3_capabilities); - if (c == NULL) { - return NGX_CONF_ERROR; - } - - *c = *d; - } - } - - size = sizeof("+OK Capability list follows" CRLF) - 1 - + sizeof("." CRLF) - 1; - - stls_only_size = size + sizeof("STLS" CRLF) - 1; - - c = conf->pop3_capabilities.elts; - for (i = 0; i < conf->pop3_capabilities.nelts; i++) { - size += c[i].len + sizeof(CRLF) - 1; - - if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) { - continue; - } - - stls_only_size += c[i].len + sizeof(CRLF) - 1; - } - - if (conf->pop3_auth_methods & NGX_MAIL_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; - } - - conf->pop3_capability.len = size; - conf->pop3_capability.data = p; - - p = ngx_cpymem(p, "+OK Capability list follows" CRLF, - sizeof("+OK Capability list follows" CRLF) - 1); - - for (i = 0; i < conf->pop3_capabilities.nelts; i++) { - p = ngx_cpymem(p, c[i].data, c[i].len); - *p++ = CR; *p++ = LF; - } - - if (conf->pop3_auth_methods & NGX_MAIL_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; - - - size += sizeof("STLS" CRLF) - 1; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->pop3_starttls_capability.len = size; - conf->pop3_starttls_capability.data = p; - - p = ngx_cpymem(p, conf->pop3_capability.data, - conf->pop3_capability.len - (sizeof("." CRLF) - 1)); - - p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1); - *p++ = '.'; *p++ = CR; *p = LF; - - - if (conf->pop3_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) { - conf->pop3_auth_capability = ngx_pop3_auth_cram_md5_capability; - - } else { - conf->pop3_auth_capability = ngx_pop3_auth_plain_capability; - } - - - p = ngx_palloc(cf->pool, stls_only_size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->pop3_starttls_only_capability.len = stls_only_size; - conf->pop3_starttls_only_capability.data = p; - - p = ngx_cpymem(p, "+OK Capability list follows" CRLF, - sizeof("+OK Capability list follows" CRLF) - 1); - - for (i = 0; i < conf->pop3_capabilities.nelts; i++) { - if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) { - continue; - } - - p = ngx_cpymem(p, c[i].data, c[i].len); - *p++ = CR; *p++ = LF; - } - - p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1); - *p++ = '.'; *p++ = CR; *p = LF; - - - if (conf->imap_capabilities.nelts == 0) { - conf->imap_capabilities = prev->imap_capabilities; - } - - if (conf->imap_capabilities.nelts == 0) { - - for (d = ngx_imap_default_capabilities; d->len; d++) { - c = ngx_array_push(&conf->imap_capabilities); - if (c == NULL) { - return NGX_CONF_ERROR; - } - - *c = *d; - } - } - - size = sizeof("* CAPABILITY" CRLF) - 1; - - c = conf->imap_capabilities.elts; - for (i = 0; i < conf->imap_capabilities.nelts; i++) { - 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) { + if (conf->protocol == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "unknown mail protocol for server in %s:%ui", + conf->file_name, conf->line); return NGX_CONF_ERROR; } - conf->imap_capability.len = size; - conf->imap_capability.data = p; - - p = ngx_cpymem(p, "* CAPABILITY", sizeof("* CAPABILITY") - 1); - - for (i = 0; i < conf->imap_capabilities.nelts; i++) { - *p++ = ' '; - 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; - - - size += sizeof(" STARTTLS") - 1; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->imap_starttls_capability.len = size; - conf->imap_starttls_capability.data = p; - - p = ngx_cpymem(p, conf->imap_capability.data, - conf->imap_capability.len - (sizeof(CRLF) - 1)); - p = ngx_cpymem(p, " STARTTLS", sizeof(" STARTTLS") - 1); - *p++ = CR; *p = LF; - - - size = (auth - conf->imap_capability.data) + sizeof(CRLF) - 1 - + sizeof(" STARTTLS LOGINDISABLED") - 1; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->imap_starttls_only_capability.len = size; - conf->imap_starttls_only_capability.data = p; - - 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; - - - size = sizeof("220 ESMTP ready" CRLF) - 1 + conf->server_name.len; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->smtp_greeting.len = size; - conf->smtp_greeting.data = p; - - *p++ = '2'; *p++ = '2'; *p++ = '0'; *p++ = ' '; - p = ngx_cpymem(p, conf->server_name.data, conf->server_name.len); - ngx_memcpy(p, " ESMTP ready" CRLF, sizeof(" ESMTP ready" CRLF) - 1); - - - size = sizeof("250 " CRLF) - 1 + conf->server_name.len; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->smtp_server_name.len = size; - conf->smtp_server_name.data = p; - - *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; - p = ngx_cpymem(p, conf->server_name.data, conf->server_name.len); - *p++ = CR; *p = LF; - - - if (conf->smtp_capabilities.nelts == 0) { - conf->smtp_capabilities = prev->smtp_capabilities; - } - - size = sizeof("250-") - 1 + conf->server_name.len + sizeof(CRLF) - 1 - + sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1; - - c = conf->smtp_capabilities.elts; - for (i = 0; i < conf->smtp_capabilities.nelts; i++) { - size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1; - } - - for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; - m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; - m <<= 1, i++) - { - if (m & conf->smtp_auth_methods) { - size += 1 + ngx_smtp_auth_methods_names[i].len; - } - } - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->smtp_capability.len = size; - conf->smtp_capability.data = p; - - *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; - p = ngx_cpymem(p, conf->server_name.data, conf->server_name.len); - *p++ = CR; *p++ = LF; - - for (i = 0; i < conf->smtp_capabilities.nelts; i++) { - *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; - p = ngx_cpymem(p, c[i].data, c[i].len); - *p++ = CR; *p++ = LF; - } - - auth = p; - - *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; - *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 <<= 1, i++) - { - if (m & conf->smtp_auth_methods) { - *p++ = ' '; - p = ngx_cpymem(p, ngx_smtp_auth_methods_names[i].data, - ngx_smtp_auth_methods_names[i].len); - } - } - - *p++ = CR; *p = LF; - - size += sizeof("250 STARTTLS" CRLF) - 1; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->smtp_starttls_capability.len = size; - conf->smtp_starttls_capability.data = p; - - p = ngx_cpymem(p, conf->smtp_capability.data, - conf->smtp_capability.len); - - p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); - *p++ = CR; *p = LF; - - p = conf->smtp_starttls_capability.data - + (auth - conf->smtp_capability.data) + 3; - *p = '-'; - - size = (auth - conf->smtp_capability.data) - + sizeof("250 STARTTLS" CRLF) - 1; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->smtp_starttls_only_capability.len = size; - conf->smtp_starttls_only_capability.data = p; - - p = ngx_cpymem(p, conf->smtp_capability.data, - auth - conf->smtp_capability.data); - - ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); - return NGX_CONF_OK; } @@ -704,7 +200,6 @@ ngx_mail_core_server(ngx_conf_t *cf, ngx ngx_mail_core_srv_conf_t *cscf, **cscfp; ngx_mail_core_main_conf_t *cmcf; - ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t)); if (ctx == NULL) { return NGX_CONF_ERROR; @@ -742,6 +237,9 @@ ngx_mail_core_server(ngx_conf_t *cf, ngx cscf = ctx->srv_conf[ngx_mail_core_module.ctx_index]; cscf->ctx = ctx; + cscf->file_name = cf->conf_file->file.name.data; + cscf->line = cf->conf_file->line; + cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index]; cscfp = ngx_array_push(&cmcf->servers); @@ -771,10 +269,13 @@ ngx_mail_core_server(ngx_conf_t *cf, ngx static char * ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { + ngx_mail_core_srv_conf_t *cscf = conf; + ngx_str_t *value; ngx_url_t u; - ngx_uint_t i; + ngx_uint_t i, m; ngx_mail_listen_t *imls; + ngx_mail_module_t *module; ngx_mail_core_main_conf_t *cmcf; value = cf->args->elts; @@ -821,6 +322,25 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx imls->family = AF_INET; imls->ctx = cf->ctx; + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_MAIL_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + + if (module->protocol == NULL) { + continue; + } + + for (i = 0; module->protocol->port[i]; i++) { + if (module->protocol->port[i] == u.port) { + cscf->protocol = module->protocol; + break; + } + } + } + if (cf->args->nelts == 2) { return NGX_CONF_OK; } @@ -837,7 +357,40 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx static char * -ngx_mail_core_capability(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_core_srv_conf_t *cscf = conf; + + ngx_str_t *value; + ngx_uint_t m; + ngx_mail_module_t *module; + + value = cf->args->elts; + + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_MAIL_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + + if (module->protocol + && ngx_strcmp(module->protocol->name.data, value[1].data) == 0) + { + cscf->protocol = module->protocol; + + return NGX_CONF_OK; + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown protocol \"%V\"", &value[1]); + return NGX_CONF_ERROR; +} + + +char * +ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *p = conf; 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 @@ -11,12 +11,6 @@ static void ngx_mail_init_session(ngx_connection_t *c); -static void ngx_mail_init_protocol(ngx_event_t *rev); -static ngx_int_t ngx_mail_decode_auth_plain(ngx_mail_session_t *s, - ngx_str_t *encoded); -static void ngx_mail_do_auth(ngx_mail_session_t *s); -static ngx_int_t ngx_mail_read_command(ngx_mail_session_t *s); -static u_char *ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len); #if (NGX_MAIL_SSL) static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c); @@ -24,40 +18,6 @@ static void ngx_mail_ssl_handshake_handl #endif -static ngx_str_t greetings[] = { - ngx_string("+OK POP3 ready" CRLF), - ngx_string("* OK IMAP4 ready" CRLF) - /* SMTP greeting */ -}; - -static ngx_str_t internal_server_errors[] = { - ngx_string("-ERR internal server error" CRLF), - ngx_string("* BAD internal server error" CRLF), - ngx_string("451 4.3.2 Internal server error" CRLF), -}; - -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[] = "* "; -static u_char imap_ok[] = "OK completed" CRLF; -static u_char imap_next[] = "+ OK" CRLF; -static u_char imap_bye[] = "* BYE" CRLF; -static u_char imap_invalid_command[] = "BAD invalid command" CRLF; - -static u_char smtp_ok[] = "250 2.0.0 OK" CRLF; -static u_char smtp_bye[] = "221 2.0.0 Bye" CRLF; -static u_char smtp_next[] = "334 " CRLF; -static u_char smtp_username[] = "334 VXNlcm5hbWU6" CRLF; -static u_char smtp_password[] = "334 UGFzc3dvcmQ6" CRLF; -static u_char smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF; -static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF; -static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF; - - void ngx_mail_init_connection(ngx_connection_t *c) { @@ -172,7 +132,7 @@ ngx_mail_init_connection(ngx_connection_ #if (NGX_MAIL_SSL) -static void +void ngx_mail_starttls_handler(ngx_event_t *rev) { ngx_connection_t *c; @@ -222,17 +182,20 @@ ngx_mail_ssl_init_connection(ngx_ssl_t * static void ngx_mail_ssl_handshake_handler(ngx_connection_t *c) { - ngx_mail_session_t *s; + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; if (c->ssl->handshaked) { s = c->data; if (s->starttls) { - c->read->handler = ngx_mail_init_protocol; + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + c->read->handler = cscf->protocol->init_protocol; c->write->handler = ngx_mail_send; - ngx_mail_init_protocol(c->read); + cscf->protocol->init_protocol(c->read); return; } @@ -250,18 +213,14 @@ ngx_mail_ssl_handshake_handler(ngx_conne static void ngx_mail_init_session(ngx_connection_t *c) { - u_char *p; ngx_mail_session_t *s; ngx_mail_core_srv_conf_t *cscf; - c->read->handler = ngx_mail_init_protocol; - c->write->handler = ngx_mail_send; - s = c->data; cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - s->protocol = cscf->protocol; + s->protocol = cscf->protocol->type; s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_mail_max_module); if (s->ctx == NULL) { @@ -269,60 +228,249 @@ ngx_mail_init_session(ngx_connection_t * return; } - if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) { - s->out = cscf->smtp_greeting; + c->write->handler = ngx_mail_send; + + cscf->protocol->init_session(s, c); +} + + +ngx_int_t +ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_mail_core_srv_conf_t *cscf) +{ + s->salt.data = ngx_palloc(c->pool, + sizeof(" <18446744073709551616.@>" CRLF) - 1 + + NGX_TIME_T_LEN + + cscf->server_name.len); + if (s->salt.data == NULL) { + return NGX_ERROR; + } + + s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF, + ngx_random(), ngx_time(), &cscf->server_name) + - s->salt.data; + + return NGX_OK; +} + + +#if (NGX_MAIL_SSL) + +ngx_int_t +ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_ssl_conf_t *sslcf; + + if (c->ssl) { + return 0; + } + + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { + return 1; + } + + return 0; +} - } else { - s->out = greetings[s->protocol]; +#endif + + +ngx_int_t +ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n) +{ + u_char *p, *last; + ngx_str_t *arg, plain; + + arg = s->args.elts; + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth plain: \"%V\"", &arg[n]); +#endif + + plain.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[n].len)); + if (plain.data == NULL){ + return NGX_ERROR; + } + + if (ngx_decode_base64(&plain, &arg[n]) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid base64 encoding in AUTH PLAIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + p = plain.data; + last = p + plain.len; + + while (p < last && *p++) { /* void */ } + + if (p == last) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid login in AUTH PLAIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + s->login.data = p; + + while (p < last && *p) { p++; } + + if (p == last) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid password in AUTH PLAIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; } - if ((s->protocol == NGX_MAIL_POP3_PROTOCOL - && (cscf->pop3_auth_methods - & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED))) + s->login.len = p++ - s->login.data; + + s->passwd.len = last - p; + s->passwd.data = p; + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth plain: \"%V\" \"%V\"", &s->login, &s->passwd); +#endif + + return NGX_DONE; +} + - || (s->protocol == NGX_MAIL_IMAP_PROTOCOL - && (cscf->imap_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) +ngx_int_t +ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + + arg = s->args.elts; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail 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){ + return NGX_ERROR; + } - || (s->protocol == NGX_MAIL_SMTP_PROTOCOL - && (cscf->smtp_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED))) - { - s->salt.data = ngx_palloc(c->pool, - sizeof(" <18446744073709551616.@>" CRLF) - 1 - + NGX_TIME_T_LEN - + cscf->server_name.len); - if (s->salt.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"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login username: \"%V\"", &s->login); + + return NGX_OK; +} + + +ngx_int_t +ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + + arg = s->args.elts; + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail 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){ + return NGX_ERROR; + } + + 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"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } - s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF, - ngx_random(), ngx_time(), &cscf->server_name) - - s->salt.data; +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login password: \"%V\"", &s->passwd); +#endif + + return NGX_DONE; +} + + +ngx_int_t +ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c, + char *prefix, size_t len) +{ + u_char *p; + ngx_str_t salt; + ngx_uint_t n; + + p = ngx_palloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2); + if (p == NULL) { + return NGX_ERROR; + } + + salt.data = ngx_cpymem(p, prefix, len); + s->salt.len -= 2; + + ngx_encode_base64(&salt, &s->salt); + + s->salt.len += 2; + n = len + salt.len; + p[n++] = CR; p[n++] = LF; + + s->out.len = n; + s->out.data = p; - if (s->protocol == NGX_MAIL_POP3_PROTOCOL) { - s->out.data = ngx_palloc(c->pool, - greetings[0].len + 1 + s->salt.len); - if (s->out.data == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } + return NGX_OK; +} + + +ngx_int_t +ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c) +{ + u_char *p, *last; + ngx_str_t *arg; + + arg = s->args.elts; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth cram-md5: \"%V\"", &arg[0]); - p = ngx_cpymem(s->out.data, - greetings[0].data, greetings[0].len - 2); - *p++ = ' '; - p = ngx_cpymem(p, s->salt.data, s->salt.len); + s->login.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[0].len)); + if (s->login.data == NULL){ + return NGX_ERROR; + } - s->out.len = p - s->out.data; + 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"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + 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_add_timer(c->read, cscf->timeout); - - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { - ngx_mail_close_connection(c); + 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"); + return NGX_MAIL_PARSE_INVALID_COMMAND; } - ngx_mail_send(c->write); + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd); + + s->auth_method = NGX_MAIL_AUTH_CRAM_MD5; + + return NGX_DONE; } @@ -391,1626 +539,18 @@ ngx_mail_send(ngx_event_t *wev) } -static void -ngx_mail_init_protocol(ngx_event_t *rev) +ngx_int_t +ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c) { - size_t size; - ngx_connection_t *c; - ngx_mail_session_t *s; + ssize_t n; + ngx_int_t rc; + ngx_str_t l; ngx_mail_core_srv_conf_t *cscf; - c = rev->data; - - c->log->action = "in auth state"; - - if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); - c->timedout = 1; - ngx_mail_close_connection(c); - return; - } - - s = c->data; - - switch (s->protocol) { - - case NGX_MAIL_POP3_PROTOCOL: - size = 128; - s->mail_state = ngx_pop3_start; - c->read->handler = ngx_pop3_auth_state; - break; - - case NGX_MAIL_IMAP_PROTOCOL: - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - size = cscf->imap_client_buffer_size; - s->mail_state = ngx_imap_start; - c->read->handler = ngx_imap_auth_state; - break; - - default: /* NGX_MAIL_SMTP_PROTOCOL */ - size = 512; - s->mail_state = ngx_smtp_start; - c->read->handler = ngx_smtp_auth_state; - break; - } - - if (s->buffer == NULL) { - if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) - == NGX_ERROR) - { - ngx_mail_session_internal_server_error(s); - return; - } - - s->buffer = ngx_create_temp_buf(c->pool, size); - if (s->buffer == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - } - - c->read->handler(rev); -} - - -void -ngx_pop3_auth_state(ngx_event_t *rev) -{ - u_char *p, *last, *text; - ssize_t size; - ngx_int_t rc; - ngx_str_t *arg, salt; - ngx_connection_t *c; - ngx_mail_session_t *s; - ngx_mail_core_srv_conf_t *cscf; -#if (NGX_MAIL_SSL) - ngx_mail_ssl_conf_t *sslcf; -#endif - - c = rev->data; - s = c->data; - - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 auth state"); - - if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); - c->timedout = 1; - ngx_mail_close_connection(c); - return; - } - - if (s->out.len) { - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy"); - s->blocked = 1; - return; - } - - s->blocked = 0; - - rc = ngx_mail_read_command(s); - - if (rc == NGX_AGAIN || rc == NGX_ERROR) { - return; - } - - text = pop3_ok; - size = sizeof(pop3_ok) - 1; - - if (rc == NGX_OK) { - switch (s->mail_state) { - - case ngx_pop3_start: - - switch (s->command) { - - case NGX_POP3_USER: - -#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) { - s->mail_state = ngx_pop3_user; - - arg = s->args.elts; - 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); - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 login: \"%V\"", &s->login); - - break; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - - case NGX_POP3_CAPA: - 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) { - size = cscf->pop3_starttls_capability.len; - text = cscf->pop3_starttls_capability.data; - break; - } - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { - size = cscf->pop3_starttls_only_capability.len; - text = cscf->pop3_starttls_only_capability.data; - break; - } - } -#endif - - size = cscf->pop3_capability.len; - text = cscf->pop3_capability.data; - break; - - case NGX_POP3_APOP: - -#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 - - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - - if ((cscf->pop3_auth_methods & NGX_MAIL_AUTH_APOP_ENABLED) - && s->args.nelts == 2) - { - arg = s->args.elts; - - 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); - - ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 apop: \"%V\" \"%V\"", - &s->login, &s->passwd); - - s->auth_method = NGX_MAIL_AUTH_APOP; - - ngx_mail_do_auth(s); - return; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - - case NGX_POP3_AUTH: - -#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 - - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - - if (s->args.nelts == 0) { - size = cscf->pop3_auth_capability.len; - text = cscf->pop3_auth_capability.data; - s->state = 0; - break; - } - - arg = s->args.elts; - - if (arg[0].len == 5) { - - if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) - == 0) - { - - if (s->args.nelts != 1) { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - s->mail_state = ngx_pop3_auth_login_username; - - size = sizeof(pop3_username) - 1; - text = pop3_username; - - break; - - } else if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", - 5) - == 0) - { - - if (s->args.nelts == 1) { - s->mail_state = ngx_pop3_auth_plain; - - size = sizeof(pop3_next) - 1; - text = pop3_next; - - break; - } - - if (s->args.nelts == 2) { - - /* - * workaround for Eudora for Mac: it sends - * AUTH PLAIN [base64 encoded] - */ - - rc = ngx_mail_decode_auth_plain(s, &arg[1]); - - if (rc == NGX_OK) { - ngx_mail_do_auth(s); - return; - } - - if (rc == NGX_ERROR) { - ngx_mail_session_internal_server_error(s); - return; - } - - /* rc == NGX_MAIL_PARSE_INVALID_COMMAND */ - - break; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - } else if (arg[0].len == 8 - && ngx_strncasecmp(arg[0].data, - (u_char *) "CRAM-MD5", 8) - == 0) - { - if (!(cscf->pop3_auth_methods - & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) - || s->args.nelts != 1) - { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - s->mail_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_mail_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_MAIL_PARSE_INVALID_COMMAND; - break; - - case NGX_POP3_QUIT: - s->quit = 1; - break; - - case NGX_POP3_NOOP: - break; - -#if (NGX_MAIL_SSL) - - case NGX_POP3_STLS: - 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: - s->mail_state = ngx_pop3_start; - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - break; - - case ngx_pop3_user: - - switch (s->command) { - - case NGX_POP3_PASS: - if (s->args.nelts == 1) { - arg = s->args.elts; - s->passwd.len = arg[0].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[0].data, s->passwd.len); - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 passwd: \"%V\"", &s->passwd); -#endif - - ngx_mail_do_auth(s); - return; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - - case NGX_POP3_CAPA: - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - size = cscf->pop3_capability.len; - text = cscf->pop3_capability.data; - break; - - case NGX_POP3_QUIT: - s->quit = 1; - break; - - case NGX_POP3_NOOP: - break; - - default: - s->mail_state = ngx_pop3_start; - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - break; - - /* suppress warinings */ - case ngx_pop3_passwd: - break; - - case ngx_pop3_auth_login_username: - arg = s->args.elts; - s->mail_state = ngx_pop3_auth_login_password; - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, 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_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, - "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_MAIL_PASSWD) - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, 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_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, - "pop3 auth login password: \"%V\"", &s->passwd); -#endif - - ngx_mail_do_auth(s); - return; - - case ngx_pop3_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; - } - - if (rc == NGX_ERROR) { - ngx_mail_session_internal_server_error(s); - return; - } - - /* rc == NGX_MAIL_PARSE_INVALID_COMMAND */ - - break; - - case ngx_pop3_auth_cram_md5: - arg = s->args.elts; - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, 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_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 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; - } - } - - 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; - } - - ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 auth cram-md5: \"%V\" \"%V\"", - &s->login, &s->passwd); - - s->auth_method = NGX_MAIL_AUTH_CRAM_MD5; - - ngx_mail_do_auth(s); - return; - } - } - - if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) { - s->mail_state = ngx_pop3_start; - s->state = 0; - text = pop3_invalid_command; - size = sizeof(pop3_invalid_command) - 1; - } - - 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; - } - - s->out.data = text; - s->out.len = size; - - ngx_mail_send(c->write); -} - - -void -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, salt; - ngx_int_t rc; - ngx_uint_t tag, i; - ngx_connection_t *c; - ngx_mail_session_t *s; - ngx_mail_core_srv_conf_t *cscf; -#if (NGX_MAIL_SSL) - ngx_mail_ssl_conf_t *sslcf; -#endif - - c = rev->data; - s = c->data; - - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth state"); - - if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); - c->timedout = 1; - ngx_mail_close_connection(c); - return; - } - - if (s->out.len) { - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap send handler busy"); - s->blocked = 1; - return; - } - - s->blocked = 0; - - rc = ngx_mail_read_command(s); - - if (rc == NGX_AGAIN || rc == NGX_ERROR) { - return; - } - - tag = 1; - - text = NULL; - text_len = 0; - - last = imap_ok; - last_len = sizeof(imap_ok) - 1; - - if (rc == NGX_OK) { - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth command: %i", - s->command); - - if (s->backslash) { - - arg = s->args.elts; - - for (i = 0; i < s->args.nelts; i++) { - dst = arg[i].data; - end = dst + arg[i].len; - - for (src = dst; src < end; dst++) { - *dst = *src; - if (*src++ == '\\') { - *dst = *src++; - } - } - - arg[i].len = dst - arg[i].data; - } - - s->backslash = 0; - } - - switch (s->mail_state) { - - 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 (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 (NGX_DEBUG_MAIL_PASSWD) - 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; - } - - if (rc == NGX_ERROR) { - ngx_mail_session_internal_server_error(s); - return; - } - - /* rc == NGX_MAIL_PARSE_INVALID_COMMAND */ - - break; - - case ngx_imap_auth_cram_md5: - arg = s->args.elts; - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "imap 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_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 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; - } - } - - 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; - } - - 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) { - last = imap_next; - last_len = sizeof(imap_next) - 1; - tag = 0; - } - - 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; - } - - if (tag) { - if (s->tag.len == 0) { - s->tag.len = sizeof(imap_star) - 1; - s->tag.data = (u_char *) imap_star; - } - - if (s->tagged_line.len < s->tag.len + text_len + last_len) { - s->tagged_line.len = s->tag.len + text_len + last_len; - s->tagged_line.data = ngx_palloc(c->pool, s->tagged_line.len); - if (s->tagged_line.data == NULL) { - ngx_mail_close_connection(c); - return; - } - } - - s->out.data = s->tagged_line.data; - s->out.len = s->tag.len + text_len + last_len; - - p = s->out.data; - - if (text) { - p = ngx_cpymem(p, text, text_len); - } - p = ngx_cpymem(p, s->tag.data, s->tag.len); - ngx_memcpy(p, last, last_len); - - - } else { - s->out.data = last; - s->out.len = last_len; - } - - if (rc != NGX_IMAP_NEXT) { - s->args.nelts = 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); -} - - -void -ngx_smtp_auth_state(ngx_event_t *rev) -{ - u_char *p, *last, *text, ch; - ssize_t size; - ngx_int_t rc; - ngx_str_t *arg, salt, l; - ngx_uint_t i; - ngx_connection_t *c; - ngx_mail_session_t *s; - ngx_mail_core_srv_conf_t *cscf; -#if (NGX_MAIL_SSL) - ngx_mail_ssl_conf_t *sslcf; -#endif - - c = rev->data; - s = c->data; - - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state"); - - if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); - c->timedout = 1; - ngx_mail_close_connection(c); - return; - } - - if (s->out.len) { - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy"); - s->blocked = 1; - return; - } - - s->blocked = 0; - - rc = ngx_mail_read_command(s); - - if (rc == NGX_AGAIN || rc == NGX_ERROR) { - return; - } - - text = NULL; - size = 0; - - if (rc == NGX_OK) { - switch (s->mail_state) { - - case ngx_smtp_start: - - switch (s->command) { - - case NGX_SMTP_HELO: - case NGX_SMTP_EHLO: - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - - if (s->args.nelts != 1) { - text = smtp_invalid_argument; - size = sizeof(smtp_invalid_argument) - 1; - s->state = 0; - break; - } - - arg = s->args.elts; - - s->smtp_helo.len = arg[0].len; - - s->smtp_helo.data = ngx_palloc(c->pool, arg[0].len); - if (s->smtp_helo.data == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - - ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len); - - if (s->command == NGX_SMTP_HELO) { - size = cscf->smtp_server_name.len; - text = cscf->smtp_server_name.data; - - } else { - s->esmtp = 1; - -#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) { - size = cscf->smtp_starttls_capability.len; - text = cscf->smtp_starttls_capability.data; - break; - } - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { - size = cscf->smtp_starttls_only_capability.len; - text = cscf->smtp_starttls_only_capability.data; - break; - } - } -#endif - - size = cscf->smtp_capability.len; - text = cscf->smtp_capability.data; - } - - break; - - case NGX_SMTP_AUTH: - -#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 == 0) { - text = smtp_invalid_argument; - size = sizeof(smtp_invalid_argument) - 1; - s->state = 0; - break; - } - - arg = s->args.elts; - - if (arg[0].len == 5) { - - if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) - == 0) - { - - if (s->args.nelts != 1) { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - s->mail_state = ngx_smtp_auth_login_username; - - size = sizeof(smtp_username) - 1; - text = smtp_username; - - break; - - } else if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", - 5) - == 0) - { - if (s->args.nelts == 1) { - s->mail_state = ngx_smtp_auth_plain; - - size = sizeof(smtp_next) - 1; - text = smtp_next; - - break; - } - - if (s->args.nelts == 2) { - - rc = ngx_mail_decode_auth_plain(s, &arg[1]); - - if (rc == NGX_OK) { - ngx_mail_do_auth(s); - return; - } - - if (rc == NGX_ERROR) { - ngx_mail_session_internal_server_error(s); - return; - } - - /* rc == NGX_MAIL_PARSE_INVALID_COMMAND */ - - break; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - 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->smtp_auth_methods - & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) - || s->args.nelts != 1) - { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - s->mail_state = ngx_smtp_auth_cram_md5; - - text = ngx_palloc(c->pool, - sizeof("334 " CRLF) - 1 - + ngx_base64_encoded_length(s->salt.len)); - if (text == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - - text[0] = '3'; text[1]= '3'; text[2] = '4'; text[3]= ' '; - salt.data = &text[4]; - s->salt.len -= 2; - - ngx_encode_base64(&salt, &s->salt); - - s->salt.len += 2; - size = 4 + salt.len; - text[size++] = CR; text[size++] = LF; - - break; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - - case NGX_SMTP_QUIT: - s->quit = 1; - text = smtp_bye; - size = sizeof(smtp_bye) - 1; - break; - - case NGX_SMTP_MAIL: - - if (s->connection->log->log_level >= NGX_LOG_INFO) { - l.len = s->buffer->last - s->buffer->start; - l.data = s->buffer->start; - - for (i = 0; i < l.len; i++) { - ch = l.data[i]; - - if (ch != CR && ch != LF) { - continue; - } - - l.data[i] = ' '; - } - - while (i) { - if (l.data[i - 1] != ' ') { - break; - } - - i--; - } - - l.len = i; - - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "client was rejected: \"%V\"", &l); - } - - text = smtp_auth_required; - size = sizeof(smtp_auth_required) - 1; - break; - - case NGX_SMTP_NOOP: - case NGX_SMTP_RSET: - text = smtp_ok; - size = sizeof(smtp_ok) - 1; - break; - -#if (NGX_MAIL_SSL) - - case NGX_SMTP_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; - - /* - * RFC3207 requires us to discard any knowledge - * obtained from client before STARTTLS. - */ - - s->smtp_helo.len = 0; - s->smtp_helo.data = NULL; - - text = smtp_ok; - size = sizeof(smtp_ok) - 1; - - break; - } - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; -#endif - - default: - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - break; - - case ngx_smtp_auth_login_username: - arg = s->args.elts; - s->mail_state = ngx_smtp_auth_login_password; - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "smtp 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, - "smtp auth login username: \"%V\"", &s->login); - - size = sizeof(smtp_password) - 1; - text = smtp_password; - - break; - - case ngx_smtp_auth_login_password: - arg = s->args.elts; - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "smtp 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, - "smtp auth login password: \"%V\"", &s->passwd); -#endif - - ngx_mail_do_auth(s); - return; - - case ngx_smtp_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; - } - - if (rc == NGX_ERROR) { - ngx_mail_session_internal_server_error(s); - return; - } - - /* rc == NGX_MAIL_PARSE_INVALID_COMMAND */ - - break; - - case ngx_smtp_auth_cram_md5: - arg = s->args.elts; - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "smtp 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_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 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; - } - } - - 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; - } - - ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, - "smtp auth cram-md5: \"%V\" \"%V\"", - &s->login, &s->passwd); - - s->auth_method = NGX_MAIL_AUTH_CRAM_MD5; - - ngx_mail_do_auth(s); - return; - } - } - - if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) { - s->mail_state = ngx_smtp_start; - s->state = 0; - text = smtp_invalid_command; - size = sizeof(smtp_invalid_command) - 1; - } - - 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; - } - - s->out.data = text; - s->out.len = size; - - ngx_mail_send(c->write); -} - - -static ngx_int_t -ngx_mail_decode_auth_plain(ngx_mail_session_t *s, ngx_str_t *encoded) -{ - u_char *p, *last; - ngx_str_t plain; - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, - "mail auth plain: \"%V\"", encoded); -#endif - - plain.data = ngx_palloc(s->connection->pool, - ngx_base64_decoded_length(encoded->len)); - if (plain.data == NULL){ - return NGX_ERROR; - } - - if (ngx_decode_base64(&plain, encoded) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "client sent invalid base64 encoding " - "in AUTH PLAIN command"); - return NGX_MAIL_PARSE_INVALID_COMMAND; - } - - p = plain.data; - last = p + plain.len; - - while (p < last && *p++) { /* void */ } - - if (p == last) { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "client sent invalid login in AUTH PLAIN command"); - return NGX_MAIL_PARSE_INVALID_COMMAND; - } - - s->login.data = p; - - while (p < last && *p) { p++; } - - if (p == last) { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "client sent invalid password in AUTH PLAIN command"); - return NGX_MAIL_PARSE_INVALID_COMMAND; - } - - s->login.len = p++ - s->login.data; - - s->passwd.len = last - p; - s->passwd.data = p; - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, - "mail auth plain: \"%V\" \"%V\"", - &s->login, &s->passwd); -#endif - - return NGX_OK; -} - - -static void -ngx_mail_do_auth(ngx_mail_session_t *s) -{ - s->args.nelts = 0; - s->buffer->pos = s->buffer->start; - s->buffer->last = s->buffer->start; - s->state = 0; - - if (s->connection->read->timer_set) { - ngx_del_timer(s->connection->read); - } - - s->login_attempt++; - - ngx_mail_auth_http_init(s); -} - - -static ngx_int_t -ngx_mail_read_command(ngx_mail_session_t *s) -{ - ssize_t n; - ngx_int_t rc; - ngx_str_t l; - - n = s->connection->recv(s->connection, s->buffer->last, - s->buffer->end - s->buffer->last); + n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last); if (n == NGX_ERROR || n == 0) { - ngx_mail_close_connection(s->connection); + ngx_mail_close_connection(c); return NGX_ERROR; } @@ -2019,7 +559,7 @@ ngx_mail_read_command(ngx_mail_session_t } if (n == NGX_AGAIN) { - if (ngx_handle_read_event(s->connection->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { ngx_mail_session_internal_server_error(s); return NGX_ERROR; } @@ -2027,19 +567,9 @@ ngx_mail_read_command(ngx_mail_session_t return NGX_AGAIN; } - switch (s->protocol) { - case NGX_MAIL_POP3_PROTOCOL: - rc = ngx_pop3_parse_command(s); - break; + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - case NGX_MAIL_IMAP_PROTOCOL: - rc = ngx_imap_parse_command(s); - break; - - default: /* NGX_MAIL_SMTP_PROTOCOL */ - rc = ngx_smtp_parse_command(s); - break; - } + rc = cscf->protocol->parse_command(s); if (rc == NGX_AGAIN) { @@ -2050,7 +580,7 @@ ngx_mail_read_command(ngx_mail_session_t l.len = s->buffer->last - s->buffer->start; l.data = s->buffer->start; - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long command \"%V\"", &l); s->quit = 1; @@ -2063,7 +593,7 @@ ngx_mail_read_command(ngx_mail_session_t } if (rc == NGX_ERROR) { - ngx_mail_close_connection(s->connection); + ngx_mail_close_connection(c); return NGX_ERROR; } @@ -2072,9 +602,31 @@ ngx_mail_read_command(ngx_mail_session_t void +ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c) +{ + s->args.nelts = 0; + 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); + } + + s->login_attempt++; + + ngx_mail_auth_http_init(s); +} + + +void ngx_mail_session_internal_server_error(ngx_mail_session_t *s) { - s->out = internal_server_errors[s->protocol]; + ngx_mail_core_srv_conf_t *cscf; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + s->out = cscf->protocol->internal_server_error; s->quit = 1; ngx_mail_send(s->connection->write); @@ -2100,6 +652,10 @@ ngx_mail_close_connection(ngx_connection #endif +#if (NGX_STAT_STUB) + ngx_atomic_fetch_add(ngx_stat_active, -1); +#endif + c->destroyed = 1; pool = c->pool; @@ -2110,7 +666,7 @@ ngx_mail_close_connection(ngx_connection } -static u_char * +u_char * ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len) { u_char *p; @@ -2135,7 +691,9 @@ ngx_mail_log_error(ngx_log_t *log, u_cha return p; } - p = ngx_snprintf(buf, len, ", server: %V", s->addr_text); + p = ngx_snprintf(buf, len, "%s, server: %V", + s->starttls ? " using starttls" : "", + s->addr_text); len -= p - buf; buf = p; diff --git a/src/mail/ngx_mail_imap_handler.c b/src/mail/ngx_mail_imap_handler.c new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_imap_handler.c @@ -0,0 +1,459 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +static ngx_int_t ngx_mail_imap_login(ngx_mail_session_t *s, + ngx_connection_t *c); +static ngx_int_t ngx_mail_imap_authenticate(ngx_mail_session_t *s, + ngx_connection_t *c); +static ngx_int_t ngx_mail_imap_capability(ngx_mail_session_t *s, + ngx_connection_t *c); +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; +static u_char imap_next[] = "+ OK" CRLF; +static u_char imap_plain_next[] = "+ " CRLF; +static u_char imap_username[] = "+ VXNlcm5hbWU6" CRLF; +static u_char imap_password[] = "+ UGFzc3dvcmQ6" CRLF; +static u_char imap_bye[] = "* BYE" CRLF; +static u_char imap_invalid_command[] = "BAD invalid command" CRLF; + + +void +ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_core_srv_conf_t *cscf; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + s->out.len = sizeof(imap_greeting) - 1; + s->out.data = imap_greeting; + + c->read->handler = ngx_mail_imap_init_protocol; + + ngx_add_timer(c->read, cscf->timeout); + + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + ngx_mail_close_connection(c); + } + + ngx_mail_send(c->write); +} + + +void +ngx_mail_imap_init_protocol(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_imap_srv_conf_t *iscf; + + c = rev->data; + + c->log->action = "in auth state"; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + s = c->data; + + if (s->buffer == NULL) { + if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) + == NGX_ERROR) + { + ngx_mail_session_internal_server_error(s); + return; + } + + iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); + + s->buffer = ngx_create_temp_buf(c->pool, iscf->client_buffer_size); + if (s->buffer == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + } + + s->mail_state = ngx_imap_start; + c->read->handler = ngx_mail_imap_auth_state; + + ngx_mail_imap_auth_state(rev); +} + + +void +ngx_mail_imap_auth_state(ngx_event_t *rev) +{ + u_char *p, *dst, *src, *end; + ngx_str_t *arg; + ngx_int_t rc; + ngx_uint_t tag, i; + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth state"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + if (s->out.len) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap send handler busy"); + s->blocked = 1; + return; + } + + s->blocked = 0; + + rc = ngx_mail_read_command(s, c); + + if (rc == NGX_AGAIN || rc == NGX_ERROR) { + return; + } + + tag = 1; + s->text.len = 0; + s->out.len = sizeof(imap_ok) - 1; + s->out.data = imap_ok; + + if (rc == NGX_OK) { + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth command: %i", + s->command); + + if (s->backslash) { + + arg = s->args.elts; + + for (i = 0; i < s->args.nelts; i++) { + dst = arg[i].data; + end = dst + arg[i].len; + + for (src = dst; src < end; dst++) { + *dst = *src; + if (*src++ == '\\') { + *dst = *src++; + } + } + + arg[i].len = dst - arg[i].data; + } + + s->backslash = 0; + } + + switch (s->mail_state) { + + case ngx_imap_start: + + switch (s->command) { + + case NGX_IMAP_LOGIN: + rc = ngx_mail_imap_login(s, c); + break; + + case NGX_IMAP_AUTHENTICATE: + rc = ngx_mail_imap_authenticate(s, c); + tag = (rc != NGX_OK); + break; + + case NGX_IMAP_CAPABILITY: + rc = ngx_mail_imap_capability(s, c); + break; + + case NGX_IMAP_LOGOUT: + s->quit = 1; + s->text.len = sizeof(imap_bye) - 1; + s->text.data = imap_bye; + break; + + case NGX_IMAP_NOOP: + break; + + case NGX_IMAP_STARTTLS: + rc = ngx_mail_imap_starttls(s, c); + break; + + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + break; + } + + break; + + case ngx_imap_auth_login_username: + rc = ngx_mail_auth_login_username(s, c); + + tag = 0; + s->out.len = sizeof(imap_password) - 1; + s->out.data = imap_password; + s->mail_state = ngx_imap_auth_login_password; + + break; + + case ngx_imap_auth_login_password: + rc = ngx_mail_auth_login_password(s, c); + break; + + case ngx_imap_auth_plain: + rc = ngx_mail_auth_plain(s, c, 0); + break; + + case ngx_imap_auth_cram_md5: + rc = ngx_mail_auth_cram_md5(s, c); + break; + } + + } else if (rc == NGX_IMAP_NEXT) { + tag = 0; + s->out.len = sizeof(imap_next) - 1; + s->out.data = imap_next; + } + + switch (rc) { + + case NGX_DONE: + ngx_mail_auth(s, c); + return; + + case NGX_ERROR: + ngx_mail_session_internal_server_error(s); + return; + + case NGX_MAIL_PARSE_INVALID_COMMAND: + s->state = 0; + s->out.len = sizeof(imap_invalid_command) - 1; + s->out.data = imap_invalid_command; + s->mail_state = ngx_imap_start; + break; + } + + if (tag) { + if (s->tag.len == 0) { + s->tag.len = sizeof(imap_star) - 1; + s->tag.data = (u_char *) imap_star; + } + + if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len) { + s->tagged_line.len = s->tag.len + s->text.len + s->out.len; + s->tagged_line.data = ngx_palloc(c->pool, s->tagged_line.len); + if (s->tagged_line.data == NULL) { + ngx_mail_close_connection(c); + return; + } + } + + p = s->tagged_line.data; + + if (s->text.len) { + p = ngx_cpymem(p, s->text.data, s->text.len); + } + + p = ngx_cpymem(p, s->tag.data, s->tag.len); + ngx_memcpy(p, s->out.data, s->out.len); + + s->out.len = s->text.len + s->tag.len + s->out.len; + s->out.data = s->tagged_line.data; + } + + if (rc != NGX_IMAP_NEXT) { + s->args.nelts = 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); +} + + +static ngx_int_t +ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + arg = s->args.elts; + + if (s->args.nelts != 2 || arg[0].len == 0) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + s->login.len = arg[0].len; + s->login.data = ngx_palloc(c->pool, s->login.len); + if (s->login.data == NULL) { + return NGX_ERROR; + } + + 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) { + return NGX_ERROR; + } + + 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 + + return NGX_DONE; +} + + +static ngx_int_t +ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_imap_srv_conf_t *iscf; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + rc = ngx_mail_auth_parse(s, c); + + switch (rc) { + + case NGX_MAIL_AUTH_LOGIN: + + s->out.len = sizeof(imap_username) - 1; + s->out.data = imap_username; + s->mail_state = ngx_imap_auth_login_username; + + return NGX_OK; + + case NGX_MAIL_AUTH_PLAIN: + + s->out.len = sizeof(imap_plain_next) - 1; + s->out.data = imap_plain_next; + s->mail_state = ngx_imap_auth_plain; + + return NGX_OK; + + 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; + } + + if (s->salt.data == NULL) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + if (ngx_mail_salt(s, c, cscf) != NGX_OK) { + return NGX_ERROR; + } + } + + if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) { + s->mail_state = ngx_imap_auth_cram_md5; + return NGX_OK; + } + + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_mail_imap_capability(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_imap_srv_conf_t *iscf; +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; +#endif + + iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_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) { + s->text = iscf->starttls_capability; + return NGX_OK; + } + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { + s->text = iscf->starttls_only_capability; + return NGX_OK; + } + } +#endif + + s->text = iscf->capability; + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_imap_starttls(ngx_mail_session_t *s, ngx_connection_t *c) +{ +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; + + 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; + return NGX_OK; + } + } + +#endif + + return NGX_MAIL_PARSE_INVALID_COMMAND; +} diff --git a/src/mail/ngx_mail_imap_module.c b/src/mail/ngx_mail_imap_module.c new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_imap_module.c @@ -0,0 +1,251 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +static void *ngx_mail_imap_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + +static ngx_str_t ngx_mail_imap_default_capabilities[] = { + ngx_string("IMAP4"), + ngx_string("IMAP4rev1"), + ngx_string("UIDPLUS"), + ngx_null_string +}; + + +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_null_string, 0 } +}; + + +static ngx_str_t ngx_mail_imap_auth_methods_names[] = { + ngx_string("AUTH=PLAIN"), + ngx_string("AUTH=LOGIN"), + ngx_null_string, /* APOP */ + ngx_string("AUTH=CRAM-MD5") +}; + + +static ngx_mail_protocol_t ngx_mail_imap_protocol = { + ngx_string("imap"), + { 143, 993, 0, 0 }, + NGX_MAIL_IMAP_PROTOCOL, + + ngx_mail_imap_init_session, + ngx_mail_imap_init_protocol, + ngx_mail_imap_parse_command, + ngx_mail_imap_auth_state, + + ngx_string("* BAD internal server error" CRLF) +}; + + +static ngx_command_t ngx_mail_imap_commands[] = { + + { ngx_string("imap_client_buffer"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_imap_srv_conf_t, client_buffer_size), + NULL }, + + { ngx_string("imap_capabilities"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_capabilities, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_imap_srv_conf_t, capabilities), + NULL }, + + { 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_imap_srv_conf_t, auth_methods), + &ngx_mail_imap_auth_methods }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_imap_module_ctx = { + &ngx_mail_imap_protocol, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_imap_create_srv_conf, /* create server configuration */ + ngx_mail_imap_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_imap_module = { + NGX_MODULE_V1, + &ngx_mail_imap_module_ctx, /* module context */ + ngx_mail_imap_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void * +ngx_mail_imap_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_imap_srv_conf_t *iscf; + + iscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_imap_srv_conf_t)); + if (iscf == NULL) { + return NULL; + } + + iscf->client_buffer_size = NGX_CONF_UNSET_SIZE; + + if (ngx_array_init(&iscf->capabilities, cf->pool, 4, sizeof(ngx_str_t)) + != NGX_OK) + { + return NULL; + } + + return iscf; +} + + +static char * +ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_imap_srv_conf_t *prev = parent; + ngx_mail_imap_srv_conf_t *conf = child; + + u_char *p, *auth; + size_t size; + ngx_str_t *c, *d; + ngx_uint_t i, m; + + ngx_conf_merge_size_value(conf->client_buffer_size, + prev->client_buffer_size, + (size_t) ngx_pagesize); + + ngx_conf_merge_bitmask_value(conf->auth_methods, + prev->auth_methods, + (NGX_CONF_BITMASK_SET + |NGX_MAIL_AUTH_PLAIN_ENABLED)); + + + if (conf->capabilities.nelts == 0) { + conf->capabilities = prev->capabilities; + } + + if (conf->capabilities.nelts == 0) { + + for (d = ngx_mail_imap_default_capabilities; d->len; d++) { + c = ngx_array_push(&conf->capabilities); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + *c = *d; + } + } + + size = sizeof("* CAPABILITY" CRLF) - 1; + + c = conf->capabilities.elts; + for (i = 0; i < conf->capabilities.nelts; i++) { + 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->auth_methods) { + size += 1 + ngx_mail_imap_auth_methods_names[i].len; + } + } + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->capability.len = size; + conf->capability.data = p; + + p = ngx_cpymem(p, "* CAPABILITY", sizeof("* CAPABILITY") - 1); + + for (i = 0; i < conf->capabilities.nelts; i++) { + *p++ = ' '; + 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->auth_methods) { + *p++ = ' '; + p = ngx_cpymem(p, ngx_mail_imap_auth_methods_names[i].data, + ngx_mail_imap_auth_methods_names[i].len); + } + } + + *p++ = CR; *p = LF; + + + size += sizeof(" STARTTLS") - 1; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_capability.len = size; + conf->starttls_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + conf->capability.len - (sizeof(CRLF) - 1)); + p = ngx_cpymem(p, " STARTTLS", sizeof(" STARTTLS") - 1); + *p++ = CR; *p = LF; + + + size = (auth - conf->capability.data) + sizeof(CRLF) - 1 + + sizeof(" STARTTLS LOGINDISABLED") - 1; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_only_capability.len = size; + conf->starttls_only_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + auth - conf->capability.data); + p = ngx_cpymem(p, " STARTTLS LOGINDISABLED", + sizeof(" STARTTLS LOGINDISABLED") - 1); + *p++ = CR; *p = LF; + + return NGX_CONF_OK; +} diff --git a/src/mail/ngx_mail_imap_module.h b/src/mail/ngx_mail_imap_module.h new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_imap_module.h @@ -0,0 +1,38 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ +#define _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + size_t client_buffer_size; + + ngx_str_t capability; + ngx_str_t starttls_capability; + ngx_str_t starttls_only_capability; + + ngx_uint_t auth_methods; + + ngx_array_t capabilities; +} ngx_mail_imap_srv_conf_t; + + +void ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c); +void ngx_mail_imap_init_protocol(ngx_event_t *rev); +void ngx_mail_imap_auth_state(ngx_event_t *rev); +ngx_int_t ngx_mail_imap_parse_command(ngx_mail_session_t *s); + + +extern ngx_module_t ngx_mail_imap_module; + + +#endif /* _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ */ 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 @@ -10,7 +10,8 @@ #include -ngx_int_t ngx_pop3_parse_command(ngx_mail_session_t *s) +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; @@ -207,7 +208,8 @@ invalid: } -ngx_int_t ngx_imap_parse_command(ngx_mail_session_t *s) +ngx_int_t +ngx_mail_imap_parse_command(ngx_mail_session_t *s) { u_char ch, *p, *c; ngx_str_t *arg; @@ -613,7 +615,8 @@ invalid: } -ngx_int_t ngx_smtp_parse_command(ngx_mail_session_t *s) +ngx_int_t +ngx_mail_smtp_parse_command(ngx_mail_session_t *s) { u_char ch, *p, *c, c0, c1, c2, c3; ngx_str_t *arg; @@ -822,3 +825,56 @@ invalid: return NGX_MAIL_PARSE_INVALID_COMMAND; } + + +ngx_int_t +ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + arg = s->args.elts; + + if (arg[0].len == 5) { + + if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) { + + if (s->args.nelts == 1) { + return NGX_MAIL_AUTH_LOGIN; + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) { + + if (s->args.nelts == 1) { + return NGX_MAIL_AUTH_PLAIN; + } + + if (s->args.nelts == 2) { + return ngx_mail_auth_plain(s, c, 1); + } + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + 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) { + return NGX_MAIL_AUTH_CRAM_MD5; + } + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; +} diff --git a/src/mail/ngx_mail_pop3_handler.c b/src/mail/ngx_mail_pop3_handler.c new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_pop3_handler.c @@ -0,0 +1,501 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +static ngx_int_t ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_int_t stls); +static ngx_int_t ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c); + + +static u_char pop3_greeting[] = "+OK POP3 ready" CRLF; +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; + + +void +ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c) +{ + u_char *p; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_pop3_srv_conf_t *pscf; + + pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + if (pscf->auth_methods + & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) + { + if (ngx_mail_salt(s, c, cscf) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + + s->out.data = ngx_palloc(c->pool, sizeof(pop3_greeting) + s->salt.len); + if (s->out.data == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + + p = ngx_cpymem(s->out.data, pop3_greeting, sizeof(pop3_greeting) - 3); + *p++ = ' '; + p = ngx_cpymem(p, s->salt.data, s->salt.len); + + s->out.len = p - s->out.data; + + } else { + s->out.len = sizeof(pop3_greeting) - 1; + s->out.data = pop3_greeting; + } + + c->read->handler = ngx_mail_pop3_init_protocol; + + ngx_add_timer(c->read, cscf->timeout); + + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + ngx_mail_close_connection(c); + } + + ngx_mail_send(c->write); +} + + +void +ngx_mail_pop3_init_protocol(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + + c->log->action = "in auth state"; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + s = c->data; + + if (s->buffer == NULL) { + if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) + == NGX_ERROR) + { + ngx_mail_session_internal_server_error(s); + return; + } + + s->buffer = ngx_create_temp_buf(c->pool, 128); + if (s->buffer == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + } + + s->mail_state = ngx_pop3_start; + c->read->handler = ngx_mail_pop3_auth_state; + + ngx_mail_pop3_auth_state(rev); +} + + +void +ngx_mail_pop3_auth_state(ngx_event_t *rev) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 auth state"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + if (s->out.len) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy"); + s->blocked = 1; + return; + } + + s->blocked = 0; + + rc = ngx_mail_read_command(s, c); + + if (rc == NGX_AGAIN || rc == NGX_ERROR) { + return; + } + + s->out.len = sizeof(pop3_ok) - 1; + s->out.data = pop3_ok; + + if (rc == NGX_OK) { + switch (s->mail_state) { + + case ngx_pop3_start: + + switch (s->command) { + + case NGX_POP3_USER: + rc = ngx_mail_pop3_user(s, c); + break; + + case NGX_POP3_CAPA: + rc = ngx_mail_pop3_capa(s, c, 1); + break; + + case NGX_POP3_APOP: + rc = ngx_mail_pop3_apop(s, c); + break; + + case NGX_POP3_AUTH: + rc = ngx_mail_pop3_auth(s, c); + break; + + case NGX_POP3_QUIT: + s->quit = 1; + break; + + case NGX_POP3_NOOP: + break; + + case NGX_POP3_STLS: + rc = ngx_mail_pop3_stls(s, c); + break; + + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + s->mail_state = ngx_pop3_start; + break; + } + + break; + + case ngx_pop3_user: + + switch (s->command) { + + case NGX_POP3_PASS: + rc = ngx_mail_pop3_pass(s, c); + break; + + case NGX_POP3_CAPA: + rc = ngx_mail_pop3_capa(s, c, 0); + break; + + case NGX_POP3_QUIT: + s->quit = 1; + break; + + case NGX_POP3_NOOP: + break; + + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + s->mail_state = ngx_pop3_start; + break; + } + + break; + + /* suppress warinings */ + case ngx_pop3_passwd: + break; + + case ngx_pop3_auth_login_username: + rc = ngx_mail_auth_login_username(s, c); + + s->out.len = sizeof(pop3_password) - 1; + s->out.data = pop3_password; + s->mail_state = ngx_pop3_auth_login_password; + break; + + case ngx_pop3_auth_login_password: + rc = ngx_mail_auth_login_password(s, c); + break; + + case ngx_pop3_auth_plain: + rc = ngx_mail_auth_plain(s, c, 0); + break; + + case ngx_pop3_auth_cram_md5: + rc = ngx_mail_auth_cram_md5(s, c); + break; + } + } + + switch (rc) { + + case NGX_DONE: + ngx_mail_auth(s, c); + return; + + case NGX_ERROR: + ngx_mail_session_internal_server_error(s); + return; + + case NGX_MAIL_PARSE_INVALID_COMMAND: + s->mail_state = ngx_pop3_start; + s->state = 0; + + s->out.len = sizeof(pop3_invalid_command) - 1; + s->out.data = pop3_invalid_command; + + /* fall through */ + + case NGX_OK: + + 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); + } +} + +static ngx_int_t +ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + if (s->args.nelts != 1) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + arg = s->args.elts; + s->login.len = arg[0].len; + s->login.data = ngx_palloc(c->pool, s->login.len); + if (s->login.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->login.data, arg[0].data, s->login.len); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "pop3 login: \"%V\"", &s->login); + + s->mail_state = ngx_pop3_user; + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + + if (s->args.nelts != 1) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + arg = s->args.elts; + s->passwd.len = arg[0].len; + s->passwd.data = ngx_palloc(c->pool, s->passwd.len); + if (s->passwd.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len); + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "pop3 passwd: \"%V\"", &s->passwd); +#endif + + return NGX_DONE; +} + + +static ngx_int_t +ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls) +{ + ngx_mail_pop3_srv_conf_t *pscf; +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; +#endif + + pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); + +#if (NGX_MAIL_SSL) + + if (stls && c->ssl == NULL) { + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { + s->out = pscf->starttls_capability; + return NGX_OK; + } + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { + s->out = pscf->starttls_only_capability; + return NGX_OK; + } + } + +#endif + + s->out = pscf->capability; + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c) +{ +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; + + 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; + return NGX_OK; + } + } + +#endif + + return NGX_MAIL_PARSE_INVALID_COMMAND; +} + + +static ngx_int_t +ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + ngx_mail_pop3_srv_conf_t *pscf; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + if (s->args.nelts != 2) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); + + if (!(pscf->auth_methods & NGX_MAIL_AUTH_APOP_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + arg = s->args.elts; + + s->login.len = arg[0].len; + s->login.data = ngx_palloc(c->pool, s->login.len); + if (s->login.data == NULL) { + return NGX_ERROR; + } + + 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) { + return NGX_ERROR; + } + + ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len); + + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "pop3 apop: \"%V\" \"%V\"", &s->login, &s->passwd); + + s->auth_method = NGX_MAIL_AUTH_APOP; + + return NGX_DONE; +} + + +static ngx_int_t +ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_mail_pop3_srv_conf_t *pscf; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + 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; + } + + rc = ngx_mail_auth_parse(s, c); + + switch (rc) { + + case NGX_MAIL_AUTH_LOGIN: + + s->out.len = sizeof(pop3_username) - 1; + s->out.data = pop3_username; + s->mail_state = ngx_pop3_auth_login_username; + + return NGX_OK; + + case NGX_MAIL_AUTH_PLAIN: + + s->out.len = sizeof(pop3_next) - 1; + s->out.data = pop3_next; + s->mail_state = ngx_pop3_auth_plain; + + return NGX_OK; + + case NGX_MAIL_AUTH_CRAM_MD5: + + if (!(pscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) { + s->mail_state = ngx_pop3_auth_cram_md5; + return NGX_OK; + } + + return NGX_ERROR; + } + + return rc; +} diff --git a/src/mail/ngx_mail_pop3_module.c b/src/mail/ngx_mail_pop3_module.c new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_pop3_module.c @@ -0,0 +1,263 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +static void *ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + +static ngx_str_t ngx_mail_pop3_default_capabilities[] = { + ngx_string("TOP"), + ngx_string("USER"), + ngx_string("UIDL"), + ngx_null_string +}; + + +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_null_string, 0 } +}; + + +static ngx_str_t ngx_mail_pop3_auth_plain_capability = + ngx_string("+OK methods supported:" CRLF + "LOGIN" CRLF + "PLAIN" CRLF + "." CRLF); + + +static ngx_str_t ngx_mail_pop3_auth_cram_md5_capability = + ngx_string("+OK methods supported:" CRLF + "LOGIN" CRLF + "PLAIN" CRLF + "CRAM-MD5" CRLF + "." CRLF); + + +static ngx_mail_protocol_t ngx_mail_pop3_protocol = { + ngx_string("pop3"), + { 110, 995, 0, 0 }, + NGX_MAIL_POP3_PROTOCOL, + + ngx_mail_pop3_init_session, + ngx_mail_pop3_init_protocol, + ngx_mail_pop3_parse_command, + ngx_mail_pop3_auth_state, + + ngx_string("-ERR internal server error" CRLF) +}; + + +static ngx_command_t ngx_mail_pop3_commands[] = { + + { ngx_string("pop3_capabilities"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_capabilities, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_pop3_srv_conf_t, capabilities), + NULL }, + + { ngx_string("pop3_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_pop3_srv_conf_t, auth_methods), + &ngx_mail_pop3_auth_methods }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_pop3_module_ctx = { + &ngx_mail_pop3_protocol, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_pop3_create_srv_conf, /* create server configuration */ + ngx_mail_pop3_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_pop3_module = { + NGX_MODULE_V1, + &ngx_mail_pop3_module_ctx, /* module context */ + ngx_mail_pop3_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void * +ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_pop3_srv_conf_t *pscf; + + pscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_pop3_srv_conf_t)); + if (pscf == NULL) { + return NULL; + } + + if (ngx_array_init(&pscf->capabilities, cf->pool, 4, sizeof(ngx_str_t)) + != NGX_OK) + { + return NULL; + } + + return pscf; +} + + +static char * +ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_pop3_srv_conf_t *prev = parent; + ngx_mail_pop3_srv_conf_t *conf = child; + + u_char *p; + size_t size, stls_only_size; + ngx_str_t *c, *d; + ngx_uint_t i; + + ngx_conf_merge_bitmask_value(conf->auth_methods, + prev->auth_methods, + (NGX_CONF_BITMASK_SET + |NGX_MAIL_AUTH_PLAIN_ENABLED)); + + if (conf->capabilities.nelts == 0) { + conf->capabilities = prev->capabilities; + } + + if (conf->capabilities.nelts == 0) { + + for (d = ngx_mail_pop3_default_capabilities; d->len; d++) { + c = ngx_array_push(&conf->capabilities); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + *c = *d; + } + } + + size = sizeof("+OK Capability list follows" CRLF) - 1 + + sizeof("." CRLF) - 1; + + stls_only_size = size + sizeof("STLS" CRLF) - 1; + + c = conf->capabilities.elts; + for (i = 0; i < conf->capabilities.nelts; i++) { + size += c[i].len + sizeof(CRLF) - 1; + + if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) { + continue; + } + + stls_only_size += c[i].len + sizeof(CRLF) - 1; + } + + if (conf->auth_methods & NGX_MAIL_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; + } + + conf->capability.len = size; + conf->capability.data = p; + + p = ngx_cpymem(p, "+OK Capability list follows" CRLF, + sizeof("+OK Capability list follows" CRLF) - 1); + + for (i = 0; i < conf->capabilities.nelts; i++) { + p = ngx_cpymem(p, c[i].data, c[i].len); + *p++ = CR; *p++ = LF; + } + + if (conf->auth_methods & NGX_MAIL_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; + + + size += sizeof("STLS" CRLF) - 1; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_capability.len = size; + conf->starttls_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + conf->capability.len - (sizeof("." CRLF) - 1)); + + p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1); + *p++ = '.'; *p++ = CR; *p = LF; + + + if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) { + conf->auth_capability = ngx_mail_pop3_auth_cram_md5_capability; + + } else { + conf->auth_capability = ngx_mail_pop3_auth_plain_capability; + } + + + p = ngx_palloc(cf->pool, stls_only_size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_only_capability.len = stls_only_size; + conf->starttls_only_capability.data = p; + + p = ngx_cpymem(p, "+OK Capability list follows" CRLF, + sizeof("+OK Capability list follows" CRLF) - 1); + + for (i = 0; i < conf->capabilities.nelts; i++) { + if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) { + continue; + } + + p = ngx_cpymem(p, c[i].data, c[i].len); + *p++ = CR; *p++ = LF; + } + + p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1); + *p++ = '.'; *p++ = CR; *p = LF; + + return NGX_CONF_OK; +} diff --git a/src/mail/ngx_mail_pop3_module.h b/src/mail/ngx_mail_pop3_module.h new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_pop3_module.h @@ -0,0 +1,37 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_MAIL_POP3_MODULE_H_INCLUDED_ +#define _NGX_MAIL_POP3_MODULE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + ngx_str_t capability; + ngx_str_t starttls_capability; + ngx_str_t starttls_only_capability; + ngx_str_t auth_capability; + + ngx_uint_t auth_methods; + + ngx_array_t capabilities; +} ngx_mail_pop3_srv_conf_t; + + +void ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c); +void ngx_mail_pop3_init_protocol(ngx_event_t *rev); +void ngx_mail_pop3_auth_state(ngx_event_t *rev); +ngx_int_t ngx_mail_pop3_parse_command(ngx_mail_session_t *s); + + +extern ngx_module_t ngx_mail_pop3_module; + + +#endif /* _NGX_MAIL_POP3_MODULE_H_INCLUDED_ */ diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c --- a/src/mail/ngx_mail_proxy_module.c +++ b/src/mail/ngx_mail_proxy_module.c @@ -78,6 +78,8 @@ static ngx_command_t ngx_mail_proxy_com static ngx_mail_module_t ngx_mail_proxy_module_ctx = { + NULL, /* protocol */ + NULL, /* create main configuration */ NULL, /* init main configuration */ diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_smtp_handler.c @@ -0,0 +1,548 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev); +static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, + ngx_connection_t *c); + +static ngx_int_t ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s, + ngx_connection_t *c); + +static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s, + ngx_connection_t *c, char *err); +static void ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, + ngx_connection_t *c, char *err); + + +static u_char smtp_ok[] = "250 2.0.0 OK" CRLF; +static u_char smtp_bye[] = "221 2.0.0 Bye" CRLF; +static u_char smtp_next[] = "334 " CRLF; +static u_char smtp_username[] = "334 VXNlcm5hbWU6" CRLF; +static u_char smtp_password[] = "334 UGFzc3dvcmQ6" CRLF; +static u_char smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF; +static u_char smtp_invalid_pipelining[] = + "503 5.5.0 Improper use of SMTP command pipelining" CRLF; +static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF; +static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF; + + +void +ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_msec_t timeout; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_smtp_srv_conf_t *sscf; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout; + ngx_add_timer(c->read, timeout); + + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + ngx_mail_close_connection(c); + } + + if (sscf->greeting_delay) { + c->read->handler = ngx_mail_smtp_invalid_pipelining; + return; + } + + c->read->handler = ngx_mail_smtp_init_protocol; + + s->out = sscf->greeting; + + ngx_mail_send(c->write); +} + + +static void +ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_smtp_srv_conf_t *sscf; + + c = rev->data; + s = c->data; + + c->log->action = "in delay pipelining state"; + + if (rev->timedout) { + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "delay greeting"); + + rev->timedout = 0; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + c->read->handler = ngx_mail_smtp_init_protocol; + + ngx_add_timer(c->read, cscf->timeout); + + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + ngx_mail_close_connection(c); + return; + } + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + s->out = sscf->greeting; + + } else { + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "invalid pipelining"); + + if (s->buffer == NULL) { + if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) { + return; + } + } + + if (ngx_mail_smtp_discard_command(s, c, + "client was rejected before greeting: \"%V\"") + != NGX_OK) + { + return; + } + + s->out.len = sizeof(smtp_invalid_pipelining) - 1; + s->out.data = smtp_invalid_pipelining; + } + + ngx_mail_send(c->write); +} + + +void +ngx_mail_smtp_init_protocol(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + + c->log->action = "in auth state"; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + s = c->data; + + if (s->buffer == NULL) { + if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) { + return; + } + } + + s->mail_state = ngx_smtp_start; + c->read->handler = ngx_mail_smtp_auth_state; + + ngx_mail_smtp_auth_state(rev); +} + + +static ngx_int_t +ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_smtp_srv_conf_t *sscf; + + if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) { + ngx_mail_session_internal_server_error(s); + return NGX_ERROR; + } + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + s->buffer = ngx_create_temp_buf(c->pool, sscf->client_buffer_size); + if (s->buffer == NULL) { + ngx_mail_session_internal_server_error(s); + return NGX_ERROR; + } + + return NGX_OK; +} + + +void +ngx_mail_smtp_auth_state(ngx_event_t *rev) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + if (s->out.len) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy"); + s->blocked = 1; + return; + } + + s->blocked = 0; + + rc = ngx_mail_read_command(s, c); + + if (rc == NGX_AGAIN || rc == NGX_ERROR) { + return; + } + + s->out.len = sizeof(smtp_ok) - 1; + s->out.data = smtp_ok; + + if (rc == NGX_OK) { + switch (s->mail_state) { + + case ngx_smtp_start: + + switch (s->command) { + + case NGX_SMTP_HELO: + case NGX_SMTP_EHLO: + rc = ngx_mail_smtp_helo(s, c); + break; + + case NGX_SMTP_AUTH: + rc = ngx_mail_smtp_auth(s, c); + break; + + case NGX_SMTP_QUIT: + s->quit = 1; + s->out.len = sizeof(smtp_bye) - 1; + s->out.data = smtp_bye; + break; + + case NGX_SMTP_MAIL: + rc = ngx_mail_smtp_mail(s, c); + break; + + case NGX_SMTP_NOOP: + case NGX_SMTP_RSET: + break; + + case NGX_SMTP_STARTTLS: + rc = ngx_mail_smtp_starttls(s, c); + break; + + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + break; + } + + break; + + case ngx_smtp_auth_login_username: + rc = ngx_mail_auth_login_username(s, c); + + s->out.len = sizeof(smtp_password) - 1; + s->out.data = smtp_password; + s->mail_state = ngx_smtp_auth_login_password; + break; + + case ngx_smtp_auth_login_password: + rc = ngx_mail_auth_login_password(s, c); + break; + + case ngx_smtp_auth_plain: + rc = ngx_mail_auth_plain(s, c, 0); + break; + + case ngx_smtp_auth_cram_md5: + rc = ngx_mail_auth_cram_md5(s, c); + break; + } + } + + switch (rc) { + + case NGX_DONE: + ngx_mail_auth(s, c); + return; + + case NGX_ERROR: + ngx_mail_session_internal_server_error(s); + return; + + case NGX_MAIL_PARSE_INVALID_COMMAND: + s->mail_state = ngx_smtp_start; + s->state = 0; + + s->out.len = sizeof(smtp_invalid_command) - 1; + s->out.data = smtp_invalid_command; + + /* fall through */ + + case NGX_OK: + 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); + } +} + + +static ngx_int_t +ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + ngx_mail_smtp_srv_conf_t *sscf; +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; +#endif + + if (s->args.nelts != 1) { + s->out.len = sizeof(smtp_invalid_argument) - 1; + s->out.data = smtp_invalid_argument; + s->state = 0; + return NGX_OK; + } + + arg = s->args.elts; + + s->smtp_helo.len = arg[0].len; + + s->smtp_helo.data = ngx_palloc(c->pool, arg[0].len); + if (s->smtp_helo.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len); + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + if (s->command == NGX_SMTP_HELO) { + s->out = sscf->server_name; + + } else { + s->esmtp = 1; + +#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) { + s->out = sscf->starttls_capability; + return NGX_OK; + } + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { + s->out = sscf->starttls_only_capability; + return NGX_OK; + } + } +#endif + + s->out = sscf->capability; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_smtp_srv_conf_t *sscf; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + if (s->args.nelts == 0) { + s->out.len = sizeof(smtp_invalid_argument) - 1; + s->out.data = smtp_invalid_argument; + s->state = 0; + return NGX_OK; + } + + rc = ngx_mail_auth_parse(s, c); + + switch (rc) { + + case NGX_MAIL_AUTH_LOGIN: + + s->out.len = sizeof(smtp_username) - 1; + s->out.data = smtp_username; + s->mail_state = ngx_smtp_auth_login_username; + + return NGX_OK; + + case NGX_MAIL_AUTH_PLAIN: + + s->out.len = sizeof(smtp_next) - 1; + s->out.data = smtp_next; + s->mail_state = ngx_smtp_auth_plain; + + return NGX_OK; + + case NGX_MAIL_AUTH_CRAM_MD5: + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + if (!(sscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (s->salt.data == NULL) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + if (ngx_mail_salt(s, c, cscf) != NGX_OK) { + return NGX_ERROR; + } + } + + if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK) { + s->mail_state = ngx_smtp_auth_cram_md5; + return NGX_OK; + } + + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\""); + + s->out.len = sizeof(smtp_auth_required) - 1; + s->out.data = smtp_auth_required; + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c) +{ +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; + + if (c->ssl == NULL) { + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + if (sslcf->starttls) { + + /* + * RFC3207 requires us to discard any knowledge + * obtained from client before STARTTLS. + */ + + s->smtp_helo.len = 0; + s->smtp_helo.data = NULL; + + c->read->handler = ngx_mail_starttls_handler; + return NGX_OK; + } + } + +#endif + + return NGX_MAIL_PARSE_INVALID_COMMAND; +} + + +static ngx_int_t +ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c, + char *err) +{ + ssize_t n; + + n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last); + + if (n == NGX_ERROR || n == 0) { + ngx_mail_close_connection(c); + return NGX_ERROR; + } + + if (n > 0) { + s->buffer->last += n; + } + + if (n == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + ngx_mail_session_internal_server_error(s); + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + ngx_mail_smtp_log_rejected_command(s, c, err); + + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + + return NGX_OK; +} + + +static void +ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, ngx_connection_t *c, + char *err) +{ + u_char ch; + ngx_str_t cmd; + ngx_uint_t i; + + if (c->log->log_level < NGX_LOG_INFO) { + return; + } + + cmd.len = s->buffer->last - s->buffer->start; + cmd.data = s->buffer->start; + + for (i = 0; i < cmd.len; i++) { + ch = cmd.data[i]; + + if (ch != CR && ch != LF) { + continue; + } + + cmd.data[i] = '_'; + } + + cmd.len = i; + + ngx_log_error(NGX_LOG_INFO, c->log, 0, err, &cmd); +} diff --git a/src/mail/ngx_mail_smtp_module.c b/src/mail/ngx_mail_smtp_module.c new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_smtp_module.c @@ -0,0 +1,285 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +static void *ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + +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_null_string, 0 } +}; + + +static ngx_str_t ngx_mail_smtp_auth_methods_names[] = { + ngx_string("PLAIN"), + ngx_string("LOGIN"), + ngx_null_string, /* APOP */ + ngx_string("CRAM-MD5") +}; + + +static ngx_mail_protocol_t ngx_mail_smtp_protocol = { + ngx_string("smtp"), + { 25, 465, 587, 0 }, + NGX_MAIL_SMTP_PROTOCOL, + + ngx_mail_smtp_init_session, + ngx_mail_smtp_init_protocol, + ngx_mail_smtp_parse_command, + ngx_mail_smtp_auth_state, + + ngx_string("451 4.3.2 Internal server error" CRLF) +}; + + +static ngx_command_t ngx_mail_smtp_commands[] = { + + { ngx_string("smtp_client_buffer"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_smtp_srv_conf_t, client_buffer_size), + NULL }, + + { ngx_string("smtp_greeting_delay"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_smtp_srv_conf_t, greeting_delay), + NULL }, + + { ngx_string("smtp_capabilities"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_capabilities, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_smtp_srv_conf_t, capabilities), + NULL }, + + { ngx_string("smtp_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_smtp_srv_conf_t, auth_methods), + &ngx_mail_smtp_auth_methods }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_smtp_module_ctx = { + &ngx_mail_smtp_protocol, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_smtp_create_srv_conf, /* create server configuration */ + ngx_mail_smtp_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_smtp_module = { + NGX_MODULE_V1, + &ngx_mail_smtp_module_ctx, /* module context */ + ngx_mail_smtp_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void * +ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_smtp_srv_conf_t *sscf; + + sscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_smtp_srv_conf_t)); + if (sscf == NULL) { + return NULL; + } + + sscf->client_buffer_size = NGX_CONF_UNSET_SIZE; + sscf->greeting_delay = NGX_CONF_UNSET_MSEC; + + if (ngx_array_init(&sscf->capabilities, cf->pool, 4, sizeof(ngx_str_t)) + != NGX_OK) + { + return NULL; + } + + return sscf; +} + + +static char * +ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_smtp_srv_conf_t *prev = parent; + ngx_mail_smtp_srv_conf_t *conf = child; + + u_char *p, *auth; + size_t size; + ngx_str_t *c; + ngx_uint_t i, m; + ngx_mail_core_srv_conf_t *cscf; + + ngx_conf_merge_size_value(conf->client_buffer_size, + prev->client_buffer_size, + (size_t) ngx_pagesize); + + ngx_conf_merge_msec_value(conf->greeting_delay, + prev->greeting_delay, 0); + + ngx_conf_merge_bitmask_value(conf->auth_methods, + prev->auth_methods, + (NGX_CONF_BITMASK_SET + |NGX_MAIL_AUTH_PLAIN_ENABLED + |NGX_MAIL_AUTH_LOGIN_ENABLED)); + + + cscf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_core_module); + + size = sizeof("220 ESMTP ready" CRLF) - 1 + cscf->server_name.len; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->greeting.len = size; + conf->greeting.data = p; + + *p++ = '2'; *p++ = '2'; *p++ = '0'; *p++ = ' '; + p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); + ngx_memcpy(p, " ESMTP ready" CRLF, sizeof(" ESMTP ready" CRLF) - 1); + + + size = sizeof("250 " CRLF) - 1 + cscf->server_name.len; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->server_name.len = size; + conf->server_name.data = p; + + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; + p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); + *p++ = CR; *p = LF; + + + if (conf->capabilities.nelts == 0) { + conf->capabilities = prev->capabilities; + } + + size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1 + + sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1; + + c = conf->capabilities.elts; + for (i = 0; i < conf->capabilities.nelts; i++) { + size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1; + } + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + size += 1 + ngx_mail_smtp_auth_methods_names[i].len; + } + } + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->capability.len = size; + conf->capability.data = p; + + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; + p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); + *p++ = CR; *p++ = LF; + + for (i = 0; i < conf->capabilities.nelts; i++) { + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; + p = ngx_cpymem(p, c[i].data, c[i].len); + *p++ = CR; *p++ = LF; + } + + auth = p; + + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; + *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 <<= 1, i++) + { + if (m & conf->auth_methods) { + *p++ = ' '; + p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data, + ngx_mail_smtp_auth_methods_names[i].len); + } + } + + *p++ = CR; *p = LF; + + size += sizeof("250 STARTTLS" CRLF) - 1; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_capability.len = size; + conf->starttls_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + conf->capability.len); + + p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); + *p++ = CR; *p = LF; + + p = conf->starttls_capability.data + + (auth - conf->capability.data) + 3; + *p = '-'; + + size = (auth - conf->capability.data) + + sizeof("250 STARTTLS" CRLF) - 1; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_only_capability.len = size; + conf->starttls_only_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + auth - conf->capability.data); + + ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); + + return NGX_CONF_OK; +} diff --git a/src/mail/ngx_mail_smtp_module.h b/src/mail/ngx_mail_smtp_module.h new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_smtp_module.h @@ -0,0 +1,44 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ +#define _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ + + +#include +#include +#include +#include + + +typedef struct { + ngx_msec_t greeting_delay; + + size_t client_buffer_size; + + ngx_str_t capability; + ngx_str_t starttls_capability; + ngx_str_t starttls_only_capability; + + ngx_str_t server_name; + ngx_str_t greeting; + + ngx_uint_t auth_methods; + + ngx_array_t capabilities; +} ngx_mail_smtp_srv_conf_t; + + +void ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c); +void ngx_mail_smtp_init_protocol(ngx_event_t *rev); +void ngx_mail_smtp_auth_state(ngx_event_t *rev); +ngx_int_t ngx_mail_smtp_parse_command(ngx_mail_session_t *s); + + +extern ngx_module_t ngx_mail_smtp_module; + + +#endif /* _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ */ diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -120,6 +120,8 @@ static ngx_command_t ngx_mail_ssl_comma static ngx_mail_module_t ngx_mail_ssl_module_ctx = { + NULL, /* protocol */ + NULL, /* create main configuration */ NULL, /* init main configuration */