Skip to content

Commit 670311d

Browse files
AndrewJackson2020Andrew JacksonCommanderKeynes
authored
Implement Trust Authentication (#805)
* Implement Trust Authentication * Remove remaining LDAP stuff * Reverted LDAP changes, Cleaned up tests --------- Co-authored-by: Andrew Jackson <[email protected]> Co-authored-by: CommanderKeynes <[email protected]>
1 parent b9ec7f8 commit 670311d

File tree

7 files changed

+339
-150
lines changed

7 files changed

+339
-150
lines changed

src/auth_passthrough.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::config::AuthType;
12
use crate::errors::Error;
23
use crate::pool::ConnectionPool;
34
use crate::server::Server;
@@ -71,6 +72,7 @@ impl AuthPassthrough {
7172
pub async fn fetch_hash(&self, address: &crate::config::Address) -> Result<String, Error> {
7273
let auth_user = crate::config::User {
7374
username: self.user.clone(),
75+
auth_type: AuthType::MD5,
7476
password: Some(self.password.clone()),
7577
server_username: None,
7678
server_password: None,

src/client.rs

+184-127
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use tokio::sync::mpsc::Sender;
1414

1515
use crate::admin::{generate_server_parameters_for_admin, handle_admin};
1616
use crate::auth_passthrough::refetch_auth_hash;
17-
use crate::config::{get_config, get_idle_client_in_transaction_timeout, Address, PoolMode};
17+
use crate::config::{
18+
get_config, get_idle_client_in_transaction_timeout, Address, AuthType, PoolMode,
19+
};
1820
use crate::constants::*;
1921
use crate::messages::*;
2022
use crate::plugins::PluginOutput;
@@ -463,8 +465,8 @@ where
463465
.count()
464466
== 1;
465467

466-
// Kick any client that's not admin while we're in admin-only mode.
467468
if !admin && admin_only {
469+
// Kick any client that's not admin while we're in admin-only mode.
468470
debug!(
469471
"Rejecting non-admin connection to {} when in admin only mode",
470472
pool_name
@@ -481,72 +483,76 @@ where
481483
let process_id: i32 = rand::random();
482484
let secret_key: i32 = rand::random();
483485

484-
// Perform MD5 authentication.
485-
// TODO: Add SASL support.
486-
let salt = md5_challenge(&mut write).await?;
487-
488-
let code = match read.read_u8().await {
489-
Ok(p) => p,
490-
Err(_) => {
491-
return Err(Error::ClientSocketError(
492-
"password code".into(),
493-
client_identifier,
494-
))
495-
}
496-
};
497-
498-
// PasswordMessage
499-
if code as char != 'p' {
500-
return Err(Error::ProtocolSyncError(format!(
501-
"Expected p, got {}",
502-
code as char
503-
)));
504-
}
505-
506-
let len = match read.read_i32().await {
507-
Ok(len) => len,
508-
Err(_) => {
509-
return Err(Error::ClientSocketError(
510-
"password message length".into(),
511-
client_identifier,
512-
))
513-
}
514-
};
515-
516-
let mut password_response = vec![0u8; (len - 4) as usize];
517-
518-
match read.read_exact(&mut password_response).await {
519-
Ok(_) => (),
520-
Err(_) => {
521-
return Err(Error::ClientSocketError(
522-
"password message".into(),
523-
client_identifier,
524-
))
525-
}
526-
};
527-
528486
let mut prepared_statements_enabled = false;
529487

530488
// Authenticate admin user.
531489
let (transaction_mode, mut server_parameters) = if admin {
532490
let config = get_config();
491+
// TODO: Add SASL support.
492+
// Perform MD5 authentication.
493+
match config.general.admin_auth_type {
494+
AuthType::Trust => (),
495+
AuthType::MD5 => {
496+
let salt = md5_challenge(&mut write).await?;
497+
498+
let code = match read.read_u8().await {
499+
Ok(p) => p,
500+
Err(_) => {
501+
return Err(Error::ClientSocketError(
502+
"password code".into(),
503+
client_identifier,
504+
))
505+
}
506+
};
507+
508+
// PasswordMessage
509+
if code as char != 'p' {
510+
return Err(Error::ProtocolSyncError(format!(
511+
"Expected p, got {}",
512+
code as char
513+
)));
514+
}
533515

534-
// Compare server and client hashes.
535-
let password_hash = md5_hash_password(
536-
&config.general.admin_username,
537-
&config.general.admin_password,
538-
&salt,
539-
);
516+
let len = match read.read_i32().await {
517+
Ok(len) => len,
518+
Err(_) => {
519+
return Err(Error::ClientSocketError(
520+
"password message length".into(),
521+
client_identifier,
522+
))
523+
}
524+
};
540525

541-
if password_hash != password_response {
542-
let error = Error::ClientGeneralError("Invalid password".into(), client_identifier);
526+
let mut password_response = vec![0u8; (len - 4) as usize];
543527

544-
warn!("{}", error);
545-
wrong_password(&mut write, username).await?;
528+
match read.read_exact(&mut password_response).await {
529+
Ok(_) => (),
530+
Err(_) => {
531+
return Err(Error::ClientSocketError(
532+
"password message".into(),
533+
client_identifier,
534+
))
535+
}
536+
};
546537

547-
return Err(error);
548-
}
538+
// Compare server and client hashes.
539+
let password_hash = md5_hash_password(
540+
&config.general.admin_username,
541+
&config.general.admin_password,
542+
&salt,
543+
);
544+
545+
if password_hash != password_response {
546+
let error =
547+
Error::ClientGeneralError("Invalid password".into(), client_identifier);
549548

549+
warn!("{}", error);
550+
wrong_password(&mut write, username).await?;
551+
552+
return Err(error);
553+
}
554+
}
555+
}
550556
(false, generate_server_parameters_for_admin())
551557
}
552558
// Authenticate normal user.
@@ -573,92 +579,143 @@ where
573579
// Obtain the hash to compare, we give preference to that written in cleartext in config
574580
// if there is nothing set in cleartext and auth passthrough (auth_query) is configured, we use the hash obtained
575581
// when the pool was created. If there is no hash there, we try to fetch it one more time.
576-
let password_hash = if let Some(password) = &pool.settings.user.password {
577-
Some(md5_hash_password(username, password, &salt))
578-
} else {
579-
if !get_config().is_auth_query_configured() {
580-
wrong_password(&mut write, username).await?;
581-
return Err(Error::ClientAuthImpossible(username.into()));
582-
}
583-
584-
let mut hash = (*pool.auth_hash.read()).clone();
585-
586-
if hash.is_none() {
587-
warn!(
588-
"Query auth configured \
589-
but no hash password found \
590-
for pool {}. Will try to refetch it.",
591-
pool_name
592-
);
582+
match pool.settings.user.auth_type {
583+
AuthType::Trust => (),
584+
AuthType::MD5 => {
585+
// Perform MD5 authentication.
586+
// TODO: Add SASL support.
587+
let salt = md5_challenge(&mut write).await?;
588+
589+
let code = match read.read_u8().await {
590+
Ok(p) => p,
591+
Err(_) => {
592+
return Err(Error::ClientSocketError(
593+
"password code".into(),
594+
client_identifier,
595+
))
596+
}
597+
};
598+
599+
// PasswordMessage
600+
if code as char != 'p' {
601+
return Err(Error::ProtocolSyncError(format!(
602+
"Expected p, got {}",
603+
code as char
604+
)));
605+
}
593606

594-
match refetch_auth_hash(&pool).await {
595-
Ok(fetched_hash) => {
596-
warn!("Password for {}, obtained. Updating.", client_identifier);
607+
let len = match read.read_i32().await {
608+
Ok(len) => len,
609+
Err(_) => {
610+
return Err(Error::ClientSocketError(
611+
"password message length".into(),
612+
client_identifier,
613+
))
614+
}
615+
};
597616

598-
{
599-
let mut pool_auth_hash = pool.auth_hash.write();
600-
*pool_auth_hash = Some(fetched_hash.clone());
601-
}
617+
let mut password_response = vec![0u8; (len - 4) as usize];
602618

603-
hash = Some(fetched_hash);
619+
match read.read_exact(&mut password_response).await {
620+
Ok(_) => (),
621+
Err(_) => {
622+
return Err(Error::ClientSocketError(
623+
"password message".into(),
624+
client_identifier,
625+
))
604626
}
627+
};
605628

606-
Err(err) => {
629+
let password_hash = if let Some(password) = &pool.settings.user.password {
630+
Some(md5_hash_password(username, password, &salt))
631+
} else {
632+
if !get_config().is_auth_query_configured() {
607633
wrong_password(&mut write, username).await?;
608-
609-
return Err(Error::ClientAuthPassthroughError(
610-
err.to_string(),
611-
client_identifier,
612-
));
634+
return Err(Error::ClientAuthImpossible(username.into()));
613635
}
614-
}
615-
};
616636

617-
Some(md5_hash_second_pass(&hash.unwrap(), &salt))
618-
};
637+
let mut hash = (*pool.auth_hash.read()).clone();
619638

620-
// Once we have the resulting hash, we compare with what the client gave us.
621-
// If they do not match and auth query is set up, we try to refetch the hash one more time
622-
// to see if the password has changed since the pool was created.
623-
//
624-
// @TODO: we could end up fetching again the same password twice (see above).
625-
if password_hash.unwrap() != password_response {
626-
warn!(
627-
"Invalid password {}, will try to refetch it.",
628-
client_identifier
629-
);
639+
if hash.is_none() {
640+
warn!(
641+
"Query auth configured \
642+
but no hash password found \
643+
for pool {}. Will try to refetch it.",
644+
pool_name
645+
);
630646

631-
let fetched_hash = match refetch_auth_hash(&pool).await {
632-
Ok(fetched_hash) => fetched_hash,
633-
Err(err) => {
634-
wrong_password(&mut write, username).await?;
647+
match refetch_auth_hash(&pool).await {
648+
Ok(fetched_hash) => {
649+
warn!(
650+
"Password for {}, obtained. Updating.",
651+
client_identifier
652+
);
635653

636-
return Err(err);
637-
}
638-
};
654+
{
655+
let mut pool_auth_hash = pool.auth_hash.write();
656+
*pool_auth_hash = Some(fetched_hash.clone());
657+
}
639658

640-
let new_password_hash = md5_hash_second_pass(&fetched_hash, &salt);
659+
hash = Some(fetched_hash);
660+
}
641661

642-
// Ok password changed in server an auth is possible.
643-
if new_password_hash == password_response {
644-
warn!(
645-
"Password for {}, changed in server. Updating.",
646-
client_identifier
647-
);
662+
Err(err) => {
663+
wrong_password(&mut write, username).await?;
648664

649-
{
650-
let mut pool_auth_hash = pool.auth_hash.write();
651-
*pool_auth_hash = Some(fetched_hash);
665+
return Err(Error::ClientAuthPassthroughError(
666+
err.to_string(),
667+
client_identifier,
668+
));
669+
}
670+
}
671+
};
672+
673+
Some(md5_hash_second_pass(&hash.unwrap(), &salt))
674+
};
675+
676+
// Once we have the resulting hash, we compare with what the client gave us.
677+
// If they do not match and auth query is set up, we try to refetch the hash one more time
678+
// to see if the password has changed since the pool was created.
679+
//
680+
// @TODO: we could end up fetching again the same password twice (see above).
681+
if password_hash.unwrap() != password_response {
682+
warn!(
683+
"Invalid password {}, will try to refetch it.",
684+
client_identifier
685+
);
686+
687+
let fetched_hash = match refetch_auth_hash(&pool).await {
688+
Ok(fetched_hash) => fetched_hash,
689+
Err(err) => {
690+
wrong_password(&mut write, username).await?;
691+
692+
return Err(err);
693+
}
694+
};
695+
696+
let new_password_hash = md5_hash_second_pass(&fetched_hash, &salt);
697+
698+
// Ok password changed in server an auth is possible.
699+
if new_password_hash == password_response {
700+
warn!(
701+
"Password for {}, changed in server. Updating.",
702+
client_identifier
703+
);
704+
705+
{
706+
let mut pool_auth_hash = pool.auth_hash.write();
707+
*pool_auth_hash = Some(fetched_hash);
708+
}
709+
} else {
710+
wrong_password(&mut write, username).await?;
711+
return Err(Error::ClientGeneralError(
712+
"Invalid password".into(),
713+
client_identifier,
714+
));
715+
}
652716
}
653-
} else {
654-
wrong_password(&mut write, username).await?;
655-
return Err(Error::ClientGeneralError(
656-
"Invalid password".into(),
657-
client_identifier,
658-
));
659717
}
660718
}
661-
662719
let transaction_mode = pool.settings.pool_mode == PoolMode::Transaction;
663720
prepared_statements_enabled =
664721
transaction_mode && pool.prepared_statement_cache.is_some();

0 commit comments

Comments
 (0)