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

282 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.ecdsakey import ECDSAKey 

35from paramiko.ed25519key import Ed25519Key 

36from paramiko.hostkeys import HostKeys 

37from paramiko.rsakey import RSAKey 

38from paramiko.ssh_exception import ( 

39 SSHException, 

40 BadHostKeyException, 

41 NoValidConnectionsError, 

42) 

43from paramiko.transport import Transport 

44from paramiko.util import ClosingContextManager 

45 

46 

47class SSHClient(ClosingContextManager): 

48 """ 

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

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

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

52 

53 client = SSHClient() 

54 client.load_system_host_keys() 

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

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

57 

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

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

60 SSH agent (if one is running). 

61 

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

63 

64 .. versionadded:: 1.6 

65 """ 

66 

67 def __init__(self): 

68 """ 

69 Create a new SSHClient. 

70 """ 

71 self._system_host_keys = HostKeys() 

72 self._host_keys = HostKeys() 

73 self._host_keys_filename = None 

74 self._log_channel = None 

75 self._policy = RejectPolicy() 

76 self._transport = None 

77 self._agent = None 

78 

79 def load_system_host_keys(self, filename=None): 

80 """ 

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

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

83 

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

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

86 conflicts). 

87 

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

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

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

91 probably only useful on posix. 

92 

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

94 

95 :raises: ``IOError`` -- 

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

97 """ 

98 if filename is None: 

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

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

101 try: 

102 self._system_host_keys.load(filename) 

103 except IOError: 

104 pass 

105 return 

106 self._system_host_keys.load(filename) 

107 

108 def load_host_keys(self, filename): 

109 """ 

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

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

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

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

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

115 

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

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

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

119 

120 :param str filename: the filename to read 

121 

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

123 """ 

124 self._host_keys_filename = filename 

125 self._host_keys.load(filename) 

126 

127 def save_host_keys(self, filename): 

128 """ 

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

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

131 host keys loaded with `load_system_host_keys`. 

132 

133 :param str filename: the filename to save to 

134 

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

136 """ 

137 

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

139 # have written to the known_hosts file meanwhile. 

140 if self._host_keys_filename is not None: 

141 self.load_host_keys(self._host_keys_filename) 

142 

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

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

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

146 f.write( 

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

148 hostname, keytype, key.get_base64() 

149 ) 

150 ) 

151 

152 def get_host_keys(self): 

153 """ 

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

155 local host keys or change them. 

156 

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

158 """ 

159 return self._host_keys 

160 

161 def set_log_channel(self, name): 

162 """ 

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

164 but it can be set to anything you want. 

165 

166 :param str name: new channel name for logging 

167 """ 

168 self._log_channel = name 

169 

170 def set_missing_host_key_policy(self, policy): 

171 """ 

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

173 

174 Specifically: 

175 

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

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

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

179 subclass. 

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

181 host keys structures (those manipulated by `load_system_host_keys` 

182 and/or `load_host_keys`). 

183 

184 :param .MissingHostKeyPolicy policy: 

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

186 previously-unknown server 

187 """ 

188 if inspect.isclass(policy): 

189 policy = policy() 

190 self._policy = policy 

191 

192 def _families_and_addresses(self, hostname, port): 

193 """ 

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

195 

196 :param str hostname: the server to connect to 

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

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

199 """ 

200 guess = True 

201 addrinfos = socket.getaddrinfo( 

202 hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM 

203 ) 

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

205 if socktype == socket.SOCK_STREAM: 

206 yield family, sockaddr 

207 guess = False 

208 

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

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

211 # as socktype == SOCK_STREAM. 

212 if guess: 

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

214 yield family, sockaddr 

215 

216 def connect( 

217 self, 

218 hostname, 

219 port=SSH_PORT, 

220 username=None, 

221 password=None, 

222 pkey=None, 

223 key_filename=None, 

224 timeout=None, 

225 allow_agent=True, 

226 look_for_keys=True, 

227 compress=False, 

228 sock=None, 

229 gss_auth=False, 

230 gss_kex=False, 

231 gss_deleg_creds=True, 

232 gss_host=None, 

233 banner_timeout=None, 

234 auth_timeout=None, 

235 channel_timeout=None, 

236 gss_trust_dns=True, 

237 passphrase=None, 

238 disabled_algorithms=None, 

239 transport_factory=None, 

240 auth_strategy=None, 

241 ): 

