[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