[PATCH] mail_{ssl, auth_http}_module: add support for SSL client certificates

Sven Peter sven at ha.cki.ng
Mon Jan 13 15:28:25 UTC 2014


Hi again,


Here's an updated version of the patch. Sorry for not using hg email,
but I couldn't figure out how to convince it to reply to an existing thread.
This will probably screw something up, so I attached the mbox file to be sure.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: mail-ssl-patch.mbox
Type: application/octet-stream
Size: 11830 bytes
Desc: not available
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20140113/bf7e5dcc/attachment-0001.obj>
-------------- next part --------------


Sven


# HG changeset patch
# User Sven Peter <sven at ha.cki.ng>
# Date 1389626052 -3600
#      Mon Jan 13 16:14:12 2014 +0100
# Node ID a444733105e8eb96212f142533e714532a23cddf
# Parent  4aa64f6950313311e0d322a2af1788edeb7f036c
mail_{ssl,auth_http}_module: add support for SSL client certificates

This patch adds support for SSL client certificates to the mail proxy
capabilities of nginx both for STARTTLS and SSL mode.
Just like the HTTP SSL module a root CA is defined in the mail section
of the configuration file. Verification can be optional or mandatory.
Additionally, the result of the verification is exposed to the
auth http backend via the SSL-Verify, SSL-Subject-DN, SSL-Issuer-DN
and SSL-Serial HTTP headers.

diff -r 4aa64f695031 -r a444733105e8 src/mail/ngx_mail_auth_http_module.c
--- a/src/mail/ngx_mail_auth_http_module.c	Sat Jan 04 03:32:22 2014 +0400
+++ b/src/mail/ngx_mail_auth_http_module.c	Mon Jan 13 16:14:12 2014 +0100
@@ -1145,6 +1145,16 @@
     ngx_str_t                  login, passwd;
     ngx_mail_core_srv_conf_t  *cscf;
 
+#if (NGX_MAIL_SSL)
+    ngx_str_t ssl_client_verify;
+    ngx_str_t ssl_client_raw_s_dn;
+    ngx_str_t ssl_client_raw_i_dn;
+    ngx_str_t ssl_client_raw_serial;
+    ngx_str_t ssl_client_s_dn;
+    ngx_str_t ssl_client_i_dn;
+    ngx_str_t ssl_client_serial;
+#endif
+
     if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {
         return NULL;
     }
@@ -1153,6 +1163,51 @@
         return NULL;
     }
 
+#if (NGX_MAIL_SSL)
+    if (s->connection->ssl) {
+        /* ssl_client_verify comes from nginx itself - no need to escape */
+        if (ngx_ssl_get_client_verify(s->connection, pool,
+                                      &ssl_client_verify) != NGX_OK) {
+            return NULL;
+        }
+
+        if (ngx_ssl_get_subject_dn(s->connection, pool,
+                                   &ssl_client_raw_s_dn) != NGX_OK) {
+            return NULL;
+        }
+
+        if (ngx_ssl_get_issuer_dn(s->connection, pool,
+                                  &ssl_client_raw_i_dn) != NGX_OK) {
+            return NULL;
+        }
+
+        if (ngx_ssl_get_serial_number(s->connection, pool,
+                                      &ssl_client_raw_serial) != NGX_OK) {
+            return NULL;
+        }
+
+        if (ngx_mail_auth_http_escape(pool, &ssl_client_raw_s_dn, 
+                                      &ssl_client_s_dn) != NGX_OK) {
+            return NULL;
+        }
+
+        if (ngx_mail_auth_http_escape(pool, &ssl_client_raw_i_dn,
+                                      &ssl_client_i_dn) != NGX_OK) {
+            return NULL;
+        }
+
+        if (ngx_mail_auth_http_escape(pool, &ssl_client_raw_serial,
+                                      &ssl_client_serial) != NGX_OK) {
+            return NULL;
+        }
+    } else {
+        ngx_str_set(&ssl_client_verify, "NONE");
+        ssl_client_i_dn.len = 0;
+        ssl_client_s_dn.len = 0;
+        ssl_client_serial.len = 0;
+    }
+#endif
+
     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
