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