mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 07:54:39 +08:00
Move the per-connection SSH bootstrap pipeline -- key exchange, auth,
channel open + PTY/shell-or-exec request, and the hand-off into a
client_t -- out of ssh_server.c into a dedicated module.
Migrated to bootstrap.{c,h}:
- session_context_t (now private to bootstrap.c)
- accepted_session_t (declared in bootstrap.h, the IPC envelope from
the accept loop into the bootstrap thread)
- TNT_ACCESS_TOKEN handling: g_access_token + bootstrap_init()
- constant_time_strcmp (auth-only utility)
- bootstrap_peer_ip (peer IP read from libssh fd)
- auth_password / auth_none / auth_pubkey
- destroy_session_context, cleanup_failed_session
- channel_open_request_session, channel_pty_request,
channel_pty_window_change, channel_shell_request, channel_exec_request
- setup_session_channel_callbacks
- bootstrap_run (formerly bootstrap_client_session, the pthread entry)
Stayed in ssh_server.c:
- accept loop in ssh_server_start (now calls bootstrap_peer_ip and
pthread_create(bootstrap_run))
- ssh_server_init (now calls ratelimit_init() + bootstrap_init() +
reads only g_idle_timeout / TNT_BIND_ADDR / TNT_SSH_LOG_LEVEL)
- client_send/printf/addref/release, notify_mentions
- client_channel_window_change/eof/close (post-bootstrap, target client_t)
- client_install_channel_callbacks (renamed from
install_client_channel_callbacks, now non-static and exposed via
ssh_server.h so bootstrap.c can install them on the new client_t)
- read_username, handle_key, client_handle_session (will move to
input.c in PR2-M5)
- setup_host_key, ssh_server_start_time
Two helpers also lifted: sanitize_terminal_size moved to common.c (used
by the bootstrap PTY callback and the post-bootstrap window-change
callback), and is_valid_username already lived there from M2.
ssh_server.c shrinks from 1513 to 1026 lines (-487).
Behaviour is preserved: implementations are byte-for-byte the same.
176 lines
3.9 KiB
C
176 lines
3.9 KiB
C
#include "common.h"
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifndef PATH_MAX
|
|
#define PATH_MAX 4096
|
|
#endif
|
|
|
|
const char* tnt_state_dir(void) {
|
|
const char *dir = getenv("TNT_STATE_DIR");
|
|
|
|
if (!dir || dir[0] == '\0') {
|
|
return TNT_DEFAULT_STATE_DIR;
|
|
}
|
|
|
|
return dir;
|
|
}
|
|
|
|
int tnt_ensure_state_dir(void) {
|
|
const char *dir = tnt_state_dir();
|
|
char path[PATH_MAX];
|
|
struct stat st;
|
|
size_t len;
|
|
|
|
if (!dir || dir[0] == '\0') {
|
|
return -1;
|
|
}
|
|
|
|
if (strcmp(dir, ".") == 0 || strcmp(dir, "/") == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (snprintf(path, sizeof(path), "%s", dir) >= (int)sizeof(path)) {
|
|
return -1;
|
|
}
|
|
|
|
len = strlen(path);
|
|
while (len > 1 && path[len - 1] == '/') {
|
|
path[len - 1] = '\0';
|
|
len--;
|
|
}
|
|
|
|
for (char *p = path + 1; *p; p++) {
|
|
if (*p != '/') {
|
|
continue;
|
|
}
|
|
*p = '\0';
|
|
if (mkdir(path, 0700) < 0 && errno != EEXIST) {
|
|
return -1;
|
|
}
|
|
*p = '/';
|
|
}
|
|
|
|
if (mkdir(path, 0700) < 0 && errno != EEXIST) {
|
|
return -1;
|
|
}
|
|
|
|
if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode)) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tnt_state_path(char *buffer, size_t buf_size, const char *filename) {
|
|
const char *dir;
|
|
int written;
|
|
|
|
if (!buffer || buf_size == 0 || !filename || filename[0] == '\0') {
|
|
return -1;
|
|
}
|
|
|
|
dir = tnt_state_dir();
|
|
|
|
if (strcmp(dir, "/") == 0) {
|
|
written = snprintf(buffer, buf_size, "/%s", filename);
|
|
} else {
|
|
written = snprintf(buffer, buf_size, "%s/%s", dir, filename);
|
|
}
|
|
|
|
if (written < 0 || (size_t)written >= buf_size) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void buffer_append_bytes(char *buffer, size_t buf_size, size_t *pos,
|
|
const char *data, size_t len) {
|
|
size_t available;
|
|
size_t to_copy;
|
|
|
|
if (!buffer || !pos || !data || len == 0 || buf_size == 0 ||
|
|
*pos >= buf_size - 1) {
|
|
return;
|
|
}
|
|
|
|
available = (buf_size - 1) - *pos;
|
|
to_copy = (len < available) ? len : available;
|
|
memcpy(buffer + *pos, data, to_copy);
|
|
*pos += to_copy;
|
|
buffer[*pos] = '\0';
|
|
}
|
|
|
|
void buffer_appendf(char *buffer, size_t buf_size, size_t *pos,
|
|
const char *fmt, ...) {
|
|
va_list args;
|
|
int written;
|
|
|
|
if (!buffer || !pos || !fmt || buf_size == 0 || *pos >= buf_size - 1) {
|
|
return;
|
|
}
|
|
|
|
va_start(args, fmt);
|
|
written = vsnprintf(buffer + *pos, buf_size - *pos, fmt, args);
|
|
va_end(args);
|
|
|
|
if (written < 0) {
|
|
return;
|
|
}
|
|
|
|
if ((size_t)written >= buf_size - *pos) {
|
|
*pos = buf_size - 1;
|
|
} else {
|
|
*pos += (size_t)written;
|
|
}
|
|
}
|
|
|
|
int env_int(const char *name, int fallback, int min_val, int max_val) {
|
|
const char *env = getenv(name);
|
|
if (!env || env[0] == '\0') return fallback;
|
|
char *end;
|
|
long val = strtol(env, &end, 10);
|
|
if (*end != '\0' || val < min_val || val > max_val) return fallback;
|
|
return (int)val;
|
|
}
|
|
|
|
bool is_valid_username(const char *username) {
|
|
if (!username || username[0] == '\0') {
|
|
return false;
|
|
}
|
|
|
|
/* Reject usernames starting with special characters */
|
|
if (username[0] == ' ' || username[0] == '.' || username[0] == '-') {
|
|
return false;
|
|
}
|
|
|
|
/* Check for illegal characters that could cause injection */
|
|
const char *illegal_chars = "|;&$`\n\r<>(){}[]'\"\\";
|
|
for (size_t i = 0; i < strlen(username); i++) {
|
|
/* Reject control characters (except tab) */
|
|
if (username[i] < 32 && username[i] != 9) {
|
|
return false;
|
|
}
|
|
/* Reject shell metacharacters */
|
|
if (strchr(illegal_chars, username[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void sanitize_terminal_size(int *width, int *height) {
|
|
if (!width || !height) {
|
|
return;
|
|
}
|
|
|
|
if (*width <= 0 || *width > 500) {
|
|
*width = 80;
|
|
}
|
|
if (*height <= 0 || *height > 200) {
|
|
*height = 24;
|
|
}
|
|
}
|