From 91d29e40eaa7fa2c21b16b210a44735d74851aa9 Mon Sep 17 00:00:00 2001 From: Warren Date: Mon, 15 Jun 2026 13:14:16 +0800 Subject: [PATCH] Fix SFTP path resolution and EOF handling - Fix resolve_path() to handle non-existent files (for upload) - Add SSH_MSG_CHANNEL_EOF handling in service loop - Canonicalize root_dir in SftpHandler constructor - SFTP now fully working: pwd, ls, cd, get, put operations verified --- data/auth.sqlite | Bin 73728 -> 73728 bytes markbase-core/src/ssh_server/server.rs | 4 +++ markbase-core/src/ssh_server/sftp_handler.rs | 35 ++++++++++++++----- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/data/auth.sqlite b/data/auth.sqlite index 9caa4f7d63c15f2b98e316831d10ac4edf8a2e9a..d7701a2d520aca6afbf82e67b43fe204ba6e0c64 100644 GIT binary patch delta 177 zcmZoTz|wGlWr7qF7u!S`Cm^{opt2V!qWn~0$8tZy1 zCx4Us%?xC3{v!XJU-Bm}vn*3?er{@BNl_)+FHUArrj*p&e6}B(zvzG97x>A_%*n*| ri-Ca!%m9koV-@xHUeD+~+2Q|2h{^x|^D}yH|H9AspP$#80SN#ABX>GV delta 177 zcmZoTz|wGlWr7qF=f;ULPC#;FLc0v3#pHuh3X?fxZ?o&Yf6>IzYqj~6EGr|3(^%JQ zIr*F1Z)PBS^B4K&{E|C)nPr)B^K(=4N{TAkc5yO`GNq*E=CkeC{6+r*zrapbW=Lo_j*RN$qxTFLQMYupP$ie`xk!3|NOjW3`hU~X2m=M diff --git a/markbase-core/src/ssh_server/server.rs b/markbase-core/src/ssh_server/server.rs index c0f45cf..2fab30d 100644 --- a/markbase-core/src/ssh_server/server.rs +++ b/markbase-core/src/ssh_server/server.rs @@ -345,6 +345,10 @@ fn handle_ssh_service_loop( } break; } + Some(&pt) if pt == PacketType::SSH_MSG_CHANNEL_EOF as u8 => { + info!("Received SSH_MSG_CHANNEL_EOF"); + // EOF means client won't send more data, just acknowledge and continue + } Some(&pt) if pt == PacketType::SSH_MSG_DISCONNECT as u8 => { info!("Received SSH_MSG_DISCONNECT"); break; diff --git a/markbase-core/src/ssh_server/sftp_handler.rs b/markbase-core/src/ssh_server/sftp_handler.rs index f764ab8..b1470bf 100644 --- a/markbase-core/src/ssh_server/sftp_handler.rs +++ b/markbase-core/src/ssh_server/sftp_handler.rs @@ -236,8 +236,9 @@ pub struct SftpHandler { impl SftpHandler { pub fn new(root_dir: PathBuf) -> Self { + let canonical_root = root_dir.canonicalize().unwrap_or(root_dir); Self { - root_dir, + root_dir: canonical_root, next_handle_id: 0, handles: std::collections::HashMap::new(), } @@ -728,20 +729,36 @@ impl SftpHandler { /// 解析路径(安全性检查,参考OpenSSH sftp-server.c: path_resolve()) fn resolve_path(&self, path: &str) -> Result { - let full_path = if path.starts_with('/') { - self.root_dir.join(path.trim_start_matches('/')) + info!("resolve_path: input={}, root_dir={:?}", path, self.root_dir); + + let full_path = if path.is_empty() || path == "." { + self.root_dir.clone() + } else if path.starts_with('/') { + PathBuf::from(path) } else { self.root_dir.join(path) }; - let canonical_path = full_path.canonicalize() - .map_err(|e| anyhow!("Path resolution error: {}", e))?; + info!("resolve_path: full_path={:?}", full_path); - if !canonical_path.starts_with(&self.root_dir) { - return Err(anyhow!("Path traversal attempt detected")); + if full_path.exists() { + let canonical_path = full_path.canonicalize() + .map_err(|e| anyhow!("Path resolution error for {:?}: {}", full_path, e))?; + + info!("resolve_path: canonical_path={:?}", canonical_path); + + if !canonical_path.starts_with(&self.root_dir) { + return Err(anyhow!("Path traversal attempt detected: {:?} not under {:?}", canonical_path, self.root_dir)); + } + + Ok(canonical_path) + } else { + if !full_path.starts_with(&self.root_dir) { + return Err(anyhow!("Path traversal attempt detected: {:?} not under {:?}", full_path, self.root_dir)); + } + + Ok(full_path) } - - Ok(canonical_path) } /// 构建SSH_FXP_VERSION响应(参考OpenSSH sftp-server.c)