Commit graph

56 commits

Author SHA1 Message Date
e2b16cf477 tui: lighter, segmented title bar (M7-2)
Replace the full-width reverse-video title

    ▍ tester | 在线: 3 | 模式: INSERT | ? 帮助 ▎  (reversed)

with a single line of small chips on a normal background:

     tester · 在线 3 · INSERT                  ? 帮助

- username is bold white, online count is plain
- mode name carries its own colour, so glance + colour disambiguates
  it (INSERT cyan, NORMAL yellow, COMMAND magenta, HELP blue) without
  having to read the word
- separators are dim grey middle-dots so the eye groups segments
- mute marker shrinks from "[静音]" to a dim "静音" suffix when active
- hint "? 帮助" is right-aligned in dim grey, far from content

The horizontal rule below the chat already provides the visual divider
that the reverse-video bar used to provide, so the bar can drop the
heaviness without losing structure.
2026-05-17 12:54:27 +08:00
159073ec49 tui: framed welcome banner before username prompt (M7-1)
Replace the four lines of `==` rules + bilingual title with a centred
rounded box so the eye lands on a single visual element instead of two.

The new tui_render_welcome():
- centres horizontally and vertically (about a third of the way down)
- shows TNT + version, then 中文 subtitle, then English caption with
  decreasing visual weight (bold cyan / default / dim grey)
- falls back to a plain "TNT $version — anonymous chat over SSH" line
  when the terminal is too narrow for the frame
- declared in tui.h, called from input.c read_username()

read_username()'s prompt becomes a single short bilingual hint
("请输入用户名 (留空 anonymous): ") that lives below the box.
2026-05-17 10:48:27 +08:00
7897d8820e refactor: extract client module (PR2-M6)
Move client_t I/O and lifecycle out of ssh_server.c into a dedicated
module.  ssh_server.c is now down to the listening socket, host key
setup, ssh_server_init / ssh_server_start, and the accept loop.

Migrated to client.{c,h}:
- client_send, client_printf
- client_addref, client_release (and the ssh_session / ssh_channel /
  channel_cb teardown that fires on the final release)
- client_install_channel_callbacks
- client_channel_window_change / _eof / _close (the post-bootstrap
  callbacks that target the client_t)

The client_t struct definition stays in ssh_server.h so we don't have
to revisit every existing #include chain.  bootstrap, commands, exec,
input, and tui pick up the new client.h alongside their existing
ssh_server.h include.

ssh_server.c shrinks from 402 to 249 lines (-153).

Final per-module size after PR2:
  ssh_server.c   249  accept loop + ssh_server_init/start + host key
  bootstrap.c    493  per-connection SSH handshake / auth / channel
  client.c       162  client_t I/O + lifecycle + channel callbacks
  input.c        637  username read + handle_key + main loop +
                      notify_mentions + idle timeout
  commands.c     269  vim ':' command dispatcher
  exec.c         453  SSH exec subcommand dispatcher
  ratelimit.c    197  IP rate limit + connection counters
  tui.c          614  screen rendering
  chat_room.c    151  room + client list
  message.c      350  message log load/save/search
  utf8.c         250  UTF-8 width / validation
  common.c       133  buffer_*, env_int, is_valid_username,
                      sanitize_terminal_size, tnt_state_*
  main.c         109  process entry

Total: ~4 000 lines of C across 13 files, no file over 650 lines, every
file is single-purpose.  Behaviour is preserved: the migrated code is
byte-for-byte identical.
2026-05-17 10:16:27 +08:00
3b8a1d18d8 refactor: extract input module (PR2-M5)
Move the interactive session — username read, vim-style key dispatcher,
main poll/redraw/keepalive/idle-timeout loop, the @mention bell, and
the tear-down path — out of ssh_server.c into a dedicated module.

New API (include/input.h):
- input_init()                 -- read TNT_IDLE_TIMEOUT once at startup
- input_run_session(client_t*) -- run the interactive session for an
                                  already-bootstrapped client_t,
                                  including exec_dispatch() short-circuit
                                  when client->exec_command is set
- notify_mentions(content, sender) -- moved from ssh_server.h since
                                  the INSERT-mode send path lives here
                                  too

Renamed: client_handle_session(void *arg) -> input_run_session(client_t *).
The old void* signature was a fossil from when this was a pthread entry;
bootstrap.c calls it synchronously inside its own detached thread.
bootstrap_run() now ends with `input_run_session(client); return NULL;`.

