Use error structs for errors instead of plain strings.
This commit is contained in:
parent
09745829f1
commit
7fd6923464
10 changed files with 357 additions and 99 deletions
|
|
@ -1,6 +1,6 @@
|
|||
use crate::common::Identity;
|
||||
use crate::secret;
|
||||
use crate::server::{Mechanism, Response, Validator};
|
||||
use crate::server::{Mechanism, MechanismError, Response, Validator};
|
||||
|
||||
pub struct Plain<V: Validator<secret::Plain>> {
|
||||
validator: V,
|
||||
|
|
@ -19,19 +19,19 @@ impl<V: Validator<secret::Plain>> Mechanism for Plain<V> {
|
|||
"PLAIN"
|
||||
}
|
||||
|
||||
fn respond(&mut self, payload: &[u8]) -> Result<Response, String> {
|
||||
fn respond(&mut self, payload: &[u8]) -> Result<Response, MechanismError> {
|
||||
let mut sp = payload.split(|&b| b == 0);
|
||||
sp.next();
|
||||
let username = sp
|
||||
.next()
|
||||
.ok_or_else(|| "no username specified".to_owned())?;
|
||||
let username =
|
||||
String::from_utf8(username.to_vec()).map_err(|_| "error decoding username")?;
|
||||
.ok_or_else(|| MechanismError::NoUsernameSpecified)?;
|
||||
let username = String::from_utf8(username.to_vec())
|
||||
.map_err(|_| MechanismError::ErrorDecodingUsername)?;
|
||||
let password = sp
|
||||
.next()
|
||||
.ok_or_else(|| "no password specified".to_owned())?;
|
||||
let password =
|
||||
String::from_utf8(password.to_vec()).map_err(|_| "error decoding password")?;
|
||||
.ok_or_else(|| MechanismError::NoPasswordSpecified)?;
|
||||
let password = String::from_utf8(password.to_vec())
|
||||
.map_err(|_| MechanismError::ErrorDecodingPassword)?;
|
||||
let ident = Identity::Username(username);
|
||||
self.validator.validate(&ident, &secret::Plain(password))?;
|
||||
Ok(Response::Success(ident, Vec::new()))
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::common::scram::{generate_nonce, ScramProvider};
|
|||
use crate::common::{parse_frame, xor, ChannelBinding, Identity};
|
||||
use crate::secret;
|
||||
use crate::secret::Pbkdf2Secret;
|
||||
use crate::server::{Mechanism, Provider, Response};
|
||||
use crate::server::{Mechanism, MechanismError, Provider, Response};
|
||||
|
||||
enum ScramState {
|
||||
Init,
|
||||
|
|
@ -61,7 +61,7 @@ where
|
|||
&self.name
|
||||
}
|
||||
|
||||
fn respond(&mut self, payload: &[u8]) -> Result<Response, String> {
|
||||
fn respond(&mut self, payload: &[u8]) -> Result<Response, MechanismError> {
|
||||
let next_state;
|
||||
let ret;
|
||||
match self.state {
|
||||
|
|
@ -82,7 +82,7 @@ where
|
|||
}
|
||||
}
|
||||
if commas < 2 {
|
||||
return Err("failed to decode message".to_owned());
|
||||
return Err(MechanismError::FailedToDecodeMessage);
|
||||
}
|
||||
let gs2_header = payload[..idx].to_vec();
|
||||
let rest = payload[idx..].to_vec();
|
||||
|
|
@ -92,29 +92,29 @@ where
|
|||
// Not supported.
|
||||
if gs2_header[0] != 0x79 {
|
||||
// ord("y")
|
||||
return Err("channel binding not supported".to_owned());
|
||||
return Err(MechanismError::ChannelBindingNotSupported);
|
||||
}
|
||||
}
|
||||
ref other => {
|
||||
// Supported.
|
||||
if gs2_header[0] == 0x79 {
|
||||
// ord("y")
|
||||
return Err("channel binding is supported".to_owned());
|
||||
return Err(MechanismError::ChannelBindingIsSupported);
|
||||
} else if !other.supports("tls-unique") {
|
||||
// TODO: grab the data
|
||||
return Err("channel binding mechanism incorrect".to_owned());
|
||||
return Err(MechanismError::ChannelBindingMechanismIncorrect);
|
||||
}
|
||||
}
|
||||
}
|
||||
let frame =
|
||||
parse_frame(&rest).map_err(|_| "can't decode initial message".to_owned())?;
|
||||
let username = frame.get("n").ok_or_else(|| "no username".to_owned())?;
|
||||
parse_frame(&rest).map_err(|_| MechanismError::CannotDecodeInitialMessage)?;
|
||||
let username = frame.get("n").ok_or_else(|| MechanismError::NoUsername)?;
|
||||
let identity = Identity::Username(username.to_owned());
|
||||
let client_nonce = frame.get("r").ok_or_else(|| "no nonce".to_owned())?;
|
||||
let client_nonce = frame.get("r").ok_or_else(|| MechanismError::NoNonce)?;
|
||||
let mut server_nonce = String::new();
|
||||
server_nonce += client_nonce;
|
||||
server_nonce +=
|
||||
&generate_nonce().map_err(|_| "failed to generate nonce".to_owned())?;
|
||||
&generate_nonce().map_err(|_| MechanismError::FailedToGenerateNonce)?;
|
||||
let pbkdf2 = self.provider.provide(&identity)?;
|
||||
let mut buf = Vec::new();
|
||||
buf.extend(b"r=");
|
||||
|
|
@ -141,7 +141,8 @@ where
|
|||
ref initial_client_message,
|
||||
ref initial_server_message,
|
||||
} => {
|
||||
let frame = parse_frame(payload).map_err(|_| "can't decode response".to_owned())?;
|
||||
let frame =
|
||||
parse_frame(payload).map_err(|_| MechanismError::CannotDecodeResponse)?;
|
||||
let mut cb_data: Vec<u8> = Vec::new();
|
||||
cb_data.extend(gs2_header);
|
||||
cb_data.extend(self.channel_binding.data());
|
||||
|
|
@ -161,11 +162,11 @@ where
|
|||
let stored_key = S::hash(&client_key);
|
||||
let client_signature = S::hmac(&auth_message, &stored_key)?;
|
||||
let client_proof = xor(&client_key, &client_signature);
|
||||
let sent_proof = frame.get("p").ok_or_else(|| "no proof".to_owned())?;
|
||||
let sent_proof = frame.get("p").ok_or_else(|| MechanismError::NoProof)?;
|
||||
let sent_proof =
|
||||
base64::decode(sent_proof).map_err(|_| "can't decode proof".to_owned())?;
|
||||
base64::decode(sent_proof).map_err(|_| MechanismError::CannotDecodeProof)?;
|
||||
if client_proof != sent_proof {
|
||||
return Err("authentication failed".to_owned());
|
||||
return Err(MechanismError::AuthenticationFailed);
|
||||
}
|
||||
let server_signature = S::hmac(&auth_message, &server_key)?;
|
||||
let mut buf = Vec::new();
|
||||
|
|
@ -175,7 +176,7 @@ where
|
|||
next_state = ScramState::Done;
|
||||
}
|
||||
ScramState::Done => {
|
||||
return Err("sasl session is already over".to_owned());
|
||||
return Err(MechanismError::SaslSessionAlreadyOver);
|
||||
}
|
||||
}
|
||||
self.state = next_state;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use crate::common::scram::DeriveError;
|
||||
use crate::common::Identity;
|
||||
use crate::secret::Secret;
|
||||
use std::fmt;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_validator_using_provider {
|
||||
|
|
@ -9,11 +11,11 @@ macro_rules! impl_validator_using_provider {
|
|||
&self,
|
||||
identity: &$crate::common::Identity,
|
||||
value: &$secret,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), ValidatorError> {
|
||||
if &(self as &$crate::server::Provider<$secret>).provide(identity)? == value {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("authentication failure".to_owned())
|
||||
Err(ValidatorError::AuthenticationFailed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,16 +23,150 @@ macro_rules! impl_validator_using_provider {
|
|||
}
|
||||
|
||||
pub trait Provider<S: Secret>: Validator<S> {
|
||||
fn provide(&self, identity: &Identity) -> Result<S, String>;
|
||||
fn provide(&self, identity: &Identity) -> Result<S, ProviderError>;
|
||||
}
|
||||
|
||||
pub trait Validator<S: Secret> {
|
||||
fn validate(&self, identity: &Identity, value: &S) -> Result<(), String>;
|
||||
fn validate(&self, identity: &Identity, value: &S) -> Result<(), ValidatorError>;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ProviderError {
|
||||
AuthenticationFailed,
|
||||
DeriveError(DeriveError),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ValidatorError {
|
||||
AuthenticationFailed,
|
||||
ProviderError(ProviderError),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum MechanismError {
|
||||
NoUsernameSpecified,
|
||||
ErrorDecodingUsername,
|
||||
NoPasswordSpecified,
|
||||
ErrorDecodingPassword,
|
||||
ValidatorError(ValidatorError),
|
||||
|
||||
FailedToDecodeMessage,
|
||||
ChannelBindingNotSupported,
|
||||
ChannelBindingIsSupported,
|
||||
ChannelBindingMechanismIncorrect,
|
||||
CannotDecodeInitialMessage,
|
||||
NoUsername,
|
||||
NoNonce,
|
||||
FailedToGenerateNonce,
|
||||
ProviderError(ProviderError),
|
||||
|
||||
CannotDecodeResponse,
|
||||
InvalidKeyLength(hmac::crypto_mac::InvalidKeyLength),
|
||||
NoProof,
|
||||
CannotDecodeProof,
|
||||
AuthenticationFailed,
|
||||
SaslSessionAlreadyOver,
|
||||
}
|
||||
|
||||
impl From<DeriveError> for ProviderError {
|
||||
fn from(err: DeriveError) -> ProviderError {
|
||||
ProviderError::DeriveError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ProviderError> for ValidatorError {
|
||||
fn from(err: ProviderError) -> ValidatorError {
|
||||
ValidatorError::ProviderError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ProviderError> for MechanismError {
|
||||
fn from(err: ProviderError) -> MechanismError {
|
||||
MechanismError::ProviderError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValidatorError> for MechanismError {
|
||||
fn from(err: ValidatorError) -> MechanismError {
|
||||
MechanismError::ValidatorError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<hmac::crypto_mac::InvalidKeyLength> for MechanismError {
|
||||
fn from(err: hmac::crypto_mac::InvalidKeyLength) -> MechanismError {
|
||||
MechanismError::InvalidKeyLength(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ProviderError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "provider error")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ValidatorError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "validator error")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MechanismError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
MechanismError::NoUsernameSpecified => write!(fmt, "no username specified"),
|
||||
MechanismError::ErrorDecodingUsername => write!(fmt, "error decoding username"),
|
||||
MechanismError::NoPasswordSpecified => write!(fmt, "no password specified"),
|
||||
MechanismError::ErrorDecodingPassword => write!(fmt, "error decoding password"),
|
||||
MechanismError::ValidatorError(err) => write!(fmt, "validator error: {}", err),
|
||||
|
||||
MechanismError::FailedToDecodeMessage => write!(fmt, "failed to decode message"),
|
||||
MechanismError::ChannelBindingNotSupported => {
|
||||
write!(fmt, "channel binding not supported")
|
||||
}
|
||||
MechanismError::ChannelBindingIsSupported => {
|
||||
write!(fmt, "channel binding is supported")
|
||||
}
|
||||
MechanismError::ChannelBindingMechanismIncorrect => {
|
||||
write!(fmt, "channel binding mechanism is incorrect")
|
||||
}
|
||||
MechanismError::CannotDecodeInitialMessage => {
|
||||
write!(fmt, "can’t decode initial message")
|
||||
}
|
||||
MechanismError::NoUsername => write!(fmt, "no username"),
|
||||
MechanismError::NoNonce => write!(fmt, "no nonce"),
|
||||
MechanismError::FailedToGenerateNonce => write!(fmt, "failed to generate nonce"),
|
||||
MechanismError::ProviderError(err) => write!(fmt, "provider error: {}", err),
|
||||
|
||||
MechanismError::CannotDecodeResponse => write!(fmt, "can’t decode response"),
|
||||
MechanismError::InvalidKeyLength(err) => write!(fmt, "invalid key length: {}", err),
|
||||
MechanismError::NoProof => write!(fmt, "no proof"),
|
||||
MechanismError::CannotDecodeProof => write!(fmt, "can’t decode proof"),
|
||||
MechanismError::AuthenticationFailed => write!(fmt, "authentication failed"),
|
||||
MechanismError::SaslSessionAlreadyOver => write!(fmt, "SASL session already over"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ProviderError {}
|
||||
|
||||
impl Error for ValidatorError {}
|
||||
|
||||
use std::error::Error;
|
||||
impl Error for MechanismError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
match self {
|
||||
MechanismError::ValidatorError(err) => Some(err),
|
||||
MechanismError::ProviderError(err) => Some(err),
|
||||
// TODO: figure out how to enable the std feature on this crate.
|
||||
//MechanismError::InvalidKeyLength(err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Mechanism {
|
||||
fn name(&self) -> &str;
|
||||
fn respond(&mut self, payload: &[u8]) -> Result<Response, String>;
|
||||
fn respond(&mut self, payload: &[u8]) -> Result<Response, MechanismError>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
|
|||
Loading…
Reference in a new issue