1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 L{SSHClient}.
21 """
22
23 from binascii import hexlify
24 import getpass
25 import os
26 import socket
27 import warnings
28
29 from paramiko.agent import Agent
30 from paramiko.common import *
31 from paramiko.config import SSH_PORT
32 from paramiko.dsskey import DSSKey
33 from paramiko.hostkeys import HostKeys
34 from paramiko.resource import ResourceManager
35 from paramiko.rsakey import RSAKey
36 from paramiko.ssh_exception import SSHException, BadHostKeyException
37 from paramiko.transport import Transport
38 from paramiko.util import retry_on_signal
39
40
42 """
43 Interface for defining the policy that L{SSHClient} should use when the
44 SSH server's hostname is not in either the system host keys or the
45 application's keys. Pre-made classes implement policies for automatically
46 adding the key to the application's L{HostKeys} object (L{AutoAddPolicy}),
47 and for automatically rejecting the key (L{RejectPolicy}).
48
49 This function may be used to ask the user to verify the key, for example.
50 """
51
53 """
54 Called when an L{SSHClient} receives a server key for a server that
55 isn't in either the system or local L{HostKeys} object. To accept
56 the key, simply return. To reject, raised an exception (which will
57 be passed to the calling application).
58 """
59 pass
60
61
63 """
64 Policy for automatically adding the hostname and new host key to the
65 local L{HostKeys} object, and saving it. This is used by L{SSHClient}.
66 """
67
74
75
77 """
78 Policy for automatically rejecting the unknown hostname & key. This is
79 used by L{SSHClient}.
80 """
81
86
87
89 """
90 Policy for logging a python-style warning for an unknown host key, but
91 accepting it. This is used by L{SSHClient}.
92 """
96
97
99 """
100 A high-level representation of a session with an SSH server. This class
101 wraps L{Transport}, L{Channel}, and L{SFTPClient} to take care of most
102 aspects of authenticating and opening channels. A typical use case is::
103
104 client = SSHClient()
105 client.load_system_host_keys()
106 client.connect('ssh.example.com')
107 stdin, stdout, stderr = client.exec_command('ls -l')
108
109 You may pass in explicit overrides for authentication and server host key
110 checking. The default mechanism is to try to use local key files or an
111 SSH agent (if one is running).
112
113 @since: 1.6
114 """
115
117 """
118 Create a new SSHClient.
119 """
120 self._system_host_keys = HostKeys()
121 self._host_keys = HostKeys()
122 self._host_keys_filename = None
123 self._log_channel = None
124 self._policy = RejectPolicy()
125 self._transport = None
126 self._agent = None
127
129 """
130 Load host keys from a system (read-only) file. Host keys read with
131 this method will not be saved back by L{save_host_keys}.
132
133 This method can be called multiple times. Each new set of host keys
134 will be merged with the existing set (new replacing old if there are
135 conflicts).
136
137 If C{filename} is left as C{None}, an attempt will be made to read
138 keys from the user's local "known hosts" file, as used by OpenSSH,
139 and no exception will be raised if the file can't be read. This is
140 probably only useful on posix.
141
142 @param filename: the filename to read, or C{None}
143 @type filename: str
144
145 @raise IOError: if a filename was provided and the file could not be
146 read
147 """
148 if filename is None:
149
150 filename = os.path.expanduser('~/.ssh/known_hosts')
151 try:
152 self._system_host_keys.load(filename)
153 except IOError:
154 pass
155 return
156 self._system_host_keys.load(filename)
157
159 """
160 Load host keys from a local host-key file. Host keys read with this
161 method will be checked I{after} keys loaded via L{load_system_host_keys},
162 but will be saved back by L{save_host_keys} (so they can be modified).
163 The missing host key policy L{AutoAddPolicy} adds keys to this set and
164 saves them, when connecting to a previously-unknown server.
165
166 This method can be called multiple times. Each new set of host keys
167 will be merged with the existing set (new replacing old if there are
168 conflicts). When automatically saving, the last hostname is used.
169
170 @param filename: the filename to read
171 @type filename: str
172
173 @raise IOError: if the filename could not be read
174 """
175 self._host_keys_filename = filename
176 self._host_keys.load(filename)
177
179 """
180 Save the host keys back to a file. Only the host keys loaded with
181 L{load_host_keys} (plus any added directly) will be saved -- not any
182 host keys loaded with L{load_system_host_keys}.
183
184 @param filename: the filename to save to
185 @type filename: str
186
187 @raise IOError: if the file could not be written
188 """
189 f = open(filename, 'w')
190 f.write('# SSH host keys collected by paramiko\n')
191 for hostname, keys in self._host_keys.iteritems():
192 for keytype, key in keys.iteritems():
193 f.write('%s %s %s\n' % (hostname, keytype, key.get_base64()))
194 f.close()
195
197 """
198 Get the local L{HostKeys} object. This can be used to examine the
199 local host keys or change them.
200
201 @return: the local host keys
202 @rtype: L{HostKeys}
203 """
204 return self._host_keys
205
207 """
208 Set the channel for logging. The default is C{"paramiko.transport"}
209 but it can be set to anything you want.
210
211 @param name: new channel name for logging
212 @type name: str
213 """
214 self._log_channel = name
215
217 """
218 Set the policy to use when connecting to a server that doesn't have a
219 host key in either the system or local L{HostKeys} objects. The
220 default policy is to reject all unknown servers (using L{RejectPolicy}).
221 You may substitute L{AutoAddPolicy} or write your own policy class.
222
223 @param policy: the policy to use when receiving a host key from a
224 previously-unknown server
225 @type policy: L{MissingHostKeyPolicy}
226 """
227 self._policy = policy
228
229 - def connect(self, hostname, port=SSH_PORT, username=None, password=None, pkey=None,
230 key_filename=None, timeout=None, allow_agent=True, look_for_keys=True,
231 compress=False, sock=None):
232 """
233 Connect to an SSH server and authenticate to it. The server's host key
234 is checked against the system host keys (see L{load_system_host_keys})
235 and any local host keys (L{load_host_keys}). If the server's hostname
236 is not found in either set of host keys, the missing host key policy
237 is used (see L{set_missing_host_key_policy}). The default policy is
238 to reject the key and raise an L{SSHException}.
239
240 Authentication is attempted in the following order of priority:
241
242 - The C{pkey} or C{key_filename} passed in (if any)
243 - Any key we can find through an SSH agent
244 - Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/}
245 - Plain username/password auth, if a password was given
246
247 If a private key requires a password to unlock it, and a password is
248 passed in, that password will be used to attempt to unlock the key.
249
250 @param hostname: the server to connect to
251 @type hostname: str
252 @param port: the server port to connect to
253 @type port: int
254 @param username: the username to authenticate as (defaults to the
255 current local username)
256 @type username: str
257 @param password: a password to use for authentication or for unlocking
258 a private key
259 @type password: str
260 @param pkey: an optional private key to use for authentication
261 @type pkey: L{PKey}
262 @param key_filename: the filename, or list of filenames, of optional
263 private key(s) to try for authentication
264 @type key_filename: str or list(str)
265 @param timeout: an optional timeout (in seconds) for the TCP connect
266 @type timeout: float
267 @param allow_agent: set to False to disable connecting to the SSH agent
268 @type allow_agent: bool
269 @param look_for_keys: set to False to disable searching for discoverable
270 private key files in C{~/.ssh/}
271 @type look_for_keys: bool
272 @param compress: set to True to turn on compression
273 @type compress: bool
274 @param sock: an open socket or socket-like object (such as a
275 L{Channel}) to use for communication to the target host
276 @type sock: socket
277
278 @raise BadHostKeyException: if the server's host key could not be
279 verified
280 @raise AuthenticationException: if authentication failed
281 @raise SSHException: if there was any other error connecting or
282 establishing an SSH session
283 @raise socket.error: if a socket error occurred while connecting
284 """
285 if not sock:
286 for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM):
287 if socktype == socket.SOCK_STREAM:
288 af = family
289 addr = sockaddr
290 break
291 else:
292
293 af, _, _, _, addr = socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
294 sock = socket.socket(af, socket.SOCK_STREAM)
295 if timeout is not None:
296 try:
297 sock.settimeout(timeout)
298 except:
299 pass
300 retry_on_signal(lambda: sock.connect(addr))
301
302 t = self._transport = Transport(sock)
303 t.use_compression(compress=compress)
304 if self._log_channel is not None:
305 t.set_log_channel(self._log_channel)
306 t.start_client()
307 ResourceManager.register(self, t)
308
309 server_key = t.get_remote_server_key()
310 keytype = server_key.get_name()
311
312 if port == SSH_PORT:
313 server_hostkey_name = hostname
314 else:
315 server_hostkey_name = "[%s]:%d" % (hostname, port)
316 our_server_key = self._system_host_keys.get(server_hostkey_name, {}).get(keytype, None)
317 if our_server_key is None:
318 our_server_key = self._host_keys.get(server_hostkey_name, {}).get(keytype, None)
319 if our_server_key is None:
320
321 self._policy.missing_host_key(self, server_hostkey_name, server_key)
322
323 our_server_key = server_key
324
325 if server_key != our_server_key:
326 raise BadHostKeyException(hostname, server_key, our_server_key)
327
328 if username is None:
329 username = getpass.getuser()
330
331 if key_filename is None:
332 key_filenames = []
333 elif isinstance(key_filename, (str, unicode)):
334 key_filenames = [ key_filename ]
335 else:
336 key_filenames = key_filename
337 self._auth(username, password, pkey, key_filenames, allow_agent, look_for_keys)
338
340 """
341 Close this SSHClient and its underlying L{Transport}.
342 """
343 if self._transport is None:
344 return
345 self._transport.close()
346 self._transport = None
347
348 if self._agent != None:
349 self._agent.close()
350 self._agent = None
351
352 - def exec_command(self, command, bufsize=-1, timeout=None, get_pty=False):
353 """
354 Execute a command on the SSH server. A new L{Channel} is opened and
355 the requested command is executed. The command's input and output
356 streams are returned as python C{file}-like objects representing
357 stdin, stdout, and stderr.
358
359 @param command: the command to execute
360 @type command: str
361 @param bufsize: interpreted the same way as by the built-in C{file()} function in python
362 @type bufsize: int
363 @param timeout: set command's channel timeout. See L{Channel.settimeout}.settimeout
364 @type timeout: int
365 @return: the stdin, stdout, and stderr of the executing command
366 @rtype: tuple(L{ChannelFile}, L{ChannelFile}, L{ChannelFile})
367
368 @raise SSHException: if the server fails to execute the command
369 """
370 chan = self._transport.open_session()
371 if(get_pty):
372 chan.get_pty()
373 chan.settimeout(timeout)
374 chan.exec_command(command)
375 stdin = chan.makefile('wb', bufsize)
376 stdout = chan.makefile('rb', bufsize)
377 stderr = chan.makefile_stderr('rb', bufsize)
378 return stdin, stdout, stderr
379
380 - def invoke_shell(self, term='vt100', width=80, height=24, width_pixels=0,
381 height_pixels=0):
382 """
383 Start an interactive shell session on the SSH server. A new L{Channel}
384 is opened and connected to a pseudo-terminal using the requested
385 terminal type and size.
386
387 @param term: the terminal type to emulate (for example, C{"vt100"})
388 @type term: str
389 @param width: the width (in characters) of the terminal window
390 @type width: int
391 @param height: the height (in characters) of the terminal window
392 @type height: int
393 @param width_pixels: the width (in pixels) of the terminal window
394 @type width_pixels: int
395 @param height_pixels: the height (in pixels) of the terminal window
396 @type height_pixels: int
397 @return: a new channel connected to the remote shell
398 @rtype: L{Channel}
399
400 @raise SSHException: if the server fails to invoke a shell
401 """
402 chan = self._transport.open_session()
403 chan.get_pty(term, width, height, width_pixels, height_pixels)
404 chan.invoke_shell()
405 return chan
406
408 """
409 Open an SFTP session on the SSH server.
410
411 @return: a new SFTP session object
412 @rtype: L{SFTPClient}
413 """
414 return self._transport.open_sftp_client()
415
417 """
418 Return the underlying L{Transport} object for this SSH connection.
419 This can be used to perform lower-level tasks, like opening specific
420 kinds of channels.
421
422 @return: the Transport for this connection
423 @rtype: L{Transport}
424 """
425 return self._transport
426
427 - def _auth(self, username, password, pkey, key_filenames, allow_agent, look_for_keys):
428 """
429 Try, in order:
430
431 - The key passed in, if one was passed in.
432 - Any key we can find through an SSH agent (if allowed).
433 - Any "id_rsa" or "id_dsa" key discoverable in ~/.ssh/ (if allowed).
434 - Plain username/password auth, if a password was given.
435
436 (The password might be needed to unlock a private key, or for
437 two-factor authentication [for which it is required].)
438 """
439 saved_exception = None
440 two_factor = False
441 allowed_types = []
442
443 if pkey is not None:
444 try:
445 self._log(DEBUG, 'Trying SSH key %s' % hexlify(pkey.get_fingerprint()))
446 allowed_types = self._transport.auth_publickey(username, pkey)
447 two_factor = (allowed_types == ['password'])
448 if not two_factor:
449 return
450 except SSHException, e:
451 saved_exception = e
452
453 if not two_factor:
454 for key_filename in key_filenames:
455 for pkey_class in (RSAKey, DSSKey):
456 try:
457 key = pkey_class.from_private_key_file(key_filename, password)
458 self._log(DEBUG, 'Trying key %s from %s' % (hexlify(key.get_fingerprint()), key_filename))
459 self._transport.auth_publickey(username, key)
460 two_factor = (allowed_types == ['password'])
461 if not two_factor:
462 return
463 break
464 except SSHException, e:
465 saved_exception = e
466
467 if not two_factor and allow_agent:
468 if self._agent == None:
469 self._agent = Agent()
470
471 for key in self._agent.get_keys():
472 try:
473 self._log(DEBUG, 'Trying SSH agent key %s' % hexlify(key.get_fingerprint()))
474
475 allowed_types = self._transport.auth_publickey(username, key)
476 two_factor = (allowed_types == ['password'])
477 if not two_factor:
478 return
479 break
480 except SSHException, e:
481 saved_exception = e
482
483 if not two_factor:
484 keyfiles = []
485 rsa_key = os.path.expanduser('~/.ssh/id_rsa')
486 dsa_key = os.path.expanduser('~/.ssh/id_dsa')
487 if os.path.isfile(rsa_key):
488 keyfiles.append((RSAKey, rsa_key))
489 if os.path.isfile(dsa_key):
490 keyfiles.append((DSSKey, dsa_key))
491
492 rsa_key = os.path.expanduser('~/ssh/id_rsa')
493 dsa_key = os.path.expanduser('~/ssh/id_dsa')
494 if os.path.isfile(rsa_key):
495 keyfiles.append((RSAKey, rsa_key))
496 if os.path.isfile(dsa_key):
497 keyfiles.append((DSSKey, dsa_key))
498
499 if not look_for_keys:
500 keyfiles = []
501
502 for pkey_class, filename in keyfiles:
503 try:
504 key = pkey_class.from_private_key_file(filename, password)
505 self._log(DEBUG, 'Trying discovered key %s in %s' % (hexlify(key.get_fingerprint()), filename))
506
507 allowed_types = self._transport.auth_publickey(username, key)
508 two_factor = (allowed_types == ['password'])
509 if not two_factor:
510 return
511 break
512 except SSHException, e:
513 saved_exception = e
514 except IOError, e:
515 saved_exception = e
516
517 if password is not None:
518 try:
519 self._transport.auth_password(username, password)
520 return
521 except SSHException, e:
522 saved_exception = e
523 elif two_factor:
524 raise SSHException('Two-factor authentication requires a password')
525
526
527 if saved_exception is not None:
528 raise saved_exception
529 raise SSHException('No authentication methods available')
530
531 - def _log(self, level, msg):
532 self._transport._log(level, msg)
533