[PATCH] SSL support for the mail proxy module
Kunal Pariani
kpariani at zimbra.com
Sat Sep 13 23:14:31 UTC 2014
# HG changeset patch
# User Kunal Pariani <kpariani at zimbra.com>
# Date 1410649455 18000
# Sat Sep 13 18:04:15 2014 -0500
# Node ID 5c2524403ab7c870b1fa7294f935d3292f25fd1d
# Parent e3016ee8dba396614f28a3644996b8cc6de8f9e3
SSL support for the mail proxy module
diff -r e3016ee8dba3 -r 5c2524403ab7 src/mail/ngx_mail_proxy_module.c
--- a/src/mail/ngx_mail_proxy_module.c Sat Sep 13 21:47:13 2014 +0400
+++ b/src/mail/ngx_mail_proxy_module.c Sat Sep 13 18:04:15 2014 -0500
@@ -18,6 +18,15 @@
ngx_flag_t xclient;
size_t buffer_size;
ngx_msec_t timeout;
+#if (NGX_MAIL_SSL)
+ ngx_flag_t proxy_ssl;
+ ngx_ssl_t *ssl;
+ ngx_uint_t ssl_protocols;
+ ngx_str_t ssl_ciphers;
+ ngx_flag_t ssl_verify;
+ ngx_uint_t ssl_verify_depth;
+ ngx_str_t ssl_trusted_certificate;
+#endif
} ngx_mail_proxy_conf_t;
@@ -35,7 +44,22 @@
static void *ngx_mail_proxy_create_conf(ngx_conf_t *cf);
static char *ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent,
void *child);
+#if (NGX_MAIL_SSL)
+static char *ngx_mail_proxy_set_ssl(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static void ngx_mail_proxy_ssl_init_connection(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+static void ngx_mail_proxy_ssl_handshake(ngx_connection_t *c);
+static ngx_conf_bitmask_t ngx_mail_proxy_ssl_protocols[] = {
+ { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
+ { ngx_string("SSLv3"), NGX_SSL_SSLv3 },
+ { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
+ { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
+ { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
+ { ngx_null_string, 0 }
+};
+#endif
static ngx_command_t ngx_mail_proxy_commands[] = {
@@ -74,6 +98,52 @@
offsetof(ngx_mail_proxy_conf_t, xclient),
NULL },
+#if (NGX_MAIL_SSL)
+
+ { ngx_string("proxy_ssl"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_mail_proxy_set_ssl,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, proxy_ssl),
+ NULL },
+
+ { ngx_string("proxy_ssl_protocols"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, ssl_protocols),
+ &ngx_mail_proxy_ssl_protocols },
+
+ { ngx_string("proxy_ssl_ciphers"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, ssl_ciphers),
+ NULL },
+
+ { ngx_string("proxy_ssl_verify"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, ssl_verify),
+ NULL },
+
+ { ngx_string("proxy_ssl_verify_depth"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, ssl_verify_depth),
+ NULL },
+
+ { ngx_string("proxy_ssl_trusted_certificate"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, ssl_trusted_certificate),
+ NULL },
+
+#endif
+
ngx_null_command
};
@@ -174,6 +244,15 @@
s->out.len = 0;
+#if (NGX_MAIL_SSL)
+
+ if (pcf->proxy_ssl && p->upstream.connection->ssl == NULL) {
+ ngx_mail_proxy_ssl_init_connection(s, p->upstream.connection);
+ return;
+ }
+
+#endif
+
switch (s->protocol) {
case NGX_MAIL_POP3_PROTOCOL:
@@ -1091,6 +1170,13 @@
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
"close mail proxy connection: %d",
s->proxy->upstream.connection->fd);
+#if (NGX_MAIL_SSL)
+
+ if (s->proxy->upstream.connection->ssl) {
+ s->proxy->upstream.connection->ssl->no_wait_shutdown = 1;
+ ngx_ssl_shutdown(s->proxy->upstream.connection);
+ }
+#endif
ngx_close_connection(s->proxy->upstream.connection);
}
@@ -1109,12 +1195,26 @@
return NULL;
}
+ /*
+ * set by ngx_pcalloc():
+ *
+ * pcf->ssl_protocols = 0;
+ * pcf->ssl_ciphers = { 0, NULL };
+ * pcf->ssl_trusted_certificate = { 0, NULL };
+ */
pcf->enable = NGX_CONF_UNSET;
pcf->pass_error_message = NGX_CONF_UNSET;
pcf->xclient = NGX_CONF_UNSET;
pcf->buffer_size = NGX_CONF_UNSET_SIZE;
pcf->timeout = NGX_CONF_UNSET_MSEC;
+#if (NGX_HTTP_SSL)
+ pcf->proxy_ssl = NGX_CONF_UNSET;
+ pcf->ssl = NGX_CONF_UNSET_PTR;
+ pcf->ssl_verify = NGX_CONF_UNSET;
+ pcf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
+#endif
+
return pcf;
}
@@ -1132,5 +1232,161 @@
(size_t) ngx_pagesize);
ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);
+#if (NGX_HTTP_SSL)
+
+ ngx_conf_merge_value(conf->proxy_ssl, prev->proxy_ssl, 0);
+ ngx_conf_merge_ptr_value(conf->ssl, prev->ssl, NULL);
+
+ ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,
+ (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3
+ |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1
+ |NGX_SSL_TLSv1_2));
+
+ ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,
+ "DEFAULT");
+
+ ngx_conf_merge_value(conf->ssl_verify,
+ prev->ssl_verify, 0);
+ ngx_conf_merge_uint_value(conf->ssl_verify_depth,
+ prev->ssl_verify_depth, 1);
+ ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
+ prev->ssl_trusted_certificate, "");
+
+#endif
return NGX_CONF_OK;
}
+
+#if (NGX_MAIL_SSL)
+
+static char*
+ngx_mail_proxy_set_ssl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
+
+ ngx_pool_cleanup_t *cln;
+ char *rc;
+ ngx_mail_proxy_conf_t *pcf;
+
+ rc = ngx_conf_set_flag_slot(cf, cmd, conf);
+ if (rc != NGX_CONF_OK) {
+ return rc;
+ }
+
+ pcf = (ngx_mail_proxy_conf_t *)conf;
+
+ if (!pcf->proxy_ssl) {
+ return NGX_CONF_OK;
+ }
+
+ pcf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
+
+ if (pcf->ssl == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pcf->ssl->log = cf->log;
+
+ if (ngx_ssl_create(pcf->ssl, pcf->ssl_protocols, NULL)
+ != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ cln->handler = ngx_ssl_cleanup_ctx;
+ cln->data = pcf->ssl;
+
+ if (SSL_CTX_set_cipher_list(pcf->ssl->ctx,
+ (const char *) pcf->ssl_ciphers.data)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
+ "SSL_CTX_set_cipher_list(\"%V\") failed",
+ &pcf->ssl_ciphers);
+ return NGX_CONF_ERROR;
+ }
+
+ if (pcf->ssl_verify) {
+ if (pcf->ssl_trusted_certificate.len == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no proxy_ssl_trusted_certificate for proxy_ssl_verify");
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_trusted_certificate(cf, pcf->ssl,
+ &pcf->ssl_trusted_certificate,
+ pcf->ssl_verify_depth)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+static void
+ngx_mail_proxy_ssl_init_connection(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_int_t rc;
+ ngx_mail_proxy_conf_t *pcf;
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+
+ if (ngx_ssl_create_connection(pcf->ssl, c, NGX_SSL_CLIENT)
+ != NGX_OK)
+ {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ s->connection->log->action = "SSL handshaking to upstream";
+
+ rc = ngx_ssl_handshake(c);
+
+ if (rc == NGX_AGAIN) {
+ c->ssl->handler = ngx_mail_proxy_ssl_handshake;
+ return;
+ }
+
+ ngx_mail_proxy_ssl_handshake(c);
+}
+
+
+static void
+ngx_mail_proxy_ssl_handshake(ngx_connection_t *c)
+{
+ ngx_mail_session_t *s;
+ s = c->data;
+
+ if (c->ssl->handshaked) {
+
+ c->write->handler = ngx_mail_proxy_dummy_handler;
+ switch (s->protocol) {
+
+ case NGX_MAIL_POP3_PROTOCOL:
+ c->read->handler = ngx_mail_proxy_pop3_handler;
+ s->mail_state = ngx_pop3_start;
+ break;
+
+ case NGX_MAIL_IMAP_PROTOCOL:
+ c->read->handler = ngx_mail_proxy_imap_handler;
+ s->mail_state = ngx_imap_start;
+ break;
+
+ default: /* NGX_MAIL_SMTP_PROTOCOL */
+ c->read->handler = ngx_mail_proxy_smtp_handler;
+ s->mail_state = ngx_smtp_start;
+ break;
+ }
+
+ /* server might have send the initial welcome msg */
+ c->read->handler(c->read);
+ } else {
+ /* when handshake fails, we should close the session */
+ ngx_mail_proxy_upstream_error(s);
+ }
+}
+
+#endif
More information about the nginx-devel
mailing list