mirror of
https://github.com/Noratrieb/cluelessh.git
synced 2026-01-14 08:25:05 +01:00
small fixes
This commit is contained in:
parent
185d77e94f
commit
026965bda5
19 changed files with 124 additions and 106 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -449,11 +449,13 @@ dependencies = [
|
|||
"crypto-bigint",
|
||||
"ctr",
|
||||
"ed25519-dalek",
|
||||
"hex",
|
||||
"hex-literal",
|
||||
"p256",
|
||||
"poly1305",
|
||||
"rand_core",
|
||||
"secrecy",
|
||||
"serde",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"tracing",
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ async fn main() -> eyre::Result<()> {
|
|||
|
||||
let transport_config = cluelessh_protocol::transport::server::ServerConfig {
|
||||
host_keys: pub_host_keys,
|
||||
// This is definitely who we are.
|
||||
server_identification: b"SSH-2.0-OpenSSH_9.7\r\n".to_vec(),
|
||||
};
|
||||
|
||||
let mut listener =
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ async fn main() -> eyre::Result<()> {
|
|||
result.wrap_err("failed to prompt password")
|
||||
})
|
||||
}),
|
||||
sign_pubkey: Arc::new(move |session_identifier| {
|
||||
sign_pubkey: Arc::new(move |session_id| {
|
||||
let mut attempted_public_keys = HashSet::new();
|
||||
let username = username.clone();
|
||||
Box::pin(async move {
|
||||
|
|
@ -93,11 +93,8 @@ async fn main() -> eyre::Result<()> {
|
|||
}
|
||||
let pubkey = PublicKey::from_wire_encoding(&identity.key_blob)?;
|
||||
|
||||
let sign_data = cluelessh_keys::signature::signature_data(
|
||||
session_identifier,
|
||||
&username,
|
||||
&pubkey,
|
||||
);
|
||||
let sign_data =
|
||||
cluelessh_keys::signature::signature_data(session_id.0, &username, &pubkey);
|
||||
let signature = agent
|
||||
.sign(&identity.key_blob, &sign_data, 0)
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ pub async fn verify_signature(auth: VerifySignature) -> eyre::Result<Option<User
|
|||
// Verify signature...
|
||||
|
||||
let sign_data = cluelessh_keys::signature::signature_data(
|
||||
auth.session_identifier,
|
||||
auth.session_id.0,
|
||||
&auth.user,
|
||||
&auth.public_key,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ fn default_false() -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
|
||||
fn addr_default() -> IpAddr {
|
||||
IpAddr::V4(Ipv4Addr::UNSPECIFIED)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,10 @@ async fn connection_inner(state: SerializedConnectionState) -> Result<()> {
|
|||
let stream = TcpStream::from_std(stream)?;
|
||||
|
||||
let host_keys = state.pub_host_keys;
|
||||
let transport_config = cluelessh_transport::server::ServerConfig { host_keys };
|
||||
let transport_config = cluelessh_transport::server::ServerConfig {
|
||||
host_keys,
|
||||
server_identification: b"SSH-2.0-ClueleSSH_0.1\r\n".to_vec(),
|
||||
};
|
||||
|
||||
let rpc_client = unsafe { OwnedFd::from_raw_fd(PRIVSEP_CONNECTION_RPC_CLIENT_FD) };
|
||||
let rpc_client1 = Arc::new(rpc::Client::from_fd(rpc_client)?);
|
||||
|
|
@ -66,25 +69,13 @@ async fn connection_inner(state: SerializedConnectionState) -> Result<()> {
|
|||
let rpc_client = rpc_client1.clone();
|
||||
Box::pin(async move {
|
||||
rpc_client
|
||||
.verify_signature(
|
||||
msg.user,
|
||||
msg.session_identifier,
|
||||
msg.public_key,
|
||||
msg.signature,
|
||||
)
|
||||
.verify_signature(msg.user, msg.session_id, msg.public_key, msg.signature)
|
||||
.await
|
||||
})
|
||||
})),
|
||||
check_pubkey: Some(Arc::new(move |msg| {
|
||||
let rpc_client = rpc_client2.clone();
|
||||
Box::pin(async move {
|
||||
rpc_client
|
||||
.check_public_key(
|
||||
msg.user,
|
||||
msg.public_key,
|
||||
)
|
||||
.await
|
||||
})
|
||||
Box::pin(async move { rpc_client.check_public_key(msg.user, msg.public_key).await })
|
||||
})),
|
||||
auth_banner: config.auth.banner,
|
||||
do_key_exchange: Arc::new(move |msg| {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use cluelessh_keys::public::PublicKey;
|
|||
use cluelessh_keys::signature::Signature;
|
||||
use cluelessh_protocol::auth::VerifySignature;
|
||||
use cluelessh_transport::crypto::AlgorithmName;
|
||||
use cluelessh_transport::SessionId;
|
||||
use eyre::bail;
|
||||
use eyre::ensure;
|
||||
use eyre::eyre;
|
||||
|
|
@ -56,7 +57,7 @@ enum Request {
|
|||
/// If it is okay, store the user so we can later spawn a process as them.
|
||||
VerifySignature {
|
||||
user: String,
|
||||
session_identifier: [u8; 32],
|
||||
session_id: SessionId,
|
||||
public_key: PublicKey,
|
||||
signature: Signature,
|
||||
},
|
||||
|
|
@ -115,7 +116,7 @@ impl secrecy::DebugSecret for SerializableSharedSecret {}
|
|||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct KeyExchangeResponse {
|
||||
pub hash: [u8; 32],
|
||||
pub hash: SessionId,
|
||||
pub server_ephemeral_public_key: Vec<u8>,
|
||||
pub shared_secret: secrecy::Secret<SerializableSharedSecret>,
|
||||
pub signature: Signature,
|
||||
|
|
@ -259,7 +260,7 @@ impl Server {
|
|||
}
|
||||
Request::VerifySignature {
|
||||
user,
|
||||
session_identifier,
|
||||
session_id,
|
||||
public_key,
|
||||
signature,
|
||||
} => {
|
||||
|
|
@ -269,7 +270,7 @@ impl Server {
|
|||
}
|
||||
let is_ok = crate::auth::verify_signature(VerifySignature {
|
||||
user,
|
||||
session_identifier,
|
||||
session_id,
|
||||
public_key,
|
||||
signature,
|
||||
})
|
||||
|
|
@ -487,13 +488,13 @@ impl Client {
|
|||
pub async fn verify_signature(
|
||||
&self,
|
||||
user: String,
|
||||
session_identifier: [u8; 32],
|
||||
session_id: SessionId,
|
||||
public_key: PublicKey,
|
||||
signature: Signature,
|
||||
) -> Result<bool> {
|
||||
self.request_response::<VerifySignatureResponse>(&Request::VerifySignature {
|
||||
user,
|
||||
session_identifier,
|
||||
session_id,
|
||||
public_key,
|
||||
signature,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ use rustix::{
|
|||
use seccompiler::{BpfProgram, SeccompAction, SeccompFilter, SeccompRule, TargetArch};
|
||||
use tracing::{debug, trace, warn};
|
||||
|
||||
use crate::{SerializedConnectionState, PRIVSEP_CONNECTION_RPC_CLIENT_FD, PRIVSEP_CONNECTION_STREAM_FD};
|
||||
use crate::{
|
||||
SerializedConnectionState, PRIVSEP_CONNECTION_RPC_CLIENT_FD, PRIVSEP_CONNECTION_STREAM_FD,
|
||||
};
|
||||
|
||||
#[tracing::instrument(skip(state), ret)]
|
||||
pub fn drop_privileges(state: &SerializedConnectionState) -> Result<()> {
|
||||
|
|
@ -228,12 +230,24 @@ fn seccomp() -> Result<()> {
|
|||
(libc::SYS_eventfd2, vec![]),
|
||||
(libc::SYS_epoll_wait, vec![]),
|
||||
(libc::SYS_epoll_ctl, vec![]),
|
||||
(libc::SYS_fcntl, vec![]), // todo: restrict (72)
|
||||
(libc::SYS_fcntl, vec![]), // todo: restrict this
|
||||
(libc::SYS_socketpair, vec![]),
|
||||
(libc::SYS_sendmsg, vec![limit_fd(PRIVSEP_CONNECTION_RPC_CLIENT_FD)],),
|
||||
(libc::SYS_recvmsg, vec![limit_fd(PRIVSEP_CONNECTION_RPC_CLIENT_FD)]),
|
||||
(libc::SYS_sendto, vec![limit_fd(PRIVSEP_CONNECTION_STREAM_FD)]),
|
||||
(libc::SYS_recvfrom, vec![limit_fd(PRIVSEP_CONNECTION_STREAM_FD)]),
|
||||
(
|
||||
libc::SYS_sendmsg,
|
||||
vec![limit_fd(PRIVSEP_CONNECTION_RPC_CLIENT_FD)],
|
||||
),
|
||||
(
|
||||
libc::SYS_recvmsg,
|
||||
vec![limit_fd(PRIVSEP_CONNECTION_RPC_CLIENT_FD)],
|
||||
),
|
||||
(
|
||||
libc::SYS_sendto,
|
||||
vec![limit_fd(PRIVSEP_CONNECTION_STREAM_FD)],
|
||||
),
|
||||
(
|
||||
libc::SYS_recvfrom,
|
||||
vec![limit_fd(PRIVSEP_CONNECTION_STREAM_FD)],
|
||||
),
|
||||
(libc::SYS_getrandom, vec![]),
|
||||
(libc::SYS_rt_sigaction, vec![]),
|
||||
(libc::SYS_rt_sigprocmask, vec![]),
|
||||
|
|
|
|||
|
|
@ -153,8 +153,8 @@ impl ChannelsState {
|
|||
}
|
||||
|
||||
pub fn recv_packet(&mut self, packet: Packet) -> Result<()> {
|
||||
// TODO: window
|
||||
|
||||
// TODO: what if we mostly ignored window and just always increased it again?
|
||||
// there's an excention to ignore it entirely that we could also support...
|
||||
let mut p = packet.payload_parser();
|
||||
let packet_type = p.u8()?;
|
||||
match packet_type {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use cluelessh_format::{ParseError, Reader, Writer};
|
|||
|
||||
use crate::{private::PrivateKey, public::PublicKey};
|
||||
|
||||
// TODO SessionId newtype
|
||||
pub fn signature_data(session_id: [u8; 32], username: &str, pubkey: &PublicKey) -> Vec<u8> {
|
||||
let mut s = Writer::new();
|
||||
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@ impl ServerConnection {
|
|||
self.transport.recv_bytes(bytes)?;
|
||||
|
||||
if let ServerConnectionState::Setup(options, auth_banner) = &mut self.state {
|
||||
if let Some(session_ident) = self.transport.is_open() {
|
||||
if let Some(session_id) = self.transport.is_open() {
|
||||
self.state = ServerConnectionState::Auth(auth::ServerAuth::new(
|
||||
mem::take(options),
|
||||
auth_banner.take(),
|
||||
session_ident,
|
||||
session_id,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -177,9 +177,9 @@ impl ClientConnection {
|
|||
self.transport.recv_bytes(bytes)?;
|
||||
|
||||
if let ClientConnectionState::Setup(auth) = &mut self.state {
|
||||
if let Some(session_ident) = self.transport.is_open() {
|
||||
if let Some(session_id) = self.transport.is_open() {
|
||||
let mut auth = mem::take(auth).unwrap();
|
||||
auth.set_session_identifier(session_ident);
|
||||
auth.set_session_id(session_id);
|
||||
|
||||
debug!("Connection has been opened");
|
||||
self.state = ClientConnectionState::Auth(auth);
|
||||
|
|
@ -278,7 +278,7 @@ pub mod auth {
|
|||
|
||||
use cluelessh_format::{numbers, NameList};
|
||||
use cluelessh_keys::{public::PublicKey, signature::Signature};
|
||||
use cluelessh_transport::{packet::Packet, peer_error, Result};
|
||||
use cluelessh_transport::{packet::Packet, peer_error, Result, SessionId};
|
||||
use tracing::debug;
|
||||
|
||||
pub struct ServerAuth {
|
||||
|
|
@ -288,7 +288,7 @@ pub mod auth {
|
|||
options: HashSet<AuthOption>,
|
||||
banner: Option<String>,
|
||||
server_requests: VecDeque<ServerRequest>,
|
||||
session_ident: [u8; 32],
|
||||
session_id: SessionId,
|
||||
}
|
||||
|
||||
pub enum ServerRequest {
|
||||
|
|
@ -314,7 +314,7 @@ pub mod auth {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct VerifySignature {
|
||||
pub user: String,
|
||||
pub session_identifier: [u8; 32],
|
||||
pub session_id: SessionId,
|
||||
pub public_key: PublicKey,
|
||||
/// The signature. Guaranteed to match the algorithm of `public_key`.
|
||||
pub signature: Signature,
|
||||
|
|
@ -330,14 +330,14 @@ pub mod auth {
|
|||
pub fn new(
|
||||
options: HashSet<AuthOption>,
|
||||
banner: Option<String>,
|
||||
session_ident: [u8; 32],
|
||||
session_id: SessionId,
|
||||
) -> Self {
|
||||
Self {
|
||||
has_failed: false,
|
||||
packets_to_send: VecDeque::new(),
|
||||
options,
|
||||
is_authenticated: None,
|
||||
session_ident,
|
||||
session_id,
|
||||
banner,
|
||||
server_requests: VecDeque::new(),
|
||||
}
|
||||
|
|
@ -426,7 +426,7 @@ pub mod auth {
|
|||
self.server_requests
|
||||
.push_back(ServerRequest::VerifySignature(VerifySignature {
|
||||
user: username.to_owned(),
|
||||
session_identifier: self.session_ident,
|
||||
session_id: self.session_id,
|
||||
public_key,
|
||||
signature,
|
||||
}));
|
||||
|
|
@ -512,12 +512,12 @@ pub mod auth {
|
|||
packets_to_send: VecDeque<Packet>,
|
||||
user_requests: VecDeque<ClientUserRequest>,
|
||||
is_authenticated: bool,
|
||||
session_identifier: Option<[u8; 32]>,
|
||||
session_id: Option<SessionId>,
|
||||
}
|
||||
|
||||
pub enum ClientUserRequest {
|
||||
Password,
|
||||
PrivateKeySign { session_identifier: [u8; 32] },
|
||||
PrivateKeySign { session_id: SessionId },
|
||||
Banner(Vec<u8>),
|
||||
}
|
||||
|
||||
|
|
@ -533,13 +533,13 @@ pub mod auth {
|
|||
username,
|
||||
user_requests: VecDeque::new(),
|
||||
is_authenticated: false,
|
||||
session_identifier: None,
|
||||
session_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_session_identifier(&mut self, ident: [u8; 32]) {
|
||||
assert!(self.session_identifier.is_none());
|
||||
self.session_identifier = Some(ident);
|
||||
pub fn set_session_id(&mut self, session_id: SessionId) {
|
||||
assert!(self.session_id.is_none());
|
||||
self.session_id = Some(session_id);
|
||||
}
|
||||
|
||||
pub fn is_authenticated(&self) -> bool {
|
||||
|
|
@ -605,9 +605,9 @@ pub mod auth {
|
|||
// TODO: Ask the server whether there are any keys we can use instead of just yoloing the signature.
|
||||
self.user_requests
|
||||
.push_back(ClientUserRequest::PrivateKeySign {
|
||||
session_identifier: self
|
||||
.session_identifier
|
||||
.expect("set_session_identifier has not been called"),
|
||||
session_id: self
|
||||
.session_id
|
||||
.expect("set_session_id has not been called"),
|
||||
});
|
||||
} else {
|
||||
return Err(peer_error!(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use cluelessh_connection::{ChannelKind, ChannelNumber, ChannelOperation};
|
||||
use cluelessh_transport::SessionId;
|
||||
use std::{collections::HashMap, pin::Pin, sync::Arc};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
|
|
@ -31,7 +32,7 @@ pub struct ClientAuth {
|
|||
pub username: String,
|
||||
pub prompt_password: Arc<dyn Fn() -> BoxFuture<'static, Result<String>> + Send + Sync>,
|
||||
pub sign_pubkey:
|
||||
Arc<dyn Fn([u8; 32]) -> BoxFuture<'static, Result<SignatureResult>> + Send + Sync>,
|
||||
Arc<dyn Fn(SessionId) -> BoxFuture<'static, Result<SignatureResult>> + Send + Sync>,
|
||||
}
|
||||
|
||||
enum Operation {
|
||||
|
|
@ -59,9 +60,7 @@ impl<S: AsyncRead + AsyncWrite> ClientConnection<S> {
|
|||
channel_ops_recv,
|
||||
channels: HashMap::new(),
|
||||
proto: cluelessh_protocol::ClientConnection::new(
|
||||
cluelessh_transport::client::ClientConnection::new(
|
||||
cluelessh_protocol::OsRng,
|
||||
),
|
||||
cluelessh_transport::client::ClientConnection::new(cluelessh_protocol::OsRng),
|
||||
cluelessh_protocol::auth::ClientAuth::new(auth.username.as_bytes().to_vec()),
|
||||
),
|
||||
auth,
|
||||
|
|
@ -88,13 +87,11 @@ impl<S: AsyncRead + AsyncWrite> ClientConnection<S> {
|
|||
let _ = send.send(Operation::PasswordEntered(password)).await;
|
||||
});
|
||||
}
|
||||
cluelessh_protocol::auth::ClientUserRequest::PrivateKeySign {
|
||||
session_identifier,
|
||||
} => {
|
||||
cluelessh_protocol::auth::ClientUserRequest::PrivateKeySign { session_id } => {
|
||||
let send = self.operations_send.clone();
|
||||
let sign_pubkey = self.auth.sign_pubkey.clone();
|
||||
tokio::spawn(async move {
|
||||
let signature_result = sign_pubkey(session_identifier).await;
|
||||
let signature_result = sign_pubkey(session_id).await;
|
||||
let _ = send.send(Operation::Signature(signature_result)).await;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,10 +213,7 @@ impl<S: AsyncRead + AsyncWrite> ServerConnection<S> {
|
|||
tokio::spawn(async move {
|
||||
let result = check(check_pubkey.clone()).await;
|
||||
let _ = send
|
||||
.send(Operation::CheckPubkey(
|
||||
result,
|
||||
check_pubkey.public_key,
|
||||
))
|
||||
.send(Operation::CheckPubkey(result, check_pubkey.public_key))
|
||||
.await;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ x25519-dalek = "2.0.1"
|
|||
tracing.workspace = true
|
||||
base64 = "0.22.1"
|
||||
secrecy = "0.8.0"
|
||||
hex = "0.4.3"
|
||||
serde = { version = "1.0.209", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.4.1"
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ use tracing::{debug, info, trace};
|
|||
|
||||
use crate::{
|
||||
crypto::{
|
||||
self, AlgorithmName, EncodedSshSignature, EncryptionAlgorithm, HostKeyVerifyAlgorithm, KeyExchangeSecret, SharedSecret, SupportedAlgorithms
|
||||
self, AlgorithmName, EncodedSshSignature, EncryptionAlgorithm, HostKeyVerifyAlgorithm,
|
||||
KeyExchangeSecret, SharedSecret, SupportedAlgorithms,
|
||||
},
|
||||
packet::{Packet, PacketTransport, ProtocolIdentParser},
|
||||
peer_error, Msg, Result, SshRng, SshStatus,
|
||||
peer_error, Msg, Result, SessionId, SshRng, SshStatus,
|
||||
};
|
||||
use cluelessh_format::{numbers, NameList, Reader, Writer};
|
||||
|
||||
|
|
@ -50,10 +51,10 @@ enum ClientState {
|
|||
encryption_server_to_client: EncryptionAlgorithm,
|
||||
},
|
||||
ServiceRequest {
|
||||
session_identifier: [u8; 32],
|
||||
session_id: SessionId,
|
||||
},
|
||||
Open {
|
||||
session_identifier: [u8; 32],
|
||||
session_id: SessionId,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -310,10 +311,10 @@ impl ClientConnection {
|
|||
.queue_packet(Packet::new_msg_service_request(b"ssh-userauth"));
|
||||
|
||||
self.state = ClientState::ServiceRequest {
|
||||
session_identifier: *h,
|
||||
session_id: SessionId(*h),
|
||||
};
|
||||
}
|
||||
ClientState::ServiceRequest { session_identifier } => {
|
||||
ClientState::ServiceRequest { session_id } => {
|
||||
let mut accept = packet.payload_parser();
|
||||
let packet_type = accept.u8()?;
|
||||
if packet_type != numbers::SSH_MSG_SERVICE_ACCEPT {
|
||||
|
|
@ -326,7 +327,7 @@ impl ClientConnection {
|
|||
|
||||
debug!("Connection has been opened successfully");
|
||||
self.state = ClientState::Open {
|
||||
session_identifier: *session_identifier,
|
||||
session_id: *session_id,
|
||||
};
|
||||
}
|
||||
ClientState::Open { .. } => {
|
||||
|
|
@ -349,9 +350,9 @@ impl ClientConnection {
|
|||
self.packet_transport.queue_packet(packet);
|
||||
}
|
||||
|
||||
pub fn is_open(&self) -> Option<[u8; 32]> {
|
||||
pub fn is_open(&self) -> Option<SessionId> {
|
||||
match self.state {
|
||||
ClientState::Open { session_identifier } => Some(session_identifier),
|
||||
ClientState::Open { session_id } => Some(session_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use sha2::Digest;
|
|||
|
||||
use crate::{
|
||||
packet::{EncryptedPacket, MsgKind, Packet, RawPacket},
|
||||
peer_error, Msg, Result, SshRng,
|
||||
peer_error, Msg, Result, SessionId, SshRng,
|
||||
};
|
||||
|
||||
pub type SharedSecret = secrecy::Secret<SharedSecretInner>;
|
||||
|
|
@ -308,7 +308,7 @@ impl SupportedAlgorithms {
|
|||
}
|
||||
|
||||
pub(crate) struct Session {
|
||||
session_id: [u8; 32],
|
||||
session_id: SessionId,
|
||||
from_peer: Tunnel,
|
||||
to_peer: Tunnel,
|
||||
}
|
||||
|
|
@ -326,6 +326,7 @@ pub(crate) trait Keys: Send + Sync + 'static {
|
|||
fn encrypt_packet_to_msg(&mut self, packet: Packet, packet_number: u64) -> Msg;
|
||||
|
||||
fn additional_mac_len(&self) -> usize;
|
||||
// TODO: actually rekey...
|
||||
fn rekey(
|
||||
&mut self,
|
||||
h: [u8; 32],
|
||||
|
|
@ -362,7 +363,7 @@ impl Keys for Plaintext {
|
|||
|
||||
impl Session {
|
||||
pub(crate) fn new(
|
||||
h: [u8; 32],
|
||||
h: SessionId,
|
||||
k: &SharedSecret,
|
||||
encryption_client_to_server: EncryptionAlgorithm,
|
||||
encryption_server_to_client: EncryptionAlgorithm,
|
||||
|
|
@ -370,7 +371,7 @@ impl Session {
|
|||
) -> Self {
|
||||
Self::from_keys(
|
||||
h,
|
||||
h,
|
||||
h.0,
|
||||
k,
|
||||
encryption_client_to_server,
|
||||
encryption_server_to_client,
|
||||
|
|
@ -380,7 +381,7 @@ impl Session {
|
|||
|
||||
/// <https://datatracker.ietf.org/doc/html/rfc4253#section-7.2>
|
||||
fn from_keys(
|
||||
session_id: [u8; 32],
|
||||
session_id: SessionId,
|
||||
h: [u8; 32],
|
||||
k: &SharedSecret,
|
||||
alg_c2s: EncryptionAlgorithm,
|
||||
|
|
@ -462,7 +463,7 @@ fn derive_key(
|
|||
k: &SharedSecret,
|
||||
h: [u8; 32],
|
||||
letter: &str,
|
||||
session_id: [u8; 32],
|
||||
session_id: SessionId,
|
||||
key_size: usize,
|
||||
) -> Vec<u8> {
|
||||
let sha2len = sha2::Sha256::output_size();
|
||||
|
|
@ -476,7 +477,7 @@ fn derive_key(
|
|||
|
||||
if i == 0 {
|
||||
hash.update(letter.as_bytes());
|
||||
hash.update(session_id);
|
||||
hash.update(session_id.0);
|
||||
} else {
|
||||
hash.update(&output[..(i * sha2len)]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,13 @@ pub mod crypto;
|
|||
pub mod packet;
|
||||
pub mod server;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use cluelessh_format::ParseError;
|
||||
pub use packet::Msg;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// TODO: extensions
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SshStatus {
|
||||
|
|
@ -17,6 +22,17 @@ pub enum SshStatus {
|
|||
PeerError(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct SessionId(pub [u8; 32]);
|
||||
|
||||
impl Debug for SessionId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("SessionId")
|
||||
.field(&hex::encode(self.0))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T, E = SshStatus> = std::result::Result<T, E>;
|
||||
|
||||
impl From<ParseError> for SshStatus {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use std::mem;
|
|||
use tracing::{debug, trace};
|
||||
|
||||
use crate::crypto::{self, EncryptionAlgorithm, Keys, Plaintext, Session, SharedSecret};
|
||||
use crate::peer_error;
|
||||
use crate::Result;
|
||||
use crate::{peer_error, SessionId};
|
||||
use cluelessh_format::numbers;
|
||||
use cluelessh_format::{NameList, Reader, Writer};
|
||||
|
||||
|
|
@ -67,7 +67,8 @@ impl PacketTransport {
|
|||
}
|
||||
|
||||
fn recv_bytes_step(&mut self, bytes: &[u8]) -> Result<Option<usize>> {
|
||||
// TODO: This might not work if we buffer two packets where one changes keys in between?
|
||||
// This would not work if we buffer two packets where one changes keys in between,
|
||||
// but SSH_MSG_NEWKEYS messages guarantee that this cannot happen.
|
||||
|
||||
let result =
|
||||
self.recv_next_packet
|
||||
|
|
@ -125,7 +126,7 @@ impl PacketTransport {
|
|||
is_server,
|
||||
) {
|
||||
self.keys = Box::new(Session::new(
|
||||
h,
|
||||
SessionId(h),
|
||||
k,
|
||||
encryption_client_to_server,
|
||||
encryption_server_to_client,
|
||||
|
|
|
|||
|
|
@ -7,18 +7,14 @@ use crate::crypto::{
|
|||
use crate::packet::{
|
||||
KeyExchangeEcDhInitPacket, KeyExchangeInitPacket, Packet, PacketTransport, ProtocolIdentParser,
|
||||
};
|
||||
use crate::Result;
|
||||
use crate::{peer_error, Msg, SshRng, SshStatus};
|
||||
use crate::{Result, SessionId};
|
||||
use cluelessh_format::numbers;
|
||||
use cluelessh_format::{NameList, Reader, Writer};
|
||||
use cluelessh_keys::private::PlaintextPrivateKey;
|
||||
use cluelessh_keys::signature::Signature;
|
||||
use tracing::{debug, info, trace};
|
||||
|
||||
// This is definitely who we are.
|
||||
// TODO: dont make cluelesshd do this
|
||||
pub const SERVER_IDENTIFICATION: &[u8] = b"SSH-2.0-OpenSSH_9.7\r\n";
|
||||
|
||||
pub struct ServerConnection {
|
||||
state: ServerState,
|
||||
packet_transport: PacketTransport,
|
||||
|
|
@ -31,6 +27,7 @@ pub struct ServerConnection {
|
|||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ServerConfig {
|
||||
pub server_identification: Vec<u8>,
|
||||
pub host_keys: Vec<cluelessh_keys::public::PublicKey>,
|
||||
}
|
||||
|
||||
|
|
@ -69,10 +66,10 @@ enum ServerState {
|
|||
encryption_server_to_client: EncryptionAlgorithm,
|
||||
},
|
||||
ServiceRequest {
|
||||
session_ident: [u8; 32],
|
||||
session_id: SessionId,
|
||||
},
|
||||
Open {
|
||||
session_ident: [u8; 32],
|
||||
session_id: SessionId,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +84,7 @@ pub struct KeyExchangeParameters {
|
|||
}
|
||||
|
||||
pub struct KeyExchangeResponse {
|
||||
pub hash: [u8; 32],
|
||||
pub hash: SessionId,
|
||||
pub server_ephemeral_public_key: Vec<u8>,
|
||||
pub shared_secret: SharedSecret,
|
||||
pub signature: Signature,
|
||||
|
|
@ -111,7 +108,7 @@ impl ServerConnection {
|
|||
ident_parser.recv_bytes(bytes);
|
||||
if let Some(client_identification) = ident_parser.get_peer_ident() {
|
||||
self.packet_transport
|
||||
.queue_send_protocol_info(SERVER_IDENTIFICATION.to_vec());
|
||||
.queue_send_protocol_info(self.config.server_identification.clone());
|
||||
self.state = ServerState::KeyExchangeInit {
|
||||
client_identification,
|
||||
};
|
||||
|
|
@ -311,10 +308,11 @@ impl ServerConnection {
|
|||
*encryption_server_to_client,
|
||||
true,
|
||||
);
|
||||
self.state = ServerState::ServiceRequest { session_ident: *h };
|
||||
self.state = ServerState::ServiceRequest {
|
||||
session_id: SessionId(*h),
|
||||
};
|
||||
}
|
||||
ServerState::ServiceRequest { session_ident } => {
|
||||
// TODO: this should probably move out of here? unsure.
|
||||
ServerState::ServiceRequest { session_id } => {
|
||||
if packet.payload.first() != Some(&numbers::SSH_MSG_SERVICE_REQUEST) {
|
||||
return Err(peer_error!("did not send SSH_MSG_SERVICE_REQUEST"));
|
||||
}
|
||||
|
|
@ -335,7 +333,7 @@ impl ServerConnection {
|
|||
},
|
||||
});
|
||||
self.state = ServerState::Open {
|
||||
session_ident: *session_ident,
|
||||
session_id: *session_id,
|
||||
};
|
||||
}
|
||||
ServerState::Open { .. } => {
|
||||
|
|
@ -346,9 +344,9 @@ impl ServerConnection {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_open(&self) -> Option<[u8; 32]> {
|
||||
pub fn is_open(&self) -> Option<SessionId> {
|
||||
match self.state {
|
||||
ServerState::Open { session_ident } => Some(session_ident),
|
||||
ServerState::Open { session_id } => Some(session_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -365,7 +363,7 @@ impl ServerConnection {
|
|||
..
|
||||
} => Some(KeyExchangeParameters {
|
||||
client_ident: client_identification.clone(),
|
||||
server_ident: SERVER_IDENTIFICATION.to_vec(),
|
||||
server_ident: self.config.server_identification.to_vec(),
|
||||
client_kexinit: client_kexinit.clone(),
|
||||
server_kexinit: server_kexinit.clone(),
|
||||
eph_client_public_key: client_ephemeral_public_key.clone(),
|
||||
|
|
@ -392,7 +390,7 @@ impl ServerConnection {
|
|||
|
||||
self.packet_transport.queue_packet(packet);
|
||||
self.state = ServerState::NewKeys {
|
||||
hash: response.hash,
|
||||
hash: response.hash.0,
|
||||
shared_secret: response.shared_secret.clone(),
|
||||
encryption_client_to_server: *encryption_client_to_server,
|
||||
encryption_server_to_client: *encryption_server_to_client,
|
||||
|
|
@ -437,7 +435,7 @@ pub fn do_key_exchange(
|
|||
);
|
||||
|
||||
Ok(KeyExchangeResponse {
|
||||
hash,
|
||||
hash: SessionId(hash),
|
||||
server_ephemeral_public_key,
|
||||
shared_secret,
|
||||
signature: private.private_key.sign(&hash),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue