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