Nothing to see here, move along
at main 224 lines 8.2 kB view raw
1use sunset::{ChanData, ChanHandle, Event, PubKey, Runner, ServEvent, Server, SignKey}; 2 3const VALID_PASSWORD: &[u8] = b"lancer"; 4 5#[derive(Clone, Copy)] 6pub enum AuthMethod { 7 Password, 8 Pubkey, 9} 10 11const AUTHORIZED_KEYS: &[[u8; 32]] = &[[ 12 0x8b, 0xce, 0xb5, 0x87, 0x3e, 0x68, 0x86, 0xfd, 0xf1, 0x6c, 0x88, 0xac, 0x62, 0x8a, 0xd0, 0xa5, 13 0xf4, 0x38, 0x4a, 0xc4, 0xee, 0x1c, 0x2a, 0x30, 0x97, 0xd0, 0xf3, 0x5b, 0x38, 0x12, 0x53, 0x90, 14]]; 15 16fn is_key_authorized(presented: &[u8; 32]) -> bool { 17 AUTHORIZED_KEYS.iter().fold(false, |found, authorized| { 18 let eq = presented 19 .iter() 20 .zip(authorized.iter()) 21 .fold(0u8, |acc, (a, b)| acc | (a ^ b)) 22 == 0; 23 found | eq 24 }) 25} 26 27pub enum SshProgress { 28 Continue, 29 ShellReady, 30 Idle, 31 Disconnected, 32} 33 34pub struct SshSession<'a> { 35 runner: Runner<'a, Server>, 36 host_key: SignKey, 37 chan: Option<ChanHandle>, 38 pub shell_ready: bool, 39 pub auth_method: Option<AuthMethod>, 40 disconnected: bool, 41} 42 43impl<'a> SshSession<'a> { 44 pub fn new(inbuf: &'a mut [u8], outbuf: &'a mut [u8], host_key: SignKey) -> Self { 45 Self { 46 runner: Runner::new_server(inbuf, outbuf), 47 host_key, 48 chan: None, 49 shell_ready: false, 50 auth_method: None, 51 disconnected: false, 52 } 53 } 54 55 pub fn feed_input(&mut self, data: &[u8]) -> usize { 56 self.runner.input(data).unwrap_or(0) 57 } 58 59 pub fn progress(&mut self) -> SshProgress { 60 match self.disconnected { 61 true => SshProgress::Disconnected, 62 false => match self.runner.progress() { 63 Ok(Event::Serv(ev)) => match ev { 64 ServEvent::Hostkeys(h) => match h.hostkeys(&[&self.host_key]) { 65 Ok(()) => SshProgress::Continue, 66 Err(_) => { 67 self.disconnected = true; 68 SshProgress::Disconnected 69 } 70 }, 71 ServEvent::FirstAuth(a) => match a.reject() { 72 Ok(()) => SshProgress::Continue, 73 Err(_) => { 74 self.disconnected = true; 75 SshProgress::Disconnected 76 } 77 }, 78 ServEvent::PasswordAuth(a) => { 79 let accepted = a 80 .password() 81 .map(|pw| { 82 let pw = pw.as_bytes(); 83 pw.len() == VALID_PASSWORD.len() 84 && pw 85 .iter() 86 .zip(VALID_PASSWORD.iter()) 87 .fold(0u8, |acc, (a, b)| acc | (a ^ b)) 88 == 0 89 }) 90 .unwrap_or(false); 91 let result = match accepted { 92 true => { 93 self.auth_method = Some(AuthMethod::Password); 94 a.allow() 95 } 96 false => a.reject(), 97 }; 98 match result { 99 Ok(()) => SshProgress::Continue, 100 Err(_) => { 101 self.disconnected = true; 102 SshProgress::Disconnected 103 } 104 } 105 } 106 ServEvent::PubkeyAuth(a) => { 107 let authorized = a 108 .pubkey() 109 .ok() 110 .and_then(|pk| match pk { 111 PubKey::Ed25519(ref ed) => Some(ed.key.0), 112 _ => None, 113 }) 114 .map(|raw_key| is_key_authorized(&raw_key)) 115 .unwrap_or(false); 116 let result = match authorized { 117 true => { 118 if a.real() { 119 self.auth_method = Some(AuthMethod::Pubkey); 120 } 121 a.allow() 122 } 123 false => a.reject(), 124 }; 125 match result { 126 Ok(()) => SshProgress::Continue, 127 Err(_) => { 128 self.disconnected = true; 129 SshProgress::Disconnected 130 } 131 } 132 } 133 ServEvent::OpenSession(a) => { 134 match self.chan { 135 Some(_) => {} 136 None => { 137 if let Ok(ch) = a.accept() { 138 self.chan = Some(ch); 139 } 140 } 141 } 142 SshProgress::Continue 143 } 144 ServEvent::SessionPty(a) => match a.succeed() { 145 Ok(()) => SshProgress::Continue, 146 Err(_) => { 147 self.disconnected = true; 148 SshProgress::Disconnected 149 } 150 }, 151 ServEvent::SessionShell(a) => match a.succeed() { 152 Ok(()) => { 153 self.shell_ready = true; 154 SshProgress::ShellReady 155 } 156 Err(_) => { 157 self.disconnected = true; 158 SshProgress::Disconnected 159 } 160 }, 161 ServEvent::SessionExec(_) => SshProgress::Continue, 162 ServEvent::SessionSubsystem(_) => SshProgress::Continue, 163 ServEvent::SessionEnv(a) => match a.succeed() { 164 Ok(()) => SshProgress::Continue, 165 Err(_) => { 166 self.disconnected = true; 167 SshProgress::Disconnected 168 } 169 }, 170 ServEvent::Defunct => { 171 self.disconnected = true; 172 SshProgress::Disconnected 173 } 174 ServEvent::PollAgain => SshProgress::Continue, 175 }, 176 Ok(Event::Progressed) => SshProgress::Continue, 177 Ok(Event::None) => SshProgress::Idle, 178 Ok(_) => SshProgress::Idle, 179 Err(_) => { 180 self.disconnected = true; 181 SshProgress::Disconnected 182 } 183 }, 184 } 185 } 186 187 pub fn read_plaintext(&mut self, buf: &mut [u8]) -> usize { 188 match &self.chan { 189 Some(ch) => self 190 .runner 191 .read_channel(ch, ChanData::Normal, buf) 192 .unwrap_or(0), 193 None => 0, 194 } 195 } 196 197 pub fn write_plaintext(&mut self, data: &[u8]) -> usize { 198 match &self.chan { 199 Some(ch) => self 200 .runner 201 .write_channel(ch, ChanData::Normal, data) 202 .unwrap_or(0), 203 None => 0, 204 } 205 } 206 207 pub fn peek_output(&mut self, buf: &mut [u8]) -> usize { 208 let pending = self.runner.output_buf(); 209 let l = pending.len().min(buf.len()); 210 buf[..l].copy_from_slice(&pending[..l]); 211 l 212 } 213 214 pub fn consume_output(&mut self, l: usize) { 215 self.runner.consume_output(l); 216 } 217 218 pub fn close(&mut self) { 219 self.runner.close_input(); 220 self.runner.close_output(); 221 self.disconnected = true; 222 self.shell_ready = false; 223 } 224}