[PATCH] Add "pass_only" option to ssl_verify_client to enable app-only validation
mk.fg
nginx-forum at nginx.us
Fri Aug 10 23:54:12 UTC 2012
Maxim Dounin Wrote:
-------------------------------------------------------
> Hello!
>
> On Thu, Jul 19, 2012 at 05:13:01PM -0400, mk.fg
> wrote:
>
> >
> > Proposed patch enables use-case scenario when
> Nginx asks Client for TLS
> > certificate but does not make any attempt to
> validate it, passing it to
> > the application instead. Application itself if
> then free to decide
> > whether provided certificate is valid and is
> able to reject it with the
> > same http status codes as Nginx does.
> >
>
> I don't think that the patch is a right way to go.
> If client
> certificates without CA being known in advance is
> indeed something
> required - it probably needs something like
> optional_no_ca instead
> as done in Apache's mod_ssl, i.e. with usual
> expiration checks and so on,
> but without verification against CAs.
>
New version of the patch, with these requirements taken into account.
Option is now called "optional_no_ca", as suggested, and allows to check
all certificate parameters except for a trust chain.
I've used ssl_verify_error_is_optional macro (listing trust-chain
related errors) directly from apache 2.4.2 codebase.
Note that since ngx_ssl_get_client_verify now has to access
configuration, which is accessible from ngx_http_request_t, it wasn't
enough to pass ngx_connection_t to it, plus it was only used from
ngx_http_ssl_module.c, so I've moved the modified version of it into
ngx_http_ssl_module.c, to avoid having to include http-only stuff into
ngx_event_openssl.c.
If that was a bad idea, and there's a need to keep that function generic
(non-http-only), please suggest whether generic copy should just be kept
in ngx_event_openssl.c, it's signature should be extended to have
http-specific options or maybe there should be conditional includes for
http stuff.
URL for the patch: https://raw.github.com/gist/3319062/
diff --git a/src/event/ngx_event_openssl.c
b/src/event/ngx_event_openssl.c
index 4356a05..f2c3511 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -2309,31 +2309,6 @@ ngx_ssl_get_serial_number(ngx_connection_t *c,
ngx_pool_t *pool, ngx_str_t *s)
}
-ngx_int_t
-ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s)
-{
- X509 *cert;
-
- if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) {
- ngx_str_set(s, "FAILED");
- return NGX_OK;
- }
-
- cert = SSL_get_peer_certificate(c->ssl->connection);
-
- if (cert) {
- ngx_str_set(s, "SUCCESS");
-
- } else {
- ngx_str_set(s, "NONE");
- }
-
- X509_free(cert);
-
- return NGX_OK;
-}
-
-
static void *
ngx_openssl_create_conf(ngx_cycle_t *cycle)
{
diff --git a/src/event/ngx_event_openssl.h
b/src/event/ngx_event_openssl.h
index cd6d885..97da051 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -141,6 +141,14 @@ ngx_int_t
ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
+#define ngx_ssl_verify_error_is_optional(errnum) \
+ ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) \
+ || (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) \
+ || (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) \
+ || (errnum == X509_V_ERR_CERT_UNTRUSTED) \
+ || (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))
+
+
ngx_int_t ngx_ssl_handshake(ngx_connection_t *c);
ssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size);
ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size);
diff --git a/src/http/modules/ngx_http_ssl_module.c
b/src/http/modules/ngx_http_ssl_module.c
index d759489..8f29f3c 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -22,6 +22,8 @@ static ngx_int_t
ngx_http_ssl_static_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_ssl_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t
ngx_http_ssl_variable_get_client_verify(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_ssl_add_variables(ngx_conf_t *cf);
static void *ngx_http_ssl_create_srv_conf(ngx_conf_t *cf);
@@ -48,6 +50,7 @@ static ngx_conf_enum_t ngx_http_ssl_verify[] = {
{ ngx_string("off"), 0 },
{ ngx_string("on"), 1 },
{ ngx_string("optional"), 2 },
+ { ngx_string("optional_no_ca"), 3 },
{ ngx_null_string, 0 }
};
@@ -214,8 +217,9 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = {
{ ngx_string("ssl_client_serial"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_serial_number, NGX_HTTP_VAR_CHANGEABLE, 0
},
- { ngx_string("ssl_client_verify"), NULL, ngx_http_ssl_variable,
- (uintptr_t) ngx_ssl_get_client_verify, NGX_HTTP_VAR_CHANGEABLE, 0
},
+ { ngx_string("ssl_client_verify"), NULL,
+ ngx_http_ssl_variable_get_client_verify,
+ 0, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
};
@@ -306,6 +310,60 @@ ngx_http_ssl_add_variables(ngx_conf_t *cf)
}
+static ngx_int_t
+ngx_http_ssl_variable_get_client_verify(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_str_t s;
+ ngx_connection_t *c;
+ long rc;
+ X509 *cert;
+ ngx_http_ssl_srv_conf_t *sscf;
+
+ c = r->connection;
+
+ if (c->ssl) {
+
+ rc = SSL_get_verify_result(c->ssl->connection);
+
+ if (rc != X509_V_OK) {
+ sscf = ngx_http_get_module_srv_conf(r,
ngx_http_ssl_module);
+
+ if (sscf->verify != 3 ||
ngx_ssl_verify_error_is_optional(rc)) {
+ ngx_str_set(&s, "FAILED");
+ return NGX_OK;
+ }
+ }
+
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+
+ if (cert) {
+ ngx_str_set(&s, "SUCCESS");
+
+ } else {
+ ngx_str_set(&s, "NONE");
+ }
+
+ X509_free(cert);
+
+ v->len = s.len;
+ v->data = s.data;
+
+ if (v->len) {
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+ }
+ }
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
static void *
ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
{
@@ -466,7 +524,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void
*parent, void *child)
if (conf->verify) {
- if (conf->client_certificate.len == 0) {
+ if (conf->verify != 3 && conf->client_certificate.len == 0) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no ssl_client_certificate for
ssl_client_verify");
return NGX_CONF_ERROR;
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index cb970c5..96cec55 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1642,7 +1642,9 @@ ngx_http_process_request(ngx_http_request_t *r)
if (sscf->verify) {
rc = SSL_get_verify_result(c->ssl->connection);
- if (rc != X509_V_OK) {
+ if ((sscf->verify != 3 && rc != X509_V_OK)
+ || !(sscf->verify == 3 &&
ngx_ssl_verify_error_is_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));
Posted at Nginx Forum: http://forum.nginx.org/read.php?2,228761,229586#msg-229586
More information about the nginx
mailing list