@@ -1173,6 +1228,16 @@
           + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len
           + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len
           + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len
+#if (NGX_MAIL_SSL)
+          + sizeof("SSL-Verify: ") - 1 + ssl_client_verify.len
+          + sizeof(CRLF) - 1
+          + sizeof("SSL-Subject-DN: ") - 1 + ssl_client_s_dn.len
+          + sizeof(CRLF) - 1
+          + sizeof("SSL-Issuer-DN: ") - 1 + ssl_client_i_dn.len
+          + sizeof(CRLF) - 1
+          + sizeof("SSL-Serial: ") - 1 + ssl_client_serial.len
+          + sizeof(CRLF) - 1
+#endif
           + ahcf->header.len
           + sizeof(CRLF) - 1;
 
@@ -1255,6 +1320,34 @@
 
     }
 
+#if (NGX_MAIL_SSL)
+    if (ssl_client_verify.len) {
+        b->last = ngx_cpymem(b->last, "SSL-Verify: ",
+                             sizeof("SSL-Verify: ") - 1);
+        b->last = ngx_copy(b->last, ssl_client_verify.data,
+                           ssl_client_verify.len);
+        *b->last++ = CR; *b->last++ = LF;
+
+        b->last = ngx_cpymem(b->last, "SSL-Subject-DN: ",
+                             sizeof("SSL-Subject-DN: ") - 1);
+        b->last = ngx_copy(b->last, ssl_client_s_dn.data,
+                           ssl_client_s_dn.len);
+        *b->last++ = CR; *b->last++ = LF;
+
+        b->last = ngx_cpymem(b->last, "SSL-Issuer-DN: ",
+                             sizeof("SSL-Issuer-DN: ") - 1);
+        b->last = ngx_copy(b->last, ssl_client_i_dn.data,
+                           ssl_client_i_dn.len);
+        *b->last++ = CR; *b->last++ = LF;
+
+        b->last = ngx_cpymem(b->last, "SSL-Serial: ",
+                             sizeof("SSL-Serial: ") - 1);
+        b->last = ngx_copy(b->last, ssl_client_serial.data,
+                           ssl_client_serial.len);
+        *b->last++ = CR; *b->last++ = LF;
+    }
+#endif
+
     if (ahcf->header.len) {
         b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len);
     }
