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