1use std::fmt::Debug;
16use std::sync::Exclusive;
17
18use ::auth::{
19 BEARER_TOKEN_USER, Identity, Password, PgAuthInfo, PgScramSha256Verifier, UserInfoRef,
20 UserProviderRef, userinfo_by_name,
21};
22use async_trait::async_trait;
23use base64::Engine;
24use base64::engine::general_purpose::STANDARD as BASE64;
25use common_catalog::parse_catalog_and_schema_from_db_string;
26use common_error::ext::ErrorExt;
27use common_error::status_code::StatusCode;
28use common_time::Timezone;
29use futures::{Sink, SinkExt};
30use pgwire::api::auth::StartupHandler;
31use pgwire::api::auth::sasl::SCRAM_SHA_256_METHOD;
32use pgwire::api::{ClientInfo, PgWireConnectionState, auth};
33use pgwire::error::{ErrorInfo, PgWireError, PgWireResult};
34use pgwire::messages::response::ErrorResponse;
35use pgwire::messages::startup::{Authentication, PasswordMessageFamily, SecretKey};
36use pgwire::messages::{PgWireBackendMessage, PgWireFrontendMessage};
37use session::Session;
38use snafu::IntoError;
39use tokio::sync::Mutex;
40
41use crate::error::{AuthSnafu, Result};
42use crate::metrics::METRIC_AUTH_FAILURE;
43use crate::postgres::PostgresServerHandlerInner;
44use crate::postgres::types::PgErrorCode;
45use crate::postgres::utils::convert_err;
46use crate::query_handler::sql::ServerSqlQueryHandlerRef;
47
48pub(crate) struct PgLoginVerifier {
49 user_provider: Option<UserProviderRef>,
50 state: Mutex<PgAuthenticationState>,
51}
52
53impl PgLoginVerifier {
54 pub(crate) fn new(user_provider: Option<UserProviderRef>) -> Self {
55 Self {
56 user_provider,
57 state: Mutex::new(PgAuthenticationState::Initial),
58 }
59 }
60}
61
62enum PgAuthenticationState {
63 Initial,
64 Cleartext,
65 SaslInitial {
66 auth_info: PgAuthInfo,
67 },
68 SaslFinal {
69 verifier: PgScramSha256Verifier,
70 user_info: Option<UserInfoRef>,
71 channel_binding: String,
72 nonce: String,
73 client_first_bare: String,
74 server_first: String,
75 },
76}
77
78#[allow(dead_code)]
79struct LoginInfo {
80 user: Option<String>,
81 catalog: Option<String>,
82 schema: Option<String>,
83 host: String,
84}
85
86impl LoginInfo {
87 pub fn from_client_info<C>(client: &C) -> LoginInfo
88 where
89 C: ClientInfo,
90 {
91 LoginInfo {
92 user: client.metadata().get(super::METADATA_USER).map(Into::into),
93 catalog: client
94 .metadata()
95 .get(super::METADATA_CATALOG)
96 .map(Into::into),
97 schema: client
98 .metadata()
99 .get(super::METADATA_SCHEMA)
100 .map(Into::into),
101 host: client.socket_addr().ip().to_string(),
102 }
103 }
104}
105
106impl PgLoginVerifier {
107 async fn auth(&self, login: &LoginInfo, password: &str) -> Result<Option<UserInfoRef>> {
108 let user_provider = match &self.user_provider {
109 Some(provider) => provider,
110 None => return Ok(None),
111 };
112
113 let user_name = match &login.user {
114 Some(name) => name,
115 None => return Ok(None),
116 };
117 let catalog = match &login.catalog {
118 Some(name) => name,
119 None => return Ok(None),
120 };
121 let schema = match &login.schema {
122 Some(name) => name,
123 None => return Ok(None),
124 };
125
126 let result = if user_name == BEARER_TOKEN_USER {
127 user_provider
128 .auth_bearer_token(password, catalog, schema)
129 .await
130 } else {
131 user_provider
132 .auth(
133 Identity::UserId(user_name, None),
134 Password::PlainText(password.to_string().into()),
135 catalog,
136 schema,
137 )
138 .await
139 };
140 match result {
141 Err(e) => {
142 METRIC_AUTH_FAILURE
143 .with_label_values(&[e.status_code().as_ref()])
144 .inc();
145 Err(AuthSnafu.into_error(e))
146 }
147 Ok(user_info) => Ok(Some(user_info)),
148 }
149 }
150
151 async fn postgres_auth_info(&self, login: &LoginInfo) -> Result<PgAuthInfo> {
152 let user_provider = match &self.user_provider {
153 Some(provider) => provider,
154 None => return Ok(PgAuthInfo::Cleartext),
155 };
156
157 let user_name = match &login.user {
158 Some(name) => name,
159 None => return Ok(PgAuthInfo::Cleartext),
160 };
161 if user_name == BEARER_TOKEN_USER {
162 return Ok(PgAuthInfo::Cleartext);
163 }
164 let catalog = match &login.catalog {
165 Some(name) => name,
166 None => return Ok(PgAuthInfo::Cleartext),
167 };
168
169 match user_provider
170 .postgres_auth_info(Identity::UserId(user_name, None), catalog)
171 .await
172 {
173 Err(e) => {
174 METRIC_AUTH_FAILURE
175 .with_label_values(&[e.status_code().as_ref()])
176 .inc();
177 Err(AuthSnafu.into_error(e))
178 }
179 Ok(auth_info) => Ok(auth_info),
180 }
181 }
182
183 async fn authorize(&self, login: &LoginInfo, user_info: &UserInfoRef) -> Result<()> {
184 let user_provider = match &self.user_provider {
185 Some(provider) => provider,
186 None => return Ok(()),
187 };
188
189 let catalog = match &login.catalog {
190 Some(name) => name,
191 None => return Ok(()),
192 };
193 let schema = match &login.schema {
194 Some(name) => name,
195 None => return Ok(()),
196 };
197
198 match user_provider.authorize(catalog, schema, user_info).await {
199 Err(e) => {
200 METRIC_AUTH_FAILURE
201 .with_label_values(&[e.status_code().as_ref()])
202 .inc();
203 Err(AuthSnafu.into_error(e))
204 }
205 Ok(()) => Ok(()),
206 }
207 }
208}
209
210fn set_client_info<C>(client: &mut C, session: &Session)
211where
212 C: ClientInfo,
213{
214 if let Some(current_catalog) = client.metadata().get(super::METADATA_CATALOG) {
215 session.set_catalog(current_catalog.clone());
216 }
217 if let Some(current_schema) = client.metadata().get(super::METADATA_SCHEMA) {
218 session.set_schema(current_schema.clone());
219 }
220
221 client.set_pid_and_secret_key(0, SecretKey::I32(0));
225 }
227
228#[async_trait]
229impl StartupHandler for PostgresServerHandlerInner {
230 async fn on_startup<C>(
231 &self,
232 client: &mut C,
233 message: PgWireFrontendMessage,
234 ) -> PgWireResult<()>
235 where
236 C: ClientInfo + Sink<PgWireBackendMessage> + Unpin + Send,
237 C::Error: Debug,
238 PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>,
239 {
240 match message {
241 PgWireFrontendMessage::Startup(ref startup) => {
242 if !client.is_secure() && self.force_tls {
244 send_error(
245 client,
246 PgErrorCode::Ec28000.to_err_info("No encryption".to_string()),
247 )
248 .await?;
249 return Ok(());
250 }
251
252 auth::save_startup_parameters_to_metadata(client, startup);
253
254 match resolve_db_info(Exclusive::new(client), self.query_handler.clone()).await? {
256 DbResolution::Resolved(catalog, schema) => {
257 let metadata = client.metadata_mut();
258 let _ = metadata.insert(super::METADATA_CATALOG.to_owned(), catalog);
259 let _ = metadata.insert(super::METADATA_SCHEMA.to_owned(), schema);
260 }
261 DbResolution::NotFound(msg) => {
262 send_error(client, PgErrorCode::Ec3D000.to_err_info(msg)).await?;
263 return Ok(());
264 }
265 }
266
267 if let Some(tz) = client.metadata().get("TimeZone") {
269 match Timezone::from_tz_string(tz) {
270 Ok(tz) => self.session.set_timezone(tz),
271 Err(_) => {
272 send_error(
273 client,
274 PgErrorCode::Ec22023
275 .to_err_info(format!("Invalid TimeZone: {}", tz)),
276 )
277 .await?;
278
279 return Ok(());
280 }
281 }
282 }
283
284 if self.login_verifier.user_provider.is_some() {
285 let login_info = LoginInfo::from_client_info(client);
286 let auth_info = match self.login_verifier.postgres_auth_info(&login_info).await
287 {
288 Ok(auth_info) => auth_info,
289 Err(_) => {
290 return send_password_authentication_failed(client).await;
291 }
292 };
293 client.set_state(PgWireConnectionState::AuthenticationInProgress);
294 match auth_info {
295 PgAuthInfo::ScramSha256 { .. } => {
296 *self.login_verifier.state.lock().await =
297 PgAuthenticationState::SaslInitial { auth_info };
298 client
299 .send(PgWireBackendMessage::Authentication(Authentication::SASL(
300 vec![SCRAM_SHA_256_METHOD.to_string()],
301 )))
302 .await?;
303 }
304 PgAuthInfo::Cleartext => {
305 *self.login_verifier.state.lock().await =
306 PgAuthenticationState::Cleartext;
307 client
308 .send(PgWireBackendMessage::Authentication(
309 Authentication::CleartextPassword,
310 ))
311 .await?;
312 }
313 }
314 } else {
315 self.session.set_user_info(userinfo_by_name(
316 client.metadata().get(super::METADATA_USER).cloned(),
317 ));
318 set_client_info(client, &self.session);
319 auth::finish_authentication(client, self.param_provider.as_ref()).await?;
320 }
321 }
322 PgWireFrontendMessage::PasswordMessageFamily(pwd) => {
323 let login_info = LoginInfo::from_client_info(client);
324 match self
325 .authenticate_password_message(client, &login_info, pwd)
326 .await?
327 {
328 PgAuthenticationResult::Continue => {}
329 PgAuthenticationResult::Success(user_info) => {
330 self.session.set_user_info(user_info);
331 set_client_info(client, &self.session);
332 auth::finish_authentication(client, self.param_provider.as_ref()).await?;
333 }
334 PgAuthenticationResult::Failed => {
335 return send_password_authentication_failed(client).await;
336 }
337 }
338 }
339 _ => {}
340 }
341 Ok(())
342 }
343}
344
345enum PgAuthenticationResult {
346 Continue,
347 Success(UserInfoRef),
348 Failed,
349}
350
351impl PostgresServerHandlerInner {
352 fn record_scram_failure(result: PgAuthenticationResult) -> PgAuthenticationResult {
356 if matches!(result, PgAuthenticationResult::Failed) {
357 METRIC_AUTH_FAILURE
358 .with_label_values(&[StatusCode::UserPasswordMismatch.as_ref()])
359 .inc();
360 }
361 result
362 }
363
364 async fn authenticate_password_message<C>(
365 &self,
366 client: &mut C,
367 login_info: &LoginInfo,
368 pwd: PasswordMessageFamily,
369 ) -> PgWireResult<PgAuthenticationResult>
370 where
371 C: ClientInfo + Sink<PgWireBackendMessage> + Unpin + Send,
372 C::Error: Debug,
373 PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>,
374 {
375 let mut state = self.login_verifier.state.lock().await;
376 let current_state = std::mem::replace(&mut *state, PgAuthenticationState::Initial);
377
378 match current_state {
379 PgAuthenticationState::Cleartext => {
380 let pwd = pwd.into_password()?;
381 drop(state);
382 match self.login_verifier.auth(login_info, &pwd.password).await {
383 Ok(Some(user_info)) => Ok(PgAuthenticationResult::Success(user_info)),
384 Ok(None) | Err(_) => Ok(PgAuthenticationResult::Failed),
385 }
386 }
387 PgAuthenticationState::SaslInitial { auth_info } => {
388 let result = 'sasl: {
393 let sasl_initial = pwd.into_sasl_initial_response()?;
394 if sasl_initial.auth_method != SCRAM_SHA_256_METHOD {
395 break 'sasl PgAuthenticationResult::Failed;
396 }
397 let Some(data) = sasl_initial.data else {
398 break 'sasl PgAuthenticationResult::Failed;
399 };
400 let Some(client_first) = ScramClientFirst::parse(&data) else {
401 break 'sasl PgAuthenticationResult::Failed;
402 };
403 let PgAuthInfo::ScramSha256 {
404 verifier,
405 user_info,
406 } = auth_info
407 else {
408 break 'sasl PgAuthenticationResult::Failed;
409 };
410
411 let server_nonce = BASE64.encode(rand::random::<[u8; 18]>());
412 let nonce = format!("{}{}", client_first.nonce, server_nonce);
413 let server_first = format!(
414 "r={},s={},i={}",
415 nonce,
416 BASE64.encode(verifier.salt()),
417 verifier.iterations()
418 );
419 client
420 .send(PgWireBackendMessage::Authentication(
421 Authentication::SASLContinue(server_first.clone().into()),
422 ))
423 .await?;
424 *state = PgAuthenticationState::SaslFinal {
425 verifier,
426 user_info,
427 channel_binding: client_first.channel_binding,
428 nonce,
429 client_first_bare: client_first.bare,
430 server_first,
431 };
432 PgAuthenticationResult::Continue
433 };
434 Ok(Self::record_scram_failure(result))
435 }
436 PgAuthenticationState::SaslFinal {
437 verifier,
438 user_info,
439 channel_binding,
440 nonce,
441 client_first_bare,
442 server_first,
443 } => {
444 let result = 'sasl: {
445 let sasl_response = pwd.into_sasl_response()?;
446 let Some(client_final) = ScramClientFinal::parse(&sasl_response.data) else {
447 break 'sasl PgAuthenticationResult::Failed;
448 };
449 if client_final.channel_binding != channel_binding {
450 break 'sasl PgAuthenticationResult::Failed;
451 }
452 if client_final.nonce != nonce {
453 break 'sasl PgAuthenticationResult::Failed;
454 }
455
456 let auth_message = format!(
457 "{},{},{}",
458 client_first_bare, server_first, client_final.without_proof
459 );
460 let Ok(client_proof) = BASE64.decode(client_final.proof.as_bytes()) else {
461 break 'sasl PgAuthenticationResult::Failed;
462 };
463 let Ok(Some(server_signature)) =
464 verifier.verify_client_proof(auth_message.as_bytes(), &client_proof)
465 else {
466 break 'sasl PgAuthenticationResult::Failed;
467 };
468 let Some(user_info) = user_info else {
469 break 'sasl PgAuthenticationResult::Failed;
470 };
471
472 drop(state);
473 if self
474 .login_verifier
475 .authorize(login_info, &user_info)
476 .await
477 .is_err()
478 {
479 return Ok(PgAuthenticationResult::Failed);
482 }
483
484 client
485 .send(PgWireBackendMessage::Authentication(
486 Authentication::SASLFinal(
487 format!("v={}", BASE64.encode(server_signature)).into(),
488 ),
489 ))
490 .await?;
491 PgAuthenticationResult::Success(user_info)
492 };
493 Ok(Self::record_scram_failure(result))
494 }
495 PgAuthenticationState::Initial => Ok(PgAuthenticationResult::Failed),
496 }
497 }
498}
499
500struct ScramClientFirst {
501 channel_binding: String,
502 bare: String,
503 nonce: String,
504}
505
506impl ScramClientFirst {
507 fn parse(data: &[u8]) -> Option<Self> {
508 let message = std::str::from_utf8(data).ok()?;
509 let mut parts = message.splitn(3, ',');
510 let cbind = parts.next()?;
511 if !matches!(cbind, "n" | "y") {
512 return None;
513 }
514 let authzid = parts.next()?;
515 if !authzid.is_empty() {
516 return None;
517 }
518 let channel_binding = BASE64.encode(format!("{cbind},,"));
519 let bare = parts.next()?.to_string();
520 let nonce = bare
521 .split(',')
522 .find_map(|chunk| chunk.strip_prefix("r="))?
523 .to_string();
524 Some(Self {
525 channel_binding,
526 bare,
527 nonce,
528 })
529 }
530}
531
532struct ScramClientFinal {
533 channel_binding: String,
534 nonce: String,
535 without_proof: String,
536 proof: String,
537}
538
539impl ScramClientFinal {
540 fn parse(data: &[u8]) -> Option<Self> {
541 let message = std::str::from_utf8(data).ok()?;
542 let proof_pos = message.rfind(",p=")?;
543 let without_proof = message[..proof_pos].to_string();
544 let proof = message[proof_pos + 3..].to_string();
545 let channel_binding = without_proof
546 .split(',')
547 .find_map(|chunk| chunk.strip_prefix("c="))?;
548 let nonce = without_proof
549 .split(',')
550 .find_map(|chunk| chunk.strip_prefix("r="))?;
551 if nonce.is_empty() || proof.is_empty() {
552 return None;
553 }
554
555 Some(Self {
556 channel_binding: channel_binding.to_string(),
557 nonce: nonce.to_string(),
558 without_proof,
559 proof,
560 })
561 }
562}
563
564async fn send_error<C>(client: &mut C, err_info: ErrorInfo) -> PgWireResult<()>
565where
566 C: ClientInfo + Sink<PgWireBackendMessage> + Unpin + Send,
567 C::Error: Debug,
568 PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>,
569{
570 let error = ErrorResponse::from(err_info);
571 client
572 .feed(PgWireBackendMessage::ErrorResponse(error))
573 .await?;
574 client.close().await?;
575 Ok(())
576}
577
578async fn send_password_authentication_failed<C>(client: &mut C) -> PgWireResult<()>
579where
580 C: ClientInfo + Sink<PgWireBackendMessage> + Unpin + Send,
581 C::Error: Debug,
582 PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>,
583{
584 send_error(
585 client,
586 PgErrorCode::Ec28P01.to_err_info("password authentication failed".to_string()),
587 )
588 .await
589}
590
591enum DbResolution {
592 Resolved(String, String),
593 NotFound(String),
594}
595
596async fn resolve_db_info<C>(
598 client: Exclusive<&mut C>,
599 query_handler: ServerSqlQueryHandlerRef,
600) -> PgWireResult<DbResolution>
601where
602 C: ClientInfo + Unpin + Send,
603{
604 let db_ref = client.into_inner().metadata().get(super::METADATA_DATABASE);
605 if let Some(db) = db_ref {
606 let (catalog, schema) = parse_catalog_and_schema_from_db_string(db);
607 if query_handler
608 .is_valid_schema(&catalog, &schema)
609 .await
610 .map_err(convert_err)?
611 {
612 Ok(DbResolution::Resolved(catalog, schema))
613 } else {
614 Ok(DbResolution::NotFound(format!("Database not found: {db}")))
615 }
616 } else {
617 Ok(DbResolution::NotFound("Database not specified".to_owned()))
618 }
619}
620
621#[cfg(test)]
622mod tests {
623 use super::*;
624
625 #[test]
626 fn test_scram_client_first_parse() {
627 let message = b"n,,n=greptime,r=clientnonce";
628 let client_first = ScramClientFirst::parse(message).unwrap();
629 assert_eq!("biws", client_first.channel_binding);
630 assert_eq!("n=greptime,r=clientnonce", client_first.bare);
631 assert_eq!("clientnonce", client_first.nonce);
632
633 let message = b"y,,n=greptime,r=clientnonce";
634 let client_first = ScramClientFirst::parse(message).unwrap();
635 assert_eq!("eSws", client_first.channel_binding);
636 assert_eq!("n=greptime,r=clientnonce", client_first.bare);
637 assert_eq!("clientnonce", client_first.nonce);
638
639 assert!(ScramClientFirst::parse(b"p=tls-server-end-point,,n=greptime,r=nonce").is_none());
640 assert!(ScramClientFirst::parse(b"n,a=authzid,n=greptime,r=nonce").is_none());
641 }
642
643 #[test]
644 fn test_scram_client_final_parse() {
645 let message = b"c=biws,r=clientnonceservernonce,p=dGVzdA==";
646 let client_final = ScramClientFinal::parse(message).unwrap();
647 assert_eq!("biws", client_final.channel_binding);
648 assert_eq!("clientnonceservernonce", client_final.nonce);
649 assert_eq!(
650 "c=biws,r=clientnonceservernonce",
651 client_final.without_proof
652 );
653 assert_eq!("dGVzdA==", client_final.proof);
654
655 assert!(ScramClientFinal::parse(b"r=nonce,p=dGVzdA==").is_none());
656 assert!(ScramClientFinal::parse(b"c=biws,r=nonce").is_none());
657 assert!(ScramClientFinal::parse(b"c=biws,r=,p=dGVzdA==").is_none());
658 }
659}