Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/transport.py: 15%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1381 statements  

1# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> 

2# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> 

3# 

4# This file is part of paramiko. 

5# 

6# Paramiko is free software; you can redistribute it and/or modify it under the 

7# terms of the GNU Lesser General Public License as published by the Free 

8# Software Foundation; either version 2.1 of the License, or (at your option) 

9# any later version. 

10# 

11# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 

12# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 

13# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 

14# details. 

15# 

16# You should have received a copy of the GNU Lesser General Public License 

17# along with Paramiko; if not, write to the Free Software Foundation, Inc., 

18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 

19 

20""" 

21Core protocol implementation 

22""" 

23 

24import os 

25import socket 

26import sys 

27import threading 

28import time 

29import weakref 

30from hashlib import md5, sha1, sha256, sha512 

31 

32from cryptography.hazmat.backends import default_backend 

33from cryptography.hazmat.primitives.ciphers import ( 

34 Cipher, 

35 aead, 

36 algorithms, 

37 modes, 

38) 

39 

40import paramiko 

41from paramiko import util 

42from paramiko.auth_handler import AuthHandler, AuthOnlyHandler 

43from paramiko.channel import Channel 

44from paramiko.common import ( 

45 CONNECTION_FAILED_CODE, 

46 DEBUG, 

47 DEFAULT_MAX_PACKET_SIZE, 

48 DEFAULT_WINDOW_SIZE, 

49 ERROR, 

50 HIGHEST_USERAUTH_MESSAGE_ID, 

51 INFO, 

52 MAX_WINDOW_SIZE, 

53 MIN_PACKET_SIZE, 

54 MIN_WINDOW_SIZE, 

55 MSG_CHANNEL_CLOSE, 

56 MSG_CHANNEL_DATA, 

57 MSG_CHANNEL_EOF, 

58 MSG_CHANNEL_EXTENDED_DATA, 

59 MSG_CHANNEL_FAILURE, 

60 MSG_CHANNEL_OPEN, 

61 MSG_CHANNEL_OPEN_FAILURE, 

62 MSG_CHANNEL_OPEN_SUCCESS, 

63 MSG_CHANNEL_REQUEST, 

64 MSG_CHANNEL_SUCCESS, 

65 MSG_CHANNEL_WINDOW_ADJUST, 

66 MSG_DEBUG, 

67 MSG_DISCONNECT, 

68 MSG_EXT_INFO, 

69 MSG_GLOBAL_REQUEST, 

70 MSG_IGNORE, 

71 MSG_KEXINIT, 

72 MSG_NAMES, 

73 MSG_NEWKEYS, 

74 MSG_REQUEST_FAILURE, 

75 MSG_REQUEST_SUCCESS, 

76 MSG_SERVICE_ACCEPT, 

77 MSG_UNIMPLEMENTED, 

78 OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, 

79 OPEN_SUCCEEDED, 

80 WARNING, 

81 byte_ord, 

82 cMSG_CHANNEL_OPEN, 

83 cMSG_CHANNEL_OPEN_FAILURE, 

84 cMSG_CHANNEL_OPEN_SUCCESS, 

85 cMSG_EXT_INFO, 

86 cMSG_GLOBAL_REQUEST, 

87 cMSG_IGNORE, 

88 cMSG_KEXINIT, 

89 cMSG_NEWKEYS, 

90 cMSG_REQUEST_FAILURE, 

91 cMSG_REQUEST_SUCCESS, 

92 cMSG_SERVICE_REQUEST, 

93 cMSG_UNIMPLEMENTED, 

94 xffffffff, 

95) 

96from paramiko.compress import ZlibCompressor, ZlibDecompressor 

97from paramiko.ecdsakey import ECDSAKey 

98from paramiko.ed25519key import Ed25519Key 

99from paramiko.kex_curve25519 import KexCurve25519 

100from paramiko.kex_ecdh_nist import KexNistp256, KexNistp384, KexNistp521 

101from paramiko.kex_gex import KexGexSHA256 

102from paramiko.kex_group14 import KexGroup14SHA256 

103from paramiko.kex_group16 import KexGroup16SHA512 

104from paramiko.message import Message 

105from paramiko.packet import NeedRekeyException, Packetizer 

106from paramiko.pkey import PKey 

107from paramiko.primes import ModulusPack 

108from paramiko.rsakey import RSAKey 

109from paramiko.server import ServerInterface 

110from paramiko.sftp_client import SFTPClient 

111from paramiko.ssh_exception import ( 

112 BadAuthenticationType, 

113 ChannelException, 

114 IncompatiblePeer, 

115 MessageOrderError, 

116 ProxyCommandFailure, 

117 SSHException, 

118) 

119from paramiko.util import ( 

120 ClosingContextManager, 

121 b, 

122 clamp_value, 

123) 

124 

125# TripleDES is moving from `cryptography.hazmat.primitives.ciphers.algorithms` 

126# in cryptography>=43.0.0 to `cryptography.hazmat.decrepit.ciphers.algorithms` 

127# It will be removed from `cryptography.hazmat.primitives.ciphers.algorithms` 

128# in cryptography==48.0.0. 

129# 

130# Source References: 

131# - https://github.com/pyca/cryptography/commit/722a6393e61b3ac 

132# - https://github.com/pyca/cryptography/pull/11407/files 

133try: 

134 from cryptography.hazmat.decrepit.ciphers.algorithms import TripleDES 

135except ImportError: 

136 from cryptography.hazmat.primitives.ciphers.algorithms import TripleDES 

137 

138 

139# for thread cleanup 

140_active_threads = [] 

141 

142 

143def _join_lingering_threads(): 

144 for thr in _active_threads: 

145 thr.stop_thread() 

146 

147 

148import atexit 

149 

150atexit.register(_join_lingering_threads) 

151 

152 

153class Transport(threading.Thread, ClosingContextManager): 

154 """ 

155 An SSH Transport attaches to a stream (usually a socket), negotiates an 

156 encrypted session, authenticates, and then creates stream tunnels, called 

157 `channels <.Channel>`, across the session. Multiple channels can be 

158 multiplexed across a single session (and often are, in the case of port 

159 forwardings). 

160 

161 Instances of this class may be used as context managers. 

162 """ 

163 

164 _ENCRYPT = object() 

165 _DECRYPT = object() 

166 

167 _PROTO_ID = "2.0" 

168 _CLIENT_ID = "paramiko_{}".format(paramiko.__version__) 

169 

170 # These tuples of algorithm identifiers are in preference order; do not 

171 # reorder without reason! 

172 # NOTE: if you need to modify these, we suggest leveraging the 

173 # `disabled_algorithms` constructor argument (also available in SSHClient) 

174 # instead of monkeypatching or subclassing. 

175 _preferred_ciphers = ( 

176 "aes128-ctr", 

177 "aes192-ctr", 

178 "aes256-ctr", 

179 "aes128-cbc", 

180 "aes192-cbc", 

181 "aes256-cbc", 

182 "3des-cbc", 

183 "aes128-gcm@openssh.com", 

184 "aes256-gcm@openssh.com", 

185 ) 

186 _preferred_macs = ( 

187 "hmac-sha2-256", 

188 "hmac-sha2-512", 

189 "hmac-sha2-256-etm@openssh.com", 

190 "hmac-sha2-512-etm@openssh.com", 

191 "hmac-sha1", 

192 "hmac-md5", 

193 "hmac-sha1-96", 

194 "hmac-md5-96", 

195 ) 

196 # ~= HostKeyAlgorithms in OpenSSH land 

197 _preferred_keys = ( 

198 "ssh-ed25519", 

199 "ecdsa-sha2-nistp256", 

200 "ecdsa-sha2-nistp384", 

201 "ecdsa-sha2-nistp521", 

202 "rsa-sha2-512", 

203 "rsa-sha2-256", 

204 ) 

205 # ~= PubkeyAcceptedAlgorithms 

206 _preferred_pubkeys = ( 

207 "ssh-ed25519", 

208 "ecdsa-sha2-nistp256", 

209 "ecdsa-sha2-nistp384", 

210 "ecdsa-sha2-nistp521", 

211 "rsa-sha2-512", 

212 "rsa-sha2-256", 

213 ) 

214 _preferred_kex = ( 

215 "ecdh-sha2-nistp256", 

216 "ecdh-sha2-nistp384", 

217 "ecdh-sha2-nistp521", 

218 "diffie-hellman-group16-sha512", 

219 "diffie-hellman-group-exchange-sha256", 

220 "diffie-hellman-group14-sha256", 

221 ) 

222 if KexCurve25519.is_available(): 

223 _preferred_kex = ("curve25519-sha256@libssh.org",) + _preferred_kex 

224 _preferred_compression = ("none",) 

225 

226 _cipher_info = { 

227 "aes128-ctr": { 

228 "class": algorithms.AES, 

229 "mode": modes.CTR, 

230 "block-size": 16, 

231 "key-size": 16, 

232 }, 

233 "aes192-ctr": { 

234 "class": algorithms.AES, 

235 "mode": modes.CTR, 

236 "block-size": 16, 

237 "key-size": 24, 

238 }, 

239 "aes256-ctr": { 

240 "class": algorithms.AES, 

241 "mode": modes.CTR, 

242 "block-size": 16, 

243 "key-size": 32, 

244 }, 

245 "aes128-cbc": { 

246 "class": algorithms.AES, 

247 "mode": modes.CBC, 

248 "block-size": 16, 

249 "key-size": 16, 

250 }, 

251 "aes192-cbc": { 

252 "class": algorithms.AES, 

253 "mode": modes.CBC, 

254 "block-size": 16, 

255 "key-size": 24, 

256 }, 

257 "aes256-cbc": { 

258 "class": algorithms.AES, 

259 "mode": modes.CBC, 

260 "block-size": 16, 

261 "key-size": 32, 

262 }, 

263 "3des-cbc": { 

264 "class": TripleDES, 

265 "mode": modes.CBC, 

266 "block-size": 8, 

267 "key-size": 24, 

268 }, 

269 "aes128-gcm@openssh.com": { 

270 "class": aead.AESGCM, 

271 "block-size": 16, 

272 "iv-size": 12, 

273 "key-size": 16, 

274 "is_aead": True, 

275 }, 

276 "aes256-gcm@openssh.com": { 

277 "class": aead.AESGCM, 

278 "block-size": 16, 

279 "iv-size": 12, 

280 "key-size": 32, 

281 "is_aead": True, 

282 }, 

283 } 

284 

285 _mac_info = { 

286 "hmac-sha1": {"class": sha1, "size": 20}, 

287 "hmac-sha1-96": {"class": sha1, "size": 12}, 

288 "hmac-sha2-256": {"class": sha256, "size": 32}, 

289 "hmac-sha2-256-etm@openssh.com": {"class": sha256, "size": 32}, 

290 "hmac-sha2-512": {"class": sha512, "size": 64}, 

291 "hmac-sha2-512-etm@openssh.com": {"class": sha512, "size": 64}, 

292 "hmac-md5": {"class": md5, "size": 16}, 

293 "hmac-md5-96": {"class": md5, "size": 12}, 

294 } 

295 

296 _key_info = { 

297 # TODO: do some downstream uses of this need to be able to 'see' 

298 # ssh-rsa in not-using-SHA1 contexts? 

299 # TODO: NO!!! good. 

300 # TODO: it's used in: 

301 # - Transport._verify_key - verification - do not want ssh-rsa 

302 # - SecurityOptions - only really uses this as a filter for what's 

303 # allowed to be overwritten into its .key_types (which == 

304 # transport._preferred_keys), and since the latter doesn't want ssh-rsa 

305 # in it, this use case doesn't require that string in here either. 

306 # - AuthHandler._generate_key_from_request - server-side auth 

307 # support - is looking at the 'algorithm' field in the request when it 

308 # references this structure, so yup, do not want ssh-rsa 

309 "rsa-sha2-256": RSAKey, 

310 "rsa-sha2-256-cert-v01@openssh.com": RSAKey, 

311 "rsa-sha2-512": RSAKey, 

312 "rsa-sha2-512-cert-v01@openssh.com": RSAKey, 

313 "ecdsa-sha2-nistp256": ECDSAKey, 

314 "ecdsa-sha2-nistp256-cert-v01@openssh.com": ECDSAKey, 

315 "ecdsa-sha2-nistp384": ECDSAKey, 

316 "ecdsa-sha2-nistp384-cert-v01@openssh.com": ECDSAKey, 

317 "ecdsa-sha2-nistp521": ECDSAKey, 

318 "ecdsa-sha2-nistp521-cert-v01@openssh.com": ECDSAKey, 

319 "ssh-ed25519": Ed25519Key, 

320 "ssh-ed25519-cert-v01@openssh.com": Ed25519Key, 

321 } 

322 

323 _kex_info = { 

324 "diffie-hellman-group-exchange-sha256": KexGexSHA256, 

325 "diffie-hellman-group14-sha256": KexGroup14SHA256, 

326 "diffie-hellman-group16-sha512": KexGroup16SHA512, 

327 "ecdh-sha2-nistp256": KexNistp256, 

328 "ecdh-sha2-nistp384": KexNistp384, 

329 "ecdh-sha2-nistp521": KexNistp521, 

330 } 

331 if KexCurve25519.is_available(): 

332 _kex_info["curve25519-sha256@libssh.org"] = KexCurve25519 

333 

334 _compression_info = { 

335 # zlib@openssh.com is just zlib, but only turned on after a successful 

336 # authentication. openssh servers may only offer this type because 

337 # they've had troubles with security holes in zlib in the past. 

338 "zlib@openssh.com": (ZlibCompressor, ZlibDecompressor), 

339 "zlib": (ZlibCompressor, ZlibDecompressor), 

340 "none": (None, None), 

341 } 

342 

343 _modulus_pack = None 

344 _active_check_timeout = 0.1 

345 

346 def __init__( 

347 self, 

348 sock, 

349 default_window_size=DEFAULT_WINDOW_SIZE, 

350 default_max_packet_size=DEFAULT_MAX_PACKET_SIZE, 

351 disabled_algorithms=None, 

352 server_sig_algs=True, 

353 strict_kex=True, 

354 packetizer_class=None, 

355 ): 

356 """ 

357 Create a new SSH session over an existing socket, or socket-like 

358 object. This only creates the `.Transport` object; it doesn't begin 

359 the SSH session yet. Use `connect` or `start_client` to begin a client 

360 session, or `start_server` to begin a server session. 

361 

362 If the object is not actually a socket, it must have the following 

363 methods: 

364 

365 - ``send(bytes)``: Writes from 1 to ``len(bytes)`` bytes, and returns 

366 an int representing the number of bytes written. Returns 

367 0 or raises ``EOFError`` if the stream has been closed. 

368 - ``recv(int)``: Reads from 1 to ``int`` bytes and returns them as a 

369 string. Returns 0 or raises ``EOFError`` if the stream has been 

370 closed. 

371 - ``close()``: Closes the socket. 

372 - ``settimeout(n)``: Sets a (float) timeout on I/O operations. 

373 

374 For ease of use, you may also pass in an address (as a tuple) or a host 

375 string as the ``sock`` argument. (A host string is a hostname with an 

376 optional port (separated by ``":"``) which will be converted into a 

377 tuple of ``(hostname, port)``.) A socket will be connected to this 

378 address and used for communication. Exceptions from the ``socket`` 

379 call may be thrown in this case. 

380 

381 .. note:: 

382 Modifying the the window and packet sizes might have adverse 

383 effects on your channels created from this transport. The default 

384 values are the same as in the OpenSSH code base and have been 

385 battle tested. 

386 

387 :param socket sock: 

388 a socket or socket-like object to create the session over. 

389 :param int default_window_size: 

390 sets the default window size on the transport. (defaults to 

391 2097152) 

392 :param int default_max_packet_size: 

393 sets the default max packet size on the transport. (defaults to 

394 32768) 

395 :param dict disabled_algorithms: 

396 If given, must be a dictionary mapping algorithm type to an 

397 iterable of algorithm identifiers, which will be disabled for the 

398 lifetime of the transport. 

399 

400 Keys should match the last word in the class' builtin algorithm 

401 tuple attributes, such as ``"ciphers"`` to disable names within 

402 ``_preferred_ciphers``; or ``"kex"`` to disable something defined 

403 inside ``_preferred_kex``. Values should exactly match members of 

404 the matching attribute. 

405 

406 For example, if you need to disable 

407 ``diffie-hellman-group16-sha512`` key exchange (perhaps because 

408 your code talks to a server which implements it differently from 

409 Paramiko), specify ``disabled_algorithms={"kex": 

410 ["diffie-hellman-group16-sha512"]}``. 

411 :param bool server_sig_algs: 

412 Whether to send an extra message to compatible clients, in server 

413 mode, with a list of supported pubkey algorithms. Default: 

414 ``True``. 

415 :param bool strict_kex: 

416 Whether to advertise (and implement, if client also advertises 

417 support for) a "strict kex" mode for safer handshaking. Default: 

418 ``True``. 

419 :param packetizer_class: 

420 Which class to use for instantiating the internal packet handler. 

421 Default: ``None`` (i.e.: use `Packetizer` as normal). 

422 

423 .. versionchanged:: 1.15 

424 Added the ``default_window_size`` and ``default_max_packet_size`` 

425 arguments. 

426 .. versionchanged:: 2.6 

427 Added the ``disabled_algorithms`` kwarg. 

428 .. versionchanged:: 2.9 

429 Added the ``server_sig_algs`` kwarg. 

430 .. versionchanged:: 3.4 

431 Added the ``strict_kex`` kwarg. 

432 .. versionchanged:: 3.4 

433 Added the ``packetizer_class`` kwarg. 

434 """ 

