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

1390 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.kex_mlkem import KexMLKEM768X25519 

105from paramiko.message import Message 

106from paramiko.packet import NeedRekeyException, Packetizer 

107from paramiko.pkey import PKey 

108from paramiko.primes import ModulusPack 

109from paramiko.rsakey import RSAKey 

110from paramiko.server import ServerInterface 

111from paramiko.sftp_client import SFTPClient 

112from paramiko.ssh_exception import ( 

113 BadAuthenticationType, 

114 ChannelException, 

115 IncompatiblePeer, 

116 MessageOrderError, 

117 ProxyCommandFailure, 

118 SSHException, 

119) 

120from paramiko.util import ( 

121 ClosingContextManager, 

122 b, 

123 clamp_value, 

124) 

125 

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

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

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

129# in cryptography==48.0.0. 

130# 

131# Source References: 

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

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

134try: 

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

136except ImportError: 

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

138 

139 

140# for thread cleanup 

141_active_threads = [] 

142 

143 

144def _join_lingering_threads(): 

145 for thr in _active_threads: 

146 thr.stop_thread() 

147 

148 

149import atexit 

150 

151atexit.register(_join_lingering_threads) 

152 

153 

154class Transport(threading.Thread, ClosingContextManager): 

155 """ 

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

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

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

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

160 forwardings). 

161 

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

163 """ 

164 

165 _ENCRYPT = object() 

166 _DECRYPT = object() 

167 

168 _PROTO_ID = "2.0" 

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

170 

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

172 # reorder without reason! 

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

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

175 # instead of monkeypatching or subclassing. 

176 _preferred_ciphers = ( 

177 "aes128-ctr", 

178 "aes192-ctr", 

179 "aes256-ctr", 

180 "aes128-cbc", 

181 "aes192-cbc", 

182 "aes256-cbc", 

183 "3des-cbc", 

184 "aes128-gcm@openssh.com", 

185 "aes256-gcm@openssh.com", 

186 ) 

187 _preferred_macs = ( 

188 "hmac-sha2-256", 

189 "hmac-sha2-512", 

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

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

192 "hmac-sha1", 

193 "hmac-md5", 

194 "hmac-sha1-96", 

195 "hmac-md5-96", 

196 ) 

197 # ~= HostKeyAlgorithms in OpenSSH land 

198 _preferred_keys = ( 

199 "ssh-ed25519", 

200 "ecdsa-sha2-nistp256", 

201 "ecdsa-sha2-nistp384", 

202 "ecdsa-sha2-nistp521", 

203 "rsa-sha2-512", 

204 "rsa-sha2-256", 

205 ) 

206 # ~= PubkeyAcceptedAlgorithms 

207 _preferred_pubkeys = ( 

208 "ssh-ed25519", 

209 "ecdsa-sha2-nistp256", 

210 "ecdsa-sha2-nistp384", 

211 "ecdsa-sha2-nistp521", 

212 "rsa-sha2-512", 

213 "rsa-sha2-256", 

214 ) 

215 _preferred_kex = ( 

216 "ecdh-sha2-nistp256", 

217 "ecdh-sha2-nistp384", 

218 "ecdh-sha2-nistp521", 

219 "diffie-hellman-group16-sha512", 

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

221 "diffie-hellman-group14-sha256", 

222 ) 

223 if KexCurve25519.is_available(): 

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

225 if KexMLKEM768X25519.is_available(): 

226 _preferred_kex = ("mlkem768x25519-sha256",) + _preferred_kex 

227 _preferred_compression = ("none",) 

228 

229 _cipher_info = { 

230 "aes128-ctr": { 

231 "class": algorithms.AES, 

232 "mode": modes.CTR, 

233 "block-size": 16, 

234 "key-size": 16, 

235 }, 

236 "aes192-ctr": { 

237 "class": algorithms.AES, 

238 "mode": modes.CTR, 

239 "block-size": 16, 

240 "key-size": 24, 

241 }, 

242 "aes256-ctr": { 

243 "class": algorithms.AES, 

244 "mode": modes.CTR, 

245 "block-size": 16, 

246 "key-size": 32, 

247 }, 

248 "aes128-cbc": { 

249 "class": algorithms.AES, 

250 "mode": modes.CBC, 

251 "block-size": 16, 

252 "key-size": 16, 

253 }, 

254 "aes192-cbc": { 

255 "class": algorithms.AES, 

256 "mode": modes.CBC, 

257 "block-size": 16, 

258 "key-size": 24, 

259 }, 

260 "aes256-cbc": { 

261 "class": algorithms.AES, 

262 "mode": modes.CBC, 

263 "block-size": 16, 

264 "key-size": 32, 

265 }, 

266 "3des-cbc": { 

267 "class": TripleDES, 

268 "mode": modes.CBC, 

269 "block-size": 8, 

270 "key-size": 24, 

271 }, 

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

273 "class": aead.AESGCM, 

274 "block-size": 16, 

275 "iv-size": 12, 

276 "key-size": 16, 

277 "is_aead": True, 

278 }, 

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

280 "class": aead.AESGCM, 

281 "block-size": 16, 

282 "iv-size": 12, 

283 "key-size": 32, 

284 "is_aead": True, 

285 }, 

286 } 

287 

288 _mac_info = { 

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

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

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

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

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

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

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

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

297 } 

298 

299 _key_info = { 

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

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

302 # TODO: NO!!! good. 

303 # TODO: it's used in: 

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

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

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

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

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

309 # - AuthHandler._generate_key_from_request - server-side auth 

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

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

312 "rsa-sha2-256": RSAKey, 

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

314 "rsa-sha2-512": RSAKey, 

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

316 "ecdsa-sha2-nistp256": ECDSAKey, 

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

318 "ecdsa-sha2-nistp384": ECDSAKey, 

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

320 "ecdsa-sha2-nistp521": ECDSAKey, 

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

322 "ssh-ed25519": Ed25519Key, 

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

324 } 

325 

326 _kex_info = { 

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

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

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

330 "ecdh-sha2-nistp256": KexNistp256, 

331 "ecdh-sha2-nistp384": KexNistp384, 

332 "ecdh-sha2-nistp521": KexNistp521, 

333 } 

334 if KexCurve25519.is_available(): 

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

336 if KexMLKEM768X25519.is_available(): 

337 _kex_info["mlkem768x25519-sha256"] = KexMLKEM768X25519 

338 

339 _compression_info = { 

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

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

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

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

344 "zlib": (ZlibCompressor, ZlibDecompressor), 

345 "none": (None, None), 

346 } 

347 

348 _modulus_pack = None 

349 _active_check_timeout = 0.1 

350 

351 def __init__( 

352 self, 

353 sock, 

354 default_window_size=DEFAULT_WINDOW_SIZE, 

355 default_max_packet_size=DEFAULT_MAX_PACKET_SIZE, 

356 disabled_algorithms=None, 

357 server_sig_algs=True, 

358 strict_kex=True, 

359 packetizer_class=None, 

360 ): 

361 """ 

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

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

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

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

366 

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

368 methods: 

369 

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

371 an int representing the number of bytes written. Returns 

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

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

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

375 closed. 

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

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

378 

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

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

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

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

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

384 call may be thrown in this case. 

385 

386 .. note:: 

387 Modifying the the window and packet sizes might have adverse 

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

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

390 battle tested. 

391 

392 :param socket sock: 

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

394 :param int default_window_size: 

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

396 2097152) 

397 :param int default_max_packet_size: 

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

399 32768) 

400 :param dict disabled_algorithms: 

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

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

403 lifetime of the transport. 

404 

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

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

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

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

409 the matching attribute. 

410 

411 For example, if you need to disable 

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

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

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

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

416 :param bool server_sig_algs: 

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

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

419 ``True``. 

420 :param bool strict_kex: 

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

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

423 ``True``. 

424 :param packetizer_class: 

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

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

427 

428 .. versionchanged:: 1.15 

429 Added the ``default_window_size`` and ``default_max_packet_size`` 

430 arguments. 

431 .. versionchanged:: 2.6 

432 Added the ``disabled_algorithms`` kwarg. 

433 .. versionchanged:: 2.9 

434 Added the ``server_sig_algs`` kwarg. 

435 .. versionchanged:: 3.4 

436 Added the ``strict_kex`` kwarg. 

437 .. versionchanged:: 3.4 

438 Added the ``packetizer_class`` kwarg. 

439 """ 

440 self.active = False 

441 self.hostname = None 

442 self.server_extensions = {} 

443 self.advertise_strict_kex = strict_kex 

444 self.agreed_on_strict_kex = False 

445 

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

447 # many ways to do it! 

448 if isinstance(sock, str): 

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

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

451 self.hostname = hl[0] 

452 if len(hl) == 1: 

453 sock = (hl[0], 22) 

454 else: 

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

456 if type(sock) is tuple: 

457 # connect to the given (host, port) 

458 hostname, port = sock 

459 self.hostname = hostname 

460 reason = "No suitable address family" 

461 addrinfos = socket.getaddrinfo( 

462 hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM 

463 ) 

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

465 if socktype == socket.SOCK_STREAM: 

466 af = family 

467 # addr = sockaddr 

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

469 try: 

470 sock.connect((hostname, port)) 

471 except socket.error as e: 

472 reason = str(e) 

473 else: 

474 break 

475 else: 

476 raise SSHException( 

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

478 ) 

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

480 threading.Thread.__init__(self) 

481 self.daemon = True 

482 self.sock = sock 

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

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

485 self.sock.settimeout(self._active_check_timeout) 

486 

487 # negotiated crypto parameters 

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

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

490 self.remote_version = "" 

491 self.local_cipher = self.remote_cipher = "" 

492 self.local_kex_init = self.remote_kex_init = None 

493 self.local_mac = self.remote_mac = None 

494 self.local_compression = self.remote_compression = None 

495 self.session_id = None 

496 self.host_key_type = None 

497 self.host_key = None 

498 

499 # state used during negotiation 

500 self.kex_engine = None 

501 self.H = None 

502 self.K = None 

503 

504 self.initial_kex_done = False 

505 self.in_kex = False 

