whoami/
whoami.rs

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.set_option(SshOption::LogLevel(LogLevel::Packet))?;
120    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}