ssh2/
agent.rs

1use parking_lot::{Mutex, MutexGuard};
2use std::ffi::{CStr, CString};
3use std::ptr::null_mut;
4use std::slice;
5use std::str;
6use std::sync::Arc;
7
8use {raw, Error, ErrorCode, SessionInner};
9
10/// A structure representing a connection to an SSH agent.
11///
12/// Agents can be used to authenticate a session.
13pub struct Agent {
14    raw: *mut raw::LIBSSH2_AGENT,
15    sess: Arc<Mutex<SessionInner>>,
16}
17
18// Agent is both Send and Sync; the compiler can't see it because it
19// is pessimistic about the raw pointer.  We use Arc/Mutex to guard accessing
20// the raw pointer so we are safe for both.
21unsafe impl Send for Agent {}
22unsafe impl Sync for Agent {}
23
24/// A public key which is extracted from an SSH agent.
25#[derive(Debug, PartialEq, Eq)]
26pub struct PublicKey {
27    blob: Vec<u8>,
28    comment: String,
29}
30
31impl Agent {
32    pub(crate) fn from_raw_opt(
33        raw: *mut raw::LIBSSH2_AGENT,
34        err: Option<Error>,
35        sess: &Arc<Mutex<SessionInner>>,
36    ) -> Result<Self, Error> {
37        if raw.is_null() {
38            Err(err.unwrap_or_else(Error::unknown))
39        } else {
40            Ok(Self {
41                raw,
42                sess: Arc::clone(sess),
43            })
44        }
45    }
46
47    /// Connect to an ssh-agent running on the system.
48    pub fn connect(&mut self) -> Result<(), Error> {
49        let sess = self.sess.lock();
50        unsafe { sess.rc(raw::libssh2_agent_connect(self.raw)) }
51    }
52
53    /// Close a connection to an ssh-agent.
54    pub fn disconnect(&mut self) -> Result<(), Error> {
55        let sess = self.sess.lock();
56        unsafe { sess.rc(raw::libssh2_agent_disconnect(self.raw)) }
57    }
58
59    /// Request an ssh-agent to list of public keys, and stores them in the
60    /// internal collection of the handle.
61    ///
62    /// Call `identities` to get the public keys.
63    pub fn list_identities(&mut self) -> Result<(), Error> {
64        let sess = self.sess.lock();
65        unsafe { sess.rc(raw::libssh2_agent_list_identities(self.raw)) }
66    }
67
68    /// Get list of the identities of this agent.
69    pub fn identities(&self) -> Result<Vec<PublicKey>, Error> {
70        let sess = self.sess.lock();
71        let mut res = vec![];
72        let mut prev = null_mut();
73        let mut next = null_mut();
74        loop {
75            match unsafe { raw::libssh2_agent_get_identity(self.raw, &mut next, prev) } {
76                0 => {
77                    prev = next;
78                    res.push(unsafe { PublicKey::from_raw(next) });
79                }
80                1 => break,
81                rc => return Err(Error::from_session_error_raw(sess.raw, rc)),
82            }
83        }
84        Ok(res)
85    }
86
87    fn resolve_raw_identity(
88        &self,
89        sess: &MutexGuard<SessionInner>,
90        identity: &PublicKey,
91    ) -> Result<Option<*mut raw::libssh2_agent_publickey>, Error> {
92        let mut prev = null_mut();
93        let mut next = null_mut();
94        loop {
95            match unsafe { raw::libssh2_agent_get_identity(self.raw, &mut next, prev) } {
96                0 => {
97                    prev = next;
98                    let this_ident = unsafe { PublicKey::from_raw(next) };
99                    if this_ident == *identity {
100                        return Ok(Some(next));
101                    }
102                }
103                1 => break,
104                rc => return Err(Error::from_session_error_raw(sess.raw, rc)),
105            }
106        }
107        Ok(None)
108    }
109
110    /// Attempt public key authentication with the help of ssh-agent.
111    pub fn userauth(&self, username: &str, identity: &PublicKey) -> Result<(), Error> {
112        let username = CString::new(username)?;
113        let sess = self.sess.lock();
114        let raw_ident = self.resolve_raw_identity(&sess, identity)?.ok_or_else(|| {
115            Error::new(
116                ErrorCode::Session(raw::LIBSSH2_ERROR_BAD_USE),
117                "Identity not found in agent",
118            )
119        })?;
120        unsafe {
121            sess.rc(raw::libssh2_agent_userauth(
122                self.raw,
123                username.as_ptr(),
124                raw_ident,
125            ))
126        }
127    }
128}
129
130impl Drop for Agent {
131    fn drop(&mut self) {
132        unsafe { raw::libssh2_agent_free(self.raw) }
133    }
134}
135
136impl PublicKey {
137    unsafe fn from_raw(raw: *mut raw::libssh2_agent_publickey) -> Self {
138        let blob = slice::from_raw_parts_mut((*raw).blob, (*raw).blob_len as usize);
139        let comment = (*raw).comment;
140        let comment = if comment.is_null() {
141            String::new()
142        } else {
143            CStr::from_ptr(comment).to_string_lossy().into_owned()
144        };
145        Self {
146            blob: blob.to_vec(),
147            comment,
148        }
149    }
150
151    /// Return the data of this public key.
152    pub fn blob(&self) -> &[u8] {
153        &self.blob
154    }
155
156    /// Returns the comment in a printable format
157    pub fn comment(&self) -> &str {
158        &self.comment
159    }
160}