1#[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#[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 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#[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#[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#[derive(Debug)]
127#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
128pub struct ExtInfo<'a> {
129 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 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#[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 }
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
243impl 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 pub sig_algo: &'a str,
257 pub pubkey: Blob<PubKey<'a>>,
258 pub sig: Option<Blob<Signature<'a>>>,
259 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 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 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#[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 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 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 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(unknown)]
550 Unknown(Unknown<'a>),
551}
552
553#[derive(Debug, SSHEncode)]
564#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
565#[sshwire(no_variant_names)]
566pub enum RequestSuccess {
567 SuccessEmpty,
568 }
570
571impl<'de> SSHDecode<'de> for RequestSuccess {
572 fn dec<S>(_s: &mut S) -> WireResult<Self>
573 where
574 S: SSHSource<'de>,
575 {
576 Ok(Self::SuccessEmpty)
582 }
583}
584
585#[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 #[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(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 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 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 #[sshwire(variant_name = req)]
703 pub want_reply: bool,
704 pub req: ChannelReqType<'a>,
705}
706
707#[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 #[sshwire(variant = "break")]
735 Break(Break),
736 #[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#[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#[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#[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 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#[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#[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#[derive(Default, Clone, Debug)]
880pub struct ParseContext {
881 pub cli_auth_type: Option<auth::AuthType>,
882
883 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
894macro_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 $(
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 $(
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 $(
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 let p = match ty {
964 $(
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 let p = match ty {
981 $(
982 MessageNumber::$SSH_MESSAGE_NAME => Packet::$SpecificPacketVariant(u.arbitrary()?),
983 )*
984 };
985 Ok(p)
986 }
987}
988
989#[derive(Debug)]
991pub enum Packet<'a> {
992 $(
994 $SpecificPacketVariant($SpecificPacketType),
995 )*
996}
997
998impl<'a> Packet<'a> {
999 pub fn message_num(&self) -> MessageNumber {
1000 match self {
1001 $(
1005 Packet::$SpecificPacketVariant(_) => {
1006 MessageNumber::$SSH_MESSAGE_NAME
1007 }
1008 )*
1009 }
1010 }
1011
1012 pub fn category(&self) -> Category {
1013 match self {
1014 $(
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} } pub enum Category {
1034 All,
1037 Kex,
1040 Auth,
1042 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(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(60, Userauth60, Userauth60<'a>, SSH_MSG_USERAUTH_60, Auth),
1069(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 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 fn roundtrip_authpubkey() {
1112 init_test_log();
1113 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 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 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 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 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}