mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-25 16:17:56 +01:00
Merge branch 'feature/rsasl-update' into development
* feature/rsasl-update: Update to latest rsasl Port rsasl
This commit is contained in:
commit
1971515601
773
Cargo.lock
generated
773
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
@ -84,7 +84,9 @@ capnp = "0.14"
|
|||||||
capnp-rpc = "0.14.1"
|
capnp-rpc = "0.14.1"
|
||||||
|
|
||||||
# API Authentication
|
# API Authentication
|
||||||
desfire = "0.2.0-alpha1"
|
#desfire = "0.2.0-alpha1"
|
||||||
|
desfire = { path = "../nfc_rs" }
|
||||||
|
|
||||||
hex = { version = "0.4.3", features = ["serde"] }
|
hex = { version = "0.4.3", features = ["serde"] }
|
||||||
|
|
||||||
futures-signals = "0.3.22"
|
futures-signals = "0.3.22"
|
||||||
@ -112,10 +114,9 @@ rustls-native-certs = "0.6.1"
|
|||||||
shadow-rs = "0.11"
|
shadow-rs = "0.11"
|
||||||
|
|
||||||
[dependencies.rsasl]
|
[dependencies.rsasl]
|
||||||
git = "https://github.com/dequbed/rsasl.git"
|
version = "2.0.0-rc.3"
|
||||||
rev = "0b5012d0"
|
|
||||||
default_features = false
|
default_features = false
|
||||||
features = ["unstable_custom_mechanism", "provider", "registry_static", "plain"]
|
features = ["unstable_custom_mechanism", "provider", "registry_static", "config_builder", "plain"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
futures-test = "0.3.16"
|
futures-test = "0.3.16"
|
||||||
@ -126,3 +127,6 @@ shadow-rs = "0.11"
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["runtime/*", "modules/*", "api"]
|
members = ["runtime/*", "modules/*", "api"]
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
desfire = { path = "../nfc_rs" }
|
@ -2,43 +2,23 @@ mod server;
|
|||||||
pub use server::FabFire;
|
pub use server::FabFire;
|
||||||
|
|
||||||
use rsasl::mechname::Mechname;
|
use rsasl::mechname::Mechname;
|
||||||
use rsasl::registry::{Mechanism, MECHANISMS};
|
use rsasl::registry::{Mechanism, MECHANISMS, Side};
|
||||||
use rsasl::session::Side;
|
|
||||||
|
|
||||||
const MECHNAME: &'static Mechname = &Mechname::const_new_unchecked(b"X-FABFIRE");
|
const MECHNAME: &'static Mechname = &Mechname::const_new_unchecked(b"X-FABFIRE");
|
||||||
|
|
||||||
#[linkme::distributed_slice(MECHANISMS)]
|
#[linkme::distributed_slice(MECHANISMS)]
|
||||||
pub static FABFIRE: Mechanism = Mechanism {
|
pub static FABFIRE: Mechanism =
|
||||||
mechanism: MECHNAME,
|
Mechanism::build(MECHNAME, 300, None, Some(FabFire::new_server), Side::Client);
|
||||||
priority: 300,
|
|
||||||
// In this situation there's one struct for both sides, however you can just as well use
|
|
||||||
// different types than then have different `impl Authentication` instead of checking a value
|
|
||||||
// in self.
|
|
||||||
client: None,
|
|
||||||
server: Some(FabFire::new_server),
|
|
||||||
first: Side::Client,
|
|
||||||
};
|
|
||||||
|
|
||||||
use rsasl::property::{Property, PropertyDefinition, PropertyQ};
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use rsasl::property::SizedProperty;
|
||||||
|
|
||||||
// All Property types must implement Debug.
|
// All Property types must implement Debug.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
// The `PhantomData` in the constructor is only used so external crates can't construct this type.
|
// The `PhantomData` in the constructor is only used so external crates can't construct this type.
|
||||||
pub struct FabFireCardKey(PhantomData<()>);
|
pub struct FabFireCardKey(PhantomData<()>);
|
||||||
impl PropertyQ for FabFireCardKey {
|
|
||||||
// This is the type stored for this property. This could also be the struct itself if you
|
impl SizedProperty<'_> for FabFireCardKey {
|
||||||
// so choose
|
type Value = [u8; 16];
|
||||||
type Item = [u8; 16];
|
const DESCRIPTION: &'static str = "A AES128 key for a FabFire card";
|
||||||
// You need to return the constant you define below here for things to work properly
|
|
||||||
fn property() -> Property {
|
|
||||||
FABFIRECARDKEY
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// This const is used by your mechanism to query and by your users to set your property. It
|
|
||||||
// thus needs to be exported from your crate
|
|
||||||
pub const FABFIRECARDKEY: Property = Property::new(&PropertyDefinition::new(
|
|
||||||
// Short name, used in `Debug` output
|
|
||||||
"FabFireCardKey",
|
|
||||||
// A longer user-facing name used in `Display` output
|
|
||||||
"A AES128 key for a FabFire card",
|
|
||||||
));
|
|
||||||
|
@ -2,11 +2,10 @@ use desfire::desfire::desfire::MAX_BYTES_PER_TRANSACTION;
|
|||||||
use desfire::desfire::Desfire;
|
use desfire::desfire::Desfire;
|
||||||
use desfire::error::Error as DesfireError;
|
use desfire::error::Error as DesfireError;
|
||||||
use desfire::iso7816_4::apduresponse::APDUResponse;
|
use desfire::iso7816_4::apduresponse::APDUResponse;
|
||||||
use rsasl::error::{MechanismError, MechanismErrorKind, SASLError, SessionError};
|
use rsasl::callback::SessionData;
|
||||||
use rsasl::mechanism::Authentication;
|
use rsasl::mechanism::{Authentication, MechanismData, MechanismError, MechanismErrorKind, State, ThisProvider};
|
||||||
|
use rsasl::prelude::{MessageSent, SASLConfig, SASLError, SessionError};
|
||||||
use rsasl::property::AuthId;
|
use rsasl::property::AuthId;
|
||||||
use rsasl::session::{SessionData, StepResult};
|
|
||||||
use rsasl::SASL;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
@ -63,6 +62,10 @@ impl Display for FabFireError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for FabFireError {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
impl MechanismError for FabFireError {
|
impl MechanismError for FabFireError {
|
||||||
fn kind(&self) -> MechanismErrorKind {
|
fn kind(&self) -> MechanismErrorKind {
|
||||||
match self {
|
match self {
|
||||||
@ -154,7 +157,7 @@ pub struct FabFire {
|
|||||||
const MAGIC: &'static str = "FABACCESS\0DESFIRE\01.0\0";
|
const MAGIC: &'static str = "FABACCESS\0DESFIRE\01.0\0";
|
||||||
|
|
||||||
impl FabFire {
|
impl FabFire {
|
||||||
pub fn new_server(_sasl: &SASL) -> Result<Box<dyn Authentication>, SASLError> {
|
pub fn new_server(_sasl: &SASLConfig) -> Result<Box<dyn Authentication>, SASLError> {
|
||||||
Ok(Box::new(Self {
|
Ok(Box::new(Self {
|
||||||
step: Step::New,
|
step: Step::New,
|
||||||
card_info: None,
|
card_info: None,
|
||||||
@ -174,10 +177,10 @@ impl FabFire {
|
|||||||
impl Authentication for FabFire {
|
impl Authentication for FabFire {
|
||||||
fn step(
|
fn step(
|
||||||
&mut self,
|
&mut self,
|
||||||
session: &mut SessionData,
|
session: &mut MechanismData<'_>,
|
||||||
input: Option<&[u8]>,
|
input: Option<&[u8]>,
|
||||||
writer: &mut dyn Write,
|
writer: &mut dyn Write,
|
||||||
) -> StepResult {
|
) -> Result<State, SessionError> {
|
||||||
match self.step {
|
match self.step {
|
||||||
Step::New => {
|
Step::New => {
|
||||||
tracing::trace!("Step: New");
|
tracing::trace!("Step: New");
|
||||||
@ -216,7 +219,7 @@ impl Authentication for FabFire {
|
|||||||
writer
|
writer
|
||||||
.write_all(&send_buf)
|
.write_all(&send_buf)
|
||||||
.map_err(|e| SessionError::Io { source: e })?;
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
Ok(State::Running)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
||||||
@ -282,7 +285,7 @@ impl Authentication for FabFire {
|
|||||||
writer
|
writer
|
||||||
.write_all(&send_buf)
|
.write_all(&send_buf)
|
||||||
.map_err(|e| SessionError::Io { source: e })?;
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
Ok(State::Running)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
||||||
@ -365,7 +368,7 @@ impl Authentication for FabFire {
|
|||||||
writer
|
writer
|
||||||
.write_all(&send_buf)
|
.write_all(&send_buf)
|
||||||
.map_err(|e| SessionError::Io { source: e })?;
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
Ok(State::Running)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
||||||
@ -452,7 +455,7 @@ impl Authentication for FabFire {
|
|||||||
writer
|
writer
|
||||||
.write_all(&send_buf)
|
.write_all(&send_buf)
|
||||||
.map_err(|e| SessionError::Io { source: e })?;
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
Ok(State::Running)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
||||||
@ -491,24 +494,9 @@ impl Authentication for FabFire {
|
|||||||
match apdu_response.body {
|
match apdu_response.body {
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
let token = String::from_utf8(data).unwrap();
|
let token = String::from_utf8(data).unwrap();
|
||||||
session.set_property::<AuthId>(Arc::new(
|
let prov =
|
||||||
token.trim_matches(char::from(0)).to_string(),
|
ThisProvider::<AuthId>::with(token.trim_matches(char::from(0)));
|
||||||
));
|
let key = session.need_with::<FabFireCardKey, _, _>(&prov, |key| Ok(Box::from(key.as_slice())))?;
|
||||||
let key = match session.get_property_or_callback::<FabFireCardKey>()
|
|
||||||
{
|
|
||||||
Ok(Some(key)) => Box::from(key.as_slice()),
|
|
||||||
Ok(None) => {
|
|
||||||
tracing::error!("No keys on file for token");
|
|
||||||
return Err(FabFireError::InvalidCredentials(
|
|
||||||
"No keys on file for token".to_string(),
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("Failed to get key: {:?}", e);
|
|
||||||
return Err(FabFireError::Session(e).into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.key_info = Some(KeyInfo { key_id: 0x01, key });
|
self.key_info = Some(KeyInfo { key_id: 0x01, key });
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@ -546,7 +534,7 @@ impl Authentication for FabFire {
|
|||||||
writer
|
writer
|
||||||
.write_all(&send_buf)
|
.write_all(&send_buf)
|
||||||
.map_err(|e| SessionError::Io { source: e })?;
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
Ok(State::Running)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to serialize command: {:?}", e);
|
tracing::error!("Failed to serialize command: {:?}", e);
|
||||||
@ -616,7 +604,7 @@ impl Authentication for FabFire {
|
|||||||
writer
|
writer
|
||||||
.write_all(&send_buf)
|
.write_all(&send_buf)
|
||||||
.map_err(|e| SessionError::Io { source: e })?;
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
Ok(State::Running)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to serialize command: {:?}", e);
|
tracing::error!("Failed to serialize command: {:?}", e);
|
||||||
@ -691,9 +679,7 @@ impl Authentication for FabFire {
|
|||||||
writer
|
writer
|
||||||
.write_all(&send_buf)
|
.write_all(&send_buf)
|
||||||
.map_err(|e| SessionError::Io { source: e })?;
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
return Ok(rsasl::session::Step::Done(Some(
|
return Ok(State::Finished(MessageSent::Yes));
|
||||||
send_buf.len(),
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
@ -722,6 +708,6 @@ impl Authentication for FabFire {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(rsasl::session::Step::Done(None));
|
return Ok(State::Finished(MessageSent::No));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use crate::users::Users;
|
use crate::users::Users;
|
||||||
use miette::{Context, IntoDiagnostic};
|
use miette::{IntoDiagnostic, WrapErr};
|
||||||
use rsasl::error::SessionError;
|
|
||||||
use rsasl::mechname::Mechname;
|
|
||||||
use rsasl::property::{AuthId, Password};
|
|
||||||
use rsasl::session::{Session, SessionData};
|
|
||||||
use rsasl::validate::{validations, Validation};
|
|
||||||
use rsasl::{Property, SASL};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use rsasl::callback::{CallbackError, Request, SessionCallback, SessionData, Context};
|
||||||
|
use rsasl::mechanism::SessionError;
|
||||||
|
use rsasl::prelude::{Mechname, SASLConfig, SASLServer, Session, Validation};
|
||||||
|
use rsasl::property::{AuthId, AuthzId, Password};
|
||||||
|
use rsasl::validate::{Validate, ValidationError};
|
||||||
|
|
||||||
use crate::authentication::fabfire::FabFireCardKey;
|
use crate::authentication::fabfire::FabFireCardKey;
|
||||||
|
use crate::users::db::User;
|
||||||
|
|
||||||
mod fabfire;
|
mod fabfire;
|
||||||
|
|
||||||
@ -22,89 +22,78 @@ impl Callback {
|
|||||||
Self { users, span }
|
Self { users, span }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl rsasl::callback::Callback for Callback {
|
impl SessionCallback for Callback {
|
||||||
fn provide_prop(
|
fn callback(&self, session_data: &SessionData, context: &Context, request: &mut Request) -> Result<(), SessionError> {
|
||||||
&self,
|
if let Some(authid) = context.get_ref::<AuthId>() {
|
||||||
session: &mut rsasl::session::SessionData,
|
request.satisfy_with::<FabFireCardKey, _>(|| {
|
||||||
property: Property,
|
let user = self.users.get_user(authid).ok_or(CallbackError::NoValue)?;
|
||||||
) -> Result<(), SessionError> {
|
let kv = user.userdata.kv.get("cardkey").ok_or(CallbackError::NoValue)?;
|
||||||
match property {
|
|
||||||
fabfire::FABFIRECARDKEY => {
|
|
||||||
let authcid = session.get_property_or_callback::<AuthId>()?;
|
|
||||||
let user = self
|
|
||||||
.users
|
|
||||||
.get_user(authcid.unwrap().as_ref())
|
|
||||||
.ok_or(SessionError::AuthenticationFailure)?;
|
|
||||||
let kv = user
|
|
||||||
.userdata
|
|
||||||
.kv
|
|
||||||
.get("cardkey")
|
|
||||||
.ok_or(SessionError::AuthenticationFailure)?;
|
|
||||||
let card_key = <[u8; 16]>::try_from(
|
let card_key = <[u8; 16]>::try_from(
|
||||||
hex::decode(kv).map_err(|_| SessionError::AuthenticationFailure)?,
|
hex::decode(kv).map_err(|_| CallbackError::NoValue)?,
|
||||||
)
|
).map_err(|_| CallbackError::NoValue)?;
|
||||||
.map_err(|_| SessionError::AuthenticationFailure)?;
|
Ok(card_key)
|
||||||
session.set_property::<FabFireCardKey>(Arc::new(card_key));
|
})?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(SessionError::NoProperty { property }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate(
|
fn validate(&self, session_data: &SessionData, context: &Context, validate: &mut Validate<'_>) -> Result<(), ValidationError> {
|
||||||
&self,
|
|
||||||
session: &mut SessionData,
|
|
||||||
validation: Validation,
|
|
||||||
_mechanism: &Mechname,
|
|
||||||
) -> Result<(), SessionError> {
|
|
||||||
let span = tracing::info_span!(parent: &self.span, "validate");
|
let span = tracing::info_span!(parent: &self.span, "validate");
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
match validation {
|
if validate.is::<V>() {
|
||||||
validations::SIMPLE => {
|
match session_data.mechanism().mechanism.as_str() {
|
||||||
let authnid = session
|
"PLAIN" => {
|
||||||
.get_property::<AuthId>()
|
let authcid = context.get_ref::<AuthId>()
|
||||||
.ok_or(SessionError::no_property::<AuthId>())?;
|
.ok_or(ValidationError::MissingRequiredProperty)?;
|
||||||
tracing::debug!(authid=%authnid, "SIMPLE validation requested");
|
let authzid = context.get_ref::<AuthzId>();
|
||||||
|
let password = context.get_ref::<Password>()
|
||||||
|
.ok_or(ValidationError::MissingRequiredProperty)?;
|
||||||
|
|
||||||
if let Some(user) = self.users.get_user(authnid.as_str()) {
|
if authzid.is_some() {
|
||||||
let passwd = session
|
return Ok(())
|
||||||
.get_property::<Password>()
|
}
|
||||||
.ok_or(SessionError::no_property::<Password>())?;
|
|
||||||
|
|
||||||
if user
|
if let Some(user) = self.users.get_user(authcid) {
|
||||||
.check_password(passwd.as_bytes())
|
match user.check_password(password) {
|
||||||
.map_err(|_e| SessionError::AuthenticationFailure)?
|
Ok(true) => {
|
||||||
{
|
validate.finalize::<V>(user)
|
||||||
return Ok(());
|
}
|
||||||
} else {
|
Ok(false) => {
|
||||||
tracing::warn!(authid=%authnid, "AUTH FAILED: bad password");
|
tracing::warn!(authid=%authcid, "AUTH FAILED: bad password");
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::warn!(authid=%authcid, "Bad DB entry: {}", error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tracing::warn!(authid=%authnid, "AUTH FAILED: no such user '{}'", authnid);
|
tracing::warn!(authid=%authcid, "AUTH FAILED: no such user");
|
||||||
}
|
|
||||||
|
|
||||||
Err(SessionError::AuthenticationFailure)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
tracing::error!(?validation, "Unimplemented validation requested");
|
|
||||||
Err(SessionError::no_validate(validation))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct V;
|
||||||
|
impl Validation for V {
|
||||||
|
type Value = User;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Inner {
|
struct Inner {
|
||||||
rsasl: SASL,
|
rsasl: Arc<SASLConfig>,
|
||||||
}
|
}
|
||||||
impl Inner {
|
impl Inner {
|
||||||
pub fn new(rsasl: SASL) -> Self {
|
pub fn new(rsasl: Arc<SASLConfig>) -> Self {
|
||||||
Self { rsasl }
|
Self { rsasl }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AuthenticationHandle {
|
pub struct AuthenticationHandle {
|
||||||
inner: Arc<Inner>,
|
inner: Inner,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthenticationHandle {
|
impl AuthenticationHandle {
|
||||||
@ -112,11 +101,13 @@ impl AuthenticationHandle {
|
|||||||
let span = tracing::debug_span!("authentication");
|
let span = tracing::debug_span!("authentication");
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
|
|
||||||
let mut rsasl = SASL::new();
|
let config = SASLConfig::builder()
|
||||||
rsasl.install_callback(Arc::new(Callback::new(userdb)));
|
.with_defaults()
|
||||||
|
.with_callback(Callback::new(userdb))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mechs: Vec<&'static str> = rsasl
|
let mechs: Vec<&'static str> = SASLServer::<V>::new(config.clone())
|
||||||
.server_mech_list()
|
.get_available()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|m| m.mechanism.as_str())
|
.map(|m| m.mechanism.as_str())
|
||||||
.collect();
|
.collect();
|
||||||
@ -124,24 +115,18 @@ impl AuthenticationHandle {
|
|||||||
tracing::debug!(?mechs, "available mechs");
|
tracing::debug!(?mechs, "available mechs");
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
inner: Arc::new(Inner::new(rsasl)),
|
inner: Inner::new(config),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&self, mechanism: &Mechname) -> miette::Result<Session> {
|
pub fn start(&self, mechanism: &Mechname) -> miette::Result<Session<V>> {
|
||||||
Ok(self
|
Ok(SASLServer::new(self.inner.rsasl.clone())
|
||||||
.inner
|
.start_suggested(mechanism)
|
||||||
.rsasl
|
|
||||||
.server_start(mechanism)
|
|
||||||
.into_diagnostic()
|
.into_diagnostic()
|
||||||
.wrap_err("Failed to start a SASL authentication with the given mechanism")?)
|
.wrap_err("Failed to start a SASL authentication with the given mechanism")?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_available_mechs(&self) -> impl IntoIterator<Item = &Mechname> {
|
pub fn sess(&self) -> SASLServer<V> {
|
||||||
self.inner
|
SASLServer::new(self.inner.rsasl.clone())
|
||||||
.rsasl
|
|
||||||
.server_mech_list()
|
|
||||||
.into_iter()
|
|
||||||
.map(|m| m.mechanism)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,11 @@ use capnp::Error;
|
|||||||
use capnp_rpc::pry;
|
use capnp_rpc::pry;
|
||||||
use rsasl::mechname::Mechname;
|
use rsasl::mechname::Mechname;
|
||||||
use rsasl::property::AuthId;
|
use rsasl::property::AuthId;
|
||||||
use rsasl::session::{Session, Step};
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::{Formatter, Write};
|
use std::fmt::{Formatter, Write};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
use rsasl::prelude::{MessageSent, Session};
|
||||||
|
use rsasl::prelude::State as SaslState;
|
||||||
use tracing::Span;
|
use tracing::Span;
|
||||||
|
|
||||||
use crate::capnp::session::APISession;
|
use crate::capnp::session::APISession;
|
||||||
@ -15,6 +16,7 @@ use api::authenticationsystem_capnp::authentication::{
|
|||||||
AbortParams, AbortResults, Server as AuthenticationSystem, StepParams, StepResults,
|
AbortParams, AbortResults, Server as AuthenticationSystem, StepParams, StepResults,
|
||||||
};
|
};
|
||||||
use api::authenticationsystem_capnp::{response, response::Error as ErrorCode};
|
use api::authenticationsystem_capnp::{response, response::Error as ErrorCode};
|
||||||
|
use crate::authentication::V;
|
||||||
|
|
||||||
const TARGET: &str = "bffh::api::authenticationsystem";
|
const TARGET: &str = "bffh::api::authenticationsystem";
|
||||||
|
|
||||||
@ -27,7 +29,7 @@ impl Authentication {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
parent: &Span,
|
parent: &Span,
|
||||||
mechanism: &Mechname, /* TODO: this is stored in session as well, get it out of there. */
|
mechanism: &Mechname, /* TODO: this is stored in session as well, get it out of there. */
|
||||||
session: Session,
|
session: Session<V>,
|
||||||
sessionmanager: SessionManager,
|
sessionmanager: SessionManager,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let span = tracing::info_span!(
|
let span = tracing::info_span!(
|
||||||
@ -92,7 +94,7 @@ enum State {
|
|||||||
InvalidMechanism,
|
InvalidMechanism,
|
||||||
Finished,
|
Finished,
|
||||||
Aborted,
|
Aborted,
|
||||||
Running(Session, SessionManager),
|
Running(Session<V>, SessionManager),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthenticationSystem for Authentication {
|
impl AuthenticationSystem for Authentication {
|
||||||
@ -121,36 +123,35 @@ impl AuthenticationSystem for Authentication {
|
|||||||
{
|
{
|
||||||
let data: &[u8] = pry!(pry!(params.get()).get_data());
|
let data: &[u8] = pry!(pry!(params.get()).get_data());
|
||||||
|
|
||||||
let mut out = Cursor::new(Vec::new());
|
let mut out = Vec::new();
|
||||||
match session.step(Some(data), &mut out) {
|
match session.step(Some(data), &mut out) {
|
||||||
Ok(Step::Done(data)) => {
|
Ok(SaslState::Finished(sent)) => {
|
||||||
self.state = State::Finished;
|
self.state = State::Finished;
|
||||||
|
|
||||||
let uid = pry!(session.get_property::<AuthId>().ok_or_else(|| {
|
if let Some(user) = session.validation() {
|
||||||
tracing::warn!("Authentication didn't provide an authid as required.");
|
let session = manager.open(&self.span, user);
|
||||||
capnp::Error::failed(
|
|
||||||
"Authentication didn't provide an authid as required".to_string(),
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
let session = pry!(manager.open(&self.span, uid.as_ref()).ok_or_else(|| {
|
|
||||||
tracing::warn!(uid = uid.as_str(), "Failed to lookup the given user");
|
|
||||||
capnp::Error::failed("Failed to lookup the given user".to_string())
|
|
||||||
}));
|
|
||||||
|
|
||||||
response = Response {
|
response = Response {
|
||||||
union_field: "successful",
|
union_field: "successful",
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut builder = builder.init_successful();
|
let mut builder = builder.init_successful();
|
||||||
if data.is_some() {
|
if sent == MessageSent::Yes {
|
||||||
builder.set_additional_data(out.into_inner().as_slice());
|
builder.set_additional_data(out.as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
APISession::build(session, builder)
|
APISession::build(session, builder)
|
||||||
|
} else {
|
||||||
|
let mut builder = builder.init_failed();
|
||||||
|
builder.set_code(ErrorCode::InvalidCredentials);
|
||||||
|
|
||||||
|
response = Response {
|
||||||
|
union_field: "error",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Ok(Step::NeedsMore(_)) => {
|
}
|
||||||
|
Ok(SaslState::Running) => {
|
||||||
self.state = State::Running(session, manager);
|
self.state = State::Running(session, manager);
|
||||||
builder.set_challenge(out.into_inner().as_slice());
|
builder.set_challenge(out.as_slice());
|
||||||
|
|
||||||
response = Response {
|
response = Response {
|
||||||
union_field: "challenge",
|
union_field: "challenge",
|
||||||
|
@ -95,9 +95,10 @@ impl bootstrap::Server for BootCap {
|
|||||||
let builder = result.get();
|
let builder = result.get();
|
||||||
let mechs: Vec<_> = self
|
let mechs: Vec<_> = self
|
||||||
.authentication
|
.authentication
|
||||||
.list_available_mechs()
|
.sess()
|
||||||
|
.get_available()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|m| m.as_str())
|
.map(|m| m.mechanism.as_str())
|
||||||
.collect();
|
.collect();
|
||||||
let mut mechbuilder = builder.init_mechs(mechs.len() as u32);
|
let mut mechbuilder = builder.init_mechs(mechs.len() as u32);
|
||||||
for (i, m) in mechs.iter().enumerate() {
|
for (i, m) in mechs.iter().enumerate() {
|
||||||
@ -146,7 +147,7 @@ impl bootstrap::Server for BootCap {
|
|||||||
|
|
||||||
tracing::trace!(params.mechanism = mechanism, "method call");
|
tracing::trace!(params.mechanism = mechanism, "method call");
|
||||||
|
|
||||||
let mechname = Mechname::new(mechanism.as_bytes());
|
let mechname = Mechname::parse(mechanism.as_bytes());
|
||||||
let auth = if let Ok(mechname) = mechname {
|
let auth = if let Ok(mechname) = mechname {
|
||||||
if let Ok(session) = self.authentication.start(mechname) {
|
if let Ok(session) = self.authentication.start(mechname) {
|
||||||
Authentication::new(&self.span, mechname, session, self.sessionmanager.clone())
|
Authentication::new(&self.span, mechname, session, self.sessionmanager.clone())
|
||||||
|
@ -56,7 +56,7 @@ impl InitiatorCallbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_session(&self, uid: &str) -> Option<SessionHandle> {
|
pub fn open_session(&self, uid: &str) -> Option<SessionHandle> {
|
||||||
self.sessions.open(&self.span, uid)
|
self.sessions.try_open(&self.span, uid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ use crate::resources::Resource;
|
|||||||
use crate::users::{db, UserRef};
|
use crate::users::{db, UserRef};
|
||||||
use crate::Users;
|
use crate::Users;
|
||||||
use tracing::Span;
|
use tracing::Span;
|
||||||
|
use crate::users::db::User;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SessionManager {
|
pub struct SessionManager {
|
||||||
@ -16,25 +17,25 @@ impl SessionManager {
|
|||||||
Self { users, roles }
|
Self { users, roles }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_open(&self, parent: &Span, uid: impl AsRef<str>) -> Option<SessionHandle> {
|
||||||
|
self.users.get_user(uid.as_ref()).map(|user| self.open(parent, user))
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: make infallible
|
// TODO: make infallible
|
||||||
pub fn open(&self, parent: &Span, uid: impl AsRef<str>) -> Option<SessionHandle> {
|
pub fn open(&self, parent: &Span, user: User) -> SessionHandle {
|
||||||
let uid = uid.as_ref();
|
let uid = user.id.as_str();
|
||||||
if let Some(user) = self.users.get_user(uid) {
|
|
||||||
let span = tracing::info_span!(
|
let span = tracing::info_span!(
|
||||||
target: "bffh::api",
|
target: "bffh::api",
|
||||||
parent: parent,
|
parent: parent,
|
||||||
"session",
|
"session",
|
||||||
uid = uid,
|
uid,
|
||||||
);
|
);
|
||||||
tracing::trace!(parent: &span, uid, ?user, "opening session");
|
tracing::trace!(parent: &span, uid, ?user, "opening session");
|
||||||
Some(SessionHandle {
|
SessionHandle {
|
||||||
span,
|
span,
|
||||||
users: self.users.clone(),
|
users: self.users.clone(),
|
||||||
roles: self.roles.clone(),
|
roles: self.roles.clone(),
|
||||||
user: UserRef::new(user.id),
|
user: UserRef::new(user.id),
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,11 +34,9 @@ fn hash_pw(pw: &[u8]) -> argon2::Result<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
pub fn check_password(&self, pwd: &[u8]) -> miette::Result<bool> {
|
pub fn check_password(&self, pwd: &[u8]) -> Result<bool, argon2::Error> {
|
||||||
if let Some(ref encoded) = self.userdata.passwd {
|
if let Some(ref encoded) = self.userdata.passwd {
|
||||||
argon2::verify_encoded(encoded, pwd)
|
argon2::verify_encoded(encoded, pwd)
|
||||||
.into_diagnostic()
|
|
||||||
.wrap_err("Stored password is an invalid string")
|
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user