1use parking_lot::{Mutex, MutexGuard};
2use std::ffi::{CStr, CString};
3use std::path::{Path, PathBuf};
4use std::ptr::null_mut;
5use std::slice;
6use std::str;
7use std::sync::Arc;
8
9use util;
10use {raw, Error, ErrorCode, SessionInner};
11
12pub struct Agent {
16 raw: *mut raw::LIBSSH2_AGENT,
17 sess: Arc<Mutex<SessionInner>>,
18}
19
20unsafe impl Send for Agent {}
24unsafe impl Sync for Agent {}
25
26#[derive(Debug, PartialEq, Eq)]
28pub struct PublicKey {
29 blob: Vec<u8>,
30 comment: String,
31}
32
33impl Agent {
34 pub(crate) fn from_raw_opt(
35 raw: *mut raw::LIBSSH2_AGENT,
36 err: Option<Error>,
37 sess: &Arc<Mutex<SessionInner>>,
38 ) -> Result<Self, Error> {
39 if raw.is_null() {
40 Err(err.unwrap_or_else(Error::unknown))
41 } else {
42 Ok(Self {
43 raw,
44 sess: Arc::clone(sess),
45 })
46 }
47 }
48
49 pub fn connect(&mut self) -> Result<(), Error> {
51 let sess = self.sess.lock();
52 unsafe { sess.rc(raw::libssh2_agent_connect(self.raw)) }
53 }
54
55 pub fn disconnect(&mut self) -> Result<(), Error> {
57 let sess = self.sess.lock();
58 unsafe { sess.rc(raw::libssh2_agent_disconnect(self.raw)) }
59 }
60
61 pub fn list_identities(&mut self) -> Result<(), Error> {
66 let sess = self.sess.lock();
67 unsafe { sess.rc(raw::libssh2_agent_list_identities(self.raw)) }
68 }
69
70 pub fn identities(&self) -> Result<Vec<PublicKey>, Error> {
72 let sess = self.sess.lock();
73 let mut res = vec![];
74 let mut prev = null_mut();
75 let mut next = null_mut();
76 loop {
77 match unsafe { raw::libssh2_agent_get_identity(self.raw, &mut next, prev) } {
78 0 => {
79 prev = next;
80 res.push(unsafe { PublicKey::from_raw(next) });
81 }
82 1 => break,
83 rc => return Err(Error::from_session_error_raw(sess.raw, rc)),
84 }
85 }
86 Ok(res)
87 }
88
89 fn resolve_raw_identity(
90 &self,
91 sess: &MutexGuard<SessionInner>,
92 identity: &PublicKey,
93 ) -> Result<Option<*mut raw::libssh2_agent_publickey>, Error> {
94 let mut prev = null_mut();
95 let mut next = null_mut();
96 loop {
97 match unsafe { raw::libssh2_agent_get_identity(self.raw, &mut next, prev) } {
98 0 => {
99 prev = next;
100 let this_ident = unsafe { PublicKey::from_raw(next) };
101 if this_ident == *identity {
102 return Ok(Some(next));
103 }
104 }
105 1 => break,
106 rc => return Err(Error::from_session_error_raw(sess.raw, rc)),
107 }
108 }
109 Ok(None)
110 }
111
112 pub fn userauth(&self, username: &str, identity: &PublicKey) -> Result<(), Error> {
114 let username = CString::new(username)?;
115 let sess = self.sess.lock();
116 let raw_ident = self.resolve_raw_identity(&sess, identity)?.ok_or_else(|| {
117 Error::new(
118 ErrorCode::Session(raw::LIBSSH2_ERROR_BAD_USE),
119 "Identity not found in agent",
120 )
121 })?;
122 unsafe {
123 sess.rc(raw::libssh2_agent_userauth(
124 self.raw,
125 username.as_ptr(),
126 raw_ident,
127 ))
128 }
129 }
130
131 pub fn set_identity_path(&mut self, path: &Path) -> Result<(), Error> {
133 let path = CString::new(util::path2bytes(path)?)?;
134 unsafe {
135 raw::libssh2_agent_set_identity_path(self.raw, path.as_ptr());
136 }
137 Ok(())
138 }
139
140 pub fn identity_path(&self) -> Option<PathBuf> {
142 unsafe {
143 let ptr = raw::libssh2_agent_get_identity_path(self.raw);
144 if ptr.is_null() {
145 None
146 } else {
147 Some(util::mkpath(CStr::from_ptr(ptr).to_bytes()))
148 }
149 }
150 }
151}
152
153impl Drop for Agent {
154 fn drop(&mut self) {
155 unsafe { raw::libssh2_agent_free(self.raw) }
156 }
157}
158
159impl PublicKey {
160 unsafe fn from_raw(raw: *mut raw::libssh2_agent_publickey) -> Self {
161 let blob = slice::from_raw_parts_mut((*raw).blob, (*raw).blob_len as usize);
162 let comment = (*raw).comment;
163 let comment = if comment.is_null() {
164 String::new()
165 } else {
166 CStr::from_ptr(comment).to_string_lossy().into_owned()
167 };
168 Self {
169 blob: blob.to_vec(),
170 comment,
171 }
172 }
173
174 pub fn blob(&self) -> &[u8] {
176 &self.blob
177 }
178
179 pub fn comment(&self) -> &str {
181 &self.comment
182 }
183}