Implement SSH Banner/MOTD support

- Add banner and banner_file fields to SshSecurityConfig
- Enterprise default: 'MarkBaseSSH - Secure File Transfer Server'
- Support banner_file for reading from /etc/motd
- Send SSH_MSG_USERAUTH_BANNER before USERAUTH_SUCCESS
- Pass security_config to perform_ssh_auth function

All 229 tests pass.
This commit is contained in:
Warren
2026-06-20 23:33:19 +08:00
parent 783356852e
commit 487b4450f8
2 changed files with 45 additions and 1 deletions

View File

@@ -179,7 +179,7 @@ fn handle_connection_complete(
)
};
let mut auth_handler = AuthHandler::new(provider);
let auth_user = perform_ssh_auth(&mut stream, &mut auth_handler, &mut encryption_ctx)?;
let auth_user = perform_ssh_auth(&mut stream, &mut auth_handler, &mut encryption_ctx, security_config.clone())?;
info!("SSH authentication succeeded: user={}", auth_user.username);
let upload_hook = if upload_hook_config.enabled {
@@ -335,6 +335,7 @@ fn perform_ssh_auth(
stream: &mut TcpStream,
auth_handler: &mut AuthHandler,
encryption_ctx: &mut EncryptionContext,
security_config: Arc<Mutex<SshSecurityConfig>>,
) -> Result<AuthUser> {
info!("Starting SSH authentication");
info!(
@@ -395,6 +396,29 @@ fn perform_ssh_auth(
match auth_handler.handle_userauth_request(&auth_request, &session_id)? {
AuthResult::Success => {
// Send banner if configured (SSH_MSG_USERAUTH_BANNER)
let security = security_config.lock().unwrap();
let banner = if let Some(file) = &security.banner_file {
std::fs::read_to_string(file).ok()
} else {
security.banner.clone()
};
drop(security);
if let Some(banner_text) = banner {
let mut banner_payload = Vec::new();
banner_payload.write_u8(PacketType::SSH_MSG_USERAUTH_BANNER as u8)?;
banner_payload.write_u32::<BigEndian>(banner_text.len() as u32)?;
banner_payload.write_all(banner_text.as_bytes())?;
// Language tag (empty SSH string)
banner_payload.write_u32::<BigEndian>(0)?;
let encrypted_banner =
EncryptedPacket::new(&banner_payload, encryption_ctx, true)?;
encrypted_banner.write(stream)?;
info!("Sent SSH_MSG_USERAUTH_BANNER");
}
let success_payload = vec![PacketType::SSH_MSG_USERAUTH_SUCCESS as u8];
let encrypted_success =
EncryptedPacket::new(&success_payload, encryption_ctx, true)?;