Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/client.py: 16%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

283 statements  

1# Copyright (C) 2006-2007 Robey Pointer <robeypointer@gmail.com> 

2# 

3# This file is part of paramiko. 

4# 

5# Paramiko is free software; you can redistribute it and/or modify it under the 

6# terms of the GNU Lesser General Public License as published by the Free 

7# Software Foundation; either version 2.1 of the License, or (at your option) 

8# any later version. 

9# 

10# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 

11# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 

12# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 

13# details. 

14# 

15# You should have received a copy of the GNU Lesser General Public License 

16# along with Paramiko; if not, write to the Free Software Foundation, Inc., 

17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 

18 

19""" 

20SSH client & key policies 

21""" 

22 

23from binascii import hexlify 

24import getpass 

25import inspect 

26import os 

27import socket 

28import warnings 

29from errno import ECONNREFUSED, EHOSTUNREACH 

30 

31from paramiko.agent import Agent 

32from paramiko.common import DEBUG 

33from paramiko.config import SSH_PORT 

34from paramiko.dsskey import DSSKey 

35from paramiko.ecdsakey import ECDSAKey 

36from paramiko.ed25519key import Ed25519Key 

37from paramiko.hostkeys import HostKeys 

38from paramiko.rsakey import RSAKey 

39from paramiko.ssh_exception import ( 

40 SSHException, 

41 BadHostKeyException, 

42 NoValidConnectionsError, 

43) 

44from paramiko.transport import Transport 

45from paramiko.util import ClosingContextManager 

46 

47 

48class SSHClient(ClosingContextManager): 

49 """ 

50 A high-level representation of a session with an SSH server. This class 

51 wraps `.Transport`, `.Channel`, and `.SFTPClient` to take care of most 

52 aspects of authenticating and opening channels. A typical use case is:: 

53 

54 client = SSHClient() 

55 client.load_system_host_keys() 

56 client.connect('ssh.example.com') 

57 stdin, stdout, stderr = client.exec_command('ls -l') 

58 

59 You may pass in explicit overrides for authentication and server host key 

60 checking. The default mechanism is to try to use local key files or an 

61 SSH agent (if one is running). 

62 

63 Instances of this class may be used as context managers. 

64 

65 .. versionadded:: 1.6 

66 """ 

67 

68 def __init__(self): 

69 """ 

70 Create a new SSHClient. 

71 """ 

72 self._system_host_keys = HostKeys() 

73 self._host_keys = HostKeys() 

74 self._host_keys_filename = None 

75 self._log_channel = None 

76 self._policy = RejectPolicy() 

77 self._transport = None 

78 self._agent = None 

79 

80 def load_system_host_keys(self, filename=None): 

81 """ 

82 Load host keys from a system (read-only) file. Host keys read with 

83 this method will not be saved back by `save_host_keys`. 

84 

85 This method can be called multiple times. Each new set of host keys 

86 will be merged with the existing set (new replacing old if there are 

87 conflicts). 

88 

89 If ``filename`` is left as ``None``, an attempt will be made to read 

90 keys from the user's local "known hosts" file, as used by OpenSSH, 

91 and no exception will be raised if the file can't be read. This is 

92 probably only useful on posix. 

93 

94 :param str filename: the filename to read, or ``None`` 

95 

96 :raises: ``IOError`` -- 

97 if a filename was provided and the file could not be read 

98 """ 

99 if filename is None: 

100 # try the user's .ssh key file, and mask exceptions 

101 filename = os.path.expanduser("~/.ssh/known_hosts") 

102 try: 

103 self._system_host_keys.load(filename) 

104 except IOError: 

105 pass 

106 return 

107 self._system_host_keys.load(filename) 

108 

109 def load_host_keys(self, filename): 

110 """ 

111 Load host keys from a local host-key file. Host keys read with this 

112 method will be checked after keys loaded via `load_system_host_keys`, 

113 but will be saved back by `save_host_keys` (so they can be modified). 

114 The missing host key policy `.AutoAddPolicy` adds keys to this set and 

115 saves them, when connecting to a previously-unknown server. 

116 

117 This method can be called multiple times. Each new set of host keys 

118 will be merged with the existing set (new replacing old if there are 

119 conflicts). When automatically saving, the last hostname is used. 

120 

121 :param str filename: the filename to read 

122 

123 :raises: ``IOError`` -- if the filename could not be read 

124 """ 

