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 break
134 elif self.__inr == fd:
135 data = self.__inr.recv(512)
136 if len(data) != 0:
137 self._agent._conn.send(data)
138 else:
139 break
140 time.sleep(io_sleep)
141
143 """
144 Class to be used when wanting to ask a local SSH Agent being
145 asked from a remote fake agent (so use a unix socket for ex.)
146 """
149
151 """ Return a pair of socket object and string address
152 May Block !
153 """
154 conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
155 try:
156 conn.bind(self._agent._get_filename())
157 conn.listen(1)
158 (r,addr) = conn.accept()
159 return (r, addr)
160 except:
161 raise
162 return None
163
165 """
166 Class to be used when wanting to ask a remote SSH Agent
167 """
171
173 """
174 Class to be used when wanting to ask a local SSH Agent being
175 asked from a remote fake agent (so use a unix socket for ex.)
176 """
177 return (self.__chan, None)
178
180 """
181 Class proxying request as a client:
182 -> client ask for a request_forward_agent()
183 -> server creates a proxy and a fake SSH Agent
184 -> server ask for establishing a connection when needed,
185 calling the forward_agent_handler at client side.
186 -> the forward_agent_handler launch a thread for connecting
187 the remote fake agent and the local agent
188 -> Communication occurs ...
189 """
191 self._conn = None
192 self.__chanR = chanRemote
193 self.thread = AgentRemoteProxy(self, chanRemote)
194 self.thread.start()
195
198
200 """
201 Method automatically called by the run() method of the AgentProxyThread
202 """
203 if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
204 conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
205 try:
206 retry_on_signal(lambda: conn.connect(os.environ['SSH_AUTH_SOCK']))
207 except:
208
209 return
210 elif sys.platform == 'win32':
211 import win_pageant
212 if win_pageant.can_talk_to_agent():
213 conn = win_pageant.PageantConnection()
214 else:
215 return
216 else:
217
218 return
219 self._conn = conn
220
222 """
223 Close the current connection and terminate the agent
224 Should be called manually
225 """
226 if hasattr(self, "thread"):
227 self.thread._exit = True
228 self.thread.join(1000)
229 if self._conn is not None:
230 self._conn.close()
231
233 """
234 @param t : transport used for the Forward for SSH Agent communication
235
236 @raise SSHException: mostly if we lost the agent
237 """
239 AgentSSH.__init__(self)
240 self.__t = t
241 self._dir = tempfile.mkdtemp('sshproxy')
242 os.chmod(self._dir, stat.S_IRWXU)
243 self._file = self._dir + '/sshproxy.ssh'
244 self.thread = AgentLocalProxy(self)
245 self.thread.start()
246
249
256
258 """
259 Terminate the agent, clean the files, close connections
260 Should be called manually
261 """
262 os.remove(self._file)
263 os.rmdir(self._dir)
264 self.thread._exit = True
265 self.thread.join(1000)
266 self._close()
267
269 """
270 Helper for the environnement under unix
271
272 @return: the SSH_AUTH_SOCK Environnement variables
273 @rtype: dict
274 """
275 env = {}
276 env['SSH_AUTH_SOCK'] = self._get_filename()
277 return env
278
281
284 self._conn = None
285 self.__chanC = chanClient
286 chanClient.request_forward_agent(self._forward_agent_handler)
287 self.__clientProxys = []
288
291
294
296 for p in self.__clientProxys:
297 p.close()
298
300 """
301 Client interface for using private keys from an SSH agent running on the
302 local machine. If an SSH agent is running, this class can be used to
303 connect to it and retreive L{PKey} objects which can be used when
304 attempting to authenticate to remote SSH servers.
305
306 Because the SSH agent protocol uses environment variables and unix-domain
307 sockets, this probably doesn't work on Windows. It does work on most
308 posix platforms though (Linux and MacOS X, for example).
309 """
310
312 """
313 Open a session with the local machine's SSH agent, if one is running.
314 If no agent is running, initialization will succeed, but L{get_keys}
315 will return an empty tuple.
316
317 @raise SSHException: if an SSH agent is found, but speaks an
318 incompatible protocol
319 """
320 AgentSSH.__init__(self)
321
322 if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
323 conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
324 try:
325 conn.connect(os.environ['SSH_AUTH_SOCK'])
326 except:
327
328 return
329 elif sys.platform == 'win32':
330 import win_pageant
331 if win_pageant.can_talk_to_agent():
332 conn = win_pageant.PageantConnection()
333 else:
334 return
335 else:
336
337 return
338 self._connect(conn)
339
341 """
342 Close the SSH agent connection.
343 """
344 self._close()
345
347 """
348 Private key held in a local SSH agent. This type of key can be used for
349 authenticating to a remote server (signing). Most other key operations
350 work as expected.
351 """
352
357
360
363
374