1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 SSH Agent interface for Unix clients.
21 """
22
23 import os
24 import socket
25 import struct
26 import sys
27 import threading
28 import time
29 import tempfile
30 import stat
31 from select import select
32
33 from paramiko.ssh_exception import SSHException
34 from paramiko.message import Message
35 from paramiko.pkey import PKey
36 from paramiko.channel import Channel
37 from paramiko.common import io_sleep
38 from paramiko.util import retry_on_signal
39
40 SSH2_AGENTC_REQUEST_IDENTITIES, SSH2_AGENT_IDENTITIES_ANSWER, \
41 SSH2_AGENTC_SIGN_REQUEST, SSH2_AGENT_SIGN_RESPONSE = range(11, 15)
42
44 """
45 Client interface for using private keys from an SSH agent running on the
46 local machine. If an SSH agent is running, this class can be used to
47 connect to it and retreive L{PKey} objects which can be used when
48 attempting to authenticate to remote SSH servers.
49
50 Because the SSH agent protocol uses environment variables and unix-domain
51 sockets, this probably doesn't work on Windows. It does work on most
52 posix platforms though (Linux and MacOS X, for example).
53 """
55 self._conn = None
56 self._keys = ()
57
59 """
60 Return the list of keys available through the SSH agent, if any. If
61 no SSH agent was running (or it couldn't be contacted), an empty list
62 will be returned.
63
64 @return: a list of keys available on the SSH agent
65 @rtype: tuple of L{AgentKey}
66 """
67 return self._keys
68
79
81
82 self._conn = None
83 self._keys = ()
84
86 msg = str(msg)
87 self._conn.send(struct.pack('>I', len(msg)) + msg)
88 l = self._read_all(4)
89 msg = Message(self._read_all(struct.unpack('>I', l)[0]))
90 return ord(msg.get_byte()), msg
91
93 result = self._conn.recv(wanted)
94 while len(result) < wanted:
95 if len(result) == 0:
96 raise SSHException('lost ssh-agent')
97 extra = self._conn.recv(wanted - len(result))
98 if len(extra) == 0:
99 raise SSHException('lost ssh-agent')
100 result += extra
101 return result
102
104 """ Class in charge of communication between two chan """
106 threading.Thread.__init__(self, target=self.run)
107 self._agent = agent
108 self._exit = False
109
111 try:
112 (r,addr) = self.get_connection()
113 self.__inr = r
114 self.__addr = addr
115 self._agent.connect()
116 self._communicate()
117 except:
118
119 raise
120
122 import fcntl
123 oldflags = fcntl.fcntl(self.__inr, fcntl.F_GETFL)
124 fcntl.fcntl(self.__inr, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
125 while not self._exit:
126 events = select([self._agent._conn, self.__inr], [], [], 0.5)
127 for fd in events[0]:
128 if self._agent._conn == fd:
129 data = self._agent._conn.recv(512)
130 if len(data) != 0:
131 self.__inr.send(data)
132 else:
133 self._close()
134 break
135 elif self.__inr == fd:
136 data = self.__inr.recv(512)
137 if len(data) != 0:
138 self._agent._conn.send(data)
139 else:
140 self._close()
141 break
142 time.sleep(io_sleep)
143
145 self._exit = True
146 self.__inr.close()
147 self._agent._conn.close()
148
150 """
151 Class to be used when wanting to ask a local SSH Agent being
152 asked from a remote fake agent (so use a unix socket for ex.)
153 """
156
158 """ Return a pair of socket object and string address
159 May Block !
160 """
161 conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
162 try:
163 conn.bind(self._agent._get_filename())
164 conn.listen(1)
165 (r,addr) = conn.accept()
166 return (r, addr)
167 except:
168 raise
169 return None
170
172 """
173 Class to be used when wanting to ask a remote SSH Agent
174 """
178
180 """
181 Class to be used when wanting to ask a local SSH Agent being
182 asked from a remote fake agent (so use a unix socket for ex.)
183 """
184 return (self.__chan, None)
185
187 """
188 Class proxying request as a client:
189 -> client ask for a request_forward_agent()
190 -> server creates a proxy and a fake SSH Agent
191 -> server ask for establishing a connection when needed,
192 calling the forward_agent_handler at client side.
193 -> the forward_agent_handler launch a thread for connecting
194 the remote fake agent and the local agent
195 -> Communication occurs ...
196 """
198 self._conn = None
199 self.__chanR = chanRemote
200 self.thread = AgentRemoteProxy(self, chanRemote)
201 self.thread.start()
202
205
207 """
208 Method automatically called by the run() method of the AgentProxyThread
209 """
210 if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
211 conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
212 try:
213 retry_on_signal(lambda: conn.connect(os.environ['SSH_AUTH_SOCK']))
214 except:
215
216 return
217 elif sys.platform == 'win32':
218 import win_pageant
219 if win_pageant.can_talk_to_agent():
220 conn = win_pageant.PageantConnection()
221 else:
222 return
223 else:
224
225 return
226 self._conn = conn
227
229 """
230 Close the current connection and terminate the agent
231 Should be called manually
232 """
233 if hasattr(self, "thread"):
234 self.thread._exit = True
235 self.thread.join(1000)
236 if self._conn is not None:
237 self._conn.close()
238
240 """
241 @param t : transport used for the Forward for SSH Agent communication
242
243 @raise SSHException: mostly if we lost the agent
244 """
246 AgentSSH.__init__(self)
247 self.__t = t
248 self._dir = tempfile.mkdtemp('sshproxy')
249 os.chmod(self._dir, stat.S_IRWXU)
250 self._file = self._dir + '/sshproxy.ssh'
251 self.thread = AgentLocalProxy(self)
252 self.thread.start()
253
256
263
265 """
266 Terminate the agent, clean the files, close connections
267 Should be called manually
268 """
269 os.remove(self._file)
270 os.rmdir(self._dir)
271 self.thread._exit = True
272 self.thread.join(1000)
273 self._close()
274
276 """
277 Helper for the environnement under unix
278
279 @return: the SSH_AUTH_SOCK Environnement variables
280 @rtype: dict
281 """
282 env = {}
283 env['SSH_AUTH_SOCK'] = self._get_filename()
284 return env
285
288
291 self._conn = None
292 self.__chanC = chanClient
293 chanClient.request_forward_agent(self._forward_agent_handler)
294 self.__clientProxys = []
295
298
301
303 for p in self.__clientProxys:
304 p.close()
305
307 """
308 Client interface for using private keys from an SSH agent running on the
309 local machine. If an SSH agent is running, this class can be used to
310 connect to it and retreive L{PKey} objects which can be used when
311 attempting to authenticate to remote SSH servers.
312
313 Because the SSH agent protocol uses environment variables and unix-domain
314 sockets, this probably doesn't work on Windows. It does work on most
315 posix platforms though (Linux and MacOS X, for example).
316 """
317
319 """
320 Open a session with the local machine's SSH agent, if one is running.
321 If no agent is running, initialization will succeed, but L{get_keys}
322 will return an empty tuple.
323
324 @raise SSHException: if an SSH agent is found, but speaks an
325 incompatible protocol
326 """
327 AgentSSH.__init__(self)
328
329 if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
330 conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
331 try:
332 conn.connect(os.environ['SSH_AUTH_SOCK'])
333 except:
334
335 return
336 elif sys.platform == 'win32':
337 import win_pageant
338 if win_pageant.can_talk_to_agent():
339 conn = win_pageant.PageantConnection()
340 else:
341 return
342 else:
343
344 return
345 self._connect(conn)
346
348 """
349 Close the SSH agent connection.
350 """
351 self._close()
352
354 """
355 Private key held in a local SSH agent. This type of key can be used for
356 authenticating to a remote server (signing). Most other key operations
357 work as expected.
358 """
359
364
367
370
381