From: Ian Jackson Date: Sun, 26 Jul 2020 00:25:09 +0000 (+0100) Subject: wip cmds X-Git-Tag: otter-0.2.0~1258 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=7d8530a83a9d1e67fc0bab7fe7b77ff88ad42dc4;p=otter.git wip cmds --- diff --git a/src/cmdlistener.rs b/src/cmdlistener.rs index 5b4444ca..4173fe2c 100644 --- a/src/cmdlistener.rs +++ b/src/cmdlistener.rs @@ -23,21 +23,22 @@ pub struct CommandListener { type CSWrite = BufWriter; -struct CommandStream { +struct CommandStream<'d> { euid : Result, read : io::Lines>, write : CSWrite, scope : Option, + desc : &'d str, } type CSE = anyhow::Error; -impl CommandStream { +impl CommandStream<'_> { #[throws(CSE)] pub fn mainloop(mut self) { for l in &mut self.read { let l = l.context("read")?; - decode_and_process(&l, &mut self.write)?; + decode_and_process(&mut self, &l)?; write!(&mut self.write, "\n")?; self.write.flush()?; } @@ -57,69 +58,145 @@ use MgmtError::*; type ME = MgmtError; #[throws(CSE)] -pub fn decode_and_process(s: &str, w: &mut CSWrite) { - let resp = self::decode_process_inner(s) +pub fn decode_and_process(cs: &mut CommandStream, s: &str) { + let resp = self::decode_process_inner(cs, s) .unwrap_or_else(|e| MgmtResponse::Error(format!("{}", e))); - serde_lexpr::to_writer(w, &resp)?; + serde_lexpr::to_writer(&mut cs.write, &resp)?; } #[throws(ME)] -fn decode_process_inner(s: &str)-> MgmtResponse { +fn decode_process_inner(cs: &mut CommandStream, s: &str)-> MgmtResponse { let cmd : MgmtCommand = serde_lexpr::from_str(s)?; - execute(cmd)? + execute(cs, cmd)? } const USERLIST : &str = "/etc/userlist"; -struct Authorized; +#[derive(Error,Debug)] +#[error("internal AuthorisationError {0}")] +struct AuthorisationError(String); + +struct Authorised (PhantomData); +//struct AuthorisedScope (Authorised, ManagementScope); +struct AuthorisedSatisfactory (ManagementScope); +use libc::uid_t; + +impl AuthorisedSatisfactory { + fn into_inner(self) -> ManagementScope { self.0 } +} + +impl Authorised { + fn authorise() -> Authorised { Authorised(PhantomData) } +} + +impl From<(Authorised, ManagementScope)> for AuthorisedSatisfactory { + fn from((_,s): (Authorised, ManagementScope)) -> Self { Self(s) } +} +impl From<((Authorised, Authorised), ManagementScope)> for AuthorisedSatisfactory { + fn from(((..),s): ((Authorised, Authorised), ManagementScope)) -> Self { Self(s) } +} + +impl From for AuthorisationError { + fn from(a: anyhow::Error) -> AuthorisationError { + AuthorisationError(format!("{}",a)) + } +} + +impl CommandStream<'_> { + #[throws(AuthorisationError)] + fn authorised_uid(&self, wanted: Option) + -> Authorised<(Passwd,uid_t)> { + let client_euid = self.euid.context("client euid")?; + let server_euid = unsafe { libc::getuid() }; + if client_euid == 0 || + client_euid == server_euid || + Some(client_euid) == wanted + { + return Authorised::authorise(); + } + Err(anyhow!("{}: euid mismatch: client={:?} server={:?} wanted={:?}", + &self.desc, client_euid, server_euid, wanted))? + } + + fn map_auth_err(&self, ae: AuthorisationError) -> MgmtError { + eprintln!("command connection {}: authorisation error: {:?}", + self.desc, ae.0); + return MgmtError::AuthorisationError; + } +} + +#[throws(AuthorisationError)] +fn authorise_scope(cs: &CommandStream, wanted: &ManagementScope) + -> AuthorisedSatisfactory { + type AS = (T, ManagementScope); +// fn AS(a:T, s:ManagementScope) -> AuthorisedScope +// { AuthorisedScope(a,s) } + use fs::File; -fn authorize_scope(cs: &CommandStream, wanted: &ManagementScope) { - type AS = AuthorizedScope; - match &wanted { ManagementScope::XXX => { - let y : AS<( - Authorized<(Passwd,uid_t)>, - )> = { - let our_euid = unsafe { libc::getuid() }; - let ok = cs.authorized_uid(our_euid)?; - AS((ok,), - ManagementScope::XXX) + let y : AS< + Authorised<(Passwd,uid_t)>, + > = { + let ok = cs.authorised_uid(None)?; + (ok, + ManagementScope::XXX) }; - y.into() + return y.into() }, - Unix(user) => { + ManagementScope::Unix { user: wanted } => { let y : AS<( - Authorized<(Passwd,uid_t)>, // caller_has - Authorized, // in_userlist: + Authorised<(Passwd,uid_t)>, // caller_has + Authorised, // in_userlist: )> = { - let pwent = Passwd::from_name(user)?; - let caller_has = cs.authorized_uid(pwent.uid)?; - let found = (||{ - let allowed = File::open(USERLIST)?; - let found = allowed.lines()?.map(|l| l.trim() == user).any(); - Ok(found) - })?; - let in_userlist = Authorized::from_bool(USERLIST)?; - AS((caller_has, in_userlist), - ManagementScope::Unix(pwent.username)) + let pwent = Passwd::from_name(&wanted) + .map_err( + |e| anyhow!("looking up requested username {:?}: {:?}", + &wanted, &e) + )? + .ok_or_else( + || AuthorisationError(format!( + "requested username {:?} not found", &wanted + )) + )?; + let caller_has = cs.authorised_uid(Some(pwent.uid))?; + let in_userlist = (||{ >::Ok({ + let allowed = BufReader::new(File::open(USERLIST)?); + allowed + .lines() + .filter_map(|le| match le { + Ok(l) if l.trim() == wanted => Some(Ok(Authorised::authorise())), + Ok(_) => None, + Err(e) => Some(>::Err(e.into())), + }) + .next() + .unwrap_or_else( + || Err(anyhow!("requested username {:?} not in {:?}", + &wanted, USERLIST)) + )? + })})()?; + ((caller_has, + in_userlist), + ManagementScope::Unix { user: pwent.name }) }; y.into() } - }; + } } #[throws(ME)] fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { - use MgmtError::*; +// use MgmtError::*; match cmd { Noop { } => Fine { }, - Scope(wanted_scope) => { - let (_, authorized) : (AuthorizedConclusion, ManagementScope) = - authorize_scope(cs, &wanted_scope)?; - cs.scope = authorized; + SetScope(wanted_scope) => { + let authorised : AuthorisedSatisfactory = + authorise_scope(cs, &wanted_scope) + .map_err(|e| cs.map_auth_err(e)) + ?; + cs.scope = Some(authorised.into_inner()); Fine { } } /* @@ -197,7 +274,10 @@ impl CommandListener { let write = conn; let write = BufWriter::new(write); - let cs = CommandStream { read, write, euid }; + let cs = CommandStream { + scope: None, desc: &desc, + read, write, euid, + }; cs.mainloop()?; >::Ok(()) diff --git a/src/commands.rs b/src/commands.rs index 73c8102a..f9b72f7e 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -4,6 +4,7 @@ use crate::imports::*; #[derive(Debug,Serialize,Deserialize)] pub enum MgmtCommand { Noop { }, + SetScope(ManagementScope), AddPiece(Box), } @@ -16,7 +17,7 @@ pub enum MgmtResponse { #[derive(Debug,Error)] pub enum MgmtError { ParseFailed(String), - SetScope(ManagementScope), + AuthorisationError, XXXU(&'static str), } display_as_debug!{MgmtError}