125 self._host_keys_filename = filename 

126 self._host_keys.load(filename) 

127 

128 def save_host_keys(self, filename): 

129 """ 

130 Save the host keys back to a file. Only the host keys loaded with 

131 `load_host_keys` (plus any added directly) will be saved -- not any 

132 host keys loaded with `load_system_host_keys`. 

133 

134 :param str filename: the filename to save to 

135 

136 :raises: ``IOError`` -- if the file could not be written 

137 """ 

138 

139 # update local host keys from file (in case other SSH clients 

140 # have written to the known_hosts file meanwhile. 

141 if self._host_keys_filename is not None: 

142 self.load_host_keys(self._host_keys_filename) 

143 

144 with open(filename, "w") as f: 

145 for hostname, keys in self._host_keys.items(): 

146 for keytype, key in keys.items(): 

147 f.write( 

148 "{} {} {}\n".format( 

149 hostname, keytype, key.get_base64() 

150 ) 

151 ) 

152 

153 def get_host_keys(self): 

154 """ 

155 Get the local `.HostKeys` object. This can be used to examine the 

156 local host keys or change them. 

157 

158 :return: the local host keys as a `.HostKeys` object. 

159 """ 

160 return self._host_keys 

161 

162 def set_log_channel(self, name): 

163 """ 

164 Set the channel for logging. The default is ``"paramiko.transport"`` 

165 but it can be set to anything you want. 

166 

167 :param str name: new channel name for logging 

168 """ 

169 self._log_channel = name 

170 

171 def set_missing_host_key_policy(self, policy): 

172 """ 

173 Set policy to use when connecting to servers without a known host key. 

174 

175 Specifically: 

176 

177 * A **policy** is a "policy class" (or instance thereof), namely some 

178 subclass of `.MissingHostKeyPolicy` such as `.RejectPolicy` (the 

179 default), `.AutoAddPolicy`, `.WarningPolicy`, or a user-created 

180 subclass. 

181 * A host key is **known** when it appears in the client object's cached 

182 host keys structures (those manipulated by `load_system_host_keys` 

183 and/or `load_host_keys`). 

184 

185 :param .MissingHostKeyPolicy policy: 

186 the policy to use when receiving a host key from a 

187 previously-unknown server 

188 """ 

189 if inspect.isclass(policy): 

190 policy = policy() 

191 self._policy = policy 

192 

193 def _families_and_addresses(self, hostname, port): 

194 """ 

195 Yield pairs of address families and addresses to try for connecting. 

196 

197 :param str hostname: the server to connect to 

198 :param int port: the server port to connect to 

199 :returns: Yields an iterable of ``(family, address)`` tuples 

200 """ 

201 guess = True 

202 addrinfos = socket.getaddrinfo( 

203 hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM 

204 ) 

205 for (family, socktype, proto, canonname, sockaddr) in addrinfos: 

206 if socktype == socket.SOCK_STREAM: 

207 yield family, sockaddr 

208 guess = False 

209 

210 # some OS like AIX don't indicate SOCK_STREAM support, so just 

211 # guess. :( We only do this if we did not get a single result marked 

212 # as socktype == SOCK_STREAM. 

213 if guess: 

214 for family, _, _, _, sockaddr in addrinfos: 

215 yield family, sockaddr 

216 

217 def connect( 

218 self, 

219 hostname, 

220 port=SSH_PORT, 

221 username=None, 

222 password=None, 

223 pkey=None, 

224 key_filename=None, 

225 timeout=None, 

226 allow_agent=True, 

227 look_for_keys=True, 

228 compress=False, 

229 sock=None, 

230 gss_auth=False, 

231 gss_kex=False, 

232 gss_deleg_creds=True, 

233 gss_host=None, 

234 banner_timeout=None, 

235 auth_timeout=None, 

236 channel_timeout=None, 

237 gss_trust_dns=True, 

238 passphrase=None, 

239 disabled_algorithms=None, 

240 transport_factory=None, 

241 auth_strategy=None, 

242 ): 

243 """ 

244 Connect to an SSH server and authenticate to it. The server's host key 

245 is checked against the system host keys (see `load_system_host_keys`) 

246 and any local host keys (`load_host_keys`). If the server's hostname 

247 is not found in either set of host keys, the missing host key policy 

248 is used (see `set_missing_host_key_policy`). The default policy is 

249 to reject the key and raise an `.SSHException`. 

250 

251 Authentication is attempted in the following order of priority: 

252 

253 - The ``pkey`` or ``key_filename`` passed in (if any) 

254 

255 - ``key_filename`` may contain OpenSSH public certificate paths 

256 as well as regular private-key paths; when files ending in 

257 ``-cert.pub`` are found, they are assumed to match a private 

258 key, and both components will be loaded. (The private key 

259 itself does *not* need to be listed in ``key_filename`` for 

260 this to occur - *just* the certificate.) 

261 

262 - Any key we can find through an SSH agent 

263 - Any "id_rsa", "id_dsa" or "id_ecdsa" key discoverable in 

264 ``~/.ssh/`` 

265 

266 - When OpenSSH-style public certificates exist that match an 

267 existing such private key (so e.g. one has ``id_rsa`` and 

268 ``id_rsa-cert.pub``) the certificate will be loaded alongside 

269 the private key and used for authentication. 

270 

271 - Plain username/password auth, if a password was given 

272 

273 If a private key requires a password to unlock it, and a password is 

274 passed in, that password will be used to attempt to unlock the key. 

275 

276 :param str hostname: the server to connect to 

277 :param int port: the server port to connect to 

278 :param str username: 

279 the username to authenticate as (defaults to the current local 

280 username) 

281 :param str password: 

282 Used for password authentication; is also used for private key 

283 decryption if ``passphrase`` is not given. 

284 :param str passphrase: 

285 Used for decrypting private keys. 

286 :param .PKey pkey: an optional private key to use for authentication 

287 :param str key_filename: 

288 the filename, or list of filenames, of optional private key(s) 

289 and/or certs to try for authentication 

290 :param float timeout: 

291 an optional timeout (in seconds) for the TCP connect 

292 :param bool allow_agent: 

293 set to False to disable connecting to the SSH agent 

294 :param bool look_for_keys: 

295 set to False to disable searching for discoverable private key 

296 files in ``~/.ssh/`` 

297 :param bool compress: set to True to turn on compression 

298 :param socket sock: 

299 an open socket or socket-like object (such as a `.Channel`) to use 

300 for communication to the target host 

301 :param bool gss_auth: 

302 ``True`` if you want to use GSS-API authentication 

303 :param bool gss_kex: 

304 Perform GSS-API Key Exchange and user authentication 

305 :param bool gss_deleg_creds: Delegate GSS-API client credentials or not 

306 :param str gss_host: 

307 The targets name in the kerberos database. default: hostname 

308 :param bool gss_trust_dns: 

309 Indicates whether or not the DNS is trusted to securely 

310 canonicalize the name of the host being connected to (default 

311 ``True``). 

312 :param float banner_timeout: an optional timeout (in seconds) to wait 

313 for the SSH banner to be presented. 

314 :param float auth_timeout: an optional timeout (in seconds) to wait for 

315 an authentication response. 

316 :param float channel_timeout: an optional timeout (in seconds) to wait 

317 for a channel open response. 

318 :param dict disabled_algorithms: 

319 an optional dict passed directly to `.Transport` and its keyword 

320 argument of the same name. 

321 :param transport_factory: 

322 an optional callable which is handed a subset of the constructor 

323 arguments (primarily those related to the socket, GSS 

324 functionality, and algorithm selection) and generates a 

325 `.Transport` instance to be used by this client. Defaults to 

326 `.Transport.__init__`. 

327 :param auth_strategy: 

328 an optional instance of `.AuthStrategy`, triggering use of this 

329 newer authentication mechanism instead of SSHClient's legacy auth 

330 method. 

331 

332 .. warning:: 

333 This parameter is **incompatible** with all other 

334 authentication-related parameters (such as, but not limited to, 

335 ``password``, ``key_filename`` and ``allow_agent``) and will 

336 trigger an exception if given alongside them. 

337 

338 :returns: 

339 `.AuthResult` if ``auth_strategy`` is non-``None``; otherwise, 

340 returns ``None``. 

341 

342 :raises BadHostKeyException: 

343 if the server's host key could not be verified. 

344 :raises AuthenticationException: 

345 if authentication failed. 

346 :raises UnableToAuthenticate: 

347 if authentication failed (when ``auth_strategy`` is non-``None``; 

348 and note that this is a subclass of ``AuthenticationException``). 

349 :raises socket.error: 

350 if a socket error (other than connection-refused or 

351 host-unreachable) occurred while connecting. 

352 :raises NoValidConnectionsError: 

353 if all valid connection targets for the requested hostname (eg IPv4 

354 and IPv6) yielded connection-refused or host-unreachable socket 

355 errors. 

356 :raises SSHException: 

357 if there was any other error connecting or establishing an SSH 

358 session. 

359 

360 .. versionchanged:: 1.15 

361 Added the ``banner_timeout``, ``gss_auth``, ``gss_kex``, 

362 ``gss_deleg_creds`` and ``gss_host`` arguments. 

363 .. versionchanged:: 2.3 

364 Added the ``gss_trust_dns`` argument. 

365 .. versionchanged:: 2.4 

366 Added the ``passphrase`` argument. 

367 .. versionchanged:: 2.6 

368 Added the ``disabled_algorithms`` argument. 

369 .. versionchanged:: 2.12 

370 Added the ``transport_factory`` argument. 

371 .. versionchanged:: 3.2 

372 Added the ``auth_strategy`` argument. 

373 """ 

374 if not sock: 

375 errors = {} 

376 # Try multiple possible address families (e.g. IPv4 vs IPv6) 

377 to_try = list(self._families_and_addresses(hostname, port)) 

378 for af, addr in to_try: 

379 try: 

380 sock = socket.socket(af, socket.SOCK_STREAM) 

381 if timeout is not None: 

382 try: 

383 sock.settimeout(timeout) 

384 except: 

385 pass 

386 sock.connect(addr) 

387 # Break out of the loop on success 

388 break 

389 except socket.error as e: 

390 # As mentioned in socket docs it is better 

391 # to close sockets explicitly 

392 if sock: 

393 sock.close() 

394 # Raise anything that isn't a straight up connection error 

395 # (such as a resolution error) 

396 if e.errno not in (ECONNREFUSED, EHOSTUNREACH): 

397 raise 

398 # Capture anything else so we know how the run looks once 

399 # iteration is complete. Retain info about which attempt 

400 # this was. 

401 errors[addr] = e 

402 

403 # Make sure we explode usefully if no address family attempts 

404 # succeeded. We've no way of knowing which error is the "right" 

405 # one, so we construct a hybrid exception containing all the real 

406 # ones, of a subclass that client code should still be watching for 

407 # (socket.error) 

408 if len(errors) == len(to_try): 

409 raise NoValidConnectionsError(errors) 

410 

411 if transport_factory is None: 

412 transport_factory = Transport 

413 t = self._transport = transport_factory( 

414 sock, 

415 gss_kex=gss_kex, 

416 gss_deleg_creds=gss_deleg_creds, 

417 disabled_algorithms=disabled_algorithms, 

418 ) 

419 t.use_compression(compress=compress) 

420 t.set_gss_host( 

421 # t.hostname may be None, but GSS-API requires a target name. 

422 # Therefore use hostname as fallback. 

423 gss_host=gss_host or hostname, 

424 trust_dns=gss_trust_dns, 

425 gssapi_requested=gss_auth or gss_kex, 

426 ) 

427 if self._log_channel is not None: 

