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.
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).
## [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
### Fixed
......
......@@ -43,6 +43,7 @@ WSSSH is a universal tunneling system that provides secure access to remote mach
- **Advanced Logging**: Automatic log rotation and monitoring
- **Multiple Operating Modes**: Interactive, silent, bridge, script, and daemon modes
- **Enterprise Reliability**: Production-grade process supervision
- **Server Stability**: Robust error handling with comprehensive crash prevention and graceful client disconnection management
## Operating Modes
......@@ -314,6 +315,7 @@ wsssh [options] user@client.domain [ssh_options...]
- `--tunnel TRANSPORT`: Transport for data channel
- `--tunnel-control TRANSPORT`: Transport for control channel
- `--service SERVICE`: Service type (default: ssh)
- `--enc ENCODING`: Data encoding: hex, base64, or bin (default: hex)
- `--debug`: Enable debug output
- `--dev-tunnel`: Setup tunnel but don't launch SSH
......@@ -329,6 +331,7 @@ wsscp [options] [scp_options...] source destination
- `--tunnel TRANSPORT`: Transport for data channel
- `--tunnel-control TRANSPORT`: Transport for control channel
- `--service SERVICE`: Service type (default: ssh)
- `--enc ENCODING`: Data encoding: hex, base64, or bin (default: hex)
- `--debug`: Enable debug output
- `--dev-tunnel`: Setup tunnel but don't launch SCP
......
......@@ -19,6 +19,7 @@
- **Advanced Logging**: Automatic log rotation with comprehensive monitoring
- **Multiple Operating Modes**: Interactive, silent, bridge, script, and daemon modes
- **Enterprise Reliability**: Production-grade process supervision and high availability
- **Server Stability**: Robust error handling with comprehensive crash prevention and graceful client disconnection management
## Architecture
......
......@@ -26,6 +26,7 @@
#include "config.h"
#include "websocket.h"
#include "web.h"
#include "ssl.h"
static volatile int shutdown_requested = 0;
......@@ -148,6 +149,9 @@ int main(int argc, char *argv[]) {
websocket_free_state(state);
free_config(config);
// Clean up SSL
ssl_cleanup();
printf("WSSSH Daemon stopped cleanly\n");
return 0;
}
\ No newline at end of file
This diff is collapsed.
......@@ -22,6 +22,7 @@
#include <stdbool.h>
#include <time.h>
#include <pthread.h>
#include "config.h"
#include "tunnel.h"
#include "terminal.h"
......@@ -57,6 +58,10 @@ typedef struct {
bool debug;
const char *server_password;
time_t start_time;
pthread_mutex_t client_mutex;
pthread_mutex_t tunnel_mutex;
pthread_mutex_t terminal_mutex;
} wssshd_state_t;
// Function declarations
......
This diff is collapsed.
......@@ -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
size_t hex_len = strlen(data_hex);
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);
if (!message) {
if (debug) {
......@@ -43,9 +45,17 @@ int send_tunnel_data_message(SSL *ssl, const char *request_id, const char *data_
}
return 0;
}
snprintf(message, msg_size,
int msg_len = snprintf(message, msg_size,
"{\"type\":\"tunnel_data\",\"request_id\":\"%s\",\"size\":%zu,\"data\":\"%s\"}",
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 (debug) {
......@@ -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
size_t hex_len = strlen(data_hex);
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);
if (!message) {
if (debug) {
......@@ -467,9 +479,17 @@ int send_tunnel_response_message(SSL *ssl, const char *request_id, const char *d
}
return 0;
}
snprintf(message, msg_size,
int msg_len = snprintf(message, msg_size,
"{\"type\":\"tunnel_response\",\"request_id\":\"%s\",\"size\":%zu,\"data\":\"%s\"}",
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 (debug) {
......
......@@ -61,6 +61,7 @@ void print_usage(const char *program_name) {
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-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, "\nDestination format:\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) {
{"debug", no_argument, 0, 'd'},
{"tunnel", required_argument, 0, 't'},
{"tunnel-control", required_argument, 0, 'T'},
{"enc", required_argument, 0, 'e'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
......@@ -87,7 +89,7 @@ int parse_wsscp_args(int argc, char *argv[], wsscp_wrapper_config_t *config) {
int opt;
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) {
case 'c':
config->client_id = strdup(optarg);
......@@ -108,6 +110,9 @@ int parse_wsscp_args(int argc, char *argv[], wsscp_wrapper_config_t *config) {
case 'T':
config->tunnel_control = strdup(optarg);
break;
case 'e':
config->enc = strdup(optarg);
break;
case 'h':
print_usage(argv[0]);
return 0;
......@@ -257,6 +262,13 @@ char *build_proxy_command(wsscp_wrapper_config_t *config) {
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 (!config->wssshd_port_explicit) {
int scp_port = parse_scp_port_from_args(config);
......@@ -333,6 +345,7 @@ int main(int argc, char *argv[]) {
.debug = 0,
.tunnel = NULL,
.tunnel_control = NULL,
.enc = NULL,
.user = NULL,
.target_host = NULL,
.ssh_string = NULL,
......
......@@ -44,6 +44,7 @@ typedef struct {
int debug;
char *tunnel;
char *tunnel_control;
char *enc;
char *user;
char *target_host;
char *ssh_string;
......
......@@ -39,6 +39,7 @@ void print_wsssh_usage(const char *program_name) {
fprintf(stderr, " --debug Enable debug output\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, " --enc ENCODING Data encoding: hex, base64, or bin\n");
fprintf(stderr, "\nTarget format:\n");
fprintf(stderr, " user[@clientid[.wssshd-host[:sshstring]]]\n");
fprintf(stderr, "\nExamples:\n");
......@@ -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) {
config->tunnel_control = strdup(argv[i + 1]);
i++;
} else if (strcmp(argv[i], "--enc") == 0 && i + 1 < argc) {
config->enc = strdup(argv[i + 1]);
i++;
} else if (argv[i][0] == '-') {
// Unknown option, treat as SSH option
remaining_start = i;
......@@ -236,6 +240,12 @@ char *build_proxy_command(wsssh_wrapper_config_t *config) {
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
if (config->wssshd_port != 9898) {
char port_str[32];
......@@ -369,6 +379,7 @@ int main(int argc, char *argv[]) {
free(config.wssshd_host);
free(config.tunnel);
free(config.tunnel_control);
free(config.enc);
free(config.user);
free(config.ssh_string);
......
......@@ -35,6 +35,7 @@ typedef struct {
int debug;
char *tunnel;
char *tunnel_control;
char *enc;
char *user;
char *target_host;
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