diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index be874330..b501e01e 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -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 ---^ ******/ diff --git a/lib/ssl-client.c b/lib/ssl-client.c index e6435728..0ce654bd 100644 --- a/lib/ssl-client.c +++ b/lib/ssl-client.c @@ -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