ssh/client/
client_auth.rs

1use std::io::{Read, Write};
2use tracing::*;
3
4use crate::{
5    algorithm::{compression, Compress, Digest},
6    constant::{ssh_connection_code, ssh_str, ssh_transport_code, ssh_user_auth_code},
7    error::{SshError, SshResult},
8    model::{Data, Packet, SecPacket},
9};
10
11use super::Client;
12
13impl Client {
14    pub fn do_auth<S>(&mut self, stream: &mut S, digest: &Digest) -> SshResult<()>
15    where
16        S: Read + Write,
17    {
18        info!("Auth start");
19        let mut data = Data::new();
20        data.put_u8(ssh_transport_code::SERVICE_REQUEST)
21            .put_str(ssh_str::SSH_USERAUTH);
22        data.pack(self).write_stream(stream)?;
23
24        let mut tried_public_key = false;
25        loop {
26            let mut data = Data::unpack(SecPacket::from_stream(stream, self)?)?;
27            let message_code = data.get_u8();
28            match message_code {
29                ssh_transport_code::SERVICE_ACCEPT => {
30                    if self.config.auth.key_pair.is_none() {
31                        tried_public_key = true;
32                        // if no private key specified
33                        // just try password auth
34                        self.password_authentication(stream)?
35                    } else {
36                        // if private key was provided
37                        // use public key auth first, then fallback to password auth
38                        self.public_key_authentication(stream)?
39                    }
40                }
41                ssh_user_auth_code::FAILURE => {
42                    if !tried_public_key {
43                        error!("user auth failure. (public key)");
44                        info!("fallback to password authentication");
45                        tried_public_key = true;
46                        // keep the same with openssh
47                        // if the public key auth failed
48                        // try with password again
49                        self.password_authentication(stream)?
50                    } else {
51                        error!("user auth failure. (password)");
52                        return Err(SshError::AuthError);
53                    }
54                }
55                ssh_user_auth_code::PK_OK => {
56                    info!("user auth support this algorithm.");
57                    self.public_key_signature(stream, digest)?
58                }
59                ssh_user_auth_code::SUCCESS => {
60                    info!("user auth successful.");
61                    // <https://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt>
62                    // Now we need turn on the compressor if any
63                    if let Compress::ZlibOpenSsh = self.negotiated.c_compress[0] {
64                        let comp = compression::from(&Compress::ZlibOpenSsh);
65                        self.compressor = comp;
66                    }
67                    return Ok(());
68                }
69                ssh_connection_code::GLOBAL_REQUEST => {
70                    let mut data = Data::new();
71                    data.put_u8(ssh_connection_code::REQUEST_FAILURE);
72                    data.pack(self).write_stream(stream)?;
73                }
74                _ => {}
75            }
76        }
77    }
78
79    fn password_authentication<S>(&mut self, stream: &mut S) -> SshResult<()>
80    where
81        S: Write,
82    {
83        info!("password authentication.");
84        let mut data = Data::new();
85        data.put_u8(ssh_user_auth_code::REQUEST)
86            .put_str(self.config.auth.username.as_str())
87            .put_str(ssh_str::SSH_CONNECTION)
88            .put_str(ssh_str::PASSWORD)
89            .put_u8(false as u8)
90            .put_str(self.config.auth.password.as_str());
91
92        data.pack(self).write_stream(stream)
93    }
94
95    fn public_key_authentication<S>(&mut self, stream: &mut S) -> SshResult<()>
96    where
97        S: Write,
98    {
99        let data = {
100            let pubkey_alg = &self.negotiated.public_key[0];
101            info!(
102                "public key authentication. algorithm: {}",
103                pubkey_alg.as_ref()
104            );
105            let mut data = Data::new();
106            data.put_u8(ssh_user_auth_code::REQUEST)
107                .put_str(self.config.auth.username.as_str())
108                .put_str(ssh_str::SSH_CONNECTION)
109                .put_str(ssh_str::PUBLIC_KEY)
110                .put_u8(false as u8)
111                .put_str(pubkey_alg.as_ref())
112                .put_u8s(
113                    &self
114                        .config
115                        .auth
116                        .key_pair
117                        .as_ref()
118                        .unwrap()
119                        .get_blob(pubkey_alg),
120                );
121            data
122        };
123        data.pack(self).write_stream(stream)
124    }
125
126    pub(crate) fn public_key_signature<S>(
127        &mut self,
128        stream: &mut S,
129        digest: &Digest,
130    ) -> SshResult<()>
131    where
132        S: Write,
133    {
134        let data = {
135            let pubkey_alg = &self.negotiated.public_key[0];
136
137            let mut data = Data::new();
138            data.put_u8(ssh_user_auth_code::REQUEST)
139                .put_str(self.config.auth.username.as_str())
140                .put_str(ssh_str::SSH_CONNECTION)
141                .put_str(ssh_str::PUBLIC_KEY)
142                .put_u8(true as u8)
143                .put_str(pubkey_alg.as_ref())
144                .put_u8s(
145                    &self
146                        .config
147                        .auth
148                        .key_pair
149                        .as_ref()
150                        .unwrap()
151                        .get_blob(pubkey_alg),
152                );
153            let signature = self.config.auth.key_pair.as_ref().unwrap().signature(
154                data.as_slice(),
155                digest.hash_ctx.clone(),
156                digest.key_exchange.as_ref().unwrap().get_hash_type(),
157                pubkey_alg,
158            );
159            data.put_u8s(&signature);
160            data
161        };
162        data.pack(self).write_stream(stream)
163    }
164}