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