extern int lws_ssl_get_error(struct lws *wsi, int n);
-int
-lws_ssl_client_bio_create(struct lws *wsi)
+#if defined(USE_WOLFSSL)
+#else
+
+static int
+OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
-#if defined(LWS_USE_POLARSSL)
+#if defined(LWS_WITH_ESP32)
+// long gvr = ssl_pm_get_verify_result(
+ lwsl_notice("%s\n", __func__);
+
return 0;
#else
-#if defined(LWS_USE_MBEDTLS)
-#else
- struct lws_context *context = wsi->context;
- const char *hostname = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST);
- X509_VERIFY_PARAM *param;
+ 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 (verify_callback)\n");
+ X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
+ return 1; // ok
+ } else if ((err == X509_V_ERR_CERT_NOT_YET_VALID ||
+ err == X509_V_ERR_CERT_HAS_EXPIRED) &&
+ wsi->use_ssl & LCCSCF_ALLOW_EXPIRED) {
+ if (err == X509_V_ERR_CERT_NOT_YET_VALID)
+ lwsl_notice("accepting not yet valid certificate (verify_callback)\n");
+ else if (err == X509_V_ERR_CERT_HAS_EXPIRED)
+ lwsl_notice("accepting expired certificate (verify_callback)\n");
+ X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
+ return 1; // ok
+ }
+ }
+ }
- (void)hostname;
- (void)param;
+ 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
+
+int
+lws_ssl_client_bio_create(struct lws *wsi)
+{
+ char hostname[128], *p;
+
+ if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
+ _WSI_TOKEN_CLIENT_HOST) <= 0) {
+ lwsl_err("%s: Unable to get hostname\n", __func__);
+
+ return -1;
+ }
+
+ /*
+ * remove any :port part on the hostname... necessary for network
+ * connection but typical certificates do not contain it
+ */
+ p = hostname;
+ while (*p) {
+ if (*p == ':') {
+ *p = '\0';
+ break;
+ }
+ p++;
+ }
wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx);
+ if (!wsi->ssl) {
+ lwsl_err("SSL_new failed: %s\n",
+ ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
+ lws_ssl_elaborate_error();
+ return -1;
+ }
+
+#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
+ if (wsi->vhost->ssl_info_event_mask)
+ SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
+#endif
#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host
- param = SSL_get0_param(wsi->ssl);
- /* Enable automatic hostname checks */
- 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);
+ X509_VERIFY_PARAM *param;
+ (void)param;
+
+ if (!(wsi->use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
+ param = SSL_get0_param(wsi->ssl);
+ /* Enable automatic hostname checks */
+ X509_VERIFY_PARAM_set_hostflags(param,
+ X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+ X509_VERIFY_PARAM_set1_host(param, hostname, 0);
+ }
+
#endif
-#ifndef USE_WOLFSSL
+#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_ESP32)
+#ifndef USE_OLD_CYASSL
+ /* OpenSSL_client_verify_callback will be called @ SSL_connect() */
+ SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
+#endif
+#endif
+
+#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_ESP32)
SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
#endif
/*
#endif
#endif
#else
+#if defined(LWS_WITH_ESP32)
+// esp-idf openssl shim does not seem ready for this
+// SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
+ SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback);
+
+#else
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
SSL_set_tlsext_host_name(wsi->ssl, hostname);
#endif
#endif
+#endif
#ifdef USE_WOLFSSL
/*
#endif
#endif /* USE_WOLFSSL */
- wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE);
+#if !defined(LWS_WITH_ESP32)
+ wsi->client_bio = BIO_new_socket(wsi->desc.sockfd, BIO_NOCLOSE);
SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
+#else
+ SSL_set_fd(wsi->ssl, wsi->desc.sockfd);
+#endif
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
wolfSSL_set_using_nonblock(wsi->ssl, 1);
#endif
#else
+#if !defined(LWS_WITH_ESP32)
BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */
#endif
+#endif
+#if !defined(LWS_WITH_ESP32)
SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index,
- context);
+ wsi);
+#endif
return 0;
-#endif
-#endif
}
+#if defined(LWS_WITH_ESP32)
+int ERR_get_error(void)
+{
+ return 0;
+}
+#endif
+
int
lws_ssl_client_connect1(struct lws *wsi)
{
int n = 0;
lws_latency_pre(context, wsi);
-#if defined(LWS_USE_POLARSSL)
-#else
-#if defined(LWS_USE_MBEDTLS)
-#else
+
n = SSL_connect(wsi->ssl);
-#endif
-#endif
+
lws_latency(context, wsi,
"SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0);
return 0; /* no error */
}
+
+ {
+ struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+ char *p = (char *)&pt->serv_buf[0];
+ char *sb = p;
+
+ lwsl_err("ssl hs1 error, X509_V_ERR = %d: %s\n",
+ n, ERR_error_string(n, sb));
+ lws_ssl_elaborate_error();
+ }
+
n = -1;
}
* retry if new data comes until we
* run into the connection timeout or win
*/
-#if defined(LWS_USE_POLARSSL)
-#else
-#if defined(LWS_USE_MBEDTLS)
-#else
- n = ERR_get_error();
- if (n != SSL_ERROR_NONE) {
+ unsigned long error = ERR_get_error();
+
+ if (error != SSL_ERROR_NONE) {
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
lwsl_err("SSL connect error %lu: %s\n",
- n, ERR_error_string(n, sb));
+ error, ERR_error_string(error, sb));
return -1;
}
-#endif
-#endif
}
return 1;
lws_ssl_client_connect2(struct lws *wsi)
{
struct lws_context *context = wsi->context;
-#if defined(LWS_USE_POLARSSL)
-#else
-#if defined(LWS_USE_MBEDTLS)
-#else
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
-#endif
-#endif
int n = 0;
if (wsi->mode == LWSCM_WSCL_WAITING_SSL) {
lws_latency_pre(context, wsi);
-#if defined(LWS_USE_POLARSSL)
-#else
-#if defined(LWS_USE_MBEDTLS)
-#else
n = SSL_connect(wsi->ssl);
-#endif
-#endif
+ lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
+
lws_latency(context, wsi,
"SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0);
n = lws_ssl_get_error(wsi, n);
if (n == SSL_ERROR_WANT_READ) {
+ lwsl_info("SSL_connect WANT_READ... retrying\n");
+
wsi->mode = LWSCM_WSCL_WAITING_SSL;
return 0; /* no error */
return 0; /* no error */
}
+
n = -1;
}
* retry if new data comes until we
* run into the connection timeout or win
*/
-#if defined(LWS_USE_POLARSSL)
-#else
-#if defined(LWS_USE_MBEDTLS)
-#else
- n = ERR_get_error();
- if (n != SSL_ERROR_NONE) {
+ unsigned long error = ERR_get_error();
+ if (error != SSL_ERROR_NONE) {
lwsl_err("SSL connect error %lu: %s\n",
- n, ERR_error_string(n, sb));
+ error, ERR_error_string(error, sb));
return -1;
}
-#endif
-#endif
}
}
-#if defined(LWS_USE_POLARSSL)
-#else
-#if defined(LWS_USE_MBEDTLS)
-#else
+#if defined(LWS_WITH_ESP32)
+ {
+ X509 *peer = SSL_get_peer_certificate(wsi->ssl);
+
+ if (!peer) {
+ lwsl_notice("peer did not provide cert\n");
+
+ return -1;
+ }
+ lwsl_notice("peer provided cert\n");
+ }
+#endif
+
#ifndef USE_WOLFSSL
/*
* See comment above about wolfSSL certificate
lws_latency_pre(context, wsi);
n = SSL_get_verify_result(wsi->ssl);
lws_latency(context, wsi,
- "SSL_get_verify_result LWS_CONNMODE..HANDSHAKE",
- n, n > 0);
+ "SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0);
+
+ lwsl_debug("get_verify says %d\n", n);
if (n != X509_V_OK) {
if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
- n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && wsi->use_ssl == 2) {
+ n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
+ (wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED)) {
lwsl_notice("accepting self-signed certificate\n");
+ } else if ((n == X509_V_ERR_CERT_NOT_YET_VALID ||
+ n == X509_V_ERR_CERT_HAS_EXPIRED) &&
+ (wsi->use_ssl & LCCSCF_ALLOW_EXPIRED)) {
+ lwsl_notice("accepting expired certificate\n");
+ } else if (n == X509_V_ERR_CERT_NOT_YET_VALID) {
+ lwsl_notice("Cert is from the future... "
+ "probably our clock... accepting...\n");
} else {
lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n",
n, ERR_error_string(n, sb));
return -1;
}
}
+
#endif /* USE_WOLFSSL */
-#endif
-#endif
return 1;
}
int lws_context_init_client_ssl(struct lws_context_creation_info *info,
struct lws_vhost *vhost)
{
-#if defined(LWS_USE_POLARSSL)
- return 0;
-#else
-#if defined(LWS_USE_MBEDTLS)
-#else
- SSL_METHOD *method;
+ SSL_METHOD *method = NULL;
struct lws wsi;
- int error;
+ unsigned long error;
+#if !defined(LWS_WITH_ESP32)
+ const char *cipher_list = info->ssl_cipher_list;
+ const char *ca_filepath = info->ssl_ca_filepath;
+ const char *private_key_filepath = info->ssl_private_key_filepath;
+ const char *cert_filepath = info->ssl_cert_filepath;
+
int n;
+ /*
+ * for backwards-compatibility default to using ssl_... members, but
+ * if the newer client-specific ones are given, use those
+ */
+ if (info->client_ssl_cipher_list)
+ cipher_list = info->client_ssl_cipher_list;
+ if (info->client_ssl_ca_filepath)
+ ca_filepath = info->client_ssl_ca_filepath;
+ if (info->client_ssl_cert_filepath)
+ cert_filepath = info->client_ssl_cert_filepath;
+ if (info->client_ssl_private_key_filepath)
+ private_key_filepath = info->client_ssl_private_key_filepath;
+#endif
+
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
return 0;
/* basic openssl init already happened in context init */
+
+ /* choose the most recent spin of the api */
+#if defined(LWS_HAVE_TLS_CLIENT_METHOD)
+ method = (SSL_METHOD *)TLS_client_method();
+#elif defined(LWS_HAVE_TLSV1_2_CLIENT_METHOD)
+ method = (SSL_METHOD *)TLSv1_2_client_method();
+#else
method = (SSL_METHOD *)SSLv23_client_method();
+#endif
if (!method) {
error = ERR_get_error();
lwsl_err("problem creating ssl method %lu: %s\n",
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_NO_COMPRESSION);
#endif
+
+#if !defined(LWS_WITH_ESP32)
SSL_CTX_set_options(vhost->ssl_client_ctx,
SSL_OP_CIPHER_SERVER_PREFERENCE);
- if (info->ssl_cipher_list)
- SSL_CTX_set_cipher_list(vhost->ssl_client_ctx,
- info->ssl_cipher_list);
+
+ if (cipher_list)
+ SSL_CTX_set_cipher_list(vhost->ssl_client_ctx, cipher_list);
#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS))
#endif
/* openssl init for cert verification (for client sockets) */
- if (!info->ssl_ca_filepath) {
+ if (!ca_filepath) {
if (!SSL_CTX_load_verify_locations(
vhost->ssl_client_ctx, NULL,
LWS_OPENSSL_CLIENT_CERTS))
"Unable to load SSL Client certs from %s "
"(set by --with-client-cert-dir= "
"in configure) -- client ssl isn't "
- "going to work", LWS_OPENSSL_CLIENT_CERTS);
+ "going to work\n", LWS_OPENSSL_CLIENT_CERTS);
} else
if (!SSL_CTX_load_verify_locations(
- vhost->ssl_client_ctx, info->ssl_ca_filepath,
- NULL))
+ vhost->ssl_client_ctx, ca_filepath, NULL)) {
lwsl_err(
"Unable to load SSL Client certs "
"file from %s -- client ssl isn't "
- "going to work", info->ssl_ca_filepath);
+ "going to work\n", info->client_ssl_ca_filepath);
+ lws_ssl_elaborate_error();
+ }
else
lwsl_info("loaded ssl_ca_filepath\n");
-
+#endif
/*
* callback allowing user code to load extra verification certs
* helping the client to verify server identity
*/
+#if !defined(LWS_WITH_ESP32)
/* support for client-side certificate authentication */
- if (info->ssl_cert_filepath) {
+ if (cert_filepath) {
+ lwsl_notice("%s: doing cert filepath\n", __func__);
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_client_ctx,
- info->ssl_cert_filepath);
- if (n != 1) {
- lwsl_err("problem getting cert '%s' %lu: %s\n",
- info->ssl_cert_filepath,
- ERR_get_error(),
- ERR_error_string(ERR_get_error(),
- (char *)vhost->context->pt[0].serv_buf));
+ cert_filepath);
+ if (n < 1) {
+ lwsl_err("problem %d getting cert '%s'\n", n,
+ cert_filepath);
+ lws_ssl_elaborate_error();
return 1;
}
+ lwsl_notice("Loaded client cert %s\n", cert_filepath);
}
- if (info->ssl_private_key_filepath) {
+ if (private_key_filepath) {
+ lwsl_notice("%s: doing private key filepath\n", __func__);
lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info);
/* set the private key from KeyFile */
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_client_ctx,
- info->ssl_private_key_filepath, SSL_FILETYPE_PEM) != 1) {
- lwsl_err("use_PrivateKey_file '%s' %lu: %s\n",
- info->ssl_private_key_filepath,
- ERR_get_error(),
- ERR_error_string(ERR_get_error(),
- (char *)vhost->context->pt[0].serv_buf));
+ private_key_filepath, SSL_FILETYPE_PEM) != 1) {
+ lwsl_err("use_PrivateKey_file '%s'\n",
+ private_key_filepath);
+ lws_ssl_elaborate_error();
return 1;
}
+ lwsl_notice("Loaded client cert private key %s\n",
+ private_key_filepath);
/* verify private key */
if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) {
return 1;
}
}
-
+#endif
/*
* give him a fake wsi with context set, so he can use
* lws_get_context() in the callback
vhost->ssl_client_ctx, NULL, 0);
return 0;
-#endif
-#endif
}