Skip to content

Commit f58d389

Browse files
committed
fixup
1 parent c495d6b commit f58d389

18 files changed

Lines changed: 290 additions & 98 deletions

russh/examples/client_exec_interactive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ impl client::Handler for Client {
6969

7070
async fn check_server_key(
7171
&mut self,
72-
_server_public_key: &ssh_key::PublicKey,
72+
_server_public_key: &PublicKeyOrCertificate,
7373
) -> Result<bool, Self::Error> {
7474
Ok(true)
7575
}

russh/examples/client_exec_simple.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl client::Handler for Client {
6363

6464
async fn check_server_key(
6565
&mut self,
66-
_server_public_key: &ssh_key::PublicKey,
66+
_server_public_key: &PublicKeyOrCertificate,
6767
) -> Result<bool, Self::Error> {
6868
Ok(true)
6969
}

russh/examples/client_open_direct_tcpip.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ impl client::Handler for Client {
6262

6363
async fn check_server_key(
6464
&mut self,
65-
_server_public_key: &ssh_key::PublicKey,
65+
_server_public_key: &PublicKeyOrCertificate,
6666
) -> Result<bool, Self::Error> {
6767
Ok(true)
6868
}

russh/examples/sftp_client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ impl client::Handler for Client {
1414

1515
async fn check_server_key(
1616
&mut self,
17-
server_public_key: &ssh_key::PublicKey,
17+
server_public_key: &PublicKeyOrCertificate,
1818
) -> Result<bool, Self::Error> {
1919
info!("check_server_key: {server_public_key:?}");
2020
Ok(true)

russh/src/cert.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::keys::key::PrivateKeyWithHashAlg;
99

1010
#[derive(Debug)]
1111
#[allow(clippy::large_enum_variant)]
12-
pub(crate) enum PublicKeyOrCertificate {
12+
pub enum PublicKeyOrCertificate {
1313
PublicKey {
1414
key: PublicKey,
1515
hash_alg: Option<HashAlg>,
@@ -26,9 +26,24 @@ impl From<&PrivateKeyWithHashAlg> for PublicKeyOrCertificate {
2626
}
2727
}
2828

29+
impl From<Certificate> for PublicKeyOrCertificate {
30+
fn from(cert: Certificate) -> Self {
31+
PublicKeyOrCertificate::Certificate(cert)
32+
}
33+
}
34+
35+
impl From<PublicKey> for PublicKeyOrCertificate {
36+
fn from(key: PublicKey) -> Self {
37+
PublicKeyOrCertificate::PublicKey {
38+
key,
39+
hash_alg: None,
40+
}
41+
}
42+
}
43+
2944
impl PublicKeyOrCertificate {
3045
#[cfg(not(target_arch = "wasm32"))]
31-
pub fn decode(pubkey_algo: &str, buf: &[u8]) -> Result<Self, ssh_key::Error> {
46+
pub(crate) fn decode(pubkey_algo: &str, buf: &[u8]) -> Result<Self, ssh_key::Error> {
3247
let mut reader = buf;
3348
match Algorithm::new_certificate_ext(pubkey_algo) {
3449
Ok(Algorithm::Other(_)) | Err(ssh_key::Error::Encoding(_)) => {
@@ -43,4 +58,20 @@ impl PublicKeyOrCertificate {
4358
)?)),
4459
}
4560
}
61+
62+
pub fn public_key(&self) -> PublicKey {
63+
match self {
64+
PublicKeyOrCertificate::PublicKey { key, .. } => key.clone(),
65+
PublicKeyOrCertificate::Certificate(cert) => {
66+
PublicKey::new(cert.public_key().clone(), "")
67+
}
68+
}
69+
}
70+
71+
pub fn certificate(&self) -> Option<&Certificate> {
72+
match self {
73+
PublicKeyOrCertificate::PublicKey { .. } => None,
74+
PublicKeyOrCertificate::Certificate(cert) => Some(cert),
75+
}
76+
}
4677
}

russh/src/client/kex.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,11 @@ impl ClientKex {
272272
// looks like a bad signature and is computed entirely locally,
273273
// so there is nothing on the wire to compare against.
274274
let server_host_key_blob = Bytes::decode(r)?;
275-
let server_host_certificate = Certificate::from_bytes(&server_host_key_blob).ok();
275+
let server_host_certificate = if names.host_key_is_certificate {
276+
Some(Certificate::from_bytes(&server_host_key_blob)?)
277+
} else {
278+
None
279+
};
276280
let server_host_key = match &server_host_certificate {
277281
// The certificate's own signature is checked by the client
278282
// against its trusted authorities, not here; what the key

russh/src/client/mod.rs

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ use tokio::sync::mpsc::{
5858
use tokio::sync::oneshot;
5959

6060
pub use crate::auth::AuthResult;
61+
use crate::cert::PublicKeyOrCertificate;
6162
use crate::channels::{
6263
Channel, ChannelMsg, ChannelReadHalf, ChannelRef, ChannelWriteHalf, WindowSizeRef,
6364
};
@@ -1198,8 +1199,7 @@ impl Session {
11981199
// Keep reading the network for window adjustments, but leave
11991200
// application output in its bounded receivers while a channel is
12001201
// window-blocked.
1201-
let can_receive_outbound =
1202-
!self.kex.active() && !self.common.has_any_pending_data();
1202+
let can_receive_outbound = !self.kex.active() && !self.common.has_any_pending_data();
12031203
tokio::select! {
12041204
r = &mut reading => {
12051205
let (stream_read, mut buffer, mut opening_cipher) = match r {
@@ -1690,12 +1690,12 @@ async fn reply<H: Handler>(
16901690
// something the client was ever told to trust — asking
16911691
// about it as well would invite an implementation to
16921692
// answer yes to the wrong question.
1693-
if let Some(certificate) = &server_host_certificate {
1694-
if !handler.check_server_certificate(certificate).await? {
1693+
if let Some(certificate) = server_host_certificate {
1694+
if !handler.check_server_key(&certificate.into()).await? {
16951695
return Err(crate::Error::UnknownKey.into());
16961696
}
1697-
} else if let Some(server_host_key) = &server_host_key {
1698-
let check = handler.check_server_key(server_host_key).await?;
1697+
} else if let Some(server_host_key) = server_host_key {
1698+
let check = handler.check_server_key(&server_host_key.into()).await?;
16991699
if !check {
17001700
return Err(crate::Error::UnknownKey.into());
17011701
}
@@ -1754,7 +1754,7 @@ mod tests {
17541754
impl Handler for TestHandler {
17551755
type Error = crate::Error;
17561756

1757-
async fn check_server_key(&mut self, _: &ssh_key::PublicKey) -> Result<bool, Self::Error> {
1757+
async fn check_server_key(&mut self, _: &PublicKeyOrCertificate) -> Result<bool, Self::Error> {
17581758
Ok(true)
17591759
}
17601760
}
@@ -2158,29 +2158,17 @@ pub trait Handler: Sized + Send {
21582158
async { Ok(()) }
21592159
}
21602160

2161-
/// Called to check the server's public key. This is a very important
2162-
/// step to help prevent man-in-the-middle attacks. The default
2163-
/// implementation rejects all keys.
2164-
#[allow(unused_variables)]
2165-
/// Called instead of [`Self::check_server_key`] when the server proved its
2166-
/// identity with a certificate.
2161+
/// Called to check the server's public key or certificate.
2162+
/// This is a very important step to help prevent man-in-the-middle attacks.
2163+
/// The default implementation rejects all keys, and you must override it.
21672164
///
2168-
/// Defaults to refusing. A client that has not been taught which
2169-
/// authorities it trusts cannot answer this question, and answering it
2170-
/// wrongly accepts any machine whose operator can obtain a certificate from
2171-
/// anyone at all — so silence has to mean no.
2172-
#[allow(unused_variables)]
2173-
fn check_server_certificate(
2174-
&mut self,
2175-
certificate: &ssh_key::Certificate,
2176-
) -> impl std::future::Future<Output = Result<bool, Self::Error>> + Send {
2177-
async { Ok(false) }
2178-
}
2179-
2165+
/// The library verifies the key exchange signature before this call,
2166+
/// but it's up to the implementation to decide whether the key or certificate
2167+
/// is trusted.
21802168
#[allow(unused_variables)]
21812169
fn check_server_key(
21822170
&mut self,
2183-
server_public_key: &ssh_key::PublicKey,
2171+
server_public_key: &PublicKeyOrCertificate,
21842172
) -> impl Future<Output = Result<bool, Self::Error>> + Send {
21852173
async { Ok(false) }
21862174
}

russh/src/client/test.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ mod tests {
77
use ssh_key::PrivateKey;
88
use tokio::net::TcpListener;
99

10-
// Import client types directly since we're in the client module
10+
use crate::cert::PublicKeyOrCertificate;
11+
// Import client types directly since we're in the client module
1112
use crate::client::{Config, Handler, connect};
1213
use crate::keys::PrivateKeyWithHashAlg;
1314
use rand::rng;
@@ -74,7 +75,7 @@ mod tests {
7475
impl Handler for Client {
7576
type Error = Error;
7677

77-
async fn check_server_key(&mut self, _: &ssh_key::PublicKey) -> Result<bool, Self::Error> {
78+
async fn check_server_key(&mut self, _: &PublicKeyOrCertificate) -> Result<bool, Self::Error> {
7879
Ok(true)
7980
}
8081
}

russh/src/keys/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ pub mod known_hosts;
8686
#[cfg(not(target_arch = "wasm32"))]
8787
pub use known_hosts::{check_known_hosts, check_known_hosts_path};
8888

89+
pub use crate::cert::PublicKeyOrCertificate;
90+
8991
#[derive(Debug, Error)]
9092
pub enum Error {
9193
/// The key could not be read, for an unknown reason

0 commit comments

Comments
 (0)