[PATCH] Stream: client SSL certificate support

Lukas Prettenthaler Lukas.Prettenthaler at bwinparty.com
Fri Nov 6 20:34:11 UTC 2015


# HG changeset patch
# User Lukas Prettenthaler <lukas.prettenthaler at bwinparty.com>
# Date 1446841055 -3600
#      Fri Nov 06 21:17:35 2015 +0100
# Node ID e34abe30ed6d749deb12b768c15059165b56f9c5
# Parent  909b5b191f25d0f9e03667a10d23f6ef27d014a3
Stream: client SSL certificate support

The "ssl_verify_client", "ssl_verify_depth", "ssl_client_certificate",
"ssl_trusted_certificate", and "ssl_crl" directives introduced to control
SSL client certificate verification in the stream module.

If there is no required certificate provided during an SSL handshake
or certificate verification fails then the connection is simply closed.

diff -r 909b5b191f25 -r e34abe30ed6d src/stream/ngx_stream_handler.c
--- a/src/stream/ngx_stream_handler.c	Thu Nov 05 15:01:09 2015 +0300
+++ b/src/stream/ngx_stream_handler.c	Fri Nov 06 21:17:35 2015 +0100
@@ -17,6 +17,8 @@
 #if (NGX_STREAM_SSL)
 static void ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c);
 static void ngx_stream_ssl_handshake_handler(ngx_connection_t *c);
+static ngx_int_t ngx_stream_verify_cert(ngx_stream_session_t *s,
+    ngx_connection_t *c);
 #endif
 
 
@@ -265,11 +267,20 @@
 static void
 ngx_stream_ssl_handshake_handler(ngx_connection_t *c)
 {
+    ngx_stream_session_t        *s;
+
     if (!c->ssl->handshaked) {
         ngx_stream_close_connection(c);
         return;
     }
 
+    s = c->data;
+
+    if (ngx_stream_verify_cert(s, c) != NGX_OK) {
+        ngx_stream_close_connection(c);
+        return;
+    }
+
     if (c->read->timer_set) {
         ngx_del_timer(c->read);
     }
@@ -277,6 +288,53 @@
     ngx_stream_init_session(c);
 }
 
+static ngx_int_t
+ngx_stream_verify_cert(ngx_stream_session_t *s, ngx_connection_t *c)
+{
+    long                       rc;
+    X509                      *cert;
+    ngx_stream_ssl_conf_t       *sslcf;
+
+    sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
+
+    if (!sslcf->verify) {
+        return NGX_OK;
+    }
+
+    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)));
+
+        return NGX_ERROR;
+    }
+
+    if (sslcf->verify == 1) {
+        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)));
+
+            return NGX_ERROR;
+        }
+
+        X509_free(cert);
+    }
+
+    return NGX_OK;
+}
+
 #endif
 
 
diff -r 909b5b191f25 -r e34abe30ed6d src/stream/ngx_stream_ssl_module.c
--- a/src/stream/ngx_stream_ssl_module.c	Thu Nov 05 15:01:09 2015 +0300
+++ b/src/stream/ngx_stream_ssl_module.c	Fri Nov 06 21:17:35 2015 +0100
@@ -33,6 +33,13 @@
     { ngx_null_string, 0 }
 };
 
+static ngx_conf_enum_t  ngx_stream_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_stream_ssl_commands[] = {
 
@@ -127,6 +134,41 @@
       offsetof(ngx_stream_ssl_conf_t, session_timeout),
       NULL },
 
+    { ngx_string("ssl_verify_client"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, verify),
+      &ngx_stream_ssl_verify },
+
+    { ngx_string("ssl_verify_depth"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_num_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, verify_depth),
+      NULL },
+
+    { ngx_string("ssl_client_certificate"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, client_certificate),
+      NULL },
+
+    { ngx_string("ssl_trusted_certificate"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, trusted_certificate),
+      NULL },
+
+    { ngx_string("ssl_crl"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, crl),
+      NULL },
+
       ngx_null_command
 };
 
@@ -179,6 +221,9 @@
      *     scf->certificate_key = { 0, NULL };
      *     scf->dhparam = { 0, NULL };
      *     scf->ecdh_curve = { 0, NULL };
+     *     scf->client_certificate = { 0, NULL };
+     *     scf->trusted_certificate = { 0, NULL };
+     *     scf->crl = { 0, NULL };
      *     scf->ciphers = { 0, NULL };
      *     scf->shm_zone = NULL;
      */
@@ -186,6 +231,8 @@
     scf->handshake_timeout = NGX_CONF_UNSET_MSEC;
     scf->passwords = NGX_CONF_UNSET_PTR;
     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;
@@ -216,6 +263,9 @@
                          (NGX_CONF_BITMASK_SET|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, "");
 
@@ -226,6 +276,12 @@
     ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
                          NGX_DEFAULT_ECDH_CURVE);
 
+    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->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
 
@@ -262,6 +318,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 (SSL_CTX_set_cipher_list(conf->ssl.ctx,
                                 (const char *) conf->ciphers.data)
         == 0)
diff -r 909b5b191f25 -r e34abe30ed6d src/stream/ngx_stream_ssl_module.h
--- a/src/stream/ngx_stream_ssl_module.h	Thu Nov 05 15:01:09 2015 +0300
+++ b/src/stream/ngx_stream_ssl_module.h	Fri Nov 06 21:17:35 2015 +0100
@@ -23,6 +23,9 @@
 
     ngx_uint_t       protocols;
 
+    ngx_uint_t       verify;
+    ngx_uint_t       verify_depth;
+
     ssize_t          builtin_session_cache;
 
     time_t           session_timeout;
@@ -31,6 +34,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;
 



More information about the nginx-devel mailing list