242 """ 

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

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

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

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

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

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

249 

250 Authentication is attempted in the following order of priority: 

251 

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

253 

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

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

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

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

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

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

260 

261 - Any key we can find through an SSH agent 

262 - Any ``id_*`` keys discoverable in ``~/.ssh/`` 

263 

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

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

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

267 the private key and used for authentication. 

268 

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

270 

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

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

273 

274 :param str hostname: the server to connect to 

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

276 :param str username: 

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

278 username) 

279 :param str password: 

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

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

282 :param str passphrase: 

283 Used for decrypting private keys. 

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

285 :param str key_filename: 

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

287 and/or certs to try for authentication 

288 :param float timeout: 

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

290 :param bool allow_agent: 

291 set to False to disable connecting to the SSH agent 

292 :param bool look_for_keys: 

293 set to False to disable searching for discoverable private key 

294 files in ``~/.ssh/`` 

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

296 :param socket sock: 

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

298 for communication to the target host 

299 :param bool gss_auth: 

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

301 :param bool gss_kex: 

302 Perform GSS-API Key Exchange and user authentication 

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

304 :param str gss_host: 

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

306 :param bool gss_trust_dns: 

307 Indicates whether or not the DNS is trusted to securely 

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

309 ``True``). 

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

311 for the SSH banner to be presented. 

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

313 an authentication response. 

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

315 for a channel open response. 

316 :param dict disabled_algorithms: 

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

318 argument of the same name. 

319 :param transport_factory: 

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

321 arguments (primarily those related to the socket, GSS 

322 functionality, and algorithm selection) and generates a 

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

324 `.Transport.__init__`. 

325 :param auth_strategy: 

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

327 newer authentication mechanism instead of SSHClient's legacy auth 

328 method. 

329 

330 .. warning:: 

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

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

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

334 trigger an exception if given alongside them. 

335 

336 :returns: 

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

338 returns ``None``. 

339 

340 :raises BadHostKeyException: 

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

342 :raises AuthenticationException: 

343 if authentication failed. 

344 :raises UnableToAuthenticate: 

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

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

347 :raises socket.error: 

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

349 host-unreachable) occurred while connecting. 

350 :raises NoValidConnectionsError: 

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

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

353 errors. 

354 :raises SSHException: 

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

356 session. 

357 

358 .. versionchanged:: 1.15 

359 Added the ``banner_timeout``, ``gss_auth``, ``gss_kex``, 

360 ``gss_deleg_creds`` and ``gss_host`` arguments. 

361 .. versionchanged:: 2.3 

362 Added the ``gss_trust_dns`` argument. 

363 .. versionchanged:: 2.4 

364 Added the ``passphrase`` argument. 

365 .. versionchanged:: 2.6 

366 Added the ``disabled_algorithms`` argument. 

367 .. versionchanged:: 2.12 

368 Added the ``transport_factory`` argument. 

369 .. versionchanged:: 3.2 

370 Added the ``auth_strategy`` argument. 

371 """ 

372 if not sock: 

373 errors = {} 

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

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

376 for af, addr in to_try: 

377 try: 

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

379 if timeout is not None: 

380 try: 

381 sock.settimeout(timeout) 

382 except: 

383 pass 

384 sock.connect(addr) 

385 # Break out of the loop on success 

386 break 

387 except socket.error as e: 

388 # As mentioned in socket docs it is better 

389 # to close sockets explicitly 

390 if sock: 

391 sock.close() 

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

393 # (such as a resolution error) 

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

395 raise 

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

397 # iteration is complete. Retain info about which attempt 

398 # this was. 

399 errors[addr] = e 

400 

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

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

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

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

405 # (socket.error) 

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

407 raise NoValidConnectionsError(errors) 

408 

409 if transport_factory is None: 

410 transport_factory = Transport 

411 t = self._transport = transport_factory( 

412 sock, 

413 gss_kex=gss_kex, 

414 gss_deleg_creds=gss_deleg_creds, 

415 disabled_algorithms=disabled_algorithms, 

416 ) 

417 t.use_compression(compress=compress) 

418 t.set_gss_host( 

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

420 # Therefore use hostname as fallback. 

421 gss_host=gss_host or hostname, 

422 trust_dns=gss_trust_dns, 

423 gssapi_requested=gss_auth or gss_kex, 

424 ) 

425 if self._log_channel is not None: 

426 t.set_log_channel(self._log_channel) 

427 if banner_timeout is not None: 

428 t.banner_timeout = banner_timeout 

429 if auth_timeout is not None: 

430 t.auth_timeout = auth_timeout 

431 if channel_timeout is not None: 

432 t.channel_timeout = channel_timeout 

433 

434 if port == SSH_PORT: 

435 server_hostkey_name = hostname 

436 else: 

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

438 our_server_keys = None 

439 

440 our_server_keys = self._system_host_keys.get(server_hostkey_name) 

441 if our_server_keys is None: 

442 our_server_keys = self._host_keys.get(server_hostkey_name) 

443 if our_server_keys is not None: 

444 keytype = our_server_keys.keys()[0] 

445 sec_opts = t.get_security_options() 

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

447 sec_opts.key_types = [keytype] + other_types 

448 

449 t.start_client(timeout=timeout) 

450 

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

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

453 # well as our client. 

454 if not self._transport.gss_kex_used: 

455 server_key = t.get_remote_server_key() 

456 if our_server_keys is None: 

457 # will raise exception if the key is rejected 

458 self._policy.missing_host_key( 

459 self, server_hostkey_name, server_key 

460 ) 

461 else: 

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

463 if our_key != server_key: 

464 if our_key is None: 

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

466 raise BadHostKeyException(hostname, server_key, our_key) 

467 

468 if username is None: 

469 username = getpass.getuser() 

470 

471 # New auth flow! 

472 if auth_strategy is not None: 

473 return auth_strategy.authenticate(transport=t) 

474 

475 # Old auth flow! 

476 if key_filename is None: 

477 key_filenames = [] 

478 elif isinstance(key_filename, str): 

479 key_filenames = [key_filename] 

480 else: 

481 key_filenames = key_filename 

482 

483 self._auth( 

484 username, 

485 password, 

486 pkey, 

487 key_filenames, 

488 allow_agent, 

489 look_for_keys, 

490 gss_auth, 

491 gss_kex, 

492 gss_deleg_creds, 

493 t.gss_host, 

494 passphrase, 

495 ) 

496 

497 def close(self): 

498 """ 

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

500 

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

502 

503 .. warning:: 

504 Paramiko registers garbage collection hooks that will try to 

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

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

507 lead to end-of-process hangs! 

508 """ 

509 if self._transport is None: 

510 return 

511 self._transport.close() 

512 self._transport = None 

513 

514 if self._agent is not None: 

515 self._agent.close() 

516 self._agent = None 

517 

518 def exec_command( 

519 self, 

520 command, 

521 bufsize=-1, 

522 timeout=None, 

523 get_pty=False, 

524 environment=None, 

525 ): 

526 """ 

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

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

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

530 stdin, stdout, and stderr. 

531 

532 :param str command: the command to execute 

533 :param int bufsize: 

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

535 Python 

536 :param int timeout: 

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

538 :param bool get_pty: 

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

540 See `.Channel.get_pty` 

541 :param dict environment: 

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

543 default environment that the remote command executes within. 

544 

545 .. warning:: 

546 Servers may silently reject some environment variables; see the 

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

548 

549 :return: 

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

551 3-tuple 

552 

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

554 

555 .. versionchanged:: 1.10 

556 Added the ``get_pty`` kwarg. 

557 """ 

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

559 if get_pty: 

560 chan.get_pty() 

561 chan.settimeout(timeout) 

562 if environment: 

563 chan.update_environment(environment) 

564 chan.exec_command(command) 

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

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

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

568 return stdin, stdout, stderr 

569 

570 def invoke_shell( 

571 self, 

572 term="vt100", 

573 width=80, 

574 height=24, 

575 width_pixels=0, 

576 height_pixels=0, 

577 environment=None, 

578 ): 

579 """ 

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

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

582 terminal type and size. 

583 

584 :param str term: 

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

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

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

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

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

590 :param dict environment: the command's environment 

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

592 

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

594 """ 

595 chan = self._transport.open_session() 

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

597 chan.invoke_shell() 

598 return chan 

599 

600 def open_sftp(self): 

601 """ 

602 Open an SFTP session on the SSH server. 

603 

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

605 """ 

606 return self._transport.open_sftp_client() 

607 

608 def get_transport(self): 

609 """ 

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

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

612 kinds of channels. 

613 

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

615 """ 

616 return self._transport 

617 

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

619 """ 

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

621 

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

623 loaded. 

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

625 matching public cert will be loaded if it exists. 

626 """ 

627 cert_suffix = "-cert.pub" 

628 # Assume privkey, not cert, by default 

629 if filename.endswith(cert_suffix): 

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

631 cert_path = filename 

632 else: 

633 key_path = filename 

634 cert_path = filename + cert_suffix 

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

636 key = klass.from_private_key_file(key_path, password) 

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

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

639 # likely testing/filtering for (bah.) 

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

641 hexlify(key.get_fingerprint()), key_path 

642 ) 

643 self._log(DEBUG, msg) 

644 # Attempt to load cert if it exists. 

645 if os.path.isfile(cert_path): 

646 key.load_certificate(cert_path) 

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

648 return key 

649 

650 def _auth( 

651 self, 

652 username, 

653 password, 

654 pkey, 

655 key_filenames, 

656 allow_agent, 

657 look_for_keys, 

658 gss_auth, 

659 gss_kex, 

660 gss_deleg_creds, 

661 gss_host, 

662 passphrase, 

663 ): 

664 """ 

665 Try, in order: 

666 

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

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

669 - Any id_* key discoverable in ~/.ssh/ (if allowed). 

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

671 

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

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

674 required].) 

675 """ 

676 saved_exception = None 

677 two_factor = False 

