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

Source Code for Module paramiko.transport

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