codekingpro/portable-devtools
114k
1# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>2# Copyright (C) 2013-2014 science + computing ag3# Author: Sebastian Deiss <sebastian.deiss@t-online.de>4#5#6# This file is part of paramiko.7#8# Paramiko is free software; you can redistribute it and/or modify it under the9# terms of the GNU Lesser General Public License as published by the Free10# Software Foundation; either version 2.1 of the License, or (at your option)11# any later version.12#13# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY14# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR15# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more16# details.17#18# You should have received a copy of the GNU Lesser General Public License19# along with Paramiko; if not, write to the Free Software Foundation, Inc.,20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.21 22 23"""24This module provides GSS-API / SSPI Key Exchange as defined in :rfc:`4462`.25 26.. note:: Credential delegation is not supported in server mode.27 28.. note::29 `RFC 4462 Section 2.230 <https://tools.ietf.org/html/rfc4462.html#section-2.2>`_ says we are not31 required to implement GSS-API error messages. Thus, in many methods within32 this module, if an error occurs an exception will be thrown and the33 connection will be terminated.34 35.. seealso:: :doc:`/api/ssh_gss`36 37.. versionadded:: 1.1538"""39 40import os41from hashlib import sha142 43from paramiko.common import (44 DEBUG,45 max_byte,46 zero_byte,47 byte_chr,48 byte_mask,49 byte_ord,50)51from paramiko import util52from paramiko.message import Message53from paramiko.ssh_exception import SSHException54 55 56(57 MSG_KEXGSS_INIT,58 MSG_KEXGSS_CONTINUE,59 MSG_KEXGSS_COMPLETE,60 MSG_KEXGSS_HOSTKEY,61 MSG_KEXGSS_ERROR,62) = range(30, 35)63(MSG_KEXGSS_GROUPREQ, MSG_KEXGSS_GROUP) = range(40, 42)64(65 c_MSG_KEXGSS_INIT,66 c_MSG_KEXGSS_CONTINUE,67 c_MSG_KEXGSS_COMPLETE,68 c_MSG_KEXGSS_HOSTKEY,69 c_MSG_KEXGSS_ERROR,70) = [byte_chr(c) for c in range(30, 35)]71(c_MSG_KEXGSS_GROUPREQ, c_MSG_KEXGSS_GROUP) = [72 byte_chr(c) for c in range(40, 42)73]74 75 76class KexGSSGroup1:77 """78 GSS-API / SSPI Authenticated Diffie-Hellman Key Exchange as defined in `RFC79 4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_80 """81 82 # draft-ietf-secsh-transport-09.txt, page 1783 P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF # noqa84 G = 285 b7fffffffffffffff = byte_chr(0x7F) + max_byte * 7 # noqa86 b0000000000000000 = zero_byte * 8 # noqa87 NAME = "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g=="88 89 def __init__(self, transport):90 self.transport = transport91 self.kexgss = self.transport.kexgss_ctxt92 self.gss_host = None93 self.x = 094 self.e = 095 self.f = 096 97 def start_kex(self):98 """99 Start the GSS-API / SSPI Authenticated Diffie-Hellman Key Exchange.100 """101 self._generate_x()102 if self.transport.server_mode:103 # compute f = g^x mod p, but don't send it yet104 self.f = pow(self.G, self.x, self.P)105 self.transport._expect_packet(MSG_KEXGSS_INIT)106 return107 # compute e = g^x mod p (where g=2), and send it108 self.e = pow(self.G, self.x, self.P)109 # Initialize GSS-API Key Exchange110 self.gss_host = self.transport.gss_host111 m = Message()112 m.add_byte(c_MSG_KEXGSS_INIT)113 m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host))114 m.add_mpint(self.e)115 self.transport._send_message(m)116 self.transport._expect_packet(117 MSG_KEXGSS_HOSTKEY,118 MSG_KEXGSS_CONTINUE,119 MSG_KEXGSS_COMPLETE,120 MSG_KEXGSS_ERROR,121 )122 123 def parse_next(self, ptype, m):124 """125 Parse the next packet.126 127 :param ptype: The (string) type of the incoming packet128 :param `.Message` m: The packet content129 """130 if self.transport.server_mode and (ptype == MSG_KEXGSS_INIT):131 return self._parse_kexgss_init(m)132 elif not self.transport.server_mode and (ptype == MSG_KEXGSS_HOSTKEY):133 return self._parse_kexgss_hostkey(m)134 elif self.transport.server_mode and (ptype == MSG_KEXGSS_CONTINUE):135 return self._parse_kexgss_continue(m)136 elif not self.transport.server_mode and (ptype == MSG_KEXGSS_COMPLETE):137 return self._parse_kexgss_complete(m)138 elif ptype == MSG_KEXGSS_ERROR:139 return self._parse_kexgss_error(m)140 msg = "GSS KexGroup1 asked to handle packet type {:d}"141 raise SSHException(msg.format(ptype))142 143 # ## internals...144 145 def _generate_x(self):146 """147 generate an "x" (1 < x < q), where q is (p-1)/2.148 p is a 128-byte (1024-bit) number, where the first 64 bits are 1.149 therefore q can be approximated as a 2^1023. we drop the subset of150 potential x where the first 63 bits are 1, because some of those will151 be larger than q (but this is a tiny tiny subset of potential x).152 """153 while 1:154 x_bytes = os.urandom(128)155 x_bytes = byte_mask(x_bytes[0], 0x7F) + x_bytes[1:]156 first = x_bytes[:8]157 if first not in (self.b7fffffffffffffff, self.b0000000000000000):158 break159 self.x = util.inflate_long(x_bytes)160 161 def _parse_kexgss_hostkey(self, m):162 """163 Parse the SSH2_MSG_KEXGSS_HOSTKEY message (client mode).164 165 :param `.Message` m: The content of the SSH2_MSG_KEXGSS_HOSTKEY message166 """167 # client mode168 host_key = m.get_string()169 self.transport.host_key = host_key170 sig = m.get_string()171 self.transport._verify_key(host_key, sig)172 self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE)173 174 def _parse_kexgss_continue(self, m):175 """176 Parse the SSH2_MSG_KEXGSS_CONTINUE message.177 178 :param `.Message` m: The content of the SSH2_MSG_KEXGSS_CONTINUE179 message180 """181 if not self.transport.server_mode:182 srv_token = m.get_string()183 m = Message()184 m.add_byte(c_MSG_KEXGSS_CONTINUE)185 m.add_string(186 self.kexgss.ssh_init_sec_context(187 target=self.gss_host, recv_token=srv_token188 )189 )190 self.transport.send_message(m)191 self.transport._expect_packet(192 MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR193 )194 else:195 pass196 197 def _parse_kexgss_complete(self, m):198 """199 Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode).200 201 :param `.Message` m: The content of the202 SSH2_MSG_KEXGSS_COMPLETE message203 """204 # client mode205 if self.transport.host_key is None:206 self.transport.host_key = NullHostKey()207 self.f = m.get_mpint()208 if (self.f < 1) or (self.f > self.P - 1):209 raise SSHException('Server kex "f" is out of range')210 mic_token = m.get_string()211 # This must be TRUE, if there is a GSS-API token in this message.212 bool = m.get_boolean()213 srv_token = None214 if bool:215 srv_token = m.get_string()216 K = pow(self.f, self.x, self.P)217 # okay, build up the hash H of218 # (V_C || V_S || I_C || I_S || K_S || e || f || K)219 hm = Message()220 hm.add(221 self.transport.local_version,222 self.transport.remote_version,223 self.transport.local_kex_init,224 self.transport.remote_kex_init,225 )226 hm.add_string(self.transport.host_key.__str__())227 hm.add_mpint(self.e)228 hm.add_mpint(self.f)229 hm.add_mpint(K)230 H = sha1(str(hm)).digest()231 self.transport._set_K_H(K, H)232 if srv_token is not None:233 self.kexgss.ssh_init_sec_context(234 target=self.gss_host, recv_token=srv_token235 )236 self.kexgss.ssh_check_mic(mic_token, H)237 else:238 self.kexgss.ssh_check_mic(mic_token, H)239 self.transport.gss_kex_used = True240 self.transport._activate_outbound()241 242 def _parse_kexgss_init(self, m):243 """244 Parse the SSH2_MSG_KEXGSS_INIT message (server mode).245 246 :param `.Message` m: The content of the SSH2_MSG_KEXGSS_INIT message247 """248 # server mode249 client_token = m.get_string()250 self.e = m.get_mpint()251 if (self.e < 1) or (self.e > self.P - 1):252 raise SSHException('Client kex "e" is out of range')253 K = pow(self.e, self.x, self.P)254 self.transport.host_key = NullHostKey()255 key = self.transport.host_key.__str__()256 # okay, build up the hash H of257 # (V_C || V_S || I_C || I_S || K_S || e || f || K)258 hm = Message()259 hm.add(260 self.transport.remote_version,261 self.transport.local_version,262 self.transport.remote_kex_init,263 self.transport.local_kex_init,264 )265 hm.add_string(key)266 hm.add_mpint(self.e)267 hm.add_mpint(self.f)268 hm.add_mpint(K)269 H = sha1(hm.asbytes()).digest()270 self.transport._set_K_H(K, H)271 srv_token = self.kexgss.ssh_accept_sec_context(272 self.gss_host, client_token273 )274 m = Message()275 if self.kexgss._gss_srv_ctxt_status:276 mic_token = self.kexgss.ssh_get_mic(277 self.transport.session_id, gss_kex=True278 )279 m.add_byte(c_MSG_KEXGSS_COMPLETE)280 m.add_mpint(self.f)281 m.add_string(mic_token)282 if srv_token is not None:283 m.add_boolean(True)284 m.add_string(srv_token)285 else:286 m.add_boolean(False)287 self.transport._send_message(m)288 self.transport.gss_kex_used = True289 self.transport._activate_outbound()290 else:291 m.add_byte(c_MSG_KEXGSS_CONTINUE)292 m.add_string(srv_token)293 self.transport._send_message(m)294 self.transport._expect_packet(295 MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR296 )297 298 def _parse_kexgss_error(self, m):299 """300 Parse the SSH2_MSG_KEXGSS_ERROR message (client mode).301 The server may send a GSS-API error message. if it does, we display302 the error by throwing an exception (client mode).303 304 :param `.Message` m: The content of the SSH2_MSG_KEXGSS_ERROR message305 :raise SSHException: Contains GSS-API major and minor status as well as306 the error message and the language tag of the307 message308 """309 maj_status = m.get_int()310 min_status = m.get_int()311 err_msg = m.get_string()312 m.get_string() # we don't care about the language!313 raise SSHException(314 """GSS-API Error:315Major Status: {}316Minor Status: {}317Error Message: {}318""".format(319 maj_status, min_status, err_msg320 )321 )322 323 324class KexGSSGroup14(KexGSSGroup1):325 """326 GSS-API / SSPI Authenticated Diffie-Hellman Group14 Key Exchange as defined327 in `RFC 4462 Section 2328 <https://tools.ietf.org/html/rfc4462.html#section-2>`_329 """330 331 P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF # noqa332 G = 2333 NAME = "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g=="334 335 336class KexGSSGex:337 """338 GSS-API / SSPI Authenticated Diffie-Hellman Group Exchange as defined in339 `RFC 4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_340 """341 342 NAME = "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g=="343 min_bits = 1024344 max_bits = 8192345 preferred_bits = 2048346 347 def __init__(self, transport):348 self.transport = transport349 self.kexgss = self.transport.kexgss_ctxt350 self.gss_host = None351 self.p = None352 self.q = None353 self.g = None354 self.x = None355 self.e = None356 self.f = None357 self.old_style = False358 359 def start_kex(self):360 """361 Start the GSS-API / SSPI Authenticated Diffie-Hellman Group Exchange362 """363 if self.transport.server_mode:364 self.transport._expect_packet(MSG_KEXGSS_GROUPREQ)365 return366 # request a bit range: we accept (min_bits) to (max_bits), but prefer367 # (preferred_bits). according to the spec, we shouldn't pull the368 # minimum up above 1024.369 self.gss_host = self.transport.gss_host370 m = Message()371 m.add_byte(c_MSG_KEXGSS_GROUPREQ)372 m.add_int(self.min_bits)373 m.add_int(self.preferred_bits)374 m.add_int(self.max_bits)375 self.transport._send_message(m)376 self.transport._expect_packet(MSG_KEXGSS_GROUP)377 378 def parse_next(self, ptype, m):379 """380 Parse the next packet.381 382 :param ptype: The (string) type of the incoming packet383 :param `.Message` m: The packet content384 """385 if ptype == MSG_KEXGSS_GROUPREQ:386 return self._parse_kexgss_groupreq(m)387 elif ptype == MSG_KEXGSS_GROUP:388 return self._parse_kexgss_group(m)389 elif ptype == MSG_KEXGSS_INIT:390 return self._parse_kexgss_gex_init(m)391 elif ptype == MSG_KEXGSS_HOSTKEY:392 return self._parse_kexgss_hostkey(m)393 elif ptype == MSG_KEXGSS_CONTINUE:394 return self._parse_kexgss_continue(m)395 elif ptype == MSG_KEXGSS_COMPLETE:396 return self._parse_kexgss_complete(m)397 elif ptype == MSG_KEXGSS_ERROR:398 return self._parse_kexgss_error(m)399 msg = "KexGex asked to handle packet type {:d}"400 raise SSHException(msg.format(ptype))401 402 # ## internals...403 404 def _generate_x(self):405 # generate an "x" (1 < x < (p-1)/2).406 q = (self.p - 1) // 2407 qnorm = util.deflate_long(q, 0)408 qhbyte = byte_ord(qnorm[0])409 byte_count = len(qnorm)410 qmask = 0xFF411 while not (qhbyte & 0x80):412 qhbyte <<= 1413 qmask >>= 1414 while True:415 x_bytes = os.urandom(byte_count)416 x_bytes = byte_mask(x_bytes[0], qmask) + x_bytes[1:]417 x = util.inflate_long(x_bytes, 1)418 if (x > 1) and (x < q):419 break420 self.x = x421 422 def _parse_kexgss_groupreq(self, m):423 """424 Parse the SSH2_MSG_KEXGSS_GROUPREQ message (server mode).425 426 :param `.Message` m: The content of the427 SSH2_MSG_KEXGSS_GROUPREQ message428 """429 minbits = m.get_int()430 preferredbits = m.get_int()431 maxbits = m.get_int()432 # smoosh the user's preferred size into our own limits433 if preferredbits > self.max_bits:434 preferredbits = self.max_bits435 if preferredbits < self.min_bits:436 preferredbits = self.min_bits437 # fix min/max if they're inconsistent. technically, we could just pout438 # and hang up, but there's no harm in giving them the benefit of the439 # doubt and just picking a bitsize for them.440 if minbits > preferredbits:441 minbits = preferredbits442 if maxbits < preferredbits:443 maxbits = preferredbits444 # now save a copy445 self.min_bits = minbits446 self.preferred_bits = preferredbits447 self.max_bits = maxbits448 # generate prime449 pack = self.transport._get_modulus_pack()450 if pack is None:451 raise SSHException("Can't do server-side gex with no modulus pack")452 self.transport._log(453 DEBUG, # noqa454 "Picking p ({} <= {} <= {} bits)".format(455 minbits, preferredbits, maxbits456 ),457 )458 self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits)459 m = Message()460 m.add_byte(c_MSG_KEXGSS_GROUP)461 m.add_mpint(self.p)462 m.add_mpint(self.g)463 self.transport._send_message(m)464 self.transport._expect_packet(MSG_KEXGSS_INIT)465 466 def _parse_kexgss_group(self, m):467 """468 Parse the SSH2_MSG_KEXGSS_GROUP message (client mode).469 470 :param `Message` m: The content of the SSH2_MSG_KEXGSS_GROUP message471 """472 self.p = m.get_mpint()473 self.g = m.get_mpint()474 # reject if p's bit length < 1024 or > 8192475 bitlen = util.bit_length(self.p)476 if (bitlen < 1024) or (bitlen > 8192):477 raise SSHException(478 "Server-generated gex p (don't ask) is out of range "479 "({} bits)".format(bitlen)480 )481 self.transport._log(482 DEBUG, "Got server p ({} bits)".format(bitlen)483 ) # noqa484 self._generate_x()485 # now compute e = g^x mod p486 self.e = pow(self.g, self.x, self.p)487 m = Message()488 m.add_byte(c_MSG_KEXGSS_INIT)489 m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host))490 m.add_mpint(self.e)491 self.transport._send_message(m)492 self.transport._expect_packet(493 MSG_KEXGSS_HOSTKEY,494 MSG_KEXGSS_CONTINUE,495 MSG_KEXGSS_COMPLETE,496 MSG_KEXGSS_ERROR,497 )498 499 def _parse_kexgss_gex_init(self, m):500 """501 Parse the SSH2_MSG_KEXGSS_INIT message (server mode).502 503 :param `Message` m: The content of the SSH2_MSG_KEXGSS_INIT message504 """505 client_token = m.get_string()506 self.e = m.get_mpint()507 if (self.e < 1) or (self.e > self.p - 1):508 raise SSHException('Client kex "e" is out of range')509 self._generate_x()510 self.f = pow(self.g, self.x, self.p)511 K = pow(self.e, self.x, self.p)512 self.transport.host_key = NullHostKey()513 key = self.transport.host_key.__str__()514 # okay, build up the hash H of515 # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa516 hm = Message()517 hm.add(518 self.transport.remote_version,519 self.transport.local_version,520 self.transport.remote_kex_init,521 self.transport.local_kex_init,522 key,523 )524 hm.add_int(self.min_bits)525 hm.add_int(self.preferred_bits)526 hm.add_int(self.max_bits)527 hm.add_mpint(self.p)528 hm.add_mpint(self.g)529 hm.add_mpint(self.e)530 hm.add_mpint(self.f)531 hm.add_mpint(K)532 H = sha1(hm.asbytes()).digest()533 self.transport._set_K_H(K, H)534 srv_token = self.kexgss.ssh_accept_sec_context(535 self.gss_host, client_token536 )537 m = Message()538 if self.kexgss._gss_srv_ctxt_status:539 mic_token = self.kexgss.ssh_get_mic(540 self.transport.session_id, gss_kex=True541 )542 m.add_byte(c_MSG_KEXGSS_COMPLETE)543 m.add_mpint(self.f)544 m.add_string(mic_token)545 if srv_token is not None:546 m.add_boolean(True)547 m.add_string(srv_token)548 else:549 m.add_boolean(False)550 self.transport._send_message(m)551 self.transport.gss_kex_used = True552 self.transport._activate_outbound()553 else:554 m.add_byte(c_MSG_KEXGSS_CONTINUE)555 m.add_string(srv_token)556 self.transport._send_message(m)557 self.transport._expect_packet(558 MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR559 )560 561 def _parse_kexgss_hostkey(self, m):562 """563 Parse the SSH2_MSG_KEXGSS_HOSTKEY message (client mode).564 565 :param `Message` m: The content of the SSH2_MSG_KEXGSS_HOSTKEY message566 """567 # client mode568 host_key = m.get_string()569 self.transport.host_key = host_key570 sig = m.get_string()571 self.transport._verify_key(host_key, sig)572 self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE)573 574 def _parse_kexgss_continue(self, m):575 """576 Parse the SSH2_MSG_KEXGSS_CONTINUE message.577 578 :param `Message` m: The content of the SSH2_MSG_KEXGSS_CONTINUE message579 """580 if not self.transport.server_mode:581 srv_token = m.get_string()582 m = Message()583 m.add_byte(c_MSG_KEXGSS_CONTINUE)584 m.add_string(585 self.kexgss.ssh_init_sec_context(586 target=self.gss_host, recv_token=srv_token587 )588 )589 self.transport.send_message(m)590 self.transport._expect_packet(591 MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR592 )593 else:594 pass595 596 def _parse_kexgss_complete(self, m):597 """598 Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode).599 600 :param `Message` m: The content of the SSH2_MSG_KEXGSS_COMPLETE message601 """602 if self.transport.host_key is None:603 self.transport.host_key = NullHostKey()604 self.f = m.get_mpint()605 mic_token = m.get_string()606 # This must be TRUE, if there is a GSS-API token in this message.607 bool = m.get_boolean()608 srv_token = None609 if bool:610 srv_token = m.get_string()611 if (self.f < 1) or (self.f > self.p - 1):612 raise SSHException('Server kex "f" is out of range')613 K = pow(self.f, self.x, self.p)614 # okay, build up the hash H of615 # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa616 hm = Message()617 hm.add(618 self.transport.local_version,619 self.transport.remote_version,620 self.transport.local_kex_init,621 self.transport.remote_kex_init,622 self.transport.host_key.__str__(),623 )624 if not self.old_style:625 hm.add_int(self.min_bits)626 hm.add_int(self.preferred_bits)627 if not self.old_style:628 hm.add_int(self.max_bits)629 hm.add_mpint(self.p)630 hm.add_mpint(self.g)631 hm.add_mpint(self.e)632 hm.add_mpint(self.f)633 hm.add_mpint(K)634 H = sha1(hm.asbytes()).digest()635 self.transport._set_K_H(K, H)636 if srv_token is not None:637 self.kexgss.ssh_init_sec_context(638 target=self.gss_host, recv_token=srv_token639 )640 self.kexgss.ssh_check_mic(mic_token, H)641 else:642 self.kexgss.ssh_check_mic(mic_token, H)643 self.transport.gss_kex_used = True644 self.transport._activate_outbound()645 646 def _parse_kexgss_error(self, m):647 """648 Parse the SSH2_MSG_KEXGSS_ERROR message (client mode).649 The server may send a GSS-API error message. if it does, we display650 the error by throwing an exception (client mode).651 652 :param `Message` m: The content of the SSH2_MSG_KEXGSS_ERROR message653 :raise SSHException: Contains GSS-API major and minor status as well as654 the error message and the language tag of the655 message656 """657 maj_status = m.get_int()658 min_status = m.get_int()659 err_msg = m.get_string()660 m.get_string() # we don't care about the language (lang_tag)!661 raise SSHException(662 """GSS-API Error:663Major Status: {}664Minor Status: {}665Error Message: {}666""".format(667 maj_status, min_status, err_msg668 )669 )670 671 672class NullHostKey:673 """674 This class represents the Null Host Key for GSS-API Key Exchange as defined675 in `RFC 4462 Section 5676 <https://tools.ietf.org/html/rfc4462.html#section-5>`_677 """678 679 def __init__(self):680 self.key = ""681 682 def __str__(self):683 return self.key684 685 def get_name(self):686 return self.key687 