[PATCH] SSL support for the mail proxy module

Kunal Pariani kpariani at zimbra.com
Tue Sep 16 07:13:17 UTC 2014


# HG changeset patch
# User Kunal Pariani <kpariani at zimbra.com>
# Date 1410850478 18000
#      Tue Sep 16 01:54:38 2014 -0500
# Node ID b35a5391bef67f79f7a099c0cb868477b9abeb43
# Parent  e3016ee8dba396614f28a3644996b8cc6de8f9e3
SSL support for the mail proxy module

diff -r e3016ee8dba3 -r b35a5391bef6 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	Tue Sep 16 01:54:38 2014 -0500
@@ -18,6 +18,15 @@ typedef struct {
     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_close_session
 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 @@ static ngx_command_t  ngx_mail_proxy_com
       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 @@ ngx_mail_proxy_init(ngx_mail_session_t *
 
     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_mail_proxy_close_session(ngx_mail_se
         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 @@ ngx_mail_proxy_create_conf(ngx_conf_t *c
         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_MAIL_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 @@ ngx_mail_proxy_merge_conf(ngx_conf_t *cf
                               (size_t) ngx_pagesize);
     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);
 
+#if (NGX_MAIL_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