506 self.authenticated = False 

507 self._expected_packet = tuple() 

508 # synchronization (always higher level than write_lock) 

509 self.lock = threading.Lock() 

510 

511 # tracking open channels 

512 self._channels = ChannelMap() 

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

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

515 self._channel_counter = 0 

516 self.default_max_packet_size = default_max_packet_size 

517 self.default_window_size = default_window_size 

518 self._forward_agent_handler = None 

519 self._x11_handler = None 

520 self._tcp_handler = None 

521 

522 self.saved_exception = None 

523 self.clear_to_send = threading.Event() 

524 self.clear_to_send_lock = threading.Lock() 

525 self.clear_to_send_timeout = 30.0 

526 self.log_name = "paramiko.transport" 

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

528 self.packetizer.set_log(self.logger) 

529 self.auth_handler = None 

530 # response Message from an arbitrary global request 

531 self.global_response = None 

532 # user-defined event callbacks 

533 self.completion_event = None 

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

535 self.banner_timeout = 15 

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

537 # banner sent. 

538 self.handshake_timeout = 15 

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

540 self.auth_timeout = 30 

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

542 self.channel_timeout = 60 * 60 

543 self.disabled_algorithms = disabled_algorithms or {} 

544 self.server_sig_algs = server_sig_algs 

545 

546 # server mode: 

547 self.server_mode = False 

548 self.server_object = None 

549 self.server_key_dict = {} 

550 self.server_accepts = [] 

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

552 self.subsystem_table = {} 

553 

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

555 # manipulation and subclass twiddling. 

556 self._handler_table = { 

557 MSG_EXT_INFO: self._parse_ext_info, 

558 MSG_NEWKEYS: self._parse_newkeys, 

559 MSG_GLOBAL_REQUEST: self._parse_global_request, 

560 MSG_REQUEST_SUCCESS: self._parse_request_success, 

561 MSG_REQUEST_FAILURE: self._parse_request_failure, 

562 MSG_CHANNEL_OPEN_SUCCESS: self._parse_channel_open_success, 

563 MSG_CHANNEL_OPEN_FAILURE: self._parse_channel_open_failure, 

564 MSG_CHANNEL_OPEN: self._parse_channel_open, 

565 MSG_KEXINIT: self._negotiate_keys, 

566 } 

567 

568 def _filter_algorithm(self, type_): 

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

570 return tuple( 

571 x 

572 for x in default 

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

574 ) 

575 

576 @property 

577 def preferred_ciphers(self): 

578 return self._filter_algorithm("ciphers") 

579 

580 @property 

581 def preferred_macs(self): 

582 return self._filter_algorithm("macs") 

583 

584 @property 

585 def preferred_keys(self): 

586 # Interleave cert variants here; resistant to various background 

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

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

589 # runtime 

590 filtered = self._filter_algorithm("keys") 

591 return tuple( 

592 filtered 

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

594 ) 

595 

596 @property 

597 def preferred_pubkeys(self): 

598 return self._filter_algorithm("pubkeys") 

599 

600 @property 

601 def preferred_kex(self): 

602 return self._filter_algorithm("kex") 

603 

604 @property 

605 def preferred_compression(self): 

606 return self._filter_algorithm("compression") 

607 

608 def __repr__(self): 

609 """ 

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

611 """ 

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

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

614 if not self.active: 

615 out += " (unconnected)" 

616 else: 

617 if self.local_cipher != "": 

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

619 self.local_cipher, 

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

621 ) 

622 if self.is_authenticated(): 

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

624 len(self._channels) 

625 ) 

626 elif self.initial_kex_done: 

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

628 else: 

629 out += " (connecting)" 

630 out += ">" 

631 return out 

632 

633 def atfork(self): 

634 """ 

635 Terminate this Transport without closing the session. On posix 

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

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

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

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

640 

641 .. versionadded:: 1.5.3 

642 """ 

643 self.sock.close() 

644 self.close() 

645 

646 def get_security_options(self): 

647 """ 

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

649 encryption algorithms this transport will permit (for encryption, 

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

651 of preference for them. 

652 """ 

653 return SecurityOptions(self) 

654 

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

656 """ 

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

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

659 negotiation. 

660 

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

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

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

664 

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

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

667 Otherwise an SSHException is raised. 

668 

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

670 calling `auth_password <Transport.auth_password>` or 

671 `auth_publickey <Transport.auth_publickey>`. 

672 

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

674 

675 .. note:: 

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

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

678 object. 

679 

680 :param .threading.Event event: 

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

682 

683 :param float timeout: 

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

685 

686 :raises: 

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

688 passed in) 

689 """ 

690 self.active = True 

691 if event is not None: 

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

693 self.completion_event = event 

694 self.start() 

695 return 

696 

697 # synchronous, wait for a result 

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

699 self.start() 

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

701 while True: 

702 event.wait(0.1) 

703 if not self.active: 

704 e = self.get_exception() 

705 if e is not None: 

706 raise e 

707 raise SSHException("Negotiation failed.") 

708 if event.is_set() or ( 

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

710 ): 

711 break 

712 

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

714 """ 

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

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

717 separate thread is created for protocol negotiation. 

718 

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

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

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

722 

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

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

725 Otherwise an SSHException is raised. 

726 

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

728 Override the methods `get_allowed_auths 

729 <.ServerInterface.get_allowed_auths>`, `check_auth_none 

730 <.ServerInterface.check_auth_none>`, `check_auth_password 

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

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

733 to control the authentication process. 

734 

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

736 channel. Override `check_channel_request 

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

738 object to allow channels to be opened. 

739 

740 .. note:: 

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

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

743 object. 

744 

745 :param .threading.Event event: 

746 an event to trigger when negotiation is complete. 

747 :param .ServerInterface server: 

748 an object used to perform authentication and create `channels 

749 <.Channel>` 

750 

751 :raises: 

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

753 passed in) 

754 """ 

755 if server is None: 

756 server = ServerInterface() 

757 self.server_mode = True 

758 self.server_object = server 

759 self.active = True 

760 if event is not None: 

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

762 self.completion_event = event 

763 self.start() 

764 return 

765 

766 # synchronous, wait for a result 

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

768 self.start() 

769 while True: 

770 event.wait(0.1) 

771 if not self.active: 

772 e = self.get_exception() 

773 if e is not None: 

774 raise e 

775 raise SSHException("Negotiation failed.") 

776 if event.is_set(): 

777 break 

778 

779 def add_server_key(self, key): 

780 """ 

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

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

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

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

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

786 

787 :param .PKey key: 

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

789 """ 

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

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

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

793 # names. 

794 if isinstance(key, RSAKey): 

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

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

797 

798 def get_server_key(self): 

799 """ 

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

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

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

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

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

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

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

807 the behavior is undefined. 

808 

809 :return: 

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

811 ``None``. 

812 """ 

813 try: 

814 return self.server_key_dict[self.host_key_type] 

815 except KeyError: 

816 pass 

817 return None 

818 

819 @staticmethod 

820 def load_server_moduli(filename=None): 

821 """ 

822 (optional) 

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

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

825 safely ignored. 

826 

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

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

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

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

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

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

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

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

835 support that method of key negotiation. 

836 

837 :param str filename: 

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

839 not in a standard location. 

840 :return: 

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

842 

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

844 """ 

845 Transport._modulus_pack = ModulusPack() 

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

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

848 if filename is not None: 

849 file_list.insert(0, filename) 

850 for fn in file_list: 

851 try: 

852 Transport._modulus_pack.read_file(fn) 

853 return True 

854 except IOError: 

855 pass 

856 # none succeeded 

857 Transport._modulus_pack = None 

858 return False 

859 

860 def close(self): 

861 """ 

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

863 """ 

864 if not self.active: 

865 return 

866 self.stop_thread() 

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

868 chan._unlink() 

869 self.sock.close() 

870 

871 def get_remote_server_key(self): 

872 """ 

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

874 

875 .. note:: 

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

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

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

879 

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

881 

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

883 """ 

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

885 raise SSHException("No existing session") 

886 return self.host_key 

887 

888 def is_active(self): 

889 """ 

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

891 

892 :return: 

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

894 closed 

895 """ 

896 return self.active 

897 

898 def open_session( 

899 self, window_size=None, max_packet_size=None, timeout=None 

900 ): 

901 """ 

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

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

904 ``"session"``. 

905 

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

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

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

909 

910 :param int window_size: 

911 optional window size for this session. 

912 :param int max_packet_size: 

913 optional max packet size for this session. 

914 

915 :return: a new `.Channel` 

916 

917 :raises: 

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

919 prematurely 

920 

921 .. versionchanged:: 1.13.4/1.14.3/1.15.3 

922 Added the ``timeout`` argument. 

923 .. versionchanged:: 1.15 

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

925 """ 

926 return self.open_channel( 

927 "session", 

928 window_size=window_size, 

929 max_packet_size=max_packet_size, 

930 timeout=timeout, 

931 ) 

932 

933 def open_x11_channel(self, src_addr=None): 

934 """ 

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

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

937 

938 :param tuple src_addr: 

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

940 x11 port, ie. 6010) 

941 :return: a new `.Channel` 

942 

943 :raises: 

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

945 prematurely 

946 """ 

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

948 

949 def open_forward_agent_channel(self): 

950 """ 

951 Request a new channel to the client, of type 

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

953 

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

955 

956 :return: a new `.Channel` 

957 

958 :raises: `.SSHException` -- 

959 if the request is rejected or the session ends prematurely 

960 """ 

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

962 

963 def open_forwarded_tcpip_channel(self, src_addr, dest_addr): 

964 """ 

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

966 

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

968 incoming connections back to the client. 

969 

970 :param src_addr: originator's address 

971 :param dest_addr: local (server) connected address 

972 """ 

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

974 

975 def open_channel( 

976 self, 

977 kind, 

978 dest_addr=None, 

979 src_addr=None, 

980 window_size=None, 

981 max_packet_size=None, 

982 timeout=None, 

983 ): 

984 """ 

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

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

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

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

989 

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

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

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

993 

994 :param str kind: 

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

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

997 :param tuple dest_addr: 

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

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

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

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

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

1003 :param int window_size: 

1004 optional window size for this session. 

1005 :param int max_packet_size: 

1006 optional max packet size for this session. 

1007 :param float timeout: 

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

1009 

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

1011 

1012 :raises: 

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

1014 prematurely or there is a timeout opening a channel 

1015 

1016 .. versionchanged:: 1.15 

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

1018 """ 

1019 if not self.active: 

1020 raise SSHException("SSH session not active") 

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

1022 self.lock.acquire() 

1023 try: 

1024 window_size = self._sanitize_window_size(window_size) 

1025 max_packet_size = self._sanitize_packet_size(max_packet_size) 

1026 chanid = self._next_channel() 

1027 m = Message() 

1028 m.add_byte(cMSG_CHANNEL_OPEN) 

1029 m.add_string(kind) 

1030 m.add_int(chanid) 

1031 m.add_int(window_size) 

1032 m.add_int(max_packet_size) 

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

1034 m.add_string(dest_addr[0]) 

1035 m.add_int(dest_addr[1]) 

1036 m.add_string(src_addr[0]) 

1037 m.add_int(src_addr[1]) 

1038 elif kind == "x11": 

1039 m.add_string(src_addr[0]) 

1040 m.add_int(src_addr[1]) 

1041 chan = Channel(chanid) 

1042 self._channels.put(chanid, chan) 

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

1044 self.channels_seen[chanid] = True 

1045 chan._set_transport(self) 

1046 chan._set_window(window_size, max_packet_size) 

1047 finally: 

1048 self.lock.release() 

1049 self._send_user_message(m) 

1050 start_ts = time.time() 

1051 while True: 

1052 event.wait(0.1) 

1053 if not self.active: 

1054 e = self.get_exception() 

1055 if e is None: 

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

1057 raise e 

1058 if event.is_set(): 

1059 break 

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

1061 raise SSHException("Timeout opening channel.") 

1062 chan = self._channels.get(chanid) 

1063 if chan is not None: 

1064 return chan 

1065 e = self.get_exception() 

1066 if e is None: 

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

1068 raise e 

1069 

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

1071 """ 

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

1073 the server, across this SSH session. 

1074 

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

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

1077 

1078 handler( 

1079 channel, 

1080 (origin_addr, origin_port), 

1081 (server_addr, server_port), 

1082 ) 

1083 

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

1085 the server was listening on. 

1086 

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

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

1089 `accept`. 

1090 

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

1092 :param int port: 

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

1094 :param callable handler: 

1095 optional handler for incoming forwarded connections, of the form 

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

1097 

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

1099 

1100 :raises: 

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

1102 """ 

1103 if not self.active: 

1104 raise SSHException("SSH session not active") 

1105 port = int(port) 

1106 response = self.global_request( 

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

1108 ) 

1109 if response is None: 

1110 raise SSHException("TCP forwarding request denied") 

1111 if port == 0: 

1112 port = response.get_int() 

1113 if handler is None: 

1114 

1115 def default_handler(channel, src_addr, dest_addr_port): 

1116 # src_addr, src_port = src_addr_port 

1117 # dest_addr, dest_port = dest_addr_port 

1118 self._queue_incoming_channel(channel) 

1119 

1120 handler = default_handler 

1121 self._tcp_handler = handler 

1122 return port 

1123 

1124 def cancel_port_forward(self, address, port): 

1125 """ 

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

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

1128 ssh connection. 

1129 

1130 :param str address: the address to stop forwarding 

1131 :param int port: the port to stop forwarding 

1132 """ 

1133 if not self.active: 

1134 return 

1135 self._tcp_handler = None 

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

1137 

1138 def open_sftp_client(self): 

1139 """ 

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

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

1142 `.SFTPClient` object will be returned. 

1143 

1144 :return: 

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

1146 this transport 

1147 """ 

1148 return SFTPClient.from_transport(self) 

1149 

1150 def send_ignore(self, byte_count=None): 

1151 """ 

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

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

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

1155 firewalls. 

1156 

1157 :param int byte_count: 

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

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

1160 """ 

1161 m = Message() 

1162 m.add_byte(cMSG_IGNORE) 

1163 if byte_count is None: 

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

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

1166 self._send_user_message(m) 

1167 

1168 def renegotiate_keys(self): 

1169 """ 

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

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

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

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

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

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

1176 

1177 :raises: 

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

1179 the session to end) 

1180 """ 

1181 self.completion_event = threading.Event() 

1182 self._send_kex_init() 

1183 while True: 

1184 self.completion_event.wait(0.1) 

1185 if not self.active: 

1186 e = self.get_exception() 

1187 if e is not None: 

1188 raise e 

1189 raise SSHException("Negotiation failed.") 

1190 if self.completion_event.is_set(): 

1191 break 

1192 return 

1193 

1194 def set_keepalive(self, interval): 

1195 """ 

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

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

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

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

1200 

1201 :param int interval: 

1202 seconds to wait before sending a keepalive packet (or 

1203 0 to disable keepalives). 

1204 """ 

1205 

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

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

1208 

1209 self.packetizer.set_keepalive(interval, _request) 

1210 

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

1212 """ 

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

1214 extensions to the SSH2 protocol. 

1215 

1216 :param str kind: name of the request. 

1217 :param tuple data: 

1218 an optional tuple containing additional data to attach to the 

1219 request. 

1220 :param bool wait: 

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

1222 received; ``False`` otherwise. 

1223 :return: 

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

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

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

1227 """ 

1228 if wait: 

1229 self.completion_event = threading.Event() 

1230 m = Message() 

1231 m.add_byte(cMSG_GLOBAL_REQUEST) 

1232 m.add_string(kind) 

1233 m.add_boolean(wait) 

1234 if data is not None: 

1235 m.add(*data) 

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

1237 self._send_user_message(m) 

1238 if not wait: 

1239 return None 

1240 while True: 

1241 self.completion_event.wait(0.1) 

1242 if not self.active: 

1243 return None 

1244 if self.completion_event.is_set(): 

1245 break 

1246 return self.global_response 

1247 

1248 def accept(self, timeout=None): 

1249 """ 

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

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

1252 ``None`` is returned. 

1253 

1254 :param int timeout: 

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

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

1257 """ 

1258 self.lock.acquire() 

1259 try: 

1260 if len(self.server_accepts) > 0: 

1261 chan = self.server_accepts.pop(0) 

1262 else: 

1263 self.server_accept_cv.wait(timeout) 

1264 if len(self.server_accepts) > 0: 

1265 chan = self.server_accepts.pop(0) 

1266 else: 

1267 # timeout 

1268 chan = None 

1269 finally: 

1270 self.lock.release() 

1271 return chan 

1272 

1273 def connect( 

1274 self, 

1275 hostkey=None, 

1276 username="", 

1277 password=None, 

1278 pkey=None, 

1279 ): 

1280 """ 

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

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

1283 for `start_client`, `get_remote_server_key`, and 

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

1285 methods if you want more control. 

1286 

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

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

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

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

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

1292 transfer. 

1293 

1294 .. note:: 

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

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

1297 fail because you haven't authenticated yet. 

1298 

1299 :param .PKey hostkey: 

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

1301 want to do host key verification. 

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

1303 :param str password: 

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

1305 authentication; otherwise ``None``. 

1306 :param .PKey pkey: 

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

1308 key authentication; otherwise ``None``. 

1309 

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

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

1312 """ 

1313 if hostkey is not None: 

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

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

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

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

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

1319 # = xxx, but different, which sucks sigh 

1320 if isinstance(hostkey, RSAKey): 

1321 self._preferred_keys = [ 

1322 "rsa-sha2-512", 

1323 "rsa-sha2-256", 

1324 ] 

1325 else: 

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

1327 

1328 self.start_client() 

1329 

1330 # check host key if we were given one 

1331 if hostkey is not None: 

1332 key = self.get_remote_server_key() 

1333 if ( 

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

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

1336 ): 

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

1338 self._log( 

1339 DEBUG, 

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

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

1342 ), 

1343 ) 

1344 self._log( 

1345 DEBUG, 

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

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

1348 ), 

1349 ) 

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

1351 self._log( 

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

1353 ) 

1354 

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

1356 if pkey is not None: 

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

1358 self.auth_publickey(username, pkey) 

1359 else: 

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

1361 self.auth_password(username, password) 

1362 

1363 return 

1364 

1365 def get_exception(self): 

1366 """ 

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

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

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

1370 this call. 

1371 

1372 :return: 

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

1374 

1375 .. versionadded:: 1.1 

1376 """ 

1377 self.lock.acquire() 

1378 try: 

1379 e = self.saved_exception 

1380 self.saved_exception = None 

1381 return e 

1382 finally: 

1383 self.lock.release() 

1384 

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

1386 """ 

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

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

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

1390 detailed documentation. 

1391 

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

1393 passed to the `.SubsystemHandler` constructor later. 

1394 

1395 :param str name: name of the subsystem. 

1396 :param handler: 

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

1398 """ 

1399 try: 

1400 self.lock.acquire() 

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

1402 finally: 

1403 self.lock.release() 

1404 

1405 def is_authenticated(self): 

1406 """ 

1407 Return true if this session is active and authenticated. 

1408 

1409 :return: 

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

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

1412 closed. 

1413 """ 

1414 return ( 

1415 self.active 

1416 and self.auth_handler is not None 

1417 and self.auth_handler.is_authenticated() 

1418 ) 

1419 

1420 def get_username(self): 

1421 """ 

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

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

1424 returns ``None``. 

1425 

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

1427 """ 

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

1429 return None 

1430 return self.auth_handler.get_username() 

1431 

1432 def get_banner(self): 

1433 """ 

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

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

1436 

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

1438 

1439 .. versionadded:: 1.13 

1440 """ 

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

1442 return None 

1443 return self.auth_handler.banner 

1444 

1445 def auth_none(self, username): 

1446 """ 

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

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

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

1450 `.BadAuthenticationType` exception raised. 

1451 

1452 :param str username: the username to authenticate as 

1453 :return: 

1454 list of auth types permissible for the next stage of 

1455 authentication (normally empty) 

1456 

1457 :raises: 

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

1459 by the server for this user 

1460 :raises: 

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

1462 error 

1463 

1464 .. versionadded:: 1.5 

1465 """ 

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

1467 raise SSHException("No existing session") 

1468 my_event = threading.Event() 

1469 self.auth_handler = AuthHandler(self) 

1470 self.auth_handler.auth_none(username, my_event) 

1471 return self.auth_handler.wait_for_response(my_event) 

1472 

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

1474 """ 

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

1476 are sent over an encrypted link. 

1477 

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

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

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

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

1482 

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

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

1485 Otherwise, the method simply returns. 

1486 

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

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

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

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

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

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

1493 which turn off plain password authentication in a misguided belief 

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

1495 

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

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

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

1499 

1500 :param str username: the username to authenticate as 

1501 :param basestring password: the password to authenticate with 

1502 :param .threading.Event event: 

1503 an event to trigger when the authentication attempt is complete 

1504 (whether it was successful or not) 

1505 :param bool fallback: 

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

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

1508 :return: 

1509 list of auth types permissible for the next stage of 

1510 authentication (normally empty) 

1511 

1512 :raises: 

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

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

1515 :raises: 

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

1517 event was passed in) 

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

1519 """ 

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

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

1522 # link 

1523 raise SSHException("No existing session") 

1524 if event is None: 

1525 my_event = threading.Event() 

1526 else: 

1527 my_event = event 

1528 self.auth_handler = AuthHandler(self) 

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

1530 if event is not None: 

1531 # caller wants to wait for event themselves 

1532 return [] 

1533 try: 

1534 return self.auth_handler.wait_for_response(my_event) 

1535 except BadAuthenticationType as e: 

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

1537 # try to fudge it 

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

1539 raise 

1540 try: 

1541 

1542 def handler(title, instructions, fields): 

1543 if len(fields) > 1: 

1544 raise SSHException("Fallback authentication failed.") 

1545 if len(fields) == 0: 

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

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

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

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

1550 return [] 

1551 return [password] 

1552 

1553 return self.auth_interactive(username, handler) 

1554 except SSHException: 

1555 # attempt failed; just raise the original exception 

1556 raise e 

1557 

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

1559 """ 

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

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

1562 

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

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

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

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

1567 

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

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

1570 Otherwise, the method simply returns. 

1571 

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

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

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

1575 

1576 :param str username: the username to authenticate as 

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

1578 :param .threading.Event event: 

1579 an event to trigger when the authentication attempt is complete 

1580 (whether it was successful or not) 

1581 :return: 

1582 list of auth types permissible for the next stage of 

1583 authentication (normally empty) 

1584 

1585 :raises: 

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

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

1588 :raises: 

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

1590 event was passed in) 

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

1592 """ 

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

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

1595 raise SSHException("No existing session") 

1596 if event is None: 

1597 my_event = threading.Event() 

1598 else: 

1599 my_event = event 

1600 self.auth_handler = AuthHandler(self) 

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

1602 if event is not None: 

1603 # caller wants to wait for event themselves 

1604 return [] 

1605 return self.auth_handler.wait_for_response(my_event) 

1606 

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

1608 """ 

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

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

1611 dumb wrapper around PAM. 

1612 

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

1614 periodically calling the handler asynchronously to get answers to 

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

1616 if the server continues to ask questions. 

1617 

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

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

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

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

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

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

1624 echoed. 

1625 

1626 A sample call would thus be: 

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

1628 

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

1630 questions. 

1631 

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

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

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

1635 

1636 :param str username: the username to authenticate as 

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

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

1639 :return: 

1640 list of auth types permissible for the next stage of 

1641 authentication (normally empty). 

1642 

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

1644 allowed by the server for this user 

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

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

1647 

1648 .. versionadded:: 1.5 

1649 """ 

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

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

1652 raise SSHException("No existing session") 

1653 my_event = threading.Event() 

1654 self.auth_handler = AuthHandler(self) 

1655 self.auth_handler.auth_interactive( 

1656 username, handler, my_event, submethods 

1657 ) 

1658 return self.auth_handler.wait_for_response(my_event) 

1659 

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

1661 """ 

1662 Authenticate to the server interactively but dumber. 

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

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

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

1666 """ 

1667 

1668 if not handler: 

1669 

1670 def handler(title, instructions, prompt_list): 

1671 answers = [] 

1672 if title: 

1673 print(title.strip()) 

1674 if instructions: 

1675 print(instructions.strip()) 

1676 for prompt, show_input in prompt_list: 

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

1678 answers.append(input()) 

1679 return answers 

1680 

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

1682 

1683 def set_log_channel(self, name): 

1684 """ 

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

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

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

1688 sub-channel of the one specified. 

1689 

1690 :param str name: new channel name for logging 

1691 

1692 .. versionadded:: 1.1 

1693 """ 

1694 self.log_name = name 

1695 self.logger = util.get_logger(name) 

1696 self.packetizer.set_log(self.logger) 

1697 

1698 def get_log_channel(self): 

1699 """ 

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

1701 

1702 :return: channel name as a `str` 

1703 

1704 .. versionadded:: 1.2 

1705 """ 

1706 return self.log_name 

1707 

1708 def set_hexdump(self, hexdump): 

1709 """ 

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

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

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

1713 

1714 :param bool hexdump: 

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

1716 otherwise. 

1717 """ 

1718 self.packetizer.set_hexdump(hexdump) 

1719 

1720 def get_hexdump(self): 

1721 """ 

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

1723 protocol traffic. 

1724 

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

1726 

1727 .. versionadded:: 1.4 

1728 """ 

1729 return self.packetizer.get_hexdump() 

1730 

1731 def use_compression(self, compress=True): 

1732 """ 

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

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

1735 compression is off since it negatively affects interactive sessions. 

1736 

1737 :param bool compress: 

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

1739 ``False`` to refuse compression 

1740 

1741 .. versionadded:: 1.5.2 

1742 """ 

1743 if compress: 

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

1745 else: 

1746 self._preferred_compression = ("none",) 

1747 

1748 def getpeername(self): 

1749 """ 

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

1751 

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

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

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

1755 

1756 :return: 

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

1758 tuple. 

1759 """ 

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

1761 if gp is None: 

1762 return "unknown", 0 

1763 return gp() 

1764 

1765 def stop_thread(self): 

1766 self.active = False 

1767 self.packetizer.close() 

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

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

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

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

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

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

1774 while ( 

1775 self.is_alive() 

1776 and self is not threading.current_thread() 

1777 and not self.sock._closed 

1778 and not self.packetizer.closed 

1779 ): 

1780 self.join(0.1) 

1781 

1782 # internals... 

1783 

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

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

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

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

1788 for m in msg: 

1789 self.logger.log(level, m) 

1790 else: 

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

1792 

1793 def _get_modulus_pack(self): 

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

1795 return self._modulus_pack 

1796 

1797 def _next_channel(self): 

1798 """you are holding the lock""" 

1799 chanid = self._channel_counter 

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

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

1802 chanid = self._channel_counter 

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

1804 return chanid 

1805 

1806 def _unlink_channel(self, chanid): 

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

1808 self._channels.delete(chanid) 

1809 

1810 def _send_message(self, data): 

1811 self.packetizer.send_message(data) 

1812 

1813 def _send_user_message(self, data): 

1814 """ 

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

1816 for user-initiated requests. 

1817 """ 

1818 start = time.time() 

1819 while True: 

1820 self.clear_to_send.wait(0.1) 

1821 if not self.active: 

1822 self._log( 

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

1824 ) # noqa 

1825 return 

1826 self.clear_to_send_lock.acquire() 

1827 if self.clear_to_send.is_set(): 

1828 break 

1829 self.clear_to_send_lock.release() 

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

1831 raise SSHException( 

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

1833 ) # noqa 

1834 try: 

1835 self._send_message(data) 

1836 finally: 

1837 self.clear_to_send_lock.release() 

1838 

1839 def _set_K_H(self, k, h): 

1840 """ 

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

1842 """ 

1843 self.K = k 

1844 self.H = h 

1845 if self.session_id is None: 

1846 self.session_id = h 

1847 

1848 def _expect_packet(self, *ptypes): 

1849 """ 

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

1851 """ 

1852 self._expected_packet = tuple(ptypes) 

1853 

1854 def _verify_key(self, host_key, sig): 

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

1856 if key is None: 

1857 raise SSHException("Unknown host key type") 

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

1859 # those baked in? 

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

1861 raise SSHException( 

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

1863 self.host_key_type 

1864 ) 

1865 ) # noqa 

1866 self.host_key = key 

1867 

1868 def _add_K(self, m): 

1869 # Hybrid post-quantum kex methods (e.g. mlkem768x25519-sha256) 

1870 # produce K as a fixed-length hash output, which the draft mandates 

1871 # be encoded as an SSH string rather than an mpint. 

1872 if isinstance(self.K, bytes): 

1873 m.add_string(self.K) 

1874 else: 

1875 m.add_mpint(self.K) 

1876 

1877 def _compute_key(self, id, nbytes): 

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

1879 m = Message() 

1880 self._add_K(m) 

1881 m.add_bytes(self.H) 

1882 m.add_byte(b(id)) 

1883 m.add_bytes(self.session_id) 

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

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

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

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

1888 self.kex_engine.__class__.__name__, hash_algo 

1889 ) 

1890 if hash_algo is None: 

1891 hash_algo = sha1 

1892 hash_select_msg += ", falling back to sha1" 

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

1894 self._log(DEBUG, hash_select_msg) 

1895 setattr(self, "_logged_hash_selection", True) 

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

1897 while len(out) < nbytes: 

1898 m = Message() 

1899 self._add_K(m) 

1900 m.add_bytes(self.H) 

1901 m.add_bytes(sofar) 

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

1903 out += digest 

1904 sofar += digest 

1905 return out[:nbytes] 

1906 

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

1908 if name not in self._cipher_info: 

1909 raise SSHException("Unknown cipher " + name) 

1910 info = self._cipher_info[name] 

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

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

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

1914 if aead: 

1915 return algorithm 

1916 # All others go through the Cipher class. 

1917 cipher = Cipher( 

1918 algorithm=algorithm, 

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

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

1921 backend=default_backend(), 

1922 ) 

1923 if operation is self._ENCRYPT: 

1924 return cipher.encryptor() 

1925 else: 

1926 return cipher.decryptor() 

1927 

1928 def _set_forward_agent_handler(self, handler): 

1929 if handler is None: 

1930 

1931 def default_handler(channel): 

1932 self._queue_incoming_channel(channel) 

1933 

1934 self._forward_agent_handler = default_handler 

1935 else: 

1936 self._forward_agent_handler = handler 

1937 

1938 def _set_x11_handler(self, handler): 

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

1940 if handler is None: 

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

1942 def default_handler(channel, src_addr_port): 

1943 self._queue_incoming_channel(channel) 

1944 

1945 self._x11_handler = default_handler 

1946 else: 

1947 self._x11_handler = handler 

1948 

1949 def _queue_incoming_channel(self, channel): 

1950 self.lock.acquire() 

1951 try: 

1952 self.server_accepts.append(channel) 

1953 self.server_accept_cv.notify() 

1954 finally: 

1955 self.lock.release() 

1956 

1957 def _sanitize_window_size(self, window_size): 

1958 if window_size is None: 

1959 window_size = self.default_window_size 

1960 return clamp_value(MIN_WINDOW_SIZE, window_size, MAX_WINDOW_SIZE) 

1961 

1962 def _sanitize_packet_size(self, max_packet_size): 

1963 if max_packet_size is None: 

1964 max_packet_size = self.default_max_packet_size 

1965 return clamp_value(MIN_PACKET_SIZE, max_packet_size, MAX_WINDOW_SIZE) 

1966 

1967 def _ensure_authed(self, ptype, message): 

1968 """ 

1969 Checks message type against current auth state. 

1970 

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

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

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

1974 

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

1976 """ 

1977 if ( 

1978 not self.server_mode 

1979 or ptype <= HIGHEST_USERAUTH_MESSAGE_ID 

1980 or self.is_authenticated() 

1981 ): 

1982 return None 

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

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

1985 reply = Message() 

1986 # Global requests have no details, just failure. 

1987 if ptype == MSG_GLOBAL_REQUEST: 

1988 reply.add_byte(cMSG_REQUEST_FAILURE) 

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

1990 elif ptype == MSG_CHANNEL_OPEN: 

1991 kind = message.get_text() # noqa 

1992 chanid = message.get_int() 

1993 reply.add_byte(cMSG_CHANNEL_OPEN_FAILURE) 

1994 reply.add_int(chanid) 

1995 reply.add_int(OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED) 

1996 reply.add_string("") 

1997 reply.add_string("en") 

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

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

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

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

2002 # be empty.) 

2003 return reply 

2004 

2005 def _enforce_strict_kex(self, ptype): 

2006 """ 

2007 Conditionally raise `MessageOrderError` during strict initial kex. 

2008 

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

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

2011 more accurately. 

2012 """ 

2013 if self.agreed_on_strict_kex and not self.initial_kex_done: 

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

2015 raise MessageOrderError( 

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

2017 ) 

2018 

2019 def run(self): 

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

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

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

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

2024 

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

2026 # interpreter shutdown. 

2027 self.sys = sys 

2028 

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

2030 _active_threads.append(self) 

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

2032 if self.server_mode: 

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

2034 else: 

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

2036 try: 

2037 try: 

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

2039 self._log( 

2040 DEBUG, 

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

2042 ) # noqa 

2043 self._check_banner() 

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

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

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

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

2048 # shell. 

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

2050 # Reuse the banner timeout for now. 

2051 self.packetizer.start_handshake(self.handshake_timeout) 

2052 self._send_kex_init() 

2053 self._expect_packet(MSG_KEXINIT) 

2054 

2055 while self.active: 

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

2057 self._send_kex_init() 

2058 try: 

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

2060 except NeedRekeyException: 

2061 continue 

2062 if ptype == MSG_IGNORE: 

2063 self._enforce_strict_kex(ptype) 

2064 continue 

2065 elif ptype == MSG_DISCONNECT: 

2066 self._parse_disconnect(m) 

2067 break 

2068 elif ptype == MSG_DEBUG: 

2069 self._enforce_strict_kex(ptype) 

2070 self._parse_debug(m) 

2071 continue 

2072 if len(self._expected_packet) > 0: 

2073 if ptype not in self._expected_packet: 

2074 exc_class = SSHException 

2075 if self.agreed_on_strict_kex: 

2076 exc_class = MessageOrderError 

2077 raise exc_class( 

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

2079 self._expected_packet, ptype 

2080 ) 

2081 ) # noqa 

2082 self._expected_packet = tuple() 

2083 # These message IDs indicate key exchange & will differ 

2084 # depending on exact exchange algorithm 

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

2086 self.kex_engine.parse_next(ptype, m) 

2087 continue 

2088 

2089 if ptype in self._handler_table: 

2090 error_msg = self._ensure_authed(ptype, m) 

2091 if error_msg: 

2092 self._send_message(error_msg) 

2093 else: 

2094 self._handler_table[ptype](m) 

2095 elif ptype in self._channel_handler_table: 

2096 chanid = m.get_int() 

2097 chan = self._channels.get(chanid) 

2098 if chan is not None: 

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

2100 elif chanid in self.channels_seen: 

2101 self._log( 

2102 DEBUG, 

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

2104 chanid 

2105 ), 

2106 ) 

2107 else: 

2108 self._log( 

2109 ERROR, 

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

2111 chanid 

2112 ), 

2113 ) 

2114 break 

2115 elif ( 

2116 self.auth_handler is not None 

2117 and ptype in self.auth_handler._handler_table 

2118 ): 

2119 handler = self.auth_handler._handler_table[ptype] 

2120 handler(m) 

2121 if len(self._expected_packet) > 0: 

2122 continue 

2123 else: 

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

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

2126 # itself literally MSG_UNIMPLEMENTED, in which case, we 

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

2128 name = MSG_NAMES[ptype] 

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

2130 ptype, name 

2131 ) 

2132 self._log(WARNING, warning) 

2133 if ptype != MSG_UNIMPLEMENTED: 

2134 msg = Message() 

2135 msg.add_byte(cMSG_UNIMPLEMENTED) 

2136 msg.add_int(m.seqno) 

2137 self._send_message(msg) 

2138 self.packetizer.complete_handshake() 

2139 except SSHException as e: 

2140 self._log( 

2141 ERROR, 

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

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

2144 ), 

2145 ) 

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

2147 self.saved_exception = e 

2148 except EOFError as e: 

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

2150 self.saved_exception = e 

2151 except socket.error as e: 

2152 if type(e.args) is tuple: 

2153 if e.args: 

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

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

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

2157 else: 

2158 emsg = e.args 

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

2160 self.saved_exception = e 

2161 except Exception as e: 

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

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

2164 self.saved_exception = e 

2165 _active_threads.remove(self) 

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

2167 chan._unlink() 

2168 if self.active: 

2169 self.active = False 

2170 self.packetizer.close() 

2171 if self.completion_event is not None: 

2172 self.completion_event.set() 

2173 if self.auth_handler is not None: 

2174 self.auth_handler.abort() 

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

2176 event.set() 

2177 try: 

2178 self.lock.acquire() 

2179 self.server_accept_cv.notify() 

2180 finally: 

2181 self.lock.release() 

2182 self.sock.close() 

2183 except: 

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

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

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

2187 # appears to still exist. 

2188 if self.sys.modules is not None: 

2189 raise 

2190 

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

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

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

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

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

2196 if local == remote: 

2197 msg += local 

2198 else: 

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

2200 self._log(DEBUG, msg) 

2201 

2202 # protocol stages 

2203 

2204 def _negotiate_keys(self, m): 

2205 # throws SSHException on anything unusual 

2206 self.clear_to_send_lock.acquire() 

2207 try: 

2208 self.clear_to_send.clear() 

2209 finally: 

2210 self.clear_to_send_lock.release() 

2211 if self.local_kex_init is None: 

2212 # remote side wants to renegotiate 

2213 self._send_kex_init() 

2214 self._parse_kex_init(m) 

2215 self.kex_engine.start_kex() 

2216 

2217 def _check_banner(self): 

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

2219 for i in range(100): 

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

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

2222 if i == 0: 

2223 timeout = self.banner_timeout 

2224 else: 

2225 timeout = 2 

2226 try: 

2227 buf = self.packetizer.readline(timeout) 

2228 except ProxyCommandFailure: 

2229 raise 

2230 except Exception as e: 

2231 raise SSHException( 

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

2233 ) 

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

2235 break 

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

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

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

2239 # save this server version string for later 

2240 self.remote_version = buf 

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

2242 # pull off any attached comment 

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

2244 # since 2003. ca 877cd974b8182d26fa76d566072917ea67b64e67 

2245 i = buf.find(" ") 

2246 if i >= 0: 

2247 buf = buf[:i] 

2248 # parse out version string and make sure it matches 

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

2250 if len(segs) < 3: 

2251 raise SSHException("Invalid SSH banner") 

2252 version = segs[1] 

2253 client = segs[2] 

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

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

2256 raise IncompatiblePeer(msg.format(version)) 

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

2258 self._log(INFO, msg) 

2259 

2260 def _send_kex_init(self): 

2261 """ 

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

2263 kind of key negotiation we support. 

2264 """ 

2265 self.clear_to_send_lock.acquire() 

2266 try: 

2267 self.clear_to_send.clear() 

2268 finally: 

2269 self.clear_to_send_lock.release() 

2270 self.in_kex = True 

2271 kex_algos = list(self.preferred_kex) 

2272 if self.server_mode: 

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

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

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

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

2277 # primes 

2278 pkex = [ 

2279 k 

2280 for k in self.get_security_options().kex 

2281 if not k.startswith(mp_required_prefix) 

2282 ] 

2283 self.get_security_options().kex = pkex 

2284 available_server_keys = list( 

2285 filter( 

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

2287 # TODO: ensure tests will catch if somebody streamlines 

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

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

2290 # were filtered out of the below via disabled_algorithms. 

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

2292 # disabled algorithm(s) for hostkey use 

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

2294 # when we make kex configuration more straightforward 

2295 self.preferred_keys, 

2296 ) 

2297 ) 

2298 else: 

2299 available_server_keys = self.preferred_keys 

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

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

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

2303 # pitfall when adding this apparently). 

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

2305 

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

2307 # of above if/else. 

2308 if self.advertise_strict_kex: 

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

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

2311 

2312 m = Message() 

2313 m.add_byte(cMSG_KEXINIT) 

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

2315 m.add_list(kex_algos) 

2316 m.add_list(available_server_keys) 

2317 m.add_list(self.preferred_ciphers) 

2318 m.add_list(self.preferred_ciphers) 

2319 m.add_list(self.preferred_macs) 

2320 m.add_list(self.preferred_macs) 

2321 m.add_list(self.preferred_compression) 

2322 m.add_list(self.preferred_compression) 

2323 m.add_string(bytes()) 

2324 m.add_string(bytes()) 

2325 m.add_boolean(False) 

2326 m.add_int(0) 

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

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

2329 self._send_message(m) 

2330 

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

2332 parsed = {} 

2333 if ignore_first_byte: 

2334 m.get_byte() 

2335 m.get_bytes(16) # cookie, discarded 

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

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

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

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

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

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

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

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

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

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

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

2347 m.get_int() # unused 

2348 return parsed 

2349 

2350 def _get_latest_kex_init(self): 

2351 return self._really_parse_kex_init( 

2352 Message(self._latest_kex_init), 

2353 ignore_first_byte=True, 

2354 ) 

2355 

2356 def _parse_kex_init(self, m): 

2357 parsed = self._really_parse_kex_init(m) 

2358 kex_algo_list = parsed["kex_algo_list"] 

2359 server_key_algo_list = parsed["server_key_algo_list"] 

2360 client_encrypt_algo_list = parsed["client_encrypt_algo_list"] 

2361 server_encrypt_algo_list = parsed["server_encrypt_algo_list"] 

2362 client_mac_algo_list = parsed["client_mac_algo_list"] 

2363 server_mac_algo_list = parsed["server_mac_algo_list"] 

2364 client_compress_algo_list = parsed["client_compress_algo_list"] 

2365 server_compress_algo_list = parsed["server_compress_algo_list"] 

2366 client_lang_list = parsed["client_lang_list"] 

2367 server_lang_list = parsed["server_lang_list"] 

2368 kex_follows = parsed["kex_follows"] 

2369 

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

2371 for prefix, value in ( 

2372 ("kex algos", kex_algo_list), 

2373 ("server key", server_key_algo_list), 

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

2375 # terminology (including elsewhere in paramiko!)? 

2376 ("client encrypt", client_encrypt_algo_list), 

2377 ("server encrypt", server_encrypt_algo_list), 

2378 ("client mac", client_mac_algo_list), 

2379 ("server mac", server_mac_algo_list), 

2380 ("client compress", client_compress_algo_list), 

2381 ("server compress", server_compress_algo_list), 

2382 ("client lang", client_lang_list), 

2383 ("server lang", server_lang_list), 

2384 ): 

2385 if value == [""]: 

2386 value = ["<none>"] 

2387 value = ", ".join(value) 

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

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

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

2391 

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

2393 self._remote_ext_info = None 

2394 self._remote_strict_kex = None 

2395 to_pop = [] 

2396 for i, algo in enumerate(kex_algo_list): 

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

2398 self._remote_ext_info = algo 

2399 to_pop.insert(0, i) 

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

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

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

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

2404 # Set strict mode if agreed. 

2405 self.agreed_on_strict_kex = ( 

2406 algo == expected and self.advertise_strict_kex 

2407 ) 

2408 self._log( 

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

2410 ) 

2411 to_pop.insert(0, i) 

2412 for i in to_pop: 

2413 kex_algo_list.pop(i) 

2414 

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

2416 # init phase, if strict mode was negotiated. 

2417 if ( 

2418 self.agreed_on_strict_kex 

2419 and not self.initial_kex_done 

2420 and m.seqno != 0 

2421 ): 

2422 raise MessageOrderError( 

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

2424 ) 

2425 

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

2427 # support. 

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

2429 # supports. 

2430 if self.server_mode: 

2431 agreed_kex = list( 

2432 filter(self.preferred_kex.__contains__, kex_algo_list) 

2433 ) 

2434 else: 

2435 agreed_kex = list( 

2436 filter(kex_algo_list.__contains__, self.preferred_kex) 

2437 ) 

2438 if len(agreed_kex) == 0: 

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

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

2441 # front 

2442 raise IncompatiblePeer( 

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

2444 ) # noqa 

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

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

2447 

2448 if self.server_mode: 

2449 available_server_keys = list( 

2450 filter( 

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

2452 self.preferred_keys, 

2453 ) 

2454 ) 

2455 agreed_keys = list( 

2456 filter( 

2457 available_server_keys.__contains__, server_key_algo_list 

2458 ) 

2459 ) 

2460 else: 

2461 agreed_keys = list( 

2462 filter(server_key_algo_list.__contains__, self.preferred_keys) 

2463 ) 

2464 if len(agreed_keys) == 0: 

2465 raise IncompatiblePeer( 

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

2467 ) # noqa 

2468 self.host_key_type = agreed_keys[0] 

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

2470 raise IncompatiblePeer( 

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

2472 ) # noqa 

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

2474 

2475 if self.server_mode: 

2476 agreed_local_ciphers = list( 

2477 filter( 

2478 self.preferred_ciphers.__contains__, 

2479 server_encrypt_algo_list, 

2480 ) 

2481 ) 

2482 agreed_remote_ciphers = list( 

2483 filter( 

2484 self.preferred_ciphers.__contains__, 

2485 client_encrypt_algo_list, 

2486 ) 

2487 ) 

2488 else: 

2489 agreed_local_ciphers = list( 

2490 filter( 

2491 client_encrypt_algo_list.__contains__, 

2492 self.preferred_ciphers, 

2493 ) 

2494 ) 

2495 agreed_remote_ciphers = list( 

2496 filter( 

2497 server_encrypt_algo_list.__contains__, 

2498 self.preferred_ciphers, 

2499 ) 

2500 ) 

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

2502 raise IncompatiblePeer( 

2503 "Incompatible ssh server (no acceptable ciphers)" 

2504 ) # noqa 

2505 self.local_cipher = agreed_local_ciphers[0] 

2506 self.remote_cipher = agreed_remote_ciphers[0] 

2507 self._log_agreement( 

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

2509 ) 

2510 

2511 if self.server_mode: 

2512 agreed_remote_macs = list( 

2513 filter(self.preferred_macs.__contains__, client_mac_algo_list) 

2514 ) 

2515 agreed_local_macs = list( 

2516 filter(self.preferred_macs.__contains__, server_mac_algo_list) 

2517 ) 

2518 else: 

2519 agreed_local_macs = list( 

2520 filter(client_mac_algo_list.__contains__, self.preferred_macs) 

2521 ) 

2522 agreed_remote_macs = list( 

2523 filter(server_mac_algo_list.__contains__, self.preferred_macs) 

2524 ) 

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

2526 raise IncompatiblePeer( 

2527 "Incompatible ssh server (no acceptable macs)" 

2528 ) 

2529 self.local_mac = agreed_local_macs[0] 

2530 self.remote_mac = agreed_remote_macs[0] 

2531 self._log_agreement( 

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

2533 ) 

2534 

2535 if self.server_mode: 

2536 agreed_remote_compression = list( 

2537 filter( 

2538 self.preferred_compression.__contains__, 

2539 client_compress_algo_list, 

2540 ) 

2541 ) 

2542 agreed_local_compression = list( 

2543 filter( 

2544 self.preferred_compression.__contains__, 

2545 server_compress_algo_list, 

2546 ) 

2547 ) 

2548 else: 

2549 agreed_local_compression = list( 

2550 filter( 

2551 client_compress_algo_list.__contains__, 

2552 self.preferred_compression, 

2553 ) 

2554 ) 

2555 agreed_remote_compression = list( 

2556 filter( 

2557 server_compress_algo_list.__contains__, 

2558 self.preferred_compression, 

2559 ) 

2560 ) 

2561 if ( 

2562 len(agreed_local_compression) == 0 

2563 or len(agreed_remote_compression) == 0 

2564 ): 

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

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

2567 raise IncompatiblePeer( 

2568 msg.format( 

2569 agreed_local_compression, 

2570 agreed_remote_compression, 

2571 self.preferred_compression, 

2572 ) 

2573 ) 

2574 self.local_compression = agreed_local_compression[0] 

2575 self.remote_compression = agreed_remote_compression[0] 

2576 self._log_agreement( 

2577 "Compression", 

2578 local=self.local_compression, 

2579 remote=self.remote_compression, 

2580 ) 

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

2582 

2583 # save for computing hash later... 

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

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

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

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

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

2589 

2590 def _activate_inbound(self): 

2591 """switch on newly negotiated encryption parameters for 

2592 inbound traffic""" 

2593 info = self._cipher_info[self.remote_cipher] 

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

2595 block_size = info["block-size"] 

2596 key_size = info["key-size"] 

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

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

2599 if self.server_mode: 

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

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

2602 else: 

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

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

2605 

2606 engine = self._get_engine( 

2607 name=self.remote_cipher, 

2608 key=key_in, 

2609 iv=iv_in, 

2610 operation=self._DECRYPT, 

2611 aead=aead, 

2612 ) 

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

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

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

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

2617 # potentially truncated transmission size) 

2618 if self.server_mode: 

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

2620 else: 

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

2622 

2623 self.packetizer.set_inbound_cipher( 

2624 block_engine=engine, 

2625 block_size=block_size, 

2626 mac_engine=None if aead else mac_engine, 

2627 mac_size=16 if aead else mac_size, 

2628 mac_key=None if aead else mac_key, 

2629 etm=etm, 

2630 aead=aead, 

2631 iv_in=iv_in if aead else None, 

2632 ) 

2633 

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

2635 if compress_in is not None and ( 

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

2637 ): 

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

2639 self.packetizer.set_inbound_compressor(compress_in()) 

2640 # Reset inbound sequence number if strict mode. 

2641 if self.agreed_on_strict_kex: 

2642 self._log( 

2643 DEBUG, 

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

2645 ) 

2646 self.packetizer.reset_seqno_in() 

2647 

2648 def _activate_outbound(self): 

2649 """switch on newly negotiated encryption parameters for 

2650 outbound traffic""" 

2651 m = Message() 

2652 m.add_byte(cMSG_NEWKEYS) 

2653 self._send_message(m) 

2654 # Reset outbound sequence number if strict mode. 

2655 if self.agreed_on_strict_kex: 

2656 self._log( 

2657 DEBUG, 

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

2659 ) 

2660 self.packetizer.reset_seqno_out() 

2661 info = self._cipher_info[self.local_cipher] 

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

2663 block_size = info["block-size"] 

2664 key_size = info["key-size"] 

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

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

2667 if self.server_mode: 

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

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

2670 else: 

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

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

2673 

2674 engine = self._get_engine( 

2675 name=self.local_cipher, 

2676 key=key_out, 

2677 iv=iv_out, 

2678 operation=self._ENCRYPT, 

2679 aead=aead, 

2680 ) 

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

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

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

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

2685 # potentially truncated transmission size) 

2686 if self.server_mode: 

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

2688 else: 

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

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

2691 

2692 self.packetizer.set_outbound_cipher( 

2693 block_engine=engine, 

2694 block_size=block_size, 

2695 mac_engine=None if aead else mac_engine, 

2696 mac_size=16 if aead else mac_size, 

2697 mac_key=None if aead else mac_key, 

2698 sdctr=sdctr, 

2699 etm=etm, 

2700 aead=aead, 

2701 iv_out=iv_out if aead else None, 

2702 ) 

2703 

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

2705 if compress_out is not None and ( 

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

2707 ): 

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

2709 self.packetizer.set_outbound_compressor(compress_out()) 

2710 if not self.packetizer.need_rekey(): 

2711 self.in_kex = False 

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

2713 if ( 

2714 self.server_mode 

2715 and self.server_sig_algs 

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

2717 ): 

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

2719 m = Message() 

2720 m.add_byte(cMSG_EXT_INFO) 

2721 m.add_int(len(extensions)) 

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

2723 m.add_string(name) 

2724 m.add_string(value) 

2725 self._send_message(m) 

2726 # we always expect to receive NEWKEYS now 

2727 self._expect_packet(MSG_NEWKEYS) 

2728 

2729 def _auth_trigger(self): 

2730 self.authenticated = True 

2731 # delayed initiation of compression 

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

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

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

2735 self.packetizer.set_outbound_compressor(compress_out()) 

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

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

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

2739 self.packetizer.set_inbound_compressor(compress_in()) 

2740 

2741 def _parse_ext_info(self, msg): 

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

2743 # pairs. 

2744 extensions = {} 

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

2746 name = msg.get_text() 

2747 value = msg.get_string() 

2748 extensions[name] = value 

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

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

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

2752 # 1st. 

2753 self.server_extensions = extensions 

2754 

2755 def _parse_newkeys(self, m): 

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

2757 self._activate_inbound() 

2758 # can also free a bunch of stuff here 

2759 self.local_kex_init = self.remote_kex_init = None 

2760 self.K = None 

2761 self.kex_engine = None 

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

2763 # create auth handler for server mode 

2764 self.auth_handler = AuthHandler(self) 

2765 if not self.initial_kex_done: 

2766 # this was the first key exchange 

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

2768 # status as well, eg when seqnos rollover) 

2769 self.initial_kex_done = self.packetizer._initial_kex_done = True 

2770 # send an event? 

2771 if self.completion_event is not None: 

2772 self.completion_event.set() 

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

2774 if not self.packetizer.need_rekey(): 

2775 self.in_kex = False 

2776 self.clear_to_send_lock.acquire() 

2777 try: 

2778 self.clear_to_send.set() 

2779 finally: 

2780 self.clear_to_send_lock.release() 

2781 return 

2782 

2783 def _parse_disconnect(self, m): 

2784 code = m.get_int() 

2785 desc = m.get_text() 

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

2787 

2788 def _parse_global_request(self, m): 

2789 kind = m.get_text() 

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

2791 want_reply = m.get_boolean() 

2792 if not self.server_mode: 

2793 self._log( 

2794 DEBUG, 

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

2796 ) 

2797 ok = False 

2798 elif kind == "tcpip-forward": 

2799 address = m.get_text() 

2800 port = m.get_int() 

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

2802 if ok: 

2803 ok = (ok,) 

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

2805 address = m.get_text() 

2806 port = m.get_int() 

2807 self.server_object.cancel_port_forward_request(address, port) 

2808 ok = True 

2809 else: 

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

2811 extra = () 

2812 if type(ok) is tuple: 

2813 extra = ok 

2814 ok = True 

2815 if want_reply: 

2816 msg = Message() 

2817 if ok: 

2818 msg.add_byte(cMSG_REQUEST_SUCCESS) 

2819 msg.add(*extra) 

2820 else: 

2821 msg.add_byte(cMSG_REQUEST_FAILURE) 

2822 self._send_message(msg) 

2823 

2824 def _parse_request_success(self, m): 

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

2826 self.global_response = m 

2827 if self.completion_event is not None: 

2828 self.completion_event.set() 

2829 

2830 def _parse_request_failure(self, m): 

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

2832 self.global_response = None 

2833 if self.completion_event is not None: 

2834 self.completion_event.set() 

2835 

2836 def _parse_channel_open_success(self, m): 

2837 chanid = m.get_int() 

2838 server_chanid = m.get_int() 

2839 server_window_size = m.get_int() 

2840 server_max_packet_size = m.get_int() 

2841 chan = self._channels.get(chanid) 

2842 if chan is None: 

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

2844 return 

2845 self.lock.acquire() 

2846 try: 

2847 chan._set_remote_channel( 

2848 server_chanid, server_window_size, server_max_packet_size 

2849 ) 

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

2851 if chanid in self.channel_events: 

2852 self.channel_events[chanid].set() 

2853 del self.channel_events[chanid] 

2854 finally: 

2855 self.lock.release() 

2856 return 

2857 

2858 def _parse_channel_open_failure(self, m): 

2859 chanid = m.get_int() 

2860 reason = m.get_int() 

2861 reason_str = m.get_text() 

2862 m.get_text() # ignored language 

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

2864 self._log( 

2865 ERROR, 

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

2867 chanid, reason_str, reason_text 

2868 ), 

2869 ) 

2870 self.lock.acquire() 

2871 try: 

2872 self.saved_exception = ChannelException(reason, reason_text) 

2873 if chanid in self.channel_events: 

2874 self._channels.delete(chanid) 

2875 if chanid in self.channel_events: 

2876 self.channel_events[chanid].set() 

2877 del self.channel_events[chanid] 

2878 finally: 

2879 self.lock.release() 

2880 return 

2881 

2882 def _parse_channel_open(self, m): 

2883 kind = m.get_text() 

2884 chanid = m.get_int() 

2885 initial_window_size = m.get_int() 

2886 max_packet_size = m.get_int() 

2887 reject = False 

2888 if ( 

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

2890 and self._forward_agent_handler is not None 

2891 ): 

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

2893 self.lock.acquire() 

2894 try: 

2895 my_chanid = self._next_channel() 

2896 finally: 

2897 self.lock.release() 

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

2899 origin_addr = m.get_text() 

2900 origin_port = m.get_int() 

2901 self._log( 

2902 DEBUG, 

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

2904 origin_addr, origin_port 

2905 ), 

2906 ) 

2907 self.lock.acquire() 

2908 try: 

2909 my_chanid = self._next_channel() 

2910 finally: 

2911 self.lock.release() 

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

2913 server_addr = m.get_text() 

2914 server_port = m.get_int() 

2915 origin_addr = m.get_text() 

2916 origin_port = m.get_int() 

2917 self._log( 

2918 DEBUG, 

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

2920 origin_addr, origin_port 

2921 ), 

2922 ) 

2923 self.lock.acquire() 

2924 try: 

2925 my_chanid = self._next_channel() 

2926 finally: 

2927 self.lock.release() 

2928 elif not self.server_mode: 

2929 self._log( 

2930 DEBUG, 

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

2932 ) 

2933 reject = True 

2934 reason = OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED 

2935 else: 

2936 self.lock.acquire() 

2937 try: 

2938 my_chanid = self._next_channel() 

2939 finally: 

2940 self.lock.release() 

2941 if kind == "direct-tcpip": 

2942 # handle direct-tcpip requests coming from the client 

2943 dest_addr = m.get_text() 

2944 dest_port = m.get_int() 

2945 origin_addr = m.get_text() 

2946 origin_port = m.get_int() 

2947 reason = self.server_object.check_channel_direct_tcpip_request( 

2948 my_chanid, 

2949 (origin_addr, origin_port), 

2950 (dest_addr, dest_port), 

2951 ) 

2952 else: 

2953 reason = self.server_object.check_channel_request( 

2954 kind, my_chanid 

2955 ) 

2956 if reason != OPEN_SUCCEEDED: 

2957 self._log( 

2958 DEBUG, 

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

2960 ) 

2961 reject = True 

2962 if reject: 

2963 msg = Message() 

2964 msg.add_byte(cMSG_CHANNEL_OPEN_FAILURE) 

2965 msg.add_int(chanid) 

2966 msg.add_int(reason) 

2967 msg.add_string("") 

2968 msg.add_string("en") 

2969 self._send_message(msg) 

2970 return 

2971 

2972 chan = Channel(my_chanid) 

2973 self.lock.acquire() 

2974 try: 

2975 self._channels.put(my_chanid, chan) 

2976 self.channels_seen[my_chanid] = True 

2977 chan._set_transport(self) 

2978 chan._set_window( 

2979 self.default_window_size, self.default_max_packet_size 

2980 ) 

2981 chan._set_remote_channel( 

2982 chanid, initial_window_size, max_packet_size 

2983 ) 

2984 finally: 

2985 self.lock.release() 

2986 m = Message() 

2987 m.add_byte(cMSG_CHANNEL_OPEN_SUCCESS) 

2988 m.add_int(chanid) 

2989 m.add_int(my_chanid) 

2990 m.add_int(self.default_window_size) 

2991 m.add_int(self.default_max_packet_size) 

2992 self._send_message(m) 

2993 self._log( 

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

2995 ) 

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

2997 self._forward_agent_handler(chan) 

2998 elif kind == "x11": 

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

3000 elif kind == "forwarded-tcpip": 

3001 chan.origin_addr = (origin_addr, origin_port) 

3002 self._tcp_handler( 

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

3004 ) 

3005 else: 

3006 self._queue_incoming_channel(chan) 

3007 

3008 def _parse_debug(self, m): 

3009 m.get_boolean() # always_display 

3010 msg = m.get_string() 

3011 m.get_string() # language 

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

3013 

3014 def _get_subsystem_handler(self, name): 

3015 try: 

3016 self.lock.acquire() 

3017 if name not in self.subsystem_table: 

3018 return None, [], {} 

3019 return self.subsystem_table[name] 

3020 finally: 

3021 self.lock.release() 

3022 

3023 _channel_handler_table = { 

3024 MSG_CHANNEL_SUCCESS: Channel._request_success, 

3025 MSG_CHANNEL_FAILURE: Channel._request_failed, 

3026 MSG_CHANNEL_DATA: Channel._feed, 

3027 MSG_CHANNEL_EXTENDED_DATA: Channel._feed_extended, 

3028 MSG_CHANNEL_WINDOW_ADJUST: Channel._window_adjust, 

3029 MSG_CHANNEL_REQUEST: Channel._handle_request, 

3030 MSG_CHANNEL_EOF: Channel._handle_eof, 

3031 MSG_CHANNEL_CLOSE: Channel._handle_close, 

3032 } 

3033 

3034 

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

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

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

3038class SecurityOptions: 

3039 """ 

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

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

3042 exchange algorithms, listed in order of preference. 

3043 

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

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

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

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

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

3049 """ 

3050 

3051 __slots__ = "_transport" 

3052 

3053 def __init__(self, transport): 

3054 self._transport = transport 

3055 

3056 def __repr__(self): 

3057 """ 

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

3059 """ 

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

3061 

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

3063 if type(x) is list: 

3064 x = tuple(x) 

3065 if type(x) is not tuple: 

3066 raise TypeError("expected tuple or list") 

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

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

3069 if len(forbidden) > 0: 

3070 raise ValueError("unknown cipher") 

3071 setattr(self._transport, name, x) 

3072 

3073 @property 

3074 def ciphers(self): 

3075 """Symmetric encryption ciphers""" 

3076 return self._transport._preferred_ciphers 

3077 

3078 @ciphers.setter 

3079 def ciphers(self, x): 

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

3081 

3082 @property 

3083 def digests(self): 

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

3085 return self._transport._preferred_macs 

3086 

3087 @digests.setter 

3088 def digests(self, x): 

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

3090 

3091 @property 

3092 def key_types(self): 

3093 """Public-key algorithms""" 

3094 return self._transport._preferred_keys 

3095 

3096 @key_types.setter 

3097 def key_types(self, x): 

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

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

3100 # Transport._preferred_keys with `x`... 

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

3102 # transport._preferred_keys with x". 

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

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

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

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

3107 

3108 @property 

3109 def kex(self): 

3110 """Key exchange algorithms""" 

3111 return self._transport._preferred_kex 

3112 

3113 @kex.setter 

3114 def kex(self, x): 

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

3116 

3117 @property 

3118 def compression(self): 

3119 """Compression algorithms""" 

3120 return self._transport._preferred_compression 

3121 

3122 @compression.setter 

3123 def compression(self, x): 

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

3125 

3126 

3127class ChannelMap: 

3128 def __init__(self): 

3129 # (id -> Channel) 

3130 self._map = weakref.WeakValueDictionary() 

3131 self._lock = threading.Lock() 

3132 

3133 def put(self, chanid, chan): 

3134 self._lock.acquire() 

3135 try: 

3136 self._map[chanid] = chan 

3137 finally: 

3138 self._lock.release() 

3139 

3140 def get(self, chanid): 

3141 self._lock.acquire() 

3142 try: 

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

3144 finally: 

3145 self._lock.release() 

3146 

3147 def delete(self, chanid): 

3148 self._lock.acquire() 

3149 try: 

3150 try: 

3151 del self._map[chanid] 

3152 except KeyError: 

3153 pass 

3154 finally: 

3155 self._lock.release() 

3156 

3157 def values(self): 

3158 self._lock.acquire() 

3159 try: 

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

3161 finally: 

3162 self._lock.release() 

3163 

3164 def __len__(self): 

3165 self._lock.acquire() 

3166 try: 

3167 return len(self._map) 

3168 finally: 

3169 self._lock.release() 

3170 

3171 

3172class ServiceRequestingTransport(Transport): 

3173 """ 

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

3175 

3176 .. versionadded:: 3.2 

3177 """ 

3178 

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

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

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

3182 

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

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

3185 self._service_userauth_accepted = False 

3186 self._handler_table[MSG_SERVICE_ACCEPT] = self._parse_service_accept 

3187 

3188 def _parse_service_accept(self, m): 

3189 service = m.get_text() 

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

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

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

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

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

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

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

3197 if service != "ssh-userauth": 

3198 self._log( 

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

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

3201 # went Very Wrong. 

3202 DEBUG, 

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

3204 ) 

3205 return 

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

3207 # to submit auth requests. 

3208 self._service_userauth_accepted = True 

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

3210 

3211 def ensure_session(self): 

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

3213 # already-closed transport session; that's our responsibility, not that 

3214 # of AuthHandler. 

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

3216 # TODO: better error message? this can happen in many places, eg 

3217 # user error (authing before connecting) or developer error (some 

3218 # improperly handled pre/mid auth shutdown didn't become fatal 

3219 # enough). The latter is much more common & should ideally be fixed 

3220 # by terminating things harder? 

3221 raise SSHException("No existing session") 

3222 # Also make sure we've actually been told we are allowed to auth. 

3223 if self._service_userauth_accepted: 

3224 return 

3225 # Or request to do so, otherwise. 

3226 m = Message() 

3227 m.add_byte(cMSG_SERVICE_REQUEST) 

3228 m.add_string("ssh-userauth") 

3229 self._log(DEBUG, "Sending MSG_SERVICE_REQUEST: ssh-userauth") 

3230 self._send_message(m) 

3231 # Now we wait to hear back; the user is expecting a blocking-style auth 

3232 # request so there's no point giving control back anywhere. 

3233 while not self._service_userauth_accepted: 

3234 # TODO: feels like we're missing an AuthHandler Event like 

3235 # 'self.auth_event' which is set when AuthHandler shuts down in 

3236 # ways good AND bad. Transport only seems to have completion_event 

3237 # which is unclear re: intent, eg it's set by newkeys which always 

3238 # happens on connection, so it'll always be set by the time we get 

3239 # here. 

3240 # NOTE: this copies the timing of event.wait() in 

3241 # AuthHandler.wait_for_response, re: 1/10 of a second. Could 

3242 # presumably be smaller, but seems unlikely this period is going to 

3243 # be "too long" for any code doing ssh networking... 

3244 time.sleep(0.1) 

3245 self.auth_handler = self.get_auth_handler() 

3246 

3247 def get_auth_handler(self): 

3248 # NOTE: using new sibling subclass instead of classic AuthHandler 

3249 return AuthOnlyHandler(self) 

3250 

3251 def auth_none(self, username): 

3252 # TODO (backwards incompat): merge to parent, preserving (most of) 

3253 # docstring 

3254 self.ensure_session() 

3255 return self.auth_handler.auth_none(username) 

3256 

3257 def auth_password(self, username, password, fallback=True): 

3258 # TODO (backwards incompat): merge to parent, preserving (most of) 

3259 # docstring 

3260 self.ensure_session() 

3261 try: 

3262 return self.auth_handler.auth_password(username, password) 

3263 except BadAuthenticationType as e: 

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

3265 # try to fudge it 

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

3267 raise 

3268 try: 

3269 

3270 def handler(title, instructions, fields): 

3271 if len(fields) > 1: 

3272 raise SSHException("Fallback authentication failed.") 

3273 if len(fields) == 0: 

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

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

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

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

3278 return [] 

3279 return [password] 

3280 

3281 return self.auth_interactive(username, handler) 

3282 except SSHException: 

3283 # attempt to fudge failed; just raise the original exception 

3284 raise e 

3285 

3286 def auth_publickey(self, username, key): 

3287 # TODO (backwards incompat): merge to parent, preserving (most of) 

3288 # docstring 

3289 self.ensure_session() 

3290 return self.auth_handler.auth_publickey(username, key) 

3291 

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

3293 # TODO (backwards incompat): merge to parent, preserving (most of) 

3294 # docstring 

3295 self.ensure_session() 

3296 return self.auth_handler.auth_interactive( 

3297 username, handler, submethods 

3298 ) 

3299 

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

3301 # TODO (backwards incompat): merge to parent, preserving (most of) 

3302 # docstring 

3303 # NOTE: legacy impl omitted equiv of ensure_session since it just wraps 

3304 # another call to an auth method. however we reinstate it for 

3305 # consistency reasons. 

3306 self.ensure_session() 

3307 if not handler: 

3308 

3309 def handler(title, instructions, prompt_list): 

3310 answers = [] 

3311 if title: 

3312 print(title.strip()) 

3313 if instructions: 

3314 print(instructions.strip()) 

3315 for prompt, show_input in prompt_list: 

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

3317 answers.append(input()) 

3318 return answers 

3319 

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