678 allowed_types = set() 

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

680 if passphrase is None and password is not None: 

681 passphrase = password 

682 

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

684 # authentication with gssapi-keyex. 

685 if gss_kex and self._transport.gss_kex_used: 

686 try: 

687 self._transport.auth_gssapi_keyex(username) 

688 return 

689 except Exception as e: 

690 saved_exception = e 

691 

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

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

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

695 # why should we do that again? 

696 if gss_auth: 

697 try: 

698 return self._transport.auth_gssapi_with_mic( 

699 username, gss_host, gss_deleg_creds 

700 ) 

701 except Exception as e: 

702 saved_exception = e 

703 

704 if pkey is not None: 

705 try: 

706 self._log( 

707 DEBUG, 

708 "Trying SSH key {}".format( 

709 hexlify(pkey.get_fingerprint()) 

710 ), 

711 ) 

712 allowed_types = set( 

713 self._transport.auth_publickey(username, pkey) 

714 ) 

715 two_factor = allowed_types & two_factor_types 

716 if not two_factor: 

717 return 

718 except SSHException as e: 

719 saved_exception = e 

720 

721 if not two_factor: 

722 for key_filename in key_filenames: 

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

724 # killing SSHClient entirely 

725 for pkey_class in (RSAKey, ECDSAKey, Ed25519Key): 

726 try: 

727 key = self._key_from_filepath( 

728 key_filename, pkey_class, passphrase 

729 ) 

730 allowed_types = set( 

731 self._transport.auth_publickey(username, key) 

732 ) 

733 two_factor = allowed_types & two_factor_types 

734 if not two_factor: 

735 return 

736 break 

737 except SSHException as e: 

738 saved_exception = e 

739 

740 if not two_factor and allow_agent: 

741 if self._agent is None: 

742 self._agent = Agent() 

743 

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

745 try: 

746 id_ = hexlify(key.get_fingerprint()) 

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

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

749 # will return an allowed 2fac auth method 

750 allowed_types = set( 

751 self._transport.auth_publickey(username, key) 

752 ) 

753 two_factor = allowed_types & two_factor_types 

754 if not two_factor: 

755 return 

756 break 

757 except SSHException as e: 

758 saved_exception = e 

759 

760 if not two_factor: 

761 keyfiles = [] 

762 

763 for keytype, name in [ 

764 (RSAKey, "rsa"), 

765 (ECDSAKey, "ecdsa"), 

766 (Ed25519Key, "ed25519"), 

767 ]: 

768 # ~/ssh/ is for windows 

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

770 full_path = os.path.expanduser( 

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

772 ) 

773 if os.path.isfile(full_path): 

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

775 keyfiles.append((keytype, full_path)) 

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

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

778 

779 if not look_for_keys: 

780 keyfiles = [] 

781 

782 for pkey_class, filename in keyfiles: 

783 try: 

784 key = self._key_from_filepath( 

785 filename, pkey_class, passphrase 

786 ) 

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

788 # in ['password'] 

789 allowed_types = set( 

790 self._transport.auth_publickey(username, key) 

791 ) 

792 two_factor = allowed_types & two_factor_types 

793 if not two_factor: 

794 return 

795 break 

796 except (SSHException, IOError) as e: 

797 saved_exception = e 

798 

799 if password is not None: 

800 try: 

801 self._transport.auth_password(username, password) 

802 return 

803 except SSHException as e: 

804 saved_exception = e 

805 elif two_factor: 

806 try: 

807 self._transport.auth_interactive_dumb(username) 

808 return 

809 except SSHException as e: 

810 saved_exception = e 

811 

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

813 if saved_exception is not None: 

814 raise saved_exception 

815 raise SSHException("No authentication methods available") 

816 

817 def _log(self, level, msg): 

818 self._transport._log(level, msg) 

819 

820 

821class MissingHostKeyPolicy: 

822 """ 

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

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

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

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

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

828 

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

830 """ 

831 

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

833 """ 

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

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

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

837 be passed to the calling application). 

838 """ 

839 pass 

840 

841 

842class AutoAddPolicy(MissingHostKeyPolicy): 

843 """ 

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

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

846 """ 

847 

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

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

850 if client._host_keys_filename is not None: 

851 client.save_host_keys(client._host_keys_filename) 

852 client._log( 

853 DEBUG, 

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

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

856 ), 

857 ) 

858 

859 

860class RejectPolicy(MissingHostKeyPolicy): 

861 """ 

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

863 used by `.SSHClient`. 

864 """ 

865 

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

867 client._log( 

868 DEBUG, 

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

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

871 ), 

872 ) 

873 raise SSHException( 

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

875 ) 

876 

877 

878class WarningPolicy(MissingHostKeyPolicy): 

879 """ 

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

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

882 """ 

883 

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

885 warnings.warn( 

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

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

888 ) 

889 )