SSH key storage in scp/sftp file transfers

This commit is contained in:
ChristianVisintin
2020-12-24 17:27:57 +01:00
parent 920d3b4af4
commit 6bf503331e
3 changed files with 129 additions and 71 deletions

View File

@@ -30,6 +30,7 @@ extern crate ssh2;
// Locals
use super::{FileTransfer, FileTransferError, FileTransferErrorType};
use crate::fs::{FsDirectory, FsEntry, FsFile};
use crate::system::sshkey_storage::SshKeyStorage;
use crate::utils::parser::parse_lstime;
// Includes
@@ -46,22 +47,18 @@ use std::time::SystemTime;
pub struct ScpFileTransfer {
session: Option<Session>,
wrkdir: PathBuf,
}
impl Default for ScpFileTransfer {
fn default() -> Self {
Self::new()
}
key_storage: SshKeyStorage,
}
impl ScpFileTransfer {
/// ### new
///
/// Instantiates a new ScpFileTransfer
pub fn new() -> ScpFileTransfer {
pub fn new(key_storage: SshKeyStorage) -> ScpFileTransfer {
ScpFileTransfer {
session: None,
wrkdir: PathBuf::from("~"),
key_storage,
}
}
@@ -345,9 +342,27 @@ impl FileTransfer for ScpFileTransfer {
Some(u) => u,
None => String::from(""),
};
// Try authenticating with user agent
if session.userauth_agent(username.as_str()).is_err() {
// Try authentication with password then
// Check if it is possible to authenticate using a RSA key
match self
.key_storage
.resolve(address.as_str(), username.as_str())
{
Some(rsa_key) => {
// Authenticate with RSA key
if let Err(err) = session.userauth_pubkey_file(
username.as_str(),
None,
rsa_key.as_path(),
password.as_deref(),
) {
return Err(FileTransferError::new_ex(
FileTransferErrorType::AuthenticationFailed,
format!("{}", err),
));
}
}
None => {
// Proceeed with username/password authentication
if let Err(err) = session.userauth_password(
username.as_str(),
password.unwrap_or_else(|| String::from("")).as_str(),
@@ -358,6 +373,7 @@ impl FileTransfer for ScpFileTransfer {
));
}
}
}
// Get banner
let banner: Option<String> = match session.banner() {
Some(s) => Some(String::from(s)),
@@ -823,14 +839,14 @@ mod tests {
#[test]
fn test_filetransfer_scp_new() {
let client: ScpFileTransfer = ScpFileTransfer::new();
let client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client.session.is_none());
assert_eq!(client.is_connected(), false);
}
#[test]
fn test_filetransfer_scp_connect() {
let mut client: ScpFileTransfer = ScpFileTransfer::new();
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert_eq!(client.is_connected(), false);
assert!(client
.connect(
@@ -849,7 +865,7 @@ mod tests {
}
#[test]
fn test_filetransfer_scp_bad_auth() {
let mut client: ScpFileTransfer = ScpFileTransfer::new();
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -862,7 +878,7 @@ mod tests {
#[test]
fn test_filetransfer_scp_no_credentials() {
let mut client: ScpFileTransfer = ScpFileTransfer::new();
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(String::from("test.rebex.net"), 22, None, None)
.is_err());
@@ -870,7 +886,7 @@ mod tests {
#[test]
fn test_filetransfer_scp_bad_server() {
let mut client: ScpFileTransfer = ScpFileTransfer::new();
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("mybadserver.veryverybad.awful"),
@@ -882,7 +898,7 @@ mod tests {
}
#[test]
fn test_filetransfer_scp_pwd() {
let mut client: ScpFileTransfer = ScpFileTransfer::new();
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -902,7 +918,7 @@ mod tests {
#[test]
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
fn test_filetransfer_scp_cwd() {
let mut client: ScpFileTransfer = ScpFileTransfer::new();
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -923,7 +939,7 @@ mod tests {
#[test]
fn test_filetransfer_scp_cwd_error() {
let mut client: ScpFileTransfer = ScpFileTransfer::new();
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -946,7 +962,7 @@ mod tests {
#[test]
fn test_filetransfer_scp_ls() {
let mut client: ScpFileTransfer = ScpFileTransfer::new();
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -967,7 +983,7 @@ mod tests {
#[test]
fn test_filetransfer_scp_stat() {
let mut client: ScpFileTransfer = ScpFileTransfer::new();
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -991,7 +1007,7 @@ mod tests {
#[test]
fn test_filetransfer_scp_recv() {
let mut client: ScpFileTransfer = ScpFileTransfer::new();
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -1023,7 +1039,7 @@ mod tests {
}
#[test]
fn test_filetransfer_scp_recv_failed_nosuchfile() {
let mut client: ScpFileTransfer = ScpFileTransfer::new();
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -1058,7 +1074,7 @@ mod tests {
/* NOTE: the server doesn't allow you to create directories
#[test]
fn test_filetransfer_scp_mkdir() {
let mut client: ScpFileTransfer = ScpFileTransfer::new();
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client.connect(String::from("test.rebex.net"), 22, Some(String::from("demo")), Some(String::from("password"))).is_ok());
let dir: String = String::from("foo");
// Mkdir
@@ -1087,7 +1103,7 @@ mod tests {
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
};
let mut scp: ScpFileTransfer = ScpFileTransfer::new();
let mut scp: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(scp.change_dir(Path::new("/tmp")).is_err());
assert!(scp.disconnect().is_err());
assert!(scp.list_dir(Path::new("/tmp")).is_err());

View File

@@ -29,6 +29,7 @@ extern crate ssh2;
// Locals
use super::{FileTransfer, FileTransferError, FileTransferErrorType};
use crate::fs::{FsDirectory, FsEntry, FsFile};
use crate::system::sshkey_storage::SshKeyStorage;
// Includes
use ssh2::{FileStat, OpenFlags, OpenType, Session, Sftp};
@@ -44,23 +45,19 @@ pub struct SftpFileTransfer {
session: Option<Session>,
sftp: Option<Sftp>,
wrkdir: PathBuf,
}
impl Default for SftpFileTransfer {
fn default() -> Self {
Self::new()
}
key_storage: SshKeyStorage,
}
impl SftpFileTransfer {
/// ### new
///
/// Instantiates a new SftpFileTransfer
pub fn new() -> SftpFileTransfer {
pub fn new(key_storage: SshKeyStorage) -> SftpFileTransfer {
SftpFileTransfer {
session: None,
sftp: None,
wrkdir: PathBuf::from("~"),
key_storage,
}
}
@@ -238,9 +235,27 @@ impl FileTransfer for SftpFileTransfer {
Some(u) => u,
None => String::from(""),
};
// Try authenticating with user agent
if session.userauth_agent(username.as_str()).is_err() {
// Try authentication with password then
// Check if it is possible to authenticate using a RSA key
match self
.key_storage
.resolve(address.as_str(), username.as_str())
{
Some(rsa_key) => {
// Authenticate with RSA key
if let Err(err) = session.userauth_pubkey_file(
username.as_str(),
None,
rsa_key.as_path(),
password.as_deref(),
) {
return Err(FileTransferError::new_ex(
FileTransferErrorType::AuthenticationFailed,
format!("{}", err),
));
}
}
None => {
// Proceeed with username/password authentication
if let Err(err) = session.userauth_password(
username.as_str(),
password.unwrap_or_else(|| String::from("")).as_str(),
@@ -251,6 +266,7 @@ impl FileTransfer for SftpFileTransfer {
));
}
}
}
// Set blocking to true
session.set_blocking(true);
// Get Sftp client
@@ -600,7 +616,7 @@ mod tests {
#[test]
fn test_filetransfer_sftp_new() {
let client: SftpFileTransfer = SftpFileTransfer::new();
let client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client.session.is_none());
assert!(client.sftp.is_none());
assert_eq!(client.wrkdir, PathBuf::from("~"));
@@ -609,7 +625,7 @@ mod tests {
#[test]
fn test_filetransfer_sftp_connect() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert_eq!(client.is_connected(), false);
assert!(client
.connect(
@@ -631,7 +647,7 @@ mod tests {
#[test]
fn test_filetransfer_sftp_bad_auth() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -644,7 +660,7 @@ mod tests {
#[test]
fn test_filetransfer_sftp_no_credentials() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(String::from("test.rebex.net"), 22, None, None)
.is_err());
@@ -652,7 +668,7 @@ mod tests {
#[test]
fn test_filetransfer_sftp_bad_server() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("mybadserver.veryverybad.awful"),
@@ -665,7 +681,7 @@ mod tests {
#[test]
fn test_filetransfer_sftp_pwd() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -686,7 +702,7 @@ mod tests {
#[test]
fn test_filetransfer_sftp_cwd() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -713,7 +729,7 @@ mod tests {
#[test]
fn test_filetransfer_sftp_copy() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -748,7 +764,7 @@ mod tests {
#[test]
fn test_filetransfer_sftp_cwd_error() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -771,7 +787,7 @@ mod tests {
#[test]
fn test_filetransfer_sftp_ls() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -794,7 +810,7 @@ mod tests {
#[test]
fn test_filetransfer_sftp_stat() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -820,7 +836,7 @@ mod tests {
#[test]
fn test_filetransfer_sftp_recv() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -854,7 +870,7 @@ mod tests {
}
#[test]
fn test_filetransfer_sftp_recv_failed_nosuchfile() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
@@ -892,7 +908,7 @@ mod tests {
/* NOTE: the server doesn't allow you to create directories
#[test]
fn test_filetransfer_sftp_mkdir() {
let mut client: SftpFileTransfer = SftpFileTransfer::new();
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client.connect(String::from("test.rebex.net"), 22, Some(String::from("demo")), Some(String::from("password"))).is_ok());
let dir: String = String::from("foo");
// Mkdir
@@ -921,7 +937,7 @@ mod tests {
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
};
let mut sftp: SftpFileTransfer = SftpFileTransfer::new();
let mut sftp: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(sftp.change_dir(Path::new("/tmp")).is_err());
assert!(sftp.disconnect().is_err());
assert!(sftp.list_dir(Path::new("/tmp")).is_err());

View File

@@ -39,14 +39,14 @@ extern crate unicode_width;
// locals
use super::{Activity, Context};
use crate::filetransfer::FileTransferProtocol;
// File transfer
use crate::filetransfer::ftp_transfer::FtpFileTransfer;
use crate::filetransfer::scp_transfer::ScpFileTransfer;
use crate::filetransfer::sftp_transfer::SftpFileTransfer;
use crate::filetransfer::FileTransfer;
use crate::filetransfer::{FileTransfer, FileTransferProtocol};
use crate::fs::FsEntry;
use crate::system::config_client::ConfigClient;
use crate::system::environment;
use crate::system::sshkey_storage::SshKeyStorage;
// Includes
use chrono::{DateTime, Local};
@@ -311,9 +311,13 @@ impl FileTransferActivity {
quit: false,
context: None,
client: match protocol {
FileTransferProtocol::Sftp => Box::new(SftpFileTransfer::new()),
FileTransferProtocol::Sftp => {
Box::new(SftpFileTransfer::new(Self::make_ssh_storage()))
}
FileTransferProtocol::Ftp(ftps) => Box::new(FtpFileTransfer::new(ftps)),
FileTransferProtocol::Scp => Box::new(ScpFileTransfer::new()),
FileTransferProtocol::Scp => {
Box::new(ScpFileTransfer::new(Self::make_ssh_storage()))
}
},
params,
local: FileExplorer::new(),
@@ -329,6 +333,28 @@ impl FileTransferActivity {
transfer: TransferStates::default(),
}
}
/// ### make_ssh_storage
///
/// Make ssh storage using ConfigClient if possible, otherwise provide an empty storage.
/// This activity doesn't care of errors related to config client.
fn make_ssh_storage() -> SshKeyStorage {
match environment::init_config_dir() {
Ok(termscp_dir) => match termscp_dir {
Some(termscp_dir) => {
// Make configuration file path and ssh keys path
let (config_path, ssh_keys_path): (PathBuf, PathBuf) =
environment::get_config_paths(termscp_dir.as_path());
match ConfigClient::new(config_path.as_path(), ssh_keys_path.as_path()) {
Ok(config_client) => SshKeyStorage::storage_from_config(&config_client),
Err(_) => SshKeyStorage::empty(),
}
}
None => SshKeyStorage::empty(),
},
Err(_) => SshKeyStorage::empty(),
}
}
}
/**