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

Source Code for Module paramiko.transport

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