russh/
lib.rs

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)]
8// length checked
9// Copyright 2016 Pierre-Étienne Meunier
10//
11// Licensed under the Apache License, Version 2.0 (the "License");
12// you may not use this file except in compliance with the License.
13// You may obtain a copy of the License at
14//
15// http://www.apache.org/licenses/LICENSE-2.0
16//
17// Unless required by applicable law or agreed to in writing, software
18// distributed under the License is distributed on an "AS IS" BASIS,
19// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20// See the License for the specific language governing permissions and
21// limitations under the License.
22
23//! Server and client SSH asynchronous library, based on tokio/futures.
24//!
25//! The normal way to use this library, both for clients and for
26//! servers, is by creating *handlers*, i.e. types that implement
27//! `client::Handler` for clients and `server::Handler` for
28//! servers.
29//!
30//! * [Writing SSH clients - the `russh::client` module](client)
31//! * [Writing SSH servers - the `russh::server` module](server)
32//!
33//! # Using non-socket IO / writing tunnels
34//!
35//! The easy way to implement SSH tunnels, like `ProxyCommand` for
36//! OpenSSH, is to use the `russh-config` crate, and use the
37//! `Stream::tcp_connect` or `Stream::proxy_command` methods of that
38//! crate. That crate is a very lightweight layer above Russh, only
39//! implementing for external commands the traits used for sockets.
40//!
41//! # The SSH protocol
42//!
43//! If we exclude the key exchange and authentication phases, handled
44//! by Russh behind the scenes, the rest of the SSH protocol is
45//! relatively simple: clients and servers open *channels*, which are
46//! just integers used to handle multiple requests in parallel in a
47//! single connection. Once a client has obtained a `ChannelId` by
48//! calling one of the many `channel_open_…` methods of
49//! `client::Connection`, the client may send exec requests and data
50//! to the server.
51//!
52//! A simple client just asking the server to run one command will
53//! usually start by calling
54//! `client::Connection::channel_open_session`, then
55//! `client::Connection::exec`, then possibly
56//! `client::Connection::data` a number of times to send data to the
57//! command's standard input, and finally `Connection::channel_eof`
58//! and `Connection::channel_close`.
59//!
60//! # Design principles
61//!
62//! The main goal of this library is conciseness, and reduced size and
63//! readability of the library's code.
64//!
65//! One non-goal is to implement all possible cryptographic algorithms
66//! published since the initial release of SSH. Technical debt is
67//! easily acquired, and we would need a very strong reason to go
68//! against this principle. If you are designing a system from
69//! scratch, we urge you to consider recent cryptographic primitives
70//! such as Ed25519 for public key cryptography, and Chacha20-Poly1305
71//! for symmetric cryptography and MAC.
72//!
73//! # Internal details of the event loop
74//!
75//! It might seem a little odd that the read/write methods for server
76//! or client sessions often return neither `Result` nor
77//! `Future`. This is because the data sent to the remote side is
78//! buffered, because it needs to be encrypted first, and encryption
79//! works on buffers, and for many algorithms, not in place.
80//!
81//! Hence, the event loop keeps waiting for incoming packets, reacts
82//! to them by calling the provided `Handler`, which fills some
83//! buffers. If the buffers are non-empty, the event loop then sends
84//! them to the socket, flushes the socket, empties the buffers and
85//! starts again. In the special case of the server, unsollicited
86//! messages sent through a `server::Handle` are processed when there
87//! is no incoming packet to read.
88
89use 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;
106/// Cipher names
107pub mod cipher;
108/// Compression algorithm names
109pub mod compression;
110/// Key exchange algorithm names
111pub mod kex;
112/// MAC algorithm names
113pub 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)] // length checked
141        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/// Server side of this library.
153#[cfg(not(target_arch = "wasm32"))]
154pub mod server;
155
156/// Client side of this library.
157pub 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    /// The key file could not be parsed.
171    #[error("Could not read key")]
172    CouldNotReadKey,
173
174    /// Unspecified problem with the beginning of key exchange.
175    #[error("Key exchange init failed")]
176    KexInit,
177
178    /// Unknown algorithm name.
179    #[error("Unknown algorithm")]
180    UnknownAlgo,
181
182    /// No common algorithm found during key exchange.
183    #[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    /// Invalid SSH version string.
191    #[error("invalid SSH version string")]
192    Version,
193
194    /// Error during key exchange.
195    #[error("Key exchange failed")]
196    Kex,
197
198    /// Invalid packet authentication code.
199    #[error("Wrong packet authentication code")]
200    PacketAuth,
201
202    /// The protocol is in an inconsistent state.
203    #[error("Inconsistent state of the protocol")]
204    Inconsistent,
205
206    /// The client is not yet authenticated.
207    #[error("Not yet authenticated")]
208    NotAuthenticated,
209
210    /// Index out of bounds.
211    #[error("Index out of bounds")]
212    IndexOutOfBounds,
213
214    /// Unknown server key.
215    #[error("Unknown server key")]
216    UnknownKey,
217
218    /// The server provided a wrong signature.
219    #[error("Wrong server signature")]
220    WrongServerSig,
221
222    /// Excessive packet size.
223    #[error("Bad packet size: {0}")]
224    PacketSize(usize),
225
226    /// Message received/sent on unopened channel.
227    #[error("Channel not open")]
228    WrongChannel,
229
230    /// Server refused to open a channel.
231    #[error("Failed to open channel ({0:?})")]
232    ChannelOpenFailure(ChannelOpenFailure),
233
234    /// Disconnected
235    #[error("Disconnected")]
236    Disconnect,
237
238    /// No home directory found when trying to learn new host key.
239    #[error("No home directory when saving host key")]
240    NoHomeDir,
241
242    /// Remote key changed, this could mean a man-in-the-middle attack
243    /// is being performed on the connection.
244    #[error("Key changed, line {}", line)]
245    KeyChanged { line: usize },
246
247    /// Connection closed by the remote side.
248    #[error("Connection closed by the remote side")]
249    HUP,
250
251    /// Connection timeout.
252    #[error("Connection timeout")]
253    ConnectionTimeout,
254
255    /// Keepalive timeout.
256    #[error("Keepalive timeout")]
257    KeepaliveTimeout,
258
259    /// Inactivity timeout.
260    #[error("Inactivity timeout")]
261    InactivityTimeout,
262
263    /// Missing authentication method.
264    #[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/// The number of bytes read/written, and the number of seconds before a key
337/// re-exchange is requested.
338#[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    /// Create a new `Limits`, checking that the given bounds cannot lead to
347    /// nonce reuse.
348    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        // Following the recommendations of
361        // https://tools.ietf.org/html/rfc4253#section-9
362        Limits {
363            rekey_write_limit: 1 << 30, // 1 Gb
364            rekey_read_limit: 1 << 30,  // 1 Gb
365            rekey_time_limit: std::time::Duration::from_secs(3600),
366        }
367    }
368}
369
370pub use auth::{AgentAuthError, MethodKind, MethodSet, Signer};
371
372/// A reason for disconnection.
373#[allow(missing_docs)] // This should be relatively self-explanatory.
374#[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/// The type of signals that can be sent to a remote process. If you
421/// plan to use custom signals, read [the
422/// RFC](https://tools.ietf.org/html/rfc4254#section-6.10) to
423/// understand the encoding.
424#[allow(missing_docs)]
425// This should be relatively self-explanatory.
426#[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/// Reason for not being able to open a channel.
481#[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)]
504/// The identifier of a channel.
505pub 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/// The parameters of a channel.
538#[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    /// Has the other side confirmed the channel?
547    pub confirmed: bool,
548    #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
549    wants_reply: bool,
550    /// (buffer, extended stream #, data offset in buffer)
551    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; // "sender" is the sender of the confirmation
559        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
565/// Returns `f(val)` if `val` it is [Some], or a forever pending [Future] if it is [None].
566pub(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}