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
This commit is contained in:
BIN
data/auth.sqlite
BIN
data/auth.sqlite
Binary file not shown.
@@ -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;
|
||||
|
||||
@@ -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<PathBuf> {
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user