g_idle_timeout is now private to input.c; ssh_server_init() calls
input_init() instead of reading the env directly.

ssh_server.c shrinks from 1026 to 402 lines (-624) -- now down to just
the accept loop, ssh_server_init / ssh_server_start, host-key setup,
client_t I/O API (send/printf/addref/release), and the post-bootstrap
client_channel_* callbacks.  M6 will give those a proper home.

Behaviour is preserved: all moved code is byte-for-byte identical.
2026-05-17 10:01:48 +08:00
b5f9a17290 refactor: extract bootstrap module (PR2-M4)
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.
2026-05-17 09:47:28 +08:00
8aa34c75b8 refactor: extract commands module (PR2-M3)
Move the vim-mode `:` command dispatcher (`:list`, `:nick`, `:msg`,
`:last`, `:search`, `:mute-joins`, `:help`, `:clear`, `:q`, …) out of
ssh_server.c into a dedicated module.

New API (include/commands.h):
- commands_dispatch(client_t *)  -- single entry point invoked from
  handle_key when Enter is pressed in MODE_COMMAND.

ssh_server.c shrinks from 1769 to 1513 lines (-256).
Behaviour preserved: implementation is byte-for-byte the same.
2026-05-17 09:26:48 +08:00
7f9babf4f4 refactor: extract exec module (PR2-M2)
Move the SSH exec subcommand interface (help, health, users, stats,
tail, post) and the dispatcher out of ssh_server.c into a dedicated
module.

New API (include/exec.h):
- exec_dispatch(client_t *)  -- single entry point invoked from the
  bootstrap path when client->exec_command[0] != '\0'.

Helpers that travel with the exec subcommands:
- format_timestamp_utc, trim_ascii_whitespace, json_append_string,
  resolve_exec_username, parse_tail_count

Two cross-module bridges:
- is_valid_username() lifted into common.c/h since exec, the input
  read path, and the :nick command all need it.
- ssh_server_start_time() added to ssh_server.h as a read-only
  accessor; exec_command_stats no longer reaches into the global.
- notify_mentions stays in ssh_server.c for now and is exposed via
  ssh_server.h.  Will move to a dedicated client.c during PR2-M6.

ssh_server.c shrinks from 2200 to 1769 lines (-431).
Behaviour is preserved: implementations are byte-for-byte the same.
2026-05-17 08:49:58 +08:00
562ee5296d refactor: extract ratelimit module (PR2-M1)
Move IP rate-limiting, auth-failure tracking, and global connection
counting out of ssh_server.c into a dedicated module.

New API (include/ratelimit.h):
- ratelimit_init()
- ratelimit_check_ip() / ratelimit_release_ip()
- ratelimit_record_auth_failure()
- ratelimit_check_and_increment_total() / ratelimit_decrement_total()
- ratelimit_get_active_total()  (replaces the direct g_total_connections
  read that exec_command_stats was doing under g_conn_count_lock)

env_int() also moves up to common.{c,h} since multiple modules need it.

ssh_server.c drops from 2469 to 2200 lines.  Behaviour is preserved:
the new functions are byte-for-byte the same implementations, only the
file boundary moved.

g_idle_timeout and g_access_token reads stay inline in ssh_server_init()
for now; they will follow the auth.c and input.c extractions later.
2026-05-16 23:06:56 +08:00
d9382882d1 chore: bug fixes and code cleanup
Fixes:
- message_load() now holds g_message_file_lock for the read, so :last [N]
  can no longer observe a half-written line while message_save() is
  flushing.
- constant_time_strcmp() accumulates the length difference in size_t.
  The old code truncated to unsigned char, which collapsed pairs whose
  lengths differed by a multiple of 256 down to 0 and lost the signal.

Refactor:
- buffer_appendf() / buffer_append_bytes() moved to common.c; the two
  identical copies in ssh_server.c and tui.c have been removed.

Docs / cleanup:
- README clarifies that exec 'post' uses the SSH login name as the
  author and that anonymous mode performs no identity check.
- Removed TODO.md (both items completed) and docs/README.old.
- Trimmed the auto-generated 2025 entry block from docs/CHANGELOG.md
  and added a 2026-05-16 entry summarising this change.
2026-05-16 22:44:41 +08:00
ed5fc43cbd feat: add :last, :search, :mute-joins commands and MOTD support
- :last [N]: show last N messages from log (max 50, default 10)
- :search <keyword>: case-insensitive full-text search across log history
- :mute-joins: per-client toggle to silence join/leave system messages,
  indicated by [静音] in the title bar
- MOTD: display motd.txt from state directory on connect before entering chat
- Add message_search() to message.c/h for log file scanning
- Update :help and tui help screen (EN/ZH) with new commands
2026-04-23 12:03:27 +08:00
bb77c77b8f feat: add @mention notifications, idle timeout, and online duration
- @mention: typing @username in a message sends bell char to that user
  and highlights the message content in bold yellow for them
- Idle timeout: disconnect inactive clients after TNT_IDLE_TIMEOUT
  seconds (default 1800 = 30min, 0 to disable)
- :list now shows connection duration per user (e.g. "alice (12m)")
- Document all three features in help text, manpage, and README

Closes #46
2026-04-19 23:12:45 +08:00
ee3f89f10a feat: improve TUI visual experience with colors and indicators
- Color-code usernames using hash-based ANSI color assignment (6 colors)
- Style system messages (join/leave/rename) in gray with --> prefix
- Style /me action messages in italic cyan
- Show "↓ N new" indicator in yellow when scrolled up in NORMAL mode
- Compact timestamps from full date to HH:MM for more message space

Closes #44
2026-04-19 22:56:38 +08:00
b07348c6e1 docs: update all user-facing help text and documentation
- Add /me action command to all help surfaces (EN/ZH help screen,
  :help output, exec help, manpage, README)
- Add Ctrl+D/U/F/B page scrolling keys to help text (were only in manpage)
- Add :q/:quit/:exit disconnect command to help text
- Update README COMMAND mode section with all current commands
  (:nick, :msg, :w were missing)
- Remove redundant COMMAND MODE KEYS section from help text
  (merged into AVAILABLE COMMANDS for clarity)
- Compact help screen layout (j/k on one line, g/G on one line)
2026-04-19 22:08:59 +08:00
6dfd66ed66 fix: suppress GCC format-truncation warning in exec /me handler 2026-04-19 19:05:57 +08:00
14789cd1c8 fix: guard terminal width/height in all TUI renderers and harden edge cases
- Replace all direct client->width/height reads with local variables
  clamped to minimums (width>=10, height>=4) across tui_render_screen,
  tui_render_input, tui_render_command_output, and tui_render_help
- Fix tui_render_input underflow when width < 3 (avail = max(rw-3, 1))
- Show username in title bar instead of generic label
- Add /me action message support in exec_command_post for scripting parity
- Reject empty usernames when loading messages from log file
2026-04-19 18:58:51 +08:00
9060259558 feat: add --port, --version long options and improved --help output
- Add --port as alias for -p
- Add -V/--version flag
- Improve --help with environment variable documentation
- Update manpage with long option forms
2026-04-19 18:37:38 +08:00
e2990000e6 feat: add :nick command and /me action messages
- :nick/:name <name>: change username in-session with full validation,
  thread-safe update under write lock, and system broadcast
- /me <action>: IRC-style action messages displayed as "* user action"
- Updated help text (EN/ZH) and manpage with new commands

Closes #38
2026-04-19 18:34:02 +08:00
450f1828fd
Merge pull request #37 from m1ngsama/fix/deadlock-uaf-logrotate-tail
fix: deadlock, use-after-free, log rotation, and tail parsing
2026-04-19 18:30:45 +08:00
b1c1e5a894 fix: deadlock in whisper, use-after-free in callbacks, log rotation, tail parsing
- Whisper: copy target client ref out of room lock before calling
  client_send, preventing lock-ordering inversion deadlock
- Channel callbacks: call ssh_remove_channel_callbacks before releasing
  refs to prevent use-after-free if a callback fires during cleanup
- Log rotation: rotate messages.log to messages.log.1 when it exceeds
  10 MiB, preventing unbounded growth on public servers
- tail -nN: accept both "tail -n5" and "tail -n 5" forms, matching
  standard Unix tail behavior

Closes #36
2026-04-19 18:27:54 +08:00
629812a2d8 fix: correct pubkey auth response, strncpy warning, and NUL byte validation
- auth_pubkey: return SSH_AUTH_SUCCESS for key offers instead of
  SSH_AUTH_PARTIAL, which incorrectly signals partial authentication
- command history: replace strncpy with snprintf to eliminate
  -Wstringop-truncation warning on GCC
- utf8_is_valid_sequence: reject NUL byte (0x00) in single-byte
  validation to prevent C string truncation attacks

Closes #34
2026-04-19 18:27:50 +08:00
e10b43074c feat: consolidated improvements, manpage, and deployment prep
Bug fixes:
- Fix data race on client->width/height (now _Atomic int)
- Persist join/leave system messages via message_save()
- Make room_add_message static to enforce lock contract
- Fix execute_command mutating command_input directly
- Increase help_copy buffer from 4096 to 8192 for CJK safety

New features:
- Add :msg/:w whisper command for private messaging
- Add command history with UP/DOWN arrows in command mode
- Add Ctrl+D/U/F/B page scrolling in normal mode
- Add :q/:quit/:exit Vim-style disconnect

Unix community:
- Add tnt.1 manpage (roff format) with full documentation
- Add manpage install/uninstall to Makefile
2026-04-19 17:49:06 +08:00
200e5a2f28
Merge pull request #22 from m1ngsama/feat/expand-unit-tests
Add chat_room unit tests and integrate into CI
2026-04-19 17:39:25 +08:00
65cb5d79d7
Merge pull request #24 from m1ngsama/fix/input-handling-and-auth-hardening
Fix CJK input handling and reduce auth timeout
2026-04-19 17:39:00 +08:00
83e964028a
Merge pull request #20 from m1ngsama/fix/edge-cases-and-robustness
Fix edge cases in message loading and network I/O
2026-04-19 17:38:51 +08:00
9607d8c2f2 fix: CJK backspace display, UTF-8 in command mode, auth timeout
- Fix backspace in read_username to erase correct display width for
  CJK/wide characters (was erasing only 1 column for 2-column chars)
- Add UTF-8 multi-byte input support in COMMAND mode (was silently
  dropping non-ASCII bytes, breaking CJK command arguments)
- Reduce SSH auth timeout from 30s to 10s to limit connection-slot
  exhaustion from slow/malicious handshakes
2026-04-19 16:19:43 +08:00
ecc45f285c test: add chat_room unit tests and integrate into build
- Add 11 unit tests for chat_room.c covering: create/destroy, message
  add/overflow, broadcast sequence, get_message bounds, client
  add/remove/capacity, and null argument handling
- Add unit-test target to root Makefile so `make test` runs unit tests
  before integration tests
- Add common.c to unit test link dependencies (needed for tnt_state_path)
- Guard _DARWIN_C_SOURCE define to prevent -Wmacro-redefined warning
2026-04-19 15:22:01 +08:00
8be6476367 fix: harden edge cases in message loading and network I/O
- Check ftell() return for errors (-1) in message_load to prevent
  corrupted backward scan on I/O failures
- Cap ssh_channel_write chunks to 32KB to prevent size_t-to-uint32_t
  narrowing on large buffers
- Log evicted active connection count in rate-limit table overflow
  warning for better diagnostics
2026-04-19 15:18:09 +08:00
9bbd5acd15 fix: resolve memory safety bugs and timing side-channel
- Fix use-after-free/double-free on install_client_channel_callbacks
  failure: nullify session/channel ownership before releasing refs so
  cleanup_failed_session does not double-free resources
- Fix constant_time_strcmp to always iterate over the full secret length,
  preventing timing leak of token length
- Fix data race on client->width/height by protecting window-change
  callback writes with io_lock
- Fix potential UTF-8 mid-sequence truncation in tui_render_input by
  sizing display buffer to MAX_MESSAGE_LEN
2026-04-19 14:08:31 +08:00
0de13a6314 fix: add _DARWIN_C_SOURCE for timegm() on macOS CI
Some checks failed
CI / build-and-test (macos-latest) (push) Has been cancelled
CI / build-and-test (ubuntu-latest) (push) Has been cancelled
Deploy / test (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
2026-04-15 10:15:32 +08:00
d745a8e1fe fix: address security vulnerabilities and design flaws from comprehensive audit
Critical fixes:
- C-1: Use atomic_bool for client->connected and redraw_pending to prevent
  data races between callback and main threads
- C-2: Add reference counting for channel callbacks to prevent use-after-free
  when callbacks fire during client cleanup
- C-3/M-7: Use ssh_channel_read_timeout (5s) for UTF-8 continuation bytes
  to prevent thread blocking and stream desynchronization

High-severity fixes:
- H-1: Replace non-thread-safe setenv/tzset with timegm() in parse_rfc3339_utc
- H-2: Change room_get_message to return by value copy instead of interior pointer
- H-3: Log warning when rate-limit table evicts active IP entry
- H-4: Replace strcmp with constant-time comparison for access token validation
- H-5: Check signature_state in auth_pubkey to reject unsigned key offers

Medium/low fixes:
- M-1: Replace all atoi() with strtol() for proper error detection
- M-3: Move calloc outside rwlock in tui_render_screen to avoid blocking writers
- M-8: Fix off-by-one in rate limit threshold (> to >=)
- M-9: Trim partial UTF-8 sequences after snprintf truncation in message_format
- L-1: Validate continuation byte mask (0xC0==0x80) in utf8_decode
- D-3: Remove vestigial client_t.fd field
- L-3: Remove unreachable pthread_attr_destroy after infinite loop
2026-04-15 10:13:17 +08:00
6c6c500134 fix: reject unknown command-line arguments instead of silently ignoring them 2026-04-15 09:51:49 +08:00
cb106de31b fix: separate per-ip concurrency from connection rate 2026-03-10 19:08:28 +08:00
e473b26e0d refactor: stabilize SSH runtime and add exec interface 2026-03-10 18:52:20 +08:00
da81e17187 fix: resolve crash and hang causes found in production audit
Fixes #10.

Five bugs that caused the server to crash or become unresponsive:

1. Signal handler deadlock (main.c)
   signal_handler called room_destroy (pthread_rwlock + free) and printf —
   neither is async-signal-safe. If SIGTERM arrived while any thread held
   g_room->lock, the process deadlocked permanently.
   Fix: handler now only writes a message via write(2) and calls _exit(0).
   Also remove close(g_listen_fd) which was closing stdin (fd 0), since
   ssh_server_init returns 0 on success, not a real file descriptor.

2. NULL dereference in room_broadcast when room is empty (chat_room.c)
   calloc(0, n) may return NULL per POSIX; memcpy on NULL is undefined.
   Also: no NULL check after calloc for the OOM case.
   Fix: early return if count == 0; check calloc return value.

3. Stack buffer overflow in tui_render_screen (tui.c)
   char buffer[8192] overflows with tall terminals: 197 visible lines *
   ~1031 bytes/message ≈ 203 KiB. Title padding loop also lacked a
   bounds check (buffer[pos++] = ' ' with no guard).
   Fix: switch to malloc(65536) with buf_size used consistently.
   Add bounds check to the title padding loop.

4. sleep() inside libssh auth callback (ssh_server.c)
   auth_password is called from ssh_event_dopoll in the main thread.
   sleep(2) there blocks the entire accept loop — one attacker with
   repeated wrong passwords stalls all incoming connections.
   IP blocking via record_auth_failure already handles brute force.
   Fix: remove sleep(2) from auth_password.

5. Spurious sleep() calls in the main accept loop (ssh_server.c)
   sleep(1/2) after rejecting rate-limited or over-limit connections
   delays accepting the next legitimate connection for no benefit.
   Fix: remove all sleep() from the accept loop error paths.
2026-03-06 01:58:56 +08:00
25a277ab27 feat: add SSH keepalive and CI/CD auto-deploy
Some checks failed
CI / build-and-test (macos-latest) (push) Has been cancelled
CI / build-and-test (ubuntu-latest) (push) Has been cancelled
Deploy / test (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Send keepalive every 30s to prevent NAT/firewall from silently
dropping idle SSH connections. Add deploy workflow that auto-deploys
to production server after CI passes on main.
2026-02-08 11:54:27 +08:00
07fd7b1513
refactor: optimize rendering, log loading, and migrate to libssh callback API (#9)
This PR addresses critical performance bottlenecks, improves UX, and eliminates technical debt.

### Key Changes

**1. Performance Optimization:**
- **Startup**: Rewrote `message_load` to scan `messages.log` backwards from the end
  - Complexity reduced from O(FileSize) to O(MaxMessages)
  - Large log file startup: seconds → milliseconds
- **Rendering**: Optimized TUI rendering to use line clearing (`\033[K`) instead of full-screen clearing (`\033[2J`)
  - Eliminated visual flicker

**2. libssh API Migration:**
- Replaced deprecated message-based API with callback-based server implementation
- Removed `#pragma GCC diagnostic ignored "-Wdeprecated-declarations"`
- Ensures future libssh compatibility

**3. User Experience (Vim Mode):**
- Added `Ctrl+W` (Delete Word) and `Ctrl+U` (Delete Line) in Insert/Command modes
- Modified `Ctrl+C` behavior to safely switch modes instead of terminating connection
- Added support for `\n` as Enter key (fixing piped input issues)

**4. Project Structure:**
- Moved all test scripts to `tests/` directory
- Added `make test` target
- Updated CI/CD to run comprehensive test suite

### Verification
-  All tests passing (17/17)
-  CI passing on Ubuntu and macOS
-  AddressSanitizer clean
-  Valgrind clean (no memory leaks)
-  Zero compilation warnings

### Code Quality
**Rating:** 🟢 Good Taste
- Algorithm-driven optimization (not hacks)
- Simplified architecture (callback-based API)
- Zero breaking changes (all tests pass)
2026-02-07 23:17:55 +08:00
5f8b8fd843 feat: enhance anonymous access and long-term stability
Improvements for low-barrier anonymous access:
- Enhanced welcome message to clarify anonymous access
- Added EASY_SETUP.md guide in Chinese and English
- Updated README with anonymous access notes

Long-term stability enhancements:
- Improved systemd service with auto-restart and resource limits
- Added log rotation script (scripts/logrotate.sh)
- Added health check script (scripts/healthcheck.sh)
- Added cron setup script for automated maintenance
- Added anonymous access test suite

Testing:
- All security features verified (10/10 passed)
- Anonymous access tests passed (2/2)
- Health check verified

This ensures:
- Zero-barrier SSH access (any username, any password)
- Stable long-term operation with auto-restart
- Automated log management
- Continuous health monitoring
2026-01-22 15:06:54 +08:00
bc08269743 Merge branch 'fix/concurrency-safety' into feat/security-audit-fixes 2026-01-22 14:08:45 +08:00
93c29ca2e9 Merge branch 'fix/auth-protection' into feat/security-audit-fixes
# Conflicts:
#	src/ssh_server.c
2026-01-22 14:08:40 +08:00
4a34a776c2 Merge branch 'fix/resource-management' into feat/security-audit-fixes 2026-01-22 14:07:10 +08:00
c1d0723392 Merge branch 'fix/input-validation' into feat/security-audit-fixes 2026-01-22 14:07:06 +08:00
c8e3743e9f Merge branch 'fix/ssh-hardening' into feat/security-audit-fixes 2026-01-22 14:07:00 +08:00
a5a62f057e fix(security): implement concurrency safety improvements
- Enhance room_broadcast() reference counting:
  * Check client state (connected, show_help, command_output) before rendering
  * Perform state check while holding client ref_lock
  * Prevents rendering to disconnected/invalid clients
  * Ensures safe cleanup when ref count reaches zero

- Fix tui_render_screen() message array TOCTOU:
  * Acquire all data (online count, message count, messages) in single lock
  * Create snapshot of messages to display
  * Calculate message range while holding lock
  * Render from snapshot without holding lock
  * Prevents inconsistencies from concurrent message additions
  * Eliminates race between two separate lock acquisitions

- Fix handle_key() scroll position TOCTOU:
  * Get message count atomically when calculating scroll bounds
  * Calculate max_scroll properly accounting for message height
  * Apply consistent bounds checking for 'j' (down) and 'G' (bottom)
  * Prevents out-of-bounds access from concurrent message changes

These changes address:
- Race condition in broadcast rendering to disconnecting clients
- TOCTOU between message count read and message access
- Scroll position bounds check race conditions

Prevents:
- Use-after-free in client cleanup
- Array out-of-bounds access
- Inconsistent UI rendering
- Crashes from concurrent message list modifications

Improves thread safety without introducing deadlocks by:
- Using snapshot approach to avoid long lock holds
- Acquiring data in consistent lock order
- Minimizing critical sections
2026-01-22 14:06:15 +08:00
a50f8c9c56 fix(security): implement comprehensive authentication protection
- Add IP-based rate limiting system:
  * Track up to 256 IPs with connection counts and auth failures
  * Rate limit: max 10 connections per IP per 60-second window
  * Block for 5 minutes after 5 auth failures
  * Auto-unblock when duration expires
- Add global connection limit (default: 64, configurable)
- Add per-IP connection limit (default: 5, configurable)
- Implement optional access token authentication:
  * If TNT_ACCESS_TOKEN set, require password matching token
  * If not set, maintain open access (backward compatible)
  * Rate limit auth attempts (max 3 per session)
  * Add 2-second delay after failed auth to slow brute force
- Add client IP tracking and logging
- Implement connection count management with proper cleanup

Environment variables:
- TNT_ACCESS_TOKEN: Access token for password authentication (optional)
- TNT_MAX_CONNECTIONS: Maximum concurrent connections (default: 64)
- TNT_MAX_CONN_PER_IP: Maximum connections per IP (default: 5)
- TNT_RATE_LIMIT: Enable/disable rate limiting (default: 1)

These changes address:
- Weak authentication allowing unrestricted access
- No protection against brute force attacks
- No rate limiting or connection throttling
- No IP-based access controls

Prevents:
- Brute force password attacks
- Connection flooding DoS
- Resource exhaustion
- Unauthorized access when token is configured

Design maintains backward compatibility: without TNT_ACCESS_TOKEN,
server remains fully open as before. With token, it's protected.
2026-01-22 14:04:15 +08:00
f65e8add64 fix(security): enhance resource management
- Convert message_load() file position array from fixed 1000 to dynamic:
  * Start with capacity of 1000, grow by 2x when needed
  * Use malloc/realloc for flexible memory management
  * Proper cleanup with free() after use
  * Graceful handling of memory allocation failures
- Enhance setup_host_key() error handling:
  * Validate key file size (reject 0 bytes and >10MB)
  * Automatically regenerate if key file is empty
  * Verify and fix insecure permissions (must be 0600)
  * Better error messages with file size reporting
- Improve client thread resource cleanup:
  * Use pthread_attr for explicit detached thread creation
  * Add pthread_mutex_destroy on thread creation failure
  * Proper cleanup order: mutex -> channel -> session -> memory
  * Add error logging with strerror() for thread failures

These changes address:
- Fixed 1000-line limit causing message truncation
- Corrupted/empty key file handling
- Permission race conditions
- Resource leaks on thread creation failure

Prevents:
- DoS via large log files
- Service startup failures from bad key files
- Memory/handle leaks under error conditions
2026-01-22 14:02:05 +08:00
4f3a07c5e2 fix(security): implement comprehensive input validation
- Add is_valid_username() function to prevent injection attacks
  * Reject shell metacharacters: |;&$`<>(){}[]'"\
  * Reject control characters (except tab)
  * Reject usernames starting with space, dot, or dash
- Apply username validation in read_username() with fallback to "anonymous"
- Add rate limiting via sleep(1) on validation failure
- Sanitize message content in message_save():
  * Replace pipe, newline, carriage return to prevent log injection
  * Ensure null termination of sanitized strings
- Enhance message_load() validation:
  * Check for oversized lines
  * Validate field lengths before copying
  * Validate timestamp reasonableness (not >1 day future, <10 years past)
  * Ensure null termination of all loaded strings

These changes address:
- Username injection vulnerabilities
- Message content injection in log files
- Log file format corruption attacks
- Malformed timestamp handling

Prevents:
- Command injection via usernames
- Log poisoning attacks
- DoS via oversized messages
2026-01-22 13:59:58 +08:00
325e524cee fix(security): implement SSH hardening improvements
- Upgrade RSA key size from 2048 to 4096 bits for stronger encryption
- Fix key file permission time window with atomic generation:
  * Use umask(0077) before file creation
  * Generate key to temporary file first
  * Atomically rename to final location
- Add configurable bind address via TNT_BIND_ADDR environment variable
- Add configurable SSH log level via TNT_SSH_LOG_LEVEL (0-4)

These changes address:
- Weak 2048-bit RSA keys
- Permission race condition during key generation
- Hardcoded bind address limiting deployment flexibility
- Inflexible logging configuration

Environment variables:
- TNT_BIND_ADDR: Bind address (default: 0.0.0.0)
- TNT_SSH_LOG_LEVEL: SSH logging verbosity 0-4 (default: 1)
2026-01-22 13:57:32 +08:00
36464007e8 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
2026-01-22 13:54:15 +08:00
bf1fb99d11
Merge pull request #4 from m1ngsama/optimize/message-loading
[Optimize] Message history loading efficiency
2025-12-02 12:39:34 +08:00
cf95bcecaf
Merge pull request #2 from m1ngsama/fix/memory-race-conditions
[Fix] Critical memory and race condition bugs
2025-12-02 12:39:12 +08:00