diff -r 4aa64f695031 -r a444733105e8 src/mail/ngx_mail_handler.c
--- a/src/mail/ngx_mail_handler.c	Sat Jan 04 03:32:22 2014 +0400
+++ b/src/mail/ngx_mail_handler.c	Mon Jan 13 16:14:12 2014 +0100
@@ -236,11 +236,40 @@
 {
     ngx_mail_session_t        *s;
     ngx_mail_core_srv_conf_t  *cscf;
+    ngx_mail_ssl_conf_t *sslcf;
 
     if (c->ssl->handshaked) {
 
         s = c->data;
 
+        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+        if (sslcf->verify != NGX_MAIL_SSL_VERIFY_OFF) {
+            long rc;
+            rc = SSL_get_verify_result(c->ssl->connection);
+
+            if (rc != X509_V_OK &&
+                (sslcf->verify != NGX_MAIL_SSL_VERIFY_OPTIONAL_NO_CA && 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_mail_close_connection(c);
+                return;
+            }
+            
+            if (sslcf->verify == NGX_MAIL_SSL_VERIFY_ON) {
+                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_mail_close_connection(c); 
+                    return;          
+                }
+                X509_free(cert);
+            }
+        }
+
         if (s->starttls) {
             cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
 
diff -r 4aa64f695031 -r a444733105e8 src/mail/ngx_mail_ssl_module.c
--- a/src/mail/ngx_mail_ssl_module.c	Sat Jan 04 03:32:22 2014 +0400
+++ b/src/mail/ngx_mail_ssl_module.c	Mon Jan 13 16:14:12 2014 +0100
@@ -43,6 +43,13 @@
     { ngx_null_string, 0 }
 };
 
+static ngx_conf_enum_t ngx_mail_ssl_verify[] = {
+    { ngx_string("off"), NGX_MAIL_SSL_VERIFY_OFF },
+    { ngx_string("on"), NGX_MAIL_SSL_VERIFY_ON },
+    { ngx_string("optional"), NGX_MAIL_SSL_VERIFY_OPTIONAL },
+    { ngx_string("optional_no_ca"), NGX_MAIL_SSL_VERIFY_OPTIONAL_NO_CA },
+    { ngx_null_string, 0 }
+};
 
 static ngx_command_t  ngx_mail_ssl_commands[] = {
 
@@ -130,7 +137,40 @@
       offsetof(ngx_mail_ssl_conf_t, session_timeout),
       NULL },
 
-      ngx_null_command
+    {
+      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_null_command
 };
 
 
@@ -184,6 +224,8 @@
      *     scf->ecdh_curve = { 0, NULL };
      *     scf->ciphers = { 0, NULL };
      *     scf->shm_zone = NULL;
+     *     scf->client_certificate = { 0, NULL };
+     *     scf->trusted_certificate = { 0, NULL };
      */
 
     scf->enable = NGX_CONF_UNSET;
@@ -192,6 +234,8 @@
     scf->builtin_session_cache = NGX_CONF_UNSET;
     scf->session_timeout = NGX_CONF_UNSET;
     scf->session_ticket_keys = NGX_CONF_UNSET_PTR;
+    scf->verify = NGX_CONF_UNSET_UINT;
+    scf->verify_depth = NGX_CONF_UNSET_UINT;
 
     return scf;
 }
@@ -230,6 +274,11 @@
 
     ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
+    ngx_conf_merge_uint_value(conf->verify, prev->verify, NGX_MAIL_SSL_VERIFY_OFF);
+    ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
+
+    ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate, "");
+    ngx_conf_merge_str_value(conf->trusted_certificate, prev->trusted_certificate, "");
 
     conf->ssl.log = cf->log;
 
@@ -310,6 +359,21 @@
         return NGX_CONF_ERROR;
     }
 
+    if (conf->verify) {
+      if (conf->client_certificate.len == 0 && conf->verify != NGX_MAIL_SSL_VERIFY_OPTIONAL_NO_CA) {
+        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 (conf->prefer_server_ciphers) {
         SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
     }
diff -r 4aa64f695031 -r a444733105e8 src/mail/ngx_mail_ssl_module.h
--- a/src/mail/ngx_mail_ssl_module.h	Sat Jan 04 03:32:22 2014 +0400
+++ b/src/mail/ngx_mail_ssl_module.h	Mon Jan 13 16:14:12 2014 +0100
@@ -37,8 +37,14 @@
     ngx_str_t        dhparam;
     ngx_str_t        ecdh_curve;
 
+    ngx_str_t        client_certificate;
+    ngx_str_t        trusted_certificate;
+
     ngx_str_t        ciphers;
 
+    ngx_uint_t       verify;
+    ngx_uint_t       verify_depth;
+
     ngx_shm_zone_t  *shm_zone;
 
     ngx_array_t     *session_ticket_keys;
@@ -47,6 +53,13 @@
     ngx_uint_t       line;
 } ngx_mail_ssl_conf_t;
 
+enum ngx_mail_ssl_verify_enum {
+  NGX_MAIL_SSL_VERIFY_OFF = 0,
+  NGX_MAIL_SSL_VERIFY_ON,
+  NGX_MAIL_SSL_VERIFY_OPTIONAL,
+  NGX_MAIL_SSL_VERIFY_OPTIONAL_NO_CA,
+};
+
 
 extern ngx_module_t  ngx_mail_ssl_module;
 



More information about the nginx-devel mailing list