435 self.active = False 

436 self.hostname = None 

437 self.server_extensions = {} 

438 self.advertise_strict_kex = strict_kex 

439 self.agreed_on_strict_kex = False 

440 

441 # TODO: these two overrides on sock's type should go away sometime, too 

442 # many ways to do it! 

443 if isinstance(sock, str): 

444 # convert "host:port" into (host, port) 

445 hl = sock.split(":", 1) 

446 self.hostname = hl[0] 

447 if len(hl) == 1: 

448 sock = (hl[0], 22) 

449 else: 

450 sock = (hl[0], int(hl[1])) 

451 if type(sock) is tuple: 

452 # connect to the given (host, port) 

453 hostname, port = sock 

454 self.hostname = hostname 

455 reason = "No suitable address family" 

456 addrinfos = socket.getaddrinfo( 

457 hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM 

458 ) 

459 for family, socktype, proto, canonname, sockaddr in addrinfos: 

460 if socktype == socket.SOCK_STREAM: 

461 af = family 

462 # addr = sockaddr 

463 sock = socket.socket(af, socket.SOCK_STREAM) 

464 try: 

465 sock.connect((hostname, port)) 

466 except socket.error as e: 

467 reason = str(e) 

468 else: 

469 break 

470 else: 

471 raise SSHException( 

472 "Unable to connect to {}: {}".format(hostname, reason) 

473 ) 

474 # okay, normal socket-ish flow here... 

475 threading.Thread.__init__(self) 

476 self.daemon = True 

477 self.sock = sock 

478 # we set the timeout so we can check self.active periodically to 

479 # see if we should bail. socket.timeout exception is never propagated. 

480 self.sock.settimeout(self._active_check_timeout) 

481 

482 # negotiated crypto parameters 

483 self.packetizer = (packetizer_class or Packetizer)(sock) 

484 self.local_version = "SSH-" + self._PROTO_ID + "-" + self._CLIENT_ID 

485 self.remote_version = "" 

486 self.local_cipher = self.remote_cipher = "" 

487 self.local_kex_init = self.remote_kex_init = None 

488 self.local_mac = self.remote_mac = None 

489 self.local_compression = self.remote_compression = None 

490 self.session_id = None 

491 self.host_key_type = None 

492 self.host_key = None 

493 

494 # state used during negotiation 

495 self.kex_engine = None 

496 self.H = None 

497 self.K = None 

498 

499 self.initial_kex_done = False 

500 self.in_kex = False 

501 self.authenticated = False 

502 self._expected_packet = tuple() 

503 # synchronization (always higher level than write_lock) 

504 self.lock = threading.Lock() 

505 

506 # tracking open channels 

507 self._channels = ChannelMap() 

508 self.channel_events = {} # (id -> Event) 

509 self.channels_seen = {} # (id -> True) 

510 self._channel_counter = 0 

511 self.default_max_packet_size = default_max_packet_size 

512 self.default_window_size = default_window_size 

513 self._forward_agent_handler = None 

514 self._x11_handler = None 

515 self._tcp_handler = None 

516 

517 self.saved_exception = None 

518 self.clear_to_send = threading.Event() 

519 self.clear_to_send_lock = threading.Lock() 

520 self.clear_to_send_timeout = 30.0 

521 self.log_name = "paramiko.transport" 

522 self.logger = util.get_logger(self.log_name) 

523 self.packetizer.set_log(self.logger) 

524 self.auth_handler = None 

525 # response Message from an arbitrary global request 

526 self.global_response = None 

527 # user-defined event callbacks 

528 self.completion_event = None 

529 # how long (seconds) to wait for the SSH banner 

530 self.banner_timeout = 15 

531 # how long (seconds) to wait for the handshake to finish after SSH 

532 # banner sent. 

533 self.handshake_timeout = 15 

534 # how long (seconds) to wait for the auth response. 

535 self.auth_timeout = 30 

536 # how long (seconds) to wait for opening a channel 

537 self.channel_timeout = 60 * 60 

538 self.disabled_algorithms = disabled_algorithms or {} 

539 self.server_sig_algs = server_sig_algs 

540 

541 # server mode: 

542 self.server_mode = False 

543 self.server_object = None 

544 self.server_key_dict = {} 

545 self.server_accepts = [] 

546 self.server_accept_cv = threading.Condition(self.lock) 

547 self.subsystem_table = {} 

548 

549 # Handler table, now set at init time for easier per-instance 

550 # manipulation and subclass twiddling. 

551 self._handler_table = { 

552 MSG_EXT_INFO: self._parse_ext_info, 

553 MSG_NEWKEYS: self._parse_newkeys, 

554 MSG_GLOBAL_REQUEST: self._parse_global_request, 

555 MSG_REQUEST_SUCCESS: self._parse_request_success, 

556 MSG_REQUEST_FAILURE: self._parse_request_failure, 

557 MSG_CHANNEL_OPEN_SUCCESS: self._parse_channel_open_success, 

558 MSG_CHANNEL_OPEN_FAILURE: self._parse_channel_open_failure, 

559 MSG_CHANNEL_OPEN: self._parse_channel_open, 

560 MSG_KEXINIT: self._negotiate_keys, 

561 } 

562 

563 def _filter_algorithm(self, type_): 

564 default = getattr(self, "_preferred_{}".format(type_)) 

565 return tuple( 

566 x 

567 for x in default 

568 if x not in self.disabled_algorithms.get(type_, []) 

569 ) 

570 

571 @property 

572 def preferred_ciphers(self): 

573 return self._filter_algorithm("ciphers") 

574 

575 @property 

576 def preferred_macs(self): 

577 return self._filter_algorithm("macs") 

578 

579 @property 

580 def preferred_keys(self): 

581 # Interleave cert variants here; resistant to various background 

582 # overwriting of _preferred_keys, and necessary as hostkeys can't use 

583 # the logic pubkey auth does re: injecting/checking for certs at 

584 # runtime 

585 filtered = self._filter_algorithm("keys") 

586 return tuple( 

587 filtered 

588 + tuple("{}-cert-v01@openssh.com".format(x) for x in filtered) 

589 ) 

590 

591 @property 

592 def preferred_pubkeys(self): 

593 return self._filter_algorithm("pubkeys") 

594 

595 @property 

596 def preferred_kex(self): 

597 return self._filter_algorithm("kex") 

598 

599 @property 

600 def preferred_compression(self): 

601 return self._filter_algorithm("compression") 

602 

603 def __repr__(self): 

604 """ 

605 Returns a string representation of this object, for debugging. 

606 """ 

607 id_ = hex(id(self) & xffffffff) 

608 out = "<paramiko.Transport at {}".format(id_) 

609 if not self.active: 

610 out += " (unconnected)" 

611 else: 

612 if self.local_cipher != "": 

613 out += " (cipher {}, {:d} bits)".format( 

614 self.local_cipher, 

615 self._cipher_info[self.local_cipher]["key-size"] * 8, 

616 ) 

617 if self.is_authenticated(): 

618 out += " (active; {} open channel(s))".format( 

619 len(self._channels) 

620 ) 

621 elif self.initial_kex_done: 

622 out += " (connected; awaiting auth)" 

623 else: 

624 out += " (connecting)" 

625 out += ">" 

626 return out 

627 

628 def atfork(self): 

629 """ 

630 Terminate this Transport without closing the session. On posix 

631 systems, if a Transport is open during process forking, both parent 

632 and child will share the underlying socket, but only one process can 

633 use the connection (without corrupting the session). Use this method 

634 to clean up a Transport object without disrupting the other process. 

635 

636 .. versionadded:: 1.5.3 

637 """ 

638 self.sock.close() 

639 self.close() 

640 

641 def get_security_options(self): 

642 """ 

643 Return a `.SecurityOptions` object which can be used to tweak the 

644 encryption algorithms this transport will permit (for encryption, 

645 digest/hash operations, public keys, and key exchanges) and the order 

646 of preference for them. 

647 """ 

648 return SecurityOptions(self) 

649 

650 def start_client(self, event=None, timeout=None): 

651 """ 

652 Negotiate a new SSH2 session as a client. This is the first step after 

653 creating a new `.Transport`. A separate thread is created for protocol 

654 negotiation. 

655 

656 If an event is passed in, this method returns immediately. When 

657 negotiation is done (successful or not), the given ``Event`` will 

658 be triggered. On failure, `is_active` will return ``False``. 

659 

660 (Since 1.4) If ``event`` is ``None``, this method will not return until 

661 negotiation is done. On success, the method returns normally. 

662 Otherwise an SSHException is raised. 

663 

664 After a successful negotiation, you will usually want to authenticate, 

665 calling `auth_password <Transport.auth_password>` or 

666 `auth_publickey <Transport.auth_publickey>`. 

667 

668 .. note:: `connect` is a simpler method for connecting as a client. 

669 

670 .. note:: 

671 After calling this method (or `start_server` or `connect`), you 

672 should no longer directly read from or write to the original socket 

673 object. 

674 

675 :param .threading.Event event: 

676 an event to trigger when negotiation is complete (optional) 

677 

678 :param float timeout: 

679 a timeout, in seconds, for SSH2 session negotiation (optional) 

680 

681 :raises: 

682 `.SSHException` -- if negotiation fails (and no ``event`` was 

683 passed in) 

684 """ 

685 self.active = True 

686 if event is not None: 

687 # async, return immediately and let the app poll for completion 

688 self.completion_event = event 

689 self.start() 

690 return 

691 

692 # synchronous, wait for a result 

693 self.completion_event = event = threading.Event() 

694 self.start() 

695 max_time = time.time() + timeout if timeout is not None else None 

696 while True: 

697 event.wait(0.1) 

698 if not self.active: 

699 e = self.get_exception() 

700 if e is not None: 

701 raise e 

702 raise SSHException("Negotiation failed.") 

703 if event.is_set() or ( 

704 timeout is not None and time.time() >= max_time 

705 ): 

706 break 

707 

708 def start_server(self, event=None, server=None): 

709 """ 

710 Negotiate a new SSH2 session as a server. This is the first step after 

711 creating a new `.Transport` and setting up your server host key(s). A 

712 separate thread is created for protocol negotiation. 

713 

714 If an event is passed in, this method returns immediately. When 

715 negotiation is done (successful or not), the given ``Event`` will 

716 be triggered. On failure, `is_active` will return ``False``. 

717 

718 (Since 1.4) If ``event`` is ``None``, this method will not return until 

719 negotiation is done. On success, the method returns normally. 

720 Otherwise an SSHException is raised. 

721 

722 After a successful negotiation, the client will need to authenticate. 

723 Override the methods `get_allowed_auths 

724 <.ServerInterface.get_allowed_auths>`, `check_auth_none 

725 <.ServerInterface.check_auth_none>`, `check_auth_password 

726 <.ServerInterface.check_auth_password>`, and `check_auth_publickey 

727 <.ServerInterface.check_auth_publickey>` in the given ``server`` object 

728 to control the authentication process. 

729 

730 After a successful authentication, the client should request to open a 

731 channel. Override `check_channel_request 

732 <.ServerInterface.check_channel_request>` in the given ``server`` 

733 object to allow channels to be opened. 

734 

735 .. note:: 

736 After calling this method (or `start_client` or `connect`), you 

737 should no longer directly read from or write to the original socket 

738 object. 

739 

740 :param .threading.Event event: 

741 an event to trigger when negotiation is complete. 

742 :param .ServerInterface server: 

743 an object used to perform authentication and create `channels 

744 <.Channel>` 

745 

746 :raises: 

747 `.SSHException` -- if negotiation fails (and no ``event`` was 

748 passed in) 

749 """ 

750 if server is None: 

751 server = ServerInterface() 

752 self.server_mode = True 

753 self.server_object = server 

754 self.active = True 

755 if event is not None: 

756 # async, return immediately and let the app poll for completion 

757 self.completion_event = event 

758 self.start() 

759 return 

760 

761 # synchronous, wait for a result 

762 self.completion_event = event = threading.Event() 

763 self.start() 

764 while True: 

765 event.wait(0.1) 

766 if not self.active: 

767 e = self.get_exception() 

768 if e is not None: 

769 raise e 

770 raise SSHException("Negotiation failed.") 

771 if event.is_set(): 

772 break 

773 

774 def add_server_key(self, key): 

775 """ 

776 Add a host key to the list of keys used for server mode. When behaving 

777 as a server, the host key is used to sign certain packets during the 

778 SSH2 negotiation, so that the client can trust that we are who we say 

779 we are. Because this is used for signing, the key must contain private 

780 key info, not just the public half. Only one key of each type is kept. 

781 

782 :param .PKey key: 

783 the host key (instance of some subclass) to add 

784 """ 

785 self.server_key_dict[key.get_name()] = key 

786 # Handle SHA-2 extensions for RSA by ensuring that lookups into 

787 # self.server_key_dict will yield this key for any of the algorithm 

788 # names. 

789 if isinstance(key, RSAKey): 

790 self.server_key_dict["rsa-sha2-256"] = key 

791 self.server_key_dict["rsa-sha2-512"] = key 

792 

793 def get_server_key(self): 

794 """ 

795 Return the active host key, in server mode. After negotiating with the 

796 client, this method will return the negotiated host key. If only one 

797 type of host key was set with `add_server_key`, that's the only key 

798 that will ever be returned. But in cases where you have set more than 

799 one type of host key, the key type will be negotiated by the client, 

800 and this method will return the key of the type agreed on. If the host 

801 key has not been negotiated yet, ``None`` is returned. In client mode, 

802 the behavior is undefined. 

803 

804 :return: 

805 host key (`.PKey`) of the type negotiated by the client, or 

806 ``None``. 

807 """ 

808 try: 

809 return self.server_key_dict[self.host_key_type] 

810 except KeyError: 

811 pass 

812 return None 

813 

814 @staticmethod 

815 def load_server_moduli(filename=None): 

816 """ 

817 (optional) 

818 Load a file of prime moduli for use in doing group-exchange key 

819 negotiation in server mode. It's a rather obscure option and can be 

820 safely ignored. 

821 

822 In server mode, the remote client may request "group-exchange" key 

823 negotiation, which asks the server to send a random prime number that 

824 fits certain criteria. These primes are pretty difficult to compute, 

825 so they can't be generated on demand. But many systems contain a file 

826 of suitable primes (usually named something like ``/etc/ssh/moduli``). 

827 If you call `load_server_moduli` and it returns ``True``, then this 

828 file of primes has been loaded and we will support "group-exchange" in 

829 server mode. Otherwise server mode will just claim that it doesn't 

830 support that method of key negotiation. 

831 

832 :param str filename: 

833 optional path to the moduli file, if you happen to know that it's 

834 not in a standard location. 

835 :return: 

836 True if a moduli file was successfully loaded; False otherwise. 

837 

838 .. note:: This has no effect when used in client mode. 

839 """ 

840 Transport._modulus_pack = ModulusPack() 

841 # places to look for the openssh "moduli" file 

842 file_list = ["/etc/ssh/moduli", "/usr/local/etc/moduli"] 

843 if filename is not None: 

844 file_list.insert(0, filename) 

845 for fn in file_list: 

846 try: 

847 Transport._modulus_pack.read_file(fn) 

848 return True 

849 except IOError: 

850 pass 

851 # none succeeded 

852 Transport._modulus_pack = None 

853 return False 

854 

855 def close(self): 

856 """ 

857 Close this session, and any open channels that are tied to it. 

858 """ 

859 if not self.active: 

860 return 

861 self.stop_thread() 

862 for chan in list(self._channels.values()): 

863 chan._unlink() 

864 self.sock.close() 

865 

866 def get_remote_server_key(self): 

867 """ 

868 Return the host key of the server (in client mode). 

869 

870 .. note:: 

871 Previously this call returned a tuple of ``(key type, key 

872 string)``. You can get the same effect by calling `.PKey.get_name` 

873 for the key type, and ``str(key)`` for the key string. 

874 

875 :raises: `.SSHException` -- if no session is currently active. 

876 

877 :return: public key (`.PKey`) of the remote server 

878 """ 

879 if (not self.active) or (not self.initial_kex_done): 

880 raise SSHException("No existing session") 

881 return self.host_key 

882 

883 def is_active(self): 

884 """ 

885 Return true if this session is active (open). 

886 

887 :return: 

888 True if the session is still active (open); False if the session is 

889 closed 

890 """ 

891 return self.active 

892 

893 def open_session( 

894 self, window_size=None, max_packet_size=None, timeout=None 

895 ): 

896 """ 

897 Request a new channel to the server, of type ``"session"``. This is 

898 just an alias for calling `open_channel` with an argument of 

899 ``"session"``. 

900 

901 .. note:: Modifying the the window and packet sizes might have adverse 

902 effects on the session created. The default values are the same 

903 as in the OpenSSH code base and have been battle tested. 

904 

905 :param int window_size: 

906 optional window size for this session. 

907 :param int max_packet_size: 

908 optional max packet size for this session. 

909 

910 :return: a new `.Channel` 

911 

912 :raises: 

913 `.SSHException` -- if the request is rejected or the session ends 

914 prematurely 

915 

916 .. versionchanged:: 1.13.4/1.14.3/1.15.3 

917 Added the ``timeout`` argument. 

918 .. versionchanged:: 1.15 

919 Added the ``window_size`` and ``max_packet_size`` arguments. 

920 """ 

921 return self.open_channel( 

922 "session", 

923 window_size=window_size, 

924 max_packet_size=max_packet_size, 

925 timeout=timeout, 

926 ) 

927 

928 def open_x11_channel(self, src_addr=None): 

929 """ 

930 Request a new channel to the client, of type ``"x11"``. This 

931 is just an alias for ``open_channel('x11', src_addr=src_addr)``. 

932 

933 :param tuple src_addr: 

934 the source address (``(str, int)``) of the x11 server (port is the 

935 x11 port, ie. 6010) 

936 :return: a new `.Channel` 

937 

938 :raises: 

939 `.SSHException` -- if the request is rejected or the session ends 

940 prematurely 

941 """ 

942 return self.open_channel("x11", src_addr=src_addr) 

943 

944 def open_forward_agent_channel(self): 

945 """ 

946 Request a new channel to the client, of type 

947 ``"auth-agent@openssh.com"``. 

948 

949 This is just an alias for ``open_channel('auth-agent@openssh.com')``. 

950 

951 :return: a new `.Channel` 

952 

953 :raises: `.SSHException` -- 

954 if the request is rejected or the session ends prematurely 

955 """ 

956 return self.open_channel("auth-agent@openssh.com") 

957 

958 def open_forwarded_tcpip_channel(self, src_addr, dest_addr): 

959 """ 

960 Request a new channel back to the client, of type ``forwarded-tcpip``. 

961 

962 This is used after a client has requested port forwarding, for sending 

963 incoming connections back to the client. 

964 

965 :param src_addr: originator's address 

966 :param dest_addr: local (server) connected address 

967 """ 

968 return self.open_channel("forwarded-tcpip", dest_addr, src_addr) 

969 

970 def open_channel( 

971 self, 

972 kind, 

973 dest_addr=None, 

974 src_addr=None, 

975 window_size=None, 

976 max_packet_size=None, 

977 timeout=None, 

978 ): 

979 """ 

980 Request a new channel to the server. `Channels <.Channel>` are 

981 socket-like objects used for the actual transfer of data across the 

982 session. You may only request a channel after negotiating encryption 

983 (using `connect` or `start_client`) and authenticating. 

984 

985 .. note:: Modifying the the window and packet sizes might have adverse 

986 effects on the channel created. The default values are the same 

987 as in the OpenSSH code base and have been battle tested. 

988 

989 :param str kind: 

990 the kind of channel requested (usually ``"session"``, 

991 ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"``) 

992 :param tuple dest_addr: 

993 the destination address (address + port tuple) of this port 

994 forwarding, if ``kind`` is ``"forwarded-tcpip"`` or 

995 ``"direct-tcpip"`` (ignored for other channel types) 

996 :param src_addr: the source address of this port forwarding, if 

997 ``kind`` is ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"`` 

998 :param int window_size: 

999 optional window size for this session. 

1000 :param int max_packet_size: 

1001 optional max packet size for this session. 

1002 :param float timeout: 

1003 optional timeout opening a channel, default 3600s (1h) 

1004 

1005 :return: a new `.Channel` on success 

1006 

1007 :raises: 

1008 `.SSHException` -- if the request is rejected, the session ends 

1009 prematurely or there is a timeout opening a channel 

1010 

1011 .. versionchanged:: 1.15 

1012 Added the ``window_size`` and ``max_packet_size`` arguments. 

1013 """ 

1014 if not self.active: 

1015 raise SSHException("SSH session not active") 

1016 timeout = self.channel_timeout if timeout is None else timeout 

1017 self.lock.acquire() 

1018 try: 

1019 window_size = self._sanitize_window_size(window_size) 

1020 max_packet_size = self._sanitize_packet_size(max_packet_size) 

1021 chanid = self._next_channel() 

1022 m = Message() 

1023 m.add_byte(cMSG_CHANNEL_OPEN) 

1024 m.add_string(kind) 

1025 m.add_int(chanid) 

1026 m.add_int(window_size) 

1027 m.add_int(max_packet_size) 

1028 if (kind == "forwarded-tcpip") or (kind == "direct-tcpip"): 

1029 m.add_string(dest_addr[0]) 

1030 m.add_int(dest_addr[1]) 

1031 m.add_string(src_addr[0]) 

1032 m.add_int(src_addr[1]) 

1033 elif kind == "x11": 

1034 m.add_string(src_addr[0]) 

1035 m.add_int(src_addr[1]) 

1036 chan = Channel(chanid) 

1037 self._channels.put(chanid, chan) 

1038 self.channel_events[chanid] = event = threading.Event() 

1039 self.channels_seen[chanid] = True 

1040 chan._set_transport(self) 

1041 chan._set_window(window_size, max_packet_size) 

1042 finally: 

1043 self.lock.release() 

1044 self._send_user_message(m) 

1045 start_ts = time.time() 

1046 while True: 

1047 event.wait(0.1) 

1048 if not self.active: 

1049 e = self.get_exception() 

1050 if e is None: 

1051 e = SSHException("Unable to open channel.") 

1052 raise e 

1053 if event.is_set(): 

1054 break 

1055 elif start_ts + timeout < time.time(): 

1056 raise SSHException("Timeout opening channel.") 

1057 chan = self._channels.get(chanid) 

1058 if chan is not None: 

1059 return chan 

1060 e = self.get_exception() 

1061 if e is None: 

1062 e = SSHException("Unable to open channel.") 

1063 raise e 

1064 

1065 def request_port_forward(self, address, port, handler=None): 

1066 """ 

1067 Ask the server to forward TCP connections from a listening port on 

1068 the server, across this SSH session. 

1069 

1070 If a handler is given, that handler is called from a different thread 

1071 whenever a forwarded connection arrives. The handler parameters are:: 

1072 

1073 handler( 

1074 channel, 

1075 (origin_addr, origin_port), 

1076 (server_addr, server_port), 

1077 ) 

1078 

1079 where ``server_addr`` and ``server_port`` are the address and port that 

1080 the server was listening on. 

1081 

1082 If no handler is set, the default behavior is to send new incoming 

1083 forwarded connections into the accept queue, to be picked up via 

1084 `accept`. 

1085 

1086 :param str address: the address to bind when forwarding 

1087 :param int port: 

1088 the port to forward, or 0 to ask the server to allocate any port 

1089 :param callable handler: 

1090 optional handler for incoming forwarded connections, of the form 

1091 ``func(Channel, (str, int), (str, int))``. 

1092 

1093 :return: the port number (`int`) allocated by the server 

1094 

1095 :raises: 

1096 `.SSHException` -- if the server refused the TCP forward request 

1097 """ 

1098 if not self.active: 

1099 raise SSHException("SSH session not active") 

1100 port = int(port) 

1101 response = self.global_request( 

1102 "tcpip-forward", (address, port), wait=True 

1103 ) 

1104 if response is None: 

1105 raise SSHException("TCP forwarding request denied") 

1106 if port == 0: 

1107 port = response.get_int() 

1108 if handler is None: 

1109 

1110 def default_handler(channel, src_addr, dest_addr_port): 

1111 # src_addr, src_port = src_addr_port 

1112 # dest_addr, dest_port = dest_addr_port 

1113 self._queue_incoming_channel(channel) 

1114 

1115 handler = default_handler 

1116 self._tcp_handler = handler 

1117 return port 

1118 

1119 def cancel_port_forward(self, address, port): 

1120 """ 

1121 Ask the server to cancel a previous port-forwarding request. No more 

1122 connections to the given address & port will be forwarded across this 

1123 ssh connection. 

1124 

1125 :param str address: the address to stop forwarding 

1126 :param int port: the port to stop forwarding 

1127 """ 

1128 if not self.active: 

1129 return 

1130 self._tcp_handler = None 

1131 self.global_request("cancel-tcpip-forward", (address, port), wait=True) 

1132 

1133 def open_sftp_client(self): 

1134 """ 

1135 Create an SFTP client channel from an open transport. On success, an 

1136 SFTP session will be opened with the remote host, and a new 

1137 `.SFTPClient` object will be returned. 

1138 

1139 :return: 

1140 a new `.SFTPClient` referring to an sftp session (channel) across 

1141 this transport 

1142 """ 

1143 return SFTPClient.from_transport(self) 

1144 

1145 def send_ignore(self, byte_count=None): 

1146 """ 

1147 Send a junk packet across the encrypted link. This is sometimes used 

1148 to add "noise" to a connection to confuse would-be attackers. It can 

1149 also be used as a keep-alive for long lived connections traversing 

1150 firewalls. 

1151 

1152 :param int byte_count: 

1153 the number of random bytes to send in the payload of the ignored 

1154 packet -- defaults to a random number from 10 to 41. 

1155 """ 

1156 m = Message() 

1157 m.add_byte(cMSG_IGNORE) 

1158 if byte_count is None: 

1159 byte_count = (byte_ord(os.urandom(1)) % 32) + 10 

1160 m.add_bytes(os.urandom(byte_count)) 

1161 self._send_user_message(m) 

1162 

1163 def renegotiate_keys(self): 

1164 """ 

1165 Force this session to switch to new keys. Normally this is done 

1166 automatically after the session hits a certain number of packets or 

1167 bytes sent or received, but this method gives you the option of forcing 

1168 new keys whenever you want. Negotiating new keys causes a pause in 

1169 traffic both ways as the two sides swap keys and do computations. This 

1170 method returns when the session has switched to new keys. 

1171 

1172 :raises: 

1173 `.SSHException` -- if the key renegotiation failed (which causes 

1174 the session to end) 

1175 """ 

1176 self.completion_event = threading.Event() 

1177 self._send_kex_init() 

1178 while True: 

1179 self.completion_event.wait(0.1) 

1180 if not self.active: 

1181 e = self.get_exception() 

1182 if e is not None: 

1183 raise e 

1184 raise SSHException("Negotiation failed.") 

1185 if self.completion_event.is_set(): 

1186 break 

1187 return 

1188 

1189 def set_keepalive(self, interval): 

1190 """ 

1191 Turn on/off keepalive packets (default is off). If this is set, after 

1192 ``interval`` seconds without sending any data over the connection, a 

1193 "keepalive" packet will be sent (and ignored by the remote host). This 

1194 can be useful to keep connections alive over a NAT, for example. 

1195 

1196 :param int interval: 

1197 seconds to wait before sending a keepalive packet (or 

1198 0 to disable keepalives). 

1199 """ 

1200 

1201 def _request(x=weakref.proxy(self)): 

1202 return x.global_request("keepalive@lag.net", wait=False) 

1203 

1204 self.packetizer.set_keepalive(interval, _request) 

1205 

1206 def global_request(self, kind, data=None, wait=True): 

1207 """ 

1208 Make a global request to the remote host. These are normally 

1209 extensions to the SSH2 protocol. 

1210 

1211 :param str kind: name of the request. 

1212 :param tuple data: 

1213 an optional tuple containing additional data to attach to the 

1214 request. 

1215 :param bool wait: 

1216 ``True`` if this method should not return until a response is 

1217 received; ``False`` otherwise. 

1218 :return: 

1219 a `.Message` containing possible additional data if the request was 

1220 successful (or an empty `.Message` if ``wait`` was ``False``); 

1221 ``None`` if the request was denied. 

1222 """ 

1223 if wait: 

1224 self.completion_event = threading.Event() 

1225 m = Message() 

1226 m.add_byte(cMSG_GLOBAL_REQUEST) 

1227 m.add_string(kind) 

1228 m.add_boolean(wait) 

1229 if data is not None: 

1230 m.add(*data) 

1231 self._log(DEBUG, 'Sending global request "{}"'.format(kind)) 

1232 self._send_user_message(m) 

1233 if not wait: 

1234 return None 

1235 while True: 

1236 self.completion_event.wait(0.1) 

1237 if not self.active: 

1238 return None 

1239 if self.completion_event.is_set(): 

1240 break 

1241 return self.global_response 

1242 

1243 def accept(self, timeout=None): 

1244 """ 

1245 Return the next channel opened by the client over this transport, in 

1246 server mode. If no channel is opened before the given timeout, 

1247 ``None`` is returned. 

1248 

1249 :param int timeout: 

1250 seconds to wait for a channel, or ``None`` to wait forever 

1251 :return: a new `.Channel` opened by the client 

1252 """ 

1253 self.lock.acquire() 

1254 try: 

1255 if len(self.server_accepts) > 0: 

1256 chan = self.server_accepts.pop(0) 

1257 else: 

1258 self.server_accept_cv.wait(timeout) 

1259 if len(self.server_accepts) > 0: 

1260 chan = self.server_accepts.pop(0) 

1261 else: 

1262 # timeout 

1263 chan = None 

1264 finally: 

1265 self.lock.release() 

1266 return chan 

1267 

1268 def connect( 

1269 self, 

1270 hostkey=None, 

1271 username="", 

1272 password=None, 

1273 pkey=None, 

1274 ): 

1275 """ 

1276 Negotiate an SSH2 session, and optionally verify the server's host key 

1277 and authenticate using a password or private key. This is a shortcut 

1278 for `start_client`, `get_remote_server_key`, and 

1279 `Transport.auth_password` or `Transport.auth_publickey`. Use those 

1280 methods if you want more control. 

1281 

1282 You can use this method immediately after creating a Transport to 

1283 negotiate encryption with a server. If it fails, an exception will be 

1284 thrown. On success, the method will return cleanly, and an encrypted 

1285 session exists. You may immediately call `open_channel` or 

1286 `open_session` to get a `.Channel` object, which is used for data 

1287 transfer. 

1288 

1289 .. note:: 

1290 If you fail to supply a password or private key, this method may 

1291 succeed, but a subsequent `open_channel` or `open_session` call may 

1292 fail because you haven't authenticated yet. 

1293 

1294 :param .PKey hostkey: 

1295 the host key expected from the server, or ``None`` if you don't 

1296 want to do host key verification. 

1297 :param str username: the username to authenticate as. 

1298 :param str password: 

1299 a password to use for authentication, if you want to use password 

1300 authentication; otherwise ``None``. 

1301 :param .PKey pkey: 

1302 a private key to use for authentication, if you want to use private 

1303 key authentication; otherwise ``None``. 

1304 

1305 :raises: `.SSHException` -- if the SSH2 negotiation fails, the host key 

1306 supplied by the server is incorrect, or authentication fails. 

1307 """ 