428 t.set_log_channel(self._log_channel) 

429 if banner_timeout is not None: 

430 t.banner_timeout = banner_timeout 

431 if auth_timeout is not None: 

432 t.auth_timeout = auth_timeout 

433 if channel_timeout is not None: 

434 t.channel_timeout = channel_timeout 

435 

436 if port == SSH_PORT: 

437 server_hostkey_name = hostname 

438 else: 

439 server_hostkey_name = "[{}]:{}".format(hostname, port) 

440 our_server_keys = None 

441 

442 our_server_keys = self._system_host_keys.get(server_hostkey_name) 

443 if our_server_keys is None: 

444 our_server_keys = self._host_keys.get(server_hostkey_name) 

445 if our_server_keys is not None: 

446 keytype = our_server_keys.keys()[0] 

447 sec_opts = t.get_security_options() 

448 other_types = [x for x in sec_opts.key_types if x != keytype] 

449 sec_opts.key_types = [keytype] + other_types 

450 

451 t.start_client(timeout=timeout) 

452 

453 # If GSS-API Key Exchange is performed we are not required to check the 

454 # host key, because the host is authenticated via GSS-API / SSPI as 

455 # well as our client. 

456 if not self._transport.gss_kex_used: 

457 server_key = t.get_remote_server_key() 

458 if our_server_keys is None: 

459 # will raise exception if the key is rejected 

460 self._policy.missing_host_key( 

461 self, server_hostkey_name, server_key 

462 ) 

463 else: 

464 our_key = our_server_keys.get(server_key.get_name()) 

465 if our_key != server_key: 

466 if our_key is None: 

467 our_key = list(our_server_keys.values())[0] 

468 raise BadHostKeyException(hostname, server_key, our_key) 

469 

470 if username is None: 

471 username = getpass.getuser() 

472 

473 # New auth flow! 

474 if auth_strategy is not None: 

475 return auth_strategy.authenticate(transport=t) 

476 

477 # Old auth flow! 

478 if key_filename is None: 

479 key_filenames = [] 

480 elif isinstance(key_filename, str): 

481 key_filenames = [key_filename] 

482 else: 

483 key_filenames = key_filename 

484 

485 self._auth( 

486 username, 

487 password, 

488 pkey, 

489 key_filenames, 

490 allow_agent, 

491 look_for_keys, 

492 gss_auth, 

493 gss_kex, 

494 gss_deleg_creds, 

495 t.gss_host, 

496 passphrase, 

497 ) 

498 

499 def close(self): 

500 """ 

501 Close this SSHClient and its underlying `.Transport`. 

502 

503 This should be called anytime you are done using the client object. 

504 

505 .. warning:: 

506 Paramiko registers garbage collection hooks that will try to 

507 automatically close connections for you, but this is not presently 

508 reliable. Failure to explicitly close your client after use may 

509 lead to end-of-process hangs! 

510 """ 

511 if self._transport is None: 

512 return 

513 self._transport.close() 

514 self._transport = None 

515 

516 if self._agent is not None: 

517 self._agent.close() 

518 self._agent = None 

519 

520 def exec_command( 

521 self, 

522 command, 

523 bufsize=-1, 

524 timeout=None, 

525 get_pty=False, 

526 environment=None, 

527 ): 

528 """ 

529 Execute a command on the SSH server. A new `.Channel` is opened and 

530 the requested command is executed. The command's input and output 

531 streams are returned as Python ``file``-like objects representing 

532 stdin, stdout, and stderr. 

533 

534 :param str command: the command to execute 

535 :param int bufsize: 

536 interpreted the same way as by the built-in ``file()`` function in 

537 Python 

538 :param int timeout: 

539 set command's channel timeout. See `.Channel.settimeout` 

540 :param bool get_pty: 

541 Request a pseudo-terminal from the server (default ``False``). 

542 See `.Channel.get_pty` 

543 :param dict environment: 

544 a dict of shell environment variables, to be merged into the 

545 default environment that the remote command executes within. 

546 

547 .. warning:: 

548 Servers may silently reject some environment variables; see the 

549 warning in `.Channel.set_environment_variable` for details. 

550 

551 :return: 

552 the stdin, stdout, and stderr of the executing command, as a 

553 3-tuple 

554 

555 :raises: `.SSHException` -- if the server fails to execute the command 

556 

557 .. versionchanged:: 1.10 

558 Added the ``get_pty`` kwarg. 

559 """ 

