mirror of
https://github.com/wez/wezterm.git
synced 2024-11-26 08:25:50 +03:00
Wire up agent forward for libssh backend (#5345)
* Wire up agent forward for libssh backend TSIA. There's a drive-by change in sftpwrap.rs for bumping to new libssh-rs.
This commit is contained in:
parent
0b50725f67
commit
9b811c7a16
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -3022,11 +3022,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libssh-rs"
|
name = "libssh-rs"
|
||||||
version = "0.2.2"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb3fe324fb06b71d28abb81382ac547f25b4895e853a9968482dc5002fb3db08"
|
checksum = "c13fbf28a79bb77f11a44716baca6327655198455b7c6f6189784279f3e331db"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
|
"libc",
|
||||||
"libssh-rs-sys",
|
"libssh-rs-sys",
|
||||||
"openssl-sys",
|
"openssl-sys",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -6795,6 +6796,7 @@ dependencies = [
|
|||||||
"ssh2",
|
"ssh2",
|
||||||
"termwiz",
|
"termwiz",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"uds_windows",
|
||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -31,10 +31,11 @@ portable-pty = { version="0.8", path = "../pty" }
|
|||||||
regex = "1"
|
regex = "1"
|
||||||
smol = "1.2"
|
smol = "1.2"
|
||||||
ssh2 = {version="0.9.3", features=["openssl-on-win32"], optional = true}
|
ssh2 = {version="0.9.3", features=["openssl-on-win32"], optional = true}
|
||||||
libssh-rs = {version="0.2.1", features=["vendored"], optional = true}
|
libssh-rs = {version="0.3.1", features=["vendored"], optional = true}
|
||||||
#libssh-rs = {path="../../libssh-rs/libssh-rs", features=["vendored"], optional = true}
|
#libssh-rs = {path="../../libssh-rs/libssh-rs", features=["vendored"], optional = true}
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
socket2 = "0.5"
|
socket2 = "0.5"
|
||||||
|
uds_windows = "1.1.0"
|
||||||
|
|
||||||
# Not used directly, but is used to centralize the openssl vendor feature selection
|
# Not used directly, but is used to centralize the openssl vendor feature selection
|
||||||
async_ossl = { path = "../async_ossl" }
|
async_ossl = { path = "../async_ossl" }
|
||||||
|
@ -146,6 +146,21 @@ impl ChannelWrap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn request_auth_agent_forwarding(&mut self) -> anyhow::Result<()> {
|
||||||
|
match self {
|
||||||
|
/* libssh2 doesn't properly support agent forwarding
|
||||||
|
* at this time:
|
||||||
|
* <https://github.com/libssh2/libssh2/issues/535> */
|
||||||
|
#[cfg(feature = "ssh2")]
|
||||||
|
Self::Ssh2(_chan) => Err(anyhow::anyhow!(
|
||||||
|
"ssh2 does not support request_auth_agent_forwarding"
|
||||||
|
)),
|
||||||
|
|
||||||
|
#[cfg(feature = "libssh-rs")]
|
||||||
|
Self::LibSsh(chan) => Ok(chan.request_auth_agent()?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resize_pty(&mut self, resize: &ResizePty) -> anyhow::Result<()> {
|
pub fn resize_pty(&mut self, resize: &ResizePty) -> anyhow::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "ssh2")]
|
#[cfg(feature = "ssh2")]
|
||||||
|
@ -218,17 +218,13 @@ impl crate::sessioninner::SessionInner {
|
|||||||
|
|
||||||
let mut channel = sess.open_session()?;
|
let mut channel = sess.open_session()?;
|
||||||
|
|
||||||
/* libssh2 doesn't properly support agent forwarding
|
|
||||||
* at this time:
|
|
||||||
* <https://github.com/libssh2/libssh2/issues/535>
|
|
||||||
if let Some("yes") = self.config.get("forwardagent").map(|s| s.as_str()) {
|
if let Some("yes") = self.config.get("forwardagent").map(|s| s.as_str()) {
|
||||||
log::info!("requesting agent forwarding");
|
if self.identity_agent().is_some() {
|
||||||
if let Err(err) = channel.request_auth_agent_forwarding() {
|
if let Err(err) = channel.request_auth_agent_forwarding() {
|
||||||
log::error!("Failed to establish agent forwarding: {:#}", err);
|
log::error!("Failed to request agent forwarding: {:#}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log::info!("agent forwarding OK!");
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
channel.request_pty(&newpty)?;
|
channel.request_pty(&newpty)?;
|
||||||
|
|
||||||
|
@ -243,6 +243,13 @@ impl SessionInner {
|
|||||||
.try_send(SessionEvent::Authenticated)
|
.try_send(SessionEvent::Authenticated)
|
||||||
.context("notifying user that session is authenticated")?;
|
.context("notifying user that session is authenticated")?;
|
||||||
|
|
||||||
|
if let Some("yes") = self.config.get("forwardagent").map(|s| s.as_str()) {
|
||||||
|
if self.identity_agent().is_some() {
|
||||||
|
sess.enable_accept_agent_forward(true);
|
||||||
|
} else {
|
||||||
|
log::error!("ForwardAgent is set to yes, but IdentityAgent is not set");
|
||||||
|
}
|
||||||
|
}
|
||||||
sess.set_blocking(false);
|
sess.set_blocking(false);
|
||||||
let mut sess = SessionWrap::with_libssh(sess);
|
let mut sess = SessionWrap::with_libssh(sess);
|
||||||
self.request_loop(&mut sess)
|
self.request_loop(&mut sess)
|
||||||
@ -405,6 +412,7 @@ impl SessionInner {
|
|||||||
self.tick_io()?;
|
self.tick_io()?;
|
||||||
self.drain_request_pipe();
|
self.drain_request_pipe();
|
||||||
self.dispatch_pending_requests(sess)?;
|
self.dispatch_pending_requests(sess)?;
|
||||||
|
self.connect_pending_agent_forward_channels(sess);
|
||||||
|
|
||||||
if self.channels.is_empty() && self.session_was_dropped {
|
if self.channels.is_empty() && self.session_was_dropped {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
@ -517,8 +525,16 @@ impl SessionInner {
|
|||||||
|
|
||||||
let stdin = &mut chan.descriptors[0];
|
let stdin = &mut chan.descriptors[0];
|
||||||
if stdin.fd.is_some() && !stdin.buf.is_empty() {
|
if stdin.fd.is_some() && !stdin.buf.is_empty() {
|
||||||
write_from_buf(&mut chan.channel.writer(), &mut stdin.buf)
|
if let Err(err) = write_from_buf(&mut chan.channel.writer(), &mut stdin.buf)
|
||||||
.context("writing to channel")?;
|
.context("writing to channel")
|
||||||
|
{
|
||||||
|
log::trace!(
|
||||||
|
"Failed to write data to channel {} stdin: {:#}, closing pipe",
|
||||||
|
id,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
stdin.fd.take();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (idx, out) in chan
|
for (idx, out) in chan
|
||||||
@ -805,6 +821,62 @@ impl SessionInner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn connect_pending_agent_forward_channels(&mut self, sess: &mut SessionWrap) {
|
||||||
|
fn process_one(sess: &mut SessionInner, channel: ChannelWrap) -> anyhow::Result<()> {
|
||||||
|
let identity_agent = sess
|
||||||
|
.identity_agent()
|
||||||
|
.ok_or_else(|| anyhow!("no identity agent in config"))?;
|
||||||
|
let mut fd = {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::net::UnixStream;
|
||||||
|
FileDescriptor::new(UnixStream::connect(&identity_agent)?)
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
unsafe {
|
||||||
|
use std::os::windows::io::{FromRawSocket, IntoRawSocket};
|
||||||
|
use uds_windows::UnixStream;
|
||||||
|
FileDescriptor::from_raw_socket(
|
||||||
|
UnixStream::connect(&identity_agent)?.into_raw_socket(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fd.set_non_blocking(true)?;
|
||||||
|
|
||||||
|
let read_from_agent = fd;
|
||||||
|
let write_to_agent = read_from_agent.try_clone()?;
|
||||||
|
let channel_id = sess.next_channel_id;
|
||||||
|
sess.next_channel_id += 1;
|
||||||
|
let info = ChannelInfo {
|
||||||
|
channel_id,
|
||||||
|
channel,
|
||||||
|
exit: None,
|
||||||
|
exited: false,
|
||||||
|
descriptors: [
|
||||||
|
DescriptorState {
|
||||||
|
fd: Some(read_from_agent),
|
||||||
|
buf: VecDeque::with_capacity(8192),
|
||||||
|
},
|
||||||
|
DescriptorState {
|
||||||
|
fd: Some(write_to_agent),
|
||||||
|
buf: VecDeque::with_capacity(8192),
|
||||||
|
},
|
||||||
|
DescriptorState {
|
||||||
|
fd: None,
|
||||||
|
buf: VecDeque::with_capacity(8192),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
sess.channels.insert(channel_id, info);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
while let Some(channel) = sess.accept_agent_forward() {
|
||||||
|
if let Err(err) = process_one(self, channel) {
|
||||||
|
log::error!("error connecting agent forward: {:#}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn signal_channel(&mut self, info: &SignalChannel) -> anyhow::Result<()> {
|
pub fn signal_channel(&mut self, info: &SignalChannel) -> anyhow::Result<()> {
|
||||||
let chan_info = self
|
let chan_info = self
|
||||||
.channels
|
.channels
|
||||||
@ -944,6 +1016,13 @@ impl SessionInner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn identity_agent(&self) -> Option<String> {
|
||||||
|
self.config
|
||||||
|
.get("identityagent")
|
||||||
|
.map(|s| s.to_owned())
|
||||||
|
.or_else(|| std::env::var("SSH_AUTH_SOCK").ok())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_from_buf<W: Write>(w: &mut W, buf: &mut VecDeque<u8>) -> std::io::Result<()> {
|
fn write_from_buf<W: Write>(w: &mut W, buf: &mut VecDeque<u8>) -> std::io::Result<()> {
|
||||||
|
@ -92,4 +92,16 @@ impl SessionWrap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn accept_agent_forward(&mut self) -> Option<ChannelWrap> {
|
||||||
|
match self {
|
||||||
|
// Unimplemented for now, an error message was printed earlier when the user tries to
|
||||||
|
// enable agent forwarding so just return nothing here.
|
||||||
|
#[cfg(feature = "ssh2")]
|
||||||
|
Self::Ssh2(_sess) => None,
|
||||||
|
|
||||||
|
#[cfg(feature = "libssh-rs")]
|
||||||
|
Self::LibSsh(sess) => sess.sess.accept_agent_forward().map(ChannelWrap::LibSsh),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ impl SftpWrap {
|
|||||||
Self::LibSsh(sftp) => {
|
Self::LibSsh(sftp) => {
|
||||||
use crate::sftp::types::WriteMode;
|
use crate::sftp::types::WriteMode;
|
||||||
use libc::{O_APPEND, O_RDONLY, O_RDWR, O_WRONLY};
|
use libc::{O_APPEND, O_RDONLY, O_RDWR, O_WRONLY};
|
||||||
|
use libssh_rs::OpenFlags;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
let accesstype = match (opts.write, opts.read) {
|
let accesstype = match (opts.write, opts.read) {
|
||||||
(Some(WriteMode::Append), true) => O_RDWR | O_APPEND,
|
(Some(WriteMode::Append), true) => O_RDWR | O_APPEND,
|
||||||
@ -47,8 +48,11 @@ impl SftpWrap {
|
|||||||
(None, true) => O_RDONLY,
|
(None, true) => O_RDONLY,
|
||||||
(None, false) => 0,
|
(None, false) => 0,
|
||||||
};
|
};
|
||||||
let file =
|
let file = sftp.open(
|
||||||
sftp.open(filename.as_str(), accesstype, opts.mode.try_into().unwrap())?;
|
filename.as_str(),
|
||||||
|
OpenFlags::from_bits_truncate(accesstype),
|
||||||
|
opts.mode.try_into().unwrap(),
|
||||||
|
)?;
|
||||||
Ok(FileWrap::LibSsh(file))
|
Ok(FileWrap::LibSsh(file))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
53
wezterm-ssh/tests/e2e/agent_forward.rs
Normal file
53
wezterm-ssh/tests/e2e/agent_forward.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use crate::sshd::*;
|
||||||
|
use portable_pty::{MasterPty, PtySize};
|
||||||
|
use rstest::*;
|
||||||
|
use std::io::Read;
|
||||||
|
use wezterm_ssh::Config;
|
||||||
|
|
||||||
|
#[fixture]
|
||||||
|
async fn session_with_agent_forward(
|
||||||
|
#[future]
|
||||||
|
#[with({ let mut config = Config::new(); config.set_option("forwardagent", "yes"); config })]
|
||||||
|
session: SessionWithSshd,
|
||||||
|
) -> SessionWithSshd {
|
||||||
|
session.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[smol_potat::test]
|
||||||
|
#[cfg_attr(not(any(target_os = "macos", target_os = "linux")), ignore)]
|
||||||
|
#[cfg_attr(not(feature = "libssh-rs"), ignore)]
|
||||||
|
async fn ssh_add_should_be_able_to_list_identities_with_agent_forward(
|
||||||
|
#[future] session_with_agent_forward: SessionWithSshd,
|
||||||
|
) {
|
||||||
|
let session: SessionWithSshd = session_with_agent_forward.await;
|
||||||
|
|
||||||
|
let (pty, _child_process) = session
|
||||||
|
.request_pty("dumb", PtySize::default(), Some("ssh-add -l"), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut reader = pty.try_clone_reader().unwrap();
|
||||||
|
let mut output: String = String::new();
|
||||||
|
reader.read_to_string(&mut output).unwrap();
|
||||||
|
assert_eq!(output, "The agent has no identities.\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[smol_potat::test]
|
||||||
|
#[cfg_attr(not(any(target_os = "macos", target_os = "linux")), ignore)]
|
||||||
|
#[cfg_attr(not(feature = "libssh-rs"), ignore)]
|
||||||
|
async fn no_agent_forward_should_happen_when_disabled(#[future] session: SessionWithSshd) {
|
||||||
|
let session: SessionWithSshd = session.await;
|
||||||
|
|
||||||
|
let (pty, _child_process) = session
|
||||||
|
.request_pty("dumb", PtySize::default(), Some("ssh-add -l"), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut reader = pty.try_clone_reader().unwrap();
|
||||||
|
let mut output: String = String::new();
|
||||||
|
reader.read_to_string(&mut output).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
output,
|
||||||
|
"Could not open a connection to your authentication agent.\r\n"
|
||||||
|
);
|
||||||
|
}
|
@ -1 +1,2 @@
|
|||||||
|
mod agent_forward;
|
||||||
mod sftp;
|
mod sftp;
|
||||||
|
@ -431,10 +431,9 @@ impl std::ops::DerefMut for SessionWithSshd {
|
|||||||
|
|
||||||
#[fixture]
|
#[fixture]
|
||||||
/// Stand up an sshd instance and then connect to it and perform authentication
|
/// Stand up an sshd instance and then connect to it and perform authentication
|
||||||
pub async fn session(sshd: Sshd) -> SessionWithSshd {
|
pub async fn session(#[default(Config::new())] mut config: Config, sshd: Sshd) -> SessionWithSshd {
|
||||||
let port = sshd.port;
|
let port = sshd.port;
|
||||||
|
|
||||||
let mut config = Config::new();
|
|
||||||
config.add_default_config_files();
|
config.add_default_config_files();
|
||||||
|
|
||||||
// Load our config to point to ourselves, using current sshd instance's port,
|
// Load our config to point to ourselves, using current sshd instance's port,
|
||||||
|
Loading…
Reference in New Issue
Block a user