Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
client.py894 linesDownload Raw Back to paramiko
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 the6# terms of the GNU Lesser General Public License as published by the Free7# 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 ANY11# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR12# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more13# details.14#15# You should have received a copy of the GNU Lesser General Public License16# 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 policies21"""22 23from binascii import hexlify24import getpass25import inspect26import os27import socket28import warnings29from errno import ECONNREFUSED, EHOSTUNREACH30 31from paramiko.agent import Agent32from paramiko.common import DEBUG33from paramiko.config import SSH_PORT34from paramiko.dsskey import DSSKey35from paramiko.ecdsakey import ECDSAKey36from paramiko.ed25519key import Ed25519Key37from paramiko.hostkeys import HostKeys38from paramiko.rsakey import RSAKey39from paramiko.ssh_exception import (40    SSHException,41    BadHostKeyException,42    NoValidConnectionsError,43)44from paramiko.transport import Transport45from paramiko.util import ClosingContextManager46 47 48class SSHClient(ClosingContextManager):49    """50    A high-level representation of a session with an SSH server.  This class51    wraps `.Transport`, `.Channel`, and `.SFTPClient` to take care of most52    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 key60    checking.  The default mechanism is to try to use local key files or an61    SSH agent (if one is running).62 63    Instances of this class may be used as context managers.64 65    .. versionadded:: 1.666    """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 = None75        self._log_channel = None76        self._policy = RejectPolicy()77        self._transport = None78        self._agent = None79 80    def load_system_host_keys(self, filename=None):81        """82        Load host keys from a system (read-only) file.  Host keys read with83        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 keys86        will be merged with the existing set (new replacing old if there are87        conflicts).88 89        If ``filename`` is left as ``None``, an attempt will be made to read90        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 is92        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 read98        """99        if filename is None:100            # try the user's .ssh key file, and mask exceptions101            filename = os.path.expanduser("~/.ssh/known_hosts")102            try:103                self._system_host_keys.load(filename)104            except IOError:105                pass106            return107        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 this112        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 and115        saves them, when connecting to a previously-unknown server.116 117        This method can be called multiple times.  Each new set of host keys118        will be merged with the existing set (new replacing old if there are119        conflicts).  When automatically saving, the last hostname is used.120 121        :param str filename: the filename to read122 123        :raises: ``IOError`` -- if the filename could not be read124        """125        self._host_keys_filename = filename126        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 with131        `load_host_keys` (plus any added directly) will be saved -- not any132        host keys loaded with `load_system_host_keys`.133 134        :param str filename: the filename to save to135 136        :raises: ``IOError`` -- if the file could not be written137        """138 139        # update local host keys from file (in case other SSH clients140        # 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 the156        local host keys or change them.157 158        :return: the local host keys as a `.HostKeys` object.159        """160        return self._host_keys161 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 logging168        """169        self._log_channel = name170 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 some178          subclass of `.MissingHostKeyPolicy` such as `.RejectPolicy` (the179          default), `.AutoAddPolicy`, `.WarningPolicy`, or a user-created180          subclass.181        * A host key is **known** when it appears in the client object's cached182          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 a187            previously-unknown server188        """189        if inspect.isclass(policy):190            policy = policy()191        self._policy = policy192 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 to198        :param int port: the server port to connect to199        :returns: Yields an iterable of ``(family, address)`` tuples200        """201        guess = True202        addrinfos = socket.getaddrinfo(203            hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM204        )205        for (family, socktype, proto, canonname, sockaddr) in addrinfos:206            if socktype == socket.SOCK_STREAM:207                yield family, sockaddr208                guess = False209 210        # some OS like AIX don't indicate SOCK_STREAM support, so just211        # guess. :(  We only do this if we did not get a single result marked212        # as socktype == SOCK_STREAM.213        if guess:214            for family, _, _, _, sockaddr in addrinfos:215                yield family, sockaddr216 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 key245        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 hostname247        is not found in either set of host keys, the missing host key policy248        is used (see `set_missing_host_key_policy`).  The default policy is249        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 paths256                as well as regular private-key paths; when files ending in257                ``-cert.pub`` are found, they are assumed to match a private258                key, and both components will be loaded. (The private key259                itself does *not* need to be listed in ``key_filename`` for260                this to occur - *just* the certificate.)261 262            - Any key we can find through an SSH agent263            - Any "id_rsa", "id_dsa" or "id_ecdsa" key discoverable in264              ``~/.ssh/``265 266              - When OpenSSH-style public certificates exist that match an267                existing such private key (so e.g. one has ``id_rsa`` and268                ``id_rsa-cert.pub``) the certificate will be loaded alongside269                the private key and used for authentication.270 271            - Plain username/password auth, if a password was given272 273        If a private key requires a password to unlock it, and a password is274        passed in, that password will be used to attempt to unlock the key.275 276        :param str hostname: the server to connect to277        :param int port: the server port to connect to278        :param str username:279            the username to authenticate as (defaults to the current local280            username)281        :param str password:282            Used for password authentication; is also used for private key283            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 authentication287        :param str key_filename:288            the filename, or list of filenames, of optional private key(s)289            and/or certs to try for authentication290        :param float timeout:291            an optional timeout (in seconds) for the TCP connect292        :param bool allow_agent:293            set to False to disable connecting to the SSH agent294        :param bool look_for_keys:295            set to False to disable searching for discoverable private key296            files in ``~/.ssh/``297        :param bool compress: set to True to turn on compression298        :param socket sock:299            an open socket or socket-like object (such as a `.Channel`) to use300            for communication to the target host301        :param bool gss_auth:302            ``True`` if you want to use GSS-API authentication303        :param bool gss_kex:304            Perform GSS-API Key Exchange and user authentication305        :param bool gss_deleg_creds: Delegate GSS-API client credentials or not306        :param str gss_host:307            The targets name in the kerberos database. default: hostname308        :param bool gss_trust_dns:309            Indicates whether or not the DNS is trusted to securely310            canonicalize the name of the host being connected to (default311            ``True``).312        :param float banner_timeout: an optional timeout (in seconds) to wait313            for the SSH banner to be presented.314        :param float auth_timeout: an optional timeout (in seconds) to wait for315            an authentication response.316        :param float channel_timeout: an optional timeout (in seconds) to wait317             for a channel open response.318        :param dict disabled_algorithms:319            an optional dict passed directly to `.Transport` and its keyword320            argument of the same name.321        :param transport_factory:322            an optional callable which is handed a subset of the constructor323            arguments (primarily those related to the socket, GSS324            functionality, and algorithm selection) and generates a325            `.Transport` instance to be used by this client. Defaults to326            `.Transport.__init__`.327        :param auth_strategy:328            an optional instance of `.AuthStrategy`, triggering use of this329            newer authentication mechanism instead of SSHClient's legacy auth330            method.331 332            .. warning::333                This parameter is **incompatible** with all other334                authentication-related parameters (such as, but not limited to,335                ``password``, ``key_filename`` and ``allow_agent``) and will336                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 or351            host-unreachable) occurred while connecting.352        :raises NoValidConnectionsError:353            if all valid connection targets for the requested hostname (eg IPv4354            and IPv6) yielded connection-refused or host-unreachable socket355            errors.356        :raises SSHException:357            if there was any other error connecting or establishing an SSH358            session.359 360        .. versionchanged:: 1.15361            Added the ``banner_timeout``, ``gss_auth``, ``gss_kex``,362            ``gss_deleg_creds`` and ``gss_host`` arguments.363        .. versionchanged:: 2.3364            Added the ``gss_trust_dns`` argument.365        .. versionchanged:: 2.4366            Added the ``passphrase`` argument.367        .. versionchanged:: 2.6368            Added the ``disabled_algorithms`` argument.369        .. versionchanged:: 2.12370            Added the ``transport_factory`` argument.371        .. versionchanged:: 3.2372            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                            pass386                    sock.connect(addr)387                    # Break out of the loop on success388                    break389                except socket.error as e:390                    # As mentioned in socket docs it is better391                    # to close sockets explicitly392                    if sock:393                        sock.close()394                    # Raise anything that isn't a straight up connection error395                    # (such as a resolution error)396                    if e.errno not in (ECONNREFUSED, EHOSTUNREACH):397                        raise398                    # Capture anything else so we know how the run looks once399                    # iteration is complete. Retain info about which attempt400                    # this was.401                    errors[addr] = e402 403            # Make sure we explode usefully if no address family attempts404            # succeeded. We've no way of knowing which error is the "right"405            # one, so we construct a hybrid exception containing all the real406            # ones, of a subclass that client code should still be watching for407            # (socket.error)408            if len(errors) == len(to_try):409                raise NoValidConnectionsError(errors)410 411        if transport_factory is None:412            transport_factory = Transport413        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_timeout431        if auth_timeout is not None:432            t.auth_timeout = auth_timeout433        if channel_timeout is not None:434            t.channel_timeout = channel_timeout435 436        if port == SSH_PORT:437            server_hostkey_name = hostname438        else:439            server_hostkey_name = "[{}]:{}".format(hostname, port)440        our_server_keys = None441 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_types450 451        t.start_client(timeout=timeout)452 453        # If GSS-API Key Exchange is performed we are not required to check the454        # host key, because the host is authenticated via GSS-API / SSPI as455        # 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 rejected460                self._policy.missing_host_key(461                    self, server_hostkey_name, server_key462                )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_filename484 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 to507            automatically close connections for you, but this is not presently508            reliable. Failure to explicitly close your client after use may509            lead to end-of-process hangs!510        """511        if self._transport is None:512            return513        self._transport.close()514        self._transport = None515 516        if self._agent is not None:517            self._agent.close()518            self._agent = None519 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 and530        the requested command is executed.  The command's input and output531        streams are returned as Python ``file``-like objects representing532        stdin, stdout, and stderr.533 534        :param str command: the command to execute535        :param int bufsize:536            interpreted the same way as by the built-in ``file()`` function in537            Python538        :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 the545            default environment that the remote command executes within.546 547            .. warning::548                Servers may silently reject some environment variables; see the549                warning in `.Channel.set_environment_variable` for details.550 551        :return:552            the stdin, stdout, and stderr of the executing command, as a553            3-tuple554 555        :raises: `.SSHException` -- if the server fails to execute the command556 557        .. versionchanged:: 1.10558            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, stderr571 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 requested584        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 window589        :param int height: the height (in characters) of the terminal window590        :param int width_pixels: the width (in pixels) of the terminal window591        :param int height_pixels: the height (in pixels) of the terminal window592        :param dict environment: the command's environment593        :return: a new `.Channel` connected to the remote shell594 595        :raises: `.SSHException` -- if the server fails to invoke a shell596        """597        chan = self._transport.open_session()598        chan.get_pty(term, width, height, width_pixels, height_pixels)599        chan.invoke_shell()600        return chan601 602    def open_sftp(self):603        """604        Open an SFTP session on the SSH server.605 606        :return: a new `.SFTPClient` session object607        """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 specific614        kinds of channels.615 616        :return: the `.Transport` for this connection617        """618        return self._transport619 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 is625          loaded.626        - Otherwise, the filename is assumed to be a private key, and the627          matching public cert will be loaded if it exists.628        """629        cert_suffix = "-cert.pub"630        # Assume privkey, not cert, by default631        if filename.endswith(cert_suffix):632            key_path = filename[: -len(cert_suffix)]633            cert_path = filename634        else:635            key_path = filename636            cert_path = filename + cert_suffix637        # 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; probably640        # when #387 is released, since this is a critical log message users are641        # likely testing/filtering for (bah.)642        msg = "Trying discovered key {} in {}".format(643            hexlify(key.get_fingerprint()), key_path644        )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 key651 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 is677        required].)678        """679        saved_exception = None680        two_factor = False681        allowed_types = set()682        two_factor_types = {"keyboard-interactive", "password"}683        if passphrase is None and password is not None:684            passphrase = password685 686        # If GSS-API support and GSS-PI Key Exchange was performed, we attempt687        # authentication with gssapi-keyex.688        if gss_kex and self._transport.gss_kex_used:689            try:690                self._transport.auth_gssapi_keyex(username)691                return692            except Exception as e:693                saved_exception = e694 695        # Try GSS-API authentication (gssapi-with-mic) only if GSS-API Key696        # Exchange is not performed, because if we use GSS-API for the key697        # exchange, there is already a fully established GSS-API context, so698        # 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_creds703                )704            except Exception as e:705                saved_exception = e706 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_types719                if not two_factor:720                    return721            except SSHException as e:722                saved_exception = e723 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 just727                # killing SSHClient entirely728                for pkey_class in (RSAKey, DSSKey, ECDSAKey, Ed25519Key):729                    try:730                        key = self._key_from_filepath(731                            key_filename, pkey_class, passphrase732                        )733                        allowed_types = set(734                            self._transport.auth_publickey(username, key)735                        )736                        two_factor = allowed_types & two_factor_types737                        if not two_factor:738                            return739                        break740                    except SSHException as e:741                        saved_exception = e742 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 password752                    # will return an allowed 2fac auth method753                    allowed_types = set(754                        self._transport.auth_publickey(username, key)755                    )756                    two_factor = allowed_types & two_factor_types757                    if not two_factor:758                        return759                    break760                except SSHException as e:761                    saved_exception = e762 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 windows773                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 run779                        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, passphrase790                    )791                    # for 2-factor auth a successfully auth'd key will result792                    # in ['password']793                    allowed_types = set(794                        self._transport.auth_publickey(username, key)795                    )796                    two_factor = allowed_types & two_factor_types797                    if not two_factor:798                        return799                    break800                except (SSHException, IOError) as e:801                    saved_exception = e802 803        if password is not None:804            try:805                self._transport.auth_password(username, password)806                return807            except SSHException as e:808                saved_exception = e809        elif two_factor:810            try:811                self._transport.auth_interactive_dumb(username)812                return813            except SSHException as e:814                saved_exception = e815 816        # if we got an auth-failed exception earlier, re-raise it817        if saved_exception is not None:818            raise saved_exception819        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 the828    SSH server's hostname is not in either the system host keys or the829    application's keys.  Pre-made classes implement policies for automatically830    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 that839        isn't in either the system or local `.HostKeys` object.  To accept840        the key, simply return.  To reject, raised an exception (which will841        be passed to the calling application).842        """843        pass844 845 846class AutoAddPolicy(MissingHostKeyPolicy):847    """848    Policy for automatically adding the hostname and new host key to the849    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 is867    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, but885    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        )894 
codekingpro/portable-devtools · Team Ai