use sunset::{ChanData, ChanHandle, Event, PubKey, Runner, ServEvent, Server, SignKey}; const VALID_PASSWORD: &[u8] = b"lancer"; #[derive(Clone, Copy)] pub enum AuthMethod { Password, Pubkey, } const AUTHORIZED_KEYS: &[[u8; 32]] = &[[ 0x8b, 0xce, 0xb5, 0x87, 0x3e, 0x68, 0x86, 0xfd, 0xf1, 0x6c, 0x88, 0xac, 0x62, 0x8a, 0xd0, 0xa5, 0xf4, 0x38, 0x4a, 0xc4, 0xee, 0x1c, 0x2a, 0x30, 0x97, 0xd0, 0xf3, 0x5b, 0x38, 0x12, 0x53, 0x90, ]]; fn is_key_authorized(presented: &[u8; 32]) -> bool { AUTHORIZED_KEYS.iter().fold(false, |found, authorized| { let eq = presented .iter() .zip(authorized.iter()) .fold(0u8, |acc, (a, b)| acc | (a ^ b)) == 0; found | eq }) } pub enum SshProgress { Continue, ShellReady, Idle, Disconnected, } pub struct SshSession<'a> { runner: Runner<'a, Server>, host_key: SignKey, chan: Option, pub shell_ready: bool, pub auth_method: Option, disconnected: bool, } impl<'a> SshSession<'a> { pub fn new(inbuf: &'a mut [u8], outbuf: &'a mut [u8], host_key: SignKey) -> Self { Self { runner: Runner::new_server(inbuf, outbuf), host_key, chan: None, shell_ready: false, auth_method: None, disconnected: false, } } pub fn feed_input(&mut self, data: &[u8]) -> usize { self.runner.input(data).unwrap_or(0) } pub fn progress(&mut self) -> SshProgress { match self.disconnected { true => SshProgress::Disconnected, false => match self.runner.progress() { Ok(Event::Serv(ev)) => match ev { ServEvent::Hostkeys(h) => match h.hostkeys(&[&self.host_key]) { Ok(()) => SshProgress::Continue, Err(_) => { self.disconnected = true; SshProgress::Disconnected } }, ServEvent::FirstAuth(a) => match a.reject() { Ok(()) => SshProgress::Continue, Err(_) => { self.disconnected = true; SshProgress::Disconnected } }, ServEvent::PasswordAuth(a) => { let accepted = a .password() .map(|pw| { let pw = pw.as_bytes(); pw.len() == VALID_PASSWORD.len() && pw .iter() .zip(VALID_PASSWORD.iter()) .fold(0u8, |acc, (a, b)| acc | (a ^ b)) == 0 }) .unwrap_or(false); let result = match accepted { true => { self.auth_method = Some(AuthMethod::Password); a.allow() } false => a.reject(), }; match result { Ok(()) => SshProgress::Continue, Err(_) => { self.disconnected = true; SshProgress::Disconnected } } } ServEvent::PubkeyAuth(a) => { let authorized = a .pubkey() .ok() .and_then(|pk| match pk { PubKey::Ed25519(ref ed) => Some(ed.key.0), _ => None, }) .map(|raw_key| is_key_authorized(&raw_key)) .unwrap_or(false); let result = match authorized { true => { if a.real() { self.auth_method = Some(AuthMethod::Pubkey); } a.allow() } false => a.reject(), }; match result { Ok(()) => SshProgress::Continue, Err(_) => { self.disconnected = true; SshProgress::Disconnected } } } ServEvent::OpenSession(a) => { match self.chan { Some(_) => {} None => { if let Ok(ch) = a.accept() { self.chan = Some(ch); } } } SshProgress::Continue } ServEvent::SessionPty(a) => match a.succeed() { Ok(()) => SshProgress::Continue, Err(_) => { self.disconnected = true; SshProgress::Disconnected } }, ServEvent::SessionShell(a) => match a.succeed() { Ok(()) => { self.shell_ready = true; SshProgress::ShellReady } Err(_) => { self.disconnected = true; SshProgress::Disconnected } }, ServEvent::SessionExec(_) => SshProgress::Continue, ServEvent::SessionSubsystem(_) => SshProgress::Continue, ServEvent::SessionEnv(a) => match a.succeed() { Ok(()) => SshProgress::Continue, Err(_) => { self.disconnected = true; SshProgress::Disconnected } }, ServEvent::Defunct => { self.disconnected = true; SshProgress::Disconnected } ServEvent::PollAgain => SshProgress::Continue, }, Ok(Event::Progressed) => SshProgress::Continue, Ok(Event::None) => SshProgress::Idle, Ok(_) => SshProgress::Idle, Err(_) => { self.disconnected = true; SshProgress::Disconnected } }, } } pub fn read_plaintext(&mut self, buf: &mut [u8]) -> usize { match &self.chan { Some(ch) => self .runner .read_channel(ch, ChanData::Normal, buf) .unwrap_or(0), None => 0, } } pub fn write_plaintext(&mut self, data: &[u8]) -> usize { match &self.chan { Some(ch) => self .runner .write_channel(ch, ChanData::Normal, data) .unwrap_or(0), None => 0, } } pub fn peek_output(&mut self, buf: &mut [u8]) -> usize { let pending = self.runner.output_buf(); let l = pending.len().min(buf.len()); buf[..l].copy_from_slice(&pending[..l]); l } pub fn consume_output(&mut self, l: usize) { self.runner.consume_output(l); } pub fn close(&mut self) { self.runner.close_input(); self.runner.close_output(); self.disconnected = true; self.shell_ready = false; } }