1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use {
    super::{types::AuthPlugin, Connection, ParseBuf, Socket},
    crate::{
        error::ProtocolError,
        packets::{AuthSwitchRequest, ErrPacket},
        Deserialize, Error,
    },
    std::{future::Future, pin::Pin},
};

impl<T: Socket> Connection<T> {
    pub(super) fn continue_auth(
        &mut self,
    ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + '_>> {
        match self.options.auth_plugin.unwrap_or(self.data.auth_plugin) {
            AuthPlugin::Sha2 => {
                unimplemented!(concat!(
                    "caching_sha2_password_auth is not yet implemented.\n",
                    "You can change the auth method of an user by running `alter user \"user\"@\"host\" identified with mysql_native_password by \"password\"`.\n",
                    "To list all users, you can run `select user, host from mysql.user`.\n"
                ));
            }
            AuthPlugin::Native | AuthPlugin::Clear => {
                Box::pin(self.continue_mysql_native_password_auth())
            }
        }
    }

    async fn continue_mysql_native_password_auth(&mut self) -> Result<(), Error> {
        let packet = self.read_packet().await?;
        match packet.first() {
            Some(0x00) => Ok(()),
            Some(0xfe) if !self.data.auth_switched => {
                let auth_switch = AuthSwitchRequest::deserialize(&mut ParseBuf(&packet), ())?;
                self.perform_auth_switch(auth_switch).await
            }
            _ => Err(
                match ErrPacket::deserialize(&mut ParseBuf(&packet), self.data.capabilities) {
                    Ok(err) => err.into(),
                    Err(_) => {
                        ProtocolError::unexpected_packet(packet.to_vec(), Some("Ok or auth switch"))
                            .into()
                    }
                },
            ),
        }
    }

    async fn perform_auth_switch(
        &mut self,
        auth_switch_request: AuthSwitchRequest,
    ) -> Result<(), Error> {
        assert!(
            !self.data.auth_switched,
            "auth_switched flag should be checked by caller"
        );

        self.data.auth_switched = true;
        self.data.auth_plugin = auth_switch_request.plugin();
        self.data.nonce = auth_switch_request.into_data();

        let plugin_data = self.data.auth_plugin.gen_data(
            &self.options.password,
            &self.data.nonce,
            &self.options,
        )?;

        if let Some(plugin_data) = plugin_data {
            self.write_struct(&plugin_data).await?;
        } else {
            self.write_packet(&[]).await?;
        }

        self.continue_auth().await
    }
}