[PATCH] SSL: fix order of checks during SSL certificate verification

Piotr Sikora piotrsikora at google.com
Tue Aug 2 22:24:55 UTC 2016


# HG changeset patch
# User Piotr Sikora <piotrsikora at google.com>
# Date 1470107238 25200
#      Mon Aug 01 20:07:18 2016 -0700
# Node ID cd72e0a1164abd70aafdb391b3470869508532e5
# Parent  d43ee392e825186545d81e683b88cc58ef8479bc
SSL: fix order of checks during SSL certificate verification.

SSL_get_verify_result() should be called only if certificate was presented
by the peer, otherwise returned value is the default one, which happens to
be X509_V_OK, but it doesn't indicate success and it's considered a bug:
https://www.openssl.org/docs/manmaster/ssl/SSL_get_verify_result.html

While there, move common verification logic to ngx_ssl_verify_client() and
ngx_ssl_check_host() in order to make the callers crypto-library-agnostic.

Signed-off-by: Piotr Sikora <piotrsikora at google.com>

diff -r d43ee392e825 -r cd72e0a1164a src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -3054,12 +3054,70 @@ ngx_ssl_cleanup_ctx(void *data)
 
 
 ngx_int_t
+ngx_ssl_verify_client(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_uint_t verify)
+{
+    long   rc;
+    X509  *cert;
+
+    cert = SSL_get_peer_certificate(c->ssl->connection);
+    if (cert == NULL) {
+
+        if (verify != 1) {
+            return NGX_OK;
+        }
+
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "client sent no required SSL certificate");
+
+        ngx_ssl_remove_cached_session(ssl->ctx,
+                                      SSL_get0_session(c->ssl->connection));
+
+        return NGX_DECLINED;
+    }
+
+    X509_free(cert);
+
+    rc = SSL_get_verify_result(c->ssl->connection);
+
+    if (rc != X509_V_OK
+        && (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(ssl->ctx,
+                                      SSL_get0_session(c->ssl->connection));
+
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name)
 {
+    long    rc;
     X509   *cert;
 
     cert = SSL_get_peer_certificate(c->ssl->connection);
     if (cert == NULL) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "upstream sent no required SSL certificate");
+
+        return NGX_ERROR;
+    }
+
+    rc = SSL_get_verify_result(c->ssl->connection);
+
+    if (rc != X509_V_OK) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "upstream SSL certificate verify error: (%l:%s)",
+                      rc, X509_verify_cert_error_string(rc));
+
+        X509_free(cert);
         return NGX_ERROR;
     }
 
@@ -3170,6 +3228,10 @@ ngx_ssl_check_host(ngx_connection_t *c, 
 
 failed:
 
+    ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                  "upstream SSL certificate does not match \"%V\"",
+                  name);
+
     X509_free(cert);
     return NGX_ERROR;
 
@@ -3566,22 +3628,20 @@ ngx_ssl_get_client_verify(ngx_connection
 {
     X509  *cert;
 
+    cert = SSL_get_peer_certificate(c->ssl->connection);
+    if (cert == NULL) {
+        ngx_str_set(s, "NONE");
+        return NGX_OK;
+    }
+
+    X509_free(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);
-
+    ngx_str_set(s, "SUCCESS");
     return NGX_OK;
 }
 
diff -r d43ee392e825 -r cd72e0a1164a src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -184,6 +184,8 @@ ngx_int_t ngx_ssl_set_session(ngx_connec
      || n == X509_V_ERR_CERT_UNTRUSTED                                        \
      || n == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)
 
+ngx_int_t ngx_ssl_verify_client(ngx_connection_t *c, ngx_ssl_t *ssl,
+    ngx_uint_t verify);
 ngx_int_t ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name);
 
 
