Nothing to see here, move along
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}