ssl: add LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION
authorNamowen <namowen@github.invalid.com>
Tue, 10 Jan 2017 01:31:23 +0000 (09:31 +0800)
committerAndy Green <andy@warmcat.com>
Tue, 10 Jan 2017 01:31:23 +0000 (09:31 +0800)
lib/libwebsockets.h
lib/ssl-client.c

index be87433..b501e01 100644 (file)
@@ -1016,6 +1016,36 @@ enum lws_callback_reasons {
         * From this callback, when you have sent everything, you should let
         * lws know by calling lws_client_http_body_pending(wsi, 0)
         */
+       LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION = 58,
+       /**< Similar to LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION
+        * this callback is called during OpenSSL verification of the cert
+        * sent from the server to the client. It is sent to protocol[0]
+        * callback as no protocol has been negotiated on the connection yet.
+        * Notice that the wsi is set because lws_client_connect_via_info was
+        * successful.
+        *
+        * See http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
+        * to understand more detail about the OpenSSL callback that
+        * generates this libwebsockets callback and the meanings of the
+        * arguments passed. In this callback, user is the x509_ctx,
+        * in is the ssl pointer and len is preverify_ok.
+        *
+        * THIS IS NOT RECOMMENDED BUT if a cert validation error shall be
+        * overruled and cert shall be accepted as ok,
+        * X509_STORE_CTX_set_error((X509_STORE_CTX*)user, X509_V_OK); must be
+        * called and return value must be 0 to mean the cert is OK;
+        * returning 1 will fail the cert in any case.
+        *
+        * This also means that if you don't handle this callback then
+        * the default callback action of returning 0 will not accept the
+        * certificate in case of a validation error decided by the SSL lib.
+        *
+        * This is expected and secure behaviour when validating certificates.
+        *
+        * Note: LCCSCF_ALLOW_SELFSIGNED and
+        * LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK still work without this
+        * callback being implemented.
+        */
 
        /****** add new things just above ---^ ******/
 
index e643572..0ce654b 100644 (file)
@@ -29,6 +29,64 @@ lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info
 
 extern int lws_ssl_get_error(struct lws *wsi, int n);
 
+#if defined(LWS_USE_POLARSSL)
+#else
+#if defined(LWS_USE_MBEDTLS)
+#else
+#ifdef USE_WOLFSSL
+#else
+
+static int
+OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+       SSL *ssl;
+       int n;
+       struct lws *wsi;
+
+       /* keep old behaviour accepting self-signed server certs */
+       if (!preverify_ok) {
+               int err = X509_STORE_CTX_get_error(x509_ctx);
+
+               if (err != X509_V_OK) {
+                       ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+                       wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
+
+                       if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
+                                       err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
+                                       wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED) {
+                               lwsl_notice("accepting self-signed certificate\n");
+                               X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
+                               return 1;       // ok
+                       }
+               }
+       }
+
+       ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+       wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
+
+       n = lws_get_context_protocol(wsi->context, 0).callback(wsi, LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION, x509_ctx, ssl, preverify_ok);
+
+       /* keep old behaviour if something wrong with server certs */
+       /* if ssl error is overruled in callback and cert is ok,
+        * X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); must be set and
+        * return value is 0 from callback */
+       if (!preverify_ok) {
+               int err = X509_STORE_CTX_get_error(x509_ctx);
+
+               if (err != X509_V_OK) { /* cert validation error was not handled in callback */
+                       int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+                       const char* msg = X509_verify_cert_error_string(err);
+                       lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;depth=%d)\n", msg, preverify_ok, err, depth);
+                       return preverify_ok;    // not ok
+               }
+       }
+       /* convert callback return code from 0 = OK to verify callback return value 1 = OK */
+       return !n;
+}
+#endif
+#endif
+#endif
+
 int
 lws_ssl_client_bio_create(struct lws *wsi)
 {
@@ -37,7 +95,6 @@ lws_ssl_client_bio_create(struct lws *wsi)
 #else
 #if defined(LWS_USE_MBEDTLS)
 #else
-       struct lws_context *context = wsi->context;
        char hostname[128], *p;
 
        if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
@@ -78,10 +135,11 @@ lws_ssl_client_bio_create(struct lws *wsi)
                X509_VERIFY_PARAM_set_hostflags(param,
                                                X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
                X509_VERIFY_PARAM_set1_host(param, hostname, 0);
-               /* Configure a non-zero callback if desired */
-               SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, 0);
        }
+
 #endif
+       /* OpenSSL_client_verify_callback will be called @ SSL_connect() */
+       SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
 
 #ifndef USE_WOLFSSL
        SSL_set_mode(wsi->ssl,  SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
@@ -137,7 +195,7 @@ lws_ssl_client_bio_create(struct lws *wsi)
 #endif
 
        SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index,
-                       context);
+                       wsi);
 
        return 0;
 #endif
@@ -325,6 +383,7 @@ lws_ssl_client_connect2(struct lws *wsi)
                        return -1;
                }
        }
+
 #endif /* USE_WOLFSSL */
 #endif
 #endif