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

Source Code for Module paramiko.transport

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