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