diff --git a/Cargo.toml b/Cargo.toml index 51f8259..5faa1e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,8 +28,6 @@ capnp-rpc = "0.12" toml = "0.5" serde = { version = "1.0", features = ["derive"] } -casbin = "0.2" - uuid = { version = "0.8", features = ["serde", "v4"] } clap = "2.33" diff --git a/build.rs b/build.rs index 59bc45c..6394643 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,4 @@ fn main() { - ::capnpc::CompilerCommand::new().file("schema/api.capnp").run().unwrap() + ::capnpc::CompilerCommand::new().file("schema/api.capnp").run().unwrap(); + ::capnpc::CompilerCommand::new().file("schema/auth.capnp").run().unwrap(); } diff --git a/src/access.rs b/src/access.rs index 58196aa..52da4da 100644 --- a/src/access.rs +++ b/src/access.rs @@ -3,64 +3,20 @@ use slog::Logger; -use casbin::prelude::*; - use crate::config::Config; -use crate::auth::Authentication; -use crate::error::Result; -use std::rc::Rc; -use async_std::sync::{Arc, RwLock}; - -use std::ops::Deref; pub struct PermissionsProvider { log: Logger, - pdb: Enforcer, } impl PermissionsProvider { - pub fn new(log: Logger, pdb: Enforcer) -> Self { - Self { log, pdb } - } - - pub fn enforce(&self, actor: &str, object: &str, action: &str) -> Result { - let b = self.pdb.enforce(vec![actor, object, action])?; - if b { - trace!(self.log, "Granted {} on {} for {}", action, object, actor); - } else { - trace!(self.log, "Denied {} on {} for {}", action, object, actor); - } - Ok(b) - } -} - -#[derive(Clone)] -pub struct Permissions { - inner: Arc>, - auth: Rc, -} - -impl Permissions { - pub fn new(inner: Arc>, auth: Rc) -> Self { - Self { inner, auth } - } - - pub async fn enforce(&self, object: &str, action: &str) -> Result { - if let Some(actor) = self.auth.state.read().await.deref() { - self.inner.read().await.enforce(&actor, object, action) - } else { - Ok(false) - } + pub fn new(log: Logger) -> Self { + Self { log } } } /// This line documents init pub async fn init(log: Logger, config: &Config) -> std::result::Result> { - let model = Model::from_file(config.access.model.clone()).await?; - let adapter = Box::new(FileAdapter::new(config.access.policy.clone())); - - let e = Enforcer::new(model, adapter).await?; - - return Ok(PermissionsProvider::new(log, e)); + return Ok(PermissionsProvider::new(log)); } diff --git a/src/api.rs b/src/api.rs index ba37aae..30dcbb4 100644 --- a/src/api.rs +++ b/src/api.rs @@ -4,32 +4,12 @@ pub mod gen { include!(concat!(env!("OUT_DIR"), "/schema/api_capnp.rs")); } -use std::default::Default; use async_std::net::TcpStream; -use futures::task::Spawn; -use futures::FutureExt; -use futures_signals::signal::Mutable; -use casbin::Enforcer; -use casbin::MgmtApi; - use slog::Logger; -use std::rc::Rc; -use async_std::sync::{Arc, RwLock}; +use crate::error::Result; -use crate::machine::{MachinesProvider, Machines}; -use crate::auth::{AuthenticationProvider, Authentication}; -use crate::access::{PermissionsProvider, Permissions}; - -use capnp::{Error}; -use capnp::capability::Promise; -use capnp_rpc::RpcSystem; -use capnp_rpc::twoparty::VatNetwork; -use capnp_rpc::rpc_twoparty_capnp::Side; - -use std::ops::Deref; - -pub async fn handle_connection(log: Logger, socket: TcpStream) -> Result<(), Error> { +pub async fn handle_connection(log: Logger, socket: TcpStream) -> Result<()> { unimplemented!() } diff --git a/src/auth.rs b/src/auth.rs index bd1066a..dabc7fc 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -3,147 +3,43 @@ //! Authorization is over in `access.rs` //! Authentication using SASL -use std::collections::HashMap; -use std::fmt; -use std::error::Error; -use std::path::Path; -use std::fs::File; -use std::io::{Read, Write}; -use std::ops::Deref; - -use async_std::sync::{Arc, RwLock}; -use capnp::capability::Promise; - -use futures_signals::signal::Mutable; -use casbin::{Enforcer, Model, FileAdapter}; - use slog::Logger; +use rsasl::{SASL, Property, Step, Session, ReturnCode}; +use rsasl::sys::{Gsasl, Gsasl_session}; + use crate::error::Result; use crate::config::Config; -pub async fn init(log: Logger, config: Config) -> Result { - let passdb = open_passdb(&config.passdb).unwrap(); - - let m = Model::from_file(&config.access.model).await?; - let a = FileAdapter::new(config.access.policy); - let enforcer = Enforcer::new(m, Box::new(a)).await?; - - Ok(AuthenticationProvider::new(passdb, enforcer)) +pub mod gen { + include!(concat!(env!("OUT_DIR"), "/schema/auth_capnp.rs")); } -#[derive(Debug)] -pub enum SASLError { - /// Expected UTF-8, got something else - UTF8, - /// A bad Challenge was provided - BadChallenge, - /// Enforcer Failure - Enforcer, -} -impl fmt::Display for SASLError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Bad SASL Exchange") - } -} -impl Error for SASLError {} +extern "C" fn callback(ctx: *mut Gsasl, sctx: *mut Gsasl_session, prop: Property) -> i32 { + let sasl = SASL::from_ptr(ctx); + let mut session = Session::from_ptr(sctx); -type PassDB = HashMap; -pub fn open_passdb(path: &Path) -> Option { - if path.is_file() { - let mut fp = File::open(path).unwrap(); - let mut content = String::new(); - fp.read_to_string(&mut content).unwrap(); - let map = toml::from_str(&content).ok()?; - return Some(map); - } else { - let mut map = HashMap::new(); - map.insert("Testuser".to_string(), "Testpass".to_string()); - let mut fp = File::create(&path).unwrap(); - let toml = toml::to_string(&map).unwrap(); - fp.write_all(&toml.as_bytes()).unwrap(); - return Some(map); + let rc = match prop { + _ => { ReturnCode::GSASL_NO_CALLBACK } + }; + + rc as i32 +} + +pub struct Auth { + ctx: SASL, +} + +impl Auth { + pub fn new() -> Self { + let mut ctx = SASL::new().unwrap(); + + ctx.install_callback(Some(callback)); + + Self { ctx } } } -pub struct Plain { - // FIXME: I don't want to store passwords. - passdb: PassDB, - enforcer: Enforcer, -} - -impl Plain { - pub fn step<'a>(&self, data: &'a [u8]) -> Result<(bool, &'a str)> { - let data = std::str::from_utf8(data).map_err(|_| SASLError::UTF8)?; - if let Some((authzid, authcid, passwd)) = split_nul(data) { - - // Check if we know about that user - if let Some(pwd) = self.passdb.get(authcid) { - // Check the provided password - // FIXME: At least use hashes - if pwd == passwd { - // authzid is the Identity the user wants to act as. - // If that is unset, shortcut to Success - if authzid == "" || authzid == authcid { - return Ok((true, authcid)); - } - - if let Ok(b) = self.enforcer.enforce(vec![authcid, authzid, "su"]) { - if b { - return Ok((true, authzid)); - } else { - return Ok((false, authzid)); - } - } else { - return Err(SASLError::Enforcer.into()); - } - - } - } - Ok((false, authzid)) - } else { - return Err(SASLError::BadChallenge.into()) - } - } -} - -pub fn split_nul(string: &str) -> Option<(&str, &str, &str)> { - let mut i = string.split(|b| b == '\0'); - - let a = i.next()?; - let b = i.next()?; - let c = i.next()?; - - Some((a,b,c)) -} - - -pub struct AuthenticationProvider { - pub plain: Plain, -} - -impl AuthenticationProvider { - pub fn new(passdb: PassDB, enforcer: Enforcer) -> Self { - Self { - plain: Plain { passdb, enforcer } - } - } - - pub fn mechs(&self) -> Vec<&'static str> { - vec!["PLAIN"] - } -} - -#[derive(Clone)] -pub struct Authentication { - pub state: Arc>>, - provider: Arc>, -} -impl Authentication { - pub fn new(provider: Arc>) -> Self { - Self { - state: Arc::new(RwLock::new(None)), - provider: provider, - } - } +pub async fn init(log: Logger, config: Config) -> Result { + Ok(Auth::new()) } diff --git a/src/error.rs b/src/error.rs index 3fe1f90..7a34267 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,19 +1,42 @@ use std::io; +use std::fmt; use toml; -use crate::auth::SASLError; +use rsasl::SaslError; #[derive(Debug)] pub enum Error { TomlDe(toml::de::Error), TomlSer(toml::ser::Error), - SASL(SASLError), + SASL(SaslError), IO(io::Error), Boxed(Box), } -impl From for Error { - fn from(e: SASLError) -> Error { +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::TomlDe(e) => { + write!(f, "TOML Decoding error: {}", e) + }, + Error::TomlSer(e) => { + write!(f, "TOML Serialization error: {}", e) + }, + Error::SASL(e) => { + write!(f, "SASL Error: {}", e) + }, + Error::IO(e) => { + write!(f, "IO Error: {}", e) + }, + Error::Boxed(e) => { + write!(f, "{}", e) + } + } + } +} + +impl From for Error { + fn from(e: SaslError) -> Error { Error::SASL(e) } } diff --git a/src/machine.rs b/src/machine.rs index e45770e..d53eae0 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -10,7 +10,6 @@ use toml; use crate::error::Result; use crate::config::Config; -use crate::access::Permissions; use std::rc::Rc; use async_std::sync::{Arc, RwLock}; @@ -90,11 +89,10 @@ impl MachinesProvider { #[derive(Clone)] pub struct Machines { inner: Arc>, - perm: Rc, } impl Machines { - pub fn new(inner: Arc>, perm: Rc) -> Self { - Self { inner, perm } + pub fn new(inner: Arc>) -> Self { + Self { inner } } } diff --git a/src/main.rs b/src/main.rs index f7c766a..90d10d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,8 +17,6 @@ use signal_hook::iterator::Signals; use clap::{App, Arg}; -use api::gen as api_capnp; - use futures::prelude::*; use futures::executor::{LocalPool, ThreadPool}; use futures::compat::Stream01CompatExt; @@ -40,6 +38,12 @@ use std::sync::Arc; use error::Error; +// Re-Export generated capnp code. +// This is necessary because the Rust generator expects types to be found in the +// `crate::_capnp` hierarchy. +use api::gen as api_capnp; +use auth::gen as auth_capnp; + // Returning a `Result` from `main` allows us to use the `?` shorthand. // In the case of an Err it will be printed using `fmt::Debug` fn main() -> Result<(), Error> {