mirror of
https://github.com/Noratrieb/cluelessh.git
synced 2026-01-14 16:35:06 +01:00
start client public auth
This commit is contained in:
parent
157d6081b8
commit
85f1def4b5
9 changed files with 132 additions and 16 deletions
|
|
@ -1,4 +1,5 @@
|
|||
use std::{
|
||||
fmt::Display,
|
||||
io::Write,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
|
@ -28,9 +29,9 @@ enum Subcommand {
|
|||
},
|
||||
/// Generate a new SSH key
|
||||
Generate {
|
||||
#[arg(short, long = "type")]
|
||||
#[arg(short, long = "type", default_value_t = KeyType::Ed25519)]
|
||||
type_: KeyType,
|
||||
#[arg(short, long)]
|
||||
#[arg(short, long, default_value_t = String::default())]
|
||||
comment: String,
|
||||
#[arg(short, long)]
|
||||
path: PathBuf,
|
||||
|
|
@ -55,6 +56,14 @@ enum KeyType {
|
|||
Ed25519,
|
||||
}
|
||||
|
||||
impl Display for KeyType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Ed25519 => f.write_str("ed25519"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> eyre::Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
|
|
|
|||
|
|
@ -16,3 +16,4 @@ tracing-subscriber = { version = "0.3.18", features = ["json", "env-filter"] }
|
|||
|
||||
tracing.workspace = true
|
||||
rpassword = "7.3.1"
|
||||
users = "0.11.0"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use std::io::Write;
|
||||
use std::{collections::HashSet, io::Write};
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use eyre::Context;
|
||||
use eyre::{bail, Context, ContextCompat, OptionExt};
|
||||
use rand::RngCore;
|
||||
use ssh_transport::{key::PublicKey, parse::Writer};
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::TcpStream,
|
||||
|
|
@ -27,12 +28,19 @@ impl ssh_protocol::transport::SshRng for ThreadRngRand {
|
|||
struct Args {
|
||||
#[arg(short = 'p', long, default_value_t = 22)]
|
||||
port: u16,
|
||||
#[arg(short, long)]
|
||||
user: Option<String>,
|
||||
destination: String,
|
||||
command: Vec<String>,
|
||||
}
|
||||
|
||||
enum Operation {
|
||||
PasswordEntered(std::io::Result<String>),
|
||||
Signature {
|
||||
key_alg_name: &'static str,
|
||||
public_key: Vec<u8>,
|
||||
signature: Vec<u8>,
|
||||
},
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
|
@ -42,12 +50,29 @@ async fn main() -> eyre::Result<()> {
|
|||
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
tracing_subscriber::fmt().with_env_filter(env_filter).init();
|
||||
|
||||
let username = match args.user {
|
||||
None => {
|
||||
tokio::task::spawn_blocking(|| {
|
||||
users::get_current_username()
|
||||
.wrap_err("getting username")
|
||||
.and_then(|username| {
|
||||
username
|
||||
.to_str()
|
||||
.ok_or_eyre("your username is invalid UTF-8???")
|
||||
.map(ToOwned::to_owned)
|
||||
})
|
||||
})
|
||||
.await??
|
||||
}
|
||||
Some(user) => user,
|
||||
};
|
||||
|
||||
let mut attempted_public_keys = HashSet::new();
|
||||
|
||||
let mut conn = TcpStream::connect(&format!("{}:{}", args.destination, args.port))
|
||||
.await
|
||||
.wrap_err("connecting")?;
|
||||
|
||||
let username = "hans-peter";
|
||||
|
||||
let mut state = ssh_protocol::ClientConnection::new(
|
||||
transport::client::ClientConnection::new(ThreadRngRand),
|
||||
ssh_protocol::auth::ClientAuth::new(username.as_bytes().to_vec()),
|
||||
|
|
@ -68,7 +93,7 @@ async fn main() -> eyre::Result<()> {
|
|||
for req in auth.user_requests() {
|
||||
match req {
|
||||
ssh_protocol::auth::ClientUserRequest::Password => {
|
||||
let username = username.to_owned();
|
||||
let username = username.clone();
|
||||
let destination = args.destination.clone();
|
||||
let send_op = send_op.clone();
|
||||
std::thread::spawn(move || {
|
||||
|
|
@ -80,16 +105,50 @@ async fn main() -> eyre::Result<()> {
|
|||
});
|
||||
}
|
||||
ssh_protocol::auth::ClientUserRequest::PrivateKeySign {
|
||||
session_identifier: _,
|
||||
session_identifier,
|
||||
} => {
|
||||
// TODO: support agentless manual key opening
|
||||
// TODO: move
|
||||
let mut agent = ssh_agent_client::SocketAgentConnection::from_env()
|
||||
.await
|
||||
.wrap_err("failed to connect to SSH agent")?;
|
||||
let identities = agent.list_identities().await?;
|
||||
for identity in identities {
|
||||
debug!(comment = ?identity.comment, "Found identity");
|
||||
for identity in &identities {
|
||||
let pubkey = PublicKey::from_wire_encoding(&identity.key_blob)
|
||||
.wrap_err("received invalid public key from SSH agent")?;
|
||||
debug!(comment = ?identity.comment, %pubkey, "Found identity");
|
||||
}
|
||||
if identities.len() > 1 {
|
||||
todo!("try identities");
|
||||
}
|
||||
let identity = &identities[0];
|
||||
if attempted_public_keys.insert(identity.key_blob.clone()) {
|
||||
bail!("authentication denied (publickey)");
|
||||
}
|
||||
let pubkey = PublicKey::from_wire_encoding(&identity.key_blob)?;
|
||||
|
||||
let mut sig = Writer::new();
|
||||
sig.string(session_identifier);
|
||||
sig.string(&username);
|
||||
sig.string("ssh-connection");
|
||||
sig.string("publickey");
|
||||
sig.bool(true);
|
||||
sig.string(pubkey.algorithm_name());
|
||||
sig.string(&identity.key_blob);
|
||||
|
||||
let data = sig.finish();
|
||||
let signature = agent
|
||||
.sign(&identity.key_blob, &data, 0)
|
||||
.await
|
||||
.wrap_err("signing for authentication")?;
|
||||
|
||||
send_op
|
||||
.send(Operation::Signature {
|
||||
key_alg_name: pubkey.algorithm_name(),
|
||||
public_key: identity.key_blob.clone(),
|
||||
signature,
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
ssh_protocol::auth::ClientUserRequest::Banner(banner) => {
|
||||
let banner = String::from_utf8_lossy(&banner);
|
||||
|
|
@ -128,6 +187,15 @@ async fn main() -> eyre::Result<()> {
|
|||
debug!("Ignoring entered password as the state has moved on");
|
||||
}
|
||||
}
|
||||
Some(Operation::Signature{
|
||||
key_alg_name, public_key, signature,
|
||||
}) => {
|
||||
if let Some(auth) = state.auth() {
|
||||
auth.send_signature(key_alg_name, &public_key, &signature);
|
||||
} else {
|
||||
debug!("Ignoring signature as the state has moved on");
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
state.progress();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue