Skip to content

Add binding for agent forwarding #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions libssh-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub(crate) struct SessionHolder {
sess: sys::ssh_session,
callbacks: sys::ssh_callbacks_struct,
auth_callback: Option<Box<dyn FnMut(&str, bool, bool, Option<String>) -> SshResult<String>>>,
pending_agent_forward_channels: Vec<sys::ssh_channel>,
}
unsafe impl Send for SessionHolder {}

Expand All @@ -87,6 +88,7 @@ impl std::ops::Deref for SessionHolder {

impl Drop for SessionHolder {
fn drop(&mut self) {
self.clear_pending_agent_forward_channels();
unsafe {
sys::ssh_free(self.sess);
}
Expand Down Expand Up @@ -157,6 +159,15 @@ impl SessionHolder {
}
}
}

fn clear_pending_agent_forward_channels(&mut self) {
for chan in self.pending_agent_forward_channels.drain(..) {
unsafe {
// We have no callbacks on these channels, no need to cleanup.
sys::ssh_channel_free(chan);
}
}
}
}

/// A Session represents the state needed to make a connection to
Expand Down Expand Up @@ -201,6 +212,7 @@ impl Session {
sess,
callbacks,
auth_callback: None,
pending_agent_forward_channels: Vec::new(),
}));

{
Expand Down Expand Up @@ -274,6 +286,21 @@ impl Session {
}
}

unsafe extern "C" fn channel_open_request_auth_agent_callback(
session: sys::ssh_session,
userdata: *mut ::std::os::raw::c_void,
) -> sys::ssh_channel {
let sess: &mut SessionHolder = &mut *(userdata as *mut SessionHolder);
let chan = sys::ssh_channel_new(session);
if chan.is_null() {
eprintln!("ssh_channel_new failed: {:?}", sess.last_error());
return std::ptr::null_mut();
}
// We are guarenteed to be holding a session lock here.
sess.pending_agent_forward_channels.push(chan);
chan
}

/// Sets a callback that is used by libssh when it needs to prompt
/// for the passphrase during public key authentication.
/// This is NOT used for password or keyboard interactive authentication.
Expand Down Expand Up @@ -326,6 +353,30 @@ impl Session {
sess.callbacks.auth_function = Some(Self::bridge_auth_callback);
}

/// Enable or disable creating channels when the remote side requests a new channel for SSH
/// agent forwarding.
/// You are supposed to periodically check whether there's pending channels (already bound to
/// remote side's agent client) by using the `accept_agent_forward` function.
pub fn enable_accept_agent_forward(&self, enable: bool) {
let mut sess = self.lock_session();
sess.callbacks.channel_open_request_auth_agent_function = if enable {
Some(Self::channel_open_request_auth_agent_callback)
} else {
sess.clear_pending_agent_forward_channels();
// libssh denies auth agent channel requests with no callback set.
None
}
}

// Accept an auth agent forward channel.
// Returns a `Channel` bound to the remote side SSH agent client, or `None` if no pending
// request from the server.
pub fn accept_agent_forward(&self) -> Option<Channel> {
let mut sess = self.lock_session();
let chan = sess.pending_agent_forward_channels.pop()?;
Some(Channel::new(&self.sess, chan))
}

/// Create a new channel.
/// Channels are used to handle I/O for commands and forwarded streams.
pub fn new_channel(&self) -> SshResult<Channel> {
Expand Down
Loading