Skip to main content

auth/
common.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::sync::{Arc, LazyLock};
16
17use common_base::secrets::SecretString;
18use digest::Digest;
19use hmac::{Hmac, Mac};
20use pbkdf2::pbkdf2_hmac;
21use sha1::Sha1;
22use sha2::Sha256;
23use snafu::{OptionExt, ensure};
24use subtle::ConstantTimeEq;
25
26use crate::error::{IllegalParamSnafu, InvalidConfigSnafu, Result, UserPasswordMismatchSnafu};
27use crate::user_info::DefaultUserInfo;
28use crate::user_provider::static_user_provider::{STATIC_USER_PROVIDER, StaticUserProvider};
29use crate::user_provider::watch_file_user_provider::{
30    WATCH_FILE_USER_PROVIDER, WatchFileUserProvider,
31};
32use crate::{UserInfoRef, UserProviderRef};
33
34pub(crate) const DEFAULT_USERNAME: &str = "greptime";
35pub const DEFAULT_PBKDF2_SHA256_ITERATIONS: u32 = 4096;
36pub const DEFAULT_PBKDF2_SHA256_SALT_LEN: usize = 16;
37pub const PBKDF2_SHA256_HASH_LEN: usize = 32;
38pub const MAX_PBKDF2_SHA256_ITERATIONS: u32 = 1_000_000;
39pub const MAX_PBKDF2_SHA256_SALT_LEN: usize = 1024;
40pub const PG_SCRAM_SHA256_KEY_LEN: usize = 32;
41
42type HmacSha256 = Hmac<Sha256>;
43
44/// Process-wide secret used to derive a stable mock salt for unknown users, so
45/// the SCRAM `server-first-message` can't be used to enumerate usernames.
46static PG_SCRAM_MOCK_SECRET: LazyLock<[u8; PG_SCRAM_SHA256_KEY_LEN]> = LazyLock::new(rand::random);
47
48/// construct a [`UserInfo`](crate::user_info::UserInfo) impl with name
49/// use default username `greptime` if None is provided
50pub fn userinfo_by_name(username: Option<String>) -> UserInfoRef {
51    DefaultUserInfo::with_name(username.unwrap_or_else(|| DEFAULT_USERNAME.to_string()))
52}
53
54pub fn user_provider_from_option(opt: &str) -> Result<UserProviderRef> {
55    let (name, content) = opt.split_once(':').with_context(|| InvalidConfigSnafu {
56        value: opt.to_string(),
57        msg: "UserProviderOption must be in format `<option>:<value>`",
58    })?;
59    match name {
60        STATIC_USER_PROVIDER => {
61            let provider =
62                StaticUserProvider::new(content).map(|p| Arc::new(p) as UserProviderRef)?;
63            Ok(provider)
64        }
65        WATCH_FILE_USER_PROVIDER => {
66            WatchFileUserProvider::new(content).map(|p| Arc::new(p) as UserProviderRef)
67        }
68        _ => InvalidConfigSnafu {
69            value: name.to_string(),
70            msg: "Invalid UserProviderOption",
71        }
72        .fail(),
73    }
74}
75
76pub fn static_user_provider_from_option(opt: &str) -> Result<StaticUserProvider> {
77    let (name, content) = opt.split_once(':').with_context(|| InvalidConfigSnafu {
78        value: opt.to_string(),
79        msg: "UserProviderOption must be in format `<option>:<value>`",
80    })?;
81    match name {
82        STATIC_USER_PROVIDER => {
83            let provider = StaticUserProvider::new(content)?;
84            Ok(provider)
85        }
86        _ => InvalidConfigSnafu {
87            value: name.to_string(),
88            msg: format!("Invalid UserProviderOption, expect only {STATIC_USER_PROVIDER}"),
89        }
90        .fail(),
91    }
92}
93
94type Username<'a> = &'a str;
95type HostOrIp<'a> = &'a str;
96
97#[derive(Debug, Clone)]
98pub enum Identity<'a> {
99    UserId(Username<'a>, Option<HostOrIp<'a>>),
100}
101
102pub type HashedPassword<'a> = &'a [u8];
103pub type Salt<'a> = &'a [u8];
104
105/// Authentication information sent by the client.
106pub enum Password<'a> {
107    PlainText(SecretString),
108    MysqlNativePassword(HashedPassword<'a>, Salt<'a>),
109    PgMD5(HashedPassword<'a>, Salt<'a>),
110}
111
112impl Password<'_> {
113    pub fn r#type(&self) -> &str {
114        match self {
115            Password::PlainText(_) => "plain_text",
116            Password::MysqlNativePassword(_, _) => "mysql_native_password",
117            Password::PgMD5(_, _) => "pg_md5",
118        }
119    }
120}
121
122pub fn auth_mysql(
123    auth_data: HashedPassword,
124    salt: Salt,
125    username: &str,
126    save_pwd: &[u8],
127) -> Result<()> {
128    let hash_stage_2 = mysql_native_password_hash(save_pwd);
129    auth_mysql_with_hash_stage_2(auth_data, salt, username, &hash_stage_2)
130}
131
132pub(crate) fn auth_mysql_with_hash_stage_2(
133    auth_data: HashedPassword,
134    salt: Salt,
135    username: &str,
136    hash_stage_2: &[u8],
137) -> Result<()> {
138    ensure!(
139        auth_data.len() == 20,
140        IllegalParamSnafu {
141            msg: "Illegal mysql password length"
142        }
143    );
144    ensure!(
145        hash_stage_2.len() == 20,
146        InvalidConfigSnafu {
147            value: hash_stage_2.len().to_string(),
148            msg: "Illegal mysql native password verifier length",
149        }
150    );
151    // ref: https://github.com/mysql/mysql-server/blob/a246bad76b9271cb4333634e954040a970222e0a/sql/auth/password.cc#L62
152    let tmp = sha1_two(salt, hash_stage_2);
153    // xor auth_data and tmp
154    let mut xor_result = [0u8; 20];
155    for i in 0..20 {
156        xor_result[i] = auth_data[i] ^ tmp[i];
157    }
158    let candidate_stage_2 = sha1_one(&xor_result);
159    if candidate_stage_2 == hash_stage_2 {
160        Ok(())
161    } else {
162        UserPasswordMismatchSnafu {
163            username: username.to_string(),
164        }
165        .fail()
166    }
167}
168
169pub fn mysql_native_password_hash(save_pwd: &[u8]) -> Vec<u8> {
170    double_sha1(save_pwd)
171}
172
173pub fn format_mysql_native_password_verifier(password: &[u8]) -> String {
174    format!(
175        "mysql_native_password:{}",
176        hex::encode(mysql_native_password_hash(password))
177    )
178}
179
180pub fn format_pbkdf2_sha256_password_verifier(
181    password: &[u8],
182    salt: &[u8],
183    iterations: u32,
184) -> Result<String> {
185    ensure!(
186        iterations > 0 && iterations <= MAX_PBKDF2_SHA256_ITERATIONS,
187        IllegalParamSnafu {
188            msg: format!(
189                "pbkdf2_sha256 iterations must be in 1..={}",
190                MAX_PBKDF2_SHA256_ITERATIONS
191            )
192        }
193    );
194    ensure!(
195        !salt.is_empty() && salt.len() <= MAX_PBKDF2_SHA256_SALT_LEN,
196        IllegalParamSnafu {
197            msg: format!(
198                "pbkdf2_sha256 salt length must be in 1..={}",
199                MAX_PBKDF2_SHA256_SALT_LEN
200            )
201        }
202    );
203
204    let mut hash = [0u8; PBKDF2_SHA256_HASH_LEN];
205    pbkdf2_hmac::<Sha256>(password, salt, iterations, &mut hash);
206    Ok(format!(
207        "pbkdf2_sha256:{iterations}:{}:{}",
208        hex::encode(salt),
209        hex::encode(hash)
210    ))
211}
212
213#[derive(Clone, PartialEq, Eq)]
214pub struct PgScramSha256Verifier {
215    iterations: u32,
216    salt: Vec<u8>,
217    stored_key: Vec<u8>,
218    server_key: Vec<u8>,
219}
220
221impl std::fmt::Debug for PgScramSha256Verifier {
222    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223        f.debug_struct("PgScramSha256Verifier")
224            .field("iterations", &self.iterations)
225            .field("salt", &"<REDACTED>")
226            .field("stored_key", &"<REDACTED>")
227            .field("server_key", &"<REDACTED>")
228            .finish()
229    }
230}
231
232impl PgScramSha256Verifier {
233    pub fn new(
234        iterations: u32,
235        salt: Vec<u8>,
236        stored_key: Vec<u8>,
237        server_key: Vec<u8>,
238    ) -> Result<Self> {
239        ensure!(
240            iterations > 0 && iterations <= MAX_PBKDF2_SHA256_ITERATIONS,
241            IllegalParamSnafu {
242                msg: format!(
243                    "pg_scram_sha256 iterations must be in 1..={}",
244                    MAX_PBKDF2_SHA256_ITERATIONS
245                )
246            }
247        );
248        ensure!(
249            !salt.is_empty() && salt.len() <= MAX_PBKDF2_SHA256_SALT_LEN,
250            IllegalParamSnafu {
251                msg: format!(
252                    "pg_scram_sha256 salt length must be in 1..={}",
253                    MAX_PBKDF2_SHA256_SALT_LEN
254                )
255            }
256        );
257        ensure!(
258            stored_key.len() == PG_SCRAM_SHA256_KEY_LEN,
259            IllegalParamSnafu {
260                msg: "pg_scram_sha256 stored key must be 32 bytes"
261            }
262        );
263        ensure!(
264            server_key.len() == PG_SCRAM_SHA256_KEY_LEN,
265            IllegalParamSnafu {
266                msg: "pg_scram_sha256 server key must be 32 bytes"
267            }
268        );
269
270        Ok(Self {
271            iterations,
272            salt,
273            stored_key,
274            server_key,
275        })
276    }
277
278    pub fn from_password(password: &[u8], salt: &[u8], iterations: u32) -> Result<Self> {
279        let salted_password = pg_scram_sha256_salted_password(password, salt, iterations)?;
280        Self::from_salted_password(salted_password, salt.to_vec(), iterations)
281    }
282
283    pub fn from_salted_password(
284        salted_password: Vec<u8>,
285        salt: Vec<u8>,
286        iterations: u32,
287    ) -> Result<Self> {
288        ensure!(
289            salted_password.len() == PG_SCRAM_SHA256_KEY_LEN,
290            IllegalParamSnafu {
291                msg: "pg_scram_sha256 salted password must be 32 bytes"
292            }
293        );
294        let client_key = hmac_sha256(&salted_password, b"Client Key");
295        let stored_key = sha256(&client_key);
296        let server_key = hmac_sha256(&salted_password, b"Server Key");
297        Self::new(iterations, salt, stored_key, server_key)
298    }
299
300    /// Builds a throwaway verifier for an unknown user. The salt is derived
301    /// deterministically from the username and a process-wide secret, so the
302    /// SCRAM `server-first-message` (salt + iterations) stays stable per
303    /// username and indistinguishable from a real user across reconnects. No
304    /// PBKDF2 is run (avoids a CPU-exhaustion DoS keyed on unknown usernames),
305    /// and the random keys guarantee the client proof never matches.
306    pub fn mock_for_unknown_user(username: &[u8]) -> Self {
307        let mock_salt = hmac_sha256(PG_SCRAM_MOCK_SECRET.as_slice(), username);
308        Self {
309            iterations: DEFAULT_PBKDF2_SHA256_ITERATIONS,
310            salt: mock_salt[..DEFAULT_PBKDF2_SHA256_SALT_LEN].to_vec(),
311            stored_key: rand::random::<[u8; PG_SCRAM_SHA256_KEY_LEN]>().to_vec(),
312            server_key: rand::random::<[u8; PG_SCRAM_SHA256_KEY_LEN]>().to_vec(),
313        }
314    }
315
316    pub fn iterations(&self) -> u32 {
317        self.iterations
318    }
319
320    pub fn salt(&self) -> &[u8] {
321        &self.salt
322    }
323
324    pub fn verify_plain_password(&self, password: &[u8]) -> Result<bool> {
325        let salted_password =
326            pg_scram_sha256_salted_password(password, &self.salt, self.iterations)?;
327        let client_key = hmac_sha256(&salted_password, b"Client Key");
328        let stored_key = sha256(&client_key);
329        Ok(self.stored_key.ct_eq(&stored_key).into())
330    }
331
332    pub fn verify_client_proof(
333        &self,
334        auth_message: &[u8],
335        client_proof: &[u8],
336    ) -> Result<Option<Vec<u8>>> {
337        if client_proof.len() != PG_SCRAM_SHA256_KEY_LEN {
338            return Ok(None);
339        }
340
341        let client_signature = hmac_sha256(&self.stored_key, auth_message);
342        let client_key = xor(client_proof, &client_signature);
343        let stored_key = sha256(&client_key);
344        if self.stored_key.ct_eq(&stored_key).into() {
345            Ok(Some(hmac_sha256(&self.server_key, auth_message)))
346        } else {
347            Ok(None)
348        }
349    }
350}
351
352pub fn format_pg_scram_sha256_password_verifier(
353    password: &[u8],
354    salt: &[u8],
355    iterations: u32,
356) -> Result<String> {
357    let verifier = PgScramSha256Verifier::from_password(password, salt, iterations)?;
358    Ok(format!(
359        "pg_scram_sha256:{iterations}:{}:{}:{}",
360        hex::encode(verifier.salt),
361        hex::encode(verifier.stored_key),
362        hex::encode(verifier.server_key)
363    ))
364}
365
366fn pg_scram_sha256_salted_password(
367    password: &[u8],
368    salt: &[u8],
369    iterations: u32,
370) -> Result<Vec<u8>> {
371    ensure!(
372        iterations > 0 && iterations <= MAX_PBKDF2_SHA256_ITERATIONS,
373        IllegalParamSnafu {
374            msg: format!(
375                "pg_scram_sha256 iterations must be in 1..={}",
376                MAX_PBKDF2_SHA256_ITERATIONS
377            )
378        }
379    );
380    ensure!(
381        !salt.is_empty() && salt.len() <= MAX_PBKDF2_SHA256_SALT_LEN,
382        IllegalParamSnafu {
383            msg: format!(
384                "pg_scram_sha256 salt length must be in 1..={}",
385                MAX_PBKDF2_SHA256_SALT_LEN
386            )
387        }
388    );
389
390    let prepared_password = std::str::from_utf8(password)
391        .ok()
392        .and_then(|password| stringprep::saslprep(password).ok());
393    let password = prepared_password
394        .as_deref()
395        .map(str::as_bytes)
396        .unwrap_or(password);
397
398    let mut salted_password = [0u8; PG_SCRAM_SHA256_KEY_LEN];
399    pbkdf2_hmac::<Sha256>(password, salt, iterations, &mut salted_password);
400    Ok(salted_password.to_vec())
401}
402
403fn hmac_sha256(key: &[u8], msg: &[u8]) -> Vec<u8> {
404    let mut mac = HmacSha256::new_from_slice(key).expect("HMAC accepts any key length");
405    mac.update(msg);
406    mac.finalize().into_bytes().to_vec()
407}
408
409fn sha256(data: &[u8]) -> Vec<u8> {
410    let mut hasher = Sha256::new();
411    hasher.update(data);
412    hasher.finalize().to_vec()
413}
414
415fn xor(lhs: &[u8], rhs: &[u8]) -> Vec<u8> {
416    lhs.iter().zip(rhs).map(|(l, r)| l ^ r).collect()
417}
418
419fn sha1_two(input_1: &[u8], input_2: &[u8]) -> Vec<u8> {
420    let mut hasher = Sha1::new();
421    hasher.update(input_1);
422    hasher.update(input_2);
423    hasher.finalize().to_vec()
424}
425
426fn sha1_one(data: &[u8]) -> Vec<u8> {
427    let mut hasher = Sha1::new();
428    hasher.update(data);
429    hasher.finalize().to_vec()
430}
431
432fn double_sha1(data: &[u8]) -> Vec<u8> {
433    sha1_one(&sha1_one(data))
434}
435
436#[cfg(test)]
437mod tests {
438    use super::*;
439
440    #[test]
441    fn test_sha() {
442        let sha_1_answer: Vec<u8> = vec![
443            124, 74, 141, 9, 202, 55, 98, 175, 97, 229, 149, 32, 148, 61, 194, 100, 148, 248, 148,
444            27,
445        ];
446        let sha_1 = sha1_one("123456".as_bytes());
447        assert_eq!(sha_1, sha_1_answer);
448
449        let double_sha1_answer: Vec<u8> = vec![
450            107, 180, 131, 126, 183, 67, 41, 16, 94, 228, 86, 141, 218, 125, 198, 126, 210, 202,
451            42, 217,
452        ];
453        let double_sha1 = double_sha1("123456".as_bytes());
454        assert_eq!(double_sha1, double_sha1_answer);
455
456        let sha1_2_answer: Vec<u8> = vec![
457            132, 115, 215, 211, 99, 186, 164, 206, 168, 152, 217, 192, 117, 47, 240, 252, 142, 244,
458            37, 204,
459        ];
460        let sha1_2 = sha1_two("123456".as_bytes(), "654321".as_bytes());
461        assert_eq!(sha1_2, sha1_2_answer);
462    }
463
464    #[test]
465    fn test_format_mysql_native_password_verifier() {
466        let verifier = format_mysql_native_password_verifier("123456".as_bytes());
467        assert_eq!(
468            "mysql_native_password:6bb4837eb74329105ee4568dda7dc67ed2ca2ad9",
469            verifier
470        );
471    }
472
473    #[test]
474    fn test_format_pbkdf2_sha256_password_verifier() {
475        let verifier =
476            format_pbkdf2_sha256_password_verifier("password".as_bytes(), b"salt", 4096).unwrap();
477        assert_eq!(
478            "pbkdf2_sha256:4096:73616c74:c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a",
479            verifier
480        );
481
482        assert!(format_pbkdf2_sha256_password_verifier(b"password", b"", 4096).is_err());
483        assert!(format_pbkdf2_sha256_password_verifier(b"password", b"salt", 0).is_err());
484        assert!(
485            format_pbkdf2_sha256_password_verifier(
486                b"password",
487                b"salt",
488                MAX_PBKDF2_SHA256_ITERATIONS + 1,
489            )
490            .is_err()
491        );
492    }
493
494    #[test]
495    fn test_format_pg_scram_sha256_password_verifier() {
496        let verifier =
497            format_pg_scram_sha256_password_verifier("password".as_bytes(), b"salt", 4096).unwrap();
498        assert_eq!(
499            "pg_scram_sha256:4096:73616c74:945e1c466fc9932efadc23781edc5d1e78d5e10f005933652af1a6105154f084:b9bf0e811b1fb6793671c0cc3adedf7c75cd72291191092ad65878c5a02aad2c",
500            verifier
501        );
502    }
503
504    #[test]
505    fn test_pg_scram_sha256_applies_saslprep() {
506        let normalized = PgScramSha256Verifier::from_password(b"pass word", b"salt", 4096).unwrap();
507        let non_breaking_space =
508            PgScramSha256Verifier::from_password("pass\u{00a0}word".as_bytes(), b"salt", 4096)
509                .unwrap();
510
511        assert_eq!(normalized, non_breaking_space);
512        assert!(
513            non_breaking_space
514                .verify_plain_password(b"pass word")
515                .unwrap()
516        );
517    }
518
519    #[test]
520    fn test_pg_scram_sha256_uses_original_bytes_when_saslprep_is_not_possible() {
521        let invalid_utf8 = b"password\xff";
522        let prohibited = b"password\x07";
523
524        for password in [invalid_utf8.as_slice(), prohibited.as_slice()] {
525            let verifier = PgScramSha256Verifier::from_password(password, b"salt", 4096).unwrap();
526            assert!(verifier.verify_plain_password(password).unwrap());
527        }
528    }
529
530    #[test]
531    fn test_mock_verifier_is_stable_per_username() {
532        let alice = PgScramSha256Verifier::mock_for_unknown_user(b"alice");
533        let alice_again = PgScramSha256Verifier::mock_for_unknown_user(b"alice");
534        let bob = PgScramSha256Verifier::mock_for_unknown_user(b"bob");
535
536        // Same username must yield the same salt and iterations across reconnects,
537        // otherwise the SCRAM server-first message leaks user (non-)existence.
538        assert_eq!(alice.salt(), alice_again.salt());
539        assert_ne!(alice.salt(), bob.salt());
540        assert_eq!(alice.iterations(), DEFAULT_PBKDF2_SHA256_ITERATIONS);
541        assert_eq!(alice.salt().len(), DEFAULT_PBKDF2_SHA256_SALT_LEN);
542
543        // The mock verifier must never accept a client proof.
544        assert!(
545            alice
546                .verify_client_proof(b"auth-message", &[0u8; PG_SCRAM_SHA256_KEY_LEN])
547                .unwrap()
548                .is_none()
549        );
550    }
551}