[PATCH] SSL support for the mail proxy module

Franck Levionnois flevionnois at gmail.com
Tue Oct 21 07:59:04 UTC 2014


Hello,

The patch below has been submited some months ago. It do about the same,
and it support to return a name by the auth script.

Kind regards
Franck Levionnois.

---------- Forwarded message ----------
From: <flevionnois at gmail.com>
Date: 2014-01-24 21:40 GMT+01:00
Subject: [PATCH 1 of 1] Mail: added support for SSL client certificate
To: nginx-devel at nginx.org


# HG changeset patch
# User Franck Levionnois <flevionnois at gmail.com>
# Date 1390577176 -3600
#      Fri Jan 24 16:26:16 2014 +0100
# Node ID d7b8381c200e300c2b6729574f4c2a
b537804f56
# Parent  a387ce36744aa36b50e8171dbf01ef716748327e
Mail: added support for SSL client certificate

Add support for SSL module like HTTP.

Added mail configuration directives (like http):
ssl_verify_client, ssl_verify_depth,  ssl_client_certificate,
ssl_trusted_certificate, ssl_crl

Added headers:
Auth-Certificate, Auth-Certificate-Verify, Auth-Issuer-DN, Auth-Subject-DN,
Auth-Subject-Serial

diff -r a387ce36744a -r d7b8381c200e src/mail/ngx_mail_auth_http_module.c
--- a/src/mail/ngx_mail_auth_http_module.c      Thu Jan 23 22:09:59 2014
+0900
+++ b/src/mail/ngx_mail_auth_http_module.c      Fri Jan 24 16:26:16 2014
+0100
@@ -1135,6 +1135,32 @@
                    "mail auth http dummy handler");
 }

