Use error structs for errors instead of plain strings.

This commit is contained in:
Emmanuel Gil Peyrot 2020-05-15 13:48:27 +02:00
commit 7fd6923464
10 changed files with 357 additions and 99 deletions

View file

@ -1,6 +1,6 @@
//! Provides the SASL "ANONYMOUS" mechanism.
use crate::client::Mechanism;
use crate::client::{Mechanism, MechanismError};
use crate::common::{Credentials, Secret};
/// A struct for the SASL ANONYMOUS mechanism.
@ -21,11 +21,11 @@ impl Mechanism for Anonymous {
"ANONYMOUS"
}
fn from_credentials(credentials: Credentials) -> Result<Anonymous, String> {
fn from_credentials(credentials: Credentials) -> Result<Anonymous, MechanismError> {
if let Secret::None = credentials.secret {
Ok(Anonymous)
} else {
Err("the anonymous sasl mechanism requires no credentials".to_owned())
Err(MechanismError::AnonymousRequiresNoCredentials)
}
}
}

View file

@ -1,6 +1,6 @@
//! Provides the SASL "PLAIN" mechanism.
use crate::client::Mechanism;
use crate::client::{Mechanism, MechanismError};
use crate::common::{Credentials, Identity, Password, Secret};
/// A struct for the SASL PLAIN mechanism.
@ -27,15 +27,15 @@ impl Mechanism for Plain {
"PLAIN"
}
fn from_credentials(credentials: Credentials) -> Result<Plain, String> {
fn from_credentials(credentials: Credentials) -> Result<Plain, MechanismError> {
if let Secret::Password(Password::Plain(password)) = credentials.secret {
if let Identity::Username(username) = credentials.identity {
Ok(Plain::new(username, password))
} else {
Err("PLAIN requires a username".to_owned())
Err(MechanismError::PlainRequiresUsername)
}
} else {
Err("PLAIN requires a plaintext password".to_owned())
Err(MechanismError::PlainRequiresPlaintextPassword)
}
}

View file

@ -2,7 +2,7 @@
use base64;
use crate::client::Mechanism;
use crate::client::{Mechanism, MechanismError};
use crate::common::scram::{generate_nonce, ScramProvider};
use crate::common::{parse_frame, xor, ChannelBinding, Credentials, Identity, Password, Secret};
@ -80,16 +80,16 @@ impl<S: ScramProvider> Mechanism for Scram<S> {
&self.name
}
fn from_credentials(credentials: Credentials) -> Result<Scram<S>, String> {
fn from_credentials(credentials: Credentials) -> Result<Scram<S>, MechanismError> {
if let Secret::Password(password) = credentials.secret {
if let Identity::Username(username) = credentials.identity {
Scram::new(username, password, credentials.channel_binding)
.map_err(|_| "can't generate nonce".to_owned())
.map_err(|_| MechanismError::CannotGenerateNonce)
} else {
Err("SCRAM requires a username".to_owned())
Err(MechanismError::ScramRequiresUsername)
}
} else {
Err("SCRAM requires a password".to_owned())
Err(MechanismError::ScramRequiresPassword)
}
}
@ -111,7 +111,7 @@ impl<S: ScramProvider> Mechanism for Scram<S> {
data
}
fn response(&mut self, challenge: &[u8]) -> Result<Vec<u8>, String> {
fn response(&mut self, challenge: &[u8]) -> Result<Vec<u8>, MechanismError> {
let next_state;
let ret;
match self.state {
@ -120,13 +120,13 @@ impl<S: ScramProvider> Mechanism for Scram<S> {
ref gs2_header,
} => {
let frame =
parse_frame(challenge).map_err(|_| "can't decode challenge".to_owned())?;
parse_frame(challenge).map_err(|_| MechanismError::CannotDecodeChallenge)?;
let server_nonce = frame.get("r");
let salt = frame.get("s").and_then(|v| base64::decode(v).ok());
let iterations = frame.get("i").and_then(|v| v.parse().ok());
let server_nonce = server_nonce.ok_or_else(|| "no server nonce".to_owned())?;
let salt = salt.ok_or_else(|| "no server salt".to_owned())?;
let iterations = iterations.ok_or_else(|| "no server iterations".to_owned())?;
let server_nonce = server_nonce.ok_or_else(|| MechanismError::NoServerNonce)?;
let salt = salt.ok_or_else(|| MechanismError::NoServerSalt)?;
let iterations = iterations.ok_or_else(|| MechanismError::NoServerIterations)?;
// TODO: SASLprep
let mut client_final_message_bare = Vec::new();
client_final_message_bare.extend(b"c=");
@ -159,15 +159,15 @@ impl<S: ScramProvider> Mechanism for Scram<S> {
ret = client_final_message;
}
_ => {
return Err("not in the right state to receive this response".to_owned());
return Err(MechanismError::InvalidState);
}
}
self.state = next_state;
Ok(ret)
}
fn success(&mut self, data: &[u8]) -> Result<(), String> {
let frame = parse_frame(data).map_err(|_| "can't decode success response".to_owned())?;
fn success(&mut self, data: &[u8]) -> Result<(), MechanismError> {
let frame = parse_frame(data).map_err(|_| MechanismError::CannotDecodeSuccessResponse)?;
match self.state {
ScramState::GotServerData {
ref server_signature,
@ -176,13 +176,13 @@ impl<S: ScramProvider> Mechanism for Scram<S> {
if sig == *server_signature {
Ok(())
} else {
Err("invalid signature in success response".to_owned())
Err(MechanismError::InvalidSignatureInSuccessResponse)
}
} else {
Err("no signature in success response".to_owned())
Err(MechanismError::NoSignatureInSuccessResponse)
}
}
_ => Err("not in the right state to get a success response".to_owned()),
_ => Err(MechanismError::InvalidState),
}
}
}

View file

@ -1,4 +1,80 @@
use crate::common::scram::DeriveError;
use crate::common::Credentials;
use hmac::crypto_mac::InvalidKeyLength;
use std::fmt;
#[derive(Debug, PartialEq)]
pub enum MechanismError {
AnonymousRequiresNoCredentials,
PlainRequiresUsername,
PlainRequiresPlaintextPassword,
CannotGenerateNonce,
ScramRequiresUsername,
ScramRequiresPassword,
CannotDecodeChallenge,
NoServerNonce,
NoServerSalt,
NoServerIterations,
DeriveError(DeriveError),
InvalidKeyLength(InvalidKeyLength),
InvalidState,
CannotDecodeSuccessResponse,
InvalidSignatureInSuccessResponse,
NoSignatureInSuccessResponse,
}
impl From<DeriveError> for MechanismError {
fn from(err: DeriveError) -> MechanismError {
MechanismError::DeriveError(err)
}
}
impl From<InvalidKeyLength> for MechanismError {
fn from(err: InvalidKeyLength) -> MechanismError {
MechanismError::InvalidKeyLength(err)
}
}
impl fmt::Display for MechanismError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"{}",
match self {
MechanismError::AnonymousRequiresNoCredentials =>
"ANONYMOUS mechanism requires no credentials",
MechanismError::PlainRequiresUsername => "PLAIN requires a username",
MechanismError::PlainRequiresPlaintextPassword =>
"PLAIN requires a plaintext password",
MechanismError::CannotGenerateNonce => "can't generate nonce",
MechanismError::ScramRequiresUsername => "SCRAM requires a username",
MechanismError::ScramRequiresPassword => "SCRAM requires a password",
MechanismError::CannotDecodeChallenge => "can't decode challenge",
MechanismError::NoServerNonce => "no server nonce",
MechanismError::NoServerSalt => "no server salt",
MechanismError::NoServerIterations => "no server iterations",
MechanismError::DeriveError(err) => return write!(fmt, "derive error: {}", err),
MechanismError::InvalidKeyLength(err) =>
return write!(fmt, "invalid key length: {}", err),
MechanismError::InvalidState => "not in the right state to receive this response",
MechanismError::CannotDecodeSuccessResponse => "can't decode success response",
MechanismError::InvalidSignatureInSuccessResponse =>
"invalid signature in success response",
MechanismError::NoSignatureInSuccessResponse => "no signature in success response",
}
)
}
}
impl std::error::Error for MechanismError {}
/// A trait which defines SASL mechanisms.
pub trait Mechanism {
@ -6,7 +82,7 @@ pub trait Mechanism {
fn name(&self) -> &str;
/// Creates this mechanism from `Credentials`.
fn from_credentials(credentials: Credentials) -> Result<Self, String>
fn from_credentials(credentials: Credentials) -> Result<Self, MechanismError>
where
Self: Sized;
@ -16,12 +92,12 @@ pub trait Mechanism {
}
/// Creates a response to the SASL challenge.
fn response(&mut self, _challenge: &[u8]) -> Result<Vec<u8>, String> {
fn response(&mut self, _challenge: &[u8]) -> Result<Vec<u8>, MechanismError> {
Ok(Vec::new())
}
/// Verifies the server success response, if there is one.
fn success(&mut self, _data: &[u8]) -> Result<(), String> {
fn success(&mut self, _data: &[u8]) -> Result<(), MechanismError> {
Ok(())
}
}