mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2025-01-01 13:13:45 +08:00
Support for password hashing schemes between curly brackets (#8).
This commit is contained in:
parent
cdb42c1a79
commit
d96d7840a2
1 changed files with 72 additions and 61 deletions
|
@ -47,73 +47,80 @@ impl Principal {
|
|||
}
|
||||
}
|
||||
|
||||
async fn verify_hash_prefix(hashed_secret: &str, secret: &str) -> bool {
|
||||
if hashed_secret.starts_with("$argon2")
|
||||
|| hashed_secret.starts_with("$pbkdf2")
|
||||
|| hashed_secret.starts_with("$scrypt")
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let secret = secret.to_string();
|
||||
let hashed_secret = hashed_secret.to_string();
|
||||
|
||||
tokio::task::spawn_blocking(move || match PasswordHash::new(&hashed_secret) {
|
||||
Ok(hash) => {
|
||||
tx.send(
|
||||
hash.verify_password(&[&Argon2::default(), &Pbkdf2, &Scrypt], &secret)
|
||||
.is_ok(),
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::warn!(
|
||||
context = "directory",
|
||||
event = "error",
|
||||
hash = hashed_secret,
|
||||
"Invalid password hash"
|
||||
);
|
||||
tx.send(false).ok();
|
||||
}
|
||||
});
|
||||
|
||||
match rx.await {
|
||||
Ok(result) => result,
|
||||
Err(_) => {
|
||||
tracing::warn!(context = "directory", event = "error", "Thread join error");
|
||||
false
|
||||
}
|
||||
}
|
||||
} else if hashed_secret.starts_with("$2") {
|
||||
// Blowfish crypt
|
||||
bcrypt::verify(secret, hashed_secret)
|
||||
} else if hashed_secret.starts_with("$6$") {
|
||||
// SHA-512 crypt
|
||||
sha512_crypt::verify(secret, hashed_secret)
|
||||
} else if hashed_secret.starts_with("$5$") {
|
||||
// SHA-256 crypt
|
||||
sha256_crypt::verify(secret, hashed_secret)
|
||||
} else if hashed_secret.starts_with("$sha1") {
|
||||
// SHA-1 crypt
|
||||
sha1_crypt::verify(secret, hashed_secret)
|
||||
} else if hashed_secret.starts_with("$1") {
|
||||
// MD5 based hash
|
||||
md5_crypt::verify(secret, hashed_secret)
|
||||
} else {
|
||||
// Unknown hash
|
||||
tracing::warn!(
|
||||
context = "directory",
|
||||
event = "error",
|
||||
hash = hashed_secret,
|
||||
"Invalid password hash"
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
async fn verify_secret_hash(hashed_secret: &str, secret: &str) -> bool {
|
||||
if hashed_secret.starts_with('$') {
|
||||
if hashed_secret.starts_with("$argon2")
|
||||
|| hashed_secret.starts_with("$pbkdf2")
|
||||
|| hashed_secret.starts_with("$scrypt")
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let secret = secret.to_string();
|
||||
let hashed_secret = hashed_secret.to_string();
|
||||
|
||||
tokio::task::spawn_blocking(move || match PasswordHash::new(&hashed_secret) {
|
||||
Ok(hash) => {
|
||||
tx.send(
|
||||
hash.verify_password(&[&Argon2::default(), &Pbkdf2, &Scrypt], &secret)
|
||||
.is_ok(),
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::warn!(
|
||||
context = "directory",
|
||||
event = "error",
|
||||
hash = hashed_secret,
|
||||
"Invalid password hash"
|
||||
);
|
||||
tx.send(false).ok();
|
||||
}
|
||||
});
|
||||
|
||||
match rx.await {
|
||||
Ok(result) => result,
|
||||
Err(_) => {
|
||||
tracing::warn!(context = "directory", event = "error", "Thread join error");
|
||||
false
|
||||
}
|
||||
}
|
||||
} else if hashed_secret.starts_with("$2") {
|
||||
// Blowfish crypt
|
||||
bcrypt::verify(secret, hashed_secret)
|
||||
} else if hashed_secret.starts_with("$6$") {
|
||||
// SHA-512 crypt
|
||||
sha512_crypt::verify(secret, hashed_secret)
|
||||
} else if hashed_secret.starts_with("$5$") {
|
||||
// SHA-256 crypt
|
||||
sha256_crypt::verify(secret, hashed_secret)
|
||||
} else if hashed_secret.starts_with("$sha1") {
|
||||
// SHA-1 crypt
|
||||
sha1_crypt::verify(secret, hashed_secret)
|
||||
} else if hashed_secret.starts_with("$1") {
|
||||
// MD5 based hash
|
||||
md5_crypt::verify(secret, hashed_secret)
|
||||
} else {
|
||||
// Unknown hash
|
||||
tracing::warn!(
|
||||
context = "directory",
|
||||
event = "error",
|
||||
hash = hashed_secret,
|
||||
"Invalid password hash"
|
||||
);
|
||||
false
|
||||
}
|
||||
verify_hash_prefix(hashed_secret, secret).await
|
||||
} else if hashed_secret.starts_with('_') {
|
||||
// Enhanced DES-based hash
|
||||
bsdi_crypt::verify(secret, hashed_secret)
|
||||
} else if let Some(hashed_secret) = hashed_secret.strip_prefix('{') {
|
||||
if let Some((algo, hashed_secret)) = hashed_secret.split_once('}') {
|
||||
match algo {
|
||||
"ARGON2" | "ARGON2I" | "ARGON2ID" | "PBKDF2" => {
|
||||
verify_hash_prefix(hashed_secret, secret).await
|
||||
}
|
||||
"SHA" => {
|
||||
// SHA-1
|
||||
let mut hasher = Sha1::new();
|
||||
|
@ -175,8 +182,12 @@ async fn verify_secret_hash(hashed_secret: &str, secret: &str) -> bool {
|
|||
== hashed_secret
|
||||
}
|
||||
"CRYPT" | "crypt" => {
|
||||
// Unix crypt
|
||||
unix_crypt::verify(secret, hashed_secret)
|
||||
if hashed_secret.starts_with('$') {
|
||||
verify_hash_prefix(hashed_secret, secret).await
|
||||
} else {
|
||||
// Unix crypt
|
||||
unix_crypt::verify(secret, hashed_secret)
|
||||
}
|
||||
}
|
||||
"PLAIN" | "plain" | "CLEAR" | "clear" => hashed_secret == secret,
|
||||
_ => {
|
||||
|
|
Loading…
Reference in a new issue