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