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) { ...@@ -37,6 +37,7 @@ static void set_default_config(wssshd_config_t *config) {
config->web_host = NULL; config->web_host = NULL;
config->web_port = 0; config->web_port = 0;
config->web_https = false; config->web_https = false;
config->websocket_tls_only = true; // Default to TLS-only for security
config->debug = false; config->debug = false;
config->debug_web = false; config->debug_web = false;
config->debug_database = false; config->debug_database = false;
...@@ -109,6 +110,8 @@ static void load_config_file(wssshd_config_t *config, const char *config_file) { ...@@ -109,6 +110,8 @@ static void load_config_file(wssshd_config_t *config, const char *config_file) {
config->web_port = atoi(value); config->web_port = atoi(value);
} else if (strcmp(key, "web-https") == 0) { } else if (strcmp(key, "web-https") == 0) {
config->web_https = (strcmp(value, "true") == 0 || strcmp(value, "1") == 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[]) { ...@@ -149,6 +152,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
{"web-host", required_argument, 0, 'w'}, {"web-host", required_argument, 0, 'w'},
{"web-port", required_argument, 0, 'W'}, {"web-port", required_argument, 0, 'W'},
{"web-https", no_argument, 0, 's'}, {"web-https", no_argument, 0, 's'},
{"websocket-tls-only", no_argument, 0, 't'},
{"debug", no_argument, 0, 'D'}, {"debug", no_argument, 0, 'D'},
{"debug-web", no_argument, 0, 'E'}, {"debug-web", no_argument, 0, 'E'},
{"debug-database", no_argument, 0, 'F'}, {"debug-database", no_argument, 0, 'F'},
...@@ -159,7 +163,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) { ...@@ -159,7 +163,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
int opt; int opt;
int option_index = 0; 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) { switch (opt) {
case 'c': case 'c':
if (config->config_file) free(config->config_file); if (config->config_file) free(config->config_file);
...@@ -190,6 +194,9 @@ wssshd_config_t *load_config(int argc, char *argv[]) { ...@@ -190,6 +194,9 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
case 's': case 's':
config->web_https = true; config->web_https = true;
break; break;
case 't':
config->websocket_tls_only = false;
break;
case 'D': case 'D':
config->debug = true; config->debug = true;
break; break;
...@@ -210,6 +217,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) { ...@@ -210,6 +217,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
printf(" --web-host HOST Web interface host\n"); printf(" --web-host HOST Web interface host\n");
printf(" --web-port PORT Web interface port\n"); printf(" --web-port PORT Web interface port\n");
printf(" --web-https Enable HTTPS for web interface\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 Enable debug output\n");
printf(" --debug-web Enable comprehensive web interface debug output\n"); printf(" --debug-web Enable comprehensive web interface debug output\n");
printf(" --debug-database Enable database debug output\n"); printf(" --debug-database Enable database debug output\n");
...@@ -235,6 +243,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) { ...@@ -235,6 +243,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
config->port = DEFAULT_PORT; config->port = DEFAULT_PORT;
config->web_port = 0; config->web_port = 0;
config->web_https = false; config->web_https = false;
config->websocket_tls_only = true;
load_config_file(config, config->config_file); load_config_file(config, config->config_file);
if (config->debug) { if (config->debug) {
...@@ -284,6 +293,7 @@ void print_config(const wssshd_config_t *config) { ...@@ -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 host: %s\n", config->web_host ? config->web_host : "none");
printf(" Web port: %d\n", config->web_port); printf(" Web port: %d\n", config->web_port);
printf(" Web HTTPS: %s\n", config->web_https ? "yes" : "no"); 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: %s\n", config->debug ? "yes" : "no");
printf(" Debug Web: %s\n", config->debug_web ? "yes" : "no"); printf(" Debug Web: %s\n", config->debug_web ? "yes" : "no");
printf(" Debug Database: %s\n", config->debug_database ? "yes" : "no"); printf(" Debug Database: %s\n", config->debug_database ? "yes" : "no");
......
...@@ -32,6 +32,7 @@ typedef struct { ...@@ -32,6 +32,7 @@ typedef struct {
char *web_host; char *web_host;
int web_port; int web_port;
bool web_https; bool web_https;
bool websocket_tls_only;
bool debug; bool debug;
bool debug_web; bool debug_web;
bool debug_database; bool debug_database;
......
...@@ -95,12 +95,6 @@ export default class RFB extends EventTargetMixin { ...@@ -95,12 +95,6 @@ export default class RFB extends EventTargetMixin {
throw new Error("Must specify URL, WebSocket or RTCDataChannel"); 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(); super();
this._target = target; this._target = target;
......
...@@ -892,7 +892,7 @@ static int parse_http_request(int client_fd, http_request_t *req) { ...@@ -892,7 +892,7 @@ static int parse_http_request(int client_fd, http_request_t *req) {
buffer[bytes_read] = '\0'; 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); printf("[WEB-DEBUG] Raw buffer (%zd bytes): '", bytes_read);
for (ssize_t i = 0; i < bytes_read; i++) { for (ssize_t i = 0; i < bytes_read; i++) {
if (buffer[i] == '\r') printf("\\r"); if (buffer[i] == '\r') printf("\\r");
...@@ -907,7 +907,7 @@ static int parse_http_request(int client_fd, http_request_t *req) { ...@@ -907,7 +907,7 @@ static int parse_http_request(int client_fd, http_request_t *req) {
char *line = strtok(buffer, "\r\n"); char *line = strtok(buffer, "\r\n");
if (!line) return -1; if (!line) return -1;
sscanf(line, "%s %s", req->method, req->path); sscanf(line, "%9s %1023s", req->method, req->path);
// Parse query string // Parse query string
char *query = strchr(req->path, '?'); char *query = strchr(req->path, '?');
...@@ -1547,24 +1547,27 @@ static int handle_request(int client_fd, const http_request_t *req) { ...@@ -1547,24 +1547,27 @@ static int handle_request(int client_fd, const http_request_t *req) {
int client_has_ssh = 0; int client_has_ssh = 0;
int client_found = 0; int client_found = 0;
for (size_t i = 0; i < global_state->clients_count; i++) { 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; client_found = 1;
if (global_config && global_config->debug_web) { 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); 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 // Check if SSH is in services
char services_copy[256]; if (global_state->clients[i].services[0] != '\0') {
strcpy(services_copy, global_state->clients[i].services); char services_copy[256];
char *service = strtok(services_copy, ","); strcpy(services_copy, global_state->clients[i].services);
while (service) { char *service = strtok(services_copy, ",");
while (*service == ' ') service++; while (service) {
char *end = service + strlen(service) - 1; while (*service == ' ') service++;
while (end > service && *end == ' ') *end-- = '\0'; char *end = service + strlen(service) - 1;
if (strcmp(service, "ssh") == 0) { while (end > service && *end == ' ') *end-- = '\0';
client_has_ssh = 1; if (strcmp(service, "ssh") == 0) {
break; client_has_ssh = 1;
break;
}
service = strtok(NULL, ",");
} }
service = strtok(NULL, ",");
} }
break; break;
} }
...@@ -1582,7 +1585,7 @@ static int handle_request(int client_fd, const http_request_t *req) { ...@@ -1582,7 +1585,7 @@ static int handle_request(int client_fd, const http_request_t *req) {
// Generate terminal HTML with client_id // Generate terminal HTML with client_id
char html[32768]; char html[32768];
int len = snprintf(html, sizeof(html), terminal_page_html, 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); send_response(client_fd, 200, "OK", "text/html", html, len, NULL, NULL);
} else { } else {
// Handle terminal actions (connect, data, disconnect, resize) // Handle terminal actions (connect, data, disconnect, resize)
......
...@@ -1722,7 +1722,12 @@ void *status_thread(void *arg) { ...@@ -1722,7 +1722,12 @@ void *status_thread(void *arg) {
// WebSocket server functions // WebSocket server functions
int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state) { 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 // Create TCP socket
server_sock = socket(AF_INET, SOCK_STREAM, 0); server_sock = socket(AF_INET, SOCK_STREAM, 0);
...@@ -1759,52 +1764,57 @@ int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state) ...@@ -1759,52 +1764,57 @@ int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state)
return -1; return -1;
} }
// Initialize SSL // Initialize SSL only if TLS is enabled
printf("Creating SSL context...\n"); if (config->websocket_tls_only) {
ssl_ctx = ssl_create_context(); printf("Creating SSL context...\n");
if (!ssl_ctx) { ssl_ctx = ssl_create_context();
fprintf(stderr, "Failed to create SSL context\n"); if (!ssl_ctx) {
close(server_sock); fprintf(stderr, "Failed to create SSL context\n");
return -1; close(server_sock);
} return -1;
printf("SSL context created successfully\n"); }
printf("SSL context created successfully\n");
// Load or generate certificates
char cert_file[256], key_file[256]; // Load or generate certificates
// Try system location first, then fall back to local directory char cert_file[256], key_file[256];
snprintf(cert_file, sizeof(cert_file), "/etc/wssshd/cert.pem"); // Try system location first, then fall back to local directory
snprintf(key_file, sizeof(key_file), "/etc/wssshd/key.pem"); snprintf(cert_file, sizeof(cert_file), "/etc/wssshd/cert.pem");
snprintf(key_file, sizeof(key_file), "/etc/wssshd/key.pem");
// If system location doesn't exist and we can't write there, use local directory
if (access(cert_file, F_OK) != 0) {
char local_dir[256];
snprintf(local_dir, sizeof(local_dir), "./certs");
mkdir(local_dir, 0755); // Create directory if it doesn't exist
snprintf(cert_file, sizeof(cert_file), "./certs/cert.pem");
snprintf(key_file, sizeof(key_file), "./certs/key.pem");
}
printf("Using certificates: %s, %s\n", cert_file, key_file); // If system location doesn't exist and we can't write there, use local directory
if (access(cert_file, F_OK) != 0) {
char local_dir[256];
snprintf(local_dir, sizeof(local_dir), "./certs");
mkdir(local_dir, 0755); // Create directory if it doesn't exist
snprintf(cert_file, sizeof(cert_file), "./certs/cert.pem");
snprintf(key_file, sizeof(key_file), "./certs/key.pem");
}
printf("Using certificates: %s, %s\n", cert_file, key_file);
if (access(cert_file, F_OK) != 0 || access(key_file, F_OK) != 0) {
printf("Generating self-signed certificate...\n");
if (ssl_generate_self_signed_cert(cert_file, key_file) != 0) {
fprintf(stderr, "Failed to generate certificate\n");
SSL_CTX_free(ssl_ctx);
close(server_sock);
return -1;
}
printf("Certificate generated successfully\n");
}
if (access(cert_file, F_OK) != 0 || access(key_file, F_OK) != 0) { printf("Loading certificates...\n");
printf("Generating self-signed certificate...\n"); if (ssl_load_certificates(ssl_ctx, cert_file, key_file) != 0) {
if (ssl_generate_self_signed_cert(cert_file, key_file) != 0) { fprintf(stderr, "Failed to load certificates\n");
fprintf(stderr, "Failed to generate certificate\n");
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(server_sock); close(server_sock);
return -1; return -1;
} }
printf("Certificate generated successfully\n"); printf("Certificates loaded successfully\n");
} } else {
printf("WebSocket TLS disabled - allowing non-TLS connections\n");
printf("Loading certificates...\n"); ssl_ctx = NULL;
if (ssl_load_certificates(ssl_ctx, cert_file, key_file) != 0) {
fprintf(stderr, "Failed to load certificates\n");
SSL_CTX_free(ssl_ctx);
close(server_sock);
return -1;
} }
printf("Certificates loaded successfully\n");
printf("WebSocket server listening on %s:%d\n", config->host, config->port); printf("WebSocket server listening on %s:%d\n", config->host, config->port);
server_running = 1; server_running = 1;
...@@ -1828,29 +1838,68 @@ int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state) ...@@ -1828,29 +1838,68 @@ int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state)
continue; continue;
} }
// Create SSL connection ws_connection_t *conn = NULL;
SSL *ssl = SSL_new(ssl_ctx); SSL *ssl = NULL;
if (!ssl) {
close(client_sock); if (config->websocket_tls_only) {
continue; // TLS-only mode: always use SSL
} ssl = SSL_new(ssl_ctx);
if (!ssl) {
close(client_sock);
continue;
}
SSL_set_fd(ssl, client_sock); SSL_set_fd(ssl, client_sock);
if (SSL_accept(ssl) <= 0) { if (SSL_accept(ssl) <= 0) {
ERR_print_errors_fp(stderr); ERR_print_errors_fp(stderr);
SSL_free(ssl); SSL_free(ssl);
close(client_sock); close(client_sock);
continue; continue;
} }
// Create WebSocket connection // Create WebSocket connection with SSL
ws_connection_t *conn = ws_connection_create(ssl, client_sock); conn = ws_connection_create(ssl, client_sock);
if (!conn) { if (!conn) {
// ws_connection_create failed, clean up SSL and socket // ws_connection_create failed, clean up SSL and socket
SSL_free(ssl); SSL_free(ssl);
close(client_sock); close(client_sock);
continue; 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 // Start client handler thread
......
...@@ -46,6 +46,10 @@ ws_connection_t *ws_connection_create(SSL *ssl, int sock_fd) { ...@@ -46,6 +46,10 @@ ws_connection_t *ws_connection_create(SSL *ssl, int sock_fd) {
return conn; 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) { void ws_connection_free(ws_connection_t *conn) {
if (!conn) return; if (!conn) return;
......
...@@ -64,6 +64,7 @@ typedef struct { ...@@ -64,6 +64,7 @@ typedef struct {
// Function declarations // Function declarations
ws_connection_t *ws_connection_create(SSL *ssl, int sock_fd); 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); void ws_connection_free(ws_connection_t *conn);
// HTTP/WebSocket handshake // 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