+#if (NGX_MAIL_SSL)
+static ngx_int_t
+ngx_ssl_get_certificate_oneline(ngx_connection_t *c, ngx_pool_t *pool,
+                                ngx_str_t *b64_cert)
+{
+    ngx_str_t    pemCert;
+    if (ngx_ssl_get_raw_certificate(c, pool, &pemCert) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (pemCert.len == 0) {
+        b64_cert->len = 0;
+        return NGX_OK;
+    }
+
+    b64_cert->len = ngx_base64_encoded_length(pemCert.len);
+    b64_cert->data = ngx_palloc( pool, b64_cert->len);
+    if (b64_cert->data == NULL) {
+        b64_cert->len = 0;
+        return NGX_ERROR;
+    }
+    ngx_encode_base64(b64_cert, &pemCert);
+
+    return NGX_OK;
+}
+#endif

 static ngx_buf_t *
 ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool,
@@ -1142,7 +1168,9 @@
 {
     size_t                     len;
     ngx_buf_t                 *b;
-    ngx_str_t                  login, passwd;
+    ngx_str_t                  login, passwd, client_cert, client_verify,
+                               client_subject, client_issuer,
+                               client_serial;
     ngx_mail_core_srv_conf_t  *cscf;

     if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {
@@ -1155,6 +1183,42 @@

     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

+#if (NGX_MAIL_SSL)
+    if (s->connection->ssl) {
+        if (ngx_ssl_get_client_verify(s->connection, pool,
+                                     &client_verify) != NGX_OK) {
+            return NULL;
+        }
+
+        if (ngx_ssl_get_subject_dn(s->connection, pool,
+                                  &client_subject) != NGX_OK) {
+            return NULL;
+        }
+
+        if (ngx_ssl_get_issuer_dn(s->connection, pool,
+                                  &client_issuer) != NGX_OK) {
+            return NULL;
+        }
+
+        if (ngx_ssl_get_serial_number(s->connection, pool,
+                                      &client_serial) != NGX_OK) {
+            return NULL;
+        }
+
+        if (ngx_ssl_get_certificate_oneline(s->connection, pool,
+                                            &client_cert) != NGX_OK) {
+            return NULL;
+        }
+    } else {
+        client_verify.len = 0;
+        client_issuer.len = 0;
+        client_subject.len = 0;
+        client_serial.len = 0;
+        client_cert.len = 0;
+    }
+
+#endif
+
     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
@@ -1163,6 +1227,18 @@
           + 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
+#if (NGX_MAIL_SSL)
+          + sizeof("Auth-Certificate: ") - 1 + client_cert.len
+          + sizeof(CRLF) - 1
+          + sizeof("Auth-Certificate-Verify: ") - 1 + client_verify.len
+          + sizeof(CRLF) - 1
+          + sizeof("Auth-Issuer-DN: ") - 1 + client_issuer.len
+          + sizeof(CRLF) - 1
+          + sizeof("Auth-Subject-DN: ") - 1 + client_subject.len
+          + sizeof(CRLF) - 1
+          + sizeof("Auth-Subject-Serial: ") - 1 + client_serial.len
+          + sizeof(CRLF) - 1
+#endif
           + sizeof("Auth-Protocol: ") - 1 + cscf->protocol->name.len
                 + sizeof(CRLF) - 1
           + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN
@@ -1212,7 +1288,43 @@

         s->passwd.data = NULL;
     }
-
+#if (NGX_MAIL_SSL)
+    if ( client_cert.len )
+    {
+        b->last = ngx_cpymem(b->last, "Auth-Certificate: ",
+                             sizeof("Auth-Certificate: ") - 1);
+        b->last = ngx_copy(b->last, client_cert.data, client_cert.len);
+        *b->last++ = CR; *b->last++ = LF;
+    }
+    if ( client_verify.len )
+    {
+        b->last = ngx_cpymem(b->last, "Auth-Certificate-Verify: ",
+                             sizeof("Auth-Certificate-Verify: ") - 1);
+        b->last = ngx_copy(b->last, client_verify.data, client_verify.len);
+        *b->last++ = CR; *b->last++ = LF;
+    }
+    if ( client_issuer.len )
+    {
+        b->last = ngx_cpymem(b->last, "Auth-Issuer-DN: ",
+                             sizeof("Auth-Issuer-DN: ") - 1);
+        b->last = ngx_copy(b->last, client_issuer.data, client_issuer.len);
+        *b->last++ = CR; *b->last++ = LF;
+    }
+    if ( client_subject.len )
+    {
+        b->last = ngx_cpymem(b->last, "Auth-Subject-DN: ",
+                             sizeof("Auth-Subject-DN: ") - 1);
+        b->last = ngx_copy(b->last, client_subject.data,
client_subject.len);
+        *b->last++ = CR; *b->last++ = LF;
+    }
+    if ( client_serial.len )
+    {
+        b->last = ngx_cpymem(b->last, "Auth-Subject-Serial: ",
+                             sizeof("Auth-Subject-Serial: ") - 1);
+        b->last = ngx_copy(b->last, client_serial.data, client_serial.len);
+        *b->last++ = CR; *b->last++ = LF;
+    }
+#endif
     b->last = ngx_cpymem(b->last, "Auth-Protocol: ",
                          sizeof("Auth-Protocol: ") - 1);
     b->last = ngx_cpymem(b->last, cscf->protocol->name.data,
diff -r a387ce36744a -r d7b8381c200e src/mail/ngx_mail_handler.c
--- a/src/mail/ngx_mail_handler.c       Thu Jan 23 22:09:59 2014 +0900
+++ b/src/mail/ngx_mail_handler.c       Fri Jan 24 16:26:16 2014 +0100
@@ -236,11 +236,59 @@
 {
     ngx_mail_session_t        *s;
     ngx_mail_core_srv_conf_t  *cscf;
+#if (NGX_MAIL_SSL)
+    ngx_mail_ssl_conf_t       *sslcf;
+#endif
+
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+        "ngx_mail_ssl_handshake_handler handshaked: %d ",
+        c->ssl->handshaked );

     if (c->ssl->handshaked) {

         s = c->data;

+#if (NGX_MAIL_SSL)
+        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+        if (sslcf->verify) {
+            long                      rc;
+
+            rc = SSL_get_verify_result(c->ssl->connection);
+
+            if (rc != X509_V_OK
+                && (sslcf->verify != 3 ||
!ngx_ssl_verify_error_optional(rc)))
+            {
+                ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                    "client SSL certificate verify error: (%l:%s)",
+                    rc, X509_verify_cert_error_string(rc));
+
+                ngx_ssl_remove_cached_session(sslcf->ssl.ctx,
+                    (SSL_get0_session(c->ssl->connection)));
+
+                ngx_mail_close_connection(c);
+                return;
+            }
+
+            if (sslcf->verify == 1) {
+                X509                     *cert;
+                cert = SSL_get_peer_certificate(c->ssl->connection);
+
+                if (cert == NULL) {
+                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                        "client sent no required SSL certificate");
+
+                    ngx_ssl_remove_cached_session(sslcf->ssl.ctx,
+                        (SSL_get0_session(c->ssl->connection)));
+
+                    ngx_mail_close_connection(c);
+                    return;
+                }
+
+                X509_free(cert);
+            }
+        }
+#endif
+
         if (s->starttls) {
             cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

@@ -276,6 +324,10 @@

     s->protocol = cscf->protocol->type;

+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+        "ngx_mail_init_session protocol: %d ",
+        cscf->protocol->type );
+
     s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_mail_max_module);
     if (s->ctx == NULL) {
         ngx_mail_session_internal_server_error(s);
diff -r a387ce36744a -r d7b8381c200e src/mail/ngx_mail_ssl_module.c
--- a/src/mail/ngx_mail_ssl_module.c    Thu Jan 23 22:09:59 2014 +0900
+++ b/src/mail/ngx_mail_ssl_module.c    Fri Jan 24 16:26:16 2014 +0100
@@ -43,6 +43,13 @@
     { ngx_null_string, 0 }
 };

+static ngx_conf_enum_t  ngx_mail_ssl_verify[] = {
+    { ngx_string("off"), 0 },
+    { ngx_string("on"), 1 },
+    { ngx_string("optional"), 2 },
+    { ngx_string("optional_no_ca"), 3 },
+    { ngx_null_string, 0 }
+};

 static ngx_command_t  ngx_mail_ssl_commands[] = {

@@ -102,6 +109,34 @@
       offsetof(ngx_mail_ssl_conf_t, ciphers),
       NULL },

+    { ngx_string("ssl_verify_client"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_ssl_conf_t, verify),
+      &ngx_mail_ssl_verify },
+
+    { ngx_string("ssl_verify_depth"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+      ngx_conf_set_num_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_ssl_conf_t, verify_depth),
+      NULL },
+
+    { ngx_string("ssl_client_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_ssl_conf_t, client_certificate),
+      NULL },
+
+    { ngx_string("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_ssl_conf_t, trusted_certificate),
+      NULL },
+
     { ngx_string("ssl_prefer_server_ciphers"),
       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -137,6 +172,13 @@
       offsetof(ngx_mail_ssl_conf_t, session_timeout),
       NULL },

+    { ngx_string("ssl_crl"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_ssl_conf_t, crl),
+      NULL },
+
       ngx_null_command
 };

@@ -196,6 +238,8 @@
     scf->enable = NGX_CONF_UNSET;
     scf->starttls = NGX_CONF_UNSET_UINT;
     scf->prefer_server_ciphers = NGX_CONF_UNSET;
+    scf->verify = NGX_CONF_UNSET_UINT;
+    scf->verify_depth = NGX_CONF_UNSET_UINT;
     scf->builtin_session_cache = NGX_CONF_UNSET;
     scf->session_timeout = NGX_CONF_UNSET;
     scf->session_tickets = NGX_CONF_UNSET;
@@ -228,11 +272,20 @@
                          (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1
                           |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));

+    ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
+    ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
+
     ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
     ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key,
"");

     ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");

+    ngx_conf_merge_str_value(conf->client_certificate,
prev->client_certificate,
+                         "");
+    ngx_conf_merge_str_value(conf->trusted_certificate,
+                         prev->trusted_certificate, "");
+    ngx_conf_merge_str_value(conf->crl, prev->crl, "");
+
     ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
                          NGX_DEFAULT_ECDH_CURVE);

@@ -318,6 +371,35 @@
         return NGX_CONF_ERROR;
     }

+    if (conf->verify) {
+
+        if (conf->client_certificate.len == 0 && conf->verify != 3) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "no ssl_client_certificate for
ssl_client_verify");
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_ssl_client_certificate(cf, &conf->ssl,
+                                       &conf->client_certificate,
+                                       conf->verify_depth)
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    if (ngx_ssl_trusted_certificate(cf, &conf->ssl,
+                                    &conf->trusted_certificate,
+                                    conf->verify_depth)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
     if (conf->prefer_server_ciphers) {
         SSL_CTX_set_options(conf->ssl.ctx,
SSL_OP_CIPHER_SERVER_PREFERENCE);
     }
diff -r a387ce36744a -r d7b8381c200e src/mail/ngx_mail_ssl_module.h
--- a/src/mail/ngx_mail_ssl_module.h    Thu Jan 23 22:09:59 2014 +0900
+++ b/src/mail/ngx_mail_ssl_module.h    Fri Jan 24 16:26:16 2014 +0100
@@ -28,6 +28,8 @@
     ngx_uint_t       starttls;
     ngx_uint_t       protocols;

+    ngx_uint_t       verify;
+    ngx_uint_t       verify_depth;
     ssize_t          builtin_session_cache;

     time_t           session_timeout;
@@ -36,6 +38,9 @@
     ngx_str_t        certificate_key;
     ngx_str_t        dhparam;
     ngx_str_t        ecdh_curve;
+    ngx_str_t        client_certificate;
+    ngx_str_t        trusted_certificate;
+    ngx_str_t        crl;

     ngx_str_t        ciphers;

2014-09-17 2:23 GMT+02:00 Kunal Pariani <kpariani at zimbra.com>:

> I guess these diffs can still be applied with limitation of having the
> same ssl setting for all upstreams until the support to return a name by
> the auth script is added later ?
>
> Thanks
> -Kunal
>
> ----- Original Message -----
> From: "Maxim Dounin" <mdounin at mdounin.ru>
> To: "nginx-devel" <nginx-devel at nginx.org>
> Sent: Tuesday, September 16, 2014 5:03:06 AM
> Subject: Re: [PATCH] SSL support for the mail proxy module
>
> Hello!
>
> On Tue, Sep 16, 2014 at 02:30:17AM -0500, Kunal Pariani wrote:
>
> > Updated the diffs after addressing the first 2 issues.
> > Regarding the 3rd comment, you are correct. Only 1 set of ssl
> > settings for all the mail backends with my patch. I guess this
> > will be a limitation with the current mail proxy workflow ? Am
> > not sure of the exact changes that will be required to address
> > this issue completely.
>
> Probably, returning a name by the auth script is a way to go.
>
> --
> Maxim Dounin
> http://nginx.org/
>
> _______________________________________________
> nginx-devel mailing list
> nginx-devel at nginx.org
> http://mailman.nginx.org/mailman/listinfo/nginx-devel
>
> _______________________________________________
> nginx-devel mailing list
> nginx-devel at nginx.org
> http://mailman.nginx.org/mailman/listinfo/nginx-devel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20141021/4ad6f3d8/attachment-0001.html>


More information about the nginx-devel mailing list