mirror of
https://github.com/wez/wezterm.git
synced 2024-11-23 06:54:45 +03:00
wezterm-ssh: add pubkey based auth
I can't get this to succeed though; I suspect there may be a lingering bug from libssh2 and/or trailing support for newer openssh features. refs: https://github.com/wez/wezterm/issues/457
This commit is contained in:
parent
6bb941b653
commit
13f13d7309
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -4576,6 +4576,7 @@ dependencies = [
|
||||
"k9",
|
||||
"log",
|
||||
"portable-pty",
|
||||
"pretty_env_logger",
|
||||
"regex",
|
||||
"shell-words",
|
||||
"smol",
|
||||
|
@ -19,6 +19,7 @@ ssh2 = "0.9"
|
||||
|
||||
[dev-dependencies]
|
||||
k9 = "0.11.0"
|
||||
pretty_env_logger = "0.4"
|
||||
shell-words = "1.0"
|
||||
structopt = "0.3"
|
||||
termwiz = { path = "../termwiz" }
|
||||
|
@ -45,6 +45,7 @@ struct Opt {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
pretty_env_logger::init();
|
||||
let opts = Opt::from_args();
|
||||
|
||||
let mut config = Config::new();
|
||||
|
@ -2,6 +2,7 @@ use crate::session::SessionEvent;
|
||||
use anyhow::Context;
|
||||
use smol::channel::{bounded, Sender};
|
||||
use std::collections::HashSet;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AuthenticationPrompt {
|
||||
@ -28,6 +29,97 @@ impl AuthenticationEvent {
|
||||
}
|
||||
|
||||
impl crate::session::SessionInner {
|
||||
fn agent_auth(&mut self, sess: &ssh2::Session, user: &str) -> anyhow::Result<bool> {
|
||||
if let Some(only) = self.config.get("identitiesonly") {
|
||||
if only == "yes" {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
let mut agent = sess.agent()?;
|
||||
agent.connect()?;
|
||||
agent.list_identities()?;
|
||||
let identities = agent.identities()?;
|
||||
for identity in identities {
|
||||
if agent.userauth(user, &identity).is_ok() {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn pubkey_auth(
|
||||
&mut self,
|
||||
sess: &ssh2::Session,
|
||||
user: &str,
|
||||
host: &str,
|
||||
) -> anyhow::Result<bool> {
|
||||
if let Some(files) = self.config.get("identityfile") {
|
||||
for file in files.split_whitespace() {
|
||||
let pubkey: PathBuf = format!("{}.pub", file).into();
|
||||
let file = Path::new(file);
|
||||
|
||||
if !file.exists() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let pubkey = if pubkey.exists() && false {
|
||||
Some(pubkey.as_ref())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match sess.userauth_pubkey_file(user, pubkey, &file, None) {
|
||||
Ok(_) => return Ok(true),
|
||||
Err(err) => {
|
||||
if err.code() == ssh2::ErrorCode::Session(-16)
|
||||
|| err.code() == ssh2::ErrorCode::Session(-18)
|
||||
{
|
||||
// Need a passphrase to decrypt the key
|
||||
|
||||
let (reply, answers) = bounded(1);
|
||||
self.tx_event
|
||||
.try_send(SessionEvent::Authenticate(AuthenticationEvent {
|
||||
username: "".to_string(),
|
||||
instructions: "".to_string(),
|
||||
prompts: vec![AuthenticationPrompt {
|
||||
prompt: format!(
|
||||
"Passphrase to decrypt {} for {}@{}: ",
|
||||
file.display(),
|
||||
user,
|
||||
host
|
||||
),
|
||||
echo: false,
|
||||
}],
|
||||
reply,
|
||||
}))
|
||||
.context("sending Authenticate request to user")?;
|
||||
|
||||
let answers = smol::block_on(answers.recv())
|
||||
.context("waiting for authentication answers from user")?;
|
||||
|
||||
if answers.is_empty() {
|
||||
anyhow::bail!("user cancelled authentication");
|
||||
}
|
||||
|
||||
let passphrase = &answers[0];
|
||||
|
||||
match sess.userauth_pubkey_file(user, pubkey, &file, Some(passphrase)) {
|
||||
Ok(_) => return Ok(true),
|
||||
Err(err) => {
|
||||
log::warn!("pubkey auth: {:#}", err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::warn!("pubkey auth: {:#}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
pub fn authenticate(
|
||||
&mut self,
|
||||
sess: &ssh2::Session,
|
||||
@ -46,10 +138,20 @@ impl crate::session::SessionInner {
|
||||
log::trace!("ssh auth methods: {:?}", methods);
|
||||
|
||||
if !sess.authenticated() && methods.contains("publickey") {
|
||||
if let Err(err) = sess.userauth_agent(&user) {
|
||||
log::warn!("while attempting agent auth: {}", err);
|
||||
} else {
|
||||
continue;
|
||||
match self.agent_auth(sess, user) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(err) => {
|
||||
log::warn!("while attempting agent auth: {}", err)
|
||||
}
|
||||
}
|
||||
|
||||
match self.pubkey_auth(sess, user, host) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(err) => {
|
||||
log::warn!("while attempting auth: {}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,6 +279,18 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
if !result.contains_key("identityfile") {
|
||||
if let Some(home) = self.resolve_home() {
|
||||
result.insert(
|
||||
"identityfile".to_string(),
|
||||
format!(
|
||||
"{}/.ssh/id_dsa {}/.ssh/id_ecdsa {}/.ssh/id_ed25519 {}/.ssh/id_rsa",
|
||||
home, home, home, home
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !result.contains_key("identityagent") {
|
||||
if let Some(sock_path) = self.resolve_env("SSH_AUTH_SOCK") {
|
||||
result.insert("identityagent".to_string(), sock_path);
|
||||
|
@ -114,6 +114,7 @@ impl SessionInner {
|
||||
.context("setting TCP NODELAY on ssh connection")?;
|
||||
|
||||
let mut sess = ssh2::Session::new()?;
|
||||
// sess.trace(ssh2::TraceFlags::all());
|
||||
sess.set_tcp_stream(tcp);
|
||||
sess.handshake()
|
||||
.with_context(|| format!("ssh handshake with {}", remote_address))?;
|
||||
|
Loading…
Reference in New Issue
Block a user