codekingpro/portable-devtools
114k
1# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>2# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>3#4# This file is part of paramiko.5#6# Paramiko is free software; you can redistribute it and/or modify it under the7# terms of the GNU Lesser General Public License as published by the Free8# Software Foundation; either version 2.1 of the License, or (at your option)9# any later version.10#11# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY12# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR13# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more14# details.15#16# You should have received a copy of the GNU Lesser General Public License17# along with Paramiko; if not, write to the Free Software Foundation, Inc.,18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.19 20"""21Core protocol implementation22"""23 24import os25import socket26import sys27import threading28import time29import weakref30from hashlib import md5, sha1, sha256, sha51231 32from cryptography.hazmat.backends import default_backend33from cryptography.hazmat.primitives.ciphers import algorithms, Cipher, modes34 35import paramiko36from paramiko import util37from paramiko.auth_handler import AuthHandler, AuthOnlyHandler38from paramiko.ssh_gss import GSSAuth39from paramiko.channel import Channel40from paramiko.common import (41 xffffffff,42 cMSG_CHANNEL_OPEN,43 cMSG_IGNORE,44 cMSG_GLOBAL_REQUEST,45 DEBUG,46 MSG_KEXINIT,47 MSG_IGNORE,48 MSG_DISCONNECT,49 MSG_DEBUG,50 ERROR,51 WARNING,52 cMSG_UNIMPLEMENTED,53 INFO,54 cMSG_KEXINIT,55 cMSG_NEWKEYS,56 MSG_NEWKEYS,57 cMSG_REQUEST_SUCCESS,58 cMSG_REQUEST_FAILURE,59 CONNECTION_FAILED_CODE,60 OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,61 OPEN_SUCCEEDED,62 cMSG_CHANNEL_OPEN_FAILURE,63 cMSG_CHANNEL_OPEN_SUCCESS,64 MSG_GLOBAL_REQUEST,65 MSG_REQUEST_SUCCESS,66 MSG_REQUEST_FAILURE,67 cMSG_SERVICE_REQUEST,68 MSG_SERVICE_ACCEPT,69 MSG_CHANNEL_OPEN_SUCCESS,70 MSG_CHANNEL_OPEN_FAILURE,71 MSG_CHANNEL_OPEN,72 MSG_CHANNEL_SUCCESS,73 MSG_CHANNEL_FAILURE,74 MSG_CHANNEL_DATA,75 MSG_CHANNEL_EXTENDED_DATA,76 MSG_CHANNEL_WINDOW_ADJUST,77 MSG_CHANNEL_REQUEST,78 MSG_CHANNEL_EOF,79 MSG_CHANNEL_CLOSE,80 MIN_WINDOW_SIZE,81 MIN_PACKET_SIZE,82 MAX_WINDOW_SIZE,83 DEFAULT_WINDOW_SIZE,84 DEFAULT_MAX_PACKET_SIZE,85 HIGHEST_USERAUTH_MESSAGE_ID,86 MSG_UNIMPLEMENTED,87 MSG_NAMES,88 MSG_EXT_INFO,89 cMSG_EXT_INFO,90 byte_ord,91)92from paramiko.compress import ZlibCompressor, ZlibDecompressor93from paramiko.dsskey import DSSKey94from paramiko.ed25519key import Ed25519Key95from paramiko.kex_curve25519 import KexCurve2551996from paramiko.kex_gex import KexGex, KexGexSHA25697from paramiko.kex_group1 import KexGroup198from paramiko.kex_group14 import KexGroup14, KexGroup14SHA25699from paramiko.kex_group16 import KexGroup16SHA512100from paramiko.kex_ecdh_nist import KexNistp256, KexNistp384, KexNistp521101from paramiko.kex_gss import KexGSSGex, KexGSSGroup1, KexGSSGroup14102from paramiko.message import Message103from paramiko.packet import Packetizer, NeedRekeyException104from paramiko.primes import ModulusPack105from paramiko.rsakey import RSAKey106from paramiko.ecdsakey import ECDSAKey107from paramiko.server import ServerInterface108from paramiko.sftp_client import SFTPClient109from paramiko.ssh_exception import (110 BadAuthenticationType,111 ChannelException,112 IncompatiblePeer,113 MessageOrderError,114 ProxyCommandFailure,115 SSHException,116)117from paramiko.util import (118 ClosingContextManager,119 clamp_value,120 b,121)122 123 124# for thread cleanup125_active_threads = []126 127 128def _join_lingering_threads():129 for thr in _active_threads:130 thr.stop_thread()131 132 133import atexit134 135atexit.register(_join_lingering_threads)136 137 138class Transport(threading.Thread, ClosingContextManager):139 """140 An SSH Transport attaches to a stream (usually a socket), negotiates an141 encrypted session, authenticates, and then creates stream tunnels, called142 `channels <.Channel>`, across the session. Multiple channels can be143 multiplexed across a single session (and often are, in the case of port144 forwardings).145 146 Instances of this class may be used as context managers.147 """148 149 _ENCRYPT = object()150 _DECRYPT = object()151 152 _PROTO_ID = "2.0"153 _CLIENT_ID = "paramiko_{}".format(paramiko.__version__)154 155 # These tuples of algorithm identifiers are in preference order; do not156 # reorder without reason!157 # NOTE: if you need to modify these, we suggest leveraging the158 # `disabled_algorithms` constructor argument (also available in SSHClient)159 # instead of monkeypatching or subclassing.160 _preferred_ciphers = (161 "aes128-ctr",162 "aes192-ctr",163 "aes256-ctr",164 "aes128-cbc",165 "aes192-cbc",166 "aes256-cbc",167 "3des-cbc",168 )169 _preferred_macs = (170 "hmac-sha2-256",171 "hmac-sha2-512",172 "hmac-sha2-256-etm@openssh.com",173 "hmac-sha2-512-etm@openssh.com",174 "hmac-sha1",175 "hmac-md5",176 "hmac-sha1-96",177 "hmac-md5-96",178 )179 # ~= HostKeyAlgorithms in OpenSSH land180 _preferred_keys = (181 "ssh-ed25519",182 "ecdsa-sha2-nistp256",183 "ecdsa-sha2-nistp384",184 "ecdsa-sha2-nistp521",185 "rsa-sha2-512",186 "rsa-sha2-256",187 "ssh-rsa",188 "ssh-dss",189 )190 # ~= PubKeyAcceptedAlgorithms191 _preferred_pubkeys = (192 "ssh-ed25519",193 "ecdsa-sha2-nistp256",194 "ecdsa-sha2-nistp384",195 "ecdsa-sha2-nistp521",196 "rsa-sha2-512",197 "rsa-sha2-256",198 "ssh-rsa",199 "ssh-dss",200 )201 _preferred_kex = (202 "ecdh-sha2-nistp256",203 "ecdh-sha2-nistp384",204 "ecdh-sha2-nistp521",205 "diffie-hellman-group16-sha512",206 "diffie-hellman-group-exchange-sha256",207 "diffie-hellman-group14-sha256",208 "diffie-hellman-group-exchange-sha1",209 "diffie-hellman-group14-sha1",210 "diffie-hellman-group1-sha1",211 )212 if KexCurve25519.is_available():213 _preferred_kex = ("curve25519-sha256@libssh.org",) + _preferred_kex214 _preferred_gsskex = (215 "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==",216 "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==",217 "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==",218 )219 _preferred_compression = ("none",)220 221 _cipher_info = {222 "aes128-ctr": {223 "class": algorithms.AES,224 "mode": modes.CTR,225 "block-size": 16,226 "key-size": 16,227 },228 "aes192-ctr": {229 "class": algorithms.AES,230 "mode": modes.CTR,231 "block-size": 16,232 "key-size": 24,233 },234 "aes256-ctr": {235 "class": algorithms.AES,236 "mode": modes.CTR,237 "block-size": 16,238 "key-size": 32,239 },240 "aes128-cbc": {241 "class": algorithms.AES,242 "mode": modes.CBC,243 "block-size": 16,244 "key-size": 16,245 },246 "aes192-cbc": {247 "class": algorithms.AES,248 "mode": modes.CBC,249 "block-size": 16,250 "key-size": 24,251 },252 "aes256-cbc": {253 "class": algorithms.AES,254 "mode": modes.CBC,255 "block-size": 16,256 "key-size": 32,257 },258 "3des-cbc": {259 "class": algorithms.TripleDES,260 "mode": modes.CBC,261 "block-size": 8,262 "key-size": 24,263 },264 }265 266 _mac_info = {267 "hmac-sha1": {"class": sha1, "size": 20},268 "hmac-sha1-96": {"class": sha1, "size": 12},269 "hmac-sha2-256": {"class": sha256, "size": 32},270 "hmac-sha2-256-etm@openssh.com": {"class": sha256, "size": 32},271 "hmac-sha2-512": {"class": sha512, "size": 64},272 "hmac-sha2-512-etm@openssh.com": {"class": sha512, "size": 64},273 "hmac-md5": {"class": md5, "size": 16},274 "hmac-md5-96": {"class": md5, "size": 12},275 }276 277 _key_info = {278 # TODO: at some point we will want to drop this as it's no longer279 # considered secure due to using SHA-1 for signatures. OpenSSH 8.8 no280 # longer supports it. Question becomes at what point do we want to281 # prevent users with older setups from using this?282 "ssh-rsa": RSAKey,283 "ssh-rsa-cert-v01@openssh.com": RSAKey,284 "rsa-sha2-256": RSAKey,285 "rsa-sha2-256-cert-v01@openssh.com": RSAKey,286 "rsa-sha2-512": RSAKey,287 "rsa-sha2-512-cert-v01@openssh.com": RSAKey,288 "ssh-dss": DSSKey,289 "ssh-dss-cert-v01@openssh.com": DSSKey,290 "ecdsa-sha2-nistp256": ECDSAKey,291 "ecdsa-sha2-nistp256-cert-v01@openssh.com": ECDSAKey,292 "ecdsa-sha2-nistp384": ECDSAKey,293 "ecdsa-sha2-nistp384-cert-v01@openssh.com": ECDSAKey,294 "ecdsa-sha2-nistp521": ECDSAKey,295 "ecdsa-sha2-nistp521-cert-v01@openssh.com": ECDSAKey,296 "ssh-ed25519": Ed25519Key,297 "ssh-ed25519-cert-v01@openssh.com": Ed25519Key,298 }299 300 _kex_info = {301 "diffie-hellman-group1-sha1": KexGroup1,302 "diffie-hellman-group14-sha1": KexGroup14,303 "diffie-hellman-group-exchange-sha1": KexGex,304 "diffie-hellman-group-exchange-sha256": KexGexSHA256,305 "diffie-hellman-group14-sha256": KexGroup14SHA256,306 "diffie-hellman-group16-sha512": KexGroup16SHA512,307 "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==": KexGSSGroup1,308 "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==": KexGSSGroup14,309 "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==": KexGSSGex,310 "ecdh-sha2-nistp256": KexNistp256,311 "ecdh-sha2-nistp384": KexNistp384,312 "ecdh-sha2-nistp521": KexNistp521,313 }314 if KexCurve25519.is_available():315 _kex_info["curve25519-sha256@libssh.org"] = KexCurve25519316 317 _compression_info = {318 # zlib@openssh.com is just zlib, but only turned on after a successful319 # authentication. openssh servers may only offer this type because320 # they've had troubles with security holes in zlib in the past.321 "zlib@openssh.com": (ZlibCompressor, ZlibDecompressor),322 "zlib": (ZlibCompressor, ZlibDecompressor),323 "none": (None, None),324 }325 326 _modulus_pack = None327 _active_check_timeout = 0.1328 329 def __init__(330 self,331 sock,332 default_window_size=DEFAULT_WINDOW_SIZE,333 default_max_packet_size=DEFAULT_MAX_PACKET_SIZE,334 gss_kex=False,335 gss_deleg_creds=True,336 disabled_algorithms=None,337 server_sig_algs=True,338 strict_kex=True,339 packetizer_class=None,340 ):341 """342 Create a new SSH session over an existing socket, or socket-like343 object. This only creates the `.Transport` object; it doesn't begin344 the SSH session yet. Use `connect` or `start_client` to begin a client345 session, or `start_server` to begin a server session.346 347 If the object is not actually a socket, it must have the following348 methods:349 350 - ``send(bytes)``: Writes from 1 to ``len(bytes)`` bytes, and returns351 an int representing the number of bytes written. Returns352 0 or raises ``EOFError`` if the stream has been closed.353 - ``recv(int)``: Reads from 1 to ``int`` bytes and returns them as a354 string. Returns 0 or raises ``EOFError`` if the stream has been355 closed.356 - ``close()``: Closes the socket.357 - ``settimeout(n)``: Sets a (float) timeout on I/O operations.358 359 For ease of use, you may also pass in an address (as a tuple) or a host360 string as the ``sock`` argument. (A host string is a hostname with an361 optional port (separated by ``":"``) which will be converted into a362 tuple of ``(hostname, port)``.) A socket will be connected to this363 address and used for communication. Exceptions from the ``socket``364 call may be thrown in this case.365 366 .. note::367 Modifying the the window and packet sizes might have adverse368 effects on your channels created from this transport. The default369 values are the same as in the OpenSSH code base and have been370 battle tested.371 372 :param socket sock:373 a socket or socket-like object to create the session over.374 :param int default_window_size:375 sets the default window size on the transport. (defaults to376 2097152)377 :param int default_max_packet_size:378 sets the default max packet size on the transport. (defaults to379 32768)380 :param bool gss_kex:381 Whether to enable GSSAPI key exchange when GSSAPI is in play.382 Default: ``False``.383 :param bool gss_deleg_creds:384 Whether to enable GSSAPI credential delegation when GSSAPI is in385 play. Default: ``True``.386 :param dict disabled_algorithms:387 If given, must be a dictionary mapping algorithm type to an388 iterable of algorithm identifiers, which will be disabled for the389 lifetime of the transport.390 391 Keys should match the last word in the class' builtin algorithm392 tuple attributes, such as ``"ciphers"`` to disable names within393 ``_preferred_ciphers``; or ``"kex"`` to disable something defined394 inside ``_preferred_kex``. Values should exactly match members of395 the matching attribute.396 397 For example, if you need to disable398 ``diffie-hellman-group16-sha512`` key exchange (perhaps because399 your code talks to a server which implements it differently from400 Paramiko), specify ``disabled_algorithms={"kex":401 ["diffie-hellman-group16-sha512"]}``.402 :param bool server_sig_algs:403 Whether to send an extra message to compatible clients, in server404 mode, with a list of supported pubkey algorithms. Default:405 ``True``.406 :param bool strict_kex:407 Whether to advertise (and implement, if client also advertises408 support for) a "strict kex" mode for safer handshaking. Default:409 ``True``.410 :param packetizer_class:411 Which class to use for instantiating the internal packet handler.412 Default: ``None`` (i.e.: use `Packetizer` as normal).413 414 .. versionchanged:: 1.15415 Added the ``default_window_size`` and ``default_max_packet_size``416 arguments.417 .. versionchanged:: 1.15418 Added the ``gss_kex`` and ``gss_deleg_creds`` kwargs.419 .. versionchanged:: 2.6420 Added the ``disabled_algorithms`` kwarg.421 .. versionchanged:: 2.9422 Added the ``server_sig_algs`` kwarg.423 .. versionchanged:: 3.4424 Added the ``strict_kex`` kwarg.425 .. versionchanged:: 3.4426 Added the ``packetizer_class`` kwarg.427 """428 self.active = False429 self.hostname = None430 self.server_extensions = {}431 self.advertise_strict_kex = strict_kex432 self.agreed_on_strict_kex = False433 434 # TODO: these two overrides on sock's type should go away sometime, too435 # many ways to do it!436 if isinstance(sock, str):437 # convert "host:port" into (host, port)438 hl = sock.split(":", 1)439 self.hostname = hl[0]440 if len(hl) == 1:441 sock = (hl[0], 22)442 else:443 sock = (hl[0], int(hl[1]))444 if type(sock) is tuple:445 # connect to the given (host, port)446 hostname, port = sock447 self.hostname = hostname448 reason = "No suitable address family"449 addrinfos = socket.getaddrinfo(450 hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM451 )452 for family, socktype, proto, canonname, sockaddr in addrinfos:453 if socktype == socket.SOCK_STREAM:454 af = family455 # addr = sockaddr456 sock = socket.socket(af, socket.SOCK_STREAM)457 try:458 sock.connect((hostname, port))459 except socket.error as e:460 reason = str(e)461 else:462 break463 else:464 raise SSHException(465 "Unable to connect to {}: {}".format(hostname, reason)466 )467 # okay, normal socket-ish flow here...468 threading.Thread.__init__(self)469 self.daemon = True470 self.sock = sock471 # we set the timeout so we can check self.active periodically to472 # see if we should bail. socket.timeout exception is never propagated.473 self.sock.settimeout(self._active_check_timeout)474 475 # negotiated crypto parameters476 self.packetizer = (packetizer_class or Packetizer)(sock)477 self.local_version = "SSH-" + self._PROTO_ID + "-" + self._CLIENT_ID478 self.remote_version = ""479 self.local_cipher = self.remote_cipher = ""480 self.local_kex_init = self.remote_kex_init = None481 self.local_mac = self.remote_mac = None482 self.local_compression = self.remote_compression = None483 self.session_id = None484 self.host_key_type = None485 self.host_key = None486 487 # GSS-API / SSPI Key Exchange488 self.use_gss_kex = gss_kex489 # This will be set to True if GSS-API Key Exchange was performed490 self.gss_kex_used = False491 self.kexgss_ctxt = None492 self.gss_host = None493 if self.use_gss_kex:494 self.kexgss_ctxt = GSSAuth("gssapi-keyex", gss_deleg_creds)495 self._preferred_kex = self._preferred_gsskex + self._preferred_kex496 497 # state used during negotiation498 self.kex_engine = None499 self.H = None500 self.K = None501 502 self.initial_kex_done = False503 self.in_kex = False504 self.authenticated = False505 self._expected_packet = tuple()506 # synchronization (always higher level than write_lock)507 self.lock = threading.Lock()508 509 # tracking open channels510 self._channels = ChannelMap()511 self.channel_events = {} # (id -> Event)512 self.channels_seen = {} # (id -> True)513 self._channel_counter = 0514 self.default_max_packet_size = default_max_packet_size515 self.default_window_size = default_window_size516 self._forward_agent_handler = None517 self._x11_handler = None518 self._tcp_handler = None519 520 self.saved_exception = None521 self.clear_to_send = threading.Event()522 self.clear_to_send_lock = threading.Lock()523 self.clear_to_send_timeout = 30.0524 self.log_name = "paramiko.transport"525 self.logger = util.get_logger(self.log_name)526 self.packetizer.set_log(self.logger)527 self.auth_handler = None528 # response Message from an arbitrary global request529 self.global_response = None530 # user-defined event callbacks531 self.completion_event = None532 # how long (seconds) to wait for the SSH banner533 self.banner_timeout = 15534 # how long (seconds) to wait for the handshake to finish after SSH535 # banner sent.536 self.handshake_timeout = 15537 # how long (seconds) to wait for the auth response.538 self.auth_timeout = 30539 # how long (seconds) to wait for opening a channel540 self.channel_timeout = 60 * 60541 self.disabled_algorithms = disabled_algorithms or {}542 self.server_sig_algs = server_sig_algs543 544 # server mode:545 self.server_mode = False546 self.server_object = None547 self.server_key_dict = {}548 self.server_accepts = []549 self.server_accept_cv = threading.Condition(self.lock)550 self.subsystem_table = {}551 552 # Handler table, now set at init time for easier per-instance553 # manipulation and subclass twiddling.554 self._handler_table = {555 MSG_EXT_INFO: self._parse_ext_info,556 MSG_NEWKEYS: self._parse_newkeys,557 MSG_GLOBAL_REQUEST: self._parse_global_request,558 MSG_REQUEST_SUCCESS: self._parse_request_success,559 MSG_REQUEST_FAILURE: self._parse_request_failure,560 MSG_CHANNEL_OPEN_SUCCESS: self._parse_channel_open_success,561 MSG_CHANNEL_OPEN_FAILURE: self._parse_channel_open_failure,562 MSG_CHANNEL_OPEN: self._parse_channel_open,563 MSG_KEXINIT: self._negotiate_keys,564 }565 566 def _filter_algorithm(self, type_):567 default = getattr(self, "_preferred_{}".format(type_))568 return tuple(569 x570 for x in default571 if x not in self.disabled_algorithms.get(type_, [])572 )573 574 @property575 def preferred_ciphers(self):576 return self._filter_algorithm("ciphers")577 578 @property579 def preferred_macs(self):580 return self._filter_algorithm("macs")581 582 @property583 def preferred_keys(self):584 # Interleave cert variants here; resistant to various background585 # overwriting of _preferred_keys, and necessary as hostkeys can't use586 # the logic pubkey auth does re: injecting/checking for certs at587 # runtime588 filtered = self._filter_algorithm("keys")589 return tuple(590 filtered591 + tuple("{}-cert-v01@openssh.com".format(x) for x in filtered)592 )593 594 @property595 def preferred_pubkeys(self):596 return self._filter_algorithm("pubkeys")597 598 @property599 def preferred_kex(self):600 return self._filter_algorithm("kex")601 602 @property603 def preferred_compression(self):604 return self._filter_algorithm("compression")605 606 def __repr__(self):607 """608 Returns a string representation of this object, for debugging.609 """610 id_ = hex(id(self) & xffffffff)611 out = "<paramiko.Transport at {}".format(id_)612 if not self.active:613 out += " (unconnected)"614 else:615 if self.local_cipher != "":616 out += " (cipher {}, {:d} bits)".format(617 self.local_cipher,618 self._cipher_info[self.local_cipher]["key-size"] * 8,619 )620 if self.is_authenticated():621 out += " (active; {} open channel(s))".format(622 len(self._channels)623 )624 elif self.initial_kex_done:625 out += " (connected; awaiting auth)"626 else:627 out += " (connecting)"628 out += ">"629 return out630 631 def atfork(self):632 """633 Terminate this Transport without closing the session. On posix634 systems, if a Transport is open during process forking, both parent635 and child will share the underlying socket, but only one process can636 use the connection (without corrupting the session). Use this method637 to clean up a Transport object without disrupting the other process.638 639 .. versionadded:: 1.5.3640 """641 self.sock.close()642 self.close()643 644 def get_security_options(self):645 """646 Return a `.SecurityOptions` object which can be used to tweak the647 encryption algorithms this transport will permit (for encryption,648 digest/hash operations, public keys, and key exchanges) and the order649 of preference for them.650 """651 return SecurityOptions(self)652 653 def set_gss_host(self, gss_host, trust_dns=True, gssapi_requested=True):654 """655 Normalize/canonicalize ``self.gss_host`` depending on various factors.656 657 :param str gss_host:658 The explicitly requested GSS-oriented hostname to connect to (i.e.659 what the host's name is in the Kerberos database.) Defaults to660 ``self.hostname`` (which will be the 'real' target hostname and/or661 host portion of given socket object.)662 :param bool trust_dns:663 Indicates whether or not DNS is trusted; if true, DNS will be used664 to canonicalize the GSS hostname (which again will either be665 ``gss_host`` or the transport's default hostname.)666 (Defaults to True due to backwards compatibility.)667 :param bool gssapi_requested:668 Whether GSSAPI key exchange or authentication was even requested.669 If not, this is a no-op and nothing happens670 (and ``self.gss_host`` is not set.)671 (Defaults to True due to backwards compatibility.)672 :returns: ``None``.673 """674 # No GSSAPI in play == nothing to do675 if not gssapi_requested:676 return677 # Obtain the correct host first - did user request a GSS-specific name678 # to use that is distinct from the actual SSH target hostname?679 if gss_host is None:680 gss_host = self.hostname681 # Finally, canonicalize via DNS if DNS is trusted.682 if trust_dns and gss_host is not None:683 gss_host = socket.getfqdn(gss_host)684 # And set attribute for reference later.685 self.gss_host = gss_host686 687 def start_client(self, event=None, timeout=None):688 """689 Negotiate a new SSH2 session as a client. This is the first step after690 creating a new `.Transport`. A separate thread is created for protocol691 negotiation.692 693 If an event is passed in, this method returns immediately. When694 negotiation is done (successful or not), the given ``Event`` will695 be triggered. On failure, `is_active` will return ``False``.696 697 (Since 1.4) If ``event`` is ``None``, this method will not return until698 negotiation is done. On success, the method returns normally.699 Otherwise an SSHException is raised.700 701 After a successful negotiation, you will usually want to authenticate,702 calling `auth_password <Transport.auth_password>` or703 `auth_publickey <Transport.auth_publickey>`.704 705 .. note:: `connect` is a simpler method for connecting as a client.706 707 .. note::708 After calling this method (or `start_server` or `connect`), you709 should no longer directly read from or write to the original socket710 object.711 712 :param .threading.Event event:713 an event to trigger when negotiation is complete (optional)714 715 :param float timeout:716 a timeout, in seconds, for SSH2 session negotiation (optional)717 718 :raises:719 `.SSHException` -- if negotiation fails (and no ``event`` was720 passed in)721 """722 self.active = True723 if event is not None:724 # async, return immediately and let the app poll for completion725 self.completion_event = event726 self.start()727 return728 729 # synchronous, wait for a result730 self.completion_event = event = threading.Event()731 self.start()732 max_time = time.time() + timeout if timeout is not None else None733 while True:734 event.wait(0.1)735 if not self.active:736 e = self.get_exception()737 if e is not None:738 raise e739 raise SSHException("Negotiation failed.")740 if event.is_set() or (741 timeout is not None and time.time() >= max_time742 ):743 break744 745 def start_server(self, event=None, server=None):746 """747 Negotiate a new SSH2 session as a server. This is the first step after748 creating a new `.Transport` and setting up your server host key(s). A749 separate thread is created for protocol negotiation.750 751 If an event is passed in, this method returns immediately. When752 negotiation is done (successful or not), the given ``Event`` will753 be triggered. On failure, `is_active` will return ``False``.754 755 (Since 1.4) If ``event`` is ``None``, this method will not return until756 negotiation is done. On success, the method returns normally.757 Otherwise an SSHException is raised.758 759 After a successful negotiation, the client will need to authenticate.760 Override the methods `get_allowed_auths761 <.ServerInterface.get_allowed_auths>`, `check_auth_none762 <.ServerInterface.check_auth_none>`, `check_auth_password763 <.ServerInterface.check_auth_password>`, and `check_auth_publickey764 <.ServerInterface.check_auth_publickey>` in the given ``server`` object765 to control the authentication process.766 767 After a successful authentication, the client should request to open a768 channel. Override `check_channel_request769 <.ServerInterface.check_channel_request>` in the given ``server``770 object to allow channels to be opened.771 772 .. note::773 After calling this method (or `start_client` or `connect`), you774 should no longer directly read from or write to the original socket775 object.776 777 :param .threading.Event event:778 an event to trigger when negotiation is complete.779 :param .ServerInterface server:780 an object used to perform authentication and create `channels781 <.Channel>`782 783 :raises:784 `.SSHException` -- if negotiation fails (and no ``event`` was785 passed in)786 """787 if server is None:788 server = ServerInterface()789 self.server_mode = True790 self.server_object = server791 self.active = True792 if event is not None:793 # async, return immediately and let the app poll for completion794 self.completion_event = event795 self.start()796 return797 798 # synchronous, wait for a result799 self.completion_event = event = threading.Event()800 self.start()801 while True:802 event.wait(0.1)803 if not self.active:804 e = self.get_exception()805 if e is not None:806 raise e807 raise SSHException("Negotiation failed.")808 if event.is_set():809 break810 811 def add_server_key(self, key):812 """813 Add a host key to the list of keys used for server mode. When behaving814 as a server, the host key is used to sign certain packets during the815 SSH2 negotiation, so that the client can trust that we are who we say816 we are. Because this is used for signing, the key must contain private817 key info, not just the public half. Only one key of each type (RSA or818 DSS) is kept.819 820 :param .PKey key:821 the host key to add, usually an `.RSAKey` or `.DSSKey`.822 """823 self.server_key_dict[key.get_name()] = key824 # Handle SHA-2 extensions for RSA by ensuring that lookups into825 # self.server_key_dict will yield this key for any of the algorithm826 # names.827 if isinstance(key, RSAKey):828 self.server_key_dict["rsa-sha2-256"] = key829 self.server_key_dict["rsa-sha2-512"] = key830 831 def get_server_key(self):832 """833 Return the active host key, in server mode. After negotiating with the834 client, this method will return the negotiated host key. If only one835 type of host key was set with `add_server_key`, that's the only key836 that will ever be returned. But in cases where you have set more than837 one type of host key (for example, an RSA key and a DSS key), the key838 type will be negotiated by the client, and this method will return the839 key of the type agreed on. If the host key has not been negotiated840 yet, ``None`` is returned. In client mode, the behavior is undefined.841 842 :return:843 host key (`.PKey`) of the type negotiated by the client, or844 ``None``.845 """846 try:847 return self.server_key_dict[self.host_key_type]848 except KeyError:849 pass850 return None851 852 @staticmethod853 def load_server_moduli(filename=None):854 """855 (optional)856 Load a file of prime moduli for use in doing group-exchange key857 negotiation in server mode. It's a rather obscure option and can be858 safely ignored.859 860 In server mode, the remote client may request "group-exchange" key861 negotiation, which asks the server to send a random prime number that862 fits certain criteria. These primes are pretty difficult to compute,863 so they can't be generated on demand. But many systems contain a file864 of suitable primes (usually named something like ``/etc/ssh/moduli``).865 If you call `load_server_moduli` and it returns ``True``, then this866 file of primes has been loaded and we will support "group-exchange" in867 server mode. Otherwise server mode will just claim that it doesn't868 support that method of key negotiation.869 870 :param str filename:871 optional path to the moduli file, if you happen to know that it's872 not in a standard location.873 :return:874 True if a moduli file was successfully loaded; False otherwise.875 876 .. note:: This has no effect when used in client mode.877 """878 Transport._modulus_pack = ModulusPack()879 # places to look for the openssh "moduli" file880 file_list = ["/etc/ssh/moduli", "/usr/local/etc/moduli"]881 if filename is not None:882 file_list.insert(0, filename)883 for fn in file_list:884 try:885 Transport._modulus_pack.read_file(fn)886 return True887 except IOError:888 pass889 # none succeeded890 Transport._modulus_pack = None891 return False892 893 def close(self):894 """895 Close this session, and any open channels that are tied to it.896 """897 if not self.active:898 return899 self.stop_thread()900 for chan in list(self._channels.values()):901 chan._unlink()902 self.sock.close()903 904 def get_remote_server_key(self):905 """906 Return the host key of the server (in client mode).907 908 .. note::909 Previously this call returned a tuple of ``(key type, key910 string)``. You can get the same effect by calling `.PKey.get_name`911 for the key type, and ``str(key)`` for the key string.912 913 :raises: `.SSHException` -- if no session is currently active.914 915 :return: public key (`.PKey`) of the remote server916 """917 if (not self.active) or (not self.initial_kex_done):918 raise SSHException("No existing session")919 return self.host_key920 921 def is_active(self):922 """923 Return true if this session is active (open).924 925 :return:926 True if the session is still active (open); False if the session is927 closed928 """929 return self.active930 931 def open_session(932 self, window_size=None, max_packet_size=None, timeout=None933 ):934 """935 Request a new channel to the server, of type ``"session"``. This is936 just an alias for calling `open_channel` with an argument of937 ``"session"``.938 939 .. note:: Modifying the the window and packet sizes might have adverse940 effects on the session created. The default values are the same941 as in the OpenSSH code base and have been battle tested.942 943 :param int window_size:944 optional window size for this session.945 :param int max_packet_size:946 optional max packet size for this session.947 948 :return: a new `.Channel`949 950 :raises:951 `.SSHException` -- if the request is rejected or the session ends952 prematurely953 954 .. versionchanged:: 1.13.4/1.14.3/1.15.3955 Added the ``timeout`` argument.956 .. versionchanged:: 1.15957 Added the ``window_size`` and ``max_packet_size`` arguments.958 """959 return self.open_channel(960 "session",961 window_size=window_size,962 max_packet_size=max_packet_size,963 timeout=timeout,964 )965 966 def open_x11_channel(self, src_addr=None):967 """968 Request a new channel to the client, of type ``"x11"``. This969 is just an alias for ``open_channel('x11', src_addr=src_addr)``.970 971 :param tuple src_addr:972 the source address (``(str, int)``) of the x11 server (port is the973 x11 port, ie. 6010)974 :return: a new `.Channel`975 976 :raises:977 `.SSHException` -- if the request is rejected or the session ends978 prematurely979 """980 return self.open_channel("x11", src_addr=src_addr)981 982 def open_forward_agent_channel(self):983 """984 Request a new channel to the client, of type985 ``"auth-agent@openssh.com"``.986 987 This is just an alias for ``open_channel('auth-agent@openssh.com')``.988 989 :return: a new `.Channel`990 991 :raises: `.SSHException` --992 if the request is rejected or the session ends prematurely993 """994 return self.open_channel("auth-agent@openssh.com")995 996 def open_forwarded_tcpip_channel(self, src_addr, dest_addr):997 """998 Request a new channel back to the client, of type ``forwarded-tcpip``.999 1000 This is used after a client has requested port forwarding, for sending1001 incoming connections back to the client.1002 1003 :param src_addr: originator's address1004 :param dest_addr: local (server) connected address1005 """1006 return self.open_channel("forwarded-tcpip", dest_addr, src_addr)1007 1008 def open_channel(1009 self,1010 kind,1011 dest_addr=None,1012 src_addr=None,1013 window_size=None,1014 max_packet_size=None,1015 timeout=None,1016 ):1017 """1018 Request a new channel to the server. `Channels <.Channel>` are1019 socket-like objects used for the actual transfer of data across the1020 session. You may only request a channel after negotiating encryption1021 (using `connect` or `start_client`) and authenticating.1022 1023 .. note:: Modifying the the window and packet sizes might have adverse1024 effects on the channel created. The default values are the same1025 as in the OpenSSH code base and have been battle tested.1026 1027 :param str kind:1028 the kind of channel requested (usually ``"session"``,1029 ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"``)1030 :param tuple dest_addr:1031 the destination address (address + port tuple) of this port1032 forwarding, if ``kind`` is ``"forwarded-tcpip"`` or1033 ``"direct-tcpip"`` (ignored for other channel types)1034 :param src_addr: the source address of this port forwarding, if1035 ``kind`` is ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"``1036 :param int window_size:1037 optional window size for this session.1038 :param int max_packet_size:1039 optional max packet size for this session.1040 :param float timeout:1041 optional timeout opening a channel, default 3600s (1h)1042 1043 :return: a new `.Channel` on success1044 1045 :raises:1046 `.SSHException` -- if the request is rejected, the session ends1047 prematurely or there is a timeout opening a channel1048 1049 .. versionchanged:: 1.151050 Added the ``window_size`` and ``max_packet_size`` arguments.1051 """1052 if not self.active:1053 raise SSHException("SSH session not active")1054 timeout = self.channel_timeout if timeout is None else timeout1055 self.lock.acquire()1056 try:1057 window_size = self._sanitize_window_size(window_size)1058 max_packet_size = self._sanitize_packet_size(max_packet_size)1059 chanid = self._next_channel()1060 m = Message()1061 m.add_byte(cMSG_CHANNEL_OPEN)1062 m.add_string(kind)1063 m.add_int(chanid)1064 m.add_int(window_size)1065 m.add_int(max_packet_size)1066 if (kind == "forwarded-tcpip") or (kind == "direct-tcpip"):1067 m.add_string(dest_addr[0])1068 m.add_int(dest_addr[1])1069 m.add_string(src_addr[0])1070 m.add_int(src_addr[1])1071 elif kind == "x11":1072 m.add_string(src_addr[0])1073 m.add_int(src_addr[1])1074 chan = Channel(chanid)1075 self._channels.put(chanid, chan)1076 self.channel_events[chanid] = event = threading.Event()1077 self.channels_seen[chanid] = True1078 chan._set_transport(self)1079 chan._set_window(window_size, max_packet_size)1080 finally:1081 self.lock.release()1082 self._send_user_message(m)1083 start_ts = time.time()1084 while True:1085 event.wait(0.1)1086 if not self.active:1087 e = self.get_exception()1088 if e is None:1089 e = SSHException("Unable to open channel.")1090 raise e1091 if event.is_set():1092 break1093 elif start_ts + timeout < time.time():1094 raise SSHException("Timeout opening channel.")1095 chan = self._channels.get(chanid)1096 if chan is not None:1097 return chan1098 e = self.get_exception()1099 if e is None:1100 e = SSHException("Unable to open channel.")1101 raise e1102 1103 def request_port_forward(self, address, port, handler=None):1104 """1105 Ask the server to forward TCP connections from a listening port on1106 the server, across this SSH session.1107 1108 If a handler is given, that handler is called from a different thread1109 whenever a forwarded connection arrives. The handler parameters are::1110 1111 handler(1112 channel,1113 (origin_addr, origin_port),1114 (server_addr, server_port),1115 )1116 1117 where ``server_addr`` and ``server_port`` are the address and port that1118 the server was listening on.1119 1120 If no handler is set, the default behavior is to send new incoming1121 forwarded connections into the accept queue, to be picked up via1122 `accept`.1123 1124 :param str address: the address to bind when forwarding1125 :param int port:1126 the port to forward, or 0 to ask the server to allocate any port1127 :param callable handler:1128 optional handler for incoming forwarded connections, of the form1129 ``func(Channel, (str, int), (str, int))``.1130 1131 :return: the port number (`int`) allocated by the server1132 1133 :raises:1134 `.SSHException` -- if the server refused the TCP forward request1135 """1136 if not self.active:1137 raise SSHException("SSH session not active")1138 port = int(port)1139 response = self.global_request(1140 "tcpip-forward", (address, port), wait=True1141 )1142 if response is None:1143 raise SSHException("TCP forwarding request denied")1144 if port == 0:1145 port = response.get_int()1146 if handler is None:1147 1148 def default_handler(channel, src_addr, dest_addr_port):1149 # src_addr, src_port = src_addr_port1150 # dest_addr, dest_port = dest_addr_port1151 self._queue_incoming_channel(channel)1152 1153 handler = default_handler1154 self._tcp_handler = handler1155 return port1156 1157 def cancel_port_forward(self, address, port):1158 """1159 Ask the server to cancel a previous port-forwarding request. No more1160 connections to the given address & port will be forwarded across this1161 ssh connection.1162 1163 :param str address: the address to stop forwarding1164 :param int port: the port to stop forwarding1165 """1166 if not self.active:1167 return1168 self._tcp_handler = None1169 self.global_request("cancel-tcpip-forward", (address, port), wait=True)1170 1171 def open_sftp_client(self):1172 """1173 Create an SFTP client channel from an open transport. On success, an1174 SFTP session will be opened with the remote host, and a new1175 `.SFTPClient` object will be returned.1176 1177 :return:1178 a new `.SFTPClient` referring to an sftp session (channel) across1179 this transport1180 """1181 return SFTPClient.from_transport(self)1182 1183 def send_ignore(self, byte_count=None):1184 """1185 Send a junk packet across the encrypted link. This is sometimes used1186 to add "noise" to a connection to confuse would-be attackers. It can1187 also be used as a keep-alive for long lived connections traversing1188 firewalls.1189 1190 :param int byte_count:1191 the number of random bytes to send in the payload of the ignored1192 packet -- defaults to a random number from 10 to 41.1193 """1194 m = Message()1195 m.add_byte(cMSG_IGNORE)1196 if byte_count is None:1197 byte_count = (byte_ord(os.urandom(1)) % 32) + 101198 m.add_bytes(os.urandom(byte_count))1199 self._send_user_message(m)1200 