mirror of
https://github.com/Noratrieb/cluelessh.git
synced 2026-01-15 17:05:05 +01:00
stuff that kinda works
This commit is contained in:
parent
7696484f0b
commit
9c923e4aa9
7 changed files with 186 additions and 114 deletions
|
|
@ -1,9 +1,9 @@
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
|
|
||||||
use ssh_transport::client_error;
|
|
||||||
use ssh_transport::packet::Packet;
|
use ssh_transport::packet::Packet;
|
||||||
use ssh_transport::Result;
|
use ssh_transport::Result;
|
||||||
|
use ssh_transport::{client_error, numbers};
|
||||||
|
|
||||||
/// A channel number (on our side).
|
/// A channel number (on our side).
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
|
@ -118,7 +118,7 @@ impl ServerChannelsState {
|
||||||
let mut packet = packet.payload_parser();
|
let mut packet = packet.payload_parser();
|
||||||
let packet_type = packet.u8()?;
|
let packet_type = packet.u8()?;
|
||||||
match packet_type {
|
match packet_type {
|
||||||
Packet::SSH_MSG_GLOBAL_REQUEST => {
|
numbers::SSH_MSG_GLOBAL_REQUEST => {
|
||||||
let request_name = packet.utf8_string()?;
|
let request_name = packet.utf8_string()?;
|
||||||
let want_reply = packet.bool()?;
|
let want_reply = packet.bool()?;
|
||||||
debug!(%request_name, %want_reply, "Received global request");
|
debug!(%request_name, %want_reply, "Received global request");
|
||||||
|
|
@ -126,7 +126,7 @@ impl ServerChannelsState {
|
||||||
self.packets_to_send
|
self.packets_to_send
|
||||||
.push_back(Packet::new_msg_request_failure());
|
.push_back(Packet::new_msg_request_failure());
|
||||||
}
|
}
|
||||||
Packet::SSH_MSG_CHANNEL_OPEN => {
|
numbers::SSH_MSG_CHANNEL_OPEN => {
|
||||||
// <https://datatracker.ietf.org/doc/html/rfc4254#section-5.1>
|
// <https://datatracker.ietf.org/doc/html/rfc4254#section-5.1>
|
||||||
let channel_type = packet.utf8_string()?;
|
let channel_type = packet.utf8_string()?;
|
||||||
let sender_channel = packet.u32()?;
|
let sender_channel = packet.u32()?;
|
||||||
|
|
@ -141,7 +141,7 @@ impl ServerChannelsState {
|
||||||
self.packets_to_send
|
self.packets_to_send
|
||||||
.push_back(Packet::new_msg_channel_open_failure(
|
.push_back(Packet::new_msg_channel_open_failure(
|
||||||
sender_channel,
|
sender_channel,
|
||||||
3, // SSH_OPEN_UNKNOWN_CHANNEL_TYPE
|
numbers::SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
|
||||||
b"unknown channel type",
|
b"unknown channel type",
|
||||||
b"",
|
b"",
|
||||||
));
|
));
|
||||||
|
|
@ -178,7 +178,7 @@ impl ServerChannelsState {
|
||||||
|
|
||||||
debug!(%channel_type, %our_number, "Successfully opened channel");
|
debug!(%channel_type, %our_number, "Successfully opened channel");
|
||||||
}
|
}
|
||||||
Packet::SSH_MSG_CHANNEL_DATA => {
|
numbers::SSH_MSG_CHANNEL_DATA => {
|
||||||
let our_channel = packet.u32()?;
|
let our_channel = packet.u32()?;
|
||||||
let our_channel = self.validate_channel(our_channel)?;
|
let our_channel = self.validate_channel(our_channel)?;
|
||||||
let data = packet.string()?;
|
let data = packet.string()?;
|
||||||
|
|
@ -190,7 +190,7 @@ impl ServerChannelsState {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Packet::SSH_MSG_CHANNEL_EOF => {
|
numbers::SSH_MSG_CHANNEL_EOF => {
|
||||||
// <https://datatracker.ietf.org/doc/html/rfc4254#section-5.3>
|
// <https://datatracker.ietf.org/doc/html/rfc4254#section-5.3>
|
||||||
let our_channel = packet.u32()?;
|
let our_channel = packet.u32()?;
|
||||||
let our_channel = self.validate_channel(our_channel)?;
|
let our_channel = self.validate_channel(our_channel)?;
|
||||||
|
|
@ -200,7 +200,7 @@ impl ServerChannelsState {
|
||||||
kind: ChannelUpdateKind::Eof,
|
kind: ChannelUpdateKind::Eof,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Packet::SSH_MSG_CHANNEL_CLOSE => {
|
numbers::SSH_MSG_CHANNEL_CLOSE => {
|
||||||
// <https://datatracker.ietf.org/doc/html/rfc4254#section-5.3>
|
// <https://datatracker.ietf.org/doc/html/rfc4254#section-5.3>
|
||||||
let our_channel = packet.u32()?;
|
let our_channel = packet.u32()?;
|
||||||
let our_channel = self.validate_channel(our_channel)?;
|
let our_channel = self.validate_channel(our_channel)?;
|
||||||
|
|
@ -219,7 +219,7 @@ impl ServerChannelsState {
|
||||||
|
|
||||||
debug!("Channel has been closed");
|
debug!("Channel has been closed");
|
||||||
}
|
}
|
||||||
Packet::SSH_MSG_CHANNEL_REQUEST => {
|
numbers::SSH_MSG_CHANNEL_REQUEST => {
|
||||||
let our_channel = packet.u32()?;
|
let our_channel = packet.u32()?;
|
||||||
let our_channel = self.validate_channel(our_channel)?;
|
let our_channel = self.validate_channel(our_channel)?;
|
||||||
let request_type = packet.utf8_string()?;
|
let request_type = packet.utf8_string()?;
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ impl ServerConnection {
|
||||||
pub mod auth {
|
pub mod auth {
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use ssh_transport::{client_error, packet::Packet, parse::NameList, Result};
|
use ssh_transport::{client_error, numbers, packet::Packet, parse::NameList, Result};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
pub struct BadAuth {
|
pub struct BadAuth {
|
||||||
|
|
@ -104,7 +104,7 @@ pub mod auth {
|
||||||
// It's not very good, but it's good enough for now.
|
// It's not very good, but it's good enough for now.
|
||||||
let mut auth_req = packet.payload_parser();
|
let mut auth_req = packet.payload_parser();
|
||||||
|
|
||||||
if auth_req.u8()? != Packet::SSH_MSG_USERAUTH_REQUEST {
|
if auth_req.u8()? != numbers::SSH_MSG_USERAUTH_REQUEST {
|
||||||
return Err(client_error!("did not send SSH_MSG_SERVICE_REQUEST"));
|
return Err(client_error!("did not send SSH_MSG_SERVICE_REQUEST"));
|
||||||
}
|
}
|
||||||
let username = auth_req.utf8_string()?;
|
let username = auth_req.utf8_string()?;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use aes_gcm::aead::{Aead, AeadMutInPlace};
|
use aes_gcm::aead::AeadMutInPlace;
|
||||||
use chacha20::cipher::{KeyInit, StreamCipher, StreamCipherSeek};
|
use chacha20::cipher::{KeyInit, StreamCipher, StreamCipherSeek};
|
||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
use subtle::ConstantTimeEq;
|
use subtle::ConstantTimeEq;
|
||||||
|
|
@ -231,7 +231,10 @@ impl Session {
|
||||||
algorithm: alg_c2s,
|
algorithm: alg_c2s,
|
||||||
state: {
|
state: {
|
||||||
let mut state = derive_key(k, h, "C", session_id, alg_c2s.key_size);
|
let mut state = derive_key(k, h, "C", session_id, alg_c2s.key_size);
|
||||||
state.extend_from_slice(&derive_key(k, h, "A", session_id, alg_c2s.iv_size));
|
eprintln!("k={state:x?}");
|
||||||
|
let iv = derive_key(k, h, "A", session_id, alg_c2s.iv_size);
|
||||||
|
state.extend_from_slice(&iv);
|
||||||
|
eprintln!("n={iv:x?}");
|
||||||
state
|
state
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -307,16 +310,10 @@ fn derive_key(
|
||||||
key_size: usize,
|
key_size: usize,
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
let sha2len = sha2::Sha256::output_size();
|
let sha2len = sha2::Sha256::output_size();
|
||||||
let mut output = vec![0; key_size];
|
let padded_key_size = key_size.next_multiple_of(sha2len);
|
||||||
|
let mut output = vec![0; padded_key_size];
|
||||||
|
|
||||||
//let mut hash = sha2::Sha256::new();
|
for i in 0..(padded_key_size / sha2len) {
|
||||||
//encode_mpint_for_hash(&k, |data| hash.update(data));
|
|
||||||
//hash.update(h);
|
|
||||||
//hash.update(letter.as_bytes());
|
|
||||||
//hash.update(session_id);
|
|
||||||
//output[..sha2len].copy_from_slice(&hash.finalize());
|
|
||||||
|
|
||||||
for i in 0..(key_size / sha2len) {
|
|
||||||
let mut hash = <sha2::Sha256 as sha2::Digest>::new();
|
let mut hash = <sha2::Sha256 as sha2::Digest>::new();
|
||||||
encode_mpint_for_hash(&k, |data| hash.update(data));
|
encode_mpint_for_hash(&k, |data| hash.update(data));
|
||||||
hash.update(h);
|
hash.update(h);
|
||||||
|
|
@ -331,6 +328,7 @@ fn derive_key(
|
||||||
output[(i * sha2len)..][..sha2len].copy_from_slice(&hash.finalize())
|
output[(i * sha2len)..][..sha2len].copy_from_slice(&hash.finalize())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output.truncate(key_size);
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -492,22 +490,19 @@ impl<'a> Aes256GcmOpenSsh<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt_packet(&mut self, packet: Packet, _packet_number: u64) -> EncryptedPacket {
|
fn encrypt_packet(&mut self, packet: Packet, _packet_number: u64) -> EncryptedPacket {
|
||||||
let bytes = packet.to_bytes(
|
let mut bytes = packet.to_bytes(
|
||||||
false,
|
false,
|
||||||
<aes_gcm::aes::Aes256 as aes_gcm::aes::cipher::BlockSizeUser>::block_size() as u8,
|
<aes_gcm::aes::Aes256 as aes_gcm::aes::cipher::BlockSizeUser>::block_size() as u8,
|
||||||
);
|
);
|
||||||
|
|
||||||
let cipher = aes_gcm::Aes256Gcm::new(&self.key);
|
let mut cipher = aes_gcm::Aes256Gcm::new(&self.key);
|
||||||
|
|
||||||
let bytes = cipher
|
let (aad, plaintext) = bytes.split_at_mut(4);
|
||||||
.encrypt(
|
|
||||||
(&*self.nonce).into(),
|
let tag = cipher
|
||||||
aes_gcm::aead::Payload {
|
.encrypt_in_place_detached((&*self.nonce).into(), aad, plaintext)
|
||||||
aad: &bytes[..4],
|
|
||||||
msg: &bytes[4..],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
bytes.extend_from_slice(&tag);
|
||||||
self.inc_nonce();
|
self.inc_nonce();
|
||||||
|
|
||||||
EncryptedPacket::from_encrypted_full_bytes(bytes)
|
EncryptedPacket::from_encrypted_full_bytes(bytes)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
mod crypto;
|
mod crypto;
|
||||||
|
pub mod numbers;
|
||||||
pub mod packet;
|
pub mod packet;
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
|
|
||||||
|
|
@ -142,35 +143,23 @@ impl ServerConnection {
|
||||||
self.packet_transport.recv_bytes(bytes)?;
|
self.packet_transport.recv_bytes(bytes)?;
|
||||||
|
|
||||||
while let Some(packet) = self.packet_transport.recv_next_packet() {
|
while let Some(packet) = self.packet_transport.recv_next_packet() {
|
||||||
trace!(packet_type = %packet.payload.get(0).unwrap_or(&0xFF), packet_len = %packet.payload.len(), "Received packet");
|
let packet_type = packet.payload.get(0).unwrap_or(&0xFF);
|
||||||
|
let packet_type_string =
|
||||||
|
numbers::packet_type_to_string(*packet_type).unwrap_or("<unknown>");
|
||||||
|
|
||||||
|
trace!(%packet_type, %packet_type_string, packet_len = %packet.payload.len(), "Received packet");
|
||||||
|
|
||||||
// Handle some packets ignoring the state.
|
// Handle some packets ignoring the state.
|
||||||
match packet.payload.get(0).copied() {
|
match packet.payload.get(0).copied() {
|
||||||
Some(Packet::SSH_MSG_DISCONNECT) => {
|
Some(numbers::SSH_MSG_DISCONNECT) => {
|
||||||
// <https://datatracker.ietf.org/doc/html/rfc4253#section-11.1>
|
// <https://datatracker.ietf.org/doc/html/rfc4253#section-11.1>
|
||||||
let mut disconnect = Parser::new(&packet.payload[1..]);
|
let mut disconnect = Parser::new(&packet.payload[1..]);
|
||||||
let reason = disconnect.u32()?;
|
let reason = disconnect.u32()?;
|
||||||
let description = disconnect.utf8_string()?;
|
let description = disconnect.utf8_string()?;
|
||||||
let _language_tag = disconnect.utf8_string()?;
|
let _language_tag = disconnect.utf8_string()?;
|
||||||
|
|
||||||
let reason_string = match reason {
|
let reason_string =
|
||||||
1 => "SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT",
|
numbers::disconnect_reason_to_string(reason).unwrap_or("<unknown>");
|
||||||
2 => "SSH_DISCONNECT_PROTOCOL_ERROR",
|
|
||||||
3 => "SSH_DISCONNECT_KEY_EXCHANGE_FAILED",
|
|
||||||
4 => "SSH_DISCONNECT_RESERVED",
|
|
||||||
5 => "SSH_DISCONNECT_MAC_ERROR",
|
|
||||||
6 => "SSH_DISCONNECT_COMPRESSION_ERROR",
|
|
||||||
7 => "SSH_DISCONNECT_SERVICE_NOT_AVAILABLE",
|
|
||||||
8 => "SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED",
|
|
||||||
9 => "SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE",
|
|
||||||
10 => "SSH_DISCONNECT_CONNECTION_LOST",
|
|
||||||
11 => "SSH_DISCONNECT_BY_APPLICATION",
|
|
||||||
12 => "SSH_DISCONNECT_TOO_MANY_CONNECTIONS",
|
|
||||||
13 => "SSH_DISCONNECT_AUTH_CANCELLED_BY_USER",
|
|
||||||
14 => "SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE",
|
|
||||||
15 => "SSH_DISCONNECT_ILLEGAL_USER_NAME",
|
|
||||||
_ => "<unknown>",
|
|
||||||
};
|
|
||||||
|
|
||||||
info!(%reason, %reason_string, %description, "Client disconnecting");
|
info!(%reason, %reason_string, %description, "Client disconnecting");
|
||||||
|
|
||||||
|
|
@ -368,12 +357,12 @@ impl ServerConnection {
|
||||||
encryption_client_to_server,
|
encryption_client_to_server,
|
||||||
encryption_server_to_client,
|
encryption_server_to_client,
|
||||||
} => {
|
} => {
|
||||||
if packet.payload != [Packet::SSH_MSG_NEWKEYS] {
|
if packet.payload != [numbers::SSH_MSG_NEWKEYS] {
|
||||||
return Err(client_error!("did not send SSH_MSG_NEWKEYS"));
|
return Err(client_error!("did not send SSH_MSG_NEWKEYS"));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.packet_transport.queue_packet(Packet {
|
self.packet_transport.queue_packet(Packet {
|
||||||
payload: vec![Packet::SSH_MSG_NEWKEYS],
|
payload: vec![numbers::SSH_MSG_NEWKEYS],
|
||||||
});
|
});
|
||||||
self.packet_transport.set_key(
|
self.packet_transport.set_key(
|
||||||
*h,
|
*h,
|
||||||
|
|
@ -385,7 +374,7 @@ impl ServerConnection {
|
||||||
}
|
}
|
||||||
ServerState::ServiceRequest => {
|
ServerState::ServiceRequest => {
|
||||||
// TODO: this should probably move out of here? unsure.
|
// TODO: this should probably move out of here? unsure.
|
||||||
if packet.payload.first() != Some(&Packet::SSH_MSG_SERVICE_REQUEST) {
|
if packet.payload.first() != Some(&numbers::SSH_MSG_SERVICE_REQUEST) {
|
||||||
return Err(client_error!("did not send SSH_MSG_SERVICE_REQUEST"));
|
return Err(client_error!("did not send SSH_MSG_SERVICE_REQUEST"));
|
||||||
}
|
}
|
||||||
let mut p = Parser::new(&packet.payload[1..]);
|
let mut p = Parser::new(&packet.payload[1..]);
|
||||||
|
|
@ -399,7 +388,7 @@ impl ServerConnection {
|
||||||
self.packet_transport.queue_packet(Packet {
|
self.packet_transport.queue_packet(Packet {
|
||||||
payload: {
|
payload: {
|
||||||
let mut writer = Writer::new();
|
let mut writer = Writer::new();
|
||||||
writer.u8(Packet::SSH_MSG_SERVICE_ACCEPT);
|
writer.u8(numbers::SSH_MSG_SERVICE_ACCEPT);
|
||||||
writer.string(service.as_bytes());
|
writer.string(service.as_bytes());
|
||||||
writer.finish()
|
writer.finish()
|
||||||
},
|
},
|
||||||
|
|
|
||||||
141
ssh-transport/src/numbers.rs
Normal file
141
ssh-transport/src/numbers.rs
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
//! Constants for SSH.
|
||||||
|
//! <https://datatracker.ietf.org/doc/html/rfc4250>
|
||||||
|
|
||||||
|
// -----
|
||||||
|
// Transport layer protocol:
|
||||||
|
|
||||||
|
// 1 to 19 Transport layer generic (e.g., disconnect, ignore, debug, etc.)
|
||||||
|
pub const SSH_MSG_DISCONNECT: u8 = 1;
|
||||||
|
pub const SSH_MSG_IGNORE: u8 = 2;
|
||||||
|
pub const SSH_MSG_UNIMPLEMENTED: u8 = 3;
|
||||||
|
pub const SSH_MSG_DEBUG: u8 = 4;
|
||||||
|
pub const SSH_MSG_SERVICE_REQUEST: u8 = 5;
|
||||||
|
pub const SSH_MSG_SERVICE_ACCEPT: u8 = 6;
|
||||||
|
|
||||||
|
// 20 to 29 Algorithm negotiation
|
||||||
|
pub const SSH_MSG_KEXINIT: u8 = 20;
|
||||||
|
pub const SSH_MSG_NEWKEYS: u8 = 21;
|
||||||
|
|
||||||
|
// 30 to 49 Key exchange method specific (numbers can be reused for different authentication methods)
|
||||||
|
pub const SSH_MSG_KEXDH_INIT: u8 = 30;
|
||||||
|
pub const SSH_MSG_KEX_ECDH_INIT: u8 = 30; // Same number
|
||||||
|
pub const SSH_MSG_KEXDH_REPLY: u8 = 31;
|
||||||
|
pub const SSH_MSG_KEX_ECDH_REPLY: u8 = 31;
|
||||||
|
|
||||||
|
// -----
|
||||||
|
// User authentication protocol:
|
||||||
|
|
||||||
|
// 50 to 59 User authentication generic
|
||||||
|
pub const SSH_MSG_USERAUTH_REQUEST: u8 = 50;
|
||||||
|
pub const SSH_MSG_USERAUTH_FAILURE: u8 = 51;
|
||||||
|
pub const SSH_MSG_USERAUTH_SUCCESS: u8 = 52;
|
||||||
|
pub const SSH_MSG_USERAUTH_BANNER: u8 = 53;
|
||||||
|
|
||||||
|
// 60 to 79 User authentication method specific (numbers can be reused for different authentication methods)
|
||||||
|
|
||||||
|
// -----
|
||||||
|
// Connection protocol:
|
||||||
|
|
||||||
|
// 80 to 89 Connection protocol generic
|
||||||
|
pub const SSH_MSG_GLOBAL_REQUEST: u8 = 80;
|
||||||
|
pub const SSH_MSG_REQUEST_SUCCESS: u8 = 81;
|
||||||
|
pub const SSH_MSG_REQUEST_FAILURE: u8 = 82;
|
||||||
|
|
||||||
|
// 90 to 127 Channel related messages
|
||||||
|
pub const SSH_MSG_CHANNEL_OPEN: u8 = 90;
|
||||||
|
pub const SSH_MSG_CHANNEL_OPEN_CONFIRMATION: u8 = 91;
|
||||||
|
pub const SSH_MSG_CHANNEL_OPEN_FAILURE: u8 = 92;
|
||||||
|
pub const SSH_MSG_CHANNEL_WINDOW_ADJUST: u8 = 93;
|
||||||
|
pub const SSH_MSG_CHANNEL_DATA: u8 = 94;
|
||||||
|
pub const SSH_MSG_CHANNEL_EXTENDED_DATA: u8 = 95;
|
||||||
|
pub const SSH_MSG_CHANNEL_EOF: u8 = 96;
|
||||||
|
pub const SSH_MSG_CHANNEL_CLOSE: u8 = 97;
|
||||||
|
pub const SSH_MSG_CHANNEL_REQUEST: u8 = 98;
|
||||||
|
pub const SSH_MSG_CHANNEL_SUCCESS: u8 = 99;
|
||||||
|
pub const SSH_MSG_CHANNEL_FAILURE: u8 = 100;
|
||||||
|
|
||||||
|
pub fn packet_type_to_string(packet_type: u8) -> Option<&'static str> {
|
||||||
|
Some(match packet_type {
|
||||||
|
1 => "SSH_MSG_DISCONNECT",
|
||||||
|
2 => "SSH_MSG_IGNORE",
|
||||||
|
3 => "SSH_MSG_UNIMPLEMENTED",
|
||||||
|
4 => "SSH_MSG_DEBUG",
|
||||||
|
5 => "SSH_MSG_SERVICE_REQUEST",
|
||||||
|
6 => "SSH_MSG_SERVICE_ACCEPT",
|
||||||
|
20 => "SSH_MSG_KEXINIT",
|
||||||
|
21 => "SSH_MSG_NEWKEYS",
|
||||||
|
50 => "SSH_MSG_USERAUTH_REQUEST",
|
||||||
|
51 => "SSH_MSG_USERAUTH_FAILURE",
|
||||||
|
52 => "SSH_MSG_USERAUTH_SUCCESS",
|
||||||
|
53 => "SSH_MSG_USERAUTH_BANNER",
|
||||||
|
80 => "SSH_MSG_GLOBAL_REQUEST",
|
||||||
|
81 => "SSH_MSG_REQUEST_SUCCESS",
|
||||||
|
82 => "SSH_MSG_REQUEST_FAILURE",
|
||||||
|
90 => "SSH_MSG_CHANNEL_OPEN",
|
||||||
|
91 => "SSH_MSG_CHANNEL_OPEN_CONFIRMATION",
|
||||||
|
92 => "SSH_MSG_CHANNEL_OPEN_FAILURE",
|
||||||
|
93 => "SSH_MSG_CHANNEL_WINDOW_ADJUST",
|
||||||
|
94 => "SSH_MSG_CHANNEL_DATA",
|
||||||
|
95 => "SSH_MSG_CHANNEL_EXTENDED_DATA",
|
||||||
|
96 => "SSH_MSG_CHANNEL_EOF",
|
||||||
|
97 => "SSH_MSG_CHANNEL_CLOSE",
|
||||||
|
98 => "SSH_MSG_CHANNEL_REQUEST",
|
||||||
|
99 => "SSH_MSG_CHANNEL_SUCCESS",
|
||||||
|
100 => "SSH_MSG_CHANNEL_FAILURE",
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT: u32 = 1;
|
||||||
|
pub const SSH_DISCONNECT_PROTOCOL_ERROR: u32 = 2;
|
||||||
|
pub const SSH_DISCONNECT_KEY_EXCHANGE_FAILED: u32 = 3;
|
||||||
|
pub const SSH_DISCONNECT_RESERVED: u32 = 4;
|
||||||
|
pub const SSH_DISCONNECT_MAC_ERROR: u32 = 5;
|
||||||
|
pub const SSH_DISCONNECT_COMPRESSION_ERROR: u32 = 6;
|
||||||
|
pub const SSH_DISCONNECT_SERVICE_NOT_AVAILABLE: u32 = 7;
|
||||||
|
pub const SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED: u32 = 8;
|
||||||
|
pub const SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE: u32 = 9;
|
||||||
|
pub const SSH_DISCONNECT_CONNECTION_LOST: u32 = 10;
|
||||||
|
pub const SSH_DISCONNECT_BY_APPLICATION: u32 = 11;
|
||||||
|
pub const SSH_DISCONNECT_TOO_MANY_CONNECTIONS: u32 = 12;
|
||||||
|
pub const SSH_DISCONNECT_AUTH_CANCELLED_BY_USER: u32 = 13;
|
||||||
|
pub const SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE: u32 = 14;
|
||||||
|
pub const SSH_DISCONNECT_ILLEGAL_USER_NAME: u32 = 15;
|
||||||
|
|
||||||
|
pub fn disconnect_reason_to_string(reason: u32) -> Option<&'static str> {
|
||||||
|
Some(match reason {
|
||||||
|
1 => "SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT",
|
||||||
|
2 => "SSH_DISCONNECT_PROTOCOL_ERROR",
|
||||||
|
3 => "SSH_DISCONNECT_KEY_EXCHANGE_FAILED",
|
||||||
|
4 => "SSH_DISCONNECT_RESERVED",
|
||||||
|
5 => "SSH_DISCONNECT_MAC_ERROR",
|
||||||
|
6 => "SSH_DISCONNECT_COMPRESSION_ERROR",
|
||||||
|
7 => "SSH_DISCONNECT_SERVICE_NOT_AVAILABLE",
|
||||||
|
8 => "SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED",
|
||||||
|
9 => "SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE",
|
||||||
|
10 => "SSH_DISCONNECT_CONNECTION_LOST",
|
||||||
|
11 => "SSH_DISCONNECT_BY_APPLICATION",
|
||||||
|
12 => "SSH_DISCONNECT_TOO_MANY_CONNECTIONS",
|
||||||
|
13 => "SSH_DISCONNECT_AUTH_CANCELLED_BY_USER",
|
||||||
|
14 => "SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE",
|
||||||
|
15 => "SSH_DISCONNECT_ILLEGAL_USER_NAME",
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SSH_OPEN_ADMINISTRATIVELY_PROHIBITED: u32 = 1;
|
||||||
|
pub const SSH_OPEN_CONNECT_FAILED: u32 = 2;
|
||||||
|
pub const SSH_OPEN_UNKNOWN_CHANNEL_TYPE: u32 = 3;
|
||||||
|
pub const SSH_OPEN_RESOURCE_SHORTAGE: u32 = 4;
|
||||||
|
|
||||||
|
pub fn channel_connection_failure_to_string(reason: u32) -> Option<&'static str> {
|
||||||
|
Some(match reason {
|
||||||
|
1 => "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED",
|
||||||
|
2 => "SSH_OPEN_CONNECT_FAILED",
|
||||||
|
3 => "SSH_OPEN_UNKNOWN_CHANNEL_TYPE",
|
||||||
|
4 => "SSH_OPEN_RESOURCE_SHORTAGE",
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SSH_EXTENDED_DATA_STDERR: u32 = 1;
|
||||||
|
|
@ -2,10 +2,10 @@ mod ctors;
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use crate::client_error;
|
|
||||||
use crate::crypto::{EncryptionAlgorithm, Keys, Plaintext, Session};
|
use crate::crypto::{EncryptionAlgorithm, Keys, Plaintext, Session};
|
||||||
use crate::parse::{NameList, Parser, Writer};
|
use crate::parse::{NameList, Parser, Writer};
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
use crate::{client_error, numbers};
|
||||||
|
|
||||||
/// Frames the byte stream into packets.
|
/// Frames the byte stream into packets.
|
||||||
pub(crate) struct PacketTransport {
|
pub(crate) struct PacketTransport {
|
||||||
|
|
@ -146,59 +146,6 @@ pub struct Packet {
|
||||||
pub payload: Vec<u8>,
|
pub payload: Vec<u8>,
|
||||||
}
|
}
|
||||||
impl Packet {
|
impl Packet {
|
||||||
// -----
|
|
||||||
// Transport layer protocol:
|
|
||||||
|
|
||||||
// 1 to 19 Transport layer generic (e.g., disconnect, ignore, debug, etc.)
|
|
||||||
pub const SSH_MSG_DISCONNECT: u8 = 1;
|
|
||||||
pub const SSH_MSG_IGNORE: u8 = 2;
|
|
||||||
pub const SSH_MSG_UNIMPLEMENTED: u8 = 3;
|
|
||||||
pub const SSH_MSG_DEBUG: u8 = 4;
|
|
||||||
pub const SSH_MSG_SERVICE_REQUEST: u8 = 5;
|
|
||||||
pub const SSH_MSG_SERVICE_ACCEPT: u8 = 6;
|
|
||||||
|
|
||||||
// 20 to 29 Algorithm negotiation
|
|
||||||
pub const SSH_MSG_KEXINIT: u8 = 20;
|
|
||||||
pub const SSH_MSG_NEWKEYS: u8 = 21;
|
|
||||||
|
|
||||||
// 30 to 49 Key exchange method specific (numbers can be reused for different authentication methods)
|
|
||||||
pub const SSH_MSG_KEXDH_INIT: u8 = 30;
|
|
||||||
pub const SSH_MSG_KEX_ECDH_INIT: u8 = 30; // Same number
|
|
||||||
pub const SSH_MSG_KEXDH_REPLY: u8 = 31;
|
|
||||||
pub const SSH_MSG_KEX_ECDH_REPLY: u8 = 31;
|
|
||||||
|
|
||||||
// -----
|
|
||||||
// User authentication protocol:
|
|
||||||
|
|
||||||
// 50 to 59 User authentication generic
|
|
||||||
pub const SSH_MSG_USERAUTH_REQUEST: u8 = 50;
|
|
||||||
pub const SSH_MSG_USERAUTH_FAILURE: u8 = 51;
|
|
||||||
pub const SSH_MSG_USERAUTH_SUCCESS: u8 = 52;
|
|
||||||
pub const SSH_MSG_USERAUTH_BANNER: u8 = 53;
|
|
||||||
|
|
||||||
// 60 to 79 User authentication method specific (numbers can be reused for different authentication methods)
|
|
||||||
|
|
||||||
// -----
|
|
||||||
// Connection protocol:
|
|
||||||
|
|
||||||
// 80 to 89 Connection protocol generic
|
|
||||||
pub const SSH_MSG_GLOBAL_REQUEST: u8 = 80;
|
|
||||||
pub const SSH_MSG_REQUEST_SUCCESS: u8 = 81;
|
|
||||||
pub const SSH_MSG_REQUEST_FAILURE: u8 = 82;
|
|
||||||
|
|
||||||
// 90 to 127 Channel related messages
|
|
||||||
pub const SSH_MSG_CHANNEL_OPEN: u8 = 90;
|
|
||||||
pub const SSH_MSG_CHANNEL_OPEN_CONFIRMATION: u8 = 91;
|
|
||||||
pub const SSH_MSG_CHANNEL_OPEN_FAILURE: u8 = 92;
|
|
||||||
pub const SSH_MSG_CHANNEL_WINDOW_ADJUST: u8 = 93;
|
|
||||||
pub const SSH_MSG_CHANNEL_DATA: u8 = 94;
|
|
||||||
pub const SSH_MSG_CHANNEL_EXTENDED_DATA: u8 = 95;
|
|
||||||
pub const SSH_MSG_CHANNEL_EOF: u8 = 96;
|
|
||||||
pub const SSH_MSG_CHANNEL_CLOSE: u8 = 97;
|
|
||||||
pub const SSH_MSG_CHANNEL_REQUEST: u8 = 98;
|
|
||||||
pub const SSH_MSG_CHANNEL_SUCCESS: u8 = 99;
|
|
||||||
pub const SSH_MSG_CHANNEL_FAILURE: u8 = 100;
|
|
||||||
|
|
||||||
pub const DEFAULT_BLOCK_SIZE: u8 = 8;
|
pub const DEFAULT_BLOCK_SIZE: u8 = 8;
|
||||||
|
|
||||||
pub(crate) fn from_full(bytes: &[u8]) -> Result<Self> {
|
pub(crate) fn from_full(bytes: &[u8]) -> Result<Self> {
|
||||||
|
|
@ -291,7 +238,7 @@ impl<'a> KeyExchangeInitPacket<'a> {
|
||||||
let mut c = Parser::new(payload);
|
let mut c = Parser::new(payload);
|
||||||
|
|
||||||
let kind = c.u8()?;
|
let kind = c.u8()?;
|
||||||
if kind != Packet::SSH_MSG_KEXINIT {
|
if kind != numbers::SSH_MSG_KEXINIT {
|
||||||
return Err(client_error!(
|
return Err(client_error!(
|
||||||
"expected SSH_MSG_KEXINIT packet, found {kind}"
|
"expected SSH_MSG_KEXINIT packet, found {kind}"
|
||||||
));
|
));
|
||||||
|
|
@ -332,7 +279,7 @@ impl<'a> KeyExchangeInitPacket<'a> {
|
||||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||||
let mut data = Writer::new();
|
let mut data = Writer::new();
|
||||||
|
|
||||||
data.u8(Packet::SSH_MSG_KEXINIT);
|
data.u8(numbers::SSH_MSG_KEXINIT);
|
||||||
data.write(&self.cookie);
|
data.write(&self.cookie);
|
||||||
data.name_list(self.kex_algorithms);
|
data.name_list(self.kex_algorithms);
|
||||||
data.name_list(self.server_host_key_algorithms);
|
data.name_list(self.server_host_key_algorithms);
|
||||||
|
|
@ -360,7 +307,7 @@ impl<'a> KeyExchangeEcDhInitPacket<'a> {
|
||||||
let mut c = Parser::new(payload);
|
let mut c = Parser::new(payload);
|
||||||
|
|
||||||
let kind = c.u8()?;
|
let kind = c.u8()?;
|
||||||
if kind != Packet::SSH_MSG_KEX_ECDH_INIT {
|
if kind != numbers::SSH_MSG_KEX_ECDH_INIT {
|
||||||
return Err(client_error!(
|
return Err(client_error!(
|
||||||
"expected SSH_MSG_KEXDH_INIT packet, found {kind}"
|
"expected SSH_MSG_KEXDH_INIT packet, found {kind}"
|
||||||
));
|
));
|
||||||
|
|
@ -404,7 +351,7 @@ impl<'a> DhKeyExchangeInitReplyPacket<'a> {
|
||||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||||
let mut data = Writer::new();
|
let mut data = Writer::new();
|
||||||
|
|
||||||
data.u8(Packet::SSH_MSG_KEX_ECDH_REPLY);
|
data.u8(numbers::SSH_MSG_KEX_ECDH_REPLY);
|
||||||
data.write(&self.public_host_key.to_bytes());
|
data.write(&self.public_host_key.to_bytes());
|
||||||
data.string(self.ephemeral_public_key);
|
data.string(self.ephemeral_public_key);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ macro_rules! ctors {
|
||||||
) -> Packet {
|
) -> Packet {
|
||||||
let mut w = Writer::new();
|
let mut w = Writer::new();
|
||||||
|
|
||||||
w.u8(Packet::$msg_type);
|
w.u8($crate::numbers::$msg_type);
|
||||||
|
|
||||||
$(
|
$(
|
||||||
w.$ssh_type($name);
|
w.$ssh_type($name);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue