mirror of
https://gitlab.sectorq.eu/jaydee/omv_backup.git
synced 2025-07-03 00:23:06 +02:00
added v3
This commit is contained in:
686
venv/lib/python3.11/site-packages/paramiko/kex_gss.py
Normal file
686
venv/lib/python3.11/site-packages/paramiko/kex_gss.py
Normal file
@ -0,0 +1,686 @@
|
||||
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
|
||||
# Copyright (C) 2013-2014 science + computing ag
|
||||
# Author: Sebastian Deiss <sebastian.deiss@t-online.de>
|
||||
#
|
||||
#
|
||||
# This file is part of paramiko.
|
||||
#
|
||||
# Paramiko is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the GNU Lesser General Public License as published by the Free
|
||||
# Software Foundation; either version 2.1 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
"""
|
||||
This module provides GSS-API / SSPI Key Exchange as defined in :rfc:`4462`.
|
||||
|
||||
.. note:: Credential delegation is not supported in server mode.
|
||||
|
||||
.. note::
|
||||
`RFC 4462 Section 2.2
|
||||
<https://tools.ietf.org/html/rfc4462.html#section-2.2>`_ says we are not
|
||||
required to implement GSS-API error messages. Thus, in many methods within
|
||||
this module, if an error occurs an exception will be thrown and the
|
||||
connection will be terminated.
|
||||
|
||||
.. seealso:: :doc:`/api/ssh_gss`
|
||||
|
||||
.. versionadded:: 1.15
|
||||
"""
|
||||
|
||||
import os
|
||||
from hashlib import sha1
|
||||
|
||||
from paramiko.common import (
|
||||
DEBUG,
|
||||
max_byte,
|
||||
zero_byte,
|
||||
byte_chr,
|
||||
byte_mask,
|
||||
byte_ord,
|
||||
)
|
||||
from paramiko import util
|
||||
from paramiko.message import Message
|
||||
from paramiko.ssh_exception import SSHException
|
||||
|
||||
|
||||
(
|
||||
MSG_KEXGSS_INIT,
|
||||
MSG_KEXGSS_CONTINUE,
|
||||
MSG_KEXGSS_COMPLETE,
|
||||
MSG_KEXGSS_HOSTKEY,
|
||||
MSG_KEXGSS_ERROR,
|
||||
) = range(30, 35)
|
||||
(MSG_KEXGSS_GROUPREQ, MSG_KEXGSS_GROUP) = range(40, 42)
|
||||
(
|
||||
c_MSG_KEXGSS_INIT,
|
||||
c_MSG_KEXGSS_CONTINUE,
|
||||
c_MSG_KEXGSS_COMPLETE,
|
||||
c_MSG_KEXGSS_HOSTKEY,
|
||||
c_MSG_KEXGSS_ERROR,
|
||||
) = [byte_chr(c) for c in range(30, 35)]
|
||||
(c_MSG_KEXGSS_GROUPREQ, c_MSG_KEXGSS_GROUP) = [
|
||||
byte_chr(c) for c in range(40, 42)
|
||||
]
|
||||
|
||||
|
||||
class KexGSSGroup1:
|
||||
"""
|
||||
GSS-API / SSPI Authenticated Diffie-Hellman Key Exchange as defined in `RFC
|
||||
4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_
|
||||
"""
|
||||
|
||||
# draft-ietf-secsh-transport-09.txt, page 17
|
||||
P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF # noqa
|
||||
G = 2
|
||||
b7fffffffffffffff = byte_chr(0x7F) + max_byte * 7 # noqa
|
||||
b0000000000000000 = zero_byte * 8 # noqa
|
||||
NAME = "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g=="
|
||||
|
||||
def __init__(self, transport):
|
||||
self.transport = transport
|
||||
self.kexgss = self.transport.kexgss_ctxt
|
||||
self.gss_host = None
|
||||
self.x = 0
|
||||
self.e = 0
|
||||
self.f = 0
|
||||
|
||||
def start_kex(self):
|
||||
"""
|
||||
Start the GSS-API / SSPI Authenticated Diffie-Hellman Key Exchange.
|
||||
"""
|
||||
self._generate_x()
|
||||
if self.transport.server_mode:
|
||||
# compute f = g^x mod p, but don't send it yet
|
||||
self.f = pow(self.G, self.x, self.P)
|
||||
self.transport._expect_packet(MSG_KEXGSS_INIT)
|
||||
return
|
||||
# compute e = g^x mod p (where g=2), and send it
|
||||
self.e = pow(self.G, self.x, self.P)
|
||||
# Initialize GSS-API Key Exchange
|
||||
self.gss_host = self.transport.gss_host
|
||||
m = Message()
|
||||
m.add_byte(c_MSG_KEXGSS_INIT)
|
||||
m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host))
|
||||
m.add_mpint(self.e)
|
||||
self.transport._send_message(m)
|
||||
self.transport._expect_packet(
|
||||
MSG_KEXGSS_HOSTKEY,
|
||||
MSG_KEXGSS_CONTINUE,
|
||||
MSG_KEXGSS_COMPLETE,
|
||||
MSG_KEXGSS_ERROR,
|
||||
)
|
||||
|
||||
def parse_next(self, ptype, m):
|
||||
"""
|
||||
Parse the next packet.
|
||||
|
||||
:param ptype: The (string) type of the incoming packet
|
||||
:param `.Message` m: The packet content
|
||||
"""
|
||||
if self.transport.server_mode and (ptype == MSG_KEXGSS_INIT):
|
||||
return self._parse_kexgss_init(m)
|
||||
elif not self.transport.server_mode and (ptype == MSG_KEXGSS_HOSTKEY):
|
||||
return self._parse_kexgss_hostkey(m)
|
||||
elif self.transport.server_mode and (ptype == MSG_KEXGSS_CONTINUE):
|
||||
return self._parse_kexgss_continue(m)
|
||||
elif not self.transport.server_mode and (ptype == MSG_KEXGSS_COMPLETE):
|
||||
return self._parse_kexgss_complete(m)
|
||||
elif ptype == MSG_KEXGSS_ERROR:
|
||||
return self._parse_kexgss_error(m)
|
||||
msg = "GSS KexGroup1 asked to handle packet type {:d}"
|
||||
raise SSHException(msg.format(ptype))
|
||||
|
||||
# ## internals...
|
||||
|
||||
def _generate_x(self):
|
||||
"""
|
||||
generate an "x" (1 < x < q), where q is (p-1)/2.
|
||||
p is a 128-byte (1024-bit) number, where the first 64 bits are 1.
|
||||
therefore q can be approximated as a 2^1023. we drop the subset of
|
||||
potential x where the first 63 bits are 1, because some of those will
|
||||
be larger than q (but this is a tiny tiny subset of potential x).
|
||||
"""
|
||||
while 1:
|
||||
x_bytes = os.urandom(128)
|
||||
x_bytes = byte_mask(x_bytes[0], 0x7F) + x_bytes[1:]
|
||||
first = x_bytes[:8]
|
||||
if first not in (self.b7fffffffffffffff, self.b0000000000000000):
|
||||
break
|
||||
self.x = util.inflate_long(x_bytes)
|
||||
|
||||
def _parse_kexgss_hostkey(self, m):
|
||||
"""
|
||||
Parse the SSH2_MSG_KEXGSS_HOSTKEY message (client mode).
|
||||
|
||||
:param `.Message` m: The content of the SSH2_MSG_KEXGSS_HOSTKEY message
|
||||
"""
|
||||
# client mode
|
||||
host_key = m.get_string()
|
||||
self.transport.host_key = host_key
|
||||
sig = m.get_string()
|
||||
self.transport._verify_key(host_key, sig)
|
||||
self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE)
|
||||
|
||||
def _parse_kexgss_continue(self, m):
|
||||
"""
|
||||
Parse the SSH2_MSG_KEXGSS_CONTINUE message.
|
||||
|
||||
:param `.Message` m: The content of the SSH2_MSG_KEXGSS_CONTINUE
|
||||
message
|
||||
"""
|
||||
if not self.transport.server_mode:
|
||||
srv_token = m.get_string()
|
||||
m = Message()
|
||||
m.add_byte(c_MSG_KEXGSS_CONTINUE)
|
||||
m.add_string(
|
||||
self.kexgss.ssh_init_sec_context(
|
||||
target=self.gss_host, recv_token=srv_token
|
||||
)
|
||||
)
|
||||
self.transport.send_message(m)
|
||||
self.transport._expect_packet(
|
||||
MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
|
||||
)
|
||||
else:
|
||||
pass
|
||||
|
||||
def _parse_kexgss_complete(self, m):
|
||||
"""
|
||||
Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode).
|
||||
|
||||
:param `.Message` m: The content of the
|
||||
SSH2_MSG_KEXGSS_COMPLETE message
|
||||
"""
|
||||
# client mode
|
||||
if self.transport.host_key is None:
|
||||
self.transport.host_key = NullHostKey()
|
||||
self.f = m.get_mpint()
|
||||
if (self.f < 1) or (self.f > self.P - 1):
|
||||
raise SSHException('Server kex "f" is out of range')
|
||||
mic_token = m.get_string()
|
||||
# This must be TRUE, if there is a GSS-API token in this message.
|
||||
bool = m.get_boolean()
|
||||
srv_token = None
|
||||
if bool:
|
||||
srv_token = m.get_string()
|
||||
K = pow(self.f, self.x, self.P)
|
||||
# okay, build up the hash H of
|
||||
# (V_C || V_S || I_C || I_S || K_S || e || f || K)
|
||||
hm = Message()
|
||||
hm.add(
|
||||
self.transport.local_version,
|
||||
self.transport.remote_version,
|
||||
self.transport.local_kex_init,
|
||||
self.transport.remote_kex_init,
|
||||
)
|
||||
hm.add_string(self.transport.host_key.__str__())
|
||||
hm.add_mpint(self.e)
|
||||
hm.add_mpint(self.f)
|
||||
hm.add_mpint(K)
|
||||
H = sha1(str(hm)).digest()
|
||||
self.transport._set_K_H(K, H)
|
||||
if srv_token is not None:
|
||||
self.kexgss.ssh_init_sec_context(
|
||||
target=self.gss_host, recv_token=srv_token
|
||||
)
|
||||
self.kexgss.ssh_check_mic(mic_token, H)
|
||||
else:
|
||||
self.kexgss.ssh_check_mic(mic_token, H)
|
||||
self.transport.gss_kex_used = True
|
||||
self.transport._activate_outbound()
|
||||
|
||||
def _parse_kexgss_init(self, m):
|
||||
"""
|
||||
Parse the SSH2_MSG_KEXGSS_INIT message (server mode).
|
||||
|
||||
:param `.Message` m: The content of the SSH2_MSG_KEXGSS_INIT message
|
||||
"""
|
||||
# server mode
|
||||
client_token = m.get_string()
|
||||
self.e = m.get_mpint()
|
||||
if (self.e < 1) or (self.e > self.P - 1):
|
||||
raise SSHException('Client kex "e" is out of range')
|
||||
K = pow(self.e, self.x, self.P)
|
||||
self.transport.host_key = NullHostKey()
|
||||
key = self.transport.host_key.__str__()
|
||||
# okay, build up the hash H of
|
||||
# (V_C || V_S || I_C || I_S || K_S || e || f || K)
|
||||
hm = Message()
|
||||
hm.add(
|
||||
self.transport.remote_version,
|
||||
self.transport.local_version,
|
||||
self.transport.remote_kex_init,
|
||||
self.transport.local_kex_init,
|
||||
)
|
||||
hm.add_string(key)
|
||||
hm.add_mpint(self.e)
|
||||
hm.add_mpint(self.f)
|
||||
hm.add_mpint(K)
|
||||
H = sha1(hm.asbytes()).digest()
|
||||
self.transport._set_K_H(K, H)
|
||||
srv_token = self.kexgss.ssh_accept_sec_context(
|
||||
self.gss_host, client_token
|
||||
)
|
||||
m = Message()
|
||||
if self.kexgss._gss_srv_ctxt_status:
|
||||
mic_token = self.kexgss.ssh_get_mic(
|
||||
self.transport.session_id, gss_kex=True
|
||||
)
|
||||
m.add_byte(c_MSG_KEXGSS_COMPLETE)
|
||||
m.add_mpint(self.f)
|
||||
m.add_string(mic_token)
|
||||
if srv_token is not None:
|
||||
m.add_boolean(True)
|
||||
m.add_string(srv_token)
|
||||
else:
|
||||
m.add_boolean(False)
|
||||
self.transport._send_message(m)
|
||||
self.transport.gss_kex_used = True
|
||||
self.transport._activate_outbound()
|
||||
else:
|
||||
m.add_byte(c_MSG_KEXGSS_CONTINUE)
|
||||
m.add_string(srv_token)
|
||||
self.transport._send_message(m)
|
||||
self.transport._expect_packet(
|
||||
MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
|
||||
)
|
||||
|
||||
def _parse_kexgss_error(self, m):
|
||||
"""
|
||||
Parse the SSH2_MSG_KEXGSS_ERROR message (client mode).
|
||||
The server may send a GSS-API error message. if it does, we display
|
||||
the error by throwing an exception (client mode).
|
||||
|
||||
:param `.Message` m: The content of the SSH2_MSG_KEXGSS_ERROR message
|
||||
:raise SSHException: Contains GSS-API major and minor status as well as
|
||||
the error message and the language tag of the
|
||||
message
|
||||
"""
|
||||
maj_status = m.get_int()
|
||||
min_status = m.get_int()
|
||||
err_msg = m.get_string()
|
||||
m.get_string() # we don't care about the language!
|
||||
raise SSHException(
|
||||
"""GSS-API Error:
|
||||
Major Status: {}
|
||||
Minor Status: {}
|
||||
Error Message: {}
|
||||
""".format(
|
||||
maj_status, min_status, err_msg
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class KexGSSGroup14(KexGSSGroup1):
|
||||
"""
|
||||
GSS-API / SSPI Authenticated Diffie-Hellman Group14 Key Exchange as defined
|
||||
in `RFC 4462 Section 2
|
||||
<https://tools.ietf.org/html/rfc4462.html#section-2>`_
|
||||
"""
|
||||
|
||||
P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF # noqa
|
||||
G = 2
|
||||
NAME = "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g=="
|
||||
|
||||
|
||||
class KexGSSGex:
|
||||
"""
|
||||
GSS-API / SSPI Authenticated Diffie-Hellman Group Exchange as defined in
|
||||
`RFC 4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_
|
||||
"""
|
||||
|
||||
NAME = "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g=="
|
||||
min_bits = 1024
|
||||
max_bits = 8192
|
||||
preferred_bits = 2048
|
||||
|
||||
def __init__(self, transport):
|
||||
self.transport = transport
|
||||
self.kexgss = self.transport.kexgss_ctxt
|
||||
self.gss_host = None
|
||||
self.p = None
|
||||
self.q = None
|
||||
self.g = None
|
||||
self.x = None
|
||||
self.e = None
|
||||
self.f = None
|
||||
self.old_style = False
|
||||
|
||||
def start_kex(self):
|
||||
"""
|
||||
Start the GSS-API / SSPI Authenticated Diffie-Hellman Group Exchange
|
||||
"""
|
||||
if self.transport.server_mode:
|
||||
self.transport._expect_packet(MSG_KEXGSS_GROUPREQ)
|
||||
return
|
||||
# request a bit range: we accept (min_bits) to (max_bits), but prefer
|
||||
# (preferred_bits). according to the spec, we shouldn't pull the
|
||||
# minimum up above 1024.
|
||||
self.gss_host = self.transport.gss_host
|
||||
m = Message()
|
||||
m.add_byte(c_MSG_KEXGSS_GROUPREQ)
|
||||
m.add_int(self.min_bits)
|
||||
m.add_int(self.preferred_bits)
|
||||
m.add_int(self.max_bits)
|
||||
self.transport._send_message(m)
|
||||
self.transport._expect_packet(MSG_KEXGSS_GROUP)
|
||||
|
||||
def parse_next(self, ptype, m):
|
||||
"""
|
||||
Parse the next packet.
|
||||
|
||||
:param ptype: The (string) type of the incoming packet
|
||||
:param `.Message` m: The packet content
|
||||
"""
|
||||
if ptype == MSG_KEXGSS_GROUPREQ:
|
||||
return self._parse_kexgss_groupreq(m)
|
||||
elif ptype == MSG_KEXGSS_GROUP:
|
||||
return self._parse_kexgss_group(m)
|
||||
elif ptype == MSG_KEXGSS_INIT:
|
||||
return self._parse_kexgss_gex_init(m)
|
||||
elif ptype == MSG_KEXGSS_HOSTKEY:
|
||||
return self._parse_kexgss_hostkey(m)
|
||||
elif ptype == MSG_KEXGSS_CONTINUE:
|
||||
return self._parse_kexgss_continue(m)
|
||||
elif ptype == MSG_KEXGSS_COMPLETE:
|
||||
return self._parse_kexgss_complete(m)
|
||||
elif ptype == MSG_KEXGSS_ERROR:
|
||||
return self._parse_kexgss_error(m)
|
||||
msg = "KexGex asked to handle packet type {:d}"
|
||||
raise SSHException(msg.format(ptype))
|
||||
|
||||
# ## internals...
|
||||
|
||||
def _generate_x(self):
|
||||
# generate an "x" (1 < x < (p-1)/2).
|
||||
q = (self.p - 1) // 2
|
||||
qnorm = util.deflate_long(q, 0)
|
||||
qhbyte = byte_ord(qnorm[0])
|
||||
byte_count = len(qnorm)
|
||||
qmask = 0xFF
|
||||
while not (qhbyte & 0x80):
|
||||
qhbyte <<= 1
|
||||
qmask >>= 1
|
||||
while True:
|
||||
x_bytes = os.urandom(byte_count)
|
||||
x_bytes = byte_mask(x_bytes[0], qmask) + x_bytes[1:]
|
||||
x = util.inflate_long(x_bytes, 1)
|
||||
if (x > 1) and (x < q):
|
||||
break
|
||||
self.x = x
|
||||
|
||||
def _parse_kexgss_groupreq(self, m):
|
||||
"""
|
||||
Parse the SSH2_MSG_KEXGSS_GROUPREQ message (server mode).
|
||||
|
||||
:param `.Message` m: The content of the
|
||||
SSH2_MSG_KEXGSS_GROUPREQ message
|
||||
"""
|
||||
minbits = m.get_int()
|
||||
preferredbits = m.get_int()
|
||||
maxbits = m.get_int()
|
||||
# smoosh the user's preferred size into our own limits
|
||||
if preferredbits > self.max_bits:
|
||||
preferredbits = self.max_bits
|
||||
if preferredbits < self.min_bits:
|
||||
preferredbits = self.min_bits
|
||||
# fix min/max if they're inconsistent. technically, we could just pout
|
||||
# and hang up, but there's no harm in giving them the benefit of the
|
||||
# doubt and just picking a bitsize for them.
|
||||
if minbits > preferredbits:
|
||||
minbits = preferredbits
|
||||
if maxbits < preferredbits:
|
||||
maxbits = preferredbits
|
||||
# now save a copy
|
||||
self.min_bits = minbits
|
||||
self.preferred_bits = preferredbits
|
||||
self.max_bits = maxbits
|
||||
# generate prime
|
||||
pack = self.transport._get_modulus_pack()
|
||||
if pack is None:
|
||||
raise SSHException("Can't do server-side gex with no modulus pack")
|
||||
self.transport._log(
|
||||
DEBUG, # noqa
|
||||
"Picking p ({} <= {} <= {} bits)".format(
|
||||
minbits, preferredbits, maxbits
|
||||
),
|
||||
)
|
||||
self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits)
|
||||
m = Message()
|
||||
m.add_byte(c_MSG_KEXGSS_GROUP)
|
||||
m.add_mpint(self.p)
|
||||
m.add_mpint(self.g)
|
||||
self.transport._send_message(m)
|
||||
self.transport._expect_packet(MSG_KEXGSS_INIT)
|
||||
|
||||
def _parse_kexgss_group(self, m):
|
||||
"""
|
||||
Parse the SSH2_MSG_KEXGSS_GROUP message (client mode).
|
||||
|
||||
:param `Message` m: The content of the SSH2_MSG_KEXGSS_GROUP message
|
||||
"""
|
||||
self.p = m.get_mpint()
|
||||
self.g = m.get_mpint()
|
||||
# reject if p's bit length < 1024 or > 8192
|
||||
bitlen = util.bit_length(self.p)
|
||||
if (bitlen < 1024) or (bitlen > 8192):
|
||||
raise SSHException(
|
||||
"Server-generated gex p (don't ask) is out of range "
|
||||
"({} bits)".format(bitlen)
|
||||
)
|
||||
self.transport._log(
|
||||
DEBUG, "Got server p ({} bits)".format(bitlen)
|
||||
) # noqa
|
||||
self._generate_x()
|
||||
# now compute e = g^x mod p
|
||||
self.e = pow(self.g, self.x, self.p)
|
||||
m = Message()
|
||||
m.add_byte(c_MSG_KEXGSS_INIT)
|
||||
m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host))
|
||||
m.add_mpint(self.e)
|
||||
self.transport._send_message(m)
|
||||
self.transport._expect_packet(
|
||||
MSG_KEXGSS_HOSTKEY,
|
||||
MSG_KEXGSS_CONTINUE,
|
||||
MSG_KEXGSS_COMPLETE,
|
||||
MSG_KEXGSS_ERROR,
|
||||
)
|
||||
|
||||
def _parse_kexgss_gex_init(self, m):
|
||||
"""
|
||||
Parse the SSH2_MSG_KEXGSS_INIT message (server mode).
|
||||
|
||||
:param `Message` m: The content of the SSH2_MSG_KEXGSS_INIT message
|
||||
"""
|
||||
client_token = m.get_string()
|
||||
self.e = m.get_mpint()
|
||||
if (self.e < 1) or (self.e > self.p - 1):
|
||||
raise SSHException('Client kex "e" is out of range')
|
||||
self._generate_x()
|
||||
self.f = pow(self.g, self.x, self.p)
|
||||
K = pow(self.e, self.x, self.p)
|
||||
self.transport.host_key = NullHostKey()
|
||||
key = self.transport.host_key.__str__()
|
||||
# okay, build up the hash H of
|
||||
# (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa
|
||||
hm = Message()
|
||||
hm.add(
|
||||
self.transport.remote_version,
|
||||
self.transport.local_version,
|
||||
self.transport.remote_kex_init,
|
||||
self.transport.local_kex_init,
|
||||
key,
|
||||
)
|
||||
hm.add_int(self.min_bits)
|
||||
hm.add_int(self.preferred_bits)
|
||||
hm.add_int(self.max_bits)
|
||||
hm.add_mpint(self.p)
|
||||
hm.add_mpint(self.g)
|
||||
hm.add_mpint(self.e)
|
||||
hm.add_mpint(self.f)
|
||||
hm.add_mpint(K)
|
||||
H = sha1(hm.asbytes()).digest()
|
||||
self.transport._set_K_H(K, H)
|
||||
srv_token = self.kexgss.ssh_accept_sec_context(
|
||||
self.gss_host, client_token
|
||||
)
|
||||
m = Message()
|
||||
if self.kexgss._gss_srv_ctxt_status:
|
||||
mic_token = self.kexgss.ssh_get_mic(
|
||||
self.transport.session_id, gss_kex=True
|
||||
)
|
||||
m.add_byte(c_MSG_KEXGSS_COMPLETE)
|
||||
m.add_mpint(self.f)
|
||||
m.add_string(mic_token)
|
||||
if srv_token is not None:
|
||||
m.add_boolean(True)
|
||||
m.add_string(srv_token)
|
||||
else:
|
||||
m.add_boolean(False)
|
||||
self.transport._send_message(m)
|
||||
self.transport.gss_kex_used = True
|
||||
self.transport._activate_outbound()
|
||||
else:
|
||||
m.add_byte(c_MSG_KEXGSS_CONTINUE)
|
||||
m.add_string(srv_token)
|
||||
self.transport._send_message(m)
|
||||
self.transport._expect_packet(
|
||||
MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
|
||||
)
|
||||
|
||||
def _parse_kexgss_hostkey(self, m):
|
||||
"""
|
||||
Parse the SSH2_MSG_KEXGSS_HOSTKEY message (client mode).
|
||||
|
||||
:param `Message` m: The content of the SSH2_MSG_KEXGSS_HOSTKEY message
|
||||
"""
|
||||
# client mode
|
||||
host_key = m.get_string()
|
||||
self.transport.host_key = host_key
|
||||
sig = m.get_string()
|
||||
self.transport._verify_key(host_key, sig)
|
||||
self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE)
|
||||
|
||||
def _parse_kexgss_continue(self, m):
|
||||
"""
|
||||
Parse the SSH2_MSG_KEXGSS_CONTINUE message.
|
||||
|
||||
:param `Message` m: The content of the SSH2_MSG_KEXGSS_CONTINUE message
|
||||
"""
|
||||
if not self.transport.server_mode:
|
||||
srv_token = m.get_string()
|
||||
m = Message()
|
||||
m.add_byte(c_MSG_KEXGSS_CONTINUE)
|
||||
m.add_string(
|
||||
self.kexgss.ssh_init_sec_context(
|
||||
target=self.gss_host, recv_token=srv_token
|
||||
)
|
||||
)
|
||||
self.transport.send_message(m)
|
||||
self.transport._expect_packet(
|
||||
MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
|
||||
)
|
||||
else:
|
||||
pass
|
||||
|
||||
def _parse_kexgss_complete(self, m):
|
||||
"""
|
||||
Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode).
|
||||
|
||||
:param `Message` m: The content of the SSH2_MSG_KEXGSS_COMPLETE message
|
||||
"""
|
||||
if self.transport.host_key is None:
|
||||
self.transport.host_key = NullHostKey()
|
||||
self.f = m.get_mpint()
|
||||
mic_token = m.get_string()
|
||||
# This must be TRUE, if there is a GSS-API token in this message.
|
||||
bool = m.get_boolean()
|
||||
srv_token = None
|
||||
if bool:
|
||||
srv_token = m.get_string()
|
||||
if (self.f < 1) or (self.f > self.p - 1):
|
||||
raise SSHException('Server kex "f" is out of range')
|
||||
K = pow(self.f, self.x, self.p)
|
||||
# okay, build up the hash H of
|
||||
# (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa
|
||||
hm = Message()
|
||||
hm.add(
|
||||
self.transport.local_version,
|
||||
self.transport.remote_version,
|
||||
self.transport.local_kex_init,
|
||||
self.transport.remote_kex_init,
|
||||
self.transport.host_key.__str__(),
|
||||
)
|
||||
if not self.old_style:
|
||||
hm.add_int(self.min_bits)
|
||||
hm.add_int(self.preferred_bits)
|
||||
if not self.old_style:
|
||||
hm.add_int(self.max_bits)
|
||||
hm.add_mpint(self.p)
|
||||
hm.add_mpint(self.g)
|
||||
hm.add_mpint(self.e)
|
||||
hm.add_mpint(self.f)
|
||||
hm.add_mpint(K)
|
||||
H = sha1(hm.asbytes()).digest()
|
||||
self.transport._set_K_H(K, H)
|
||||
if srv_token is not None:
|
||||
self.kexgss.ssh_init_sec_context(
|
||||
target=self.gss_host, recv_token=srv_token
|
||||
)
|
||||
self.kexgss.ssh_check_mic(mic_token, H)
|
||||
else:
|
||||
self.kexgss.ssh_check_mic(mic_token, H)
|
||||
self.transport.gss_kex_used = True
|
||||
self.transport._activate_outbound()
|
||||
|
||||
def _parse_kexgss_error(self, m):
|
||||
"""
|
||||
Parse the SSH2_MSG_KEXGSS_ERROR message (client mode).
|
||||
The server may send a GSS-API error message. if it does, we display
|
||||
the error by throwing an exception (client mode).
|
||||
|
||||
:param `Message` m: The content of the SSH2_MSG_KEXGSS_ERROR message
|
||||
:raise SSHException: Contains GSS-API major and minor status as well as
|
||||
the error message and the language tag of the
|
||||
message
|
||||
"""
|
||||
maj_status = m.get_int()
|
||||
min_status = m.get_int()
|
||||
err_msg = m.get_string()
|
||||
m.get_string() # we don't care about the language (lang_tag)!
|
||||
raise SSHException(
|
||||
"""GSS-API Error:
|
||||
Major Status: {}
|
||||
Minor Status: {}
|
||||
Error Message: {}
|
||||
""".format(
|
||||
maj_status, min_status, err_msg
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class NullHostKey:
|
||||
"""
|
||||
This class represents the Null Host Key for GSS-API Key Exchange as defined
|
||||
in `RFC 4462 Section 5
|
||||
<https://tools.ietf.org/html/rfc4462.html#section-5>`_
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.key = ""
|
||||
|
||||
def __str__(self):
|
||||
return self.key
|
||||
|
||||
def get_name(self):
|
||||
return self.key
|
Reference in New Issue
Block a user