1use std::convert::TryFrom;
2use std::fmt::{Debug, Display, Formatter};
3use std::future::{Future, Pending};
4
5use futures::future::Either as EitherFuture;
6use log::{debug, warn};
7use parsing::ChannelOpenConfirmation;
8pub use russh_cryptovec::CryptoVec;
9use ssh_encoding::{Decode, Encode};
10use thiserror::Error;
11
12#[cfg(test)]
13mod tests;
14
15mod auth;
16
17mod cert;
18pub mod cipher;
20pub mod compression;
22pub mod kex;
24pub mod mac;
26
27pub mod keys;
28
29mod msg;
30mod negotiation;
31mod ssh_read;
32mod sshbuffer;
33
34pub use negotiation::{Names, Preferred};
35
36mod pty;
37
38pub use pty::Pty;
39pub use sshbuffer::SshId;
40
41mod helpers;
42
43pub(crate) use helpers::map_err;
44
45macro_rules! push_packet {
46 ( $buffer:expr, $x:expr ) => {{
47 use byteorder::{BigEndian, ByteOrder};
48 let i0 = $buffer.len();
49 $buffer.extend(b"\0\0\0\0");
50 let x = $x;
51 let i1 = $buffer.len();
52 use std::ops::DerefMut;
53 let buf = $buffer.deref_mut();
54 #[allow(clippy::indexing_slicing)] BigEndian::write_u32(&mut buf[i0..], (i1 - i0 - 4) as u32);
56 x
57 }};
58}
59
60mod channels;
61pub use channels::{Channel, ChannelMsg, ChannelReadHalf, ChannelStream, ChannelWriteHalf};
62
63mod parsing;
64mod session;
65
66#[cfg(not(target_arch = "wasm32"))]
68pub mod server;
69
70pub mod client;
72
73#[derive(Debug)]
74pub enum AlgorithmKind {
75 Kex,
76 Key,
77 Cipher,
78 Compression,
79 Mac,
80}
81
82#[derive(Debug, Error)]
83pub enum Error {
84 #[error("Could not read key")]
86 CouldNotReadKey,
87
88 #[error("Key exchange init failed")]
90 KexInit,
91
92 #[error("Unknown algorithm")]
94 UnknownAlgo,
95
96 #[error("No common {kind:?} algorithm - ours: {ours:?}, theirs: {theirs:?}")]
98 NoCommonAlgo {
99 kind: AlgorithmKind,
100 ours: Vec<String>,
101 theirs: Vec<String>,
102 },
103
104 #[error("invalid SSH version string")]
106 Version,
107
108 #[error("Key exchange failed")]
110 Kex,
111
112 #[error("Wrong packet authentication code")]
114 PacketAuth,
115
116 #[error("Inconsistent state of the protocol")]
118 Inconsistent,
119
120 #[error("Not yet authenticated")]
122 NotAuthenticated,
123
124 #[error("Unsupported authentication method")]
126 UnsupportedAuthMethod,
127
128 #[error("Index out of bounds")]
130 IndexOutOfBounds,
131
132 #[error("Unknown server key")]
134 UnknownKey,
135
136 #[error("Wrong server signature")]
138 WrongServerSig,
139
140 #[error("Bad packet size: {0}")]
142 PacketSize(usize),
143
144 #[error("Channel not open")]
146 WrongChannel,
147
148 #[error("Failed to open channel ({0:?})")]
150 ChannelOpenFailure(ChannelOpenFailure),
151
152 #[error("Disconnected")]
154 Disconnect,
155
156 #[error("No home directory when saving host key")]
158 NoHomeDir,
159
160 #[error("Key changed, line {}", line)]
163 KeyChanged { line: usize },
164
165 #[error("Connection closed by the remote side")]
167 HUP,
168
169 #[error("Connection timeout")]
171 ConnectionTimeout,
172
173 #[error("Keepalive timeout")]
175 KeepaliveTimeout,
176
177 #[error("Inactivity timeout")]
179 InactivityTimeout,
180
181 #[error("No authentication method")]
183 NoAuthMethod,
184
185 #[error("Channel send error")]
186 SendError,
187
188 #[error("Pending buffer limit reached")]
189 Pending,
190
191 #[error("Failed to decrypt a packet")]
192 DecryptionError,
193
194 #[error("The request was rejected by the other party")]
195 RequestDenied,
196
197 #[error(transparent)]
198 Keys(#[from] crate::keys::Error),
199
200 #[error(transparent)]
201 IO(#[from] std::io::Error),
202
203 #[error(transparent)]
204 Utf8(#[from] std::str::Utf8Error),
205
206 #[error(transparent)]
207 #[cfg(feature = "flate2")]
208 Compress(#[from] flate2::CompressError),
209
210 #[error(transparent)]
211 #[cfg(feature = "flate2")]
212 Decompress(#[from] flate2::DecompressError),
213
214 #[error(transparent)]
215 Join(#[from] russh_util::runtime::JoinError),
216
217 #[error(transparent)]
218 Elapsed(#[from] tokio::time::error::Elapsed),
219
220 #[error("Violation detected during strict key exchange, message {message_type} at seq no {sequence_number}")]
221 StrictKeyExchangeViolation {
222 message_type: u8,
223 sequence_number: usize,
224 },
225
226 #[error("Signature: {0}")]
227 Signature(#[from] signature::Error),
228
229 #[error("SshKey: {0}")]
230 SshKey(#[from] ssh_key::Error),
231
232 #[error("SshEncoding: {0}")]
233 SshEncoding(#[from] ssh_encoding::Error),
234
235 #[error("Invalid config: {0}")]
236 InvalidConfig(String),
237
238 #[error("Unable to receive more messages from the channel")]
241 RecvError,
242}
243
244pub(crate) fn strict_kex_violation(message_type: u8, sequence_number: usize) -> crate::Error {
245 warn!(
246 "strict kex violated at sequence no. {sequence_number:?}, message type: {message_type:?}"
247 );
248 crate::Error::StrictKeyExchangeViolation {
249 message_type,
250 sequence_number,
251 }
252}
253
254#[derive(Debug, Error)]
255#[error("Could not reach the event loop")]
256pub struct SendError {}
257
258#[derive(Debug, Clone)]
261pub struct Limits {
262 pub rekey_write_limit: usize,
263 pub rekey_read_limit: usize,
264 pub rekey_time_limit: std::time::Duration,
265}
266
267impl Limits {
268 pub fn new(write_limit: usize, read_limit: usize, time_limit: std::time::Duration) -> Limits {
271 assert!(write_limit <= 1 << 30 && read_limit <= 1 << 30);
272 Limits {
273 rekey_write_limit: write_limit,
274 rekey_read_limit: read_limit,
275 rekey_time_limit: time_limit,
276 }
277 }
278}
279
280impl Default for Limits {
281 fn default() -> Self {
282 Limits {
285 rekey_write_limit: 1 << 30, rekey_read_limit: 1 << 30, rekey_time_limit: std::time::Duration::from_secs(3600),
288 }
289 }
290}
291
292pub use auth::{AgentAuthError, MethodKind, MethodSet, Signer};
293
294#[allow(missing_docs)] #[allow(clippy::manual_non_exhaustive)]
297#[derive(Debug)]
298pub enum Disconnect {
299 HostNotAllowedToConnect = 1,
300 ProtocolError = 2,
301 KeyExchangeFailed = 3,
302 #[doc(hidden)]
303 Reserved = 4,
304 MACError = 5,
305 CompressionError = 6,
306 ServiceNotAvailable = 7,
307 ProtocolVersionNotSupported = 8,
308 HostKeyNotVerifiable = 9,
309 ConnectionLost = 10,
310 ByApplication = 11,
311 TooManyConnections = 12,
312 AuthCancelledByUser = 13,
313 NoMoreAuthMethodsAvailable = 14,
314 IllegalUserName = 15,
315}
316
317impl TryFrom<u32> for Disconnect {
318 type Error = crate::Error;
319
320 fn try_from(value: u32) -> Result<Self, Self::Error> {
321 Ok(match value {
322 1 => Self::HostNotAllowedToConnect,
323 2 => Self::ProtocolError,
324 3 => Self::KeyExchangeFailed,
325 4 => Self::Reserved,
326 5 => Self::MACError,
327 6 => Self::CompressionError,
328 7 => Self::ServiceNotAvailable,
329 8 => Self::ProtocolVersionNotSupported,
330 9 => Self::HostKeyNotVerifiable,
331 10 => Self::ConnectionLost,
332 11 => Self::ByApplication,
333 12 => Self::TooManyConnections,
334 13 => Self::AuthCancelledByUser,
335 14 => Self::NoMoreAuthMethodsAvailable,
336 15 => Self::IllegalUserName,
337 _ => return Err(crate::Error::Inconsistent),
338 })
339 }
340}
341
342#[allow(missing_docs)]
347#[derive(Debug, Clone)]
349pub enum Sig {
350 ABRT,
351 ALRM,
352 FPE,
353 HUP,
354 ILL,
355 INT,
356 KILL,
357 PIPE,
358 QUIT,
359 SEGV,
360 TERM,
361 USR1,
362 Custom(String),
363}
364
365impl Sig {
366 fn name(&self) -> &str {
367 match *self {
368 Sig::ABRT => "ABRT",
369 Sig::ALRM => "ALRM",
370 Sig::FPE => "FPE",
371 Sig::HUP => "HUP",
372 Sig::ILL => "ILL",
373 Sig::INT => "INT",
374 Sig::KILL => "KILL",
375 Sig::PIPE => "PIPE",
376 Sig::QUIT => "QUIT",
377 Sig::SEGV => "SEGV",
378 Sig::TERM => "TERM",
379 Sig::USR1 => "USR1",
380 Sig::Custom(ref c) => c,
381 }
382 }
383 fn from_name(name: &str) -> Sig {
384 match name {
385 "ABRT" => Sig::ABRT,
386 "ALRM" => Sig::ALRM,
387 "FPE" => Sig::FPE,
388 "HUP" => Sig::HUP,
389 "ILL" => Sig::ILL,
390 "INT" => Sig::INT,
391 "KILL" => Sig::KILL,
392 "PIPE" => Sig::PIPE,
393 "QUIT" => Sig::QUIT,
394 "SEGV" => Sig::SEGV,
395 "TERM" => Sig::TERM,
396 "USR1" => Sig::USR1,
397 x => Sig::Custom(x.to_string()),
398 }
399 }
400}
401
402#[derive(Debug, Copy, Clone, PartialEq, Eq)]
404#[allow(missing_docs)]
405pub enum ChannelOpenFailure {
406 AdministrativelyProhibited = 1,
407 ConnectFailed = 2,
408 UnknownChannelType = 3,
409 ResourceShortage = 4,
410 Unknown = 0,
411}
412
413impl ChannelOpenFailure {
414 fn from_u32(x: u32) -> Option<ChannelOpenFailure> {
415 match x {
416 1 => Some(ChannelOpenFailure::AdministrativelyProhibited),
417 2 => Some(ChannelOpenFailure::ConnectFailed),
418 3 => Some(ChannelOpenFailure::UnknownChannelType),
419 4 => Some(ChannelOpenFailure::ResourceShortage),
420 _ => None,
421 }
422 }
423}
424
425#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
426pub struct ChannelId(u32);
428
429impl Decode for ChannelId {
430 type Error = ssh_encoding::Error;
431
432 fn decode(reader: &mut impl ssh_encoding::Reader) -> Result<Self, Self::Error> {
433 Ok(Self(u32::decode(reader)?))
434 }
435}
436
437impl Encode for ChannelId {
438 fn encoded_len(&self) -> Result<usize, ssh_encoding::Error> {
439 self.0.encoded_len()
440 }
441
442 fn encode(&self, writer: &mut impl ssh_encoding::Writer) -> Result<(), ssh_encoding::Error> {
443 self.0.encode(writer)
444 }
445}
446
447impl From<ChannelId> for u32 {
448 fn from(c: ChannelId) -> u32 {
449 c.0
450 }
451}
452
453impl Display for ChannelId {
454 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
455 write!(f, "{}", self.0)
456 }
457}
458
459#[derive(Debug)]
461pub(crate) struct ChannelParams {
462 recipient_channel: u32,
463 sender_channel: ChannelId,
464 recipient_window_size: u32,
465 sender_window_size: u32,
466 recipient_maximum_packet_size: u32,
467 sender_maximum_packet_size: u32,
468 pub confirmed: bool,
470 #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
471 wants_reply: bool,
472 pending_data: std::collections::VecDeque<(CryptoVec, Option<u32>, usize)>,
474 pending_eof: bool,
475 pending_close: bool,
476}
477
478impl ChannelParams {
479 pub fn confirm(&mut self, c: &ChannelOpenConfirmation) {
480 self.recipient_channel = c.sender_channel; self.recipient_window_size = c.initial_window_size;
482 self.recipient_maximum_packet_size = c.maximum_packet_size;
483 self.confirmed = true;
484 }
485}
486
487pub(crate) fn future_or_pending<R, F: Future<Output = R>, T>(
489 val: Option<T>,
490 f: impl FnOnce(T) -> F,
491) -> EitherFuture<Pending<R>, F> {
492 match val {
493 None => EitherFuture::Left(core::future::pending()),
494 Some(x) => EitherFuture::Right(f(x)),
495 }
496}