1#![deny(
2 clippy::unwrap_used,
3 clippy::expect_used,
4 clippy::indexing_slicing,
5 clippy::panic
6)]
7#![allow(clippy::single_match, clippy::upper_case_acronyms)]
8use std::convert::TryFrom;
90use std::fmt::{Debug, Display, Formatter};
91use std::future::{Future, Pending};
92
93use futures::future::Either as EitherFuture;
94use log::{debug, warn};
95use parsing::ChannelOpenConfirmation;
96pub use russh_cryptovec::CryptoVec;
97use ssh_encoding::{Decode, Encode};
98use thiserror::Error;
99
100#[cfg(test)]
101mod tests;
102
103mod auth;
104
105mod cert;
106pub mod cipher;
108pub mod compression;
110pub mod kex;
112pub mod mac;
114
115pub mod keys;
116
117mod msg;
118mod negotiation;
119mod ssh_read;
120mod sshbuffer;
121
122pub use negotiation::Preferred;
123
124mod pty;
125
126pub use pty::Pty;
127pub use sshbuffer::SshId;
128
129mod helpers;
130
131macro_rules! push_packet {
132 ( $buffer:expr, $x:expr ) => {{
133 use byteorder::{BigEndian, ByteOrder};
134 let i0 = $buffer.len();
135 $buffer.extend(b"\0\0\0\0");
136 let x = $x;
137 let i1 = $buffer.len();
138 use std::ops::DerefMut;
139 let buf = $buffer.deref_mut();
140 #[allow(clippy::indexing_slicing)] BigEndian::write_u32(&mut buf[i0..], (i1 - i0 - 4) as u32);
142 x
143 }};
144}
145
146mod channels;
147pub use channels::{Channel, ChannelMsg, ChannelReadHalf, ChannelStream, ChannelWriteHalf};
148
149mod parsing;
150mod session;
151
152#[cfg(not(target_arch = "wasm32"))]
154pub mod server;
155
156pub mod client;
158
159#[derive(Debug)]
160pub enum AlgorithmKind {
161 Kex,
162 Key,
163 Cipher,
164 Compression,
165 Mac,
166}
167
168#[derive(Debug, Error)]
169pub enum Error {
170 #[error("Could not read key")]
172 CouldNotReadKey,
173
174 #[error("Key exchange init failed")]
176 KexInit,
177
178 #[error("Unknown algorithm")]
180 UnknownAlgo,
181
182 #[error("No common {kind:?} algorithm - ours: {ours:?}, theirs: {theirs:?}")]
184 NoCommonAlgo {
185 kind: AlgorithmKind,
186 ours: Vec<String>,
187 theirs: Vec<String>,
188 },
189
190 #[error("invalid SSH version string")]
192 Version,
193
194 #[error("Key exchange failed")]
196 Kex,
197
198 #[error("Wrong packet authentication code")]
200 PacketAuth,
201
202 #[error("Inconsistent state of the protocol")]
204 Inconsistent,
205
206 #[error("Not yet authenticated")]
208 NotAuthenticated,
209
210 #[error("Index out of bounds")]
212 IndexOutOfBounds,
213
214 #[error("Unknown server key")]
216 UnknownKey,
217
218 #[error("Wrong server signature")]
220 WrongServerSig,
221
222 #[error("Bad packet size: {0}")]
224 PacketSize(usize),
225
226 #[error("Channel not open")]
228 WrongChannel,
229
230 #[error("Failed to open channel ({0:?})")]
232 ChannelOpenFailure(ChannelOpenFailure),
233
234 #[error("Disconnected")]
236 Disconnect,
237
238 #[error("No home directory when saving host key")]
240 NoHomeDir,
241
242 #[error("Key changed, line {}", line)]
245 KeyChanged { line: usize },
246
247 #[error("Connection closed by the remote side")]
249 HUP,
250
251 #[error("Connection timeout")]
253 ConnectionTimeout,
254
255 #[error("Keepalive timeout")]
257 KeepaliveTimeout,
258
259 #[error("Inactivity timeout")]
261 InactivityTimeout,
262
263 #[error("No authentication method")]
265 NoAuthMethod,
266
267 #[error("Channel send error")]
268 SendError,
269
270 #[error("Pending buffer limit reached")]
271 Pending,
272
273 #[error("Failed to decrypt a packet")]
274 DecryptionError,
275
276 #[error("The request was rejected by the other party")]
277 RequestDenied,
278
279 #[error(transparent)]
280 Keys(#[from] crate::keys::Error),
281
282 #[error(transparent)]
283 IO(#[from] std::io::Error),
284
285 #[error(transparent)]
286 Utf8(#[from] std::str::Utf8Error),
287
288 #[error(transparent)]
289 #[cfg(feature = "flate2")]
290 Compress(#[from] flate2::CompressError),
291
292 #[error(transparent)]
293 #[cfg(feature = "flate2")]
294 Decompress(#[from] flate2::DecompressError),
295
296 #[error(transparent)]
297 Join(#[from] russh_util::runtime::JoinError),
298
299 #[error(transparent)]
300 Elapsed(#[from] tokio::time::error::Elapsed),
301
302 #[error("Violation detected during strict key exchange, message {message_type} at seq no {sequence_number}")]
303 StrictKeyExchangeViolation {
304 message_type: u8,
305 sequence_number: usize,
306 },
307
308 #[error("Signature: {0}")]
309 Signature(#[from] signature::Error),
310
311 #[error("SshKey: {0}")]
312 SshKey(#[from] ssh_key::Error),
313
314 #[error("SshEncoding: {0}")]
315 SshEncoding(#[from] ssh_encoding::Error),
316
317 #[error("Invalid config: {0}")]
318 InvalidConfig(String),
319}
320
321pub(crate) fn strict_kex_violation(message_type: u8, sequence_number: usize) -> crate::Error {
322 warn!(
323 "strict kex violated at sequence no. {:?}, message type: {:?}",
324 sequence_number, message_type
325 );
326 crate::Error::StrictKeyExchangeViolation {
327 message_type,
328 sequence_number,
329 }
330}
331
332#[derive(Debug, Error)]
333#[error("Could not reach the event loop")]
334pub struct SendError {}
335
336#[derive(Debug, Clone)]
339pub struct Limits {
340 pub rekey_write_limit: usize,
341 pub rekey_read_limit: usize,
342 pub rekey_time_limit: std::time::Duration,
343}
344
345impl Limits {
346 pub fn new(write_limit: usize, read_limit: usize, time_limit: std::time::Duration) -> Limits {
349 assert!(write_limit <= 1 << 30 && read_limit <= 1 << 30);
350 Limits {
351 rekey_write_limit: write_limit,
352 rekey_read_limit: read_limit,
353 rekey_time_limit: time_limit,
354 }
355 }
356}
357
358impl Default for Limits {
359 fn default() -> Self {
360 Limits {
363 rekey_write_limit: 1 << 30, rekey_read_limit: 1 << 30, rekey_time_limit: std::time::Duration::from_secs(3600),
366 }
367 }
368}
369
370pub use auth::{AgentAuthError, MethodKind, MethodSet, Signer};
371
372#[allow(missing_docs)] #[allow(clippy::manual_non_exhaustive)]
375#[derive(Debug)]
376pub enum Disconnect {
377 HostNotAllowedToConnect = 1,
378 ProtocolError = 2,
379 KeyExchangeFailed = 3,
380 #[doc(hidden)]
381 Reserved = 4,
382 MACError = 5,
383 CompressionError = 6,
384 ServiceNotAvailable = 7,
385 ProtocolVersionNotSupported = 8,
386 HostKeyNotVerifiable = 9,
387 ConnectionLost = 10,
388 ByApplication = 11,
389 TooManyConnections = 12,
390 AuthCancelledByUser = 13,
391 NoMoreAuthMethodsAvailable = 14,
392 IllegalUserName = 15,
393}
394
395impl TryFrom<u32> for Disconnect {
396 type Error = crate::Error;
397
398 fn try_from(value: u32) -> Result<Self, Self::Error> {
399 Ok(match value {
400 1 => Self::HostNotAllowedToConnect,
401 2 => Self::ProtocolError,
402 3 => Self::KeyExchangeFailed,
403 4 => Self::Reserved,
404 5 => Self::MACError,
405 6 => Self::CompressionError,
406 7 => Self::ServiceNotAvailable,
407 8 => Self::ProtocolVersionNotSupported,
408 9 => Self::HostKeyNotVerifiable,
409 10 => Self::ConnectionLost,
410 11 => Self::ByApplication,
411 12 => Self::TooManyConnections,
412 13 => Self::AuthCancelledByUser,
413 14 => Self::NoMoreAuthMethodsAvailable,
414 15 => Self::IllegalUserName,
415 _ => return Err(crate::Error::Inconsistent),
416 })
417 }
418}
419
420#[allow(missing_docs)]
425#[derive(Debug, Clone)]
427pub enum Sig {
428 ABRT,
429 ALRM,
430 FPE,
431 HUP,
432 ILL,
433 INT,
434 KILL,
435 PIPE,
436 QUIT,
437 SEGV,
438 TERM,
439 USR1,
440 Custom(String),
441}
442
443impl Sig {
444 fn name(&self) -> &str {
445 match *self {
446 Sig::ABRT => "ABRT",
447 Sig::ALRM => "ALRM",
448 Sig::FPE => "FPE",
449 Sig::HUP => "HUP",
450 Sig::ILL => "ILL",
451 Sig::INT => "INT",
452 Sig::KILL => "KILL",
453 Sig::PIPE => "PIPE",
454 Sig::QUIT => "QUIT",
455 Sig::SEGV => "SEGV",
456 Sig::TERM => "TERM",
457 Sig::USR1 => "USR1",
458 Sig::Custom(ref c) => c,
459 }
460 }
461 fn from_name(name: &str) -> Sig {
462 match name {
463 "ABRT" => Sig::ABRT,
464 "ALRM" => Sig::ALRM,
465 "FPE" => Sig::FPE,
466 "HUP" => Sig::HUP,
467 "ILL" => Sig::ILL,
468 "INT" => Sig::INT,
469 "KILL" => Sig::KILL,
470 "PIPE" => Sig::PIPE,
471 "QUIT" => Sig::QUIT,
472 "SEGV" => Sig::SEGV,
473 "TERM" => Sig::TERM,
474 "USR1" => Sig::USR1,
475 x => Sig::Custom(x.to_string()),
476 }
477 }
478}
479
480#[derive(Debug, Copy, Clone, PartialEq, Eq)]
482#[allow(missing_docs)]
483pub enum ChannelOpenFailure {
484 AdministrativelyProhibited = 1,
485 ConnectFailed = 2,
486 UnknownChannelType = 3,
487 ResourceShortage = 4,
488 Unknown = 0,
489}
490
491impl ChannelOpenFailure {
492 fn from_u32(x: u32) -> Option<ChannelOpenFailure> {
493 match x {
494 1 => Some(ChannelOpenFailure::AdministrativelyProhibited),
495 2 => Some(ChannelOpenFailure::ConnectFailed),
496 3 => Some(ChannelOpenFailure::UnknownChannelType),
497 4 => Some(ChannelOpenFailure::ResourceShortage),
498 _ => None,
499 }
500 }
501}
502
503#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
504pub struct ChannelId(u32);
506
507impl Decode for ChannelId {
508 type Error = ssh_encoding::Error;
509
510 fn decode(reader: &mut impl ssh_encoding::Reader) -> Result<Self, Self::Error> {
511 Ok(Self(u32::decode(reader)?))
512 }
513}
514
515impl Encode for ChannelId {
516 fn encoded_len(&self) -> Result<usize, ssh_encoding::Error> {
517 self.0.encoded_len()
518 }
519
520 fn encode(&self, writer: &mut impl ssh_encoding::Writer) -> Result<(), ssh_encoding::Error> {
521 self.0.encode(writer)
522 }
523}
524
525impl From<ChannelId> for u32 {
526 fn from(c: ChannelId) -> u32 {
527 c.0
528 }
529}
530
531impl Display for ChannelId {
532 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
533 write!(f, "{}", self.0)
534 }
535}
536
537#[derive(Debug)]
539pub(crate) struct ChannelParams {
540 recipient_channel: u32,
541 sender_channel: ChannelId,
542 recipient_window_size: u32,
543 sender_window_size: u32,
544 recipient_maximum_packet_size: u32,
545 sender_maximum_packet_size: u32,
546 pub confirmed: bool,
548 #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
549 wants_reply: bool,
550 pending_data: std::collections::VecDeque<(CryptoVec, Option<u32>, usize)>,
552 pending_eof: bool,
553 pending_close: bool,
554}
555
556impl ChannelParams {
557 pub fn confirm(&mut self, c: &ChannelOpenConfirmation) {
558 self.recipient_channel = c.sender_channel; self.recipient_window_size = c.initial_window_size;
560 self.recipient_maximum_packet_size = c.maximum_packet_size;
561 self.confirmed = true;
562 }
563}
564
565pub(crate) fn future_or_pending<R, F: Future<Output = R>, T>(
567 val: Option<T>,
568 f: impl FnOnce(T) -> F,
569) -> EitherFuture<Pending<R>, F> {
570 match val {
571 None => EitherFuture::Left(core::future::pending()),
572 Some(x) => EitherFuture::Right(f(x)),
573 }
574}