560 chan = self._transport.open_session(timeout=timeout) 

561 if get_pty: 

562 chan.get_pty() 

563 chan.settimeout(timeout) 

564 if environment: 

565 chan.update_environment(environment) 

566 chan.exec_command(command) 

567 stdin = chan.makefile_stdin("wb", bufsize) 

568 stdout = chan.makefile("r", bufsize) 

569 stderr = chan.makefile_stderr("r", bufsize) 

570 return stdin, stdout, stderr 

571 

572 def invoke_shell( 

573 self, 

574 term="vt100", 

575 width=80, 

576 height=24, 

577 width_pixels=0, 

578 height_pixels=0, 

579 environment=None, 

580 ): 

581 """ 

582 Start an interactive shell session on the SSH server. A new `.Channel` 

583 is opened and connected to a pseudo-terminal using the requested 

584 terminal type and size. 

585 

586 :param str term: 

587 the terminal type to emulate (for example, ``"vt100"``) 

588 :param int width: the width (in characters) of the terminal window 

589 :param int height: the height (in characters) of the terminal window 

590 :param int width_pixels: the width (in pixels) of the terminal window 

591 :param int height_pixels: the height (in pixels) of the terminal window 

592 :param dict environment: the command's environment 

593 :return: a new `.Channel` connected to the remote shell 

594 

595 :raises: `.SSHException` -- if the server fails to invoke a shell 

596 """ 

597 chan = self._transport.open_session() 

598 chan.get_pty(term, width, height, width_pixels, height_pixels) 

599 chan.invoke_shell() 

600 return chan 

601 

602 def open_sftp(self): 

603 """ 

604 Open an SFTP session on the SSH server. 

605 

606 :return: a new `.SFTPClient` session object 

607 """ 

608 return self._transport.open_sftp_client() 

609 

610 def get_transport(self): 

611 """ 

612 Return the underlying `.Transport` object for this SSH connection. 

613 This can be used to perform lower-level tasks, like opening specific 

614 kinds of channels. 

615 

616 :return: the `.Transport` for this connection 

617 """ 

618 return self._transport 

619 

620 def _key_from_filepath(self, filename, klass, password): 

621 """ 

622 Attempt to derive a `.PKey` from given string path ``filename``: 

623 

624 - If ``filename`` appears to be a cert, the matching private key is 

625 loaded. 

626 - Otherwise, the filename is assumed to be a private key, and the 

627 matching public cert will be loaded if it exists. 

628 """ 

629 cert_suffix = "-cert.pub" 

630 # Assume privkey, not cert, by default 

631 if filename.endswith(cert_suffix): 

632 key_path = filename[: -len(cert_suffix)] 

633 cert_path = filename 

634 else: 

635 key_path = filename 

636 cert_path = filename + cert_suffix 

637 # Blindly try the key path; if no private key, nothing will work. 

638 key = klass.from_private_key_file(key_path, password) 

639 # TODO: change this to 'Loading' instead of 'Trying' sometime; probably 

640 # when #387 is released, since this is a critical log message users are 

641 # likely testing/filtering for (bah.) 

642 msg = "Trying discovered key {} in {}".format( 

643 hexlify(key.get_fingerprint()), key_path 

644 ) 

645 self._log(DEBUG, msg) 

646 # Attempt to load cert if it exists. 

647 if os.path.isfile(cert_path): 

648 key.load_certificate(cert_path) 

649 self._log(DEBUG, "Adding public certificate {}".format(cert_path)) 

650 return key 

651 

652 def _auth( 

653 self, 

654 username, 

655 password, 

656 pkey, 

657 key_filenames, 

658 allow_agent, 

659 look_for_keys, 

660 gss_auth, 

661 gss_kex, 

662 gss_deleg_creds, 

663 gss_host, 

664 passphrase, 

665 ): 

666 """ 

667 Try, in order: 

668 

669 - The key(s) passed in, if one was passed in. 

670 - Any key we can find through an SSH agent (if allowed). 

671 - Any "id_rsa", "id_dsa" or "id_ecdsa" key discoverable in ~/.ssh/ 

672 (if allowed). 

673 - Plain username/password auth, if a password was given. 

674 

675 (The password might be needed to unlock a private key [if 'passphrase' 

676 isn't also given], or for two-factor authentication [for which it is 

677 required].) 

678 """ 

679 saved_exception = None 

680 two_factor = False 

681 allowed_types = set() 

682 two_factor_types = {"keyboard-interactive", "password"} 

683 if passphrase is None and password is not None: 

684 passphrase = password 

685 

686 # If GSS-API support and GSS-PI Key Exchange was performed, we attempt 

687 # authentication with gssapi-keyex. 

688 if gss_kex and self._transport.gss_kex_used: 

689 try: 

690 self._transport.auth_gssapi_keyex(username) 

691 return 

692 except Exception as e: 

693 saved_exception = e 

694 

695 # Try GSS-API authentication (gssapi-with-mic) only if GSS-API Key 

696 # Exchange is not performed, because if we use GSS-API for the key 

697 # exchange, there is already a fully established GSS-API context, so 

698 # why should we do that again? 

699 if gss_auth: 

700 try: 

701 return self._transport.auth_gssapi_with_mic( 

702 username, gss_host, gss_deleg_creds 

703 ) 

704 except Exception as e: 

705 saved_exception = e 

706 

707 if pkey is not None: 

708 try: 

709 self._log( 

710 DEBUG, 

711 "Trying SSH key {}".format( 

712 hexlify(pkey.get_fingerprint()) 

713 ), 

714 ) 

715 allowed_types = set( 

716 self._transport.auth_publickey(username, pkey) 

717 ) 

718 two_factor = allowed_types & two_factor_types 

719 if not two_factor: 

720 return 

721 except SSHException as e: 

722 saved_exception = e 

723 

724 if not two_factor: 

725 for key_filename in key_filenames: 

726 # TODO 4.0: leverage PKey.from_path() if we don't end up just 

727 # killing SSHClient entirely 

728 for pkey_class in (RSAKey, DSSKey, ECDSAKey, Ed25519Key): 

729 try: 

730 key = self._key_from_filepath( 

731 key_filename, pkey_class, passphrase 

732 ) 

733 allowed_types = set( 

734 self._transport.auth_publickey(username, key) 

735 ) 

736 two_factor = allowed_types & two_factor_types 

737 if not two_factor: 

738 return 

739 break 

740 except SSHException as e: 

741 saved_exception = e 

742 

743 if not two_factor and allow_agent: 

744 if self._agent is None: 

745 self._agent = Agent() 

746 

747 for key in self._agent.get_keys(): 

748 try: 

749 id_ = hexlify(key.get_fingerprint()) 

750 self._log(DEBUG, "Trying SSH agent key {}".format(id_)) 

751 # for 2-factor auth a successfully auth'd key password 

752 # will return an allowed 2fac auth method 

753 allowed_types = set( 

754 self._transport.auth_publickey(username, key) 

755 ) 

756 two_factor = allowed_types & two_factor_types 

757 if not two_factor: 

758 return 

759 break 

760 except SSHException as e: 

761 saved_exception = e 

762 

763 if not two_factor: 

764 keyfiles = [] 

765 

766 for keytype, name in [ 

767 (RSAKey, "rsa"), 

768 (DSSKey, "dsa"), 

769 (ECDSAKey, "ecdsa"), 

770 (Ed25519Key, "ed25519"), 

771 ]: 

772 # ~/ssh/ is for windows 

773 for directory in [".ssh", "ssh"]: 

774 full_path = os.path.expanduser( 

775 "~/{}/id_{}".format(directory, name) 

776 ) 

777 if os.path.isfile(full_path): 

778 # TODO: only do this append if below did not run 

779 keyfiles.append((keytype, full_path)) 

780 if os.path.isfile(full_path + "-cert.pub"): 

781 keyfiles.append((keytype, full_path + "-cert.pub")) 

782 

783 if not look_for_keys: 

784 keyfiles = [] 

785 

786 for pkey_class, filename in keyfiles: 

787 try: 

788 key = self._key_from_filepath( 

789 filename, pkey_class, passphrase 

790 ) 

791 # for 2-factor auth a successfully auth'd key will result 

792 # in ['password'] 

793 allowed_types = set( 

794 self._transport.auth_publickey(username, key) 

795 ) 

796 two_factor = allowed_types & two_factor_types 

797 if not two_factor: 

798 return 

799 break 

800 except (SSHException, IOError) as e: 

801 saved_exception = e 

802 

803 if password is not None: 

804 try: 

805 self._transport.auth_password(username, password) 

806 return 

807 except SSHException as e: 

808 saved_exception = e 

809 elif two_factor: 

810 try: 

811 self._transport.auth_interactive_dumb(username) 

812 return 

813 except SSHException as e: 

814 saved_exception = e 

815 

816 # if we got an auth-failed exception earlier, re-raise it 

817 if saved_exception is not None: 

818 raise saved_exception 

819 raise SSHException("No authentication methods available") 

820 

821 def _log(self, level, msg): 

822 self._transport._log(level, msg) 

823 

824 

825class MissingHostKeyPolicy: 

826 """ 

827 Interface for defining the policy that `.SSHClient` should use when the 

828 SSH server's hostname is not in either the system host keys or the 

829 application's keys. Pre-made classes implement policies for automatically 

830 adding the key to the application's `.HostKeys` object (`.AutoAddPolicy`), 

831 and for automatically rejecting the key (`.RejectPolicy`). 

832 

833 This function may be used to ask the user to verify the key, for example. 

834 """ 

835 

836 def missing_host_key(self, client, hostname, key): 

837 """ 

838 Called when an `.SSHClient` receives a server key for a server that 

839 isn't in either the system or local `.HostKeys` object. To accept 

840 the key, simply return. To reject, raised an exception (which will 

841 be passed to the calling application). 

842 """ 

843 pass 

844 

845 

846class AutoAddPolicy(MissingHostKeyPolicy): 

847 """ 

848 Policy for automatically adding the hostname and new host key to the 

849 local `.HostKeys` object, and saving it. This is used by `.SSHClient`. 

850 """ 

851 

852 def missing_host_key(self, client, hostname, key): 

853 client._host_keys.add(hostname, key.get_name(), key) 

854 if client._host_keys_filename is not None: 

855 client.save_host_keys(client._host_keys_filename) 

856 client._log( 

857 DEBUG, 

858 "Adding {} host key for {}: {}".format( 

859 key.get_name(), hostname, hexlify(key.get_fingerprint()) 

860 ), 

861 ) 

862 

863 

864class RejectPolicy(MissingHostKeyPolicy): 

865 """ 

866 Policy for automatically rejecting the unknown hostname & key. This is 

867 used by `.SSHClient`. 

868 """ 

869 

870 def missing_host_key(self, client, hostname, key): 

871 client._log( 

872 DEBUG, 

873 "Rejecting {} host key for {}: {}".format( 

874 key.get_name(), hostname, hexlify(key.get_fingerprint()) 

875 ), 

876 ) 

877 raise SSHException( 

878 "Server {!r} not found in known_hosts".format(hostname) 

879 ) 

880 

881 

882class WarningPolicy(MissingHostKeyPolicy): 

883 """ 

884 Policy for logging a Python-style warning for an unknown host key, but 

885 accepting it. This is used by `.SSHClient`. 

886 """ 

887 

888 def missing_host_key(self, client, hostname, key): 

889 warnings.warn( 

890 "Unknown {} host key for {}: {}".format( 

891 key.get_name(), hostname, hexlify(key.get_fingerprint()) 

892 ) 

893 )