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