Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/kex_gss.py: 14%
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
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
1# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
2# Copyright (C) 2013-2014 science + computing ag
3# 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 the
9# terms of the GNU Lesser General Public License as published by the Free
10# 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 ANY
14# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16# details.
17#
18# You should have received a copy of the GNU Lesser General Public License
19# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23"""
24This module provides GSS-API / SSPI Key Exchange as defined in :rfc:`4462`.
26.. note:: Credential delegation is not supported in server mode.
28.. note::
29 `RFC 4462 Section 2.2
30 <https://tools.ietf.org/html/rfc4462.html#section-2.2>`_ says we are not
31 required to implement GSS-API error messages. Thus, in many methods within
32 this module, if an error occurs an exception will be thrown and the
33 connection will be terminated.
35.. seealso:: :doc:`/api/ssh_gss`
37.. versionadded:: 1.15
38"""
40import os
41from hashlib import sha1
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 util
52from paramiko.message import Message
53from paramiko.ssh_exception import SSHException
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]
76class KexGSSGroup1:
77 """
78 GSS-API / SSPI Authenticated Diffie-Hellman Key Exchange as defined in `RFC
79 4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_
80 """
82 # draft-ietf-secsh-transport-09.txt, page 17
83 P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF # noqa
84 G = 2
85 b7fffffffffffffff = byte_chr(0x7F) + max_byte * 7 # noqa
86 b0000000000000000 = zero_byte * 8 # noqa
87 NAME = "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g=="
89 def __init__(self, transport):
90 self.transport = transport
91 self.kexgss = self.transport.kexgss_ctxt
92 self.gss_host = None
93 self.x = 0
94 self.e = 0
95 self.f = 0
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 yet
104 self.f = pow(self.G, self.x, self.P)
105 self.transport._expect_packet(MSG_KEXGSS_INIT)
106 return
107 # compute e = g^x mod p (where g=2), and send it
108 self.e = pow(self.G, self.x, self.P)
109 # Initialize GSS-API Key Exchange
110 self.gss_host = self.transport.gss_host
111 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 )
123 def parse_next(self, ptype, m):
124 """
125 Parse the next packet.
127 :param ptype: The (string) type of the incoming packet
128 :param `.Message` m: The packet content
129 """
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))
143 # ## internals...
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 of
150 potential x where the first 63 bits are 1, because some of those will
151 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 break
159 self.x = util.inflate_long(x_bytes)
161 def _parse_kexgss_hostkey(self, m):
162 """
163 Parse the SSH2_MSG_KEXGSS_HOSTKEY message (client mode).
165 :param `.Message` m: The content of the SSH2_MSG_KEXGSS_HOSTKEY message
166 """
167 # client mode
168 host_key = m.get_string()
169 self.transport.host_key = host_key
170 sig = m.get_string()
171 self.transport._verify_key(host_key, sig)
172 self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE)
174 def _parse_kexgss_continue(self, m):
175 """
176 Parse the SSH2_MSG_KEXGSS_CONTINUE message.
178 :param `.Message` m: The content of the SSH2_MSG_KEXGSS_CONTINUE
179 message
180 """
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_token
188 )
189 )
190 self.transport.send_message(m)
191 self.transport._expect_packet(
192 MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
193 )
194 else:
195 pass
197 def _parse_kexgss_complete(self, m):
198 """
199 Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode).
201 :param `.Message` m: The content of the
202 SSH2_MSG_KEXGSS_COMPLETE message
203 """
204 # client mode
205 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 = None
214 if bool:
215 srv_token = m.get_string()
216 K = pow(self.f, self.x, self.P)
217 # okay, build up the hash H of
218 # (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_token
235 )
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 = True
240 self.transport._activate_outbound()
242 def _parse_kexgss_init(self, m):
243 """
244 Parse the SSH2_MSG_KEXGSS_INIT message (server mode).
246 :param `.Message` m: The content of the SSH2_MSG_KEXGSS_INIT message
247 """
248 # server mode
249 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 of
257 # (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_token
273 )
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=True
278 )
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 = True
289 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_ERROR
296 )
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 display
302 the error by throwing an exception (client mode).
304 :param `.Message` m: The content of the SSH2_MSG_KEXGSS_ERROR message
305 :raise SSHException: Contains GSS-API major and minor status as well as
306 the error message and the language tag of the
307 message
308 """
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_msg
320 )
321 )
324class KexGSSGroup14(KexGSSGroup1):
325 """
326 GSS-API / SSPI Authenticated Diffie-Hellman Group14 Key Exchange as defined
327 in `RFC 4462 Section 2
328 <https://tools.ietf.org/html/rfc4462.html#section-2>`_
329 """
331 P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF # noqa
332 G = 2
333 NAME = "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g=="
336class KexGSSGex:
337 """
338 GSS-API / SSPI Authenticated Diffie-Hellman Group Exchange as defined in
339 `RFC 4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_
340 """
342 NAME = "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g=="
343 min_bits = 1024
344 max_bits = 8192
345 preferred_bits = 2048
347 def __init__(self, transport):
348 self.transport = transport
349 self.kexgss = self.transport.kexgss_ctxt
350 self.gss_host = None
351 self.p = None
352 self.q = None
353 self.g = None
354 self.x = None
355 self.e = None
356 self.f = None
357 self.old_style = False
359 def start_kex(self):
360 """
361 Start the GSS-API / SSPI Authenticated Diffie-Hellman Group Exchange
362 """
363 if self.transport.server_mode:
364 self.transport._expect_packet(MSG_KEXGSS_GROUPREQ)
365 return
366 # request a bit range: we accept (min_bits) to (max_bits), but prefer
367 # (preferred_bits). according to the spec, we shouldn't pull the
368 # minimum up above 1024.
369 self.gss_host = self.transport.gss_host
370 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)
378 def parse_next(self, ptype, m):
379 """
380 Parse the next packet.
382 :param ptype: The (string) type of the incoming packet
383 :param `.Message` m: The packet content
384 """
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))
402 # ## internals...
404 def _generate_x(self):
405 # generate an "x" (1 < x < (p-1)/2).
406 q = (self.p - 1) // 2
407 qnorm = util.deflate_long(q, 0)
408 qhbyte = byte_ord(qnorm[0])
409 byte_count = len(qnorm)
410 qmask = 0xFF
411 while not (qhbyte & 0x80):
412 qhbyte <<= 1
413 qmask >>= 1
414 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 break
420 self.x = x
422 def _parse_kexgss_groupreq(self, m):
423 """
424 Parse the SSH2_MSG_KEXGSS_GROUPREQ message (server mode).
426 :param `.Message` m: The content of the
427 SSH2_MSG_KEXGSS_GROUPREQ message
428 """
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 limits
433 if preferredbits > self.max_bits:
434 preferredbits = self.max_bits
435 if preferredbits < self.min_bits:
436 preferredbits = self.min_bits
437 # fix min/max if they're inconsistent. technically, we could just pout
438 # and hang up, but there's no harm in giving them the benefit of the
439 # doubt and just picking a bitsize for them.
440 if minbits > preferredbits:
441 minbits = preferredbits
442 if maxbits < preferredbits:
443 maxbits = preferredbits
444 # now save a copy
445 self.min_bits = minbits
446 self.preferred_bits = preferredbits
447 self.max_bits = maxbits
448 # generate prime
449 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, # noqa
454 "Picking p ({} <= {} <= {} bits)".format(
455 minbits, preferredbits, maxbits
456 ),
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)
466 def _parse_kexgss_group(self, m):
467 """
468 Parse the SSH2_MSG_KEXGSS_GROUP message (client mode).
470 :param `Message` m: The content of the SSH2_MSG_KEXGSS_GROUP message
471 """
472 self.p = m.get_mpint()
473 self.g = m.get_mpint()
474 # reject if p's bit length < 1024 or > 8192
475 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 ) # noqa
484 self._generate_x()
485 # now compute e = g^x mod p
486 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 )
499 def _parse_kexgss_gex_init(self, m):
500 """
501 Parse the SSH2_MSG_KEXGSS_INIT message (server mode).
503 :param `Message` m: The content of the SSH2_MSG_KEXGSS_INIT message
504 """
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 of
515 # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa
516 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_token
536 )
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=True
541 )
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 = True
552 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_ERROR
559 )
561 def _parse_kexgss_hostkey(self, m):
562 """
563 Parse the SSH2_MSG_KEXGSS_HOSTKEY message (client mode).
565 :param `Message` m: The content of the SSH2_MSG_KEXGSS_HOSTKEY message
566 """
567 # client mode
568 host_key = m.get_string()
569 self.transport.host_key = host_key
570 sig = m.get_string()
571 self.transport._verify_key(host_key, sig)
572 self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE)
574 def _parse_kexgss_continue(self, m):
575 """
576 Parse the SSH2_MSG_KEXGSS_CONTINUE message.
578 :param `Message` m: The content of the SSH2_MSG_KEXGSS_CONTINUE message
579 """
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_token
587 )
588 )
589 self.transport.send_message(m)
590 self.transport._expect_packet(
591 MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
592 )
593 else:
594 pass
596 def _parse_kexgss_complete(self, m):
597 """
598 Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode).
600 :param `Message` m: The content of the SSH2_MSG_KEXGSS_COMPLETE message
601 """
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 = None
609 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 of
615 # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa
616 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_token
639 )
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 = True
644 self.transport._activate_outbound()
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 display
650 the error by throwing an exception (client mode).
652 :param `Message` m: The content of the SSH2_MSG_KEXGSS_ERROR message
653 :raise SSHException: Contains GSS-API major and minor status as well as
654 the error message and the language tag of the
655 message
656 """
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_msg
668 )
669 )
672class NullHostKey:
673 """
674 This class represents the Null Host Key for GSS-API Key Exchange as defined
675 in `RFC 4462 Section 5
676 <https://tools.ietf.org/html/rfc4462.html#section-5>`_
677 """
679 def __init__(self):
680 self.key = ""
682 def __str__(self):
683 return self.key
685 def get_name(self):
686 return self.key