Enable non-TLS WebSocket connections for noVNC

- Remove secure context check in noVNC RFB.js to allow HTTP usage
- Add --websocket-tls-only configuration option (defaults to true for security)
- Implement mixed TLS/non-TLS WebSocket server support
- Add plain WebSocket connection support in protocol layer
- Fix critical bugs: buffer overflows, race conditions, HTML template issues
- All changes maintain backward compatibility while enabling HTTP WebSocket usage
parent 5fff97e9
......@@ -37,6 +37,7 @@ static void set_default_config(wssshd_config_t *config) {
config->web_host = NULL;
config->web_port = 0;
config->web_https = false;
config->websocket_tls_only = true; // Default to TLS-only for security
config->debug = false;
config->debug_web = false;
config->debug_database = false;
......@@ -109,6 +110,8 @@ static void load_config_file(wssshd_config_t *config, const char *config_file) {
config->web_port = atoi(value);
} else if (strcmp(key, "web-https") == 0) {
config->web_https = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
} else if (strcmp(key, "websocket-tls-only") == 0) {
config->websocket_tls_only = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
}
}
}
......@@ -149,6 +152,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
{"web-host", required_argument, 0, 'w'},
{"web-port", required_argument, 0, 'W'},
{"web-https", no_argument, 0, 's'},
{"websocket-tls-only", no_argument, 0, 't'},
{"debug", no_argument, 0, 'D'},
{"debug-web", no_argument, 0, 'E'},
{"debug-database", no_argument, 0, 'F'},
......@@ -159,7 +163,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, "c:h:p:d:P:w:W:sDEF?", long_options, &option_index)) != -1) {
while ((opt = getopt_long(argc, argv, "c:h:p:d:P:w:W:stDEF?", long_options, &option_index)) != -1) {
switch (opt) {
case 'c':
if (config->config_file) free(config->config_file);
......@@ -190,6 +194,9 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
case 's':
config->web_https = true;
break;
case 't':
config->websocket_tls_only = false;
break;
case 'D':
config->debug = true;
break;
......@@ -210,6 +217,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
printf(" --web-host HOST Web interface host\n");
printf(" --web-port PORT Web interface port\n");
printf(" --web-https Enable HTTPS for web interface\n");
printf(" --websocket-tls-only Allow non-TLS WebSocket connections (default: TLS-only)\n");
printf(" --debug Enable debug output\n");
printf(" --debug-web Enable comprehensive web interface debug output\n");
printf(" --debug-database Enable database debug output\n");
......@@ -235,6 +243,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
config->port = DEFAULT_PORT;
config->web_port = 0;
config->web_https = false;
config->websocket_tls_only = true;
load_config_file(config, config->config_file);
if (config->debug) {
......@@ -284,6 +293,7 @@ void print_config(const wssshd_config_t *config) {
printf(" Web host: %s\n", config->web_host ? config->web_host : "none");
printf(" Web port: %d\n", config->web_port);
printf(" Web HTTPS: %s\n", config->web_https ? "yes" : "no");
printf(" WebSocket TLS-only: %s\n", config->websocket_tls_only ? "yes" : "no");
printf(" Debug: %s\n", config->debug ? "yes" : "no");
printf(" Debug Web: %s\n", config->debug_web ? "yes" : "no");
printf(" Debug Database: %s\n", config->debug_database ? "yes" : "no");
......
......@@ -32,6 +32,7 @@ typedef struct {
char *web_host;
int web_port;
bool web_https;
bool websocket_tls_only;
bool debug;
bool debug_web;
bool debug_database;
......
......@@ -95,12 +95,6 @@ export default class RFB extends EventTargetMixin {
throw new Error("Must specify URL, WebSocket or RTCDataChannel");
}
// We rely on modern APIs which might not be available in an
// insecure context
if (!window.isSecureContext) {
Log.Error("noVNC requires a secure context (TLS). Expect crashes!");
}
super();
this._target = target;
......
......@@ -892,7 +892,7 @@ static int parse_http_request(int client_fd, http_request_t *req) {
buffer[bytes_read] = '\0';
if (global_config && global_config->debug_web && !strstr(req->path, "/xterm/data")) {
if (global_config && global_config->debug_web) {
printf("[WEB-DEBUG] Raw buffer (%zd bytes): '", bytes_read);
for (ssize_t i = 0; i < bytes_read; i++) {
if (buffer[i] == '\r') printf("\\r");
......@@ -907,7 +907,7 @@ static int parse_http_request(int client_fd, http_request_t *req) {
char *line = strtok(buffer, "\r\n");
if (!line) return -1;
sscanf(line, "%s %s", req->method, req->path);
sscanf(line, "%9s %1023s", req->method, req->path);
// Parse query string
char *query = strchr(req->path, '?');
......@@ -1547,12 +1547,14 @@ static int handle_request(int client_fd, const http_request_t *req) {
int client_has_ssh = 0;
int client_found = 0;
for (size_t i = 0; i < global_state->clients_count; i++) {
if (strcmp(global_state->clients[i].client_id, client_id) == 0) {
if (global_state->clients[i].active &&
strcmp(global_state->clients[i].client_id, client_id) == 0) {
client_found = 1;
if (global_config && global_config->debug_web) {
printf("[WEB-DEBUG] Found client %s, active=%d, services='%s'\n", client_id, global_state->clients[i].active, global_state->clients[i].services);
}
// Check if SSH is in services
if (global_state->clients[i].services[0] != '\0') {
char services_copy[256];
strcpy(services_copy, global_state->clients[i].services);
char *service = strtok(services_copy, ",");
......@@ -1566,6 +1568,7 @@ static int handle_request(int client_fd, const http_request_t *req) {
}
service = strtok(NULL, ",");
}
}
break;
}
}
......@@ -1582,7 +1585,7 @@ static int handle_request(int client_fd, const http_request_t *req) {
// Generate terminal HTML with client_id
char html[32768];
int len = snprintf(html, sizeof(html), terminal_page_html,
client_id, client_id, client_id, client_id, client_id, client_id, client_id, client_id, client_id);
client_id, client_id, client_id, client_id, client_id, client_id, client_id, client_id, client_id, client_id);
send_response(client_fd, 200, "OK", "text/html", html, len, NULL, NULL);
} else {
// Handle terminal actions (connect, data, disconnect, resize)
......
......@@ -1722,7 +1722,12 @@ void *status_thread(void *arg) {
// WebSocket server functions
int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state) {
printf("Starting WebSocket server on %s:%d\n", config->host, config->port);
printf("Starting WebSocket server on %s:%d", config->host, config->port);
if (config->websocket_tls_only) {
printf(" (TLS-only)\n");
} else {
printf(" (TLS and non-TLS)\n");
}
// Create TCP socket
server_sock = socket(AF_INET, SOCK_STREAM, 0);
......@@ -1759,7 +1764,8 @@ int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state)
return -1;
}
// Initialize SSL
// Initialize SSL only if TLS is enabled
if (config->websocket_tls_only) {
printf("Creating SSL context...\n");
ssl_ctx = ssl_create_context();
if (!ssl_ctx) {
......@@ -1805,6 +1811,10 @@ int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state)
return -1;
}
printf("Certificates loaded successfully\n");
} else {
printf("WebSocket TLS disabled - allowing non-TLS connections\n");
ssl_ctx = NULL;
}
printf("WebSocket server listening on %s:%d\n", config->host, config->port);
server_running = 1;
......@@ -1828,8 +1838,12 @@ int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state)
continue;
}
// Create SSL connection
SSL *ssl = SSL_new(ssl_ctx);
ws_connection_t *conn = NULL;
SSL *ssl = NULL;
if (config->websocket_tls_only) {
// TLS-only mode: always use SSL
ssl = SSL_new(ssl_ctx);
if (!ssl) {
close(client_sock);
continue;
......@@ -1844,14 +1858,49 @@ int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state)
continue;
}
// Create WebSocket connection
ws_connection_t *conn = ws_connection_create(ssl, client_sock);
// Create WebSocket connection with SSL
conn = ws_connection_create(ssl, client_sock);
if (!conn) {
// ws_connection_create failed, clean up SSL and socket
SSL_free(ssl);
close(client_sock);
continue;
}
} else {
// Mixed mode: try TLS first, fall back to plain WebSocket
ssl = SSL_new(ssl_ctx);
if (ssl) {
SSL_set_fd(ssl, client_sock);
if (SSL_accept(ssl) > 0) {
// TLS handshake successful
conn = ws_connection_create(ssl, client_sock);
if (!conn) {
SSL_free(ssl);
close(client_sock);
continue;
}
} else {
// TLS handshake failed, try plain WebSocket
SSL_free(ssl);
ssl = NULL;
// Create plain WebSocket connection
conn = ws_connection_create_plain(client_sock);
if (!conn) {
close(client_sock);
continue;
}
}
} else {
// SSL context creation failed, use plain WebSocket
conn = ws_connection_create(NULL, client_sock);
if (!conn) {
close(client_sock);
continue;
}
}
}
// Start client handler thread
client_thread_args_t *thread_args = malloc(sizeof(client_thread_args_t));
......
......@@ -46,6 +46,10 @@ ws_connection_t *ws_connection_create(SSL *ssl, int sock_fd) {
return conn;
}
ws_connection_t *ws_connection_create_plain(int sock_fd) {
return ws_connection_create(NULL, sock_fd);
}
void ws_connection_free(ws_connection_t *conn) {
if (!conn) return;
......
......@@ -64,6 +64,7 @@ typedef struct {
// Function declarations
ws_connection_t *ws_connection_create(SSL *ssl, int sock_fd);
ws_connection_t *ws_connection_create_plain(int sock_fd);
void ws_connection_free(ws_connection_t *conn);
// HTTP/WebSocket handshake
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment