Package paramiko :: Module transport
[frames] | no frames]

Source Code for Module paramiko.transport

   1  # Copyright (C) 2003-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 distrubuted 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  # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. 
  18   
  19  """ 
  20  L{Transport} handles the core SSH2 protocol. 
  21  """ 
  22   
  23  import os 
  24  import socket 
  25  import string 
  26  import struct 
  27  import sys 
  28  import threading 
  29  import time 
  30  import weakref 
  31   
  32  import paramiko 
  33  from paramiko import util 
  34  from paramiko.auth_handler import AuthHandler 
  35  from paramiko.channel import Channel 
  36  from paramiko.common import * 
  37  from paramiko.compress import ZlibCompressor, ZlibDecompressor 
  38  from paramiko.dsskey import DSSKey 
  39  from paramiko.kex_gex import KexGex 
  40  from paramiko.kex_group1 import KexGroup1 
  41  from paramiko.message import Message 
  42  from paramiko.packet import Packetizer, NeedRekeyException 
  43  from paramiko.primes import ModulusPack 
  44  from paramiko.rsakey import RSAKey 
  45  from paramiko.server import ServerInterface 
  46  from paramiko.sftp_client import SFTPClient 
  47  from paramiko.ssh_exception import (SSHException, BadAuthenticationType, 
  48      ChannelException, ProxyCommandFailure) 
  49  from paramiko.util import retry_on_signal 
  50   
  51  from Crypto import Random 
  52  from Crypto.Cipher import Blowfish, AES, DES3, ARC4 
  53  from Crypto.Hash import SHA, MD5 
  54  try: 
  55      from Crypto.Util import Counter 
  56  except ImportError: 
  57      from paramiko.util import Counter 
  58   
  59   
  60  # for thread cleanup 
  61  _active_threads = [] 
62 -def _join_lingering_threads():
63 for thr in _active_threads: 64 thr.stop_thread()
65 import atexit 66 atexit.register(_join_lingering_threads) 67 68
69 -class SecurityOptions (object):
70 """ 71 Simple object containing the security preferences of an ssh transport. 72 These are tuples of acceptable ciphers, digests, key types, and key 73 exchange algorithms, listed in order of preference. 74 75 Changing the contents and/or order of these fields affects the underlying 76 L{Transport} (but only if you change them before starting the session). 77 If you try to add an algorithm that paramiko doesn't recognize, 78 C{ValueError} will be raised. If you try to assign something besides a 79 tuple to one of the fields, C{TypeError} will be raised. 80 """ 81 __slots__ = [ 'ciphers', 'digests', 'key_types', 'kex', 'compression', '_transport' ] 82
83 - def __init__(self, transport):
84 self._transport = transport
85
86 - def __repr__(self):
87 """ 88 Returns a string representation of this object, for debugging. 89 90 @rtype: str 91 """ 92 return '<paramiko.SecurityOptions for %s>' % repr(self._transport)
93
94 - def _get_ciphers(self):
95 return self._transport._preferred_ciphers
96
97 - def _get_digests(self):
98 return self._transport._preferred_macs
99
100 - def _get_key_types(self):
101 return self._transport._preferred_keys
102
103 - def _get_kex(self):
104 return self._transport._preferred_kex
105
106 - def _get_compression(self):
107 return self._transport._preferred_compression
108
109 - def _set(self, name, orig, x):
110 if type(x) is list: 111 x = tuple(x) 112 if type(x) is not tuple: 113 raise TypeError('expected tuple or list') 114 possible = getattr(self._transport, orig).keys() 115 forbidden = filter(lambda n: n not in possible, x) 116 if len(forbidden) > 0: 117 raise ValueError('unknown cipher') 118 setattr(self._transport, name, x)
119
120 - def _set_ciphers(self, x):
121 self._set('_preferred_ciphers', '_cipher_info', x)
122
123 - def _set_digests(self, x):
124 self._set('_preferred_macs', '_mac_info', x)
125
126 - def _set_key_types(self, x):
127 self._set('_preferred_keys', '_key_info', x)
128
129 - def _set_kex(self, x):
130 self._set('_preferred_kex', '_kex_info', x)
131
132 - def _set_compression(self, x):
133 self._set('_preferred_compression', '_compression_info', x)
134 135 ciphers = property(_get_ciphers, _set_ciphers, None, 136 "Symmetric encryption ciphers") 137 digests = property(_get_digests, _set_digests, None, 138 "Digest (one-way hash) algorithms") 139 key_types = property(_get_key_types, _set_key_types, None, 140 "Public-key algorithms") 141 kex = property(_get_kex, _set_kex, None, "Key exchange algorithms") 142 compression = property(_get_compression, _set_compression, None, 143 "Compression algorithms")
144 145
146 -class ChannelMap (object):
147 - def __init__(self):
148 # (id -> Channel) 149 self._map = weakref.WeakValueDictionary() 150 self._lock = threading.Lock()
151
152 - def put(self, chanid, chan):
153 self._lock.acquire() 154 try: 155 self._map[chanid] = chan 156 finally: 157 self._lock.release()
158
159 - def get(self, chanid):
160 self._lock.acquire() 161 try: 162 return self._map.get(chanid, None) 163 finally: 164 self._lock.release()
165
166 - def delete(self, chanid):
167 self._lock.acquire() 168 try: 169 try: 170 del self._map[chanid] 171 except KeyError: 172 pass 173 finally: 174 self._lock.release()
175
176 - def values(self):
177 self._lock.acquire() 178 try: 179 return self._map.values() 180 finally: 181 self._lock.release()
182
183 - def __len__(self):
184 self._lock.acquire() 185 try: 186 return len(self._map) 187 finally: 188 self._lock.release()
189 190
191 -class Transport (threading.Thread):
192 """ 193 An SSH Transport attaches to a stream (usually a socket), negotiates an 194 encrypted session, authenticates, and then creates stream tunnels, called 195 L{Channel}s, across the session. Multiple channels can be multiplexed 196 across a single session (and often are, in the case of port forwardings). 197 """ 198 199 _PROTO_ID = '2.0' 200 _CLIENT_ID = 'paramiko_%s' % (paramiko.__version__) 201 202 _preferred_ciphers = ( 'aes128-ctr', 'aes256-ctr', 'aes128-cbc', 'blowfish-cbc', 'aes256-cbc', '3des-cbc', 203 'arcfour128', 'arcfour256' ) 204 _preferred_macs = ( 'hmac-sha1', 'hmac-md5', 'hmac-sha1-96', 'hmac-md5-96' ) 205 _preferred_keys = ( 'ssh-rsa', 'ssh-dss' ) 206 _preferred_kex = ( 'diffie-hellman-group1-sha1', 'diffie-hellman-group-exchange-sha1' ) 207 _preferred_compression = ( 'none', ) 208 209 _cipher_info = { 210 'aes128-ctr': { 'class': AES, 'mode': AES.MODE_CTR, 'block-size': 16, 'key-size': 16 }, 211 'aes256-ctr': { 'class': AES, 'mode': AES.MODE_CTR, 'block-size': 16, 'key-size': 32 }, 212 'blowfish-cbc': { 'class': Blowfish, 'mode': Blowfish.MODE_CBC, 'block-size': 8, 'key-size': 16 }, 213 'aes128-cbc': { 'class': AES, 'mode': AES.MODE_CBC, 'block-size': 16, 'key-size': 16 }, 214 'aes256-cbc': { 'class': AES, 'mode': AES.MODE_CBC, 'block-size': 16, 'key-size': 32 }, 215 '3des-cbc': { 'class': DES3, 'mode': DES3.MODE_CBC, 'block-size': 8, 'key-size': 24 }, 216 'arcfour128': { 'class': ARC4, 'mode': None, 'block-size': 8, 'key-size': 16 }, 217 'arcfour256': { 'class': ARC4, 'mode': None, 'block-size': 8, 'key-size': 32 }, 218 } 219 220 _mac_info = { 221 'hmac-sha1': { 'class': SHA, 'size': 20 }, 222 'hmac-sha1-96': { 'class': SHA, 'size': 12 }, 223 'hmac-md5': { 'class': MD5, 'size': 16 }, 224 'hmac-md5-96': { 'class': MD5, 'size': 12 }, 225 } 226 227 _key_info = { 228 'ssh-rsa': RSAKey, 229 'ssh-dss': DSSKey, 230 } 231 232 _kex_info = { 233 'diffie-hellman-group1-sha1': KexGroup1, 234 'diffie-hellman-group-exchange-sha1': KexGex, 235 } 236 237 _compression_info = { 238 # zlib@openssh.com is just zlib, but only turned on after a successful 239 # authentication. openssh servers may only offer this type because 240 # they've had troubles with security holes in zlib in the past. 241 'zlib@openssh.com': ( ZlibCompressor, ZlibDecompressor ), 242 'zlib': ( ZlibCompressor, ZlibDecompressor ), 243 'none': ( None, None ), 244 } 245 246 247 _modulus_pack = None 248
249 - def __init__(self, sock):
250 """ 251 Create a new SSH session over an existing socket, or socket-like 252 object. This only creates the Transport object; it doesn't begin the 253 SSH session yet. Use L{connect} or L{start_client} to begin a client 254 session, or L{start_server} to begin a server session. 255 256 If the object is not actually a socket, it must have the following 257 methods: 258 - C{send(str)}: Writes from 1 to C{len(str)} bytes, and 259 returns an int representing the number of bytes written. Returns 260 0 or raises C{EOFError} if the stream has been closed. 261 - C{recv(int)}: Reads from 1 to C{int} bytes and returns them as a 262 string. Returns 0 or raises C{EOFError} if the stream has been 263 closed. 264 - C{close()}: Closes the socket. 265 - C{settimeout(n)}: Sets a (float) timeout on I/O operations. 266 267 For ease of use, you may also pass in an address (as a tuple) or a host 268 string as the C{sock} argument. (A host string is a hostname with an 269 optional port (separated by C{":"}) which will be converted into a 270 tuple of C{(hostname, port)}.) A socket will be connected to this 271 address and used for communication. Exceptions from the C{socket} call 272 may be thrown in this case. 273 274 @param sock: a socket or socket-like object to create the session over. 275 @type sock: socket 276 """ 277 if isinstance(sock, (str, unicode)): 278 # convert "host:port" into (host, port) 279 hl = sock.split(':', 1) 280 if len(hl) == 1: 281 sock = (hl[0], 22) 282 else: 283 sock = (hl[0], int(hl[1])) 284 if type(sock) is tuple: 285 # connect to the given (host, port) 286 hostname, port = sock 287 reason = 'No suitable address family' 288 for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM): 289 if socktype == socket.SOCK_STREAM: 290 af = family 291 addr = sockaddr 292 sock = socket.socket(af, socket.SOCK_STREAM) 293 try: 294 retry_on_signal(lambda: sock.connect((hostname, port))) 295 except socket.error, e: 296 reason = str(e) 297 else: 298 break 299 else: 300 raise SSHException( 301 'Unable to connect to %s: %s' % (hostname, reason)) 302 # okay, normal socket-ish flow here... 303 threading.Thread.__init__(self) 304 self.setDaemon(True) 305 self.rng = rng 306 self.sock = sock 307 # Python < 2.3 doesn't have the settimeout method - RogerB 308 try: 309 # we set the timeout so we can check self.active periodically to 310 # see if we should bail. socket.timeout exception is never 311 # propagated. 312 self.sock.settimeout(0.1) 313 except AttributeError: 314 pass 315 316 # negotiated crypto parameters 317 self.packetizer = Packetizer(sock) 318 self.local_version = 'SSH-' + self._PROTO_ID + '-' + self._CLIENT_ID 319 self.remote_version = '' 320 self.local_cipher = self.remote_cipher = '' 321 self.local_kex_init = self.remote_kex_init = None 322 self.local_mac = self.remote_mac = None 323 self.local_compression = self.remote_compression = None 324 self.session_id = None 325 self.host_key_type = None 326 self.host_key = None 327 328 # state used during negotiation 329 self.kex_engine = None 330 self.H = None 331 self.K = None 332 333 self.active = False 334 self.initial_kex_done = False 335 self.in_kex = False 336 self.authenticated = False 337 self._expected_packet = tuple() 338 self.lock = threading.Lock() # synchronization (always higher level than write_lock) 339 340 # tracking open channels 341 self._channels = ChannelMap() 342 self.channel_events = { } # (id -> Event) 343 self.channels_seen = { } # (id -> True) 344 self._channel_counter = 1 345 self.window_size = 65536 346 self.max_packet_size = 34816 347 self._forward_agent_handler = None 348 self._x11_handler = None 349 self._tcp_handler = None 350 351 self.saved_exception = None 352 self.clear_to_send = threading.Event() 353 self.clear_to_send_lock = threading.Lock() 354 self.clear_to_send_timeout = 30.0 355 self.log_name = 'paramiko.transport' 356 self.logger = util.get_logger(self.log_name) 357 self.packetizer.set_log(self.logger) 358 self.auth_handler = None 359 self.global_response = None # response Message from an arbitrary global request 360 self.completion_event = None # user-defined event callbacks 361 self.banner_timeout = 15 # how long (seconds) to wait for the SSH banner 362 363 # server mode: 364 self.server_mode = False 365 self.server_object = None 366 self.server_key_dict = { } 367 self.server_accepts = [ ] 368 self.server_accept_cv = threading.Condition(self.lock) 369 self.subsystem_table = { }
370
371 - def __repr__(self):
372 """ 373 Returns a string representation of this object, for debugging. 374 375 @rtype: str 376 """ 377 out = '<paramiko.Transport at %s' % hex(long(id(self)) & 0xffffffffL) 378 if not self.active: 379 out += ' (unconnected)' 380 else: 381 if self.local_cipher != '': 382 out += ' (cipher %s, %d bits)' % (self.local_cipher, 383 self._cipher_info[self.local_cipher]['key-size'] * 8) 384 if self.is_authenticated(): 385 out += ' (active; %d open channel(s))' % len(self._channels) 386 elif self.initial_kex_done: 387 out += ' (connected; awaiting auth)' 388 else: 389 out += ' (connecting)' 390 out += '>' 391 return out
392
393 - def atfork(self):
394 """ 395 Terminate this Transport without closing the session. On posix 396 systems, if a Transport is open during process forking, both parent 397 and child will share the underlying socket, but only one process can 398 use the connection (without corrupting the session). Use this method 399 to clean up a Transport object without disrupting the other process. 400 401 @since: 1.5.3 402 """ 403 self.sock.close() 404 self.close()
405
406 - def get_security_options(self):
407 """ 408 Return a L{SecurityOptions} object which can be used to tweak the 409 encryption algorithms this transport will permit, and the order of 410 preference for them. 411 412 @return: an object that can be used to change the preferred algorithms 413 for encryption, digest (hash), public key, and key exchange. 414 @rtype: L{SecurityOptions} 415 """ 416 return SecurityOptions(self)
417
418 - def start_client(self, event=None):
419 """ 420 Negotiate a new SSH2 session as a client. This is the first step after 421 creating a new L{Transport}. A separate thread is created for protocol 422 negotiation. 423 424 If an event is passed in, this method returns immediately. When 425 negotiation is done (successful or not), the given C{Event} will 426 be triggered. On failure, L{is_active} will return C{False}. 427 428 (Since 1.4) If C{event} is C{None}, this method will not return until 429 negotation is done. On success, the method returns normally. 430 Otherwise an SSHException is raised. 431 432 After a successful negotiation, you will usually want to authenticate, 433 calling L{auth_password <Transport.auth_password>} or 434 L{auth_publickey <Transport.auth_publickey>}. 435 436 @note: L{connect} is a simpler method for connecting as a client. 437 438 @note: After calling this method (or L{start_server} or L{connect}), 439 you should no longer directly read from or write to the original 440 socket object. 441 442 @param event: an event to trigger when negotiation is complete 443 (optional) 444 @type event: threading.Event 445 446 @raise SSHException: if negotiation fails (and no C{event} was passed 447 in) 448 """ 449 self.active = True 450 if event is not None: 451 # async, return immediately and let the app poll for completion 452 self.completion_event = event 453 self.start() 454 return 455 456 # synchronous, wait for a result 457 self.completion_event = event = threading.Event() 458 self.start() 459 Random.atfork() 460 while True: 461 event.wait(0.1) 462 if not self.active: 463 e = self.get_exception() 464 if e is not None: 465 raise e 466 raise SSHException('Negotiation failed.') 467 if event.isSet(): 468 break
469
470 - def start_server(self, event=None, server=None):
471 """ 472 Negotiate a new SSH2 session as a server. This is the first step after 473 creating a new L{Transport} and setting up your server host key(s). A 474 separate thread is created for protocol negotiation. 475 476 If an event is passed in, this method returns immediately. When 477 negotiation is done (successful or not), the given C{Event} will 478 be triggered. On failure, L{is_active} will return C{False}. 479 480 (Since 1.4) If C{event} is C{None}, this method will not return until 481 negotation is done. On success, the method returns normally. 482 Otherwise an SSHException is raised. 483 484 After a successful negotiation, the client will need to authenticate. 485 Override the methods 486 L{get_allowed_auths <ServerInterface.get_allowed_auths>}, 487 L{check_auth_none <ServerInterface.check_auth_none>}, 488 L{check_auth_password <ServerInterface.check_auth_password>}, and 489 L{check_auth_publickey <ServerInterface.check_auth_publickey>} in the 490 given C{server} object to control the authentication process. 491 492 After a successful authentication, the client should request to open 493 a channel. Override 494 L{check_channel_request <ServerInterface.check_channel_request>} in the 495 given C{server} object to allow channels to be opened. 496 497 @note: After calling this method (or L{start_client} or L{connect}), 498 you should no longer directly read from or write to the original 499 socket object. 500 501 @param event: an event to trigger when negotiation is complete. 502 @type event: threading.Event 503 @param server: an object used to perform authentication and create 504 L{Channel}s. 505 @type server: L{server.ServerInterface} 506 507 @raise SSHException: if negotiation fails (and no C{event} was passed 508 in) 509 """ 510 if server is None: 511 server = ServerInterface() 512 self.server_mode = True 513 self.server_object = server 514 self.active = True 515 if event is not None: 516 # async, return immediately and let the app poll for completion 517 self.completion_event = event 518 self.start() 519 return 520 521 # synchronous, wait for a result 522 self.completion_event = event = threading.Event() 523 self.start() 524 while True: 525 event.wait(0.1) 526 if not self.active: 527 e = self.get_exception() 528 if e is not None: 529 raise e 530 raise SSHException('Negotiation failed.') 531 if event.isSet(): 532 break
533
534 - def add_server_key(self, key):
535 """ 536 Add a host key to the list of keys used for server mode. When behaving 537 as a server, the host key is used to sign certain packets during the 538 SSH2 negotiation, so that the client can trust that we are who we say 539 we are. Because this is used for signing, the key must contain private 540 key info, not just the public half. Only one key of each type (RSA or 541 DSS) is kept. 542 543 @param key: the host key to add, usually an L{RSAKey <rsakey.RSAKey>} or 544 L{DSSKey <dsskey.DSSKey>}. 545 @type key: L{PKey <pkey.PKey>} 546 """ 547 self.server_key_dict[key.get_name()] = key
548
549 - def get_server_key(self):
550 """ 551 Return the active host key, in server mode. After negotiating with the 552 client, this method will return the negotiated host key. If only one 553 type of host key was set with L{add_server_key}, that's the only key 554 that will ever be returned. But in cases where you have set more than 555 one type of host key (for example, an RSA key and a DSS key), the key 556 type will be negotiated by the client, and this method will return the 557 key of the type agreed on. If the host key has not been negotiated 558 yet, C{None} is returned. In client mode, the behavior is undefined. 559 560 @return: host key of the type negotiated by the client, or C{None}. 561 @rtype: L{PKey <pkey.PKey>} 562 """ 563 try: 564 return self.server_key_dict[self.host_key_type] 565 except KeyError: 566 pass 567 return None
568
569 - def load_server_moduli(filename=None):
570 """ 571 I{(optional)} 572 Load a file of prime moduli for use in doing group-exchange key 573 negotiation in server mode. It's a rather obscure option and can be 574 safely ignored. 575 576 In server mode, the remote client may request "group-exchange" key 577 negotiation, which asks the server to send a random prime number that 578 fits certain criteria. These primes are pretty difficult to compute, 579 so they can't be generated on demand. But many systems contain a file 580 of suitable primes (usually named something like C{/etc/ssh/moduli}). 581 If you call C{load_server_moduli} and it returns C{True}, then this 582 file of primes has been loaded and we will support "group-exchange" in 583 server mode. Otherwise server mode will just claim that it doesn't 584 support that method of key negotiation. 585 586 @param filename: optional path to the moduli file, if you happen to 587 know that it's not in a standard location. 588 @type filename: str 589 @return: True if a moduli file was successfully loaded; False 590 otherwise. 591 @rtype: bool 592 593 @note: This has no effect when used in client mode. 594 """ 595 Transport._modulus_pack = ModulusPack(rng) 596 # places to look for the openssh "moduli" file 597 file_list = [ '/etc/ssh/moduli', '/usr/local/etc/moduli' ] 598 if filename is not None: 599 file_list.insert(0, filename) 600 for fn in file_list: 601 try: 602 Transport._modulus_pack.read_file(fn) 603 return True 604 except IOError: 605 pass 606 # none succeeded 607 Transport._modulus_pack = None 608 return False
609 load_server_moduli = staticmethod(load_server_moduli) 610
611 - def close(self):
612 """ 613 Close this session, and any open channels that are tied to it. 614 """ 615 if not self.active: 616 return 617 self.active = False 618 self.packetizer.close() 619 self.join() 620 for chan in self._channels.values(): 621 chan._unlink()
622
623 - def get_remote_server_key(self):
624 """ 625 Return the host key of the server (in client mode). 626 627 @note: Previously this call returned a tuple of (key type, key string). 628 You can get the same effect by calling 629 L{PKey.get_name <pkey.PKey.get_name>} for the key type, and 630 C{str(key)} for the key string. 631 632 @raise SSHException: if no session is currently active. 633 634 @return: public key of the remote server 635 @rtype: L{PKey <pkey.PKey>} 636 """ 637 if (not self.active) or (not self.initial_kex_done): 638 raise SSHException('No existing session') 639 return self.host_key
640
641 - def is_active(self):
642 """ 643 Return true if this session is active (open). 644 645 @return: True if the session is still active (open); False if the 646 session is closed 647 @rtype: bool 648 """ 649 return self.active
650
651 - def open_session(self):
652 """ 653 Request a new channel to the server, of type C{"session"}. This 654 is just an alias for C{open_channel('session')}. 655 656 @return: a new L{Channel} 657 @rtype: L{Channel} 658 659 @raise SSHException: if the request is rejected or the session ends 660 prematurely 661 """ 662 return self.open_channel('session')
663
664 - def open_x11_channel(self, src_addr=None):
665 """ 666 Request a new channel to the client, of type C{"x11"}. This 667 is just an alias for C{open_channel('x11', src_addr=src_addr)}. 668 669 @param src_addr: the source address of the x11 server (port is the 670 x11 port, ie. 6010) 671 @type src_addr: (str, int) 672 @return: a new L{Channel} 673 @rtype: L{Channel} 674 675 @raise SSHException: if the request is rejected or the session ends 676 prematurely 677 """ 678 return self.open_channel('x11', src_addr=src_addr)
679
680 - def open_forward_agent_channel(self):
681 """ 682 Request a new channel to the client, of type 683 C{"auth-agent@openssh.com"}. 684 685 This is just an alias for C{open_channel('auth-agent@openssh.com')}. 686 @return: a new L{Channel} 687 @rtype: L{Channel} 688 689 @raise SSHException: if the request is rejected or the session ends 690 prematurely 691 """ 692 return self.open_channel('auth-agent@openssh.com')
693
694 - def open_forwarded_tcpip_channel(self, (src_addr, src_port), (dest_addr, dest_port)):
695 """ 696 Request a new channel back to the client, of type C{"forwarded-tcpip"}. 697 This is used after a client has requested port forwarding, for sending 698 incoming connections back to the client. 699 700 @param src_addr: originator's address 701 @param src_port: originator's port 702 @param dest_addr: local (server) connected address 703 @param dest_port: local (server) connected port 704 """ 705 return self.open_channel('forwarded-tcpip', (dest_addr, dest_port), (src_addr, src_port))
706
707 - def open_channel(self, kind, dest_addr=None, src_addr=None):
708 """ 709 Request a new channel to the server. L{Channel}s are socket-like 710 objects used for the actual transfer of data across the session. 711 You may only request a channel after negotiating encryption (using 712 L{connect} or L{start_client}) and authenticating. 713 714 @param kind: the kind of channel requested (usually C{"session"}, 715 C{"forwarded-tcpip"}, C{"direct-tcpip"}, or C{"x11"}) 716 @type kind: str 717 @param dest_addr: the destination address of this port forwarding, 718 if C{kind} is C{"forwarded-tcpip"} or C{"direct-tcpip"} (ignored 719 for other channel types) 720 @type dest_addr: (str, int) 721 @param src_addr: the source address of this port forwarding, if 722 C{kind} is C{"forwarded-tcpip"}, C{"direct-tcpip"}, or C{"x11"} 723 @type src_addr: (str, int) 724 @return: a new L{Channel} on success 725 @rtype: L{Channel} 726 727 @raise SSHException: if the request is rejected or the session ends 728 prematurely 729 """ 730 if not self.active: 731 raise SSHException('SSH session not active') 732 self.lock.acquire() 733 try: 734 chanid = self._next_channel() 735 m = Message() 736 m.add_byte(chr(MSG_CHANNEL_OPEN)) 737 m.add_string(kind) 738 m.add_int(chanid) 739 m.add_int(self.window_size) 740 m.add_int(self.max_packet_size) 741 if (kind == 'forwarded-tcpip') or (kind == 'direct-tcpip'): 742 m.add_string(dest_addr[0]) 743 m.add_int(dest_addr[1]) 744 m.add_string(src_addr[0]) 745 m.add_int(src_addr[1]) 746 elif kind == 'x11': 747 m.add_string(src_addr[0]) 748 m.add_int(src_addr[1]) 749 chan = Channel(chanid) 750 self._channels.put(chanid, chan) 751 self.channel_events[chanid] = event = threading.Event() 752 self.channels_seen[chanid] = True 753 chan._set_transport(self) 754 chan._set_window(self.window_size, self.max_packet_size) 755 finally: 756 self.lock.release() 757 self._send_user_message(m) 758 while True: 759 event.wait(0.1); 760 if not self.active: 761 e = self.get_exception() 762 if e is None: 763 e = SSHException('Unable to open channel.') 764 raise e 765 if event.isSet(): 766 break 767 chan = self._channels.get(chanid) 768 if chan is not None: 769 return chan 770 e = self.get_exception() 771 if e is None: 772 e = SSHException('Unable to open channel.') 773 raise e
774
775 - def request_port_forward(self, address, port, handler=None):
776 """ 777 Ask the server to forward TCP connections from a listening port on 778 the server, across this SSH session. 779 780 If a handler is given, that handler is called from a different thread 781 whenever a forwarded connection arrives. The handler parameters are:: 782 783 handler(channel, (origin_addr, origin_port), (server_addr, server_port)) 784 785 where C{server_addr} and C{server_port} are the address and port that 786 the server was listening on. 787 788 If no handler is set, the default behavior is to send new incoming 789 forwarded connections into the accept queue, to be picked up via 790 L{accept}. 791 792 @param address: the address to bind when forwarding 793 @type address: str 794 @param port: the port to forward, or 0 to ask the server to allocate 795 any port 796 @type port: int 797 @param handler: optional handler for incoming forwarded connections 798 @type handler: function(Channel, (str, int), (str, int)) 799 @return: the port # allocated by the server 800 @rtype: int 801 802 @raise SSHException: if the server refused the TCP forward request 803 """ 804 if not self.active: 805 raise SSHException('SSH session not active') 806 address = str(address) 807 port = int(port) 808 response = self.global_request('tcpip-forward', (address, port), wait=True) 809 if response is None: 810 raise SSHException('TCP forwarding request denied') 811 if port == 0: 812 port = response.get_int() 813 if handler is None: 814 def default_handler(channel, (src_addr, src_port), (dest_addr, dest_port)): 815 self._queue_incoming_channel(channel)
816 handler = default_handler 817 self._tcp_handler = handler 818 return port
819
820 - def cancel_port_forward(self, address, port):
821 """ 822 Ask the server to cancel a previous port-forwarding request. No more 823 connections to the given address & port will be forwarded across this 824 ssh connection. 825 826 @param address: the address to stop forwarding 827 @type address: str 828 @param port: the port to stop forwarding 829 @type port: int 830 """ 831 if not self.active: 832 return 833 self._tcp_handler = None 834 self.global_request('cancel-tcpip-forward', (address, port), wait=True)
835
836 - def open_sftp_client(self):
837 """ 838 Create an SFTP client channel from an open transport. On success, 839 an SFTP session will be opened with the remote host, and a new 840 SFTPClient object will be returned. 841 842 @return: a new L{SFTPClient} object, referring to an sftp session 843 (channel) across this transport 844 @rtype: L{SFTPClient} 845 """ 846 return SFTPClient.from_transport(self)
847
848 - def send_ignore(self, bytes=None):
849 """ 850 Send a junk packet across the encrypted link. This is sometimes used 851 to add "noise" to a connection to confuse would-be attackers. It can 852 also be used as a keep-alive for long lived connections traversing 853 firewalls. 854 855 @param bytes: the number of random bytes to send in the payload of the 856 ignored packet -- defaults to a random number from 10 to 41. 857 @type bytes: int 858 """ 859 m = Message() 860 m.add_byte(chr(MSG_IGNORE)) 861 if bytes is None: 862 bytes = (ord(rng.read(1)) % 32) + 10 863 m.add_bytes(rng.read(bytes)) 864 self._send_user_message(m)
865
866 - def renegotiate_keys(self):
867 """ 868 Force this session to switch to new keys. Normally this is done 869 automatically after the session hits a certain number of packets or 870 bytes sent or received, but this method gives you the option of forcing 871 new keys whenever you want. Negotiating new keys causes a pause in 872 traffic both ways as the two sides swap keys and do computations. This 873 method returns when the session has switched to new keys. 874 875 @raise SSHException: if the key renegotiation failed (which causes the 876 session to end) 877 """ 878 self.completion_event = threading.Event() 879 self._send_kex_init() 880 while True: 881 self.completion_event.wait(0.1) 882 if not self.active: 883 e = self.get_exception() 884 if e is not None: 885 raise e 886 raise SSHException('Negotiation failed.') 887 if self.completion_event.isSet(): 888 break 889 return
890
891 - def set_keepalive(self, interval):
892 """ 893 Turn on/off keepalive packets (default is off). If this is set, after 894 C{interval} seconds without sending any data over the connection, a 895 "keepalive" packet will be sent (and ignored by the remote host). This 896 can be useful to keep connections alive over a NAT, for example. 897 898 @param interval: seconds to wait before sending a keepalive packet (or 899 0 to disable keepalives). 900 @type interval: int 901 """ 902 self.packetizer.set_keepalive(interval, 903 lambda x=weakref.proxy(self): x.global_request('keepalive@lag.net', wait=False))
904
905 - def global_request(self, kind, data=None, wait=True):
906 """ 907 Make a global request to the remote host. These are normally 908 extensions to the SSH2 protocol. 909 910 @param kind: name of the request. 911 @type kind: str 912 @param data: an optional tuple containing additional data to attach 913 to the request. 914 @type data: tuple 915 @param wait: C{True} if this method should not return until a response 916 is received; C{False} otherwise. 917 @type wait: bool 918 @return: a L{Message} containing possible additional data if the 919 request was successful (or an empty L{Message} if C{wait} was 920 C{False}); C{None} if the request was denied. 921 @rtype: L{Message} 922 """ 923 if wait: 924 self.completion_event = threading.Event() 925 m = Message() 926 m.add_byte(chr(MSG_GLOBAL_REQUEST)) 927 m.add_string(kind) 928 m.add_boolean(wait) 929 if data is not None: 930 m.add(*data) 931 self._log(DEBUG, 'Sending global request "%s"' % kind) 932 self._send_user_message(m) 933 if not wait: 934 return None 935 while True: 936 self.completion_event.wait(0.1) 937 if not self.active: 938 return None 939 if self.completion_event.isSet(): 940 break 941 return self.global_response
942
943 - def accept(self, timeout=None):
944 """ 945 Return the next channel opened by the client over this transport, in 946 server mode. If no channel is opened before the given timeout, C{None} 947 is returned. 948 949 @param timeout: seconds to wait for a channel, or C{None} to wait 950 forever 951 @type timeout: int 952 @return: a new Channel opened by the client 953 @rtype: L{Channel} 954 """ 955 self.lock.acquire() 956 try: 957 if len(self.server_accepts) > 0: 958 chan = self.server_accepts.pop(0) 959 else: 960 self.server_accept_cv.wait(timeout) 961 if len(self.server_accepts) > 0: 962 chan = self.server_accepts.pop(0) 963 else: 964 # timeout 965 chan = None 966 finally: 967 self.lock.release() 968 return chan
969
970 - def connect(self, hostkey=None, username='', password=None, pkey=None):
971 """ 972 Negotiate an SSH2 session, and optionally verify the server's host key 973 and authenticate using a password or private key. This is a shortcut 974 for L{start_client}, L{get_remote_server_key}, and 975 L{Transport.auth_password} or L{Transport.auth_publickey}. Use those 976 methods if you want more control. 977 978 You can use this method immediately after creating a Transport to 979 negotiate encryption with a server. If it fails, an exception will be 980 thrown. On success, the method will return cleanly, and an encrypted 981 session exists. You may immediately call L{open_channel} or 982 L{open_session} to get a L{Channel} object, which is used for data 983 transfer. 984 985 @note: If you fail to supply a password or private key, this method may 986 succeed, but a subsequent L{open_channel} or L{open_session} call may 987 fail because you haven't authenticated yet. 988 989 @param hostkey: the host key expected from the server, or C{None} if 990 you don't want to do host key verification. 991 @type hostkey: L{PKey<pkey.PKey>} 992 @param username: the username to authenticate as. 993 @type username: str 994 @param password: a password to use for authentication, if you want to 995 use password authentication; otherwise C{None}. 996 @type password: str 997 @param pkey: a private key to use for authentication, if you want to 998 use private key authentication; otherwise C{None}. 999 @type pkey: L{PKey<pkey.PKey>} 1000 1001 @raise SSHException: if the SSH2 negotiation fails, the host key 1002 supplied by the server is incorrect, or authentication fails. 1003 """ 1004 if hostkey is not None: 1005 self._preferred_keys = [ hostkey.get_name() ] 1006 1007 self.start_client() 1008 1009 # check host key if we were given one 1010 if (hostkey is not None): 1011 key = self.get_remote_server_key() 1012 if (key.get_name() != hostkey.get_name()) or (str(key) != str(hostkey)): 1013 self._log(DEBUG, 'Bad host key from server') 1014 self._log(DEBUG, 'Expected: %s: %s' % (hostkey.get_name(), repr(str(hostkey)))) 1015 self._log(DEBUG, 'Got : %s: %s' % (key.get_name(), repr(str(key)))) 1016 raise SSHException('Bad host key from server') 1017 self._log(DEBUG, 'Host key verified (%s)' % hostkey.get_name()) 1018 1019 if (pkey is not None) or (password is not None): 1020 if password is not None: 1021 self._log(DEBUG, 'Attempting password auth...') 1022 self.auth_password(username, password) 1023 else: 1024 self._log(DEBUG, 'Attempting public-key auth...') 1025 self.auth_publickey(username, pkey) 1026 1027 return
1028
1029 - def get_exception(self):
1030 """ 1031 Return any exception that happened during the last server request. 1032 This can be used to fetch more specific error information after using 1033 calls like L{start_client}. The exception (if any) is cleared after 1034 this call. 1035 1036 @return: an exception, or C{None} if there is no stored exception. 1037 @rtype: Exception 1038 1039 @since: 1.1 1040 """ 1041 self.lock.acquire() 1042 try: 1043 e = self.saved_exception 1044 self.saved_exception = None 1045 return e 1046 finally: 1047 self.lock.release()
1048
1049 - def set_subsystem_handler(self, name, handler, *larg, **kwarg):
1050 """ 1051 Set the handler class for a subsystem in server mode. If a request 1052 for this subsystem is made on an open ssh channel later, this handler 1053 will be constructed and called -- see L{SubsystemHandler} for more 1054 detailed documentation. 1055 1056 Any extra parameters (including keyword arguments) are saved and 1057 passed to the L{SubsystemHandler} constructor later. 1058 1059 @param name: name of the subsystem. 1060 @type name: str 1061 @param handler: subclass of L{SubsystemHandler} that handles this 1062 subsystem. 1063 @type handler: class 1064 """ 1065 try: 1066 self.lock.acquire() 1067 self.subsystem_table[name] = (handler, larg, kwarg) 1068 finally: 1069 self.lock.release()
1070
1071 - def is_authenticated(self):
1072 """ 1073 Return true if this session is active and authenticated. 1074 1075 @return: True if the session is still open and has been authenticated 1076 successfully; False if authentication failed and/or the session is 1077 closed. 1078 @rtype: bool 1079 """ 1080 return self.active and (self.auth_handler is not None) and self.auth_handler.is_authenticated()
1081
1082 - def get_username(self):
1083 """ 1084 Return the username this connection is authenticated for. If the 1085 session is not authenticated (or authentication failed), this method 1086 returns C{None}. 1087 1088 @return: username that was authenticated, or C{None}. 1089 @rtype: string 1090 """ 1091 if not self.active or (self.auth_handler is None): 1092 return None 1093 return self.auth_handler.get_username()
1094
1095 - def auth_none(self, username):
1096 """ 1097 Try to authenticate to the server using no authentication at all. 1098 This will almost always fail. It may be useful for determining the 1099 list of authentication types supported by the server, by catching the 1100 L{BadAuthenticationType} exception raised. 1101 1102 @param username: the username to authenticate as 1103 @type username: string 1104 @return: list of auth types permissible for the next stage of 1105 authentication (normally empty) 1106 @rtype: list 1107 1108 @raise BadAuthenticationType: if "none" authentication isn't allowed 1109 by the server for this user 1110 @raise SSHException: if the authentication failed due to a network 1111 error 1112 1113 @since: 1.5 1114 """ 1115 if (not self.active) or (not self.initial_kex_done): 1116 raise SSHException('No existing session') 1117 my_event = threading.Event() 1118 self.auth_handler = AuthHandler(self) 1119 self.auth_handler.auth_none(username, my_event) 1120 return self.auth_handler.wait_for_response(my_event)
1121
1122 - def auth_password(self, username, password, event=None, fallback=True):
1123 """ 1124 Authenticate to the server using a password. The username and password 1125 are sent over an encrypted link. 1126 1127 If an C{event} is passed in, this method will return immediately, and 1128 the event will be triggered once authentication succeeds or fails. On 1129 success, L{is_authenticated} will return C{True}. On failure, you may 1130 use L{get_exception} to get more detailed error information. 1131 1132 Since 1.1, if no event is passed, this method will block until the 1133 authentication succeeds or fails. On failure, an exception is raised. 1134 Otherwise, the method simply returns. 1135 1136 Since 1.5, if no event is passed and C{fallback} is C{True} (the 1137 default), if the server doesn't support plain password authentication 1138 but does support so-called "keyboard-interactive" mode, an attempt 1139 will be made to authenticate using this interactive mode. If it fails, 1140 the normal exception will be thrown as if the attempt had never been 1141 made. This is useful for some recent Gentoo and Debian distributions, 1142 which turn off plain password authentication in a misguided belief 1143 that interactive authentication is "more secure". (It's not.) 1144 1145 If the server requires multi-step authentication (which is very rare), 1146 this method will return a list of auth types permissible for the next 1147 step. Otherwise, in the normal case, an empty list is returned. 1148 1149 @param username: the username to authenticate as 1150 @type username: str 1151 @param password: the password to authenticate with 1152 @type password: str or unicode 1153 @param event: an event to trigger when the authentication attempt is 1154 complete (whether it was successful or not) 1155 @type event: threading.Event 1156 @param fallback: C{True} if an attempt at an automated "interactive" 1157 password auth should be made if the server doesn't support normal 1158 password auth 1159 @type fallback: bool 1160 @return: list of auth types permissible for the next stage of 1161 authentication (normally empty) 1162 @rtype: list 1163 1164 @raise BadAuthenticationType: if password authentication isn't 1165 allowed by the server for this user (and no event was passed in) 1166 @raise AuthenticationException: if the authentication failed (and no 1167 event was passed in) 1168 @raise SSHException: if there was a network error 1169 """ 1170 if (not self.active) or (not self.initial_kex_done): 1171 # we should never try to send the password unless we're on a secure link 1172 raise SSHException('No existing session') 1173 if event is None: 1174 my_event = threading.Event() 1175 else: 1176 my_event = event 1177 self.auth_handler = AuthHandler(self) 1178 self.auth_handler.auth_password(username, password, my_event) 1179 if event is not None: 1180 # caller wants to wait for event themselves 1181 return [] 1182 try: 1183 return self.auth_handler.wait_for_response(my_event) 1184 except BadAuthenticationType, x: 1185 # if password auth isn't allowed, but keyboard-interactive *is*, try to fudge it 1186 if not fallback or ('keyboard-interactive' not in x.allowed_types): 1187 raise 1188 try: 1189 def handler(title, instructions, fields): 1190 if len(fields) > 1: 1191 raise SSHException('Fallback authentication failed.') 1192 if len(fields) == 0: 1193 # for some reason, at least on os x, a 2nd request will 1194 # be made with zero fields requested. maybe it's just 1195 # to try to fake out automated scripting of the exact 1196 # type we're doing here. *shrug* :) 1197 return [] 1198 return [ password ]
1199 return self.auth_interactive(username, handler) 1200 except SSHException, ignored: 1201 # attempt failed; just raise the original exception 1202 raise x 1203 return None 1204
1205 - def auth_publickey(self, username, key, event=None):
1206 """ 1207 Authenticate to the server using a private key. The key is used to 1208 sign data from the server, so it must include the private part. 1209 1210 If an C{event} is passed in, this method will return immediately, and 1211 the event will be triggered once authentication succeeds or fails. On 1212 success, L{is_authenticated} will return C{True}. On failure, you may 1213 use L{get_exception} to get more detailed error information. 1214 1215 Since 1.1, if no event is passed, this method will block until the 1216 authentication succeeds or fails. On failure, an exception is raised. 1217 Otherwise, the method simply returns. 1218 1219 If the server requires multi-step authentication (which is very rare), 1220 this method will return a list of auth types permissible for the next 1221 step. Otherwise, in the normal case, an empty list is returned. 1222 1223 @param username: the username to authenticate as 1224 @type username: string 1225 @param key: the private key to authenticate with 1226 @type key: L{PKey <pkey.PKey>} 1227 @param event: an event to trigger when the authentication attempt is 1228 complete (whether it was successful or not) 1229 @type event: threading.Event 1230 @return: list of auth types permissible for the next stage of 1231 authentication (normally empty) 1232 @rtype: list 1233 1234 @raise BadAuthenticationType: if public-key authentication isn't 1235 allowed by the server for this user (and no event was passed in) 1236 @raise AuthenticationException: if the authentication failed (and no 1237 event was passed in) 1238 @raise SSHException: if there was a network error 1239 """ 1240 if (not self.active) or (not self.initial_kex_done): 1241 # we should never try to authenticate unless we're on a secure link 1242 raise SSHException('No existing session') 1243 if event is None: 1244 my_event = threading.Event() 1245 else: 1246 my_event = event 1247 self.auth_handler = AuthHandler(self) 1248 self.auth_handler.auth_publickey(username, key, my_event) 1249 if event is not None: 1250 # caller wants to wait for event themselves 1251 return [] 1252 return self.auth_handler.wait_for_response(my_event)
1253
1254 - def auth_interactive(self, username, handler, submethods=''):
1255 """ 1256 Authenticate to the server interactively. A handler is used to answer 1257 arbitrary questions from the server. On many servers, this is just a 1258 dumb wrapper around PAM. 1259 1260 This method will block until the authentication succeeds or fails, 1261 peroidically calling the handler asynchronously to get answers to 1262 authentication questions. The handler may be called more than once 1263 if the server continues to ask questions. 1264 1265 The handler is expected to be a callable that will handle calls of the 1266 form: C{handler(title, instructions, prompt_list)}. The C{title} is 1267 meant to be a dialog-window title, and the C{instructions} are user 1268 instructions (both are strings). C{prompt_list} will be a list of 1269 prompts, each prompt being a tuple of C{(str, bool)}. The string is 1270 the prompt and the boolean indicates whether the user text should be 1271 echoed. 1272 1273 A sample call would thus be: 1274 C{handler('title', 'instructions', [('Password:', False)])}. 1275 1276 The handler should return a list or tuple of answers to the server's 1277 questions. 1278 1279 If the server requires multi-step authentication (which is very rare), 1280 this method will return a list of auth types permissible for the next 1281 step. Otherwise, in the normal case, an empty list is returned. 1282 1283 @param username: the username to authenticate as 1284 @type username: string 1285 @param handler: a handler for responding to server questions 1286 @type handler: callable 1287 @param submethods: a string list of desired submethods (optional) 1288 @type submethods: str 1289 @return: list of auth types permissible for the next stage of 1290 authentication (normally empty). 1291 @rtype: list 1292 1293 @raise BadAuthenticationType: if public-key authentication isn't 1294 allowed by the server for this user 1295 @raise AuthenticationException: if the authentication failed 1296 @raise SSHException: if there was a network error 1297 1298 @since: 1.5 1299 """ 1300 if (not self.active) or (not self.initial_kex_done): 1301 # we should never try to authenticate unless we're on a secure link 1302 raise SSHException('No existing session') 1303 my_event = threading.Event() 1304 self.auth_handler = AuthHandler(self) 1305 self.auth_handler.auth_interactive(username, handler, my_event, submethods) 1306 return self.auth_handler.wait_for_response(my_event)
1307
1308 - def set_log_channel(self, name):
1309 """ 1310 Set the channel for this transport's logging. The default is 1311 C{"paramiko.transport"} but it can be set to anything you want. 1312 (See the C{logging} module for more info.) SSH Channels will log 1313 to a sub-channel of the one specified. 1314 1315 @param name: new channel name for logging 1316 @type name: str 1317 1318 @since: 1.1 1319 """ 1320 self.log_name = name 1321 self.logger = util.get_logger(name) 1322 self.packetizer.set_log(self.logger)
1323
1324 - def get_log_channel(self):
1325 """ 1326 Return the channel name used for this transport's logging. 1327 1328 @return: channel name. 1329 @rtype: str 1330 1331 @since: 1.2 1332 """ 1333 return self.log_name
1334
1335 - def set_hexdump(self, hexdump):
1336 """ 1337 Turn on/off logging a hex dump of protocol traffic at DEBUG level in 1338 the logs. Normally you would want this off (which is the default), 1339 but if you are debugging something, it may be useful. 1340 1341 @param hexdump: C{True} to log protocol traffix (in hex) to the log; 1342 C{False} otherwise. 1343 @type hexdump: bool 1344 """ 1345 self.packetizer.set_hexdump(hexdump)
1346
1347 - def get_hexdump(self):
1348 """ 1349 Return C{True} if the transport is currently logging hex dumps of 1350 protocol traffic. 1351 1352 @return: C{True} if hex dumps are being logged 1353 @rtype: bool 1354 1355 @since: 1.4 1356 """ 1357 return self.packetizer.get_hexdump()
1358
1359 - def use_compression(self, compress=True):
1360 """ 1361 Turn on/off compression. This will only have an affect before starting 1362 the transport (ie before calling L{connect}, etc). By default, 1363 compression is off since it negatively affects interactive sessions. 1364 1365 @param compress: C{True} to ask the remote client/server to compress 1366 traffic; C{False} to refuse compression 1367 @type compress: bool 1368 1369 @since: 1.5.2 1370 """ 1371 if compress: 1372 self._preferred_compression = ( 'zlib@openssh.com', 'zlib', 'none' ) 1373 else: 1374 self._preferred_compression = ( 'none', )
1375
1376 - def getpeername(self):
1377 """ 1378 Return the address of the remote side of this Transport, if possible. 1379 This is effectively a wrapper around C{'getpeername'} on the underlying 1380 socket. If the socket-like object has no C{'getpeername'} method, 1381 then C{("unknown", 0)} is returned. 1382 1383 @return: the address if the remote host, if known 1384 @rtype: tuple(str, int) 1385 """ 1386 gp = getattr(self.sock, 'getpeername', None) 1387 if gp is None: 1388 return ('unknown', 0) 1389 return gp()
1390
1391 - def stop_thread(self):
1392 self.active = False 1393 self.packetizer.close()
1394 1395 1396 ### internals... 1397 1398
1399 - def _log(self, level, msg, *args):
1400 if issubclass(type(msg), list): 1401 for m in msg: 1402 self.logger.log(level, m) 1403 else: 1404 self.logger.log(level, msg, *args)
1405
1406 - def _get_modulus_pack(self):
1407 "used by KexGex to find primes for group exchange" 1408 return self._modulus_pack
1409
1410 - def _next_channel(self):
1411 "you are holding the lock" 1412 chanid = self._channel_counter 1413 while self._channels.get(chanid) is not None: 1414 self._channel_counter = (self._channel_counter + 1) & 0xffffff 1415 chanid = self._channel_counter 1416 self._channel_counter = (self._channel_counter + 1) & 0xffffff 1417 return chanid
1418 1422
1423 - def _send_message(self, data):
1424 self.packetizer.send_message(data)
1425
1426 - def _send_user_message(self, data):
1427 """ 1428 send a message, but block if we're in key negotiation. this is used 1429 for user-initiated requests. 1430 """ 1431 start = time.time() 1432 while True: 1433 self.clear_to_send.wait(0.1) 1434 if not self.active: 1435 self._log(DEBUG, 'Dropping user packet because connection is dead.') 1436 return 1437 self.clear_to_send_lock.acquire() 1438 if self.clear_to_send.isSet(): 1439 break 1440 self.clear_to_send_lock.release() 1441 if time.time() > start + self.clear_to_send_timeout: 1442 raise SSHException('Key-exchange timed out waiting for key negotiation') 1443 try: 1444 self._send_message(data) 1445 finally: 1446 self.clear_to_send_lock.release()
1447
1448 - def _set_K_H(self, k, h):
1449 "used by a kex object to set the K (root key) and H (exchange hash)" 1450 self.K = k 1451 self.H = h 1452 if self.session_id == None: 1453 self.session_id = h
1454
1455 - def _expect_packet(self, *ptypes):
1456 "used by a kex object to register the next packet type it expects to see" 1457 self._expected_packet = tuple(ptypes)
1458
1459 - def _verify_key(self, host_key, sig):
1460 key = self._key_info[self.host_key_type](Message(host_key)) 1461 if key is None: 1462 raise SSHException('Unknown host key type') 1463 if not key.verify_ssh_sig(self.H, Message(sig)): 1464 raise SSHException('Signature verification (%s) failed.' % self.host_key_type) 1465 self.host_key = key
1466
1467 - def _compute_key(self, id, nbytes):
1468 "id is 'A' - 'F' for the various keys used by ssh" 1469 m = Message() 1470 m.add_mpint(self.K) 1471 m.add_bytes(self.H) 1472 m.add_byte(id) 1473 m.add_bytes(self.session_id) 1474 out = sofar = SHA.new(str(m)).digest() 1475 while len(out) < nbytes: 1476 m = Message() 1477 m.add_mpint(self.K) 1478 m.add_bytes(self.H) 1479 m.add_bytes(sofar) 1480 digest = SHA.new(str(m)).digest() 1481 out += digest 1482 sofar += digest 1483 return out[:nbytes]
1484
1485 - def _get_cipher(self, name, key, iv):
1486 if name not in self._cipher_info: 1487 raise SSHException('Unknown client cipher ' + name) 1488 if name in ('arcfour128', 'arcfour256'): 1489 # arcfour cipher 1490 cipher = self._cipher_info[name]['class'].new(key) 1491 # as per RFC 4345, the first 1536 bytes of keystream 1492 # generated by the cipher MUST be discarded 1493 cipher.encrypt(" " * 1536) 1494 return cipher 1495 elif name.endswith("-ctr"): 1496 # CTR modes, we need a counter 1497 counter = Counter.new(nbits=self._cipher_info[name]['block-size'] * 8, initial_value=util.inflate_long(iv, True)) 1498 return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv, counter) 1499 else: 1500 return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv)
1501
1502 - def _set_forward_agent_handler(self, handler):
1503 if handler is None: 1504 def default_handler(channel): 1505 self._queue_incoming_channel(channel)
1506 self._forward_agent_handler = default_handler 1507 else: 1508 self._forward_agent_handler = handler 1509
1510 - def _set_x11_handler(self, handler):
1511 # only called if a channel has turned on x11 forwarding 1512 if handler is None: 1513 # by default, use the same mechanism as accept() 1514 def default_handler(channel, (src_addr, src_port)): 1515 self._queue_incoming_channel(channel)
1516 self._x11_handler = default_handler 1517 else: 1518 self._x11_handler = handler 1519
1520 - def _queue_incoming_channel(self, channel):
1521 self.lock.acquire() 1522 try: 1523 self.server_accepts.append(channel) 1524 self.server_accept_cv.notify() 1525 finally: 1526 self.lock.release()
1527
1528 - def _ensure_authed(self, ptype, message):
1529 """ 1530 Checks message type against current auth state. 1531 1532 If server mode, and auth has not succeeded, and the message is of a 1533 post-auth type (channel open or global request) an appropriate error 1534 response Message is crafted and returned to caller for sending. 1535 1536 Otherwise (client mode, authed, or pre-auth message) returns None. 1537 """ 1538 if ( 1539 not self.server_mode 1540 or ptype <= HIGHEST_USERAUTH_MESSAGE_ID 1541 or self.is_authenticated() 1542 ): 1543 return None 1544 # WELP. We must be dealing with someone trying to do non-auth things 1545 # without being authed. Tell them off, based on message class. 1546 reply = Message() 1547 # Global requests have no details, just failure. 1548 if ptype == MSG_GLOBAL_REQUEST: 1549 reply.add_byte(chr(MSG_REQUEST_FAILURE)) 1550 # Channel opens let us reject w/ a specific type + message. 1551 elif ptype == MSG_CHANNEL_OPEN: 1552 kind = message.get_string() 1553 chanid = message.get_int() 1554 reply.add_byte(chr(MSG_CHANNEL_OPEN_FAILURE)) 1555 reply.add_int(chanid) 1556 reply.add_int(OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED) 1557 reply.add_string('') 1558 reply.add_string('en') 1559 # NOTE: Post-open channel messages do not need checking; the above will 1560 # reject attemps to open channels, meaning that even if a malicious 1561 # user tries to send a MSG_CHANNEL_REQUEST, it will simply fall under 1562 # the logic that handles unknown channel IDs (as the channel list will 1563 # be empty.) 1564 return reply
1565
1566 - def run(self):
1567 # (use the exposed "run" method, because if we specify a thread target 1568 # of a private method, threading.Thread will keep a reference to it 1569 # indefinitely, creating a GC cycle and not letting Transport ever be 1570 # GC'd. it's a bug in Thread.) 1571 1572 # Hold reference to 'sys' so we can test sys.modules to detect 1573 # interpreter shutdown. 1574 self.sys = sys 1575 1576 # Required to prevent RNG errors when running inside many subprocess 1577 # containers. 1578 Random.atfork() 1579 1580 # Hold reference to 'sys' so we can test sys.modules to detect 1581 # interpreter shutdown. 1582 self.sys = sys 1583 1584 # active=True occurs before the thread is launched, to avoid a race 1585 _active_threads.append(self) 1586 if self.server_mode: 1587 self._log(DEBUG, 'starting thread (server mode): %s' % hex(long(id(self)) & 0xffffffffL)) 1588 else: 1589 self._log(DEBUG, 'starting thread (client mode): %s' % hex(long(id(self)) & 0xffffffffL)) 1590 try: 1591 try: 1592 self.packetizer.write_all(self.local_version + '\r\n') 1593 self._check_banner() 1594 self._send_kex_init() 1595 self._expect_packet(MSG_KEXINIT) 1596 1597 while self.active: 1598 if self.packetizer.need_rekey() and not self.in_kex: 1599 self._send_kex_init() 1600 try: 1601 ptype, m = self.packetizer.read_message() 1602 except NeedRekeyException: 1603 continue 1604 if ptype == MSG_IGNORE: 1605 continue 1606 elif ptype == MSG_DISCONNECT: 1607 self._parse_disconnect(m) 1608 self.active = False 1609 self.packetizer.close() 1610 break 1611 elif ptype == MSG_DEBUG: 1612 self._parse_debug(m) 1613 continue 1614 if len(self._expected_packet) > 0: 1615 if ptype not in self._expected_packet: 1616 raise SSHException('Expecting packet from %r, got %d' % (self._expected_packet, ptype)) 1617 self._expected_packet = tuple() 1618 if (ptype >= 30) and (ptype <= 39): 1619 self.kex_engine.parse_next(ptype, m) 1620 continue 1621 1622 if ptype in self._handler_table: 1623 error_msg = self._ensure_authed(ptype, m) 1624 if error_msg: 1625 self._send_message(error_msg) 1626 else: 1627 self._handler_table[ptype](self, m) 1628 elif ptype in self._channel_handler_table: 1629 chanid = m.get_int() 1630 chan = self._channels.get(chanid) 1631 if chan is not None: 1632 self._channel_handler_table[ptype](chan, m) 1633 elif chanid in self.channels_seen: 1634 self._log(DEBUG, 'Ignoring message for dead channel %d' % chanid) 1635 else: 1636 self._log(ERROR, 'Channel request for unknown channel %d' % chanid) 1637 self.active = False 1638 self.packetizer.close() 1639 elif (self.auth_handler is not None) and (ptype in self.auth_handler._handler_table): 1640 self.auth_handler._handler_table[ptype](self.auth_handler, m) 1641 else: 1642 # Respond with "I don't implement this particular 1643 # message type" message (unless the message type was 1644 # itself literally MSG_UNIMPLEMENTED, in which case, we 1645 # just shut up to avoid causing a useless loop). 1646 name = MSG_NAMES[ptype] 1647 self._log( 1648 WARNING, 1649 "Oops, unhandled type {} ({!r})".format( 1650 ptype, name 1651 ), 1652 ) 1653 if ptype != MSG_UNIMPLEMENTED: 1654 msg = Message() 1655 msg.add_byte(chr(MSG_UNIMPLEMENTED)) 1656 msg.add_int(m.seqno) 1657 self._send_message(msg) 1658 except SSHException, e: 1659 self._log(ERROR, 'Exception: ' + str(e)) 1660 self._log(ERROR, util.tb_strings()) 1661 self.saved_exception = e 1662 except EOFError, e: 1663 self._log(DEBUG, 'EOF in transport thread') 1664 #self._log(DEBUG, util.tb_strings()) 1665 self.saved_exception = e 1666 except socket.error, e: 1667 if type(e.args) is tuple: 1668 emsg = '%s (%d)' % (e.args[1], e.args[0]) 1669 else: 1670 emsg = e.args 1671 self._log(ERROR, 'Socket exception: ' + emsg) 1672 self.saved_exception = e 1673 except Exception, e: 1674 self._log(ERROR, 'Unknown exception: ' + str(e)) 1675 self._log(ERROR, util.tb_strings()) 1676 self.saved_exception = e 1677 _active_threads.remove(self) 1678 for chan in self._channels.values(): 1679 chan._unlink() 1680 if self.active: 1681 self.active = False 1682 self.packetizer.close() 1683 if self.completion_event != None: 1684 self.completion_event.set() 1685 if self.auth_handler is not None: 1686 self.auth_handler.abort() 1687 for event in self.channel_events.values(): 1688 event.set() 1689 try: 1690 self.lock.acquire() 1691 self.server_accept_cv.notify() 1692 finally: 1693 self.lock.release() 1694 self.sock.close() 1695 except: 1696 # Don't raise spurious 'NoneType has no attribute X' errors when we 1697 # wake up during interpreter shutdown. Or rather -- raise 1698 # everything *if* sys.modules (used as a convenient sentinel) 1699 # appears to still exist. 1700 if self.sys.modules is not None: 1701 raise
1702 1703 1704 ### protocol stages 1705 1706
1707 - def _negotiate_keys(self, m):
1708 # throws SSHException on anything unusual 1709 self.clear_to_send_lock.acquire() 1710 try: 1711 self.clear_to_send.clear() 1712 finally: 1713 self.clear_to_send_lock.release() 1714 if self.local_kex_init == None: 1715 # remote side wants to renegotiate 1716 self._send_kex_init() 1717 self._parse_kex_init(m) 1718 self.kex_engine.start_kex()
1719
1720 - def _check_banner(self):
1721 # this is slow, but we only have to do it once 1722 for i in range(100): 1723 # give them 15 seconds for the first line, then just 2 seconds 1724 # each additional line. (some sites have very high latency.) 1725 if i == 0: 1726 timeout = self.banner_timeout 1727 else: 1728 timeout = 2 1729 try: 1730 buf = self.packetizer.readline(timeout) 1731 except ProxyCommandFailure: 1732 raise 1733 except Exception, x: 1734 raise SSHException('Error reading SSH protocol banner' + str(x)) 1735 if buf[:4] == 'SSH-': 1736 break 1737 self._log(DEBUG, 'Banner: ' + buf) 1738 if buf[:4] != 'SSH-': 1739 raise SSHException('Indecipherable protocol version "' + buf + '"') 1740 # save this server version string for later 1741 self.remote_version = buf 1742 # pull off any attached comment 1743 comment = '' 1744 i = string.find(buf, ' ') 1745 if i >= 0: 1746 comment = buf[i+1:] 1747 buf = buf[:i] 1748 # parse out version string and make sure it matches 1749 segs = buf.split('-', 2) 1750 if len(segs) < 3: 1751 raise SSHException('Invalid SSH banner') 1752 version = segs[1] 1753 client = segs[2] 1754 if version != '1.99' and version != '2.0': 1755 raise SSHException('Incompatible version (%s instead of 2.0)' % (version,)) 1756 self._log(INFO, 'Connected (version %s, client %s)' % (version, client))
1757
1758 - def _send_kex_init(self):
1759 """ 1760 announce to the other side that we'd like to negotiate keys, and what 1761 kind of key negotiation we support. 1762 """ 1763 self.clear_to_send_lock.acquire() 1764 try: 1765 self.clear_to_send.clear() 1766 finally: 1767 self.clear_to_send_lock.release() 1768 self.in_kex = True 1769 if self.server_mode: 1770 if (self._modulus_pack is None) and ('diffie-hellman-group-exchange-sha1' in self._preferred_kex): 1771 # can't do group-exchange if we don't have a pack of potential primes 1772 pkex = list(self.get_security_options().kex) 1773 pkex.remove('diffie-hellman-group-exchange-sha1') 1774 self.get_security_options().kex = pkex 1775 available_server_keys = filter(self.server_key_dict.keys().__contains__, 1776 self._preferred_keys) 1777 else: 1778 available_server_keys = self._preferred_keys 1779 1780 m = Message() 1781 m.add_byte(chr(MSG_KEXINIT)) 1782 m.add_bytes(rng.read(16)) 1783 m.add_list(self._preferred_kex) 1784 m.add_list(available_server_keys) 1785 m.add_list(self._preferred_ciphers) 1786 m.add_list(self._preferred_ciphers) 1787 m.add_list(self._preferred_macs) 1788 m.add_list(self._preferred_macs) 1789 m.add_list(self._preferred_compression) 1790 m.add_list(self._preferred_compression) 1791 m.add_string('') 1792 m.add_string('') 1793 m.add_boolean(False) 1794 m.add_int(0) 1795 # save a copy for later (needed to compute a hash) 1796 self.local_kex_init = str(m) 1797 self._send_message(m)
1798
1799 - def _parse_kex_init(self, m):
1800 cookie = m.get_bytes(16) 1801 kex_algo_list = m.get_list() 1802 server_key_algo_list = m.get_list() 1803 client_encrypt_algo_list = m.get_list() 1804 server_encrypt_algo_list = m.get_list() 1805 client_mac_algo_list = m.get_list() 1806 server_mac_algo_list = m.get_list() 1807 client_compress_algo_list = m.get_list() 1808 server_compress_algo_list = m.get_list() 1809 client_lang_list = m.get_list() 1810 server_lang_list = m.get_list() 1811 kex_follows = m.get_boolean() 1812 unused = m.get_int() 1813 1814 self._log(DEBUG, 'kex algos:' + str(kex_algo_list) + ' server key:' + str(server_key_algo_list) + \ 1815 ' client encrypt:' + str(client_encrypt_algo_list) + \ 1816 ' server encrypt:' + str(server_encrypt_algo_list) + \ 1817 ' client mac:' + str(client_mac_algo_list) + \ 1818 ' server mac:' + str(server_mac_algo_list) + \ 1819 ' client compress:' + str(client_compress_algo_list) + \ 1820 ' server compress:' + str(server_compress_algo_list) + \ 1821 ' client lang:' + str(client_lang_list) + \ 1822 ' server lang:' + str(server_lang_list) + \ 1823 ' kex follows?' + str(kex_follows)) 1824 1825 # as a server, we pick the first item in the client's list that we support. 1826 # as a client, we pick the first item in our list that the server supports. 1827 if self.server_mode: 1828 agreed_kex = filter(self._preferred_kex.__contains__, kex_algo_list) 1829 else: 1830 agreed_kex = filter(kex_algo_list.__contains__, self._preferred_kex) 1831 if len(agreed_kex) == 0: 1832 raise SSHException('Incompatible ssh peer (no acceptable kex algorithm)') 1833 self.kex_engine = self._kex_info[agreed_kex[0]](self) 1834 1835 if self.server_mode: 1836 available_server_keys = filter(self.server_key_dict.keys().__contains__, 1837 self._preferred_keys) 1838 agreed_keys = filter(available_server_keys.__contains__, server_key_algo_list) 1839 else: 1840 agreed_keys = filter(server_key_algo_list.__contains__, self._preferred_keys) 1841 if len(agreed_keys) == 0: 1842 raise SSHException('Incompatible ssh peer (no acceptable host key)') 1843 self.host_key_type = agreed_keys[0] 1844 if self.server_mode and (self.get_server_key() is None): 1845 raise SSHException('Incompatible ssh peer (can\'t match requested host key type)') 1846 1847 if self.server_mode: 1848 agreed_local_ciphers = filter(self._preferred_ciphers.__contains__, 1849 server_encrypt_algo_list) 1850 agreed_remote_ciphers = filter(self._preferred_ciphers.__contains__, 1851 client_encrypt_algo_list) 1852 else: 1853 agreed_local_ciphers = filter(client_encrypt_algo_list.__contains__, 1854 self._preferred_ciphers) 1855 agreed_remote_ciphers = filter(server_encrypt_algo_list.__contains__, 1856 self._preferred_ciphers) 1857 if (len(agreed_local_ciphers) == 0) or (len(agreed_remote_ciphers) == 0): 1858 raise SSHException('Incompatible ssh server (no acceptable ciphers)') 1859 self.local_cipher = agreed_local_ciphers[0] 1860 self.remote_cipher = agreed_remote_ciphers[0] 1861 self._log(DEBUG, 'Ciphers agreed: local=%s, remote=%s' % (self.local_cipher, self.remote_cipher)) 1862 1863 if self.server_mode: 1864 agreed_remote_macs = filter(self._preferred_macs.__contains__, client_mac_algo_list) 1865 agreed_local_macs = filter(self._preferred_macs.__contains__, server_mac_algo_list) 1866 else: 1867 agreed_local_macs = filter(client_mac_algo_list.__contains__, self._preferred_macs) 1868 agreed_remote_macs = filter(server_mac_algo_list.__contains__, self._preferred_macs) 1869 if (len(agreed_local_macs) == 0) or (len(agreed_remote_macs) == 0): 1870 raise SSHException('Incompatible ssh server (no acceptable macs)') 1871 self.local_mac = agreed_local_macs[0] 1872 self.remote_mac = agreed_remote_macs[0] 1873 1874 if self.server_mode: 1875 agreed_remote_compression = filter(self._preferred_compression.__contains__, client_compress_algo_list) 1876 agreed_local_compression = filter(self._preferred_compression.__contains__, server_compress_algo_list) 1877 else: 1878 agreed_local_compression = filter(client_compress_algo_list.__contains__, self._preferred_compression) 1879 agreed_remote_compression = filter(server_compress_algo_list.__contains__, self._preferred_compression) 1880 if (len(agreed_local_compression) == 0) or (len(agreed_remote_compression) == 0): 1881 raise SSHException('Incompatible ssh server (no acceptable compression) %r %r %r' % (agreed_local_compression, agreed_remote_compression, self._preferred_compression)) 1882 self.local_compression = agreed_local_compression[0] 1883 self.remote_compression = agreed_remote_compression[0] 1884 1885 self._log(DEBUG, 'using kex %s; server key type %s; cipher: local %s, remote %s; mac: local %s, remote %s; compression: local %s, remote %s' % 1886 (agreed_kex[0], self.host_key_type, self.local_cipher, self.remote_cipher, self.local_mac, 1887 self.remote_mac, self.local_compression, self.remote_compression)) 1888 1889 # save for computing hash later... 1890 # now wait! openssh has a bug (and others might too) where there are 1891 # actually some extra bytes (one NUL byte in openssh's case) added to 1892 # the end of the packet but not parsed. turns out we need to throw 1893 # away those bytes because they aren't part of the hash. 1894 self.remote_kex_init = chr(MSG_KEXINIT) + m.get_so_far()
1895
1896 - def _activate_inbound(self):
1897 "switch on newly negotiated encryption parameters for inbound traffic" 1898 block_size = self._cipher_info[self.remote_cipher]['block-size'] 1899 if self.server_mode: 1900 IV_in = self._compute_key('A', block_size) 1901 key_in = self._compute_key('C', self._cipher_info[self.remote_cipher]['key-size']) 1902 else: 1903 IV_in = self._compute_key('B', block_size) 1904 key_in = self._compute_key('D', self._cipher_info[self.remote_cipher]['key-size']) 1905 engine = self._get_cipher(self.remote_cipher, key_in, IV_in) 1906 mac_size = self._mac_info[self.remote_mac]['size'] 1907 mac_engine = self._mac_info[self.remote_mac]['class'] 1908 # initial mac keys are done in the hash's natural size (not the potentially truncated 1909 # transmission size) 1910 if self.server_mode: 1911 mac_key = self._compute_key('E', mac_engine.digest_size) 1912 else: 1913 mac_key = self._compute_key('F', mac_engine.digest_size) 1914 self.packetizer.set_inbound_cipher(engine, block_size, mac_engine, mac_size, mac_key) 1915 compress_in = self._compression_info[self.remote_compression][1] 1916 if (compress_in is not None) and ((self.remote_compression != 'zlib@openssh.com') or self.authenticated): 1917 self._log(DEBUG, 'Switching on inbound compression ...') 1918 self.packetizer.set_inbound_compressor(compress_in())
1919
1920 - def _activate_outbound(self):
1921 "switch on newly negotiated encryption parameters for outbound traffic" 1922 m = Message() 1923 m.add_byte(chr(MSG_NEWKEYS)) 1924 self._send_message(m) 1925 block_size = self._cipher_info[self.local_cipher]['block-size'] 1926 if self.server_mode: 1927 IV_out = self._compute_key('B', block_size) 1928 key_out = self._compute_key('D', self._cipher_info[self.local_cipher]['key-size']) 1929 else: 1930 IV_out = self._compute_key('A', block_size) 1931 key_out = self._compute_key('C', self._cipher_info[self.local_cipher]['key-size']) 1932 engine = self._get_cipher(self.local_cipher, key_out, IV_out) 1933 mac_size = self._mac_info[self.local_mac]['size'] 1934 mac_engine = self._mac_info[self.local_mac]['class'] 1935 # initial mac keys are done in the hash's natural size (not the potentially truncated 1936 # transmission size) 1937 if self.server_mode: 1938 mac_key = self._compute_key('F', mac_engine.digest_size) 1939 else: 1940 mac_key = self._compute_key('E', mac_engine.digest_size) 1941 sdctr = self.local_cipher.endswith('-ctr') 1942 self.packetizer.set_outbound_cipher(engine, block_size, mac_engine, mac_size, mac_key, sdctr) 1943 compress_out = self._compression_info[self.local_compression][0] 1944 if (compress_out is not None) and ((self.local_compression != 'zlib@openssh.com') or self.authenticated): 1945 self._log(DEBUG, 'Switching on outbound compression ...') 1946 self.packetizer.set_outbound_compressor(compress_out()) 1947 if not self.packetizer.need_rekey(): 1948 self.in_kex = False 1949 # we always expect to receive NEWKEYS now 1950 self._expect_packet(MSG_NEWKEYS)
1951
1952 - def _auth_trigger(self):
1953 self.authenticated = True 1954 # delayed initiation of compression 1955 if self.local_compression == 'zlib@openssh.com': 1956 compress_out = self._compression_info[self.local_compression][0] 1957 self._log(DEBUG, 'Switching on outbound compression ...') 1958 self.packetizer.set_outbound_compressor(compress_out()) 1959 if self.remote_compression == 'zlib@openssh.com': 1960 compress_in = self._compression_info[self.remote_compression][1] 1961 self._log(DEBUG, 'Switching on inbound compression ...') 1962 self.packetizer.set_inbound_compressor(compress_in())
1963
1964 - def _parse_newkeys(self, m):
1965 self._log(DEBUG, 'Switch to new keys ...') 1966 self._activate_inbound() 1967 # can also free a bunch of stuff here 1968 self.local_kex_init = self.remote_kex_init = None 1969 self.K = None 1970 self.kex_engine = None 1971 if self.server_mode and (self.auth_handler is None): 1972 # create auth handler for server mode 1973 self.auth_handler = AuthHandler(self) 1974 if not self.initial_kex_done: 1975 # this was the first key exchange 1976 self.initial_kex_done = True 1977 # send an event? 1978 if self.completion_event != None: 1979 self.completion_event.set() 1980 # it's now okay to send data again (if this was a re-key) 1981 if not self.packetizer.need_rekey(): 1982 self.in_kex = False 1983 self.clear_to_send_lock.acquire() 1984 try: 1985 self.clear_to_send.set() 1986 finally: 1987 self.clear_to_send_lock.release() 1988 return
1989
1990 - def _parse_disconnect(self, m):
1991 code = m.get_int() 1992 desc = m.get_string() 1993 self._log(INFO, 'Disconnect (code %d): %s' % (code, desc))
1994
1995 - def _parse_global_request(self, m):
1996 kind = m.get_string() 1997 self._log(DEBUG, 'Received global request "%s"' % kind) 1998 want_reply = m.get_boolean() 1999 if not self.server_mode: 2000 self._log(DEBUG, 'Rejecting "%s" global request from server.' % kind) 2001 ok = False 2002 elif kind == 'tcpip-forward': 2003 address = m.get_string() 2004 port = m.get_int() 2005 ok = self.server_object.check_port_forward_request(address, port) 2006 if ok != False: 2007 ok = (ok,) 2008 elif kind == 'cancel-tcpip-forward': 2009 address = m.get_string() 2010 port = m.get_int() 2011 self.server_object.cancel_port_forward_request(address, port) 2012 ok = True 2013 else: 2014 ok = self.server_object.check_global_request(kind, m) 2015 extra = () 2016 if type(ok) is tuple: 2017 extra = ok 2018 ok = True 2019 if want_reply: 2020 msg = Message() 2021 if ok: 2022 msg.add_byte(chr(MSG_REQUEST_SUCCESS)) 2023 msg.add(*extra) 2024 else: 2025 msg.add_byte(chr(MSG_REQUEST_FAILURE)) 2026 self._send_message(msg)
2027
2028 - def _parse_request_success(self, m):
2029 self._log(DEBUG, 'Global request successful.') 2030 self.global_response = m 2031 if self.completion_event is not None: 2032 self.completion_event.set()
2033
2034 - def _parse_request_failure(self, m):
2035 self._log(DEBUG, 'Global request denied.') 2036 self.global_response = None 2037 if self.completion_event is not None: 2038 self.completion_event.set()
2039
2040 - def _parse_channel_open_success(self, m):
2041 chanid = m.get_int() 2042 server_chanid = m.get_int() 2043 server_window_size = m.get_int() 2044 server_max_packet_size = m.get_int() 2045 chan = self._channels.get(chanid) 2046 if chan is None: 2047 self._log(WARNING, 'Success for unrequested channel! [??]') 2048 return 2049 self.lock.acquire() 2050 try: 2051 chan._set_remote_channel(server_chanid, server_window_size, server_max_packet_size) 2052 self._log(INFO, 'Secsh channel %d opened.' % chanid) 2053 if chanid in self.channel_events: 2054 self.channel_events[chanid].set() 2055 del self.channel_events[chanid] 2056 finally: 2057 self.lock.release() 2058 return
2059
2060 - def _parse_channel_open_failure(self, m):
2061 chanid = m.get_int() 2062 reason = m.get_int() 2063 reason_str = m.get_string() 2064 lang = m.get_string() 2065 reason_text = CONNECTION_FAILED_CODE.get(reason, '(unknown code)') 2066 self._log(INFO, 'Secsh channel %d open FAILED: %s: %s' % (chanid, reason_str, reason_text)) 2067 self.lock.acquire() 2068 try: 2069 self.saved_exception = ChannelException(reason, reason_text) 2070 if chanid in self.channel_events: 2071 self._channels.delete(chanid) 2072 if chanid in self.channel_events: 2073 self.channel_events[chanid].set() 2074 del self.channel_events[chanid] 2075 finally: 2076 self.lock.release() 2077 return
2078
2079 - def _parse_channel_open(self, m):
2080 kind = m.get_string() 2081 chanid = m.get_int() 2082 initial_window_size = m.get_int() 2083 max_packet_size = m.get_int() 2084 reject = False 2085 if (kind == 'auth-agent@openssh.com') and (self._forward_agent_handler is not None): 2086 self._log(DEBUG, 'Incoming forward agent connection') 2087 self.lock.acquire() 2088 try: 2089 my_chanid = self._next_channel() 2090 finally: 2091 self.lock.release() 2092 elif (kind == 'x11') and (self._x11_handler is not None): 2093 origin_addr = m.get_string() 2094 origin_port = m.get_int() 2095 self._log(DEBUG, 'Incoming x11 connection from %s:%d' % (origin_addr, origin_port)) 2096 self.lock.acquire() 2097 try: 2098 my_chanid = self._next_channel() 2099 finally: 2100 self.lock.release() 2101 elif (kind == 'forwarded-tcpip') and (self._tcp_handler is not None): 2102 server_addr = m.get_string() 2103 server_port = m.get_int() 2104 origin_addr = m.get_string() 2105 origin_port = m.get_int() 2106 self._log(DEBUG, 'Incoming tcp forwarded connection from %s:%d' % (origin_addr, origin_port)) 2107 self.lock.acquire() 2108 try: 2109 my_chanid = self._next_channel() 2110 finally: 2111 self.lock.release() 2112 elif not self.server_mode: 2113 self._log(DEBUG, 'Rejecting "%s" channel request from server.' % kind) 2114 reject = True 2115 reason = OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED 2116 else: 2117 self.lock.acquire() 2118 try: 2119 my_chanid = self._next_channel() 2120 finally: 2121 self.lock.release() 2122 if kind == 'direct-tcpip': 2123 # handle direct-tcpip requests comming from the client 2124 dest_addr = m.get_string() 2125 dest_port = m.get_int() 2126 origin_addr = m.get_string() 2127 origin_port = m.get_int() 2128 reason = self.server_object.check_channel_direct_tcpip_request( 2129 my_chanid, (origin_addr, origin_port), 2130 (dest_addr, dest_port)) 2131 else: 2132 reason = self.server_object.check_channel_request(kind, my_chanid) 2133 if reason != OPEN_SUCCEEDED: 2134 self._log(DEBUG, 'Rejecting "%s" channel request from client.' % kind) 2135 reject = True 2136 if reject: 2137 msg = Message() 2138 msg.add_byte(chr(MSG_CHANNEL_OPEN_FAILURE)) 2139 msg.add_int(chanid) 2140 msg.add_int(reason) 2141 msg.add_string('') 2142 msg.add_string('en') 2143 self._send_message(msg) 2144 return 2145 2146 chan = Channel(my_chanid) 2147 self.lock.acquire() 2148 try: 2149 self._channels.put(my_chanid, chan) 2150 self.channels_seen[my_chanid] = True 2151 chan._set_transport(self) 2152 chan._set_window(self.window_size, self.max_packet_size) 2153 chan._set_remote_channel(chanid, initial_window_size, max_packet_size) 2154 finally: 2155 self.lock.release() 2156 m = Message() 2157 m.add_byte(chr(MSG_CHANNEL_OPEN_SUCCESS)) 2158 m.add_int(chanid) 2159 m.add_int(my_chanid) 2160 m.add_int(self.window_size) 2161 m.add_int(self.max_packet_size) 2162 self._send_message(m) 2163 self._log(INFO, 'Secsh channel %d (%s) opened.', my_chanid, kind) 2164 if kind == 'auth-agent@openssh.com': 2165 self._forward_agent_handler(chan) 2166 elif kind == 'x11': 2167 self._x11_handler(chan, (origin_addr, origin_port)) 2168 elif kind == 'forwarded-tcpip': 2169 chan.origin_addr = (origin_addr, origin_port) 2170 self._tcp_handler(chan, (origin_addr, origin_port), (server_addr, server_port)) 2171 else: 2172 self._queue_incoming_channel(chan)
2173
2174 - def _parse_debug(self, m):
2175 always_display = m.get_boolean() 2176 msg = m.get_string() 2177 lang = m.get_string() 2178 self._log(DEBUG, 'Debug msg: ' + util.safe_string(msg))
2179
2180 - def _get_subsystem_handler(self, name):
2181 try: 2182 self.lock.acquire() 2183 if name not in self.subsystem_table: 2184 return (None, [], {}) 2185 return self.subsystem_table[name] 2186 finally: 2187 self.lock.release()
2188 2189 _handler_table = { 2190 MSG_NEWKEYS: _parse_newkeys, 2191 MSG_GLOBAL_REQUEST: _parse_global_request, 2192 MSG_REQUEST_SUCCESS: _parse_request_success, 2193 MSG_REQUEST_FAILURE: _parse_request_failure, 2194 MSG_CHANNEL_OPEN_SUCCESS: _parse_channel_open_success, 2195 MSG_CHANNEL_OPEN_FAILURE: _parse_channel_open_failure, 2196 MSG_CHANNEL_OPEN: _parse_channel_open, 2197 MSG_KEXINIT: _negotiate_keys, 2198 } 2199 2200 _channel_handler_table = { 2201 MSG_CHANNEL_SUCCESS: Channel._request_success, 2202 MSG_CHANNEL_FAILURE: Channel._request_failed, 2203 MSG_CHANNEL_DATA: Channel._feed, 2204 MSG_CHANNEL_EXTENDED_DATA: Channel._feed_extended, 2205 MSG_CHANNEL_WINDOW_ADJUST: Channel._window_adjust, 2206 MSG_CHANNEL_REQUEST: Channel._handle_request, 2207 MSG_CHANNEL_EOF: Channel._handle_eof, 2208 MSG_CHANNEL_CLOSE: Channel._handle_close, 2209 } 2210