sqlx_core_oldapi/postgres/message/
authentication.rs

1use std::str::from_utf8;
2
3use bytes::{Buf, Bytes};
4use memchr::memchr;
5
6use crate::error::Error;
7use crate::io::Decode;
8
9// On startup, the server sends an appropriate authentication request message,
10// to which the frontend must reply with an appropriate authentication
11// response message (such as a password).
12
13// For all authentication methods except GSSAPI, SSPI and SASL, there is at
14// most one request and one response. In some methods, no response at all is
15// needed from the frontend, and so no authentication request occurs.
16
17// For GSSAPI, SSPI and SASL, multiple exchanges of packets may
18// be needed to complete the authentication.
19
20// <https://www.postgresql.org/docs/devel/protocol-flow.html#id-1.10.5.7.3>
21// <https://www.postgresql.org/docs/devel/protocol-message-formats.html>
22
23#[derive(Debug)]
24pub enum Authentication {
25    /// The authentication exchange is successfully completed.
26    Ok,
27
28    /// The frontend must now send a [PasswordMessage] containing the
29    /// password in clear-text form.
30    CleartextPassword,
31
32    /// The frontend must now send a [PasswordMessage] containing the
33    /// password (with user name) encrypted via MD5, then encrypted
34    /// again using the 4-byte random salt.
35    Md5Password(AuthenticationMd5Password),
36
37    /// The frontend must now initiate a SASL negotiation,
38    /// using one of the SASL mechanisms listed in the message.
39    ///
40    /// The frontend will send a [SaslInitialResponse] with the name
41    /// of the selected mechanism, and the first part of the SASL
42    /// data stream in response to this.
43    ///
44    /// If further messages are needed, the server will
45    /// respond with [Authentication::SaslContinue].
46    Sasl(AuthenticationSasl),
47
48    /// This message contains challenge data from the previous step of SASL negotiation.
49    ///
50    /// The frontend must respond with a [SaslResponse] message.
51    SaslContinue(AuthenticationSaslContinue),
52
53    /// SASL authentication has completed with additional mechanism-specific
54    /// data for the client.
55    ///
56    /// The server will next send [Authentication::Ok] to
57    /// indicate successful authentication.
58    SaslFinal(AuthenticationSaslFinal),
59}
60
61impl Decode<'_> for Authentication {
62    fn decode_with(mut buf: Bytes, _: ()) -> Result<Self, Error> {
63        Ok(match buf.get_u32() {
64            0 => Authentication::Ok,
65
66            3 => Authentication::CleartextPassword,
67
68            5 => {
69                let mut salt = [0; 4];
70                buf.copy_to_slice(&mut salt);
71
72                Authentication::Md5Password(AuthenticationMd5Password { salt })
73            }
74
75            10 => Authentication::Sasl(AuthenticationSasl(buf)),
76            11 => Authentication::SaslContinue(AuthenticationSaslContinue::decode(buf)?),
77            12 => Authentication::SaslFinal(AuthenticationSaslFinal::decode(buf)?),
78
79            ty => {
80                return Err(err_protocol!("unknown authentication method: {}", ty));
81            }
82        })
83    }
84}
85
86/// Body of [Authentication::Md5Password].
87#[derive(Debug)]
88pub struct AuthenticationMd5Password {
89    pub salt: [u8; 4],
90}
91
92/// Body of [Authentication::Sasl].
93#[derive(Debug)]
94pub struct AuthenticationSasl(Bytes);
95
96impl AuthenticationSasl {
97    #[inline]
98    pub fn mechanisms(&self) -> SaslMechanisms<'_> {
99        SaslMechanisms(&self.0)
100    }
101}
102
103/// An iterator over the SASL authentication mechanisms provided by the server.
104pub struct SaslMechanisms<'a>(&'a [u8]);
105
106impl<'a> Iterator for SaslMechanisms<'a> {
107    type Item = &'a str;
108
109    fn next(&mut self) -> Option<Self::Item> {
110        if !self.0.is_empty() && self.0[0] == b'\0' {
111            return None;
112        }
113
114        let mechanism = memchr(b'\0', self.0).and_then(|nul| from_utf8(&self.0[..nul]).ok())?;
115
116        self.0 = &self.0[(mechanism.len() + 1)..];
117
118        Some(mechanism)
119    }
120}
121
122#[derive(Debug)]
123pub struct AuthenticationSaslContinue {
124    pub salt: Vec<u8>,
125    pub iterations: u32,
126    pub nonce: String,
127    pub message: String,
128}
129
130impl Decode<'_> for AuthenticationSaslContinue {
131    fn decode_with(buf: Bytes, _: ()) -> Result<Self, Error> {
132        let mut iterations: u32 = 4096;
133        let mut salt = Vec::new();
134        let mut nonce = Bytes::new();
135
136        // [Example]
137        // r=/z+giZiTxAH7r8sNAeHr7cvpqV3uo7G/bJBIJO3pjVM7t3ng,s=4UV68bIkC8f9/X8xH7aPhg==,i=4096
138
139        for item in buf.split(|b| *b == b',') {
140            let key = item[0];
141            let value = &item[2..];
142
143            match key {
144                b'r' => {
145                    nonce = buf.slice_ref(value);
146                }
147
148                b'i' => {
149                    iterations = atoi::atoi(value).unwrap_or(4096);
150                }
151
152                b's' => {
153                    salt =
154                        base64::Engine::decode(&base64::engine::general_purpose::STANDARD, value)
155                            .map_err(Error::protocol)?;
156                }
157
158                _ => {}
159            }
160        }
161
162        Ok(Self {
163            iterations,
164            salt,
165            nonce: from_utf8(&*nonce).map_err(Error::protocol)?.to_owned(),
166            message: from_utf8(&*buf).map_err(Error::protocol)?.to_owned(),
167        })
168    }
169}
170
171#[derive(Debug)]
172pub struct AuthenticationSaslFinal {
173    pub verifier: Vec<u8>,
174}
175
176impl Decode<'_> for AuthenticationSaslFinal {
177    fn decode_with(buf: Bytes, _: ()) -> Result<Self, Error> {
178        let mut verifier = Vec::new();
179
180        for item in buf.split(|b| *b == b',') {
181            let key = item[0];
182            let value = &item[2..];
183
184            if let b'v' = key {
185                verifier =
186                    base64::Engine::decode(&base64::engine::general_purpose::STANDARD, value)
187                        .map_err(Error::protocol)?;
188            }
189        }
190
191        Ok(Self { verifier })
192    }
193}