1308 if hostkey is not None: 

1309 # TODO: a more robust implementation would be to ask each key class 

1310 # for its nameS plural, and just use that. 

1311 # TODO: that could be used in a bunch of other spots too 

1312 # TODO: don't we have that now, lol 

1313 # TODO: either way this is ~= like using SecurityOptions.key_types 

1314 # = xxx, but different, which sucks sigh 

1315 if isinstance(hostkey, RSAKey): 

1316 self._preferred_keys = [ 

1317 "rsa-sha2-512", 

1318 "rsa-sha2-256", 

1319 ] 

1320 else: 

1321 self._preferred_keys = [hostkey.get_name()] 

1322 

1323 self.start_client() 

1324 

1325 # check host key if we were given one 

1326 if hostkey is not None: 

1327 key = self.get_remote_server_key() 

1328 if ( 

1329 key.get_name() != hostkey.get_name() 

1330 or key.asbytes() != hostkey.asbytes() 

1331 ): 

1332 self._log(DEBUG, "Bad host key from server") 

1333 self._log( 

1334 DEBUG, 

1335 "Expected: {}: {}".format( 

1336 hostkey.get_name(), repr(hostkey.asbytes()) 

1337 ), 

1338 ) 

1339 self._log( 

1340 DEBUG, 

1341 "Got : {}: {}".format( 

1342 key.get_name(), repr(key.asbytes()) 

1343 ), 

1344 ) 

1345 raise SSHException("Bad host key from server") 

1346 self._log( 

1347 DEBUG, "Host key verified ({})".format(hostkey.get_name()) 

1348 ) 

1349 

1350 if (pkey is not None) or (password is not None): 

1351 if pkey is not None: 

1352 self._log(DEBUG, "Attempting public-key auth...") 

1353 self.auth_publickey(username, pkey) 

1354 else: 

1355 self._log(DEBUG, "Attempting password auth...") 

1356 self.auth_password(username, password) 

1357 

1358 return 

1359 

1360 def get_exception(self): 

1361 """ 

1362 Return any exception that happened during the last server request. 

1363 This can be used to fetch more specific error information after using 

1364 calls like `start_client`. The exception (if any) is cleared after 

1365 this call. 

1366 

1367 :return: 

1368 an exception, or ``None`` if there is no stored exception. 

1369 

1370 .. versionadded:: 1.1 

1371 """ 

1372 self.lock.acquire() 

1373 try: 

1374 e = self.saved_exception 

1375 self.saved_exception = None 

1376 return e 

1377 finally: 

1378 self.lock.release() 

1379 

1380 def set_subsystem_handler(self, name, handler, *args, **kwargs): 

1381 """ 

1382 Set the handler class for a subsystem in server mode. If a request 

1383 for this subsystem is made on an open ssh channel later, this handler 

1384 will be constructed and called -- see `.SubsystemHandler` for more 

1385 detailed documentation. 

1386 

1387 Any extra parameters (including keyword arguments) are saved and 

1388 passed to the `.SubsystemHandler` constructor later. 

1389 

1390 :param str name: name of the subsystem. 

1391 :param handler: 

1392 subclass of `.SubsystemHandler` that handles this subsystem. 

1393 """ 

1394 try: 

1395 self.lock.acquire() 

1396 self.subsystem_table[name] = (handler, args, kwargs) 

1397 finally: 

1398 self.lock.release() 

1399 

1400 def is_authenticated(self): 

1401 """ 

1402 Return true if this session is active and authenticated. 

1403 

1404 :return: 

1405 True if the session is still open and has been authenticated 

1406 successfully; False if authentication failed and/or the session is 

1407 closed. 

1408 """ 

1409 return ( 

1410 self.active 

1411 and self.auth_handler is not None 

1412 and self.auth_handler.is_authenticated() 

1413 ) 

1414 

1415 def get_username(self): 

1416 """ 

1417 Return the username this connection is authenticated for. If the 

1418 session is not authenticated (or authentication failed), this method 

1419 returns ``None``. 

1420 

1421 :return: username that was authenticated (a `str`), or ``None``. 

1422 """ 

1423 if not self.active or (self.auth_handler is None): 

1424 return None 

1425 return self.auth_handler.get_username() 

1426 

1427 def get_banner(self): 

1428 """ 

1429 Return the banner supplied by the server upon connect. If no banner is 

1430 supplied, this method returns ``None``. 

1431 

1432 :returns: server supplied banner (`str`), or ``None``. 

1433 

1434 .. versionadded:: 1.13 

1435 """ 

1436 if not self.active or (self.auth_handler is None): 

1437 return None 

1438 return self.auth_handler.banner 

1439 

1440 def auth_none(self, username): 

1441 """ 

1442 Try to authenticate to the server using no authentication at all. 

1443 This will almost always fail. It may be useful for determining the 

1444 list of authentication types supported by the server, by catching the 

1445 `.BadAuthenticationType` exception raised. 

1446 

1447 :param str username: the username to authenticate as 

1448 :return: 

1449 list of auth types permissible for the next stage of 

1450 authentication (normally empty) 

1451 

1452 :raises: 

1453 `.BadAuthenticationType` -- if "none" authentication isn't allowed 

1454 by the server for this user 

1455 :raises: 

1456 `.SSHException` -- if the authentication failed due to a network 

1457 error 

1458 

1459 .. versionadded:: 1.5 

1460 """ 

1461 if (not self.active) or (not self.initial_kex_done): 

1462 raise SSHException("No existing session") 

1463 my_event = threading.Event() 

1464 self.auth_handler = AuthHandler(self) 

1465 self.auth_handler.auth_none(username, my_event) 

1466 return self.auth_handler.wait_for_response(my_event) 

1467 

1468 def auth_password(self, username, password, event=None, fallback=True): 

1469 """ 

1470 Authenticate to the server using a password. The username and password 

1471 are sent over an encrypted link. 

1472 

1473 If an ``event`` is passed in, this method will return immediately, and 

1474 the event will be triggered once authentication succeeds or fails. On 

1475 success, `is_authenticated` will return ``True``. On failure, you may 

1476 use `get_exception` to get more detailed error information. 

1477 

1478 Since 1.1, if no event is passed, this method will block until the 

1479 authentication succeeds or fails. On failure, an exception is raised. 

1480 Otherwise, the method simply returns. 

1481 

1482 Since 1.5, if no event is passed and ``fallback`` is ``True`` (the 

1483 default), if the server doesn't support plain password authentication 

1484 but does support so-called "keyboard-interactive" mode, an attempt 

1485 will be made to authenticate using this interactive mode. If it fails, 

1486 the normal exception will be thrown as if the attempt had never been 

1487 made. This is useful for some recent Gentoo and Debian distributions, 

1488 which turn off plain password authentication in a misguided belief 

1489 that interactive authentication is "more secure". (It's not.) 

1490 

1491 If the server requires multi-step authentication (which is very rare), 

1492 this method will return a list of auth types permissible for the next 

1493 step. Otherwise, in the normal case, an empty list is returned. 

1494 

1495 :param str username: the username to authenticate as 

1496 :param basestring password: the password to authenticate with 

1497 :param .threading.Event event: 

1498 an event to trigger when the authentication attempt is complete 

1499 (whether it was successful or not) 

1500 :param bool fallback: 

1501 ``True`` if an attempt at an automated "interactive" password auth 

1502 should be made if the server doesn't support normal password auth 

1503 :return: 

1504 list of auth types permissible for the next stage of 

1505 authentication (normally empty) 

1506 

1507 :raises: 

1508 `.BadAuthenticationType` -- if password authentication isn't 

1509 allowed by the server for this user (and no event was passed in) 

1510 :raises: 

1511 `.AuthenticationException` -- if the authentication failed (and no 

1512 event was passed in) 

1513 :raises: `.SSHException` -- if there was a network error 

1514 """ 

1515 if (not self.active) or (not self.initial_kex_done): 

1516 # we should never try to send the password unless we're on a secure 

1517 # link 

1518 raise SSHException("No existing session") 

1519 if event is None: 

1520 my_event = threading.Event() 

1521 else: 

1522 my_event = event 

1523 self.auth_handler = AuthHandler(self) 

1524 self.auth_handler.auth_password(username, password, my_event) 

1525 if event is not None: 

1526 # caller wants to wait for event themselves 

1527 return [] 

1528 try: 

1529 return self.auth_handler.wait_for_response(my_event) 

1530 except BadAuthenticationType as e: 

1531 # if password auth isn't allowed, but keyboard-interactive *is*, 

1532 # try to fudge it 

1533 if not fallback or ("keyboard-interactive" not in e.allowed_types): 

1534 raise 

1535 try: 

1536 

1537 def handler(title, instructions, fields): 

1538 if len(fields) > 1: 

1539 raise SSHException("Fallback authentication failed.") 

1540 if len(fields) == 0: 

1541 # for some reason, at least on os x, a 2nd request will 

1542 # be made with zero fields requested. maybe it's just 

1543 # to try to fake out automated scripting of the exact 

1544 # type we're doing here. *shrug* :) 

1545 return [] 

1546 return [password] 

1547 

1548 return self.auth_interactive(username, handler) 

1549 except SSHException: 

1550 # attempt failed; just raise the original exception 

1551 raise e 

1552 

1553 def auth_publickey(self, username, key, event=None): 

1554 """ 

1555 Authenticate to the server using a private key. The key is used to 

1556 sign data from the server, so it must include the private part. 

1557 

1558 If an ``event`` is passed in, this method will return immediately, and 

1559 the event will be triggered once authentication succeeds or fails. On 

1560 success, `is_authenticated` will return ``True``. On failure, you may 

1561 use `get_exception` to get more detailed error information. 

1562 

1563 Since 1.1, if no event is passed, this method will block until the 

1564 authentication succeeds or fails. On failure, an exception is raised. 

1565 Otherwise, the method simply returns. 

1566 

1567 If the server requires multi-step authentication (which is very rare), 

1568 this method will return a list of auth types permissible for the next 

1569 step. Otherwise, in the normal case, an empty list is returned. 

1570 

1571 :param str username: the username to authenticate as 

1572 :param .PKey key: the private key to authenticate with 

1573 :param .threading.Event event: 

1574 an event to trigger when the authentication attempt is complete 

1575 (whether it was successful or not) 

1576 :return: 

1577 list of auth types permissible for the next stage of 

1578 authentication (normally empty) 

1579 

1580 :raises: 

1581 `.BadAuthenticationType` -- if public-key authentication isn't 

1582 allowed by the server for this user (and no event was passed in) 

1583 :raises: 

1584 `.AuthenticationException` -- if the authentication failed (and no 

1585 event was passed in) 

1586 :raises: `.SSHException` -- if there was a network error 

1587 """ 

1588 if (not self.active) or (not self.initial_kex_done): 

1589 # we should never try to authenticate unless we're on a secure link 

1590 raise SSHException("No existing session") 

1591 if event is None: 

1592 my_event = threading.Event() 

1593 else: 

1594 my_event = event 

1595 self.auth_handler = AuthHandler(self) 

1596 self.auth_handler.auth_publickey(username, key, my_event) 

1597 if event is not None: 

1598 # caller wants to wait for event themselves 

1599 return [] 

1600 return self.auth_handler.wait_for_response(my_event) 

1601 

1602 def auth_interactive(self, username, handler, submethods=""): 

1603 """ 

1604 Authenticate to the server interactively. A handler is used to answer 

1605 arbitrary questions from the server. On many servers, this is just a 

1606 dumb wrapper around PAM. 

1607 

1608 This method will block until the authentication succeeds or fails, 

1609 periodically calling the handler asynchronously to get answers to 

1610 authentication questions. The handler may be called more than once 

1611 if the server continues to ask questions. 

1612 

1613 The handler is expected to be a callable that will handle calls of the 

1614 form: ``handler(title, instructions, prompt_list)``. The ``title`` is 

1615 meant to be a dialog-window title, and the ``instructions`` are user 

1616 instructions (both are strings). ``prompt_list`` will be a list of 

1617 prompts, each prompt being a tuple of ``(str, bool)``. The string is 

1618 the prompt and the boolean indicates whether the user text should be 

1619 echoed. 

1620 

1621 A sample call would thus be: 

1622 ``handler('title', 'instructions', [('Password:', False)])``. 

1623 

1624 The handler should return a list or tuple of answers to the server's 

1625 questions. 

1626 

1627 If the server requires multi-step authentication (which is very rare), 

1628 this method will return a list of auth types permissible for the next 

1629 step. Otherwise, in the normal case, an empty list is returned. 

1630 

1631 :param str username: the username to authenticate as 

1632 :param callable handler: a handler for responding to server questions 

1633 :param str submethods: a string list of desired submethods (optional) 

1634 :return: 

1635 list of auth types permissible for the next stage of 

1636 authentication (normally empty). 

1637 

1638 :raises: `.BadAuthenticationType` -- if public-key authentication isn't 

1639 allowed by the server for this user 

1640 :raises: `.AuthenticationException` -- if the authentication failed 

1641 :raises: `.SSHException` -- if there was a network error 

1642 

1643 .. versionadded:: 1.5 

1644 """ 

1645 if (not self.active) or (not self.initial_kex_done): 

1646 # we should never try to authenticate unless we're on a secure link 

1647 raise SSHException("No existing session") 

1648 my_event = threading.Event() 

1649 self.auth_handler = AuthHandler(self) 

1650 self.auth_handler.auth_interactive( 

1651 username, handler, my_event, submethods 

1652 ) 

1653 return self.auth_handler.wait_for_response(my_event) 

1654 

1655 def auth_interactive_dumb(self, username, handler=None, submethods=""): 

1656 """ 

1657 Authenticate to the server interactively but dumber. 

1658 Just print the prompt and / or instructions to stdout and send back 

1659 the response. This is good for situations where partial auth is 

1660 achieved by key and then the user has to enter a 2fac token. 

1661 """ 

1662 

1663 if not handler: 

1664 

1665 def handler(title, instructions, prompt_list): 

1666 answers = [] 

1667 if title: 

1668 print(title.strip()) 

1669 if instructions: 

1670 print(instructions.strip()) 

1671 for prompt, show_input in prompt_list: 

1672 print(prompt.strip(), end=" ") 

1673 answers.append(input()) 

1674 return answers 

1675 

1676 return self.auth_interactive(username, handler, submethods) 

1677 

1678 def set_log_channel(self, name): 

1679 """ 

1680 Set the channel for this transport's logging. The default is 

1681 ``"paramiko.transport"`` but it can be set to anything you want. (See 

1682 the `.logging` module for more info.) SSH Channels will log to a 

1683 sub-channel of the one specified. 

1684 

1685 :param str name: new channel name for logging 

1686 

1687 .. versionadded:: 1.1 

1688 """ 

1689 self.log_name = name 

1690 self.logger = util.get_logger(name) 

1691 self.packetizer.set_log(self.logger) 

1692 

1693 def get_log_channel(self): 

1694 """ 

1695 Return the channel name used for this transport's logging. 

1696 

1697 :return: channel name as a `str` 

1698 

1699 .. versionadded:: 1.2 

1700 """ 

1701 return self.log_name 

1702 

1703 def set_hexdump(self, hexdump): 

1704 """ 

1705 Turn on/off logging a hex dump of protocol traffic at DEBUG level in 

1706 the logs. Normally you would want this off (which is the default), 

1707 but if you are debugging something, it may be useful. 

1708 

1709 :param bool hexdump: 

1710 ``True`` to log protocol traffix (in hex) to the log; ``False`` 

1711 otherwise. 

1712 """ 

