[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