1use std::fmt::Debug;
16use std::sync::Arc;
17use std::sync::atomic::Ordering;
18
19use ::auth::{
20 BEARER_TOKEN_USER, Identity, Password, PgAuthInfo, PgScramSha256Verifier, UserInfoRef,
21 UserProviderRef, userinfo_by_name,
22};
23use async_trait::async_trait;
24use common_catalog::parse_catalog_and_schema_from_db_string;
25use common_error::ext::ErrorExt;
26use common_error::status_code::StatusCode;
27use common_time::Timezone;
28use futures::{Sink, SinkExt};
29use pgwire::api::auth::StartupHandler;
30use pgwire::api::auth::cleartext::CleartextPasswordAuth;
31use pgwire::api::auth::mechanism::AuthMechanismFactory;
32use pgwire::api::auth::sasl::scram::{ScramAuth, ScramSecret};
33use pgwire::api::auth::{AuthOutcome, AuthSource, LoginInfo as WireLoginInfo};
34use pgwire::api::{ClientInfo, PidSecretKeyGenerator, auth};
35use pgwire::error::{ErrorInfo, PgWireError, PgWireResult};
36use pgwire::messages::response::ErrorResponse;
37use pgwire::messages::startup::SecretKey;
38use pgwire::messages::{PgWireBackendMessage, PgWireFrontendMessage};
39use session::Session;
40use snafu::IntoError;
41
42use crate::error::{AuthSnafu, Result};
43use crate::metrics::METRIC_AUTH_FAILURE;
44use crate::postgres::PostgresServerHandlerInner;
45use crate::postgres::types::PgErrorCode;
46use crate::postgres::utils::convert_err;
47use crate::query_handler::sql::ServerSqlQueryHandlerRef;
48
49pub(crate) struct ZeroPidSecretKeyGenerator;
52
53impl PidSecretKeyGenerator for ZeroPidSecretKeyGenerator {
54 fn generate(&self, _client: &dyn ClientInfo) -> (i32, SecretKey) {
55 (0, SecretKey::I32(0))
56 }
57}
58
59fn record_auth_failure(status_code: StatusCode) {
60 METRIC_AUTH_FAILURE
61 .with_label_values(&[status_code.as_ref()])
62 .inc();
63}
64
65pub(crate) struct PgUserProviderSource {
75 user_provider: UserProviderRef,
76}
77
78impl Debug for PgUserProviderSource {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 f.debug_struct("PgUserProviderSource")
81 .finish_non_exhaustive()
82 }
83}
84
85impl PgUserProviderSource {
86 pub(crate) fn new(user_provider: UserProviderRef) -> Self {
87 Self { user_provider }
88 }
89
90 fn resolve(login: &WireLoginInfo<'_>) -> Option<(String, String, String)> {
94 let user = login.user()?.to_string();
95 let (catalog, schema) = parse_catalog_and_schema_from_db_string(login.database()?);
96 Some((user, catalog, schema))
97 }
98
99 async fn postgres_auth_info(&self, login: &WireLoginInfo<'_>) -> Result<PgAuthInfo> {
103 let Some((user, catalog, _)) = Self::resolve(login) else {
104 return Ok(PgAuthInfo::Cleartext);
105 };
106
107 match self
108 .user_provider
109 .postgres_auth_info(Identity::UserId(&user, None), &catalog)
110 .await
111 {
112 Err(e) => {
113 record_auth_failure(e.status_code());
114 Err(AuthSnafu.into_error(e))
115 }
116 Ok(auth_info) => Ok(auth_info),
117 }
118 }
119
120 async fn authorize(
124 &self,
125 login: &WireLoginInfo<'_>,
126 user_info: &UserInfoRef,
127 ) -> PgWireResult<()> {
128 let Some((_, catalog, schema)) = Self::resolve(login) else {
129 return Err(PgWireError::UserNameRequired);
130 };
131
132 match self
133 .user_provider
134 .authorize(&catalog, &schema, user_info)
135 .await
136 {
137 Err(e) => {
138 record_auth_failure(e.status_code());
139 Err(PgWireError::ApiError(Box::new(AuthSnafu.into_error(e))))
140 }
141 Ok(()) => Ok(()),
142 }
143 }
144}
145
146#[async_trait]
147impl AuthSource<UserInfoRef> for PgUserProviderSource {
148 async fn get_password(
149 &self,
150 _login: &WireLoginInfo,
151 ) -> PgWireResult<pgwire::api::auth::Password> {
152 Err(PgWireError::ApiError(
153 "no cleartext password available from the user provider".into(),
154 ))
155 }
156
157 async fn verify_password(
160 &self,
161 login: &WireLoginInfo,
162 presented_password: &[u8],
163 ) -> PgWireResult<Option<UserInfoRef>> {
164 let Some((user, catalog, _schema)) = Self::resolve(login) else {
165 return Err(PgWireError::UserNameRequired);
166 };
167 let password = String::from_utf8_lossy(presented_password).into_owned();
168
169 let result = if user == BEARER_TOKEN_USER {
170 self.user_provider
171 .authenticate_bearer_token(&password, &catalog)
172 .await
173 } else {
174 self.user_provider
175 .authenticate(
176 Identity::UserId(&user, None),
177 Password::PlainText(password.into()),
178 )
179 .await
180 };
181 match result {
182 Err(e) => {
183 record_auth_failure(e.status_code());
184 Err(PgWireError::ApiError(Box::new(AuthSnafu.into_error(e))))
185 }
186 Ok(user_info) => Ok(Some(user_info)),
187 }
188 }
189
190 async fn get_scram_secret(
191 &self,
192 login: &WireLoginInfo,
193 ) -> PgWireResult<(ScramSecret, Option<UserInfoRef>)> {
194 match self.postgres_auth_info(login).await {
195 Ok(PgAuthInfo::ScramSha256 {
199 verifier,
200 user_info,
201 }) => Ok((to_scram_secret(&verifier), user_info)),
202 _ => {
205 let user = login.user().unwrap_or_default();
206 Ok((
207 to_scram_secret(&PgScramSha256Verifier::mock_for_unknown_user(
208 user.as_bytes(),
209 )),
210 None,
211 ))
212 }
213 }
214 }
215}
216
217fn to_scram_secret(verifier: &PgScramSha256Verifier) -> ScramSecret {
218 ScramSecret::Verifier {
219 salt: verifier.salt().to_vec(),
220 iterations: verifier.iterations() as usize,
221 stored_key: verifier.stored_key().to_vec(),
222 server_key: verifier.server_key().to_vec(),
223 }
224}
225
226fn set_client_info<C>(client: &mut C, session: &Session)
227where
228 C: ClientInfo,
229{
230 if let Some(current_catalog) = client.metadata().get(super::METADATA_CATALOG) {
231 session.set_catalog(current_catalog.clone());
232 }
233 if let Some(current_schema) = client.metadata().get(super::METADATA_SCHEMA) {
234 session.set_schema(current_schema.clone());
235 }
236}
237
238enum MechanismSelection {
239 NoAuth,
241 Cleartext(Arc<PgUserProviderSource>),
242 Scram(Arc<PgUserProviderSource>),
243 Failed,
245}
246
247impl PostgresServerHandlerInner {
248 async fn select_mechanism<C>(&self, client: &C) -> MechanismSelection
249 where
250 C: ClientInfo + Sync,
251 {
252 let Some(source) = self.auth_source.clone() else {
253 return MechanismSelection::NoAuth;
254 };
255 let login = WireLoginInfo::from_client_info(client);
256 if login.user() == Some(BEARER_TOKEN_USER) {
257 return MechanismSelection::Cleartext(source);
258 }
259 match source.postgres_auth_info(&login).await {
260 Err(_) => MechanismSelection::Failed,
261 Ok(PgAuthInfo::Cleartext) => MechanismSelection::Cleartext(source),
262 Ok(PgAuthInfo::ScramSha256 { .. }) => MechanismSelection::Scram(source),
263 }
264 }
265}
266
267#[async_trait]
268impl StartupHandler for PostgresServerHandlerInner {
269 async fn on_startup<C>(
270 &self,
271 client: &mut C,
272 message: PgWireFrontendMessage,
273 ) -> PgWireResult<()>
274 where
275 C: ClientInfo + Sink<PgWireBackendMessage> + Unpin + Send + Sync,
276 C::Error: Debug,
277 PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>,
278 {
279 match message {
280 PgWireFrontendMessage::Startup(ref startup) => {
281 if !client.is_secure() && self.force_tls {
283 send_error(
284 client,
285 PgErrorCode::Ec28000.to_err_info("No encryption".to_string()),
286 )
287 .await?;
288 return Ok(());
289 }
290
291 auth::protocol_negotiation(client, startup).await?;
292 auth::save_startup_parameters_to_metadata(client, startup);
293
294 match resolve_db_info(client, self.query_handler.clone()).await? {
296 DbResolution::Resolved(catalog, schema) => {
297 let metadata = client.metadata_mut();
298 let _ = metadata.insert(super::METADATA_CATALOG.to_owned(), catalog);
299 let _ = metadata.insert(super::METADATA_SCHEMA.to_owned(), schema);
300 }
301 DbResolution::NotFound(msg) => {
302 send_error(client, PgErrorCode::Ec3D000.to_err_info(msg)).await?;
303 return Ok(());
304 }
305 }
306
307 if let Some(tz) = client.metadata().get("TimeZone") {
309 match Timezone::from_tz_string(tz) {
310 Ok(tz) => self.session.set_timezone(tz),
311 Err(_) => {
312 send_error(
313 client,
314 PgErrorCode::Ec22023
315 .to_err_info(format!("Invalid TimeZone: {}", tz)),
316 )
317 .await?;
318
319 return Ok(());
320 }
321 }
322 }
323
324 match self.select_mechanism(client).await {
325 MechanismSelection::NoAuth => {
326 self.session.set_user_info(userinfo_by_name(
327 client.metadata().get(super::METADATA_USER).cloned(),
328 ));
329 set_client_info(client, &self.session);
330 self.auth_driver
331 .complete_authentication(client, None)
332 .await?;
333 }
334 MechanismSelection::Cleartext(source) => {
335 if self
336 .auth_driver
337 .begin_authentication(
338 client,
339 CleartextPasswordAuth::new(source).create(),
340 )
341 .await
342 .is_err()
343 {
344 return send_password_authentication_failed(client).await;
345 }
346 }
347 MechanismSelection::Scram(source) => {
348 self.is_scram_auth.store(true, Ordering::Relaxed);
349 if self
350 .auth_driver
351 .begin_authentication(client, ScramAuth::new(source).create())
352 .await
353 .is_err()
354 {
355 return send_password_authentication_failed(client).await;
356 }
357 }
358 MechanismSelection::Failed => {
359 return send_password_authentication_failed(client).await;
360 }
361 }
362 }
363 PgWireFrontendMessage::PasswordMessageFamily(pwd) => {
364 match self.auth_driver.advance_authentication(client, pwd).await {
365 Ok(AuthOutcome::InProgress) => {}
366 Ok(AuthOutcome::Authenticated {
367 identity,
368 final_message,
369 }) => {
370 self.is_scram_auth.store(false, Ordering::Relaxed);
371 let Some(user_info) = identity else {
372 return send_password_authentication_failed(client).await;
373 };
374
375 let authorized = {
380 let login = WireLoginInfo::from_client_info(client);
381 match &self.auth_source {
382 Some(source) => source.authorize(&login, &user_info).await.is_ok(),
383 None => false,
384 }
385 };
386 if !authorized {
387 return send_password_authentication_failed(client).await;
388 }
389
390 self.session.set_user_info(user_info);
391 set_client_info(client, &self.session);
392 self.auth_driver
393 .complete_authentication(client, final_message)
394 .await?;
395 }
396 Err(_e) => {
397 if self.is_scram_auth.swap(false, Ordering::Relaxed) {
401 record_auth_failure(StatusCode::UserPasswordMismatch);
402 }
403 return send_password_authentication_failed(client).await;
404 }
405 }
406 }
407 _ => {}
408 }
409 Ok(())
410 }
411}
412
413async fn send_error<C>(client: &mut C, err_info: ErrorInfo) -> PgWireResult<()>
414where
415 C: ClientInfo + Sink<PgWireBackendMessage> + Unpin + Send,
416 C::Error: Debug,
417 PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>,
418{
419 let error = ErrorResponse::from(err_info);
420 client
421 .feed(PgWireBackendMessage::ErrorResponse(error))
422 .await?;
423 client.close().await?;
424 Ok(())
425}
426
427async fn send_password_authentication_failed<C>(client: &mut C) -> PgWireResult<()>
428where
429 C: ClientInfo + Sink<PgWireBackendMessage> + Unpin + Send,
430 C::Error: Debug,
431 PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>,
432{
433 send_error(
434 client,
435 PgErrorCode::Ec28P01.to_err_info("password authentication failed".to_string()),
436 )
437 .await
438}
439
440enum DbResolution {
441 Resolved(String, String),
442 NotFound(String),
443}
444
445async fn resolve_db_info<C>(
447 client: &mut C,
448 query_handler: ServerSqlQueryHandlerRef,
449) -> PgWireResult<DbResolution>
450where
451 C: ClientInfo + Unpin + Send,
452{
453 let db_ref = client.metadata().get(super::METADATA_DATABASE);
454 if let Some(db) = db_ref {
455 let (catalog, schema) = parse_catalog_and_schema_from_db_string(db);
456 if query_handler
457 .is_valid_schema(&catalog, &schema)
458 .await
459 .map_err(convert_err)?
460 {
461 Ok(DbResolution::Resolved(catalog, schema))
462 } else {
463 Ok(DbResolution::NotFound(format!("Database not found: {db}")))
464 }
465 } else {
466 Ok(DbResolution::NotFound("Database not specified".to_owned()))
467 }
468}