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