diff -r d43ee392e825 -r cd72e0a1164a src/http/ngx_http_request.c
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1845,8 +1845,7 @@ ngx_http_process_request(ngx_http_reques
 #if (NGX_HTTP_SSL)
 
     if (r->http_connection->ssl) {
-        long                      rc;
-        X509                     *cert;
+        ngx_int_t                 rc;
         ngx_http_ssl_srv_conf_t  *sscf;
 
         if (c->ssl == NULL) {
@@ -1859,38 +1858,13 @@ ngx_http_process_request(ngx_http_reques
         sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
 
         if (sscf->verify) {
-            rc = SSL_get_verify_result(c->ssl->connection);
-
-            if (rc != X509_V_OK
-                && (sscf->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(sscf->ssl.ctx,
-                                       (SSL_get0_session(c->ssl->connection)));
-
-                ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);
+            rc = ngx_ssl_verify_client(c, &sscf->ssl, sscf->verify);
+            if (rc != NGX_OK) {
+                ngx_http_finalize_request(r, (rc == NGX_ERROR)
+                                             ? NGX_HTTPS_CERT_ERROR
+                                             : NGX_HTTPS_NO_CERT);
                 return;
             }
-
-            if (sscf->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(sscf->ssl.ctx,
-                                       (SSL_get0_session(c->ssl->connection)));
-
-                    ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);
-                    return;
-                }
-
-                X509_free(cert);
-            }
         }
     }
 
diff -r d43ee392e825 -r cd72e0a1164a src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -1561,7 +1561,6 @@ ngx_http_upstream_ssl_init_connection(ng
 static void
 ngx_http_upstream_ssl_handshake(ngx_connection_t *c)
 {
-    long                  rc;
     ngx_http_request_t   *r;
     ngx_http_upstream_t  *u;
 
@@ -1573,19 +1572,7 @@ ngx_http_upstream_ssl_handshake(ngx_conn
     if (c->ssl->handshaked) {
 
         if (u->conf->ssl_verify) {
-            rc = SSL_get_verify_result(c->ssl->connection);
-
-            if (rc != X509_V_OK) {
-                ngx_log_error(NGX_LOG_ERR, c->log, 0,
-                              "upstream SSL certificate verify error: (%l:%s)",
-                              rc, X509_verify_cert_error_string(rc));
-                goto failed;
-            }
-
             if (ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) {
-                ngx_log_error(NGX_LOG_ERR, c->log, 0,
-                              "upstream SSL certificate does not match \"%V\"",
-                              &u->ssl_name);
                 goto failed;
             }
         }
diff -r d43ee392e825 -r cd72e0a1164a src/mail/ngx_mail_handler.c
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -16,8 +16,6 @@ static void ngx_mail_init_session(ngx_co
 #if (NGX_MAIL_SSL)
 static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c);
 static void ngx_mail_ssl_handshake_handler(ngx_connection_t *c);
-static ngx_int_t ngx_mail_verify_cert(ngx_mail_session_t *s,
-    ngx_connection_t *c);
 #endif
 
 
@@ -247,15 +245,31 @@ ngx_mail_ssl_init_connection(ngx_ssl_t *
 static void
 ngx_mail_ssl_handshake_handler(ngx_connection_t *c)
 {
+    ngx_int_t                  rc;
     ngx_mail_session_t        *s;
+    ngx_mail_ssl_conf_t       *sslcf;
     ngx_mail_core_srv_conf_t  *cscf;
 
     if (c->ssl->handshaked) {
 
         s = c->data;
 
-        if (ngx_mail_verify_cert(s, c) != NGX_OK) {
-            return;
+        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+        if (sslcf->verify) {
+            rc = ngx_ssl_verify_client(c, &sslcf->ssl, sslcf->verify);
+            if (rc != NGX_OK) {
+                cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+                s->out = (rc == NGX_ERROR) ? cscf->protocol->cert_error
+                                           : cscf->protocol->no_cert;
+                s->quit = 1;
+
+                c->write->handler = ngx_mail_send;
+
+                ngx_mail_send(s->connection->write);
+                return;
+            }
         }
 
         if (s->starttls) {
@@ -278,71 +292,6 @@ ngx_mail_ssl_handshake_handler(ngx_conne
     ngx_mail_close_connection(c);
 }
 
-
-static ngx_int_t
-ngx_mail_verify_cert(ngx_mail_session_t *s, ngx_connection_t *c)
-{
-    long                       rc;
-    X509                      *cert;
-    ngx_mail_ssl_conf_t       *sslcf;
-    ngx_mail_core_srv_conf_t  *cscf;
-
-    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_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)));
-
-        cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
-
-        s->out = cscf->protocol->cert_error;
-        s->quit = 1;
-
-        c->write->handler = ngx_mail_send;
-
-        ngx_mail_send(s->connection->write);
-        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)));
-
-            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
-
-            s->out = cscf->protocol->no_cert;
-            s->quit = 1;
-
-            c->write->handler = ngx_mail_send;
-
-            ngx_mail_send(s->connection->write);
-            return NGX_ERROR;
-        }
-
-        X509_free(cert);
-    }
-
-    return NGX_OK;
-}
-
 #endif
 
 
diff -r d43ee392e825 -r cd72e0a1164a src/stream/ngx_stream_proxy_module.c
--- a/src/stream/ngx_stream_proxy_module.c
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -986,7 +986,6 @@ ngx_stream_proxy_ssl_init_connection(ngx
 static void
 ngx_stream_proxy_ssl_handshake(ngx_connection_t *pc)
 {
-    long                          rc;
     ngx_stream_session_t         *s;
     ngx_stream_upstream_t        *u;
     ngx_stream_proxy_srv_conf_t  *pscf;
@@ -998,21 +997,7 @@ ngx_stream_proxy_ssl_handshake(ngx_conne
     if (pc->ssl->handshaked) {
 
         if (pscf->ssl_verify) {
-            rc = SSL_get_verify_result(pc->ssl->connection);
-
-            if (rc != X509_V_OK) {
-                ngx_log_error(NGX_LOG_ERR, pc->log, 0,
-                              "upstream SSL certificate verify error: (%l:%s)",
-                              rc, X509_verify_cert_error_string(rc));
-                goto failed;
-            }
-
-            u = s->upstream;
-
-            if (ngx_ssl_check_host(pc, &u->ssl_name) != NGX_OK) {
-                ngx_log_error(NGX_LOG_ERR, pc->log, 0,
-                              "upstream SSL certificate does not match \"%V\"",
-                              &u->ssl_name);
+            if (ngx_ssl_check_host(pc, &s->upstream->ssl_name) != NGX_OK) {
                 goto failed;
             }
         }



More information about the nginx-devel mailing list