sqlx_etorreborre_postgres/message/
authentication.rs

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