1use libssh_rs::*;
2use std::io::Read;
3
4fn verify_known_hosts(sess: &Session) -> SshResult<()> {
5 let key = sess
6 .get_server_public_key()?
7 .get_public_key_hash_hexa(PublicKeyHashType::Sha256)?;
8
9 match sess.is_known_server()? {
10 KnownHosts::Ok => Ok(()),
11 KnownHosts::NotFound | KnownHosts::Unknown => {
12 eprintln!("The server is not a known host. Do you trust the host key?");
13 eprintln!("Public key hash: {}", key);
14
15 let input = prompt_stdin("Enter yes to trust the key: ")?;
16 if input == "yes" {
17 sess.update_known_hosts_file()
18 } else {
19 Err(Error::Fatal("untrusted server".to_string()))
20 }
21 }
22 KnownHosts::Changed => {
23 eprintln!("The key for the server has changed. It is now:");
24 eprintln!("{}", key);
25 Err(Error::Fatal("host key changed".to_string()))
26 }
27 KnownHosts::Other => {
28 eprintln!("The host key for this server was not found, but another");
29 eprintln!("type of key exists. An attacker might change the default");
30 eprintln!("server key to confuse your client into thinking the key");
31 eprintln!("does not exist");
32 Err(Error::Fatal("host key has wrong type".to_string()))
33 }
34 }
35}
36
37fn prompt(prompt: &str, echo: bool) -> SshResult<String> {
38 get_input(prompt, None, echo, false).ok_or_else(|| Error::Fatal("reading password".to_string()))
39}
40
41fn prompt_stdin(prompt: &str) -> SshResult<String> {
42 eprintln!("{}", prompt);
43 let mut input = String::new();
44 let _ = std::io::stdin().read_line(&mut input)?;
45 Ok(input.trim().to_string())
46}
47
48fn authenticate(sess: &Session, user_name: Option<&str>) -> SshResult<()> {
49 match sess.userauth_none(user_name)? {
50 AuthStatus::Success => return Ok(()),
51 _ => {}
52 }
53
54 loop {
55 let auth_methods = sess.userauth_list(user_name)?;
56
57 if auth_methods.contains(AuthMethods::PUBLIC_KEY) {
58 match sess.userauth_public_key_auto(None, None)? {
59 AuthStatus::Success => return Ok(()),
60 _ => {}
61 }
62 }
63
64 if auth_methods.contains(AuthMethods::INTERACTIVE) {
65 loop {
66 match sess.userauth_keyboard_interactive(None, None)? {
67 AuthStatus::Success => return Ok(()),
68 AuthStatus::Info => {
69 let info = sess.userauth_keyboard_interactive_info()?;
70 if !info.instruction.is_empty() {
71 eprintln!("{}", info.instruction);
72 }
73 let mut answers = vec![];
74 for p in &info.prompts {
75 answers.push(prompt(&p.prompt, p.echo)?);
76 }
77 sess.userauth_keyboard_interactive_set_answers(&answers)?;
78
79 continue;
80 }
81 AuthStatus::Denied => {
82 break;
83 }
84 status => {
85 return Err(Error::Fatal(format!(
86 "interactive auth status: {:?}",
87 status
88 )))
89 }
90 }
91 }
92 }
93
94 if auth_methods.contains(AuthMethods::PASSWORD) {
95 let pw = prompt("Password: ", false)?;
96
97 match sess.userauth_password(user_name, Some(&pw))? {
98 AuthStatus::Success => return Ok(()),
99 status => return Err(Error::Fatal(format!("password auth status: {:?}", status))),
100 }
101 }
102
103 return Err(Error::Fatal("unhandled auth case".to_string()));
104 }
105}
106
107fn main() -> SshResult<()> {
108 let sess = Session::new()?;
109 sess.set_auth_callback(|prompt, echo, verify, identity| {
110 let prompt = match identity {
111 Some(ident) => format!("{} ({}): ", prompt, ident),
112 None => prompt.to_string(),
113 };
114 get_input(&prompt, None, echo, verify)
115 .ok_or_else(|| Error::Fatal("reading password".to_string()))
116 });
117
118 sess.set_option(SshOption::Hostname("localhost".to_string()))?;
119 sess.options_parse_config(None)?;
121 sess.connect()?;
122 eprintln!(
123 "using {} as user name for authentication",
124 sess.get_user_name()?
125 );
126 verify_known_hosts(&sess)?;
127
128 authenticate(&sess, None)?;
129
130 let channel = sess.new_channel()?;
131 channel.open_session()?;
132 channel.request_exec("whoami")?;
133 channel.send_eof()?;
134
135 let mut stdout = String::new();
136 channel.stdout().read_to_string(&mut stdout)?;
137
138 eprintln!("whoami -> {}", stdout);
139
140 let res = channel.get_exit_status();
141 eprintln!("exit status: {:?}", res);
142
143 Ok(())
144}