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 ssh.ssh_exception import SSHException
34 from ssh.message import Message
35 from ssh.pkey import PKey
36 from ssh.channel import Channel
37 from ssh.common import io_sleep
38
39 SSH2_AGENTC_REQUEST_IDENTITIES, SSH2_AGENT_IDENTITIES_ANSWER, \
40 SSH2_AGENTC_SIGN_REQUEST, SSH2_AGENT_SIGN_RESPONSE = range(11, 15)
41
43 """
44 Client interface for using private keys from an SSH agent running on the
45 local machine. If an SSH agent is running, this class can be used to
46 connect to it and retreive L{PKey} objects which can be used when
47 attempting to authenticate to remote SSH servers.
48
49 Because the SSH agent protocol uses environment variables and unix-domain
50 sockets, this probably doesn't work on Windows. It does work on most
51 posix platforms though (Linux and MacOS X, for example).
52 """
54 self._conn = None
55 self._keys = ()
56
58 """
59 Return the list of keys available through the SSH agent, if any. If
60 no SSH agent was running (or it couldn't be contacted), an empty list
61 will be returned.
62
63 @return: a list of keys available on the SSH agent
64 @rtype: tuple of L{AgentKey}
65 """
66 return self._keys
67
78
80
81 self._conn = None
82 self._keys = ()
83
85 msg = str(msg)
86 self._conn.send(struct.pack('>I', len(msg)) + msg)
87 l = self._read_all(4)
88 msg = Message(self._read_all(struct.unpack('>I', l)[0]))
89 return ord(msg.get_byte()), msg
90
92 result = self._conn.recv(wanted)
93 while len(result) < wanted:
94 if len(result) == 0:
95 raise SSHException('lost ssh-agent')
96 extra = self._conn.recv(wanted - len(result))
97 if len(extra) == 0:
98 raise SSHException('lost ssh-agent')
99 result += extra
100 return result
101
103 """ Class in charge of communication between two chan """
105 threading.Thread.__init__(self, target=self.run)
106 self._agent = agent
107 self._exit = False
108
110 try:
111 (r,addr) = self.get_connection()
112 self.__inr = r
113 self.__addr = addr
114 self._agent.connect()
115 self._communicate()
116 except:
117
118 raise
119
121 import fcntl
122 oldflags = fcntl.fcntl(self.__inr, fcntl.F_GETFL)
123 fcntl.fcntl(self.__inr, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
124 while not self._exit:
125 events = select([self._agent._conn, self.__inr], [], [], 0.5)
126 for fd in events[0]:
127 if self._agent._conn == fd:
128 data = self._agent._conn.recv(512)
129 if len(data) != 0:
130 self.__inr.send(data)
131 else:
132 break
133 elif self.__inr == fd:
134 data = self.__inr.recv(512)
135 if len(data) != 0:
136 self._agent._conn.send(data)
137 else:
138 break
139 time.sleep(io_sleep)
140
142 """
143 Class to be used when wanting to ask a local SSH Agent being
144 asked from a remote fake agent (so use a unix socket for ex.)
145 """
148
150 """ Return a pair of socket object and string address
151 May Block !
152 """
153 conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
154 try:
155 conn.bind(self._agent._get_filename())
156 conn.listen(1)
157 (r,addr) = conn.accept()
158 return (r, addr)
159 except:
160 raise
161 return None
162
164 """
165 Class to be used when wanting to ask a remote SSH Agent
166 """
170
172 """
173 Class to be used when wanting to ask a local SSH Agent being
174 asked from a remote fake agent (so use a unix socket for ex.)
175 """
176 return (self.__chan, None)
177
179 """
180 Class proxying request as a client:
181 -> client ask for a request_forward_agent()
182 -> server creates a proxy and a fake SSH Agent
183 -> server ask for establishing a connection when needed,
184 calling the forward_agent_handler at client side.
185 -> the forward_agent_handler launch a thread for connecting
186 the remote fake agent and the local agent
187 -> Communication occurs ...
188 """
190 self._conn = None
191 self.__chanR = chanRemote
192 self.thread = AgentRemoteProxy(self, chanRemote)
193 self.thread.start()
194
197
199 """
200 Method automatically called by the run() method of the AgentProxyThread
201 """
202 if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
203 conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
204 try:
205 conn.connect(os.environ['SSH_AUTH_SOCK'])
206 except:
207
208 return
209 elif sys.platform == 'win32':
210 import win_pageant
211 if win_pageant.can_talk_to_agent():
212 conn = win_pageant.PageantConnection()
213 else:
214 return
215 else:
216
217 return
218 self._conn = conn
219
221 """
222 Close the current connection and terminate the agent
223 Should be called manually
224 """
225 if hasattr(self, "thread"):
226 self.thread._exit = True
227 self.thread.join(1000)
228 if self._conn is not None:
229 self._conn.close()
230
232 """
233 @param t : transport used for the Forward for SSH Agent communication
234
235 @raise SSHException: mostly if we lost the agent
236 """
238 AgentSSH.__init__(self)
239 self.__t = t
240 self._dir = tempfile.mkdtemp('sshproxy')
241 os.chmod(self._dir, stat.S_IRWXU)
242 self._file = self._dir + '/sshproxy.ssh'
243 self.thread = AgentLocalProxy(self)
244 self.thread.start()
245
248
255
257 """
258 Terminate the agent, clean the files, close connections
259 Should be called manually
260 """
261 os.remove(self._file)
262 os.rmdir(self._dir)
263 self.thread._exit = True
264 self.thread.join(1000)
265 self._close()
266
268 """
269 Helper for the environnement under unix
270
271 @return: the SSH_AUTH_SOCK Environnement variables
272 @rtype: dict
273 """
274 env = {}
275 env['SSH_AUTH_SOCK'] = self._get_filename()
276 return env
277
280
283 self._conn = None
284 self.__chanC = chanClient
285 chanClient.request_forward_agent(self._forward_agent_handler)
286 self.__clientProxys = []
287
290
293
295 for p in self.__clientProxys:
296 p.close()
297
299 """
300 Client interface for using private keys from an SSH agent running on the
301 local machine. If an SSH agent is running, this class can be used to
302 connect to it and retreive L{PKey} objects which can be used when
303 attempting to authenticate to remote SSH servers.
304
305 Because the SSH agent protocol uses environment variables and unix-domain
306 sockets, this probably doesn't work on Windows. It does work on most
307 posix platforms though (Linux and MacOS X, for example).
308 """
309
311 """
312 Open a session with the local machine's SSH agent, if one is running.
313 If no agent is running, initialization will succeed, but L{get_keys}
314 will return an empty tuple.
315
316 @raise SSHException: if an SSH agent is found, but speaks an
317 incompatible protocol
318 """
319 AgentSSH.__init__(self)
320
321 if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
322 conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
323 try:
324 conn.connect(os.environ['SSH_AUTH_SOCK'])
325 except:
326
327 return
328 elif sys.platform == 'win32':
329 import win_pageant
330 if win_pageant.can_talk_to_agent():
331 conn = win_pageant.PageantConnection()
332 else:
333 return
334 else:
335
336 return
337 self._connect(conn)
338
340 """
341 Close the SSH agent connection.
342 """
343 self._close()
344
346 """
347 Private key held in a local SSH agent. This type of key can be used for
348 authenticating to a remote server (signing). Most other key operations
349 work as expected.
350 """
351
356
359
362
373