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
10pub struct Agent {
14 raw: *mut raw::LIBSSH2_AGENT,
15 sess: Arc<Mutex<SessionInner>>,
16}
17
18unsafe impl Send for Agent {}
22unsafe impl Sync for Agent {}
23
24#[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 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 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 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 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 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 pub fn blob(&self) -> &[u8] {
153 &self.blob
154 }
155
156 pub fn comment(&self) -> &str {
158 &self.comment
159 }
160}