1713 self.packetizer.set_hexdump(hexdump) 

1714 

1715 def get_hexdump(self): 

1716 """ 

1717 Return ``True`` if the transport is currently logging hex dumps of 

1718 protocol traffic. 

1719 

1720 :return: ``True`` if hex dumps are being logged, else ``False``. 

1721 

1722 .. versionadded:: 1.4 

1723 """ 

1724 return self.packetizer.get_hexdump() 

1725 

1726 def use_compression(self, compress=True): 

1727 """ 

1728 Turn on/off compression. This will only have an affect before starting 

1729 the transport (ie before calling `connect`, etc). By default, 

1730 compression is off since it negatively affects interactive sessions. 

1731 

1732 :param bool compress: 

1733 ``True`` to ask the remote client/server to compress traffic; 

1734 ``False`` to refuse compression 

1735 

1736 .. versionadded:: 1.5.2 

1737 """ 

1738 if compress: 

1739 self._preferred_compression = ("zlib@openssh.com", "zlib", "none") 

1740 else: 

1741 self._preferred_compression = ("none",) 

1742 

1743 def getpeername(self): 

1744 """ 

1745 Return the address of the remote side of this Transport, if possible. 

1746 

1747 This is effectively a wrapper around ``getpeername`` on the underlying 

1748 socket. If the socket-like object has no ``getpeername`` method, then 

1749 ``("unknown", 0)`` is returned. 

1750 

1751 :return: 

1752 the address of the remote host, if known, as a ``(str, int)`` 

1753 tuple. 

1754 """ 

1755 gp = getattr(self.sock, "getpeername", None) 

1756 if gp is None: 

1757 return "unknown", 0 

1758 return gp() 

1759 

1760 def stop_thread(self): 

1761 self.active = False 

1762 self.packetizer.close() 

1763 # Keep trying to join() our main thread, quickly, until: 

1764 # * We join()ed successfully (self.is_alive() == False) 

1765 # * Or it looks like we've hit issue #520 (socket.recv hitting some 

1766 # race condition preventing it from timing out correctly), wherein 

1767 # our socket and packetizer are both closed (but where we'd 

1768 # otherwise be sitting forever on that recv()). 

1769 while ( 

1770 self.is_alive() 

1771 and self is not threading.current_thread() 

1772 and not self.sock._closed 

1773 and not self.packetizer.closed 

1774 ): 

1775 self.join(0.1) 

1776 

1777 # internals... 

1778 

1779 # TODO (backwards incompat): make a public alias for this because multiple 

1780 # other classes already explicitly rely on it...or just rewrite logging :D 

1781 def _log(self, level, msg, *args): 

1782 if issubclass(type(msg), list): 

1783 for m in msg: 

1784 self.logger.log(level, m) 

1785 else: 

1786 self.logger.log(level, msg, *args) 

1787 

1788 def _get_modulus_pack(self): 

1789 """used by KexGex to find primes for group exchange""" 

1790 return self._modulus_pack 

1791 

1792 def _next_channel(self): 

1793 """you are holding the lock""" 

1794 chanid = self._channel_counter 

1795 while self._channels.get(chanid) is not None: 

1796 self._channel_counter = (self._channel_counter + 1) & 0xFFFFFF 

1797 chanid = self._channel_counter 

1798 self._channel_counter = (self._channel_counter + 1) & 0xFFFFFF 

1799 return chanid 

1800 

1801 def _unlink_channel(self, chanid): 

1802 """used by a Channel to remove itself from the active channel list""" 

1803 self._channels.delete(chanid) 

1804 

1805 def _send_message(self, data): 

1806 self.packetizer.send_message(data) 

1807 

1808 def _send_user_message(self, data): 

1809 """ 

1810 send a message, but block if we're in key negotiation. this is used 

1811 for user-initiated requests. 

1812 """ 

1813 start = time.time() 

1814 while True: 

1815 self.clear_to_send.wait(0.1) 

1816 if not self.active: 

1817 self._log( 

1818 DEBUG, "Dropping user packet because connection is dead." 

1819 ) # noqa 

1820 return 

1821 self.clear_to_send_lock.acquire() 

1822 if self.clear_to_send.is_set(): 

1823 break 

1824 self.clear_to_send_lock.release() 

1825 if time.time() > start + self.clear_to_send_timeout: 

1826 raise SSHException( 

1827 "Key-exchange timed out waiting for key negotiation" 

1828 ) # noqa 

1829 try: 

1830 self._send_message(data) 

1831 finally: 

1832 self.clear_to_send_lock.release() 

1833 

1834 def _set_K_H(self, k, h): 

1835 """ 

1836 Used by a kex obj to set the K (root key) and H (exchange hash). 

1837 """ 

1838 self.K = k 

1839 self.H = h 

1840 if self.session_id is None: 

1841 self.session_id = h 

1842 

1843 def _expect_packet(self, *ptypes): 

1844 """ 

1845 Used by a kex obj to register the next packet type it expects to see. 

1846 """ 

1847 self._expected_packet = tuple(ptypes) 

1848 

1849 def _verify_key(self, host_key, sig): 

1850 key: PKey = self._key_info[self.host_key_type](Message(host_key)) 

1851 if key is None: 

1852 raise SSHException("Unknown host key type") 

1853 # TODO: like, here, can a host offer "ssh-rsa" but request SHA2, or are 

1854 # those baked in? 

1855 if not key.verify_ssh_sig(self.H, Message(sig)): 

1856 raise SSHException( 

1857 "Signature verification ({}) failed.".format( 

1858 self.host_key_type 

1859 ) 

1860 ) # noqa 

1861 self.host_key = key 

1862 

1863 def _compute_key(self, id, nbytes): 

1864 """id is 'A' - 'F' for the various keys used by ssh""" 

1865 m = Message() 

1866 m.add_mpint(self.K) 

1867 m.add_bytes(self.H) 

1868 m.add_byte(b(id)) 

1869 m.add_bytes(self.session_id) 

1870 # Fallback to SHA1 for kex engines that fail to specify a hex 

1871 # algorithm, or for e.g. transport tests that don't run kexinit. 

1872 hash_algo = getattr(self.kex_engine, "hash_algo", None) 

1873 hash_select_msg = "kex engine {} specified hash_algo {!r}".format( 

1874 self.kex_engine.__class__.__name__, hash_algo 

1875 ) 

1876 if hash_algo is None: 

1877 hash_algo = sha1 

1878 hash_select_msg += ", falling back to sha1" 

1879 if not hasattr(self, "_logged_hash_selection"): 

1880 self._log(DEBUG, hash_select_msg) 

1881 setattr(self, "_logged_hash_selection", True) 

1882 out = sofar = hash_algo(m.asbytes()).digest() 

1883 while len(out) < nbytes: 

1884 m = Message() 

1885 m.add_mpint(self.K) 

1886 m.add_bytes(self.H) 

1887 m.add_bytes(sofar) 

1888 digest = hash_algo(m.asbytes()).digest() 

1889 out += digest 

1890 sofar += digest 

1891 return out[:nbytes] 

1892 

1893 def _get_engine(self, name, key, iv=None, operation=None, aead=False): 

1894 if name not in self._cipher_info: 

1895 raise SSHException("Unknown cipher " + name) 

1896 info = self._cipher_info[name] 

1897 algorithm = info["class"](key) 

1898 # AEAD types (eg GCM) use their algorithm class /as/ the encryption 

1899 # engine (they expose the same encrypt/decrypt API as a CipherContext) 

1900 if aead: 

1901 return algorithm 

1902 # All others go through the Cipher class. 

1903 cipher = Cipher( 

1904 algorithm=algorithm, 

1905 # TODO: why is this getting tickled in aesgcm mode??? 

1906 mode=info["mode"](iv), 

1907 backend=default_backend(), 

1908 ) 

1909 if operation is self._ENCRYPT: 

1910 return cipher.encryptor() 

1911 else: 

1912 return cipher.decryptor() 

1913 

1914 def _set_forward_agent_handler(self, handler): 

1915 if handler is None: 

1916 

1917 def default_handler(channel): 

1918 self._queue_incoming_channel(channel) 

1919 

1920 self._forward_agent_handler = default_handler 

1921 else: 

1922 self._forward_agent_handler = handler 

1923 

1924 def _set_x11_handler(self, handler): 

1925 # only called if a channel has turned on x11 forwarding 

1926 if handler is None: 

1927 # by default, use the same mechanism as accept() 

1928 def default_handler(channel, src_addr_port): 

1929 self._queue_incoming_channel(channel) 

1930 

1931 self._x11_handler = default_handler 

1932 else: 

1933 self._x11_handler = handler 

1934 

1935 def _queue_incoming_channel(self, channel): 

1936 self.lock.acquire() 

1937 try: 

1938 self.server_accepts.append(channel) 

1939 self.server_accept_cv.notify() 

1940 finally: 

1941 self.lock.release() 

1942 

1943 def _sanitize_window_size(self, window_size): 

1944 if window_size is None: 

1945 window_size = self.default_window_size 

1946 return clamp_value(MIN_WINDOW_SIZE, window_size, MAX_WINDOW_SIZE) 

1947 

1948 def _sanitize_packet_size(self, max_packet_size): 

1949 if max_packet_size is None: 

1950 max_packet_size = self.default_max_packet_size 

1951 return clamp_value(MIN_PACKET_SIZE, max_packet_size, MAX_WINDOW_SIZE) 

1952 

1953 def _ensure_authed(self, ptype, message): 

1954 """ 

1955 Checks message type against current auth state. 

1956 

1957 If server mode, and auth has not succeeded, and the message is of a 

1958 post-auth type (channel open or global request) an appropriate error 

1959 response Message is crafted and returned to caller for sending. 

1960 

1961 Otherwise (client mode, authed, or pre-auth message) returns None. 

1962 """ 

1963 if ( 

1964 not self.server_mode 

1965 or ptype <= HIGHEST_USERAUTH_MESSAGE_ID 

1966 or self.is_authenticated() 

1967 ): 

1968 return None 

1969 # WELP. We must be dealing with someone trying to do non-auth things 

1970 # without being authed. Tell them off, based on message class. 

1971 reply = Message() 

1972 # Global requests have no details, just failure. 

1973 if ptype == MSG_GLOBAL_REQUEST: 

1974 reply.add_byte(cMSG_REQUEST_FAILURE) 

1975 # Channel opens let us reject w/ a specific type + message. 

1976 elif ptype == MSG_CHANNEL_OPEN: 

1977 kind = message.get_text() # noqa 

1978 chanid = message.get_int() 

1979 reply.add_byte(cMSG_CHANNEL_OPEN_FAILURE) 

1980 reply.add_int(chanid) 

1981 reply.add_int(OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED) 

1982 reply.add_string("") 

1983 reply.add_string("en") 

1984 # NOTE: Post-open channel messages do not need checking; the above will 

1985 # reject attempts to open channels, meaning that even if a malicious 

1986 # user tries to send a MSG_CHANNEL_REQUEST, it will simply fall under 

1987 # the logic that handles unknown channel IDs (as the channel list will 

1988 # be empty.) 

1989 return reply 

1990 

1991 def _enforce_strict_kex(self, ptype): 

1992 """ 

1993 Conditionally raise `MessageOrderError` during strict initial kex. 

1994 

1995 This method should only be called inside code that handles non-KEXINIT 

1996 messages; it does not interrogate ``ptype`` besides using it to log 

1997 more accurately. 

1998 """ 

1999 if self.agreed_on_strict_kex and not self.initial_kex_done: 

2000 name = MSG_NAMES.get(ptype, f"msg {ptype}") 

2001 raise MessageOrderError( 

2002 f"In strict-kex mode, but was sent {name!r}!" 

2003 ) 

2004 

2005 def run(self): 

2006 # (use the exposed "run" method, because if we specify a thread target 

2007 # of a private method, threading.Thread will keep a reference to it 

2008 # indefinitely, creating a GC cycle and not letting Transport ever be 

2009 # GC'd. it's a bug in Thread.) 

2010 

2011 # Hold reference to 'sys' so we can test sys.modules to detect 

2012 # interpreter shutdown. 

2013 self.sys = sys 

2014 

2015 # active=True occurs before the thread is launched, to avoid a race 

2016 _active_threads.append(self) 

2017 tid = hex(id(self) & xffffffff) 

2018 if self.server_mode: 

2019 self._log(DEBUG, "starting thread (server mode): {}".format(tid)) 

2020 else: 

2021 self._log(DEBUG, "starting thread (client mode): {}".format(tid)) 

2022 try: 

2023 try: 

2024 self.packetizer.write_all(b(self.local_version + "\r\n")) 

2025 self._log( 

2026 DEBUG, 

2027 "Local version/idstring: {}".format(self.local_version), 

2028 ) # noqa 

2029 self._check_banner() 

2030 # The above is actually very much part of the handshake, but 

2031 # sometimes the banner can be read but the machine is not 

2032 # responding, for example when the remote ssh daemon is loaded 

2033 # in to memory but we can not read from the disk/spawn a new 

2034 # shell. 

2035 # Make sure we can specify a timeout for the initial handshake. 

2036 # Reuse the banner timeout for now. 

2037 self.packetizer.start_handshake(self.handshake_timeout) 

2038 self._send_kex_init() 

2039 self._expect_packet(MSG_KEXINIT) 

2040 

2041 while self.active: 

2042 if self.packetizer.need_rekey() and not self.in_kex: 

2043 self._send_kex_init() 

2044 try: 

2045 ptype, m = self.packetizer.read_message() 

2046 except NeedRekeyException: 

2047 continue 

2048 if ptype == MSG_IGNORE: 

2049 self._enforce_strict_kex(ptype) 

2050 continue 

2051 elif ptype == MSG_DISCONNECT: 

2052 self._parse_disconnect(m) 

2053 break 

2054 elif ptype == MSG_DEBUG: 

2055 self._enforce_strict_kex(ptype) 

2056 self._parse_debug(m) 

2057 continue 

2058 if len(self._expected_packet) > 0: 

2059 if ptype not in self._expected_packet: 

2060 exc_class = SSHException 

2061 if self.agreed_on_strict_kex: 

2062 exc_class = MessageOrderError 

2063 raise exc_class( 

2064 "Expecting packet from {!r}, got {:d}".format( 

2065 self._expected_packet, ptype 

2066 ) 

2067 ) # noqa 

2068 self._expected_packet = tuple() 

2069 # These message IDs indicate key exchange & will differ 

2070 # depending on exact exchange algorithm 

2071 if (ptype >= 30) and (ptype <= 41): 

2072 self.kex_engine.parse_next(ptype, m) 

2073 continue 

2074 

2075 if ptype in self._handler_table: 

2076 error_msg = self._ensure_authed(ptype, m) 

2077 if error_msg: 

2078 self._send_message(error_msg) 

2079 else: 

2080 self._handler_table[ptype](m) 

2081 elif ptype in self._channel_handler_table: 

2082 chanid = m.get_int() 

2083 chan = self._channels.get(chanid) 

2084 if chan is not None: 

2085 self._channel_handler_table[ptype](chan, m) 

2086 elif chanid in self.channels_seen: 

2087 self._log( 

2088 DEBUG, 

2089 "Ignoring message for dead channel {:d}".format( # noqa 

2090 chanid 

2091 ), 

2092 ) 

2093 else: 

2094 self._log( 

2095 ERROR, 

2096 "Channel request for unknown channel {:d}".format( # noqa 

2097 chanid 

2098 ), 

2099 ) 

2100 break 

2101 elif ( 

2102 self.auth_handler is not None 

2103 and ptype in self.auth_handler._handler_table 

2104 ): 

2105 handler = self.auth_handler._handler_table[ptype] 

2106 handler(m) 

2107 if len(self._expected_packet) > 0: 

2108 continue 

2109 else: 

2110 # Respond with "I don't implement this particular 

2111 # message type" message (unless the message type was 

2112 # itself literally MSG_UNIMPLEMENTED, in which case, we 

2113 # just shut up to avoid causing a useless loop). 

2114 name = MSG_NAMES[ptype] 

2115 warning = "Oops, unhandled type {} ({!r})".format( 

2116 ptype, name 

2117 ) 

