Skip to content

Commit c615d05

Browse files
committed
fix: Fixed issue with SSH authentication
if the key is resolved and it fails to authenticate, if a password is provided, try to authenticate with the password before returning an error
1 parent 1c3c2da commit c615d05

File tree

4 files changed

+82
-32
lines changed

4 files changed

+82
-32
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changelog
22

33
- [Changelog](#changelog)
4+
- [0.6.3](#063)
45
- [0.6.2](#062)
56
- [0.6.1](#061)
67
- [0.6.0](#060)
@@ -20,6 +21,13 @@
2021

2122
---
2223

24+
## 0.6.3
25+
26+
Released on 21/07/2025
27+
28+
- Fixed issue with SSH authentication:
29+
- if the key is resolved and it fails to authenticate, if a password is provided, try to authenticate with the password before returning an error.
30+
2331
## 0.6.2
2432

2533
Released on 16/05/2025

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ license = "MIT"
1010
name = "remotefs-ssh"
1111
readme = "README.md"
1212
repository = "https://github.com/remotefs-rs/remotefs-rs-ssh"
13-
version = "0.6.2"
13+
version = "0.6.3"
1414
rust-version = "1.85.1"
1515

1616
[dependencies]

src/ssh/commons.rs

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ use super::SshOpts;
1515
use super::config::Config;
1616
use crate::SshAgentIdentity;
1717

18+
/// Authentication method
19+
#[derive(Debug, Clone, PartialEq, Eq)]
20+
enum Authentication {
21+
RsaKey(PathBuf),
22+
Password(String),
23+
}
24+
1825
// -- connect
1926

2027
/// Establish connection with remote server and in case of success, return the generated [`Session`]
@@ -96,30 +103,44 @@ pub fn connect(opts: &SshOpts) -> RemoteResult<Session> {
96103

97104
// Authenticate with password or key
98105
if !session.authenticated() {
99-
match opts.key_storage.as_ref().and_then(|x| {
106+
let mut methods = vec![];
107+
// first try with ssh agent
108+
if let Some(rsa_key) = opts.key_storage.as_ref().and_then(|x| {
100109
x.resolve(ssh_config.host.as_str(), ssh_config.username.as_str())
101110
.or(x.resolve(
102111
ssh_config.resolved_host.as_str(),
103112
ssh_config.username.as_str(),
104113
))
105114
}) {
106-
Some(rsa_key) => {
107-
session_auth_with_rsakey(
108-
&mut session,
109-
&ssh_config.username,
110-
rsa_key.as_path(),
111-
opts.password.as_deref(),
112-
ssh_config.params.identity_file.as_deref(),
113-
)?;
114-
}
115-
None => {
116-
session_auth_with_password(
117-
&mut session,
118-
&ssh_config.username,
119-
opts.password.as_deref(),
120-
)?;
115+
methods.push(Authentication::RsaKey(rsa_key.clone()));
116+
}
117+
// then try with password
118+
if let Some(password) = opts.password.as_ref() {
119+
methods.push(Authentication::Password(password.clone()));
120+
}
121+
122+
// try with methods
123+
let mut last_err = None;
124+
for auth_method in methods {
125+
match session_auth(&mut session, opts, &ssh_config, auth_method) {
126+
Ok(_) => {
127+
info!("Authenticated successfully");
128+
return Ok(session);
129+
}
130+
Err(err) => {
131+
error!("Authentication failed: {err}",);
132+
last_err = Some(err);
133+
}
121134
}
122135
}
136+
137+
return Err(match last_err {
138+
Some(err) => err,
139+
None => RemoteError::new_ex(
140+
RemoteErrorType::AuthenticationFailed,
141+
"no authentication method provided",
142+
),
143+
});
123144
}
124145
// Return session
125146
Ok(session)
@@ -282,15 +303,36 @@ fn session_auth_with_rsakey(
282303
))
283304
}
284305

306+
/// Authenticate on session with the provided [`Authentication`] method.
307+
fn session_auth(
308+
session: &mut Session,
309+
opts: &SshOpts,
310+
ssh_config: &Config,
311+
authentication: Authentication,
312+
) -> RemoteResult<()> {
313+
match authentication {
314+
Authentication::RsaKey(private_key) => session_auth_with_rsakey(
315+
session,
316+
&ssh_config.username,
317+
private_key.as_path(),
318+
opts.password.as_deref(),
319+
ssh_config.params.identity_file.as_deref(),
320+
),
321+
Authentication::Password(password) => {
322+
session_auth_with_password(session, &ssh_config.username, &password)
323+
}
324+
}
325+
}
326+
285327
/// Authenticate on session with username and password
286328
fn session_auth_with_password(
287329
session: &mut Session,
288330
username: &str,
289-
password: Option<&str>,
331+
password: &str,
290332
) -> RemoteResult<()> {
291333
// Username / password
292334
debug!("Authenticating with username '{}' and password", username);
293-
if let Err(err) = session.userauth_password(username, password.unwrap_or("")) {
335+
if let Err(err) = session.userauth_password(username, password) {
294336
error!("Authentication failed: {}", err);
295337
Err(RemoteError::new_ex(
296338
RemoteErrorType::AuthenticationFailed,

src/ssh/sftp.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ impl RemoteFs for SftpFs {
386386
.unwrap()
387387
.rename(src.as_path(), dest.as_path(), Some(RenameFlags::OVERWRITE))
388388
.map_err(|e| {
389-
error!("Move failed: {}", e);
389+
error!("Move failed: {e}",);
390390
RemoteError::new_ex(RemoteErrorType::FileCreateDenied, e)
391391
})
392392
}
@@ -415,7 +415,7 @@ impl RemoteFs for SftpFs {
415415
.map(SftpWriteStream::from)
416416
.map(WriteStream::from)
417417
.map_err(|e| {
418-
error!("Append failed: {}", e);
418+
error!("Append failed: {e}",);
419419
RemoteError::new_ex(RemoteErrorType::CouldNotOpenFile, e)
420420
})
421421
} else {
@@ -437,7 +437,7 @@ impl RemoteFs for SftpFs {
437437
.map(SftpWriteStream::from)
438438
.map(WriteStream::from)
439439
.map_err(|e| {
440-
error!("Create failed: {}", e);
440+
error!("Create failed: {e}",);
441441
RemoteError::new_ex(RemoteErrorType::FileCreateDenied, e)
442442
})
443443
} else {
@@ -460,7 +460,7 @@ impl RemoteFs for SftpFs {
460460
.map(SftpReadStream::from)
461461
.map(ReadStream::from)
462462
.map_err(|e| {
463-
error!("Open failed: {}", e);
463+
error!("Open failed: {e}",);
464464
RemoteError::new_ex(RemoteErrorType::CouldNotOpenFile, e)
465465
})
466466
}
@@ -481,20 +481,20 @@ impl RemoteFs for SftpFs {
481481
while bytes < transfer_size {
482482
let mut buffer: [u8; 65535] = [0; 65535];
483483
let bytes_read = reader.read(&mut buffer).map_err(|e| {
484-
error!("Failed to read from file: {}", e);
484+
error!("Failed to read from file: {e}",);
485485
RemoteError::new_ex(RemoteErrorType::IoError, e)
486486
})?;
487487
let mut delta = 0;
488488
while delta < bytes_read {
489489
delta += stream.write(&buffer[delta..bytes_read]).map_err(|e| {
490-
error!("Failed to write to stream: {}", e);
490+
error!("Failed to write to stream: {e}",);
491491
RemoteError::new_ex(RemoteErrorType::IoError, e)
492492
})?;
493493
}
494494
bytes += bytes_read;
495495
}
496496
self.on_written(stream)?;
497-
trace!("Written {} bytes to destination", bytes);
497+
trace!("Written {bytes} bytes to destination",);
498498
Ok(bytes as u64)
499499
} else {
500500
Err(RemoteError::new(RemoteErrorType::NotConnected))
@@ -515,20 +515,20 @@ impl RemoteFs for SftpFs {
515515
while bytes < transfer_size {
516516
let mut buffer: [u8; 65535] = [0; 65535];
517517
let bytes_read = reader.read(&mut buffer).map_err(|e| {
518-
error!("Failed to read from file: {}", e);
518+
error!("Failed to read from file: {e}",);
519519
RemoteError::new_ex(RemoteErrorType::IoError, e)
520520
})?;
521521
let mut delta = 0;
522522
while delta < bytes_read {
523523
delta += stream.write(&buffer[delta..bytes_read]).map_err(|e| {
524-
error!("Failed to write to stream: {}", e);
524+
error!("Failed to write to stream: {e}",);
525525
RemoteError::new_ex(RemoteErrorType::IoError, e)
526526
})?;
527527
}
528528
bytes += bytes_read;
529529
}
530530
self.on_written(stream)?;
531-
trace!("Written {} bytes to destination", bytes);
531+
trace!("Written {bytes} bytes to destination",);
532532
Ok(bytes as u64)
533533
} else {
534534
Err(RemoteError::new(RemoteErrorType::NotConnected))
@@ -544,20 +544,20 @@ impl RemoteFs for SftpFs {
544544
while bytes < transfer_size {
545545
let mut buffer: [u8; 65535] = [0; 65535];
546546
let bytes_read = stream.read(&mut buffer).map_err(|e| {
547-
error!("Failed to read from stream: {}", e);
547+
error!("Failed to read from stream: {e}");
548548
RemoteError::new_ex(RemoteErrorType::IoError, e)
549549
})?;
550550
let mut delta = 0;
551551
while delta < bytes_read {
552552
delta += dest.write(&buffer[delta..bytes_read]).map_err(|e| {
553-
error!("Failed to write to file: {}", e);
553+
error!("Failed to write to file: {e}",);
554554
RemoteError::new_ex(RemoteErrorType::IoError, e)
555555
})?;
556556
}
557557
bytes += bytes_read;
558558
}
559559
self.on_read(stream)?;
560-
trace!("Copied {} bytes to destination", bytes);
560+
trace!("Copied {bytes} bytes to destination",);
561561
Ok(bytes as u64)
562562
} else {
563563
Err(RemoteError::new(RemoteErrorType::NotConnected))

0 commit comments

Comments
 (0)