factor out auth

This commit is contained in:
nora 2024-08-25 16:14:55 +02:00
parent b0acf03502
commit 1c346659f6
7 changed files with 267 additions and 156 deletions

View file

@ -1,6 +1,6 @@
use std::{net::SocketAddr, sync::Arc}; use std::{net::SocketAddr, sync::Arc};
use cluelessh_tokio::Channel; use cluelessh_tokio::{server::ServerAuthVerify, Channel};
use eyre::{Context, Result}; use eyre::{Context, Result};
use tokio::{ use tokio::{
net::{TcpListener, TcpStream}, net::{TcpListener, TcpStream},
@ -37,7 +37,19 @@ async fn main() -> eyre::Result<()> {
let listener = TcpListener::bind(addr).await.wrap_err("binding listener")?; let listener = TcpListener::bind(addr).await.wrap_err("binding listener")?;
let mut listener = cluelessh_tokio::server::ServerListener::new(listener); let auth_verify = ServerAuthVerify {
verify_password: Some(Arc::new(|auth| {
Box::pin(async move {
info!(password = %auth.password, "Got password");
// Don't worry queen, your password is correct!
Ok(())
})
})),
verify_pubkey: None,
};
let mut listener = cluelessh_tokio::server::ServerListener::new(listener, auth_verify);
loop { loop {
let next = listener.accept().await?; let next = listener.accept().await?;

View file

@ -2,9 +2,10 @@ use std::{collections::HashSet, sync::Arc};
use clap::Parser; use clap::Parser;
use eyre::{bail, Context, ContextCompat, OptionExt, Result}; use cluelessh_tokio::client::SignatureResult;
use cluelessh_tokio::client::{PendingChannel, SignatureResult}; use cluelessh_tokio::PendingChannel;
use cluelessh_transport::{key::PublicKey, numbers, parse::Writer}; use cluelessh_transport::{key::PublicKey, numbers, parse::Writer};
use eyre::{bail, Context, ContextCompat, OptionExt, Result};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tracing::{debug, error}; use tracing::{debug, error};
@ -140,7 +141,7 @@ async fn main_channel(channel: PendingChannel) -> Result<()> {
}; };
channel channel
.send_operation(ChannelOperationKind::Request(ChannelRequest::PtyReq { .send(ChannelOperationKind::Request(ChannelRequest::PtyReq {
want_reply: true, want_reply: true,
term: "xterm-256color".to_owned(), term: "xterm-256color".to_owned(),
width_chars: 70, width_chars: 70,

View file

@ -1,5 +1,8 @@
use core::panic;
use std::collections::HashSet;
use std::mem; use std::mem;
use auth::AuthOption;
pub use cluelessh_connection as connection; pub use cluelessh_connection as connection;
use cluelessh_connection::ChannelOperation; use cluelessh_connection::ChannelOperation;
pub use cluelessh_connection::{ChannelUpdate, ChannelUpdateKind}; pub use cluelessh_connection::{ChannelUpdate, ChannelUpdateKind};
@ -21,33 +24,42 @@ pub struct ServerConnection {
} }
enum ServerConnectionState { enum ServerConnectionState {
Auth(auth::BadAuth), Setup(HashSet<AuthOption>),
Auth(auth::ServerAuth),
Open(cluelessh_connection::ChannelsState), Open(cluelessh_connection::ChannelsState),
} }
impl ServerConnection { impl ServerConnection {
pub fn new(transport: cluelessh_transport::server::ServerConnection) -> Self { pub fn new(
transport: cluelessh_transport::server::ServerConnection,
auth_options: HashSet<AuthOption>,
) -> Self {
Self { Self {
transport, transport,
state: ServerConnectionState::Auth(auth::BadAuth::new()), state: ServerConnectionState::Setup(auth_options),
} }
} }
pub fn recv_bytes(&mut self, bytes: &[u8]) -> Result<()> { pub fn recv_bytes(&mut self, bytes: &[u8]) -> Result<()> {
self.transport.recv_bytes(bytes)?; self.transport.recv_bytes(bytes)?;
if let ServerConnectionState::Setup(options) = &mut self.state {
if let Some(session_ident) = self.transport.is_open() {
self.state = ServerConnectionState::Auth(auth::ServerAuth::new(
mem::take(options),
session_ident,
));
}
}
while let Some(packet) = self.transport.next_plaintext_packet() { while let Some(packet) = self.transport.next_plaintext_packet() {
match &mut self.state { match &mut self.state {
ServerConnectionState::Setup(_) => unreachable!(),
ServerConnectionState::Auth(auth) => { ServerConnectionState::Auth(auth) => {
auth.recv_packet(packet)?; auth.recv_packet(packet)?;
for to_send in auth.packets_to_send() { for to_send in auth.packets_to_send() {
self.transport.send_plaintext_packet(to_send); self.transport.send_plaintext_packet(to_send);
} }
if auth.is_authenticated() {
self.state = ServerConnectionState::Open(
cluelessh_connection::ChannelsState::new(true),
);
}
} }
ServerConnectionState::Open(con) => { ServerConnectionState::Open(con) => {
con.recv_packet(packet)?; con.recv_packet(packet)?;
@ -66,14 +78,16 @@ impl ServerConnection {
pub fn next_channel_update(&mut self) -> Option<cluelessh_connection::ChannelUpdate> { pub fn next_channel_update(&mut self) -> Option<cluelessh_connection::ChannelUpdate> {
match &mut self.state { match &mut self.state {
ServerConnectionState::Auth(_) => None, ServerConnectionState::Setup(_) | ServerConnectionState::Auth(_) => None,
ServerConnectionState::Open(con) => con.next_channel_update(), ServerConnectionState::Open(con) => con.next_channel_update(),
} }
} }
pub fn do_operation(&mut self, op: ChannelOperation) { pub fn do_operation(&mut self, op: ChannelOperation) {
match &mut self.state { match &mut self.state {
ServerConnectionState::Auth(_) => panic!("tried to get connection during auth"), ServerConnectionState::Setup(_) | ServerConnectionState::Auth(_) => {
panic!("tried to get connection before it is ready")
}
ServerConnectionState::Open(con) => { ServerConnectionState::Open(con) => {
con.do_operation(op); con.do_operation(op);
self.progress(); self.progress();
@ -83,10 +97,15 @@ impl ServerConnection {
pub fn progress(&mut self) { pub fn progress(&mut self) {
match &mut self.state { match &mut self.state {
ServerConnectionState::Setup(_) => {}
ServerConnectionState::Auth(auth) => { ServerConnectionState::Auth(auth) => {
for to_send in auth.packets_to_send() { for to_send in auth.packets_to_send() {
self.transport.send_plaintext_packet(to_send); self.transport.send_plaintext_packet(to_send);
} }
if auth.is_authenticated() {
self.state =
ServerConnectionState::Open(cluelessh_connection::ChannelsState::new(true));
}
} }
ServerConnectionState::Open(con) => { ServerConnectionState::Open(con) => {
for to_send in con.packets_to_send() { for to_send in con.packets_to_send() {
@ -103,7 +122,7 @@ impl ServerConnection {
} }
} }
pub fn auth(&mut self) -> Option<&mut auth::BadAuth> { pub fn auth(&mut self) -> Option<&mut auth::ServerAuth> {
match &mut self.state { match &mut self.state {
ServerConnectionState::Auth(auth) => Some(auth), ServerConnectionState::Auth(auth) => Some(auth),
_ => None, _ => None,
@ -140,11 +159,10 @@ impl ClientConnection {
if let Some(session_ident) = self.transport.is_open() { if let Some(session_ident) = self.transport.is_open() {
let mut auth = mem::take(auth).unwrap(); let mut auth = mem::take(auth).unwrap();
auth.set_session_identifier(session_ident); auth.set_session_identifier(session_ident);
for to_send in auth.packets_to_send() {
self.transport.send_plaintext_packet(to_send);
}
debug!("Connection has been opened"); debug!("Connection has been opened");
self.state = ClientConnectionState::Auth(auth); self.state = ClientConnectionState::Auth(auth);
self.progress();
} }
} }
@ -235,35 +253,53 @@ impl ClientConnection {
/// <https://datatracker.ietf.org/doc/html/rfc4252> /// <https://datatracker.ietf.org/doc/html/rfc4252>
pub mod auth { pub mod auth {
use std::collections::VecDeque; use std::collections::{HashSet, VecDeque};
use cluelessh_transport::{numbers, packet::Packet, parse::NameList, peer_error, Result}; use cluelessh_transport::{numbers, packet::Packet, parse::NameList, peer_error, Result};
use tracing::{debug, info}; use tracing::{debug, info};
pub struct BadAuth { pub struct ServerAuth {
has_failed: bool, has_failed: bool,
packets_to_send: VecDeque<Packet>, packets_to_send: VecDeque<Packet>,
is_authenticated: bool, is_authenticated: bool,
options: HashSet<AuthOption>,
server_requests: VecDeque<ServerRequest>,
session_ident: [u8; 32],
} }
pub enum ServerRequest { pub enum ServerRequest {
VerifyPassword { VerifyPassword(VerifyPassword),
user: String, VerifyPubkey(VerifyPubkey),
password: String,
},
VerifyPubkey {
session_identifier: [u8; 32],
user: String,
pubkey: Vec<u8>,
},
} }
impl BadAuth { pub struct VerifyPassword {
pub fn new() -> Self { pub user: String,
pub password: String,
}
pub struct VerifyPubkey {
pub user: String,
pub session_identifier: [u8; 32],
pub pubkey_alg_name: Vec<u8>,
pub pubkey: Vec<u8>,
pub signature: Vec<u8>,
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum AuthOption {
Password,
PublicKey,
}
impl ServerAuth {
pub fn new(options: HashSet<AuthOption>, session_ident: [u8; 32]) -> Self {
Self { Self {
has_failed: false, has_failed: false,
packets_to_send: VecDeque::new(), packets_to_send: VecDeque::new(),
options,
is_authenticated: false, is_authenticated: false,
session_ident,
server_requests: VecDeque::new(),
} }
} }
@ -274,14 +310,14 @@ pub mod auth {
// We ask for a public key, and always let that one pass. // We ask for a public key, and always let that one pass.
// The reason for this is that this makes it a lot easier to test locally. // The reason for this is that this makes it a lot easier to test locally.
// 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 p = packet.payload_parser();
if auth_req.u8()? != numbers::SSH_MSG_USERAUTH_REQUEST { if p.u8()? != numbers::SSH_MSG_USERAUTH_REQUEST {
return Err(peer_error!("did not send SSH_MSG_SERVICE_REQUEST")); return Err(peer_error!("did not send SSH_MSG_SERVICE_REQUEST"));
} }
let username = auth_req.utf8_string()?; let username = p.utf8_string()?;
let service_name = auth_req.utf8_string()?; let service_name = p.utf8_string()?;
let method_name = auth_req.utf8_string()?; let method_name = p.utf8_string()?;
if method_name != "none" { if method_name != "none" {
info!( info!(
@ -300,22 +336,47 @@ pub mod auth {
match method_name { match method_name {
"password" => { "password" => {
let change_password = auth_req.bool()?; if !self.options.contains(&AuthOption::Password) {
self.has_failed = true;
self.send_failure();
}
let change_password = p.bool()?;
if change_password { if change_password {
return Err(peer_error!("client tried to change password unprompted")); return Err(peer_error!("client tried to change password unprompted"));
} }
let password = auth_req.utf8_string()?; let password = p.utf8_string()?;
info!(%password, "Got password"); self.server_requests
// Don't worry queen, your password is correct! .push_back(ServerRequest::VerifyPassword(VerifyPassword {
self.queue_packet(Packet::new_msg_userauth_success()); user: username.to_owned(),
self.is_authenticated = true; password: password.to_owned(),
}));
} }
"publickey" => { "publickey" => {
info!("Got public key"); if !self.options.contains(&AuthOption::PublicKey) {
// Don't worry queen, your key is correct! self.has_failed = true;
self.queue_packet(Packet::new_msg_userauth_success()); self.send_failure();
self.is_authenticated = true; }
// Whether the client is just checking whether the public key is allowed.
let is_check = p.bool()?;
if is_check {
todo!();
}
let pubkey_alg_name = p.string()?;
let public_key_blob = p.string()?;
let signature = p.string()?;
self.server_requests
.push_back(ServerRequest::VerifyPubkey(VerifyPubkey {
user: username.to_owned(),
session_identifier: self.session_ident,
pubkey_alg_name: pubkey_alg_name.to_vec(),
pubkey: public_key_blob.to_vec(),
signature: signature.to_vec(),
}));
} }
_ if self.has_failed => { _ if self.has_failed => {
return Err(peer_error!( return Err(peer_error!(
@ -323,8 +384,7 @@ pub mod auth {
)); ));
} }
_ => { _ => {
// Initial. // Initial:
self.queue_packet(Packet::new_msg_userauth_banner( self.queue_packet(Packet::new_msg_userauth_banner(
b"!! this system ONLY allows catgirls to enter !!\r\n\ b"!! this system ONLY allows catgirls to enter !!\r\n\
!! all other attempts WILL be prosecuted to the full extent of the rawr !!\r\n\ !! all other attempts WILL be prosecuted to the full extent of the rawr !!\r\n\
@ -333,16 +393,23 @@ pub mod auth {
b"", b"",
)); ));
self.queue_packet(Packet::new_msg_userauth_failure( self.send_failure();
NameList::one("password"),
false,
));
// Stay in the same state // Stay in the same state
} }
} }
Ok(()) Ok(())
} }
pub fn verification_result(&mut self, is_ok: bool) {
if is_ok {
self.queue_packet(Packet::new_msg_userauth_success());
self.is_authenticated = true;
} else {
self.send_failure();
self.has_failed = true;
}
}
pub fn packets_to_send(&mut self) -> impl Iterator<Item = Packet> + '_ { pub fn packets_to_send(&mut self) -> impl Iterator<Item = Packet> + '_ {
self.packets_to_send.drain(..) self.packets_to_send.drain(..)
} }
@ -352,7 +419,25 @@ pub mod auth {
} }
pub fn server_requests(&mut self) -> impl Iterator<Item = ServerRequest> + '_ { pub fn server_requests(&mut self) -> impl Iterator<Item = ServerRequest> + '_ {
[].into_iter() self.server_requests.drain(..)
}
fn send_failure(&mut self) {
self.queue_packet(Packet::new_msg_userauth_failure(
NameList(&self.option_list()),
false,
));
}
fn option_list(&self) -> String {
self.options
.iter()
.map(|op| match op {
AuthOption::Password => "password",
AuthOption::PublicKey => "publickey",
})
.collect::<Vec<&str>>()
.join(",")
} }
fn queue_packet(&mut self, packet: Packet) { fn queue_packet(&mut self, packet: Packet) {

View file

@ -1,4 +1,4 @@
use cluelessh_connection::{ChannelKind, ChannelNumber, ChannelOperation, ChannelOperationKind}; use cluelessh_connection::{ChannelKind, ChannelNumber, ChannelOperation};
use std::{collections::HashMap, pin::Pin, sync::Arc}; use std::{collections::HashMap, pin::Pin, sync::Arc};
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
@ -8,7 +8,7 @@ use futures::future::BoxFuture;
use tokio::io::{AsyncRead, AsyncWrite}; use tokio::io::{AsyncRead, AsyncWrite};
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
use crate::Channel; use crate::{Channel, ChannelState, PendingChannel};
pub struct ClientConnection<S> { pub struct ClientConnection<S> {
stream: Pin<Box<S>>, stream: Pin<Box<S>>,
@ -27,14 +27,6 @@ pub struct ClientConnection<S> {
auth: ClientAuth, auth: ClientAuth,
} }
enum ChannelState {
Pending {
ready_send: tokio::sync::oneshot::Sender<Result<(), String>>,
updates_send: tokio::sync::mpsc::Sender<ChannelUpdateKind>,
},
Ready(tokio::sync::mpsc::Sender<ChannelUpdateKind>),
}
pub struct ClientAuth { pub struct ClientAuth {
pub username: String, pub username: String,
pub prompt_password: Arc<dyn Fn() -> BoxFuture<'static, Result<String>> + Send + Sync>, pub prompt_password: Arc<dyn Fn() -> BoxFuture<'static, Result<String>> + Send + Sync>,
@ -53,11 +45,6 @@ pub struct SignatureResult {
pub signature: Vec<u8>, pub signature: Vec<u8>,
} }
pub struct PendingChannel {
ready_recv: tokio::sync::oneshot::Receiver<Result<(), String>>,
channel: Channel,
}
impl<S: AsyncRead + AsyncWrite> ClientConnection<S> { impl<S: AsyncRead + AsyncWrite> ClientConnection<S> {
pub async fn connect(stream: S, auth: ClientAuth) -> Result<Self> { pub async fn connect(stream: S, auth: ClientAuth) -> Result<Self> {
let (operations_send, operations_recv) = tokio::sync::mpsc::channel(15); let (operations_send, operations_recv) = tokio::sync::mpsc::channel(15);
@ -272,22 +259,3 @@ impl<S: AsyncRead + AsyncWrite> ClientConnection<S> {
} }
} }
} }
impl PendingChannel {
pub async fn wait_ready(self) -> Result<Channel, Option<String>> {
match self.ready_recv.await {
Ok(Ok(())) => Ok(self.channel),
Ok(Err(err)) => Err(Some(err)),
Err(_) => Err(None),
}
}
}
impl Channel {
pub async fn send_operation(&mut self, op: ChannelOperationKind) -> Result<()> {
self.ops_send
.send(self.number.construct_op(op))
.await
.map_err(Into::into)
}
}

View file

@ -31,3 +31,25 @@ impl Channel {
&self.kind &self.kind
} }
} }
enum ChannelState {
Pending {
ready_send: tokio::sync::oneshot::Sender<Result<(), String>>,
updates_send: tokio::sync::mpsc::Sender<ChannelUpdateKind>,
},
Ready(tokio::sync::mpsc::Sender<ChannelUpdateKind>),
}
pub struct PendingChannel {
ready_recv: tokio::sync::oneshot::Receiver<Result<(), String>>,
channel: Channel,
}
impl PendingChannel {
pub async fn wait_ready(self) -> Result<Channel, Option<String>> {
match self.ready_recv.await {
Ok(Ok(())) => Ok(self.channel),
Ok(Err(err)) => Err(Some(err)),
Err(_) => Err(None),
}
}
}

View file

@ -1,24 +1,30 @@
use cluelessh_connection::{ChannelKind, ChannelNumber, ChannelOperation}; use cluelessh_connection::{ChannelKind, ChannelNumber, ChannelOperation};
use futures::future::BoxFuture;
use std::{ use std::{
collections::{HashMap, VecDeque}, collections::{HashMap, HashSet, VecDeque},
net::SocketAddr, net::SocketAddr,
pin::Pin, pin::Pin,
sync::Arc,
}; };
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
net::{TcpListener, TcpStream}, net::{TcpListener, TcpStream},
}; };
use cluelessh_protocol::{ChannelUpdateKind, SshStatus}; use cluelessh_protocol::{
use eyre::{eyre, ContextCompat, Result, WrapErr}; auth::{AuthOption, VerifyPassword, VerifyPubkey},
ChannelUpdateKind, SshStatus,
};
use eyre::{eyre, ContextCompat, OptionExt, Result, WrapErr};
use tokio::io::{AsyncRead, AsyncWrite}; use tokio::io::{AsyncRead, AsyncWrite};
use tracing::info; use tracing::info;
use crate::Channel; use crate::{Channel, ChannelState, PendingChannel};
pub struct ServerListener { pub struct ServerListener {
listener: TcpListener, listener: TcpListener,
// todo ratelimits etc auth_verify: ServerAuthVerify,
// TODO ratelimits etc
} }
pub struct ServerConnection<S> { pub struct ServerConnection<S> {
@ -38,38 +44,27 @@ pub struct ServerConnection<S> {
/// New channels opened by the peer. /// New channels opened by the peer.
new_channels: VecDeque<Channel>, new_channels: VecDeque<Channel>,
}
enum ChannelState { auth_verify: ServerAuthVerify,
Pending {
ready_send: tokio::sync::oneshot::Sender<Result<(), String>>,
updates_send: tokio::sync::mpsc::Sender<ChannelUpdateKind>,
},
Ready(tokio::sync::mpsc::Sender<ChannelUpdateKind>),
} }
enum Operation { enum Operation {
VerifyPassword { VerifyPassword(Result<()>),
user: String, VerifyPubkey(Result<()>),
password: String,
},
VerifyPubkey {
session_identifier: [u8; 32],
user: String,
pubkey: Vec<u8>,
},
} }
pub struct SignatureResult { #[derive(Clone)]
pub key_alg_name: &'static str, pub struct ServerAuthVerify {
pub public_key: Vec<u8>, pub verify_password:
pub signature: Vec<u8>, Option<Arc<dyn Fn(VerifyPassword) -> BoxFuture<'static, Result<()>> + Send + Sync>>,
pub verify_pubkey:
Option<Arc<dyn Fn(VerifyPubkey) -> BoxFuture<'static, Result<()>> + Send + Sync>>,
}
fn _assert_send_sync() {
fn send<T: Send + Sync>() {}
send::<ServerAuthVerify>();
} }
pub struct PendingChannel {
ready_recv: tokio::sync::oneshot::Receiver<Result<(), String>>,
channel: Channel,
}
pub enum Error { pub enum Error {
SshStatus(SshStatus), SshStatus(SshStatus),
ServerError(eyre::Report), ServerError(eyre::Report),
@ -81,22 +76,41 @@ impl From<eyre::Report> for Error {
} }
impl ServerListener { impl ServerListener {
pub fn new(listener: TcpListener) -> Self { pub fn new(listener: TcpListener, auth_verify: ServerAuthVerify) -> Self {
Self { listener } Self {
listener,
auth_verify,
}
} }
pub async fn accept(&mut self) -> Result<ServerConnection<TcpStream>> { pub async fn accept(&mut self) -> Result<ServerConnection<TcpStream>> {
let (conn, peer_addr) = self.listener.accept().await?; let (conn, peer_addr) = self.listener.accept().await?;
Ok(ServerConnection::new(conn, peer_addr)) Ok(ServerConnection::new(
conn,
peer_addr,
self.auth_verify.clone(),
))
} }
} }
impl<S: AsyncRead + AsyncWrite> ServerConnection<S> { impl<S: AsyncRead + AsyncWrite> ServerConnection<S> {
pub fn new(stream: S, peer_addr: SocketAddr) -> Self { pub fn new(stream: S, peer_addr: SocketAddr, auth_verify: ServerAuthVerify) -> Self {
let (operations_send, operations_recv) = tokio::sync::mpsc::channel(15); let (operations_send, operations_recv) = tokio::sync::mpsc::channel(15);
let (channel_ops_send, channel_ops_recv) = tokio::sync::mpsc::channel(15); let (channel_ops_send, channel_ops_recv) = tokio::sync::mpsc::channel(15);
let mut options = HashSet::new();
if auth_verify.verify_password.is_some() {
options.insert(AuthOption::Password);
}
if auth_verify.verify_pubkey.is_some() {
options.insert(AuthOption::PublicKey);
}
if options.is_empty() {
panic!("no auth options provided");
}
Self { Self {
stream: Box::pin(stream), stream: Box::pin(stream),
peer_addr, peer_addr,
@ -110,8 +124,10 @@ impl<S: AsyncRead + AsyncWrite> ServerConnection<S> {
cluelessh_transport::server::ServerConnection::new( cluelessh_transport::server::ServerConnection::new(
cluelessh_protocol::ThreadRngRand, cluelessh_protocol::ThreadRngRand,
), ),
options,
), ),
new_channels: VecDeque::new(), new_channels: VecDeque::new(),
auth_verify,
} }
} }
@ -125,28 +141,28 @@ impl<S: AsyncRead + AsyncWrite> ServerConnection<S> {
if let Some(auth) = self.proto.auth() { if let Some(auth) = self.proto.auth() {
for req in auth.server_requests() { for req in auth.server_requests() {
match req { match req {
cluelessh_protocol::auth::ServerRequest::VerifyPassword { user, password } => { cluelessh_protocol::auth::ServerRequest::VerifyPassword(password_verify) => {
let send = self.operations_send.clone(); let send = self.operations_send.clone();
let verify = self
.auth_verify
.verify_password
.clone()
.ok_or_eyre("password auth not supported")?;
tokio::spawn(async move { tokio::spawn(async move {
let _ = send let result = verify(password_verify).await;
.send(Operation::VerifyPassword { user, password }) let _ = send.send(Operation::VerifyPassword(result)).await;
.await;
}); });
} }
cluelessh_protocol::auth::ServerRequest::VerifyPubkey { cluelessh_protocol::auth::ServerRequest::VerifyPubkey(pubkey_verify) => {
session_identifier,
pubkey,
user,
} => {
let send = self.operations_send.clone(); let send = self.operations_send.clone();
let verify = self
.auth_verify
.verify_pubkey
.clone()
.ok_or_eyre("pubkey auth not supported")?;
tokio::spawn(async move { tokio::spawn(async move {
let _ = send let result = verify(pubkey_verify).await;
.send(Operation::VerifyPubkey { let _ = send.send(Operation::VerifyPubkey(result)).await;
session_identifier,
user,
pubkey,
})
.await;
}); });
} }
} }
@ -247,7 +263,7 @@ impl<S: AsyncRead + AsyncWrite> ServerConnection<S> {
let read = read.wrap_err("reading from connection")?; let read = read.wrap_err("reading from connection")?;
if read == 0 { if read == 0 {
info!("Did not read any bytes from TCP stream, EOF"); info!("Did not read any bytes from TCP stream, EOF");
return Ok(()); return Err(Error::SshStatus(SshStatus::Disconnect));
} }
if let Err(err) = self.proto.recv_bytes(&self.buf[..read]) { if let Err(err) = self.proto.recv_bytes(&self.buf[..read]) {
return Err(Error::SshStatus(err)); return Err(Error::SshStatus(err));
@ -261,8 +277,12 @@ impl<S: AsyncRead + AsyncWrite> ServerConnection<S> {
} }
op = self.operations_recv.recv() => { op = self.operations_recv.recv() => {
match op { match op {
Some(Operation::VerifyPubkey { .. }) => todo!(), Some(Operation::VerifyPubkey(result)) => if let Some(auth) = self.proto.auth() {
Some(Operation::VerifyPassword { .. }) => todo!(), auth.verification_result(result.is_ok());
},
Some(Operation::VerifyPassword(result)) => if let Some(auth) = self.proto.auth() {
auth.verification_result(result.is_ok());
},
None => {} None => {}
} }
self.send_off_data().await?; self.send_off_data().await?;
@ -315,13 +335,3 @@ impl<S: AsyncRead + AsyncWrite> ServerConnection<S> {
self.new_channels.pop_front() self.new_channels.pop_front()
} }
} }
impl PendingChannel {
pub async fn wait_ready(self) -> Result<Channel, Option<String>> {
match self.ready_recv.await {
Ok(Ok(())) => Ok(self.channel),
Ok(Err(err)) => Err(Some(err)),
Err(_) => Err(None),
}
}
}

View file

@ -44,8 +44,12 @@ enum ServerState {
encryption_client_to_server: EncryptionAlgorithm, encryption_client_to_server: EncryptionAlgorithm,
encryption_server_to_client: EncryptionAlgorithm, encryption_server_to_client: EncryptionAlgorithm,
}, },
ServiceRequest, ServiceRequest {
Open, session_ident: [u8; 32],
},
Open {
session_ident: [u8; 32],
},
} }
impl ServerConnection { impl ServerConnection {
@ -289,9 +293,9 @@ impl ServerConnection {
*encryption_server_to_client, *encryption_server_to_client,
true, true,
); );
self.state = ServerState::ServiceRequest {}; self.state = ServerState::ServiceRequest { session_ident: *h };
} }
ServerState::ServiceRequest => { ServerState::ServiceRequest { session_ident } => {
// TODO: this should probably move out of here? unsure. // TODO: this should probably move out of here? unsure.
if packet.payload.first() != Some(&numbers::SSH_MSG_SERVICE_REQUEST) { if packet.payload.first() != Some(&numbers::SSH_MSG_SERVICE_REQUEST) {
return Err(peer_error!("did not send SSH_MSG_SERVICE_REQUEST")); return Err(peer_error!("did not send SSH_MSG_SERVICE_REQUEST"));
@ -312,9 +316,11 @@ impl ServerConnection {
writer.finish() writer.finish()
}, },
}); });
self.state = ServerState::Open; self.state = ServerState::Open {
session_ident: *session_ident,
};
} }
ServerState::Open => { ServerState::Open { .. } => {
self.plaintext_packets.push_back(packet); self.plaintext_packets.push_back(packet);
} }
} }
@ -322,6 +328,13 @@ impl ServerConnection {
Ok(()) Ok(())
} }
pub fn is_open(&self) -> Option<[u8; 32]> {
match self.state {
ServerState::Open { session_ident } => Some(session_ident),
_ => None,
}
}
pub fn next_msg_to_send(&mut self) -> Option<Msg> { pub fn next_msg_to_send(&mut self) -> Option<Msg> {
self.packet_transport.next_msg_to_send() self.packet_transport.next_msg_to_send()
} }