int lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len) { int n; X509 *peer = SSL_get_peer_certificate(wsi->tls.ssl); struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; char *sb = (char *)&pt->serv_buf[0]; if (!peer) { lwsl_info("peer did not provide cert\n"); return -1; } lwsl_info("peer provided cert\n"); n = SSL_get_verify_result(wsi->tls.ssl); lws_latency(wsi->context, wsi, "SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0); lwsl_debug("get_verify says %d\n", n); if (n == X509_V_OK) return 0; if (n == X509_V_ERR_HOSTNAME_MISMATCH && (wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) { lwsl_info("accepting certificate for invalid hostname\n"); return 0; } if (n == X509_V_ERR_INVALID_CA && (wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED)) { lwsl_info("accepting certificate from untrusted CA\n"); return 0; } if ((n == X509_V_ERR_CERT_NOT_YET_VALID || n == X509_V_ERR_CERT_HAS_EXPIRED) && (wsi->tls.use_ssl & LCCSCF_ALLOW_EXPIRED)) { lwsl_info("accepting expired or not yet valid certificate\n"); return 0; } lws_snprintf(ebuf, ebuf_len, "server's cert didn't look good, X509_V_ERR = %d: %s\n", n, ERR_error_string(n, sb)); lwsl_info("%s\n", ebuf); lws_ssl_elaborate_error(); return -1; }
int lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd) { #if !defined(LWS_WITH_XRADIO) errno = 0; #endif wsi->ssl = SSL_new(wsi->vhost->ssl_ctx); if (wsi->ssl == NULL) { lwsl_err("SSL_new failed: errno %d\n", errno); lws_ssl_elaborate_error(); return 1; } SSL_set_fd(wsi->ssl, accept_fd); if (wsi->vhost->ssl_info_event_mask) SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback); SSL_set_sni_callback(wsi->ssl, lws_mbedtls_sni_cb, wsi->context); return 0; }
LWS_VISIBLE int lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len) { int n, m; if (!wsi->ssl) return lws_ssl_capable_write_no_ssl(wsi, buf, len); n = SSL_write(wsi->ssl, buf, len); if (n > 0) return n; m = lws_ssl_get_error(wsi, n); if (m != SSL_ERROR_SYSCALL) { if (SSL_want_read(wsi->ssl)) { lwsl_notice("%s: want read\n", __func__); return LWS_SSL_CAPABLE_MORE_SERVICE; } if (SSL_want_write(wsi->ssl)) { lws_set_blocking_send(wsi); lwsl_notice("%s: want write\n", __func__); return LWS_SSL_CAPABLE_MORE_SERVICE; } } lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL)); lws_ssl_elaborate_error(); wsi->socket_is_permanently_unusable = 1; return LWS_SSL_CAPABLE_ERROR; }
int 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 lws_latency(context, wsi, "SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0); if (n < 0) { n = lws_ssl_get_error(wsi, n); if (n == SSL_ERROR_WANT_READ) { wsi->mode = LWSCM_WSCL_WAITING_SSL; return 0; /* no error */ } if (n == SSL_ERROR_WANT_WRITE) { /* * wants us to retry connect due to * state of the underlying ssl layer... * but since it may be stalled on * blocked write, no incoming data may * arrive to trigger the retry. * Force (possibly many times if the SSL * state persists in returning the * condition code, but other sockets * are getting serviced inbetweentimes) * us to get called back when writable. */ lwsl_info("SSL_connect WANT_WRITE... retrying\n"); lws_callback_on_writable(wsi); wsi->mode = LWSCM_WSCL_WAITING_SSL; return 0; /* no error */ } n = -1; } if (n <= 0) { /* * 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) { lwsl_err("SSL connect error %lu: %s\n", n, ERR_error_string(n, sb)); return -1; } #endif #endif } } #if defined(LWS_USE_POLARSSL) #else #if defined(LWS_USE_MBEDTLS) #else #ifndef USE_WOLFSSL /* * See comment above about wolfSSL certificate * verification */ 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); 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 & LCCSCF_ALLOW_SELFSIGNED) { lwsl_notice("accepting self-signed certificate\n"); } else { lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n", n, ERR_error_string(n, sb)); lws_ssl_elaborate_error(); return -1; } } #endif /* USE_WOLFSSL */ #endif #endif return 1; }
LWS_VISIBLE int lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) { struct lws_context *context = wsi->context; struct lws_vhost *vh; struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; int n, m; #if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) BIO *bio; #endif char buf[256]; (void)buf; if (!LWS_SSL_ENABLED(wsi->vhost)) return 0; switch (wsi->mode) { case LWSCM_SSL_INIT: case LWSCM_SSL_INIT_RAW: if (wsi->ssl) lwsl_err("%s: leaking ssl\n", __func__); if (accept_fd == LWS_SOCK_INVALID) assert(0); if (context->simultaneous_ssl_restriction && context->simultaneous_ssl >= context->simultaneous_ssl_restriction) { lwsl_notice("unable to deal with SSL connection\n"); return 1; } errno = 0; wsi->ssl = SSL_new(wsi->vhost->ssl_ctx); if (wsi->ssl == NULL) { lwsl_err("SSL_new failed: %d (errno %d)\n", lws_ssl_get_error(wsi, 0), errno); lws_ssl_elaborate_error(); if (accept_fd != LWS_SOCK_INVALID) compatible_close(accept_fd); goto fail; } #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 (context->simultaneous_ssl_restriction && ++context->simultaneous_ssl == context->simultaneous_ssl_restriction) /* that was the last allowed SSL connection */ lws_gate_accepts(context, 0); #if defined(LWS_WITH_STATS) context->updated = 1; #endif #if !defined(LWS_WITH_MBEDTLS) SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index, wsi); #endif SSL_set_fd(wsi->ssl, accept_fd); #ifdef USE_WOLFSSL #ifdef USE_OLD_CYASSL CyaSSL_set_using_nonblock(wsi->ssl, 1); #else wolfSSL_set_using_nonblock(wsi->ssl, 1); #endif #else #if defined(LWS_WITH_MBEDTLS) lws_plat_set_socket_options(wsi->vhost, accept_fd); #else SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); bio = SSL_get_rbio(wsi->ssl); if (bio) BIO_set_nbio(bio, 1); /* nonblocking */ else lwsl_notice("NULL rbio\n"); bio = SSL_get_wbio(wsi->ssl); if (bio) BIO_set_nbio(bio, 1); /* nonblocking */ else lwsl_notice("NULL rbio\n"); #endif #endif /* * we are not accepted yet, but we need to enter ourselves * as a live connection. That way we can retry when more * pieces come if we're not sorted yet */ if (wsi->mode == LWSCM_SSL_INIT) wsi->mode = LWSCM_SSL_ACK_PENDING; else wsi->mode = LWSCM_SSL_ACK_PENDING_RAW; if (insert_wsi_socket_into_fds(context, wsi)) { lwsl_err("%s: failed to insert into fds\n", __func__); goto fail; } lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT, context->timeout_secs); lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n"); /* fallthru */ case LWSCM_SSL_ACK_PENDING: case LWSCM_SSL_ACK_PENDING_RAW: if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { lwsl_err("%s: lws_change_pollfd failed\n", __func__); goto fail; } lws_latency_pre(context, wsi); if (wsi->vhost->allow_non_ssl_on_ssl_port) { n = recv(wsi->desc.sockfd, (char *)pt->serv_buf, context->pt_serv_buf_size, MSG_PEEK); /* * optionally allow non-SSL connect on SSL listening socket * This is disabled by default, if enabled it goes around any * SSL-level access control (eg, client-side certs) so leave * it disabled unless you know it's not a problem for you */ if (n >= 1 && pt->serv_buf[0] >= ' ') { /* * TLS content-type for Handshake is 0x16, and * for ChangeCipherSpec Record, it's 0x14 * * A non-ssl session will start with the HTTP * method in ASCII. If we see it's not a legit * SSL handshake kill the SSL for this * connection and try to handle as a HTTP * connection upgrade directly. */ wsi->use_ssl = 0; SSL_shutdown(wsi->ssl); SSL_free(wsi->ssl); wsi->ssl = NULL; if (lws_check_opt(context->options, LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS)) wsi->redirect_to_https = 1; goto accepted; } if (!n) /* * connection is gone, or nothing to read * if it's gone, we will timeout on * PENDING_TIMEOUT_SSL_ACCEPT */ break; if (n < 0 && (LWS_ERRNO == LWS_EAGAIN || LWS_ERRNO == LWS_EWOULDBLOCK)) { /* * well, we get no way to know ssl or not * so go around again waiting for something * to come and give us a hint, or timeout the * connection. */ m = SSL_ERROR_WANT_READ; goto go_again; } } /* normal SSL connection processing path */ #if defined(LWS_WITH_STATS) if (!wsi->accept_start_us) wsi->accept_start_us = time_in_microseconds(); #endif errno = 0; lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1); n = SSL_accept(wsi->ssl); lws_latency(context, wsi, "SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1); lwsl_info("SSL_accept says %d\n", n); if (n == 1) goto accepted; m = lws_ssl_get_error(wsi, n); #if defined(LWS_WITH_MBEDTLS) if (m == SSL_ERROR_SYSCALL && errno == 11) m = SSL_ERROR_WANT_READ; #endif if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL) goto failed; go_again: if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) { if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__); goto fail; } lwsl_info("SSL_ERROR_WANT_READ\n"); break; } if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) { lwsl_debug("%s: WANT_WRITE\n", __func__); if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__); goto fail; } break; } failed: lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1); wsi->socket_is_permanently_unusable = 1; lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd, lws_ssl_get_error_string(m, n, buf, sizeof(buf))); lws_ssl_elaborate_error(); goto fail; accepted: lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1); #if defined(LWS_WITH_STATS) lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY, time_in_microseconds() - wsi->accept_start_us); wsi->accept_start_us = time_in_microseconds(); #endif /* adapt our vhost to match the SNI SSL_CTX that was chosen */ vh = context->vhost_list; while (vh) { if (!vh->being_destroyed && wsi->ssl && vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) { lwsl_info("setting wsi to vh %s\n", vh->name); wsi->vhost = vh; break; } vh = vh->vhost_next; } /* OK, we are accepted... give him some time to negotiate */ lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, context->timeout_secs); if (wsi->mode == LWSCM_SSL_ACK_PENDING_RAW) wsi->mode = LWSCM_RAW; else wsi->mode = LWSCM_HTTP_SERVING; #if defined(LWS_WITH_HTTP2) if (lws_h2_configure_if_upgraded(wsi)) goto fail; #endif lwsl_debug("accepted new SSL conn\n"); break; } return 0; fail: return 1; }
LWS_VISIBLE int lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) { struct lws_context *context = wsi->context; struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; int n, m; #if !defined(USE_WOLFSSL) && !defined(LWS_USE_POLARSSL) && !defined(LWS_USE_MBEDTLS) BIO *bio; #endif if (!LWS_SSL_ENABLED(wsi->vhost)) return 0; switch (wsi->mode) { case LWSCM_SSL_INIT: if (wsi->ssl) lwsl_err("%s: leaking ssl\n", __func__); if (accept_fd == LWS_SOCK_INVALID) assert(0); #if defined(LWS_USE_POLARSSL) { ssl_session *ssn; int rc; wsi->ssl = lws_zalloc(sizeof(ssl_context)); ssn = lws_zalloc(sizeof(ssl_session)); rc = ssl_init(wsi->ssl); if (rc) { lwsl_err("ssl_init failed\n"); goto fail; } ssl_set_endpoint(wsi->ssl, SSL_IS_SERVER); ssl_set_authmode(wsi->ssl, SSL_VERIFY_OPTIONAL); ssl_set_rng(wsi->ssl, urandom_bytes, NULL); ssl_set_dbg(wsi->ssl, pssl_debug, NULL); ssl_set_bio(wsi->ssl, net_recv, &wsi->sock, net_send, &wsi->sock); ssl_set_ciphersuites(wsi->ssl, ciphers); ssl_set_session(wsi->ssl, ssn); ssl_set_ca_chain(wsi->ssl, &wsi->vhost->ssl_ctx->ca, NULL, NULL); ssl_set_own_cert_rsa(wsi->ssl, &wsi->vhost->ssl_ctx->certificate, &wsi->vhost->ssl_ctx->key); // ssl_set_dh_param(wsi->ssl, my_dhm_P, my_dhm_G); lwsl_err("%s: polarssl init done\n", __func__); } #else #if defined(LWS_USE_MBEDTLS) #else wsi->ssl = SSL_new(wsi->vhost->ssl_ctx); if (wsi->ssl == NULL) { lwsl_err("SSL_new failed: %s\n", ERR_error_string(lws_ssl_get_error(wsi, 0), NULL)); lws_decode_ssl_error(); if (accept_fd != LWS_SOCK_INVALID) compatible_close(accept_fd); goto fail; } SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index, wsi->vhost); SSL_set_fd(wsi->ssl, accept_fd); #endif #endif #ifdef USE_WOLFSSL #ifdef USE_OLD_CYASSL CyaSSL_set_using_nonblock(wsi->ssl, 1); #else wolfSSL_set_using_nonblock(wsi->ssl, 1); #endif #else #if defined(LWS_USE_POLARSSL) #else #if defined(LWS_USE_MBEDTLS) #else SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); bio = SSL_get_rbio(wsi->ssl); if (bio) BIO_set_nbio(bio, 1); /* nonblocking */ else lwsl_notice("NULL rbio\n"); bio = SSL_get_wbio(wsi->ssl); if (bio) BIO_set_nbio(bio, 1); /* nonblocking */ else lwsl_notice("NULL rbio\n"); #endif #endif #endif /* * we are not accepted yet, but we need to enter ourselves * as a live connection. That way we can retry when more * pieces come if we're not sorted yet */ wsi->mode = LWSCM_SSL_ACK_PENDING; if (insert_wsi_socket_into_fds(context, wsi)) { lwsl_err("%s: failed to insert into fds\n", __func__); goto fail; } lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT, context->timeout_secs); lwsl_info("inserted SSL accept into fds, trying SSL_accept\n"); /* fallthru */ case LWSCM_SSL_ACK_PENDING: if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { lwsl_err("%s: lws_change_pollfd failed\n", __func__); goto fail; } lws_latency_pre(context, wsi); n = recv(wsi->sock, (char *)pt->serv_buf, context->pt_serv_buf_size, MSG_PEEK); /* * optionally allow non-SSL connect on SSL listening socket * This is disabled by default, if enabled it goes around any * SSL-level access control (eg, client-side certs) so leave * it disabled unless you know it's not a problem for you */ if (wsi->vhost->allow_non_ssl_on_ssl_port) { if (n >= 1 && pt->serv_buf[0] >= ' ') { /* * TLS content-type for Handshake is 0x16, and * for ChangeCipherSpec Record, it's 0x14 * * A non-ssl session will start with the HTTP * method in ASCII. If we see it's not a legit * SSL handshake kill the SSL for this * connection and try to handle as a HTTP * connection upgrade directly. */ wsi->use_ssl = 0; #if defined(LWS_USE_POLARSSL) ssl_close_notify(wsi->ssl); ssl_free(wsi->ssl); #else #if defined(LWS_USE_MBEDTLS) #else SSL_shutdown(wsi->ssl); SSL_free(wsi->ssl); #endif #endif wsi->ssl = NULL; if (lws_check_opt(context->options, LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS)) wsi->redirect_to_https = 1; goto accepted; } if (!n) /* * connection is gone, or nothing to read * if it's gone, we will timeout on * PENDING_TIMEOUT_SSL_ACCEPT */ break; if (n < 0 && (LWS_ERRNO == LWS_EAGAIN || LWS_ERRNO == LWS_EWOULDBLOCK)) { /* * well, we get no way to know ssl or not * so go around again waiting for something * to come and give us a hint, or timeout the * connection. */ m = SSL_ERROR_WANT_READ; goto go_again; } } /* normal SSL connection processing path */ #if defined(LWS_USE_POLARSSL) n = ssl_handshake(wsi->ssl); #else #if defined(LWS_USE_MBEDTLS) #else n = SSL_accept(wsi->ssl); #endif #endif lws_latency(context, wsi, "SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1); if (n == 1) goto accepted; m = lws_ssl_get_error(wsi, n); lwsl_debug("SSL_accept failed %d / %s\n", m, ERR_error_string(m, NULL)); go_again: if (m == SSL_ERROR_WANT_READ) { if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { lwsl_err("%s: WANT_READ change_pollfd failed\n", __func__); goto fail; } lwsl_info("SSL_ERROR_WANT_READ\n"); break; } if (m == SSL_ERROR_WANT_WRITE) { if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { lwsl_err("%s: WANT_WRITE change_pollfd failed\n", __func__); goto fail; } break; } lwsl_err("SSL_accept failed skt %u: %s\n", wsi->sock, ERR_error_string(m, NULL)); lws_ssl_elaborate_error(); goto fail; accepted: /* OK, we are accepted... give him some time to negotiate */ lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, context->timeout_secs); wsi->mode = LWSCM_HTTP_SERVING; lws_http2_configure_if_upgraded(wsi); lwsl_debug("accepted new SSL conn\n"); break; } return 0; fail: return 1; }