Fix critical segmentation fault in wssshd2 when wsscp is interrupted

- Add comprehensive thread-safety with mutex locks for all shared data structures
- Implement proper tunnel cleanup when websocket connections close to prevent use-after-free
- Add immediate connection state updates when receive operations fail to prevent race conditions
- Enhance error handling with graceful failure management for SSL operations
- Prevent server crashes during client disconnections and file transfer interruptions

Root cause: Use-after-free vulnerability when freed websocket connections were still referenced by active tunnels during client interruptions.

Solution: Complete overhaul of connection lifecycle management with proper synchronization and cleanup procedures.

Fixes issue where pressing Ctrl+C during wsscp file transfers caused wssshd2 to segfault.
parent fb88c29a
...@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file. ...@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.6.7] - 2025-09-21
### Fixed
- **Critical Segmentation Fault in wssshd2**: Fixed server crash when wsscp is interrupted during file transfers
- Root cause: Use-after-free vulnerability when websocket connections were freed while other threads still referenced them
- Solution: Implemented comprehensive tunnel cleanup when websocket connections close
- Added immediate connection state updates when receive operations fail
- Prevented race conditions between connection cleanup and message forwarding
- Enhanced thread safety with proper mutex protection for all shared data access
### Technical Details
- **Thread Safety**: Added mutex locks to all shared data structures (clients, tunnels, terminals)
- **Connection Lifecycle**: Proper cleanup of tunnel references when websocket connections close
- **Race Condition Prevention**: Immediate connection state updates prevent stale references
- **Memory Safety**: Eliminated use-after-free crashes during client interruptions
- **Server Stability**: wssshd2 now handles client disconnections gracefully without crashing
### Security
- **Memory Corruption Prevention**: Fixed heap corruption that could be exploited
- **Resource Management**: Proper cleanup prevents resource leaks and dangling pointers
- **Server Resilience**: Enhanced resistance to DoS attacks via connection interruption
## [1.6.6] - 2025-09-20 ## [1.6.6] - 2025-09-20
### Fixed ### Fixed
......
...@@ -43,6 +43,7 @@ WSSSH is a universal tunneling system that provides secure access to remote mach ...@@ -43,6 +43,7 @@ WSSSH is a universal tunneling system that provides secure access to remote mach
- **Advanced Logging**: Automatic log rotation and monitoring - **Advanced Logging**: Automatic log rotation and monitoring
- **Multiple Operating Modes**: Interactive, silent, bridge, script, and daemon modes - **Multiple Operating Modes**: Interactive, silent, bridge, script, and daemon modes
- **Enterprise Reliability**: Production-grade process supervision - **Enterprise Reliability**: Production-grade process supervision
- **Server Stability**: Robust error handling with comprehensive crash prevention and graceful client disconnection management
## Operating Modes ## Operating Modes
...@@ -314,6 +315,7 @@ wsssh [options] user@client.domain [ssh_options...] ...@@ -314,6 +315,7 @@ wsssh [options] user@client.domain [ssh_options...]
- `--tunnel TRANSPORT`: Transport for data channel - `--tunnel TRANSPORT`: Transport for data channel
- `--tunnel-control TRANSPORT`: Transport for control channel - `--tunnel-control TRANSPORT`: Transport for control channel
- `--service SERVICE`: Service type (default: ssh) - `--service SERVICE`: Service type (default: ssh)
- `--enc ENCODING`: Data encoding: hex, base64, or bin (default: hex)
- `--debug`: Enable debug output - `--debug`: Enable debug output
- `--dev-tunnel`: Setup tunnel but don't launch SSH - `--dev-tunnel`: Setup tunnel but don't launch SSH
...@@ -329,6 +331,7 @@ wsscp [options] [scp_options...] source destination ...@@ -329,6 +331,7 @@ wsscp [options] [scp_options...] source destination
- `--tunnel TRANSPORT`: Transport for data channel - `--tunnel TRANSPORT`: Transport for data channel
- `--tunnel-control TRANSPORT`: Transport for control channel - `--tunnel-control TRANSPORT`: Transport for control channel
- `--service SERVICE`: Service type (default: ssh) - `--service SERVICE`: Service type (default: ssh)
- `--enc ENCODING`: Data encoding: hex, base64, or bin (default: hex)
- `--debug`: Enable debug output - `--debug`: Enable debug output
- `--dev-tunnel`: Setup tunnel but don't launch SCP - `--dev-tunnel`: Setup tunnel but don't launch SCP
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
- **Advanced Logging**: Automatic log rotation with comprehensive monitoring - **Advanced Logging**: Automatic log rotation with comprehensive monitoring
- **Multiple Operating Modes**: Interactive, silent, bridge, script, and daemon modes - **Multiple Operating Modes**: Interactive, silent, bridge, script, and daemon modes
- **Enterprise Reliability**: Production-grade process supervision and high availability - **Enterprise Reliability**: Production-grade process supervision and high availability
- **Server Stability**: Robust error handling with comprehensive crash prevention and graceful client disconnection management
## Architecture ## Architecture
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "config.h" #include "config.h"
#include "websocket.h" #include "websocket.h"
#include "web.h" #include "web.h"
#include "ssl.h"
static volatile int shutdown_requested = 0; static volatile int shutdown_requested = 0;
...@@ -148,6 +149,9 @@ int main(int argc, char *argv[]) { ...@@ -148,6 +149,9 @@ int main(int argc, char *argv[]) {
websocket_free_state(state); websocket_free_state(state);
free_config(config); free_config(config);
// Clean up SSL
ssl_cleanup();
printf("WSSSH Daemon stopped cleanly\n"); printf("WSSSH Daemon stopped cleanly\n");
return 0; return 0;
} }
\ No newline at end of file
This diff is collapsed.
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <time.h> #include <time.h>
#include <pthread.h>
#include "config.h" #include "config.h"
#include "tunnel.h" #include "tunnel.h"
#include "terminal.h" #include "terminal.h"
...@@ -57,6 +58,10 @@ typedef struct { ...@@ -57,6 +58,10 @@ typedef struct {
bool debug; bool debug;
const char *server_password; const char *server_password;
time_t start_time; time_t start_time;
pthread_mutex_t client_mutex;
pthread_mutex_t tunnel_mutex;
pthread_mutex_t terminal_mutex;
} wssshd_state_t; } wssshd_state_t;
// Function declarations // Function declarations
......
This diff is collapsed.
...@@ -34,7 +34,9 @@ int send_tunnel_data_message(SSL *ssl, const char *request_id, const char *data_ ...@@ -34,7 +34,9 @@ int send_tunnel_data_message(SSL *ssl, const char *request_id, const char *data_
// Send as tunnel_data with size information // Send as tunnel_data with size information
size_t hex_len = strlen(data_hex); size_t hex_len = strlen(data_hex);
size_t binary_size = hex_len / 2; // Size of actual binary data size_t binary_size = hex_len / 2; // Size of actual binary data
size_t msg_size = strlen("{\"type\":\"tunnel_data\",\"request_id\":\"\",\"size\":,\"data\":\"\"}") + strlen(request_id) + 20 + hex_len + 1; size_t request_id_len = strlen(request_id);
size_t json_overhead = strlen("{\"type\":\"tunnel_data\",\"request_id\":\"\",\"size\":,\"data\":\"\"}");
size_t msg_size = json_overhead + request_id_len + 32 + hex_len + 1; // Extra 32 for safety
char *message = malloc(msg_size); char *message = malloc(msg_size);
if (!message) { if (!message) {
if (debug) { if (debug) {
...@@ -43,9 +45,17 @@ int send_tunnel_data_message(SSL *ssl, const char *request_id, const char *data_ ...@@ -43,9 +45,17 @@ int send_tunnel_data_message(SSL *ssl, const char *request_id, const char *data_
} }
return 0; return 0;
} }
snprintf(message, msg_size, int msg_len = snprintf(message, msg_size,
"{\"type\":\"tunnel_data\",\"request_id\":\"%s\",\"size\":%zu,\"data\":\"%s\"}", "{\"type\":\"tunnel_data\",\"request_id\":\"%s\",\"size\":%zu,\"data\":\"%s\"}",
request_id, binary_size, data_hex); request_id, binary_size, data_hex);
if (msg_len < 0 || (size_t)msg_len >= msg_size) {
if (debug) {
printf("[DEBUG] Failed to format tunnel_data message (msg_len=%d, msg_size=%zu)\n", msg_len, msg_size);
fflush(stdout);
}
free(message);
return 0;
}
if (!send_websocket_frame(ssl, message)) { if (!send_websocket_frame(ssl, message)) {
if (debug) { if (debug) {
...@@ -458,7 +468,9 @@ int send_tunnel_response_message(SSL *ssl, const char *request_id, const char *d ...@@ -458,7 +468,9 @@ int send_tunnel_response_message(SSL *ssl, const char *request_id, const char *d
// Send as tunnel_response (from target back to WebSocket) with size information // Send as tunnel_response (from target back to WebSocket) with size information
size_t hex_len = strlen(data_hex); size_t hex_len = strlen(data_hex);
size_t binary_size = hex_len / 2; // Size of actual binary data size_t binary_size = hex_len / 2; // Size of actual binary data
size_t msg_size = strlen("{\"type\":\"tunnel_response\",\"request_id\":\"\",\"size\":,\"data\":\"\"}") + strlen(request_id) + 20 + hex_len + 1; size_t request_id_len = strlen(request_id);
size_t json_overhead = strlen("{\"type\":\"tunnel_response\",\"request_id\":\"\",\"size\":,\"data\":\"\"}");
size_t msg_size = json_overhead + request_id_len + 32 + hex_len + 1; // Extra 32 for safety
char *message = malloc(msg_size); char *message = malloc(msg_size);
if (!message) { if (!message) {
if (debug) { if (debug) {
...@@ -467,9 +479,17 @@ int send_tunnel_response_message(SSL *ssl, const char *request_id, const char *d ...@@ -467,9 +479,17 @@ int send_tunnel_response_message(SSL *ssl, const char *request_id, const char *d
} }
return 0; return 0;
} }
snprintf(message, msg_size, int msg_len = snprintf(message, msg_size,
"{\"type\":\"tunnel_response\",\"request_id\":\"%s\",\"size\":%zu,\"data\":\"%s\"}", "{\"type\":\"tunnel_response\",\"request_id\":\"%s\",\"size\":%zu,\"data\":\"%s\"}",
request_id, binary_size, data_hex); request_id, binary_size, data_hex);
if (msg_len < 0 || (size_t)msg_len >= msg_size) {
if (debug) {
printf("[DEBUG] Failed to format tunnel_response message (msg_len=%d, msg_size=%zu)\n", msg_len, msg_size);
fflush(stdout);
}
free(message);
return 0;
}
if (!send_websocket_frame(ssl, message)) { if (!send_websocket_frame(ssl, message)) {
if (debug) { if (debug) {
......
...@@ -61,6 +61,7 @@ void print_usage(const char *program_name) { ...@@ -61,6 +61,7 @@ void print_usage(const char *program_name) {
fprintf(stderr, " --debug Enable debug output\n"); fprintf(stderr, " --debug Enable debug output\n");
fprintf(stderr, " --tunnel TYPES Transport types for data channel (comma-separated or 'any', default: any)\n"); fprintf(stderr, " --tunnel TYPES Transport types for data channel (comma-separated or 'any', default: any)\n");
fprintf(stderr, " --tunnel-control TYPES Transport types for control channel (comma-separated or 'any', default: any)\n"); fprintf(stderr, " --tunnel-control TYPES Transport types for control channel (comma-separated or 'any', default: any)\n");
fprintf(stderr, " --enc ENCODING Data encoding: hex, base64, or bin\n");
fprintf(stderr, " --help Show this help\n"); fprintf(stderr, " --help Show this help\n");
fprintf(stderr, "\nDestination format:\n"); fprintf(stderr, "\nDestination format:\n");
fprintf(stderr, " user@client_id[.wssshd_host]:/remote/path\n"); fprintf(stderr, " user@client_id[.wssshd_host]:/remote/path\n");
...@@ -80,6 +81,7 @@ int parse_wsscp_args(int argc, char *argv[], wsscp_wrapper_config_t *config) { ...@@ -80,6 +81,7 @@ int parse_wsscp_args(int argc, char *argv[], wsscp_wrapper_config_t *config) {
{"debug", no_argument, 0, 'd'}, {"debug", no_argument, 0, 'd'},
{"tunnel", required_argument, 0, 't'}, {"tunnel", required_argument, 0, 't'},
{"tunnel-control", required_argument, 0, 'T'}, {"tunnel-control", required_argument, 0, 'T'},
{"enc", required_argument, 0, 'e'},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
...@@ -87,7 +89,7 @@ int parse_wsscp_args(int argc, char *argv[], wsscp_wrapper_config_t *config) { ...@@ -87,7 +89,7 @@ int parse_wsscp_args(int argc, char *argv[], wsscp_wrapper_config_t *config) {
int opt; int opt;
int option_index = 0; int option_index = 0;
while ((opt = getopt_long(argc, argv, "c:H:P:dt:T:h", long_options, &option_index)) != -1) { while ((opt = getopt_long(argc, argv, "c:H:P:dt:T:e:h", long_options, &option_index)) != -1) {
switch (opt) { switch (opt) {
case 'c': case 'c':
config->client_id = strdup(optarg); config->client_id = strdup(optarg);
...@@ -108,6 +110,9 @@ int parse_wsscp_args(int argc, char *argv[], wsscp_wrapper_config_t *config) { ...@@ -108,6 +110,9 @@ int parse_wsscp_args(int argc, char *argv[], wsscp_wrapper_config_t *config) {
case 'T': case 'T':
config->tunnel_control = strdup(optarg); config->tunnel_control = strdup(optarg);
break; break;
case 'e':
config->enc = strdup(optarg);
break;
case 'h': case 'h':
print_usage(argv[0]); print_usage(argv[0]);
return 0; return 0;
...@@ -257,6 +262,13 @@ char *build_proxy_command(wsscp_wrapper_config_t *config) { ...@@ -257,6 +262,13 @@ char *build_proxy_command(wsscp_wrapper_config_t *config) {
strcat(cmd, tunnel_control_option); strcat(cmd, tunnel_control_option);
} }
// Add enc if specified
if (config->enc) {
char enc_option[32];
sprintf(enc_option, " --enc %s", config->enc);
strcat(cmd, enc_option);
}
// If --wssshd-port was not explicitly set, check for -P in SCP arguments // If --wssshd-port was not explicitly set, check for -P in SCP arguments
if (!config->wssshd_port_explicit) { if (!config->wssshd_port_explicit) {
int scp_port = parse_scp_port_from_args(config); int scp_port = parse_scp_port_from_args(config);
...@@ -333,6 +345,7 @@ int main(int argc, char *argv[]) { ...@@ -333,6 +345,7 @@ int main(int argc, char *argv[]) {
.debug = 0, .debug = 0,
.tunnel = NULL, .tunnel = NULL,
.tunnel_control = NULL, .tunnel_control = NULL,
.enc = NULL,
.user = NULL, .user = NULL,
.target_host = NULL, .target_host = NULL,
.ssh_string = NULL, .ssh_string = NULL,
......
...@@ -44,6 +44,7 @@ typedef struct { ...@@ -44,6 +44,7 @@ typedef struct {
int debug; int debug;
char *tunnel; char *tunnel;
char *tunnel_control; char *tunnel_control;
char *enc;
char *user; char *user;
char *target_host; char *target_host;
char *ssh_string; char *ssh_string;
......
...@@ -39,6 +39,7 @@ void print_wsssh_usage(const char *program_name) { ...@@ -39,6 +39,7 @@ void print_wsssh_usage(const char *program_name) {
fprintf(stderr, " --debug Enable debug output\n"); fprintf(stderr, " --debug Enable debug output\n");
fprintf(stderr, " --tunnel TRANSPORT Select data channel transport (comma-separated or 'any')\n"); fprintf(stderr, " --tunnel TRANSPORT Select data channel transport (comma-separated or 'any')\n");
fprintf(stderr, " --tunnel-control TRANSPORT Select control channel transport (comma-separated or 'any')\n"); fprintf(stderr, " --tunnel-control TRANSPORT Select control channel transport (comma-separated or 'any')\n");
fprintf(stderr, " --enc ENCODING Data encoding: hex, base64, or bin\n");
fprintf(stderr, "\nTarget format:\n"); fprintf(stderr, "\nTarget format:\n");
fprintf(stderr, " user[@clientid[.wssshd-host[:sshstring]]]\n"); fprintf(stderr, " user[@clientid[.wssshd-host[:sshstring]]]\n");
fprintf(stderr, "\nExamples:\n"); fprintf(stderr, "\nExamples:\n");
...@@ -81,6 +82,9 @@ int parse_wsssh_args(int argc, char *argv[], wsssh_wrapper_config_t *config) { ...@@ -81,6 +82,9 @@ int parse_wsssh_args(int argc, char *argv[], wsssh_wrapper_config_t *config) {
} else if (strcmp(argv[i], "--tunnel-control") == 0 && i + 1 < argc) { } else if (strcmp(argv[i], "--tunnel-control") == 0 && i + 1 < argc) {
config->tunnel_control = strdup(argv[i + 1]); config->tunnel_control = strdup(argv[i + 1]);
i++; i++;
} else if (strcmp(argv[i], "--enc") == 0 && i + 1 < argc) {
config->enc = strdup(argv[i + 1]);
i++;
} else if (argv[i][0] == '-') { } else if (argv[i][0] == '-') {
// Unknown option, treat as SSH option // Unknown option, treat as SSH option
remaining_start = i; remaining_start = i;
...@@ -236,6 +240,12 @@ char *build_proxy_command(wsssh_wrapper_config_t *config) { ...@@ -236,6 +240,12 @@ char *build_proxy_command(wsssh_wrapper_config_t *config) {
strcat(cmd, config->tunnel_control); strcat(cmd, config->tunnel_control);
} }
// Add enc if specified
if (config->enc) {
strcat(cmd, " --enc ");
strcat(cmd, config->enc);
}
// Add wssshd-port if not default // Add wssshd-port if not default
if (config->wssshd_port != 9898) { if (config->wssshd_port != 9898) {
char port_str[32]; char port_str[32];
...@@ -369,6 +379,7 @@ int main(int argc, char *argv[]) { ...@@ -369,6 +379,7 @@ int main(int argc, char *argv[]) {
free(config.wssshd_host); free(config.wssshd_host);
free(config.tunnel); free(config.tunnel);
free(config.tunnel_control); free(config.tunnel_control);
free(config.enc);
free(config.user); free(config.user);
free(config.ssh_string); free(config.ssh_string);
......
...@@ -35,6 +35,7 @@ typedef struct { ...@@ -35,6 +35,7 @@ typedef struct {
int debug; int debug;
char *tunnel; char *tunnel;
char *tunnel_control; char *tunnel_control;
char *enc;
char *user; char *user;
char *target_host; char *target_host;
char *ssh_string; char *ssh_string;
......
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