Skip to main content

rbdc_pg/message/
authentication.rs

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