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

Source Code for Module ssh.transport

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