2118 self._log(WARNING, warning) 

2119 if ptype != MSG_UNIMPLEMENTED: 

2120 msg = Message() 

2121 msg.add_byte(cMSG_UNIMPLEMENTED) 

2122 msg.add_int(m.seqno) 

2123 self._send_message(msg) 

2124 self.packetizer.complete_handshake() 

2125 except SSHException as e: 

2126 self._log( 

2127 ERROR, 

2128 "Exception ({}): {}".format( 

2129 "server" if self.server_mode else "client", e 

2130 ), 

2131 ) 

2132 self._log(ERROR, util.tb_strings()) 

2133 self.saved_exception = e 

2134 except EOFError as e: 

2135 self._log(DEBUG, "EOF in transport thread") 

2136 self.saved_exception = e 

2137 except socket.error as e: 

2138 if type(e.args) is tuple: 

2139 if e.args: 

2140 emsg = "{} ({:d})".format(e.args[1], e.args[0]) 

2141 else: # empty tuple, e.g. socket.timeout 

2142 emsg = str(e) or repr(e) 

2143 else: 

2144 emsg = e.args 

2145 self._log(ERROR, "Socket exception: " + emsg) 

2146 self.saved_exception = e 

2147 except Exception as e: 

2148 self._log(ERROR, "Unknown exception: " + str(e)) 

2149 self._log(ERROR, util.tb_strings()) 

2150 self.saved_exception = e 

2151 _active_threads.remove(self) 

2152 for chan in list(self._channels.values()): 

2153 chan._unlink() 

2154 if self.active: 

2155 self.active = False 

2156 self.packetizer.close() 

2157 if self.completion_event is not None: 

2158 self.completion_event.set() 

2159 if self.auth_handler is not None: 

2160 self.auth_handler.abort() 

2161 for event in self.channel_events.values(): 

2162 event.set() 

2163 try: 

2164 self.lock.acquire() 

2165 self.server_accept_cv.notify() 

2166 finally: 

2167 self.lock.release() 

2168 self.sock.close() 

2169 except: 

2170 # Don't raise spurious 'NoneType has no attribute X' errors when we 

2171 # wake up during interpreter shutdown. Or rather -- raise 

2172 # everything *if* sys.modules (used as a convenient sentinel) 

2173 # appears to still exist. 

2174 if self.sys.modules is not None: 

2175 raise 

2176 

2177 def _log_agreement(self, which, local, remote): 

2178 # Log useful, non-duplicative line re: an agreed-upon algorithm. 

2179 # Old code implied algorithms could be asymmetrical (different for 

2180 # inbound vs outbound) so we preserve that possibility. 

2181 msg = "{}: ".format(which) 

2182 if local == remote: 

2183 msg += local 

2184 else: 

2185 msg += "local={}, remote={}".format(local, remote) 

2186 self._log(DEBUG, msg) 

2187 

2188 # protocol stages 

2189 

2190 def _negotiate_keys(self, m): 

2191 # throws SSHException on anything unusual 

2192 self.clear_to_send_lock.acquire() 

2193 try: 

2194 self.clear_to_send.clear() 

2195 finally: 

2196 self.clear_to_send_lock.release() 

2197 if self.local_kex_init is None: 

2198 # remote side wants to renegotiate 

2199 self._send_kex_init() 

2200 self._parse_kex_init(m) 

2201 self.kex_engine.start_kex() 

2202 

2203 def _check_banner(self): 

2204 # this is slow, but we only have to do it once 

2205 for i in range(100): 

2206 # give them 15 seconds for the first line, then just 2 seconds 

2207 # each additional line. (some sites have very high latency.) 

2208 if i == 0: 

2209 timeout = self.banner_timeout 

2210 else: 

2211 timeout = 2 

2212 try: 

2213 buf = self.packetizer.readline(timeout) 

2214 except ProxyCommandFailure: 

2215 raise 

2216 except Exception as e: 

2217 raise SSHException( 

2218 "Error reading SSH protocol banner" + str(e) 

2219 ) 

2220 if buf[:4] == "SSH-": 

2221 break 

2222 self._log(DEBUG, "Banner: " + buf) 

2223 if buf[:4] != "SSH-": 

2224 raise SSHException('Indecipherable protocol version "' + buf + '"') 

2225 # save this server version string for later 

2226 self.remote_version = buf 

2227 self._log(DEBUG, "Remote version/idstring: {}".format(buf)) 

2228 # pull off any attached comment 

2229 # NOTE: comment used to be stored in a variable and then...never used. 

2230 # since 2003. ca 877cd974b8182d26fa76d566072917ea67b64e67 

2231 i = buf.find(" ") 

2232 if i >= 0: 

2233 buf = buf[:i] 

2234 # parse out version string and make sure it matches 

2235 segs = buf.split("-", 2) 

2236 if len(segs) < 3: 

2237 raise SSHException("Invalid SSH banner") 

2238 version = segs[1] 

2239 client = segs[2] 

2240 if version != "1.99" and version != "2.0": 

2241 msg = "Incompatible version ({} instead of 2.0)" 

2242 raise IncompatiblePeer(msg.format(version)) 

2243 msg = "Connected (version {}, client {})".format(version, client) 

2244 self._log(INFO, msg) 

2245 

2246 def _send_kex_init(self): 

2247 """ 

2248 announce to the other side that we'd like to negotiate keys, and what 

2249 kind of key negotiation we support. 

2250 """ 

2251 self.clear_to_send_lock.acquire() 

2252 try: 

2253 self.clear_to_send.clear() 

2254 finally: 

2255 self.clear_to_send_lock.release() 

2256 self.in_kex = True 

2257 kex_algos = list(self.preferred_kex) 

2258 if self.server_mode: 

2259 mp_required_prefix = "diffie-hellman-group-exchange-sha" 

2260 kex_mp = [k for k in kex_algos if k.startswith(mp_required_prefix)] 

2261 if (self._modulus_pack is None) and (len(kex_mp) > 0): 

2262 # can't do group-exchange if we don't have a pack of potential 

2263 # primes 

2264 pkex = [ 

2265 k 

2266 for k in self.get_security_options().kex 

2267 if not k.startswith(mp_required_prefix) 

2268 ] 

2269 self.get_security_options().kex = pkex 

2270 available_server_keys = list( 

2271 filter( 

2272 list(self.server_key_dict.keys()).__contains__, 

2273 # TODO: ensure tests will catch if somebody streamlines 

2274 # this by mistake - case is the admittedly silly one where 

2275 # the only calls to add_server_key() contain keys which 

2276 # were filtered out of the below via disabled_algorithms. 

2277 # If this is streamlined, we would then be allowing the 

2278 # disabled algorithm(s) for hostkey use 

2279 # TODO: honestly this prob just wants to get thrown out 

2280 # when we make kex configuration more straightforward 

2281 self.preferred_keys, 

2282 ) 

2283 ) 

2284 else: 

2285 available_server_keys = self.preferred_keys 

2286 # Signal support for MSG_EXT_INFO so server will send it to us. 

2287 # NOTE: doing this here handily means we don't even consider this 

2288 # value when agreeing on real kex algo to use (which is a common 

2289 # pitfall when adding this apparently). 

2290 kex_algos.append("ext-info-c") 

2291 

2292 # Similar to ext-info, but used in both server modes, so done outside 

2293 # of above if/else. 

2294 if self.advertise_strict_kex: 

2295 which = "s" if self.server_mode else "c" 

2296 kex_algos.append(f"kex-strict-{which}-v00@openssh.com") 

2297 

2298 m = Message() 

2299 m.add_byte(cMSG_KEXINIT) 

2300 m.add_bytes(os.urandom(16)) 

2301 m.add_list(kex_algos) 

2302 m.add_list(available_server_keys) 

2303 m.add_list(self.preferred_ciphers) 

2304 m.add_list(self.preferred_ciphers) 

2305 m.add_list(self.preferred_macs) 

2306 m.add_list(self.preferred_macs) 

2307 m.add_list(self.preferred_compression) 

2308 m.add_list(self.preferred_compression) 

2309 m.add_string(bytes()) 

2310 m.add_string(bytes()) 

2311 m.add_boolean(False) 

2312 m.add_int(0) 

2313 # save a copy for later (needed to compute a hash) 

2314 self.local_kex_init = self._latest_kex_init = m.asbytes() 

2315 self._send_message(m) 

2316 

2317 def _really_parse_kex_init(self, m, ignore_first_byte=False): 

2318 parsed = {} 

2319 if ignore_first_byte: 

2320 m.get_byte() 

2321 m.get_bytes(16) # cookie, discarded 

2322 parsed["kex_algo_list"] = m.get_list() 

2323 parsed["server_key_algo_list"] = m.get_list() 

2324 parsed["client_encrypt_algo_list"] = m.get_list() 

2325 parsed["server_encrypt_algo_list"] = m.get_list() 

2326 parsed["client_mac_algo_list"] = m.get_list() 

2327 parsed["server_mac_algo_list"] = m.get_list() 

2328 parsed["client_compress_algo_list"] = m.get_list() 

2329 parsed["server_compress_algo_list"] = m.get_list() 

2330 parsed["client_lang_list"] = m.get_list() 

2331 parsed["server_lang_list"] = m.get_list() 

2332 parsed["kex_follows"] = m.get_boolean() 

2333 m.get_int() # unused 

2334 return parsed 

2335 

2336 def _get_latest_kex_init(self): 

2337 return self._really_parse_kex_init( 

2338 Message(self._latest_kex_init), 

2339 ignore_first_byte=True, 

2340 ) 

2341 

2342 def _parse_kex_init(self, m): 

2343 parsed = self._really_parse_kex_init(m) 

2344 kex_algo_list = parsed["kex_algo_list"] 

2345 server_key_algo_list = parsed["server_key_algo_list"] 

2346 client_encrypt_algo_list = parsed["client_encrypt_algo_list"] 

2347 server_encrypt_algo_list = parsed["server_encrypt_algo_list"] 

2348 client_mac_algo_list = parsed["client_mac_algo_list"] 

2349 server_mac_algo_list = parsed["server_mac_algo_list"] 

2350 client_compress_algo_list = parsed["client_compress_algo_list"] 

2351 server_compress_algo_list = parsed["server_compress_algo_list"] 

2352 client_lang_list = parsed["client_lang_list"] 

2353 server_lang_list = parsed["server_lang_list"] 

2354 kex_follows = parsed["kex_follows"] 

2355 

2356 self._log(DEBUG, "=== Key exchange possibilities ===") 

2357 for prefix, value in ( 

2358 ("kex algos", kex_algo_list), 

2359 ("server key", server_key_algo_list), 

2360 # TODO: shouldn't these two lines say "cipher" to match usual 

2361 # terminology (including elsewhere in paramiko!)? 

2362 ("client encrypt", client_encrypt_algo_list), 

2363 ("server encrypt", server_encrypt_algo_list), 

2364 ("client mac", client_mac_algo_list), 

2365 ("server mac", server_mac_algo_list), 

2366 ("client compress", client_compress_algo_list), 

2367 ("server compress", server_compress_algo_list), 

2368 ("client lang", client_lang_list), 

2369 ("server lang", server_lang_list), 

2370 ): 

2371 if value == [""]: 

2372 value = ["<none>"] 

2373 value = ", ".join(value) 

2374 self._log(DEBUG, "{}: {}".format(prefix, value)) 

2375 self._log(DEBUG, "kex follows: {}".format(kex_follows)) 

2376 self._log(DEBUG, "=== Key exchange agreements ===") 

2377 

2378 # Record, and strip out, ext-info and/or strict-kex non-algorithms 

2379 self._remote_ext_info = None 

2380 self._remote_strict_kex = None 

2381 to_pop = [] 

2382 for i, algo in enumerate(kex_algo_list): 

2383 if algo.startswith("ext-info-"): 

2384 self._remote_ext_info = algo 

2385 to_pop.insert(0, i) 

2386 elif algo.startswith("kex-strict-"): 

2387 # NOTE: this is what we are expecting from the /remote/ end. 

2388 which = "c" if self.server_mode else "s" 

2389 expected = f"kex-strict-{which}-v00@openssh.com" 

2390 # Set strict mode if agreed. 

2391 self.agreed_on_strict_kex = ( 

2392 algo == expected and self.advertise_strict_kex 

2393 ) 

2394 self._log( 

2395 DEBUG, f"Strict kex mode: {self.agreed_on_strict_kex}" 

2396 ) 

2397 to_pop.insert(0, i) 

2398 for i in to_pop: 

2399 kex_algo_list.pop(i) 

2400 

2401 # CVE mitigation: expect zeroed-out seqno anytime we are performing kex 

2402 # init phase, if strict mode was negotiated. 

2403 if ( 

2404 self.agreed_on_strict_kex 

2405 and not self.initial_kex_done 

2406 and m.seqno != 0 

2407 ): 

2408 raise MessageOrderError( 

2409 "In strict-kex mode, but KEXINIT was not the first packet!" 

2410 ) 

2411 

2412 # as a server, we pick the first item in the client's list that we 

2413 # support. 

2414 # as a client, we pick the first item in our list that the server 

2415 # supports. 

2416 if self.server_mode: 

2417 agreed_kex = list( 

2418 filter(self.preferred_kex.__contains__, kex_algo_list) 

2419 ) 

2420 else: 

2421 agreed_kex = list( 

2422 filter(kex_algo_list.__contains__, self.preferred_kex) 

2423 ) 

2424 if len(agreed_kex) == 0: 

2425 # TODO: do an auth-overhaul style aggregate exception here? 

2426 # TODO: would let us streamline log output & show all failures up 

2427 # front 

2428 raise IncompatiblePeer( 

2429 "Incompatible ssh peer (no acceptable kex algorithm)" 

2430 ) # noqa 

2431 self.kex_engine = self._kex_info[agreed_kex[0]](self) 

2432 self._log(DEBUG, "Kex: {}".format(agreed_kex[0])) 

2433 

2434 if self.server_mode: 

2435 available_server_keys = list( 

2436 filter( 

2437 list(self.server_key_dict.keys()).__contains__, 

2438 self.preferred_keys, 

2439 ) 

2440 ) 

2441 agreed_keys = list( 

2442 filter( 

2443 available_server_keys.__contains__, server_key_algo_list 

2444 ) 

2445 ) 

2446 else: 

2447 agreed_keys = list( 

2448 filter(server_key_algo_list.__contains__, self.preferred_keys) 

2449 ) 

2450 if len(agreed_keys) == 0: 

2451 raise IncompatiblePeer( 

2452 "Incompatible ssh peer (no acceptable host key)" 

2453 ) # noqa 

2454 self.host_key_type = agreed_keys[0] 

2455 if self.server_mode and (self.get_server_key() is None): 

2456 raise IncompatiblePeer( 

2457 "Incompatible ssh peer (can't match requested host key type)" 

2458 ) # noqa 

2459 self._log_agreement("HostKey", agreed_keys[0], agreed_keys[0]) 

2460 

2461 if self.server_mode: 

2462 agreed_local_ciphers = list( 

2463 filter( 

2464 self.preferred_ciphers.__contains__, 

2465 server_encrypt_algo_list, 

2466 ) 

2467 ) 

2468 agreed_remote_ciphers = list( 

2469 filter( 

2470 self.preferred_ciphers.__contains__, 

2471 client_encrypt_algo_list, 

2472 ) 

2473 ) 

2474 else: 

2475 agreed_local_ciphers = list( 

2476 filter( 

2477 client_encrypt_algo_list.__contains__, 

2478 self.preferred_ciphers, 

2479 ) 

2480 ) 

2481 agreed_remote_ciphers = list( 

2482 filter( 

2483 server_encrypt_algo_list.__contains__, 

2484 self.preferred_ciphers, 

2485 ) 

2486 ) 

2487 if len(agreed_local_ciphers) == 0 or len(agreed_remote_ciphers) == 0: 

2488 raise IncompatiblePeer( 

2489 "Incompatible ssh server (no acceptable ciphers)" 

2490 ) # noqa 

2491 self.local_cipher = agreed_local_ciphers[0] 

2492 self.remote_cipher = agreed_remote_ciphers[0] 

2493 self._log_agreement( 

2494 "Cipher", local=self.local_cipher, remote=self.remote_cipher 

2495 ) 

