From 87af5fde94c3f8010bd79761d91e2407cdb284d1 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 12 Mar 2022 17:31:53 +0100 Subject: [PATCH] Implement more API --- Cargo.lock | 4 +- Cargo.toml | 3 +- bffhd/authentication/mod.rs | 15 +++-- bffhd/authorization/mod.rs | 36 +++++++++++- bffhd/capnp/authenticationsystem.rs | 84 +++++++++++++++++++++++++--- bffhd/capnp/connection.rs | 87 ++++++++++++++++++++++++++--- bffhd/capnp/machine.rs | 5 +- bffhd/capnp/machinesystem.rs | 52 ++++++++++++++--- bffhd/capnp/mod.rs | 18 +++++- bffhd/capnp/permissionsystem.rs | 8 +++ bffhd/capnp/session.rs | 25 ++++++--- bffhd/capnp/user.rs | 5 +- bffhd/capnp/user_system.rs | 8 ++- bffhd/lib.rs | 11 +++- bffhd/session/mod.rs | 32 +++++++++++ 15 files changed, 341 insertions(+), 52 deletions(-) create mode 100644 bffhd/session/mod.rs diff --git a/Cargo.lock b/Cargo.lock index b32bba8..c74eb6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1885,9 +1885,7 @@ dependencies = [ [[package]] name = "rsasl" -version = "2.0.0-preview2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2adcc7fb89ad42cf35d527905a11232c02fa030f7b6983b8c9880c385da2ae8e" +version = "2.0.0-preview3" dependencies = [ "base64", "digest 0.10.3", diff --git a/Cargo.toml b/Cargo.toml index 41c2188..7068fd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,7 +69,8 @@ capnp = "0.14" capnp-rpc = "0.14.1" # API Authentication -rsasl = "2.0.0-preview2" +#rsasl = "2.0.0-preview3" +rsasl = { path = "../../rsasl" } futures-signals = "0.3.22" async-oneshot = "0.5" diff --git a/bffhd/authentication/mod.rs b/bffhd/authentication/mod.rs index 9594c5e..d3bff8d 100644 --- a/bffhd/authentication/mod.rs +++ b/bffhd/authentication/mod.rs @@ -16,16 +16,21 @@ impl Inner { } #[derive(Clone)] -pub struct AuthenticationHandler { +pub struct AuthenticationHandle { inner: Arc, } -impl AuthenticationHandler { - pub fn new(rsasl: SASL) -> Self { +impl AuthenticationHandle { + pub fn new() -> Self { + let rsasl = SASL::new(); Self { inner: Arc::new(Inner::new(rsasl)) } } - pub fn start(&self, mechanism: &Mechname) -> Result { - self.inner.rsasl.server_start(mechanism) + pub fn start(&self, mechanism: &Mechname) -> anyhow::Result { + Ok(self.inner.rsasl.server_start(mechanism)?) + } + + pub fn list_available_mechs(&self) -> impl IntoIterator { + self.inner.rsasl.server_mech_list().into_iter().map(|m| m.mechanism) } } \ No newline at end of file diff --git a/bffhd/authorization/mod.rs b/bffhd/authorization/mod.rs index e86a7c8..7ed788d 100644 --- a/bffhd/authorization/mod.rs +++ b/bffhd/authorization/mod.rs @@ -1,3 +1,37 @@ +use std::sync::Arc; +use crate::authorization::permissions::Permission; +use crate::authorization::roles::Role; +use crate::users::User; pub mod permissions; -pub mod roles; \ No newline at end of file +pub mod roles; + +struct Inner { + +} +impl Inner { + pub fn new() -> Self { + Self {} + } +} + +#[derive(Clone)] +pub struct AuthorizationHandle { + inner: Arc, +} + +impl AuthorizationHandle { + pub fn new() -> Self { + Self { + inner: Arc::new(Inner::new()) + } + } + + pub fn lookup_user(&self, uid: impl AsRef) -> Option { + unimplemented!() + } + + pub fn is_permitted<'a>(&self, roles: impl IntoIterator, perm: impl AsRef) -> bool { + unimplemented!() + } +} \ No newline at end of file diff --git a/bffhd/capnp/authenticationsystem.rs b/bffhd/capnp/authenticationsystem.rs index db15266..a02f96e 100644 --- a/bffhd/capnp/authenticationsystem.rs +++ b/bffhd/capnp/authenticationsystem.rs @@ -1,33 +1,101 @@ -use std::io::Cursor; use capnp::capability::Promise; use capnp::Error; use capnp_rpc::pry; -use rsasl::session::{Session, Step}; +use rsasl::property::AuthId; +use rsasl::session::{Session, Step, StepResult}; +use std::io::Cursor; +use crate::authorization::AuthorizationHandle; +use crate::capnp::session::APISession; +use crate::session::SessionManager; use api::authenticationsystem_capnp::authentication::{ - Server as AuthenticationSystem, - StepParams, StepResults, - AbortParams, AbortResults, + AbortParams, AbortResults, Server as AuthenticationSystem, StepParams, StepResults, }; +use api::authenticationsystem_capnp::{response, response::Error as ErrorCode}; pub struct Authentication { state: State, } +impl Authentication { + pub fn new(session: Session, sessionmanager: SessionManager) -> Self { + Self { + state: State::Running(session, sessionmanager), + } + } + + pub fn invalid_mechanism() -> Self { + Self { + state: State::InvalidMechanism, + } + } + + fn build_error(&self, response: response::Builder) { + if let State::Running(_, _) = self.state { + return; + } + + let mut builder = response.init_failed(); + match self.state { + State::InvalidMechanism => builder.set_code(ErrorCode::BadMechanism), + State::Finished => builder.set_code(ErrorCode::Aborted), + State::Aborted => builder.set_code(ErrorCode::Aborted), + _ => unreachable!(), + } + } +} + enum State { InvalidMechanism, Finished, Aborted, - Running(Session) + Running(Session, SessionManager), } impl AuthenticationSystem for Authentication { fn step(&mut self, params: StepParams, mut results: StepResults) -> Promise<(), Error> { - unimplemented!(); + let mut builder = results.get(); + if let State::Running(mut session, manager) = + std::mem::replace(&mut self.state, State::Aborted) + { + let data: &[u8] = pry!(pry!(params.get()).get_data()); + let mut out = Cursor::new(Vec::new()); + match session.step(Some(data), &mut out) { + Ok(Step::Done(data)) => { + self.state = State::Finished; + + let uid = pry!(session.get_property::().ok_or(capnp::Error::failed( + "Authentication didn't provide an authid as required".to_string() + ))); + let session = pry!(manager.open(uid.as_ref()).ok_or(capnp::Error::failed( + "Failed to lookup the given user".to_string() + ))); + + let mut builder = builder.init_successful(); + if data.is_some() { + builder.set_additional_data(out.into_inner().as_slice()); + } + + APISession::build(session, builder) + } + Ok(Step::NeedsMore(_)) => { + self.state = State::Running(session, manager); + builder.set_challenge(out.into_inner().as_slice()); + } + Err(_) => { + self.state = State::Aborted; + self.build_error(builder); + } + } + } else { + self.build_error(builder); + } + + Promise::ok(()) } fn abort(&mut self, _: AbortParams, _: AbortResults) -> Promise<(), Error> { self.state = State::Aborted; Promise::ok(()) } -} \ No newline at end of file +} diff --git a/bffhd/capnp/connection.rs b/bffhd/capnp/connection.rs index d0568e8..f7f46d4 100644 --- a/bffhd/capnp/connection.rs +++ b/bffhd/capnp/connection.rs @@ -1,15 +1,88 @@ -use api::connection_capnp::bootstrap::Server as Bootstrap; pub use api::connection_capnp::bootstrap::Client; +use api::connection_capnp::bootstrap; + +use capnp::capability::Promise; +use capnp_rpc::pry; +use rsasl::mechname::Mechname; +use crate::authentication::AuthenticationHandle; +use crate::capnp::authenticationsystem::Authentication; +use crate::session::SessionManager; -#[derive(Debug)] /// Cap'n Proto API Handler -pub struct BootCap; +pub struct BootCap { + authentication: AuthenticationHandle, + sessionmanager: SessionManager, +} impl BootCap { - pub fn new() -> Self { - Self + pub fn new(authentication: AuthenticationHandle, sessionmanager: SessionManager) -> Self { + Self { + authentication, + sessionmanager, + } } } -impl Bootstrap for BootCap { -} \ No newline at end of file +impl bootstrap::Server for BootCap { + fn get_a_p_i_version( + &mut self, + _: bootstrap::GetAPIVersionParams, + _: bootstrap::GetAPIVersionResults, + ) -> Promise<(), ::capnp::Error> { + Promise::ok(()) + } + + fn get_server_release( + &mut self, + _: bootstrap::GetServerReleaseParams, + mut result: bootstrap::GetServerReleaseResults, + ) -> Promise<(), ::capnp::Error> { + let mut builder = result.get(); + builder.set_name("bffhd"); + builder.set_release(crate::RELEASE_STRING); + Promise::ok(()) + } + + fn mechanisms( + &mut self, + _: bootstrap::MechanismsParams, + mut result: bootstrap::MechanismsResults, + ) -> Promise<(), ::capnp::Error> { + let mut builder = result.get(); + let mechs: Vec<_> = self.authentication.list_available_mechs() + .into_iter() + .map(|m| m.as_str()) + .collect(); + let mut mechbuilder = builder.init_mechs(mechs.len() as u32); + for (i,m) in mechs.iter().enumerate() { + mechbuilder.set(i as u32, m); + } + + Promise::ok(()) + } + + fn create_session( + &mut self, + params: bootstrap::CreateSessionParams, + mut result: bootstrap::CreateSessionResults, + ) -> Promise<(), ::capnp::Error> { + let params = pry!(params.get()); + let mechanism: &str = pry!(params.get_mechanism()); + + let mechname = Mechname::new(mechanism.as_bytes()); + let auth = if let Ok(mechname) = mechname { + if let Ok(session) = self.authentication.start(mechname) { + Authentication::new(session, self.sessionmanager.clone()) + } else { + Authentication::invalid_mechanism() + } + } else { + Authentication::invalid_mechanism() + }; + + let mut builder = result.get(); + builder.set_authentication(capnp_rpc::new_client(auth)); + + Promise::ok(()) + } +} diff --git a/bffhd/capnp/machine.rs b/bffhd/capnp/machine.rs index 43014ac..4e5bc7a 100644 --- a/bffhd/capnp/machine.rs +++ b/bffhd/capnp/machine.rs @@ -6,8 +6,11 @@ use api::machine_capnp::machine::{ manage, manage::Server as ManageServer, use_, use_::Server as UseServer, }; +use crate::session::SessionHandle; -pub struct Machine; +pub struct Machine { + session: SessionHandle, +} impl InfoServer for Machine { fn get_property_list( diff --git a/bffhd/capnp/machinesystem.rs b/bffhd/capnp/machinesystem.rs index 8a9502c..9af9b16 100644 --- a/bffhd/capnp/machinesystem.rs +++ b/bffhd/capnp/machinesystem.rs @@ -1,19 +1,55 @@ -use api::machinesystem_capnp::machine_system::Server as MachineSystem; +use crate::authorization::AuthorizationHandle; +use crate::session::SessionHandle; +use api::machinesystem_capnp::machine_system::{ + info, InfoParams, InfoResults, Server as MachineSystem, +}; +use capnp::capability::Promise; #[derive(Debug, Clone)] pub struct Machines { - + session: SessionHandle, } impl Machines { - pub fn new() -> Self { - Self { - - } + pub fn new(session: SessionHandle) -> Self { + Self { session } } - } impl MachineSystem for Machines { + fn info(&mut self, _: InfoParams, _: InfoResults) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( + "method not implemented".to_string(), + )) + } +} -} \ No newline at end of file +impl info::Server for Machines { + fn get_machine_list( + &mut self, + _: info::GetMachineListParams, + _: info::GetMachineListResults, + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( + "method not implemented".to_string(), + )) + } + fn get_machine( + &mut self, + _: info::GetMachineParams, + _: info::GetMachineResults, + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( + "method not implemented".to_string(), + )) + } + fn get_machine_u_r_n( + &mut self, + _: info::GetMachineURNParams, + _: info::GetMachineURNResults, + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( + "method not implemented".to_string(), + )) + } +} diff --git a/bffhd/capnp/mod.rs b/bffhd/capnp/mod.rs index a3f3a9b..165aac9 100644 --- a/bffhd/capnp/mod.rs +++ b/bffhd/capnp/mod.rs @@ -17,8 +17,11 @@ use std::future::Future; use std::io; use std::io::BufReader; use std::sync::Arc; +use crate::authentication::AuthenticationHandle; +use crate::authorization::AuthorizationHandle; use crate::error::Result; +use crate::session::SessionManager; mod authenticationsystem; mod connection; @@ -33,6 +36,8 @@ pub struct APIServer { executor: Executor<'static>, sockets: Vec, acceptor: TlsAcceptor, + sessionmanager: SessionManager, + authentication: AuthenticationHandle, } impl APIServer { @@ -40,11 +45,15 @@ impl APIServer { executor: Executor<'static>, sockets: Vec, acceptor: TlsAcceptor, + sessionmanager: SessionManager, + authentication: AuthenticationHandle, ) -> Self { Self { executor, sockets, acceptor, + sessionmanager, + authentication, } } @@ -52,6 +61,8 @@ impl APIServer { executor: Executor<'static>, listens: impl IntoIterator, acceptor: TlsAcceptor, + sessionmanager: SessionManager, + authentication: AuthenticationHandle, ) -> anyhow::Result { let span = tracing::info_span!("binding API listen sockets"); let _guard = span.enter(); @@ -100,10 +111,10 @@ impl APIServer { tracing::warn!("No usable listen addresses configured for the API server!"); } - Ok(Self::new(executor, sockets, acceptor)) + Ok(Self::new(executor, sockets, acceptor, sessionmanager, authentication)) } - pub async fn handle_until(&mut self, stop: impl Future) { + pub async fn handle_until(self, stop: impl Future) { stream::select_all( self.sockets .iter() @@ -132,7 +143,8 @@ impl APIServer { }; let (rx, tx) = futures_lite::io::split(stream); let vat = VatNetwork::new(rx, tx, Side::Server, Default::default()); - let bootstrap: connection::Client = capnp_rpc::new_client(connection::BootCap::new()); + + let bootstrap: connection::Client = capnp_rpc::new_client(connection::BootCap::new(self.authentication.clone(), self.sessionmanager.clone())); if let Err(e) = RpcSystem::new(Box::new(vat), Some(bootstrap.client)).await { tracing::error!("Error during RPC handling: {}", e); diff --git a/bffhd/capnp/permissionsystem.rs b/bffhd/capnp/permissionsystem.rs index bc67926..cf7f803 100644 --- a/bffhd/capnp/permissionsystem.rs +++ b/bffhd/capnp/permissionsystem.rs @@ -1,7 +1,15 @@ use api::permissionsystem_capnp::permission_system::Server as PermissionSystem; +use crate::authorization::AuthorizationHandle; +use crate::session::SessionHandle; pub struct Permissions; +impl Permissions { + pub fn new(session: SessionHandle) -> Self { + Self + } +} + impl PermissionSystem for Permissions { } \ No newline at end of file diff --git a/bffhd/capnp/session.rs b/bffhd/capnp/session.rs index 7b3190f..dfef19a 100644 --- a/bffhd/capnp/session.rs +++ b/bffhd/capnp/session.rs @@ -1,17 +1,24 @@ +use api::authenticationsystem_capnp::response::successful::Builder; +use capnp::capability::Promise; +use crate::authorization::AuthorizationHandle; use crate::capnp::machinesystem::Machines; +use crate::capnp::permissionsystem::Permissions; use crate::capnp::user_system::Users; +use crate::session::{SessionHandle, SessionManager}; +use crate::users::User; #[derive(Debug, Clone)] -pub struct Session { - resources: Machines, - users: Users, -} +pub struct APISession; -impl Session { +impl APISession { pub fn new() -> Self { - Session { - resources: Machines::new(), - users: Users::new(), - } + Self + } + + pub fn build(session: SessionHandle, mut builder: Builder) { + let mut builder = builder.init_session(); + builder.set_machine_system(capnp_rpc::new_client(Machines::new(session.clone()))); + builder.set_user_system(capnp_rpc::new_client(Users::new(session.clone()))); + builder.set_permission_system(capnp_rpc::new_client(Permissions::new(session))); } } \ No newline at end of file diff --git a/bffhd/capnp/user.rs b/bffhd/capnp/user.rs index 3c91c25..3ce5f3e 100644 --- a/bffhd/capnp/user.rs +++ b/bffhd/capnp/user.rs @@ -4,8 +4,11 @@ use api::user_capnp::user::{ manage, admin, }; +use crate::session::SessionHandle; -struct User; +struct User { + session: SessionHandle, +} impl info::Server for User { diff --git a/bffhd/capnp/user_system.rs b/bffhd/capnp/user_system.rs index 01769b0..7408e85 100644 --- a/bffhd/capnp/user_system.rs +++ b/bffhd/capnp/user_system.rs @@ -7,16 +7,18 @@ use api::usersystem_capnp::user_system::{ info, info::Server as InfoServer, manage, manage::Server as ManageServer, }; +use crate::authorization::AuthorizationHandle; +use crate::session::SessionHandle; #[derive(Debug, Clone)] pub struct Users { - + session: SessionHandle, } impl Users { - pub fn new() -> Self { + pub fn new(session: SessionHandle) -> Self { Self { - + session, } } } diff --git a/bffhd/lib.rs b/bffhd/lib.rs index b1633ef..cbb3b3a 100644 --- a/bffhd/lib.rs +++ b/bffhd/lib.rs @@ -37,6 +37,7 @@ mod tls; mod keylog; mod logging; mod audit; +mod session; use std::fs::File; use std::io::BufReader; @@ -49,10 +50,14 @@ use rustls::{Certificate, KeyLogFile, PrivateKey, ServerConfig}; use rustls::server::NoClientAuth; use signal_hook::consts::signal::*; use executor::pool::Executor; +use crate::authentication::AuthenticationHandle; use crate::capnp::APIServer; use crate::config::{Config, TlsListen}; +use crate::session::SessionManager; use crate::tls::TlsConfig; +pub const RELEASE_STRING: &'static str = env!("BFFHD_RELEASE_STRING"); + pub struct Diflouroborane { executor: Executor<'static>, } @@ -65,7 +70,6 @@ impl Diflouroborane { } fn log_version_number(&self) { - const RELEASE_STRING: &'static str = env!("BFFHD_RELEASE_STRING"); tracing::info!(version=RELEASE_STRING, "Starting"); } @@ -86,7 +90,10 @@ impl Diflouroborane { let tlsconfig = TlsConfig::new(config.tlskeylog.as_ref(), !config.is_quiet())?; let acceptor = tlsconfig.make_tls_acceptor(&config.tlsconfig)?; - let mut apiserver = self.executor.run(APIServer::bind(self.executor.clone(), &config.listens, acceptor))?; + let sessionmanager = SessionManager::new(); + let authentication = AuthenticationHandle::new(); + + let mut apiserver = self.executor.run(APIServer::bind(self.executor.clone(), &config.listens, acceptor, sessionmanager, authentication))?; let (mut tx, rx) = async_oneshot::oneshot(); diff --git a/bffhd/session/mod.rs b/bffhd/session/mod.rs new file mode 100644 index 0000000..0798d35 --- /dev/null +++ b/bffhd/session/mod.rs @@ -0,0 +1,32 @@ +use std::sync::Arc; + +struct Inner { + +} +impl Inner { + pub fn new() -> Self { + Self { } + } +} + +#[derive(Clone)] +pub struct SessionManager { + inner: Arc, +} +impl SessionManager { + pub fn new() -> Self { + Self { + inner: Arc::new(Inner::new()), + } + } + pub fn open(&self, uid: impl AsRef) -> Option { + unimplemented!() + } +} + +#[derive(Clone, Debug)] +pub struct SessionHandle { +} + +impl SessionHandle { +} \ No newline at end of file