use libssh_rs::*;
use std::io::Read;
fn verify_known_hosts(sess: &Session) -> SshResult<()> {
let key = sess
.get_server_public_key()?
.get_public_key_hash_hexa(PublicKeyHashType::Sha256)?;
match sess.is_known_server()? {
KnownHosts::Ok => Ok(()),
KnownHosts::NotFound | KnownHosts::Unknown => {
eprintln!("The server is not a known host. Do you trust the host key?");
eprintln!("Public key hash: {}", key);
let input = prompt_stdin("Enter yes to trust the key: ")?;
if input == "yes" {
sess.update_known_hosts_file()
} else {
Err(Error::Fatal("untrusted server".to_string()))
}
}
KnownHosts::Changed => {
eprintln!("The key for the server has changed. It is now:");
eprintln!("{}", key);
Err(Error::Fatal("host key changed".to_string()))
}
KnownHosts::Other => {
eprintln!("The host key for this server was not found, but another");
eprintln!("type of key exists. An attacker might change the default");
eprintln!("server key to confuse your client into thinking the key");
eprintln!("does not exist");
Err(Error::Fatal("host key has wrong type".to_string()))
}
}
}
fn prompt(prompt: &str, echo: bool) -> SshResult<String> {
get_input(prompt, None, echo, false).ok_or_else(|| Error::Fatal("reading password".to_string()))
}
fn prompt_stdin(prompt: &str) -> SshResult<String> {
eprintln!("{}", prompt);
let mut input = String::new();
let _ = std::io::stdin().read_line(&mut input)?;
Ok(input.trim().to_string())
}
fn authenticate(sess: &Session, user_name: Option<&str>) -> SshResult<()> {
match sess.userauth_none(user_name)? {
AuthStatus::Success => return Ok(()),
_ => {}
}
loop {
let auth_methods = sess.userauth_list(user_name)?;
if auth_methods.contains(AuthMethods::PUBLIC_KEY) {
match sess.userauth_public_key_auto(None, None)? {
AuthStatus::Success => return Ok(()),
_ => {}
}
}
if auth_methods.contains(AuthMethods::INTERACTIVE) {
loop {
match sess.userauth_keyboard_interactive(None, None)? {
AuthStatus::Success => return Ok(()),
AuthStatus::Info => {
let info = sess.userauth_keyboard_interactive_info()?;
if !info.instruction.is_empty() {
eprintln!("{}", info.instruction);
}
let mut answers = vec![];
for p in &info.prompts {
answers.push(prompt(&p.prompt, p.echo)?);
}
sess.userauth_keyboard_interactive_set_answers(&answers)?;
continue;
}
AuthStatus::Denied => {
break;
}
status => {
return Err(Error::Fatal(format!(
"interactive auth status: {:?}",
status
)))
}
}
}
}
if auth_methods.contains(AuthMethods::PASSWORD) {
let pw = prompt("Password: ", false)?;
match sess.userauth_password(user_name, Some(&pw))? {
AuthStatus::Success => return Ok(()),
status => return Err(Error::Fatal(format!("password auth status: {:?}", status))),
}
}
return Err(Error::Fatal("unhandled auth case".to_string()));
}
}
fn main() -> SshResult<()> {
let sess = Session::new()?;
sess.set_auth_callback(|prompt, echo, verify, identity| {
let prompt = match identity {
Some(ident) => format!("{} ({}): ", prompt, ident),
None => prompt.to_string(),
};
get_input(&prompt, None, echo, verify)
.ok_or_else(|| Error::Fatal("reading password".to_string()))
});
sess.set_option(SshOption::Hostname("localhost".to_string()))?;
sess.options_parse_config(None)?;
sess.connect()?;
eprintln!(
"using {} as user name for authentication",
sess.get_user_name()?
);
verify_known_hosts(&sess)?;
authenticate(&sess, None)?;
let channel = sess.new_channel()?;
channel.open_session()?;
channel.request_exec("whoami")?;
channel.send_eof()?;
let mut stdout = String::new();
channel.stdout().read_to_string(&mut stdout)?;
eprintln!("whoami -> {}", stdout);
let res = channel.get_exit_status();
eprintln!("exit status: {:?}", res);
Ok(())
}