2496 

2497 if self.server_mode: 

2498 agreed_remote_macs = list( 

2499 filter(self.preferred_macs.__contains__, client_mac_algo_list) 

2500 ) 

2501 agreed_local_macs = list( 

2502 filter(self.preferred_macs.__contains__, server_mac_algo_list) 

2503 ) 

2504 else: 

2505 agreed_local_macs = list( 

2506 filter(client_mac_algo_list.__contains__, self.preferred_macs) 

2507 ) 

2508 agreed_remote_macs = list( 

2509 filter(server_mac_algo_list.__contains__, self.preferred_macs) 

2510 ) 

2511 if (len(agreed_local_macs) == 0) or (len(agreed_remote_macs) == 0): 

2512 raise IncompatiblePeer( 

2513 "Incompatible ssh server (no acceptable macs)" 

2514 ) 

2515 self.local_mac = agreed_local_macs[0] 

2516 self.remote_mac = agreed_remote_macs[0] 

2517 self._log_agreement( 

2518 "MAC", local=self.local_mac, remote=self.remote_mac 

2519 ) 

2520 

2521 if self.server_mode: 

2522 agreed_remote_compression = list( 

2523 filter( 

2524 self.preferred_compression.__contains__, 

2525 client_compress_algo_list, 

2526 ) 

2527 ) 

2528 agreed_local_compression = list( 

2529 filter( 

2530 self.preferred_compression.__contains__, 

2531 server_compress_algo_list, 

2532 ) 

2533 ) 

2534 else: 

2535 agreed_local_compression = list( 

2536 filter( 

2537 client_compress_algo_list.__contains__, 

2538 self.preferred_compression, 

2539 ) 

2540 ) 

2541 agreed_remote_compression = list( 

2542 filter( 

2543 server_compress_algo_list.__contains__, 

2544 self.preferred_compression, 

2545 ) 

2546 ) 

2547 if ( 

2548 len(agreed_local_compression) == 0 

2549 or len(agreed_remote_compression) == 0 

2550 ): 

2551 msg = "Incompatible ssh server (no acceptable compression)" 

2552 msg += " {!r} {!r} {!r}" 

2553 raise IncompatiblePeer( 

2554 msg.format( 

2555 agreed_local_compression, 

2556 agreed_remote_compression, 

2557 self.preferred_compression, 

2558 ) 

2559 ) 

2560 self.local_compression = agreed_local_compression[0] 

2561 self.remote_compression = agreed_remote_compression[0] 

2562 self._log_agreement( 

2563 "Compression", 

2564 local=self.local_compression, 

2565 remote=self.remote_compression, 

2566 ) 

2567 self._log(DEBUG, "=== End of kex handshake ===") 

2568 

2569 # save for computing hash later... 

2570 # now wait! openssh has a bug (and others might too) where there are 

2571 # actually some extra bytes (one NUL byte in openssh's case) added to 

2572 # the end of the packet but not parsed. turns out we need to throw 

2573 # away those bytes because they aren't part of the hash. 

2574 self.remote_kex_init = cMSG_KEXINIT + m.get_so_far() 

2575 

2576 def _activate_inbound(self): 

2577 """switch on newly negotiated encryption parameters for 

2578 inbound traffic""" 

2579 info = self._cipher_info[self.remote_cipher] 

2580 aead = info.get("is_aead", False) 

2581 block_size = info["block-size"] 

2582 key_size = info["key-size"] 

2583 # Non-AEAD/GCM type ciphers' IV size is their block size. 

2584 iv_size = info.get("iv-size", block_size) 

2585 if self.server_mode: 

2586 iv_in = self._compute_key("A", iv_size) 

2587 key_in = self._compute_key("C", key_size) 

2588 else: 

2589 iv_in = self._compute_key("B", iv_size) 

2590 key_in = self._compute_key("D", key_size) 

2591 

2592 engine = self._get_engine( 

2593 name=self.remote_cipher, 

2594 key=key_in, 

2595 iv=iv_in, 

2596 operation=self._DECRYPT, 

2597 aead=aead, 

2598 ) 

2599 etm = (not aead) and "etm@openssh.com" in self.remote_mac 

2600 mac_size = self._mac_info[self.remote_mac]["size"] 

2601 mac_engine = self._mac_info[self.remote_mac]["class"] 

2602 # initial mac keys are done in the hash's natural size (not the 

2603 # potentially truncated transmission size) 

2604 if self.server_mode: 

2605 mac_key = self._compute_key("E", mac_engine().digest_size) 

2606 else: 

2607 mac_key = self._compute_key("F", mac_engine().digest_size) 

2608 

2609 self.packetizer.set_inbound_cipher( 

2610 block_engine=engine, 

2611 block_size=block_size, 

2612 mac_engine=None if aead else mac_engine, 

2613 mac_size=16 if aead else mac_size, 

2614 mac_key=None if aead else mac_key, 

2615 etm=etm, 

2616 aead=aead, 

2617 iv_in=iv_in if aead else None, 

2618 ) 

2619 

2620 compress_in = self._compression_info[self.remote_compression][1] 

2621 if compress_in is not None and ( 

2622 self.remote_compression != "zlib@openssh.com" or self.authenticated 

2623 ): 

2624 self._log(DEBUG, "Switching on inbound compression ...") 

2625 self.packetizer.set_inbound_compressor(compress_in()) 

2626 # Reset inbound sequence number if strict mode. 

2627 if self.agreed_on_strict_kex: 

2628 self._log( 

2629 DEBUG, 

2630 "Resetting inbound seqno after NEWKEYS due to strict mode", 

2631 ) 

2632 self.packetizer.reset_seqno_in() 

2633 

2634 def _activate_outbound(self): 

2635 """switch on newly negotiated encryption parameters for 

2636 outbound traffic""" 

2637 m = Message() 

2638 m.add_byte(cMSG_NEWKEYS) 

2639 self._send_message(m) 

2640 # Reset outbound sequence number if strict mode. 

2641 if self.agreed_on_strict_kex: 

2642 self._log( 

2643 DEBUG, 

2644 "Resetting outbound seqno after NEWKEYS due to strict mode", 

2645 ) 

2646 self.packetizer.reset_seqno_out() 

2647 info = self._cipher_info[self.local_cipher] 

2648 aead = info.get("is_aead", False) 

2649 block_size = info["block-size"] 

2650 key_size = info["key-size"] 

2651 # Non-AEAD/GCM type ciphers' IV size is their block size. 

2652 iv_size = info.get("iv-size", block_size) 

2653 if self.server_mode: 

2654 iv_out = self._compute_key("B", iv_size) 

2655 key_out = self._compute_key("D", key_size) 

2656 else: 

2657 iv_out = self._compute_key("A", iv_size) 

2658 key_out = self._compute_key("C", key_size) 

2659 

2660 engine = self._get_engine( 

2661 name=self.local_cipher, 

2662 key=key_out, 

2663 iv=iv_out, 

2664 operation=self._ENCRYPT, 

2665 aead=aead, 

2666 ) 

2667 etm = (not aead) and "etm@openssh.com" in self.local_mac 

2668 mac_size = self._mac_info[self.local_mac]["size"] 

2669 mac_engine = self._mac_info[self.local_mac]["class"] 

2670 # initial mac keys are done in the hash's natural size (not the 

2671 # potentially truncated transmission size) 

2672 if self.server_mode: 

2673 mac_key = self._compute_key("F", mac_engine().digest_size) 

2674 else: 

2675 mac_key = self._compute_key("E", mac_engine().digest_size) 

2676 sdctr = self.local_cipher.endswith("-ctr") 

2677 

2678 self.packetizer.set_outbound_cipher( 

2679 block_engine=engine, 

2680 block_size=block_size, 

2681 mac_engine=None if aead else mac_engine, 

2682 mac_size=16 if aead else mac_size, 

2683 mac_key=None if aead else mac_key, 

2684 sdctr=sdctr, 

2685 etm=etm, 

2686 aead=aead, 

2687 iv_out=iv_out if aead else None, 

2688 ) 

2689 

2690 compress_out = self._compression_info[self.local_compression][0] 

2691 if compress_out is not None and ( 

2692 self.local_compression != "zlib@openssh.com" or self.authenticated 

2693 ): 

2694 self._log(DEBUG, "Switching on outbound compression ...") 

2695 self.packetizer.set_outbound_compressor(compress_out()) 

2696 if not self.packetizer.need_rekey(): 

2697 self.in_kex = False 

2698 # If client indicated extension support, send that packet immediately 

2699 if ( 

2700 self.server_mode 

2701 and self.server_sig_algs 

2702 and self._remote_ext_info == "ext-info-c" 

2703 ): 

2704 extensions = {"server-sig-algs": ",".join(self.preferred_pubkeys)} 

2705 m = Message() 

2706 m.add_byte(cMSG_EXT_INFO) 

2707 m.add_int(len(extensions)) 

2708 for name, value in sorted(extensions.items()): 

2709 m.add_string(name) 

2710 m.add_string(value) 

2711 self._send_message(m) 

2712 # we always expect to receive NEWKEYS now 

2713 self._expect_packet(MSG_NEWKEYS) 

2714 

2715 def _auth_trigger(self): 

2716 self.authenticated = True 

2717 # delayed initiation of compression 

2718 if self.local_compression == "zlib@openssh.com": 

2719 compress_out = self._compression_info[self.local_compression][0] 

2720 self._log(DEBUG, "Switching on outbound compression ...") 

2721 self.packetizer.set_outbound_compressor(compress_out()) 

2722 if self.remote_compression == "zlib@openssh.com": 

2723 compress_in = self._compression_info[self.remote_compression][1] 

2724 self._log(DEBUG, "Switching on inbound compression ...") 

2725 self.packetizer.set_inbound_compressor(compress_in()) 

2726 

2727 def _parse_ext_info(self, msg): 

2728 # Packet is a count followed by that many key-string to possibly-bytes 

2729 # pairs. 

2730 extensions = {} 

2731 for _ in range(msg.get_int()): 

2732 name = msg.get_text() 

2733 value = msg.get_string() 

2734 extensions[name] = value 

2735 self._log(DEBUG, "Got EXT_INFO: {}".format(extensions)) 

2736 # NOTE: this should work ok in cases where a server sends /two/ such 

2737 # messages; the RFC explicitly states a 2nd one should overwrite the 

2738 # 1st. 

2739 self.server_extensions = extensions 

2740 

2741 def _parse_newkeys(self, m): 

2742 self._log(DEBUG, "Switch to new keys ...") 

2743 self._activate_inbound() 

2744 # can also free a bunch of stuff here 

2745 self.local_kex_init = self.remote_kex_init = None 

2746 self.K = None 

2747 self.kex_engine = None 

2748 if self.server_mode and (self.auth_handler is None): 

2749 # create auth handler for server mode 

2750 self.auth_handler = AuthHandler(self) 

2751 if not self.initial_kex_done: 

2752 # this was the first key exchange 

2753 # (also signal to packetizer as it sometimes wants to know this 

2754 # status as well, eg when seqnos rollover) 

2755 self.initial_kex_done = self.packetizer._initial_kex_done = True 

2756 # send an event? 

2757 if self.completion_event is not None: 

2758 self.completion_event.set() 

2759 # it's now okay to send data again (if this was a re-key) 

2760 if not self.packetizer.need_rekey(): 

2761 self.in_kex = False 

2762 self.clear_to_send_lock.acquire() 

2763 try: 

2764 self.clear_to_send.set() 

2765 finally: 

2766 self.clear_to_send_lock.release() 

2767 return 

2768 

2769 def _parse_disconnect(self, m): 

2770 code = m.get_int() 

2771 desc = m.get_text() 

2772 self._log(INFO, "Disconnect (code {:d}): {}".format(code, desc)) 

2773 

2774 def _parse_global_request(self, m): 

2775 kind = m.get_text() 

2776 self._log(DEBUG, 'Received global request "{}"'.format(kind)) 

2777 want_reply = m.get_boolean() 

2778 if not self.server_mode: 

2779 self._log( 

2780 DEBUG, 

2781 'Rejecting "{}" global request from server.'.format(kind), 

2782 ) 

2783 ok = False 

2784 elif kind == "tcpip-forward": 

2785 address = m.get_text() 

2786 port = m.get_int() 

2787 ok = self.server_object.check_port_forward_request(address, port) 

2788 if ok: 

2789 ok = (ok,) 

2790 elif kind == "cancel-tcpip-forward": 

2791 address = m.get_text() 

2792 port = m.get_int() 

2793 self.server_object.cancel_port_forward_request(address, port) 

2794 ok = True 

2795 else: 

2796 ok = self.server_object.check_global_request(kind, m) 

2797 extra = () 

2798 if type(ok) is tuple: 

2799 extra = ok 

2800 ok = True 

2801 if want_reply: 

2802 msg = Message() 

2803 if ok: 

2804 msg.add_byte(cMSG_REQUEST_SUCCESS) 

2805 msg.add(*extra) 

2806 else: 

2807 msg.add_byte(cMSG_REQUEST_FAILURE) 

2808 self._send_message(msg) 

2809 

2810 def _parse_request_success(self, m): 

2811 self._log(DEBUG, "Global request successful.") 

2812 self.global_response = m 

2813 if self.completion_event is not None: 

2814 self.completion_event.set() 

2815 

2816 def _parse_request_failure(self, m): 

2817 self._log(DEBUG, "Global request denied.") 

2818 self.global_response = None 

2819 if self.completion_event is not None: 

2820 self.completion_event.set() 

2821 

2822 def _parse_channel_open_success(self, m): 

2823 chanid = m.get_int() 

2824 server_chanid = m.get_int() 

2825 server_window_size = m.get_int() 

2826 server_max_packet_size = m.get_int() 

2827 chan = self._channels.get(chanid) 

2828 if chan is None: 

2829 self._log(WARNING, "Success for unrequested channel! [??]") 

2830 return 

2831 self.lock.acquire() 

2832 try: 

2833 chan._set_remote_channel( 

2834 server_chanid, server_window_size, server_max_packet_size 

2835 ) 

2836 self._log(DEBUG, "Secsh channel {:d} opened.".format(chanid)) 

2837 if chanid in self.channel_events: 

2838 self.channel_events[chanid].set() 

2839 del self.channel_events[chanid] 

2840 finally: 

2841 self.lock.release() 

2842 return 

2843 

2844 def _parse_channel_open_failure(self, m): 

2845 chanid = m.get_int() 

2846 reason = m.get_int() 

2847 reason_str = m.get_text() 

2848 m.get_text() # ignored language 

2849 reason_text = CONNECTION_FAILED_CODE.get(reason, "(unknown code)") 

2850 self._log( 

2851 ERROR, 

2852 "Secsh channel {:d} open FAILED: {}: {}".format( 

2853 chanid, reason_str, reason_text 

2854 ), 

2855 ) 

2856 self.lock.acquire() 

2857 try: 

2858 self.saved_exception = ChannelException(reason, reason_text) 

2859 if chanid in self.channel_events: 

2860 self._channels.delete(chanid) 

2861 if chanid in self.channel_events: 

2862 self.channel_events[chanid].set() 

2863 del self.channel_events[chanid] 

2864 finally: 

2865 self.lock.release() 

2866 return 

2867 

2868 def _parse_channel_open(self, m): 

2869 kind = m.get_text() 

2870 chanid = m.get_int() 

2871 initial_window_size = m.get_int() 

2872 max_packet_size = m.get_int() 

2873 reject = False 

2874 if ( 

2875 kind == "auth-agent@openssh.com" 

2876 and self._forward_agent_handler is not None 

2877 ): 

2878 self._log(DEBUG, "Incoming forward agent connection") 

2879 self.lock.acquire() 

2880 try: 

2881 my_chanid = self._next_channel() 

2882 finally: 

2883 self.lock.release() 

2884 elif (kind == "x11") and (self._x11_handler is not None): 

2885 origin_addr = m.get_text() 

2886 origin_port = m.get_int() 

2887 self._log( 

2888 DEBUG, 

2889 "Incoming x11 connection from {}:{:d}".format( 

2890 origin_addr, origin_port 

2891 ), 

2892 ) 

2893 self.lock.acquire() 

2894 try: 

