mirror of
https://github.com/m1ngsama/TNT.git
synced 2026-02-08 08:54:05 +00:00
fix(security): implement buffer security enhancements
- Replace all strcpy() calls with strncpy() to prevent buffer overflows - Add buffer overflow checking in client_printf() vsnprintf result - Implement UTF-8 sequence validation to prevent malformed input - Add utf8_is_valid_sequence() function with complete validation - Enhance read_username() with UTF-8 boundary checks - Add UTF-8 validation for message input handling These changes address: - Buffer overflow vulnerabilities (lines 178, 423, 510) - Insufficient vsnprintf() error checking (line 106) - Missing UTF-8 sequence validation (lines 156-171) Fixes prevent: - Buffer overflow attacks - Overlong UTF-8 encoding exploits - Invalid UTF-8 surrogates injection
This commit is contained in:
parent
abe477f713
commit
36464007e8
3 changed files with 85 additions and 3 deletions
|
|
@ -24,4 +24,7 @@ int utf8_strlen(const char *str);
|
||||||
/* Remove last UTF-8 character from string */
|
/* Remove last UTF-8 character from string */
|
||||||
void utf8_remove_last_char(char *str);
|
void utf8_remove_last_char(char *str);
|
||||||
|
|
||||||
|
/* Validate a UTF-8 byte sequence */
|
||||||
|
bool utf8_is_valid_sequence(const char *bytes, int len);
|
||||||
|
|
||||||
#endif /* UTF8_H */
|
#endif /* UTF8_H */
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,12 @@ int client_printf(client_t *client, const char *fmt, ...) {
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
int len = vsnprintf(buffer, sizeof(buffer), fmt, args);
|
int len = vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
|
/* Check for buffer overflow or encoding error */
|
||||||
|
if (len < 0 || len >= (int)sizeof(buffer)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return client_send(client, buffer, len);
|
return client_send(client, buffer, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,6 +161,10 @@ static int read_username(client_t *client) {
|
||||||
} else {
|
} else {
|
||||||
/* UTF-8 multi-byte */
|
/* UTF-8 multi-byte */
|
||||||
int len = utf8_byte_length(b);
|
int len = utf8_byte_length(b);
|
||||||
|
if (len <= 0 || len > 4) {
|
||||||
|
/* Invalid UTF-8 start byte */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
buf[0] = b;
|
buf[0] = b;
|
||||||
if (len > 1) {
|
if (len > 1) {
|
||||||
int read_bytes = ssh_channel_read(client->channel, &buf[1], len - 1, 0);
|
int read_bytes = ssh_channel_read(client->channel, &buf[1], len - 1, 0);
|
||||||
|
|
@ -163,6 +173,11 @@ static int read_username(client_t *client) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* Validate the complete UTF-8 sequence */
|
||||||
|
if (!utf8_is_valid_sequence(buf, len)) {
|
||||||
|
/* Invalid UTF-8 sequence */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (pos + len < MAX_USERNAME_LEN - 1) {
|
if (pos + len < MAX_USERNAME_LEN - 1) {
|
||||||
memcpy(username + pos, buf, len);
|
memcpy(username + pos, buf, len);
|
||||||
pos += len;
|
pos += len;
|
||||||
|
|
@ -175,7 +190,8 @@ static int read_username(client_t *client) {
|
||||||
client_printf(client, "\r\n");
|
client_printf(client, "\r\n");
|
||||||
|
|
||||||
if (username[0] == '\0') {
|
if (username[0] == '\0') {
|
||||||
strcpy(client->username, "anonymous");
|
strncpy(client->username, "anonymous", MAX_USERNAME_LEN - 1);
|
||||||
|
client->username[MAX_USERNAME_LEN - 1] = '\0';
|
||||||
} else {
|
} else {
|
||||||
strncpy(client->username, username, MAX_USERNAME_LEN - 1);
|
strncpy(client->username, username, MAX_USERNAME_LEN - 1);
|
||||||
/* Truncate to 20 characters */
|
/* Truncate to 20 characters */
|
||||||
|
|
@ -420,7 +436,8 @@ void* client_handle_session(void *arg) {
|
||||||
message_t join_msg = {
|
message_t join_msg = {
|
||||||
.timestamp = time(NULL),
|
.timestamp = time(NULL),
|
||||||
};
|
};
|
||||||
strcpy(join_msg.username, "系统");
|
strncpy(join_msg.username, "系统", MAX_USERNAME_LEN - 1);
|
||||||
|
join_msg.username[MAX_USERNAME_LEN - 1] = '\0';
|
||||||
snprintf(join_msg.content, MAX_MESSAGE_LEN, "%s 加入了聊天室", client->username);
|
snprintf(join_msg.content, MAX_MESSAGE_LEN, "%s 加入了聊天室", client->username);
|
||||||
room_broadcast(g_room, &join_msg);
|
room_broadcast(g_room, &join_msg);
|
||||||
|
|
||||||
|
|
@ -472,6 +489,10 @@ void* client_handle_session(void *arg) {
|
||||||
}
|
}
|
||||||
} else if (b >= 128) { /* UTF-8 multi-byte */
|
} else if (b >= 128) { /* UTF-8 multi-byte */
|
||||||
int char_len = utf8_byte_length(b);
|
int char_len = utf8_byte_length(b);
|
||||||
|
if (char_len <= 0 || char_len > 4) {
|
||||||
|
/* Invalid UTF-8 start byte */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
buf[0] = b;
|
buf[0] = b;
|
||||||
if (char_len > 1) {
|
if (char_len > 1) {
|
||||||
int read_bytes = ssh_channel_read(client->channel, &buf[1], char_len - 1, 0);
|
int read_bytes = ssh_channel_read(client->channel, &buf[1], char_len - 1, 0);
|
||||||
|
|
@ -480,6 +501,11 @@ void* client_handle_session(void *arg) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* Validate the complete UTF-8 sequence */
|
||||||
|
if (!utf8_is_valid_sequence(buf, char_len)) {
|
||||||
|
/* Invalid UTF-8 sequence */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
int len = strlen(input);
|
int len = strlen(input);
|
||||||
if (len + char_len < MAX_MESSAGE_LEN - 1) {
|
if (len + char_len < MAX_MESSAGE_LEN - 1) {
|
||||||
memcpy(input + len, buf, char_len);
|
memcpy(input + len, buf, char_len);
|
||||||
|
|
@ -507,7 +533,8 @@ cleanup:
|
||||||
message_t leave_msg = {
|
message_t leave_msg = {
|
||||||
.timestamp = time(NULL),
|
.timestamp = time(NULL),
|
||||||
};
|
};
|
||||||
strcpy(leave_msg.username, "系统");
|
strncpy(leave_msg.username, "系统", MAX_USERNAME_LEN - 1);
|
||||||
|
leave_msg.username[MAX_USERNAME_LEN - 1] = '\0';
|
||||||
snprintf(leave_msg.content, MAX_MESSAGE_LEN, "%s 离开了聊天室", client->username);
|
snprintf(leave_msg.content, MAX_MESSAGE_LEN, "%s 离开了聊天室", client->username);
|
||||||
|
|
||||||
client->connected = false;
|
client->connected = false;
|
||||||
|
|
|
||||||
52
src/utf8.c
52
src/utf8.c
|
|
@ -135,3 +135,55 @@ void utf8_remove_last_char(char *str) {
|
||||||
|
|
||||||
str[i] = '\0';
|
str[i] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Validate a UTF-8 byte sequence */
|
||||||
|
bool utf8_is_valid_sequence(const char *bytes, int len) {
|
||||||
|
if (len <= 0 || len > 4 || !bytes) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned char *b = (const unsigned char *)bytes;
|
||||||
|
|
||||||
|
/* Check first byte matches the expected length */
|
||||||
|
int expected_len = utf8_byte_length(b[0]);
|
||||||
|
if (expected_len != len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate continuation bytes (must be 10xxxxxx) */
|
||||||
|
for (int i = 1; i < len; i++) {
|
||||||
|
if ((b[i] & 0xC0) != 0x80) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate codepoint ranges to prevent overlong encodings */
|
||||||
|
uint32_t codepoint = 0;
|
||||||
|
switch (len) {
|
||||||
|
case 1:
|
||||||
|
/* 0xxxxxxx - valid range: 0x00-0x7F */
|
||||||
|
codepoint = b[0];
|
||||||
|
if (codepoint > 0x7F) return false;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/* 110xxxxx 10xxxxxx - valid range: 0x80-0x7FF */
|
||||||
|
codepoint = ((b[0] & 0x1F) << 6) | (b[1] & 0x3F);
|
||||||
|
if (codepoint < 0x80 || codepoint > 0x7FF) return false;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
/* 1110xxxx 10xxxxxx 10xxxxxx - valid range: 0x800-0xFFFF */
|
||||||
|
codepoint = ((b[0] & 0x0F) << 12) | ((b[1] & 0x3F) << 6) | (b[2] & 0x3F);
|
||||||
|
if (codepoint < 0x800 || codepoint > 0xFFFF) return false;
|
||||||
|
/* Reject UTF-16 surrogates (0xD800-0xDFFF) */
|
||||||
|
if (codepoint >= 0xD800 && codepoint <= 0xDFFF) return false;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
/* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - valid range: 0x10000-0x10FFFF */
|
||||||
|
codepoint = ((b[0] & 0x07) << 18) | ((b[1] & 0x3F) << 12) |
|
||||||
|
((b[2] & 0x3F) << 6) | (b[3] & 0x3F);
|
||||||
|
if (codepoint < 0x10000 || codepoint > 0x10FFFF) return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue