Skip to content

feat: Implement PTY/shell session support for server (#129)#153

Merged
inureyes merged 3 commits intomainfrom
feature/issue-129-pty-shell-session
Jan 22, 2026
Merged

feat: Implement PTY/shell session support for server (#129)#153
inureyes merged 3 commits intomainfrom
feature/issue-129-pty-shell-session

Conversation

@inureyes
Copy link
Member

Summary

  • Add PTY (pseudo-terminal) management module for Unix PTY handling
  • Implement shell session handler for interactive SSH shell sessions
  • Integrate shell_request, window_change_request, and data handlers
  • Add shell session state tracking to channel management

Changes

  • src/server/pty.rs (new): PTY master/slave pair management with async I/O using tokio's AsyncFd
  • src/server/shell.rs (new): Shell process spawning with proper session/controlling terminal setup
  • src/server/handler.rs: Implement shell_request, window_change_request, and update data handler
  • src/server/session.rs: Add ShellSession field and methods to ChannelState
  • src/server/mod.rs: Export new pty and shell modules
  • Cargo.toml: Enable nix crate 'term' and 'fs' features

Key Features

  • PTY pair creation using nix crate's openpty
  • Async I/O for PTY master file descriptor
  • Shell process spawning with login shell flag (-l)
  • Terminal environment setup (TERM, HOME, USER, SHELL, PATH)
  • Bidirectional I/O forwarding between SSH channel and PTY
  • Window resize handling via TIOCSWINSZ ioctl
  • Proper process cleanup on disconnect

Platform

  • Uses POSIX PTY APIs (Unix-specific)
  • Windows support would require ConPTY (future enhancement)

Test Plan

  • PTY creation tests pass
  • Window size setting tests pass
  • All 155 server tests pass
  • Clippy warnings resolved
  • Manual testing: ssh -p 2222 testuser@localhost
  • Manual testing: Window resize handling
  • Manual testing: Ctrl+C/Ctrl+D signal handling

Closes #129

Add Unix PTY handling for interactive shell sessions in bssh-server:
- Create pty.rs module for PTY master/slave pair management with async I/O
- Create shell.rs module for shell process spawning and I/O forwarding
- Implement shell_request handler to start interactive shell sessions
- Implement window_change_request handler for terminal resizing
- Update data handler to forward SSH data to active shell sessions
- Add shell session state tracking to ChannelState
- Enable nix crate 'term' and 'fs' features for PTY operations
@inureyes inureyes added type:enhancement New feature or request status:review Under review priority:medium Medium priority issue labels Jan 22, 2026
@inureyes
Copy link
Member Author

Security & Performance Review

Analysis Summary

  • PR Branch: feature/issue-129-pty-shell-session (all fixes committed here)
  • Scope: changed-files
  • Languages: Rust
  • Total issues: 8
  • Critical: 1 | High: 2 | Medium: 4 | Low: 1
  • Review Iteration: 1/3

Prioritized Fix Roadmap

CRITICAL

  • File descriptor leak via std::mem::forget(slave_file) (shell.rs:205): Using mem::forget on slave_file prevents the file from being dropped, but the fd is passed to child process stdio via from_raw_fd. The child owns the stdio fds, but this creates unsafe fd aliasing since slave_file and the Stdio objects both think they own the same fd. If the spawn fails after from_raw_fd but before forget, the fd could be double-closed or leaked.

HIGH

  • Shell path not validated (shell.rs:155): The shell path from user_info.shell is executed directly without validation. A malicious or misconfigured auth provider could inject arbitrary commands. Should validate shell exists and is in an allowed list.
  • Missing uid/gid privilege drop (shell.rs:186-197): Shell process is spawned but there's no setuid/setgid to drop privileges to the authenticated user. The shell runs as the server's user, not as the authenticated user. This is a major security issue for multi-user SSH servers.

MEDIUM

  • Integer truncation in window size (pty.rs:96-101): Window dimensions are truncated from u32 to u16 without bounds checking. Values > 65535 would silently overflow.
  • Unsafe TIOCSCTTY ioctl without error handling context (shell.rs:192-194): The ioctl error path returns last_os_error() but lacks context about what operation failed.
  • Clippy warnings in test code (shell.rs:424-425): Assertions on constants should use const { assert!(..) } block.
  • PTY read loop may spin on certain errors (shell.rs:250-258): The read error handling breaks on any non-WouldBlock error, but WouldBlock check is inside the match, potentially missing spurious wakeups.

LOW

  • Hardcoded PATH environment variable (shell.rs:167): PATH is hardcoded rather than derived from system configuration. Consider using the user's configured PATH or system default.

Progress Log

  • Currently: Analyzing PR changes

Manual Review Required

  • Verify that the auth provider implementations properly validate shell paths
  • Ensure uid/gid are properly set in production auth providers
  • Consider whether the server should run as root to support privilege dropping

Priority: CRITICAL/MEDIUM
Issues addressed:
- Fixed file descriptor leak via mem::forget by using dup() for each stdio fd
- Added shell path validation before spawning process
- Added bounds checking for window size dimensions (clamp to u16::MAX)
- Improved error context for TIOCSWINSZ ioctl failures
- Fixed clippy warning for const assertions in tests

Review-Iteration: 1
@inureyes
Copy link
Member Author

Security & Performance Review - Update

Analysis Summary

  • PR Branch: feature/issue-129-pty-shell-session (all fixes committed here)
  • Scope: changed-files
  • Languages: Rust
  • Review Iteration: 1/3

Fixes Applied (commit 02859da)

CRITICAL - Fixed

  • File descriptor leak via std::mem::forget(slave_file) - Now uses dup() to create separate fd for each stdio (stdin, stdout, stderr). This prevents unsafe fd aliasing and potential double-close issues.

MEDIUM - Fixed

  • Integer truncation in window size - Values exceeding u16::MAX are now clamped using .min(MAX_DIMENSION) instead of silent truncation.
  • Improved ioctl error context - Added "TIOCSWINSZ ioctl" to error message for better debugging.
  • Clippy warning in test - Changed assertions to use const { assert!(..) } block.

HIGH - Added Mitigation

  • Shell path validation - Added existence check before spawning shell process.

Remaining Issues for Manual Review

HIGH - Requires Design Decision

  • Missing uid/gid privilege drop (shell.rs:186-197): The shell process runs as the server's user, not as the authenticated user. This is intentional for many SSH server designs (like jump servers), but for multi-user SSH servers, privilege dropping is critical. Recommendation: Document this as a known limitation or add optional privilege dropping based on server configuration.

LOW - Documentation/Enhancement

  • Hardcoded PATH environment variable (shell.rs:167): PATH is hardcoded. For production deployments, consider making this configurable or deriving from system defaults.

Test Results

  • All 8 PTY tests pass
  • Shell test passes
  • Library compiles successfully

Summary

The critical file descriptor safety issue has been resolved. The code now correctly uses dup() to create separate file descriptors for stdin/stdout/stderr, avoiding unsafe aliasing. Window size bounds checking and shell validation have been added.

The uid/gid privilege drop is a design decision that should be discussed with the team, as it affects the security model of the SSH server.

@inureyes
Copy link
Member Author

Security & Performance Review - Complete

Final Summary

  • PR Branch: feature/issue-129-pty-shell-session
  • Scope: changed-files
  • Languages: Rust
  • Total issues found: 8
  • Fixed: 5 | Design Decision Required: 2 | Low Priority: 1
  • Review Iterations: 2/3

Status: Review Complete


Fixed Issues (commit 02859da)

Severity Issue Status
CRITICAL File descriptor leak via mem::forget(slave_file) Fixed - Now uses dup() for separate fds
MEDIUM Integer truncation in window size (u32 to u16) Fixed - Values clamped to u16::MAX
MEDIUM Improved ioctl error context Fixed - Added operation name to error
MEDIUM Clippy warning for const assertions Fixed - Uses const { assert!(..) }
HIGH Shell path not validated Fixed - Added existence check

Remaining Items (Design Decisions Required)

Severity Issue Recommendation
HIGH Missing uid/gid privilege drop Design decision needed: The shell runs as the server's user. For multi-user servers, consider adding optional setuid/setgid in pre_exec. Document as limitation or add config option.
LOW Hardcoded PATH Consider making configurable for production deployments

Test Results

test result: ok. 156 passed; 0 failed; 0 ignored

Code Quality

  • Library builds successfully
  • All server module tests pass
  • PTY module tests: 8 passed (including new overflow clamping test)
  • Shell module tests: 1 passed

Security Analysis Summary

What the PR does well:

  1. Uses setsid() to create a new session (prevents terminal hijacking)
  2. Uses TIOCSCTTY to properly set controlling terminal
  3. Clears environment before setting up shell environment
  4. Uses kill_on_drop(true) for proper process cleanup
  5. Handles shutdown signals properly via oneshot channel
  6. Has proper Drop implementation to clean up resources

Areas addressed by this review:

  1. Fixed unsafe fd handling that could lead to double-close or leaks
  2. Added shell existence validation before execution
  3. Added bounds checking for terminal dimensions

Areas requiring team discussion:

  1. Privilege separation model (setuid/setgid)
  2. Shell whitelist/validation beyond existence check
  3. Environment variable configuration

Review Status: Complete. Recommend merge after team confirms privilege separation design.

- Add unit tests for PTY configuration, winsize, and boundary values
- Add unit tests for PTY master operations (resize, config, path)
- Add tests for shell session structures and validation
- Update ARCHITECTURE.md with PTY/shell module documentation
- Update server-configuration.md with shell session architecture
@inureyes
Copy link
Member Author

PR Finalization Report

Project Structure Discovered

  • Project Type: Rust with Cargo
  • Test Framework: Built-in Rust test (cargo test) with inline #[cfg(test)] modules
  • Documentation System: Markdown files (ARCHITECTURE.md, docs/architecture/)
  • Multi-language Docs: No (English only)
  • Lint Tools: cargo fmt, cargo clippy

Checklist

Tests

  • Analyzed existing test structure (inline test modules in source files)
  • Identified missing test coverage for PTY and shell modules
  • Generated new tests:
    • PTY module (src/server/pty.rs): Added 10 new tests
      • test_default_constants
      • test_pty_config_clone
      • test_pty_config_debug
      • test_winsize_boundary_values
      • test_pty_master_fd_validity
      • test_pty_master_slave_path_format
      • test_pty_master_config_accessor
      • test_pty_master_multiple_resizes
      • test_pty_reader_new
      • test_pty_writer_new
    • Shell module (src/server/shell.rs): Added 5 new tests
      • test_io_buffer_size_value
      • test_shell_session_debug
      • test_pty_config_default_values
      • test_pty_config_custom_values
      • test_shell_path_validation
  • All tests passing (918+ tests)

Documentation

  • ARCHITECTURE.md updated with PTY/shell module documentation
  • docs/architecture/server-configuration.md updated with Shell Session Architecture section
    • PTY management documentation
    • Shell session handler documentation
    • SSH handler integration flow diagram

Code Quality

  • cargo fmt: Code properly formatted
  • cargo clippy -- -D warnings: All warnings resolved

Changes Made

  • Added 15 new unit tests for PTY and shell modules
  • Updated ARCHITECTURE.md with PTY and Shell module documentation
  • Updated docs/architecture/server-configuration.md with comprehensive Shell Session Architecture section including:
    • PTY configuration examples
    • Shell session lifecycle documentation
    • SSH handler integration flow diagram

Notes

  • One existing timing test (test_password_verifier_timing_attack_mitigation) is flaky due to timing variance, but this is unrelated to the PR changes and passes on subsequent runs
  • Integration tests for full shell sessions require actual russh channels, which are covered by the existing integration test infrastructure

@inureyes inureyes merged commit 816d767 into main Jan 22, 2026
1 of 2 checks passed
@inureyes inureyes deleted the feature/issue-129-pty-shell-session branch January 22, 2026 10:35
@inureyes inureyes self-assigned this Jan 24, 2026
@inureyes inureyes added status:done Completed and removed status:review Under review labels Jan 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority:medium Medium priority issue status:done Completed type:enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement PTY/shell session support for server

1 participant