diff --git a/data/auth.sqlite b/data/auth.sqlite index efe6108..10cf1ff 100644 Binary files a/data/auth.sqlite and b/data/auth.sqlite differ diff --git a/file1.txt b/file1.txt new file mode 120000 index 0000000..b462677 --- /dev/null +++ b/file1.txt @@ -0,0 +1 @@ +/Users/accusys/markbase/data/sftp_test/symlink_to_file1.txt \ No newline at end of file diff --git a/markbase-core/src/ssh_server/sftp_handler.rs b/markbase-core/src/ssh_server/sftp_handler.rs index bd19be8..8a0be40 100644 --- a/markbase-core/src/ssh_server/sftp_handler.rs +++ b/markbase-core/src/ssh_server/sftp_handler.rs @@ -852,6 +852,12 @@ impl SftpHandler { "posix-rename@openssh.com" => { self.handle_posix_rename(&mut cursor, id) } + "md5-hash@openssh.com" => { + self.handle_md5_hash(&mut cursor, id) + } + "sha256-hash@openssh.com" => { + self.handle_sha256_hash(&mut cursor, id) + } _ => { warn!("Unsupported SFTP extension: {}", extension_name); self.build_status_response(id, SftpStatus::SSH_FX_FAILURE, &format!("Unsupported extension: {}", extension_name)) @@ -989,6 +995,93 @@ impl SftpHandler { } } + /// 处理md5-hash@openssh.com扩展(Phase 11:MD5哈希计算) + fn handle_md5_hash(&self, cursor: &mut std::io::Cursor<&[u8]>, id: u32) -> Result> { + let path = read_sftp_string(cursor)?; + let offset = cursor.read_u64::()?; + let length = cursor.read_u64::()?; + + info!("md5-hash: path={}, offset={}, length={}", path, offset, length); + + let full_path = self.resolve_path(&path)?; + + match File::open(&full_path) { + Ok(mut file) => { + file.seek(SeekFrom::Start(offset))?; + + let mut buffer = vec![0u8; length as usize]; + file.read_exact(&mut buffer)?; + + // 计算MD5哈希 + let hash = md5::compute(&buffer); + let hash_hex = format!("{:x}", hash); + + // 构建响应 + let mut response = Vec::new(); + response.write_u8(SftpPacketType::SSH_FXP_EXTENDED_REPLY as u8)?; + response.write_u32::(id)?; + + // hash-algorithm (SSH string) + response.write_u32::(4)?; + response.write_all("md5".as_bytes())?; + + // hash-value (SSH string) + response.write_u32::(hash_hex.len() as u32)?; + response.write_all(hash_hex.as_bytes())?; + + self.wrap_sftp_packet(&response) + } + Err(e) => { + self.build_status_response(id, SftpStatus::SSH_FX_FAILURE, &format!("MD5 hash error: {}", e)) + } + } + } + + /// 处理sha256-hash@openssh.com扩展(Phase 11:SHA256哈希计算) + fn handle_sha256_hash(&self, cursor: &mut std::io::Cursor<&[u8]>, id: u32) -> Result> { + let path = read_sftp_string(cursor)?; + let offset = cursor.read_u64::()?; + let length = cursor.read_u64::()?; + + info!("sha256-hash: path={}, offset={}, length={}", path, offset, length); + + let full_path = self.resolve_path(&path)?; + + match File::open(&full_path) { + Ok(mut file) => { + file.seek(SeekFrom::Start(offset))?; + + let mut buffer = vec![0u8; length as usize]; + file.read_exact(&mut buffer)?; + + // 计算SHA256哈希(使用sha2 crate) + use sha2::{Sha256, Digest}; + let mut hasher = Sha256::new(); + hasher.update(&buffer); + let hash = hasher.finalize(); + let hash_hex = format!("{:x}", hash); + + // 构建响应 + let mut response = Vec::new(); + response.write_u8(SftpPacketType::SSH_FXP_EXTENDED_REPLY as u8)?; + response.write_u32::(id)?; + + // hash-algorithm (SSH string) + response.write_u32::(6)?; + response.write_all("sha256".as_bytes())?; + + // hash-value (SSH string) + response.write_u32::(hash_hex.len() as u32)?; + response.write_all(hash_hex.as_bytes())?; + + self.wrap_sftp_packet(&response) + } + Err(e) => { + self.build_status_response(id, SftpStatus::SSH_FX_FAILURE, &format!("SHA256 hash error: {}", e)) + } + } + } + /// 解析路径(安全性检查,参考OpenSSH sftp-server.c: path_resolve()) fn resolve_path(&self, path: &str) -> Result { info!("resolve_path: input={}, root_dir={:?}", path, self.root_dir);