sunset/
packets.rs

1//! SSH protocol packets.
2//!
3//! A [`Packet`] can be encoded/decoded to the
4//! SSH Binary Packet Protocol using [`sshwire`].
5//! SSH packet format is described in [RFC4253](https://tools.ietf.org/html/rfc4253#section-6) SSH Transport
6
7#[allow(unused_imports)]
8use {
9    crate::error::{Error, Result, TrapBug},
10    log::{debug, error, info, log, trace, warn},
11};
12
13use core::fmt;
14use core::fmt::{Debug, Display};
15
16#[cfg(feature = "arbitrary")]
17use arbitrary::Arbitrary;
18use pretty_hex::PrettyHex;
19
20use sunset_sshwire_derive::*;
21
22use crate::*;
23use namelist::NameList;
24use sign::{OwnedSig, SigType};
25use sshnames::*;
26use sshwire::SSHEncodeEnum;
27use sshwire::{BinString, Blob, TextString};
28use sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireError, WireResult};
29
30#[cfg(feature = "rsa")]
31use rsa::traits::PublicKeyParts;
32
33// Any `enum` needs to have special handling to select a variant when deserializing.
34// This is mostly done with `#[sshwire(...)]` attributes.
35
36#[derive(Debug, SSHEncode, SSHDecode)]
37#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
38pub struct KexInit<'a> {
39    pub cookie: KexCookie,
40    pub kex: NameList<'a>,
41    /// A list of signature algorithms
42    ///
43    /// RFC4253 refers to this as the host key algorithms, but actually they
44    /// are signature algorithms.
45    pub hostsig: NameList<'a>,
46    pub cipher_c2s: NameList<'a>,
47    pub cipher_s2c: NameList<'a>,
48    pub mac_c2s: NameList<'a>,
49    pub mac_s2c: NameList<'a>,
50    pub comp_c2s: NameList<'a>,
51    pub comp_s2c: NameList<'a>,
52    pub lang_c2s: NameList<'a>,
53    pub lang_s2c: NameList<'a>,
54    pub first_follows: bool,
55    pub reserved: u32,
56}
57
58/// Cookie for a [`KexInit`]
59#[derive(Debug, SSHEncode, SSHDecode, Clone)]
60#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
61pub struct KexCookie(pub [u8; 16]);
62
63#[derive(Debug, SSHEncode, SSHDecode)]
64#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
65pub struct NewKeys {}
66
67#[derive(Debug, SSHEncode, SSHDecode)]
68#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
69pub struct Ignore<'a> {
70    pub data: BinString<'a>,
71}
72
73/// Named to avoid clashing with [`fmt::Debug`]
74#[derive(Debug, SSHEncode, SSHDecode)]
75#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
76pub struct DebugPacket<'a> {
77    pub always_display: bool,
78    pub message: TextString<'a>,
79    pub lang: &'a str,
80}
81
82#[derive(Debug, SSHEncode, SSHDecode)]
83#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
84pub struct Disconnect<'a> {
85    pub reason: u32,
86    pub desc: TextString<'a>,
87    pub lang: TextString<'a>,
88}
89
90#[derive(Debug, SSHEncode, SSHDecode)]
91#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
92pub struct Unimplemented {
93    pub seq: u32,
94}
95
96#[derive(Debug, SSHEncode, SSHDecode)]
97#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
98pub struct KexDHInit<'a> {
99    pub q_c: BinString<'a>,
100}
101
102#[derive(Debug, SSHEncode, SSHDecode)]
103#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
104pub struct KexDHReply<'a> {
105    pub k_s: Blob<PubKey<'a>>,
106    pub q_s: BinString<'a>,
107    pub sig: Blob<Signature<'a>>,
108}
109
110#[derive(Debug, SSHEncode, SSHDecode)]
111#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
112pub struct ServiceRequest<'a> {
113    pub name: &'a str,
114}
115
116#[derive(Debug, SSHEncode, SSHDecode)]
117#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
118pub struct ServiceAccept<'a> {
119    pub name: &'a str,
120}
121
122/// MSG_EXT_INFO
123///
124/// `ExtInfo` differs from most packet structs, it only tracks known extension types
125/// rather than an unknown-sized list.
126#[derive(Debug)]
127#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
128pub struct ExtInfo<'a> {
129    // Wire format is
130    // byte       SSH_MSG_EXT_INFO (value 7)
131    // uint32     nr-extensions
132    // repeat the following 2 fields "nr-extensions" times:
133    //   string   extension-name
134    //   string   extension-value (binary)
135    pub server_sig_algs: Option<NameList<'a>>,
136}
137
138impl<'de: 'a, 'a> SSHDecode<'de> for ExtInfo<'a> {
139    fn dec<S>(s: &mut S) -> WireResult<Self>
140    where
141        S: SSHSource<'de>,
142    {
143        let mut server_sig_algs = None;
144        let num = u32::dec(s)?;
145        for _ in 0..num {
146            let ext: &str = SSHDecode::dec(s)?;
147            match ext {
148                SSH_EXT_SERVER_SIG_ALGS => {
149                    server_sig_algs = Some(SSHDecode::dec(s)?);
150                }
151                _ => {
152                    // skip over
153                    let _: BinString = SSHDecode::dec(s)?;
154                }
155            }
156        }
157        Ok(Self { server_sig_algs })
158    }
159}
160
161impl SSHEncode for ExtInfo<'_> {
162    fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> {
163        if let Some(ref algs) = self.server_sig_algs {
164            1u32.enc(s)?;
165            SSH_EXT_SERVER_SIG_ALGS.enc(s)?;
166            algs.enc(s)?;
167        }
168        Ok(())
169    }
170}
171
172#[derive(Debug, SSHEncode, SSHDecode, Clone)]
173#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
174pub struct UserauthRequest<'a> {
175    pub username: TextString<'a>,
176    pub service: &'a str,
177    pub method: AuthMethod<'a>,
178}
179
180/// The method-specific part of a [`UserauthRequest`].
181#[derive(Debug, SSHEncode, SSHDecode, Clone)]
182#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
183#[sshwire(variant_prefix)]
184pub enum AuthMethod<'a> {
185    #[sshwire(variant = SSH_AUTHMETHOD_PASSWORD)]
186    Password(MethodPassword<'a>),
187    #[sshwire(variant = SSH_AUTHMETHOD_PUBLICKEY)]
188    PubKey(MethodPubKey<'a>),
189    #[sshwire(variant = SSH_NAME_NONE)]
190    None,
191    #[sshwire(unknown)]
192    Unknown(Unknown<'a>),
193}
194
195#[derive(Debug, SSHEncode)]
196#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
197#[sshwire(no_variant_names)]
198pub enum Userauth60<'a> {
199    PkOk(UserauthPkOk<'a>),
200    PwChangeReq(UserauthPwChangeReq<'a>),
201    // TODO keyboard interactive
202}
203
204impl<'de: 'a, 'a> SSHDecode<'de> for Userauth60<'a> {
205    fn dec<S>(s: &mut S) -> WireResult<Self>
206    where
207        S: SSHSource<'de>,
208    {
209        match s.ctx().cli_auth_type {
210            Some(auth::AuthType::Password) => {
211                Ok(Self::PwChangeReq(SSHDecode::dec(s)?))
212            }
213            Some(auth::AuthType::PubKey) => Ok(Self::PkOk(SSHDecode::dec(s)?)),
214            _ => {
215                trace!("Wrong packet state for userauth60");
216                Err(WireError::PacketWrong)
217            }
218        }
219    }
220}
221
222#[derive(Debug, SSHEncode, SSHDecode)]
223#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
224pub struct UserauthPkOk<'a> {
225    pub algo: &'a str,
226    pub key: Blob<PubKey<'a>>,
227}
228
229#[derive(Debug, SSHEncode, SSHDecode)]
230#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
231pub struct UserauthPwChangeReq<'a> {
232    pub prompt: TextString<'a>,
233    pub lang: TextString<'a>,
234}
235
236#[derive(SSHEncode, SSHDecode, Clone)]
237#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
238pub struct MethodPassword<'a> {
239    pub change: bool,
240    pub password: TextString<'a>,
241}
242
243// Don't print password
244impl Debug for MethodPassword<'_> {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        f.debug_struct("MethodPassword")
247            .field("change", &self.change)
248            .finish_non_exhaustive()
249    }
250}
251
252#[derive(Debug, Clone)]
253#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
254pub struct MethodPubKey<'a> {
255    /// A signature algorithm name (not key algorithm name).
256    pub sig_algo: &'a str,
257    pub pubkey: Blob<PubKey<'a>>,
258    pub sig: Option<Blob<Signature<'a>>>,
259    // Set when serializing to create a signature. Will set the "signature present"
260    // boolean to TRUE even without a signature (signature is appended later).
261    pub force_sig: bool,
262}
263
264impl<'a> MethodPubKey<'a> {
265    pub fn new(pubkey: PubKey<'a>, sig: Option<&'a OwnedSig>) -> Result<Self> {
266        let sig_algo = Signature::sig_name_for_pubkey(&pubkey).trap()?;
267        let sig = sig.map(|s| Blob((s).into()));
268        Ok(MethodPubKey { sig_algo, pubkey: Blob(pubkey), sig, force_sig: false })
269    }
270}
271
272impl SSHEncode for MethodPubKey<'_> {
273    fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> {
274        // byte      SSH_MSG_USERAUTH_REQUEST
275        // string    user name
276        // string    service name
277        // string    "publickey"
278        // boolean   TRUE
279        // string    public key algorithm name
280        // string    public key to be used for authentication
281        // string    signature
282
283        // Signature bool will be set when signing
284        let sig = self.sig.is_some() || self.force_sig;
285        sig.enc(s)?;
286        self.sig_algo.enc(s)?;
287        self.pubkey.enc(s)?;
288        self.sig.enc(s)?;
289        Ok(())
290    }
291}
292
293impl<'de: 'a, 'a> SSHDecode<'de> for MethodPubKey<'a> {
294    fn dec<S>(s: &mut S) -> WireResult<Self>
295    where
296        S: sshwire::SSHSource<'de>,
297    {
298        let sig = bool::dec(s)?;
299        let sig_algo = SSHDecode::dec(s)?;
300        let pubkey = SSHDecode::dec(s)?;
301        let sig = if sig { Some(SSHDecode::dec(s)?) } else { None };
302        Ok(Self { sig_algo, pubkey, sig, force_sig: false })
303    }
304}
305
306#[derive(Debug, SSHEncode, SSHDecode)]
307#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
308pub struct UserauthFailure<'a> {
309    pub methods: NameList<'a>,
310    pub partial: bool,
311}
312
313#[derive(Debug, SSHEncode, SSHDecode)]
314#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
315pub struct UserauthSuccess {}
316
317#[derive(Debug, SSHEncode, SSHDecode)]
318#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
319pub struct UserauthBanner<'a> {
320    pub message: TextString<'a>,
321    pub lang: TextString<'a>,
322}
323
324#[derive(SSHEncode, SSHDecode, Debug, Clone, PartialEq)]
325#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
326#[sshwire(variant_prefix)]
327pub enum PubKey<'a> {
328    #[sshwire(variant = SSH_NAME_ED25519)]
329    Ed25519(Ed25519PubKey),
330
331    #[cfg(feature = "rsa")]
332    #[sshwire(variant = SSH_NAME_RSA)]
333    RSA(RSAPubKey),
334
335    #[sshwire(unknown)]
336    Unknown(Unknown<'a>),
337}
338
339impl PubKey<'_> {
340    /// The algorithm name presented. May be invalid.
341    pub fn algorithm_name(&self) -> Result<&str, &Unknown<'_>> {
342        match self {
343            PubKey::Ed25519(_) => Ok(SSH_NAME_ED25519),
344            #[cfg(feature = "rsa")]
345            PubKey::RSA(_) => Ok(SSH_NAME_RSA),
346            PubKey::Unknown(u) => Err(u),
347        }
348    }
349
350    #[cfg(feature = "openssh-key")]
351    pub fn matches_openssh(&self, k: &str) -> Result<bool> {
352        let k = ssh_key::PublicKey::from_openssh(k)
353            .map_err(|_| Error::msg("Unsupported OpenSSH key"))?;
354
355        let m = match (k.key_data(), self) {
356            (ssh_key::public::KeyData::Ed25519(kssh), PubKey::Ed25519(kself)) => {
357                kssh.0 == kself.key.0
358            }
359            _ => false,
360        };
361        Ok(m)
362    }
363
364    #[cfg(feature = "openssh-key")]
365    pub fn fingerprint(
366        &self,
367        hash_alg: ssh_key::HashAlg,
368    ) -> Result<ssh_key::Fingerprint> {
369        let ssh_key: ssh_key::PublicKey = self.try_into()?;
370
371        Ok(ssh_key.fingerprint(hash_alg))
372    }
373}
374
375// ssh_key::PublicKey is used for known_hosts comparisons
376#[cfg(feature = "openssh-key")]
377impl TryFrom<&PubKey<'_>> for ssh_key::PublicKey {
378    type Error = Error;
379    fn try_from(k: &PubKey) -> Result<Self> {
380        match k {
381            PubKey::Ed25519(e) => {
382                Ok(ssh_key::public::Ed25519PublicKey(e.key.0).into())
383            }
384
385            #[cfg(feature = "rsa")]
386            PubKey::RSA(r) => {
387                let k = ssh_key::public::RsaPublicKey {
388                    n: r.key.n().try_into().map_err(|_| Error::BadKey)?,
389                    e: r.key.e().try_into().map_err(|_| Error::BadKey)?,
390                };
391                Ok(k.into())
392            }
393
394            PubKey::Unknown(u) => {
395                trace!("unsupported {u}");
396                Err(Error::msg("Unsupported OpenSSH key"))
397            }
398        }
399    }
400}
401
402#[derive(Debug, Clone, PartialEq, SSHEncode, SSHDecode)]
403#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
404pub struct Ed25519PubKey {
405    pub key: Blob<[u8; 32]>,
406}
407
408#[cfg(feature = "rsa")]
409#[derive(Clone, PartialEq)]
410pub struct RSAPubKey {
411    // mpint     e
412    // mpint     n
413    pub key: rsa::RsaPublicKey,
414}
415
416#[cfg(feature = "rsa")]
417impl SSHEncode for RSAPubKey {
418    fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> {
419        self.key.e().enc(s)?;
420        self.key.n().enc(s)?;
421        Ok(())
422    }
423}
424
425#[cfg(feature = "rsa")]
426impl<'de> SSHDecode<'de> for RSAPubKey {
427    fn dec<S>(s: &mut S) -> WireResult<Self>
428    where
429        S: SSHSource<'de>,
430    {
431        let e = SSHDecode::dec(s)?;
432        let n = SSHDecode::dec(s)?;
433        let key = rsa::RsaPublicKey::new(n, e).map_err(|e| {
434            debug!("Invalid RSA public key: {e}");
435            WireError::BadKeyFormat
436        })?;
437        Ok(Self { key })
438    }
439}
440
441#[cfg(feature = "rsa")]
442impl Debug for RSAPubKey {
443    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444        f.debug_struct("RSAPubKey")
445            .field(".key bits", &(self.key.n().bits()))
446            .finish_non_exhaustive()
447    }
448}
449
450#[derive(Debug, SSHEncode, SSHDecode, Clone)]
451#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
452#[sshwire(variant_prefix)]
453pub enum Signature<'a> {
454    #[sshwire(variant = SSH_NAME_ED25519)]
455    Ed25519(Ed25519Sig<'a>),
456
457    #[cfg(feature = "rsa")]
458    #[sshwire(variant = SSH_NAME_RSA_SHA256)]
459    RSA(RSASig<'a>),
460
461    #[sshwire(unknown)]
462    Unknown(Unknown<'a>),
463}
464
465impl<'a> Signature<'a> {
466    /// The algorithm name presented. May be invalid.
467    pub fn algorithm_name(&self) -> Result<&'a str, &Unknown<'a>> {
468        match self {
469            Signature::Ed25519(_) => Ok(SSH_NAME_ED25519),
470            #[cfg(feature = "rsa")]
471            Signature::RSA(_) => Ok(SSH_NAME_RSA_SHA256),
472            Signature::Unknown(u) => Err(u),
473        }
474    }
475
476    /// Returns the signature algorithm name for a public key.
477    /// Returns (`Error::UnknownMethod`) if the PubKey is unknown
478    /// Currently can return a unique signature name for a public key
479    /// since ssh-rsa isn't supported, only rsa-sha2-256.
480    /// It's possible that in future there isn't a distinct signature
481    /// type for each key type.
482    pub fn sig_name_for_pubkey(pubkey: &PubKey) -> Result<&'static str> {
483        match pubkey {
484            PubKey::Ed25519(_) => Ok(SSH_NAME_ED25519),
485            #[cfg(feature = "rsa")]
486            PubKey::RSA(_) => Ok(SSH_NAME_RSA_SHA256),
487            PubKey::Unknown(u) => {
488                warn!("Unknown key type \"{}\"", u);
489                Err(Error::UnknownMethod { kind: "key" })
490            }
491        }
492    }
493
494    pub fn sig_type(&self) -> Result<SigType> {
495        match self {
496            Signature::Ed25519(_) => Ok(SigType::Ed25519),
497            #[cfg(feature = "rsa")]
498            Signature::RSA(_) => Ok(SigType::RSA),
499            Signature::Unknown(u) => {
500                warn!("Unknown signature type \"{}\"", u);
501                Err(Error::UnknownMethod { kind: "signature" })
502            }
503        }
504    }
505}
506
507impl<'a> From<&'a OwnedSig> for Signature<'a> {
508    fn from(s: &'a OwnedSig) -> Self {
509        match s {
510            OwnedSig::Ed25519(s) => {
511                Signature::Ed25519(Ed25519Sig { sig: BinString(s) })
512            }
513            #[cfg(feature = "rsa")]
514            OwnedSig::RSA(s) => {
515                Signature::RSA(RSASig { sig: BinString(s.as_ref()) })
516            }
517        }
518    }
519}
520
521#[derive(Debug, SSHEncode, SSHDecode, Clone)]
522#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
523pub struct Ed25519Sig<'a> {
524    pub sig: BinString<'a>,
525}
526
527#[cfg(feature = "rsa")]
528#[derive(Debug, SSHEncode, SSHDecode, Clone)]
529#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
530pub struct RSASig<'a> {
531    pub sig: BinString<'a>,
532}
533
534#[derive(Debug, SSHEncode, SSHDecode)]
535#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
536pub struct GlobalRequest<'a> {
537    #[sshwire(variant_name = req)]
538    pub want_reply: bool,
539    pub req: GlobalRequestMethod<'a>,
540}
541
542#[derive(Debug, SSHEncode, SSHDecode)]
543#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
544pub enum GlobalRequestMethod<'a> {
545    // #[sshwire(variant = "tcpip-forward")]
546    // TcpipForward(TcipForward<'a>),
547    // #[sshwire(variant = "cancel-tcpip-forward")]
548    // CancelTcpipForward(CancelTcpipForward<'a>),
549    #[sshwire(unknown)]
550    Unknown(Unknown<'a>),
551}
552
553// pub struct TcpipForward<'a> {
554//     pub address: TextString<'a>,
555//     pub port: u32,
556// }
557
558// pub struct CancelTcpipForward<'a> {
559//     pub address: TextString<'a>,
560//     pub port: u32,
561// }
562
563#[derive(Debug, SSHEncode)]
564#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
565#[sshwire(no_variant_names)]
566pub enum RequestSuccess {
567    SuccessEmpty,
568    // TcpPort(TcpPort),
569}
570
571impl<'de> SSHDecode<'de> for RequestSuccess {
572    fn dec<S>(_s: &mut S) -> WireResult<Self>
573    where
574        S: SSHSource<'de>,
575    {
576        // if s.ctx().last_req_port {
577        //     Ok(Self::TcpPort(TcpPort::dec(s)?))
578        // } else {
579        //     Ok(Self::SuccessEmpty)
580        // }
581        Ok(Self::SuccessEmpty)
582    }
583}
584
585// #[derive(Debug, SSHEncode, SSHDecode)]
586// pub struct TcpPort {
587//     pub port: u32,
588// }
589
590#[derive(Debug, SSHEncode, SSHDecode)]
591#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
592pub struct RequestFailure {}
593
594#[derive(Debug, SSHEncode, SSHDecode)]
595#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
596pub struct ChannelOpen<'a> {
597    // channel_type is implicit in ty below
598    #[sshwire(variant_name = ty)]
599    pub sender_num: u32,
600    pub initial_window: u32,
601    pub max_packet: u32,
602    pub ty: ChannelOpenType<'a>,
603}
604
605#[derive(Debug, SSHEncode, SSHDecode)]
606#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
607pub enum ChannelOpenType<'a> {
608    #[sshwire(variant = "session")]
609    Session,
610    #[sshwire(variant = "forwarded-tcpip")]
611    ForwardedTcpip(ForwardedTcpip<'a>),
612    #[sshwire(variant = "direct-tcpip")]
613    DirectTcpip(DirectTcpip<'a>),
614    // #[sshwire(variant = "x11")]
615    // Session(X11<'a>),
616    // #[sshwire(variant = "auth-agent@openssh.com")]
617    // Session(Agent<'a>),
618    #[sshwire(unknown)]
619    Unknown(Unknown<'a>),
620}
621
622#[derive(Debug, SSHEncode, SSHDecode)]
623#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
624pub struct ChannelOpenConfirmation {
625    pub num: u32,
626    pub sender_num: u32,
627    pub initial_window: u32,
628    pub max_packet: u32,
629}
630
631#[derive(Debug, SSHEncode, SSHDecode)]
632#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
633pub struct ChannelOpenFailure<'a> {
634    pub num: u32,
635    pub reason: u32,
636    pub desc: TextString<'a>,
637    pub lang: &'a str,
638}
639
640#[derive(Debug, SSHEncode, SSHDecode)]
641#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
642pub struct ChannelWindowAdjust {
643    pub num: u32,
644    pub adjust: u32,
645}
646
647#[derive(Debug, SSHEncode, SSHDecode)]
648#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
649pub struct ChannelData<'a> {
650    pub num: u32,
651    pub data: BinString<'a>,
652}
653
654impl ChannelData<'_> {
655    // offset into a packet payload, includes packet type byte
656    pub const DATA_OFFSET: usize = 9;
657}
658
659#[derive(Debug, SSHEncode, SSHDecode)]
660#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
661pub struct ChannelDataExt<'a> {
662    pub num: u32,
663    pub code: u32,
664    pub data: BinString<'a>,
665}
666
667impl ChannelDataExt<'_> {
668    // offset into a packet payload, includes packet type byte
669    pub const DATA_OFFSET: usize = 13;
670}
671
672#[derive(Debug, SSHEncode, SSHDecode)]
673#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
674pub struct ChannelEof {
675    pub num: u32,
676}
677
678#[derive(Debug, SSHEncode, SSHDecode)]
679#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
680pub struct ChannelClose {
681    pub num: u32,
682}
683
684#[derive(Debug, SSHEncode, SSHDecode)]
685#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
686pub struct ChannelSuccess {
687    pub num: u32,
688}
689
690#[derive(Debug, SSHEncode, SSHDecode)]
691#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
692pub struct ChannelFailure {
693    pub num: u32,
694}
695
696#[derive(Debug, SSHEncode, SSHDecode)]
697#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
698pub struct ChannelRequest<'a> {
699    pub num: u32,
700
701    // channel_type is implicit in req below
702    #[sshwire(variant_name = req)]
703    pub want_reply: bool,
704    pub req: ChannelReqType<'a>,
705}
706
707/// Channel Requests
708///
709/// Most are specified in [RFC4335](https://datatracker.ietf.org/doc/html/rfc4335)
710#[derive(Debug, SSHEncode, SSHDecode)]
711#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
712pub enum ChannelReqType<'a> {
713    #[sshwire(variant = "shell")]
714    Shell,
715    #[sshwire(variant = "exec")]
716    Exec(Exec<'a>),
717    #[sshwire(variant = "pty-req")]
718    Pty(PtyReq<'a>),
719    #[sshwire(variant = "subsystem")]
720    Subsystem(Subsystem<'a>),
721    #[sshwire(variant = "window-change")]
722    WinChange(WinChange),
723    #[sshwire(variant = "env")]
724    Environment(Environment<'a>),
725    #[sshwire(variant = "signal")]
726    Signal(Signal<'a>),
727    #[sshwire(variant = "exit-status")]
728    ExitStatus(ExitStatus),
729    #[sshwire(variant = "exit-signal")]
730    ExitSignal(ExitSignal<'a>),
731    /// Channel Break Request
732    ///
733    /// [RFC4335](https://datatracker.ietf.org/doc/html/rfc4335)
734    #[sshwire(variant = "break")]
735    Break(Break),
736    // Other requests that aren't implemented at present:
737    // auth-agent-req@openssh.com
738    // x11-req
739    // xon-xoff
740    #[sshwire(unknown)]
741    Unknown(Unknown<'a>),
742}
743
744#[derive(Debug, SSHEncode, SSHDecode)]
745#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
746pub struct Exec<'a> {
747    pub command: TextString<'a>,
748}
749
750#[derive(Debug, SSHEncode, SSHDecode)]
751#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
752pub struct Subsystem<'a> {
753    pub subsystem: TextString<'a>,
754}
755
756/// The contents of a `"pty-req"` request.
757///
758/// Note that most function arguments use [`channel::Pty`] rather than this struct.
759#[derive(Debug, SSHEncode, SSHDecode)]
760#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
761pub struct PtyReq<'a> {
762    pub term: TextString<'a>,
763    pub cols: u32,
764    pub rows: u32,
765    pub width: u32,
766    pub height: u32,
767    pub modes: BinString<'a>,
768}
769
770#[derive(Debug, Clone, SSHEncode, SSHDecode)]
771#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
772pub struct WinChange {
773    pub cols: u32,
774    pub rows: u32,
775    pub width: u32,
776    pub height: u32,
777}
778
779/// An environment variable
780#[derive(Debug, SSHEncode, SSHDecode)]
781#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
782pub struct Environment<'a> {
783    pub name: TextString<'a>,
784    pub value: TextString<'a>,
785}
786
787/// A unix signal channel request
788#[derive(Debug, SSHEncode, SSHDecode)]
789#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
790pub struct Signal<'a> {
791    pub sig: &'a str,
792}
793
794#[derive(Debug, SSHEncode, SSHDecode)]
795#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
796pub struct ExitStatus {
797    pub status: u32,
798}
799
800#[derive(Debug, SSHEncode, SSHDecode, Clone)]
801#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
802pub struct ExitSignal<'a> {
803    pub signal: &'a str,
804    pub core: bool,
805    pub error: TextString<'a>,
806    pub lang: &'a str,
807}
808
809#[derive(Debug, Clone, SSHEncode, SSHDecode)]
810#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
811pub struct Break {
812    /// Break length in milliseconds
813    pub length: u32,
814}
815
816#[derive(Debug, SSHEncode, SSHDecode)]
817#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
818pub struct ForwardedTcpip<'a> {
819    pub address: TextString<'a>,
820    pub port: u32,
821    pub origin: TextString<'a>,
822    pub origin_port: u32,
823}
824
825#[derive(Debug, SSHEncode, SSHDecode)]
826#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
827pub struct DirectTcpip<'a> {
828    pub address: TextString<'a>,
829    pub port: u32,
830    pub origin: TextString<'a>,
831    pub origin_port: u32,
832}
833
834/// Placeholder for unknown method names.
835///
836/// These are sometimes non-fatal and
837/// need to be handled by the relevant code, for example newly invented pubkey types.
838/// This is deliberately not `SSHEncode`, we only receive it. sshwire-derive will
839/// automatically create instances.
840#[derive(Clone, PartialEq)]
841pub struct Unknown<'a>(pub &'a [u8]);
842
843impl<'a> Unknown<'a> {
844    fn new(u: &'a [u8]) -> Self {
845        let u = Unknown(u);
846        trace!("saw unknown variant \"{u}\"");
847        u
848    }
849}
850
851impl Display for Unknown<'_> {
852    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
853        if let Ok(s) = sshwire::try_as_ascii_str(self.0) {
854            f.write_str(s)
855        } else {
856            write!(f, "non-ascii {:?}", self.0.hex_dump())
857        }
858    }
859}
860
861impl Debug for Unknown<'_> {
862    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
863        Display::fmt(self, f)
864    }
865}
866
867/// Always fails.
868///
869/// `Unknown` can't be SSHEncoded.
870#[cfg(feature = "arbitrary")]
871impl arbitrary::Arbitrary<'_> for Unknown<'_> {
872    fn arbitrary(_u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
873        Err(arbitrary::Error::IncorrectFormat)
874    }
875}
876
877/// State to be passed to decoding.
878/// Use this so the parser can select the correct enum variant to decode.
879#[derive(Default, Clone, Debug)]
880pub struct ParseContext {
881    pub cli_auth_type: Option<auth::AuthType>,
882
883    // Set to true if an unknown variant is encountered.
884    // Packet length checks should be omitted in that case.
885    pub(crate) seen_unknown: bool,
886}
887
888impl ParseContext {
889    pub fn new() -> Self {
890        ParseContext { cli_auth_type: None, seen_unknown: false }
891    }
892}
893
894/// We have repeated `match` statements for the various packet types, use a macro
895macro_rules! messagetypes {
896    (
897        $( ( $message_num:literal,
898            $SpecificPacketVariant:ident,
899            $SpecificPacketType:ty,
900            $SSH_MESSAGE_NAME:ident,
901            $category:ident
902            ),
903             )*
904    ) => {
905
906
907#[derive(Debug)]
908#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
909#[repr(u8)]
910#[allow(non_camel_case_types)]
911pub enum MessageNumber {
912    // variants are eg
913    // SSH_MSG_KEXINIT = 20,
914    $(
915    $SSH_MESSAGE_NAME = $message_num,
916    )*
917}
918
919impl TryFrom<u8> for MessageNumber {
920    type Error = Error;
921    fn try_from(v: u8) -> Result<Self> {
922        match v {
923            // eg
924            // 20 => Ok(MessageNumber::SSH_MSG_KEXINIT)
925            $(
926            $message_num => Ok(MessageNumber::$SSH_MESSAGE_NAME),
927            )*
928            _ => {
929                Err(Error::UnknownPacket { number: v })
930            }
931        }
932    }
933}
934
935impl SSHEncode for Packet<'_> {
936    fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> {
937        let t = self.message_num() as u8;
938        t.enc(s)?;
939        match self {
940            // eg
941            // Packet::KexInit(p) => {
942            // ...
943            $(
944            Packet::$SpecificPacketVariant(p) => {
945                p.enc(s)
946            }
947            )*
948        }
949    }
950}
951
952impl<'de: 'a, 'a> SSHDecode<'de> for Packet<'a> {
953    fn dec<S>(s: &mut S) -> WireResult<Self>
954    where S: SSHSource<'de> {
955        let msg_num = u8::dec(s)?;
956        let ty = MessageNumber::try_from(msg_num);
957        let ty = match ty {
958            Ok(t) => t,
959            Err(_) => return Err(WireError::UnknownPacket { number: msg_num })
960        };
961
962        // Decode based on the message number
963        let p = match ty {
964            // eg
965            // MessageNumber::SSH_MSG_KEXINIT => Packet::KexInit(
966            // ...
967            $(
968            MessageNumber::$SSH_MESSAGE_NAME => Packet::$SpecificPacketVariant(SSHDecode::dec(s)?),
969            )*
970        };
971        Ok(p)
972    }
973}
974
975#[cfg(feature = "arbitrary")]
976impl<'arb: 'a, 'a> Arbitrary<'arb> for Packet<'a> {
977    fn arbitrary(u: &mut arbitrary::Unstructured<'arb>) -> arbitrary::Result<Self> {
978        let ty: MessageNumber = u.arbitrary()?;
979        // Generate based on the message number
980        let p = match ty {
981            $(
982            MessageNumber::$SSH_MESSAGE_NAME => Packet::$SpecificPacketVariant(u.arbitrary()?),
983            )*
984        };
985        Ok(p)
986    }
987}
988
989/// Top level SSH packet enum
990#[derive(Debug)]
991pub enum Packet<'a> {
992    // eg KexInit(KexInit<'a>),
993    $(
994    $SpecificPacketVariant($SpecificPacketType),
995    )*
996}
997
998impl<'a> Packet<'a> {
999    pub fn message_num(&self) -> MessageNumber {
1000        match self {
1001            // eg
1002            // Packet::KexInit() => {
1003            // ..
1004            $(
1005            Packet::$SpecificPacketVariant(_) => {
1006                MessageNumber::$SSH_MESSAGE_NAME
1007            }
1008            )*
1009        }
1010    }
1011
1012    pub fn category(&self) -> Category {
1013        match self {
1014            // eg
1015            // Packet::KexInit() => Category::Kex,
1016            $(
1017            Packet::$SpecificPacketVariant(_) => Category::$category,
1018            )*
1019        }
1020    }
1021}
1022
1023$(
1024impl<'a> From<$SpecificPacketType> for Packet<'a> {
1025    fn from(s: $SpecificPacketType) -> Packet<'a> {
1026        Packet::$SpecificPacketVariant(s)
1027    }
1028}
1029)*
1030
1031} } // macro
1032
1033pub enum Category {
1034    /// Allowed at any time.
1035    /// TODO: may need to limit some of these during KEX.
1036    All,
1037    /// After kexinit, before newkeys complete (other packets are not allowed during
1038    /// that time).
1039    Kex,
1040    /// Post-kex
1041    Auth,
1042    /// Post-auth
1043    Sess,
1044}
1045
1046messagetypes![
1047(1, Disconnect, Disconnect<'a>, SSH_MSG_DISCONNECT, All),
1048(2, Ignore, Ignore<'a>, SSH_MSG_IGNORE, All),
1049(3, Unimplemented, Unimplemented, SSH_MSG_UNIMPLEMENTED, All),
1050(4, DebugPacket, DebugPacket<'a>, SSH_MSG_DEBUG, All),
1051(5, ServiceRequest, ServiceRequest<'a>, SSH_MSG_SERVICE_REQUEST, Auth),
1052(6, ServiceAccept, ServiceAccept<'a>, SSH_MSG_SERVICE_ACCEPT, Auth),
1053(7, ExtInfo, ExtInfo<'a>, SSH_MSG_EXT_INFO, All),
1054// 8        SSH_MSG_NEWCOMPRESS    RFC 8308
1055(20, KexInit, KexInit<'a>, SSH_MSG_KEXINIT, All),
1056(21, NewKeys, NewKeys, SSH_MSG_NEWKEYS, Kex),
1057(30, KexDHInit, KexDHInit<'a>, SSH_MSG_KEXDH_INIT, Kex),
1058(31, KexDHReply, KexDHReply<'a>, SSH_MSG_KEXDH_REPLY, Kex),
1059
1060(50, UserauthRequest, UserauthRequest<'a>, SSH_MSG_USERAUTH_REQUEST, Auth),
1061(51, UserauthFailure, UserauthFailure<'a>, SSH_MSG_USERAUTH_FAILURE, Auth),
1062(52, UserauthSuccess, UserauthSuccess, SSH_MSG_USERAUTH_SUCCESS, Auth),
1063(53, UserauthBanner, UserauthBanner<'a>, SSH_MSG_USERAUTH_BANNER, Auth),
1064// One of
1065// SSH_MSG_USERAUTH_PASSWD_CHANGEREQ
1066// SSH_MSG_USERAUTH_PK_OK
1067// SSH_MSG_USERAUTH_INFO_REQUEST
1068(60, Userauth60, Userauth60<'a>, SSH_MSG_USERAUTH_60, Auth),
1069// (61, SSH_MSG_USERAUTH_INFO_RESPONSE),
1070
1071(80, GlobalRequest, GlobalRequest<'a>, SSH_MSG_GLOBAL_REQUEST, Sess),
1072(81, RequestSuccess, RequestSuccess, SSH_MSG_REQUEST_SUCCESS, Sess),
1073(82, RequestFailure, RequestFailure, SSH_MSG_REQUEST_FAILURE, Sess),
1074
1075(90, ChannelOpen, ChannelOpen<'a>, SSH_MSG_CHANNEL_OPEN, Sess),
1076(91, ChannelOpenConfirmation, ChannelOpenConfirmation, SSH_MSG_CHANNEL_OPEN_CONFIRMATION, Sess),
1077(92, ChannelOpenFailure, ChannelOpenFailure<'a>, SSH_MSG_CHANNEL_OPEN_FAILURE, Sess),
1078(93, ChannelWindowAdjust, ChannelWindowAdjust, SSH_MSG_CHANNEL_WINDOW_ADJUST, Sess),
1079(94, ChannelData, ChannelData<'a>, SSH_MSG_CHANNEL_DATA, Sess),
1080(95, ChannelDataExt, ChannelDataExt<'a>, SSH_MSG_CHANNEL_EXTENDED_DATA, Sess),
1081(96, ChannelEof, ChannelEof, SSH_MSG_CHANNEL_EOF, Sess),
1082(97, ChannelClose, ChannelClose, SSH_MSG_CHANNEL_CLOSE, Sess),
1083(98, ChannelRequest, ChannelRequest<'a>, SSH_MSG_CHANNEL_REQUEST, Sess),
1084(99, ChannelSuccess, ChannelSuccess, SSH_MSG_CHANNEL_SUCCESS, Sess),
1085(100, ChannelFailure, ChannelFailure, SSH_MSG_CHANNEL_FAILURE, Sess),
1086];
1087
1088#[cfg(test)]
1089mod tests {
1090    use crate::packets::*;
1091
1092    use crate::packets;
1093    use crate::sshwire::tests::test_roundtrip;
1094    use crate::sshwire::{packet_from_bytes, write_ssh};
1095    use crate::sunsetlog::init_test_log;
1096    use pretty_hex::PrettyHex;
1097
1098    #[test]
1099    /// check round trip of packet enums is right
1100    fn packet_type() {
1101        for i in 0..=255 {
1102            let ty = packets::MessageNumber::try_from(i);
1103            if let Ok(ty) = ty {
1104                assert_eq!(i, ty as u8);
1105            }
1106        }
1107    }
1108
1109    #[test]
1110    /// Tests MethodPubKey custom serde
1111    fn roundtrip_authpubkey() {
1112        init_test_log();
1113        // with None sig
1114        let k = SignKey::generate(KeyType::Ed25519, None).unwrap();
1115        let method =
1116            AuthMethod::PubKey(MethodPubKey::new(k.pubkey(), None).unwrap());
1117        let p = UserauthRequest {
1118            username: "matt".into(),
1119            service: "conn".into(),
1120            method,
1121        }
1122        .into();
1123        test_roundtrip(&p);
1124
1125        // again with a sig
1126        let owned_sig = k.sign(&"hello").unwrap();
1127        let sig: Signature = (&owned_sig).into();
1128        let sig_algo = sig.algorithm_name().unwrap();
1129        let sig = Some(Blob(sig));
1130        let method = AuthMethod::PubKey(MethodPubKey {
1131            sig_algo,
1132            pubkey: Blob(k.pubkey()),
1133            sig,
1134            force_sig: false,
1135        });
1136        let p = UserauthRequest { username: "matt".into(), service: "conn", method }
1137            .into();
1138        test_roundtrip(&p);
1139    }
1140
1141    #[test]
1142    fn roundtrip_channel_open() {
1143        init_test_log();
1144        let p = Packet::ChannelOpen(ChannelOpen {
1145            sender_num: 111,
1146            initial_window: 50000,
1147            max_packet: 20000,
1148            ty: ChannelOpenType::DirectTcpip(DirectTcpip {
1149                address: "localhost".into(),
1150                port: 4444,
1151                origin: "somewhere".into(),
1152                origin_port: 0,
1153            }),
1154        });
1155        test_roundtrip(&p);
1156
1157        let p = Packet::ChannelOpen(ChannelOpen {
1158            sender_num: 0,
1159            initial_window: 899,
1160            max_packet: 14,
1161            ty: ChannelOpenType::Session,
1162        });
1163        test_roundtrip(&p);
1164    }
1165
1166    #[test]
1167    fn unknown_method() {
1168        init_test_log();
1169        let p = Packet::ChannelOpen(ChannelOpen {
1170            sender_num: 0,
1171            initial_window: 899,
1172            max_packet: 14,
1173            ty: ChannelOpenType::Session,
1174        });
1175        let mut buf1 = vec![88; 1000];
1176        let l = write_ssh(&mut buf1, &p).unwrap();
1177        buf1.truncate(l);
1178        // change a byte
1179        buf1[8] = 'X' as u8;
1180        trace!("broken: {:?}", buf1.hex_dump());
1181        let ctx = ParseContext::default();
1182        let p2 = packet_from_bytes(&buf1, &ctx).unwrap();
1183        trace!("broken: {p2:#?}");
1184        assert!(matches!(
1185            p2,
1186            Packet::ChannelOpen(ChannelOpen { ty: ChannelOpenType::Unknown(_), .. })
1187        ));
1188    }
1189
1190    #[test]
1191    /// Tests recovery from unknown variants in a blob when decoding.
1192    fn unknown_variant_in_blob() {
1193        init_test_log();
1194        let p: Packet = UserauthRequest {
1195            username: "matt".into(),
1196            service: "connection",
1197            method: AuthMethod::PubKey(MethodPubKey {
1198                sig_algo: "something",
1199                pubkey: Blob(PubKey::Ed25519(Ed25519PubKey {
1200                    key: Blob([3u8; 32]),
1201                })),
1202                sig: Some(Blob(Signature::Ed25519(Ed25519Sig {
1203                    sig: BinString(b"sighere"),
1204                }))),
1205                force_sig: false,
1206            }),
1207        }
1208        .into();
1209
1210        let mut buf1 = vec![88; 1000];
1211        let l = write_ssh(&mut buf1, &p).unwrap();
1212        buf1.truncate(l);
1213        // change a byte in the "ssh-ed25519" variant string
1214        buf1[60] = 'F' as u8;
1215        trace!("broken: {:?}", buf1.hex_dump());
1216        let ctx = ParseContext::default();
1217        let p2 = packet_from_bytes(&buf1, &ctx).unwrap();
1218        trace!("broken: {p2:#?}");
1219        assert!(matches!(
1220            p2,
1221            Packet::UserauthRequest(UserauthRequest {
1222                method: AuthMethod::PubKey(MethodPubKey {
1223                    pubkey: Blob(PubKey::Unknown(Unknown(b"ssF-ed25519"))),
1224                    sig: Some(Blob(Signature::Ed25519(_))),
1225                    ..
1226                }),
1227                ..
1228            })
1229        ));
1230    }
1231
1232    #[test]
1233    #[should_panic]
1234    fn unknown_method_ser() {
1235        init_test_log();
1236        let p = Packet::ChannelOpen(ChannelOpen {
1237            sender_num: 0,
1238            initial_window: 200000,
1239            max_packet: 88200,
1240            ty: ChannelOpenType::Unknown(Unknown(b"audio-stream")),
1241        });
1242        let mut buf1 = vec![88; 1000];
1243        write_ssh(&mut buf1, &p).unwrap();
1244    }
1245}