sqlx_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::ProtocolDecode;
8
9use crate::message::{BackendMessage, BackendMessageFormat};
10use base64::prelude::{Engine as _, BASE64_STANDARD};
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 BackendMessage for Authentication {
64    const FORMAT: BackendMessageFormat = BackendMessageFormat::Authentication;
65
66    fn decode_body(mut buf: Bytes) -> Result<Self, Error> {
67        Ok(match buf.get_u32() {
68            0 => Authentication::Ok,
69
70            3 => Authentication::CleartextPassword,
71
72            5 => {
73                let mut salt = [0; 4];
74                buf.copy_to_slice(&mut salt);
75
76                Authentication::Md5Password(AuthenticationMd5Password { salt })
77            }
78
79            10 => Authentication::Sasl(AuthenticationSasl(buf)),
80            11 => Authentication::SaslContinue(AuthenticationSaslContinue::decode(buf)?),
81            12 => Authentication::SaslFinal(AuthenticationSaslFinal::decode(buf)?),
82
83            ty => {
84                return Err(err_protocol!("unknown authentication method: {}", ty));
85            }
86        })
87    }
88}
89
90/// Body of [Authentication::Md5Password].
91#[derive(Debug)]
92pub struct AuthenticationMd5Password {
93    pub salt: [u8; 4],
94}
95
96/// Body of [Authentication::Sasl].
97#[derive(Debug)]
98pub struct AuthenticationSasl(Bytes);
99
100impl AuthenticationSasl {
101    #[inline]
102    pub fn mechanisms(&self) -> SaslMechanisms<'_> {
103        SaslMechanisms(&self.0)
104    }
105}
106
107/// An iterator over the SASL authentication mechanisms provided by the server.
108pub struct SaslMechanisms<'a>(&'a [u8]);
109
110impl<'a> Iterator for SaslMechanisms<'a> {
111    type Item = &'a str;
112
113    fn next(&mut self) -> Option<Self::Item> {
114        if !self.0.is_empty() && self.0[0] == b'\0' {
115            return None;
116        }
117
118        let mechanism = memchr(b'\0', self.0).and_then(|nul| from_utf8(&self.0[..nul]).ok())?;
119
120        self.0 = &self.0[(mechanism.len() + 1)..];
121
122        Some(mechanism)
123    }
124}
125
126#[derive(Debug)]
127pub struct AuthenticationSaslContinue {
128    pub salt: Vec<u8>,
129    pub iterations: u32,
130    pub nonce: String,
131    pub message: String,
132}
133
134impl ProtocolDecode<'_> for AuthenticationSaslContinue {
135    fn decode_with(buf: Bytes, _: ()) -> Result<Self, Error> {
136        let mut iterations: u32 = 4096;
137        let mut salt = Vec::new();
138        let mut nonce = Bytes::new();
139
140        // [Example]
141        // r=/z+giZiTxAH7r8sNAeHr7cvpqV3uo7G/bJBIJO3pjVM7t3ng,s=4UV68bIkC8f9/X8xH7aPhg==,i=4096
142
143        for item in buf.split(|b| *b == b',') {
144            let key = item[0];
145            let value = &item[2..];
146
147            match key {
148                b'r' => {
149                    nonce = buf.slice_ref(value);
150                }
151
152                b'i' => {
153                    iterations = atoi::atoi(value).unwrap_or(4096);
154                }
155
156                b's' => {
157                    salt = BASE64_STANDARD.decode(value).map_err(Error::protocol)?;
158                }
159
160                _ => {}
161            }
162        }
163
164        Ok(Self {
165            iterations,
166            salt,
167            nonce: from_utf8(&nonce).map_err(Error::protocol)?.to_owned(),
168            message: from_utf8(&buf).map_err(Error::protocol)?.to_owned(),
169        })
170    }
171}
172
173#[derive(Debug)]
174pub struct AuthenticationSaslFinal {
175    pub verifier: Vec<u8>,
176}
177
178impl ProtocolDecode<'_> for AuthenticationSaslFinal {
179    fn decode_with(buf: Bytes, _: ()) -> Result<Self, Error> {
180        let mut verifier = Vec::new();
181
182        for item in buf.split(|b| *b == b',') {
183            let key = item[0];
184            let value = &item[2..];
185
186            if let b'v' = key {
187                verifier = BASE64_STANDARD.decode(value).map_err(Error::protocol)?;
188            }
189        }
190
191        Ok(Self { verifier })
192    }
193}