2895 my_chanid = self._next_channel() 

2896 finally: 

2897 self.lock.release() 

2898 elif (kind == "forwarded-tcpip") and (self._tcp_handler is not None): 

2899 server_addr = m.get_text() 

2900 server_port = m.get_int() 

2901 origin_addr = m.get_text() 

2902 origin_port = m.get_int() 

2903 self._log( 

2904 DEBUG, 

2905 "Incoming tcp forwarded connection from {}:{:d}".format( 

2906 origin_addr, origin_port 

2907 ), 

2908 ) 

2909 self.lock.acquire() 

2910 try: 

2911 my_chanid = self._next_channel() 

2912 finally: 

2913 self.lock.release() 

2914 elif not self.server_mode: 

2915 self._log( 

2916 DEBUG, 

2917 'Rejecting "{}" channel request from server.'.format(kind), 

2918 ) 

2919 reject = True 

2920 reason = OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED 

2921 else: 

2922 self.lock.acquire() 

2923 try: 

2924 my_chanid = self._next_channel() 

2925 finally: 

2926 self.lock.release() 

2927 if kind == "direct-tcpip": 

2928 # handle direct-tcpip requests coming from the client 

2929 dest_addr = m.get_text() 

2930 dest_port = m.get_int() 

2931 origin_addr = m.get_text() 

2932 origin_port = m.get_int() 

2933 reason = self.server_object.check_channel_direct_tcpip_request( 

2934 my_chanid, 

2935 (origin_addr, origin_port), 

2936 (dest_addr, dest_port), 

2937 ) 

2938 else: 

2939 reason = self.server_object.check_channel_request( 

2940 kind, my_chanid 

2941 ) 

2942 if reason != OPEN_SUCCEEDED: 

2943 self._log( 

2944 DEBUG, 

2945 'Rejecting "{}" channel request from client.'.format(kind), 

2946 ) 

2947 reject = True 

2948 if reject: 

2949 msg = Message() 

2950 msg.add_byte(cMSG_CHANNEL_OPEN_FAILURE) 

2951 msg.add_int(chanid) 

2952 msg.add_int(reason) 

2953 msg.add_string("") 

2954 msg.add_string("en") 

2955 self._send_message(msg) 

2956 return 

2957 

2958 chan = Channel(my_chanid) 

2959 self.lock.acquire() 

2960 try: 

2961 self._channels.put(my_chanid, chan) 

2962 self.channels_seen[my_chanid] = True 

2963 chan._set_transport(self) 

2964 chan._set_window( 

2965 self.default_window_size, self.default_max_packet_size 

2966 ) 

2967 chan._set_remote_channel( 

2968 chanid, initial_window_size, max_packet_size 

2969 ) 

2970 finally: 

2971 self.lock.release() 

2972 m = Message() 

2973 m.add_byte(cMSG_CHANNEL_OPEN_SUCCESS) 

2974 m.add_int(chanid) 

2975 m.add_int(my_chanid) 

2976 m.add_int(self.default_window_size) 

2977 m.add_int(self.default_max_packet_size) 

2978 self._send_message(m) 

2979 self._log( 

2980 DEBUG, "Secsh channel {:d} ({}) opened.".format(my_chanid, kind) 

2981 ) 

2982 if kind == "auth-agent@openssh.com": 

2983 self._forward_agent_handler(chan) 

2984 elif kind == "x11": 

2985 self._x11_handler(chan, (origin_addr, origin_port)) 

2986 elif kind == "forwarded-tcpip": 

2987 chan.origin_addr = (origin_addr, origin_port) 

2988 self._tcp_handler( 

2989 chan, (origin_addr, origin_port), (server_addr, server_port) 

2990 ) 

2991 else: 

2992 self._queue_incoming_channel(chan) 

2993 

2994 def _parse_debug(self, m): 

2995 m.get_boolean() # always_display 

2996 msg = m.get_string() 

2997 m.get_string() # language 

2998 self._log(DEBUG, "Debug msg: {}".format(util.safe_string(msg))) 

2999 

3000 def _get_subsystem_handler(self, name): 

3001 try: 

3002 self.lock.acquire() 

3003 if name not in self.subsystem_table: 

3004 return None, [], {} 

3005 return self.subsystem_table[name] 

3006 finally: 

3007 self.lock.release() 

3008 

3009 _channel_handler_table = { 

3010 MSG_CHANNEL_SUCCESS: Channel._request_success, 

3011 MSG_CHANNEL_FAILURE: Channel._request_failed, 

3012 MSG_CHANNEL_DATA: Channel._feed, 

3013 MSG_CHANNEL_EXTENDED_DATA: Channel._feed_extended, 

3014 MSG_CHANNEL_WINDOW_ADJUST: Channel._window_adjust, 

3015 MSG_CHANNEL_REQUEST: Channel._handle_request, 

3016 MSG_CHANNEL_EOF: Channel._handle_eof, 

3017 MSG_CHANNEL_CLOSE: Channel._handle_close, 

3018 } 

3019 

3020 

3021# TODO (backwards incompat): drop this, we barely use it ourselves, it badly 

3022# replicates the Transport-internal algorithm management, AND does so in a way 

3023# which doesn't honor newer things like disabled_algorithms! 

3024class SecurityOptions: 

3025 """ 

3026 Simple object containing the security preferences of an ssh transport. 

3027 These are tuples of acceptable ciphers, digests, key types, and key 

3028 exchange algorithms, listed in order of preference. 

3029 

3030 Changing the contents and/or order of these fields affects the underlying 

3031 `.Transport` (but only if you change them before starting the session). 

3032 If you try to add an algorithm that paramiko doesn't recognize, 

3033 ``ValueError`` will be raised. If you try to assign something besides a 

3034 tuple to one of the fields, ``TypeError`` will be raised. 

3035 """ 

3036 

3037 __slots__ = "_transport" 

3038 

3039 def __init__(self, transport): 

3040 self._transport = transport 

3041 

3042 def __repr__(self): 

3043 """ 

3044 Returns a string representation of this object, for debugging. 

3045 """ 

3046 return "<paramiko.SecurityOptions for {!r}>".format(self._transport) 

3047 

3048 def _set(self, name, orig, x): 

3049 if type(x) is list: 

3050 x = tuple(x) 

3051 if type(x) is not tuple: 

3052 raise TypeError("expected tuple or list") 

3053 possible = list(getattr(self._transport, orig).keys()) 

3054 forbidden = [n for n in x if n not in possible] 

3055 if len(forbidden) > 0: 

3056 raise ValueError("unknown cipher") 

3057 setattr(self._transport, name, x) 

3058 

3059 @property 

3060 def ciphers(self): 

3061 """Symmetric encryption ciphers""" 

3062 return self._transport._preferred_ciphers 

3063 

3064 @ciphers.setter 

3065 def ciphers(self, x): 

3066 self._set("_preferred_ciphers", "_cipher_info", x) 

3067 

3068 @property 

3069 def digests(self): 

3070 """Digest (one-way hash) algorithms""" 

3071 return self._transport._preferred_macs 

3072 

3073 @digests.setter 

3074 def digests(self, x): 

3075 self._set("_preferred_macs", "_mac_info", x) 

3076 

3077 @property 

3078 def key_types(self): 

3079 """Public-key algorithms""" 

3080 return self._transport._preferred_keys 

3081 

3082 @key_types.setter 

3083 def key_types(self, x): 

3084 # TODO: so this reads Transport._key_info.keys(), yells if any values 

3085 # in `x` /aren't/ in that list, then overwrites 

3086 # Transport._preferred_keys with `x`... 

3087 # TODO: so you can read this pretty simply as "replace 

3088 # transport._preferred_keys with x". 

3089 # TODO: which is...bad...in cases where SSHClient is trying to simply 

3090 # load up known_hosts or system known hosts, and use those to determine 

3091 # which hostkey /algorithms/ it is willing to accept 

3092 self._set("_preferred_keys", "_key_info", x) 

3093 

3094 @property 

3095 def kex(self): 

3096 """Key exchange algorithms""" 

3097 return self._transport._preferred_kex 

3098 

3099 @kex.setter 

3100 def kex(self, x): 

3101 self._set("_preferred_kex", "_kex_info", x) 

3102 

3103 @property 

3104 def compression(self): 

3105 """Compression algorithms""" 

3106 return self._transport._preferred_compression 

3107 

3108 @compression.setter 

3109 def compression(self, x): 

3110 self._set("_preferred_compression", "_compression_info", x) 

3111 

3112 

3113class ChannelMap: 

3114 def __init__(self): 

3115 # (id -> Channel) 

3116 self._map = weakref.WeakValueDictionary() 

3117 self._lock = threading.Lock() 

3118 

3119 def put(self, chanid, chan): 

3120 self._lock.acquire() 

3121 try: 

3122 self._map[chanid] = chan 

3123 finally: 

3124 self._lock.release() 

3125 

3126 def get(self, chanid): 

3127 self._lock.acquire() 

3128 try: 

3129 return self._map.get(chanid, None) 

3130 finally: 

3131 self._lock.release() 

3132 

3133 def delete(self, chanid): 

3134 self._lock.acquire() 

3135 try: 

3136 try: 

3137 del self._map[chanid] 

3138 except KeyError: 

3139 pass 

3140 finally: 

3141 self._lock.release() 

3142 

3143 def values(self): 

3144 self._lock.acquire() 

3145 try: 

3146 return list(self._map.values()) 

3147 finally: 

3148 self._lock.release() 

3149 

3150 def __len__(self): 

3151 self._lock.acquire() 

3152 try: 

3153 return len(self._map) 

3154 finally: 

3155 self._lock.release() 

3156 

3157 

3158class ServiceRequestingTransport(Transport): 

3159 """ 

3160 Transport, but also handling service requests, like it oughtta! 

3161 

3162 .. versionadded:: 3.2 

3163 """ 

3164 

3165 # NOTE: this purposefully duplicates some of the parent class in order to 

3166 # modernize, refactor, etc. The intent is that eventually we will collapse 

3167 # this one onto the parent in a backwards incompatible release. 

3168 

3169 def __init__(self, *args, **kwargs): 

3170 super().__init__(*args, **kwargs) 

3171 self._service_userauth_accepted = False 

3172 self._handler_table[MSG_SERVICE_ACCEPT] = self._parse_service_accept 

3173 

3174 def _parse_service_accept(self, m): 

3175 service = m.get_text() 

3176 # Short-circuit for any service name not ssh-userauth. 

3177 # NOTE: it's technically possible for 'service name' in 

3178 # SERVICE_REQUEST/ACCEPT messages to be "ssh-connection" -- 

3179 # but I don't see evidence of Paramiko ever initiating or expecting to 

3180 # receive one of these. We /do/ see the 'service name' field in 

3181 # MSG_USERAUTH_REQUEST/ACCEPT/FAILURE set to this string, but that is a 

3182 # different set of handlers, so...! 

3183 if service != "ssh-userauth": 

3184 self._log( 

3185 # TODO (backwards incompat): consider erroring here (with an 

3186 # ability to opt out?) instead as it probably means something 

3187 # went Very Wrong. 

3188 DEBUG, 

3189 'Service request "{}" accepted (?)'.format(service), 

3190 ) 

3191 return 

3192 # Record that we saw a service-userauth acceptance, meaning we are free 

3193 # to submit auth requests. 

3194 self._service_userauth_accepted = True 

3195 self._log(DEBUG, "MSG_SERVICE_ACCEPT received; auth may begin") 

3196 

3197 def ensure_session(self): 

3198 # Make sure we're not trying to auth on a not-yet-open or 

3199 # already-closed transport session; that's our responsibility, not that 

3200 # of AuthHandler. 

3201 if (not self.active) or (not self.initial_kex_done): 

3202 # TODO: better error message? this can happen in many places, eg 

3203 # user error (authing before connecting) or developer error (some 

3204 # improperly handled pre/mid auth shutdown didn't become fatal 

3205 # enough). The latter is much more common & should ideally be fixed 

3206 # by terminating things harder? 

3207 raise SSHException("No existing session") 

3208 # Also make sure we've actually been told we are allowed to auth. 

3209 if self._service_userauth_accepted: 

3210 return 

3211 # Or request to do so, otherwise. 

3212 m = Message() 

3213 m.add_byte(cMSG_SERVICE_REQUEST) 

3214 m.add_string("ssh-userauth") 

3215 self._log(DEBUG, "Sending MSG_SERVICE_REQUEST: ssh-userauth") 

3216 self._send_message(m) 

3217 # Now we wait to hear back; the user is expecting a blocking-style auth 

3218 # request so there's no point giving control back anywhere. 

3219 while not self._service_userauth_accepted: 

3220 # TODO: feels like we're missing an AuthHandler Event like 

3221 # 'self.auth_event' which is set when AuthHandler shuts down in 

3222 # ways good AND bad. Transport only seems to have completion_event 

3223 # which is unclear re: intent, eg it's set by newkeys which always 

3224 # happens on connection, so it'll always be set by the time we get 

3225 # here. 

3226 # NOTE: this copies the timing of event.wait() in 

3227 # AuthHandler.wait_for_response, re: 1/10 of a second. Could 

3228 # presumably be smaller, but seems unlikely this period is going to 

3229 # be "too long" for any code doing ssh networking... 

3230 time.sleep(0.1) 

3231 self.auth_handler = self.get_auth_handler() 

3232 

3233 def get_auth_handler(self): 

3234 # NOTE: using new sibling subclass instead of classic AuthHandler 

3235 return AuthOnlyHandler(self) 

3236 

3237 def auth_none(self, username): 

3238 # TODO (backwards incompat): merge to parent, preserving (most of) 

3239 # docstring 

3240 self.ensure_session() 

3241 return self.auth_handler.auth_none(username) 

3242 

3243 def auth_password(self, username, password, fallback=True): 

3244 # TODO (backwards incompat): merge to parent, preserving (most of) 

3245 # docstring 

3246 self.ensure_session() 

3247 try: 

3248 return self.auth_handler.auth_password(username, password) 

3249 except BadAuthenticationType as e: 

3250 # if password auth isn't allowed, but keyboard-interactive *is*, 

3251 # try to fudge it 

3252 if not fallback or ("keyboard-interactive" not in e.allowed_types): 

3253 raise 

3254 try: 

3255 

3256 def handler(title, instructions, fields): 

3257 if len(fields) > 1: 

3258 raise SSHException("Fallback authentication failed.") 

3259 if len(fields) == 0: 

3260 # for some reason, at least on os x, a 2nd request will 

3261 # be made with zero fields requested. maybe it's just 

3262 # to try to fake out automated scripting of the exact 

3263 # type we're doing here. *shrug* :) 

3264 return [] 

3265 return [password] 

3266 

3267 return self.auth_interactive(username, handler) 

3268 except SSHException: 

3269 # attempt to fudge failed; just raise the original exception 

3270 raise e 

3271 

3272 def auth_publickey(self, username, key): 

3273 # TODO (backwards incompat): merge to parent, preserving (most of) 

3274 # docstring 

3275 self.ensure_session() 

3276 return self.auth_handler.auth_publickey(username, key) 

3277 

3278 def auth_interactive(self, username, handler, submethods=""): 

3279 # TODO (backwards incompat): merge to parent, preserving (most of) 

3280 # docstring 

3281 self.ensure_session() 

3282 return self.auth_handler.auth_interactive( 

3283 username, handler, submethods 

3284 ) 

3285 

3286 def auth_interactive_dumb(self, username, handler=None, submethods=""): 

3287 # TODO (backwards incompat): merge to parent, preserving (most of) 

3288 # docstring 

3289 # NOTE: legacy impl omitted equiv of ensure_session since it just wraps 

3290 # another call to an auth method. however we reinstate it for 

3291 # consistency reasons. 

3292 self.ensure_session() 

3293 if not handler: 

3294 

3295 def handler(title, instructions, prompt_list): 

3296 answers = [] 

3297 if title: 

3298 print(title.strip()) 

3299 if instructions: 

3300 print(instructions.strip()) 

3301 for prompt, show_input in prompt_list: 

3302 print(prompt.strip(), end=" ") 

3303 answers.append(input()) 

3304 return answers 

3305 

3306 return self.auth_interactive(username, handler, submethods)