attempt to implement support for kex-gex 'old' packet type, which is apparently used by putty (this would only affect paramiko in server mode)
This commit is contained in:
parent
c925c41437
commit
787b0b461d
|
@ -31,7 +31,8 @@ from paramiko.message import Message
|
|||
from paramiko.ssh_exception import SSHException
|
||||
|
||||
|
||||
_MSG_KEXDH_GEX_GROUP, _MSG_KEXDH_GEX_INIT, _MSG_KEXDH_GEX_REPLY, _MSG_KEXDH_GEX_REQUEST = range(31, 35)
|
||||
_MSG_KEXDH_GEX_REQUEST_OLD, _MSG_KEXDH_GEX_GROUP, _MSG_KEXDH_GEX_INIT, \
|
||||
_MSG_KEXDH_GEX_REPLY, _MSG_KEXDH_GEX_REQUEST = range(30, 35)
|
||||
|
||||
|
||||
class KexGex (object):
|
||||
|
@ -49,15 +50,21 @@ class KexGex (object):
|
|||
self.x = None
|
||||
self.e = None
|
||||
self.f = None
|
||||
self.old_style = False
|
||||
|
||||
def start_kex(self):
|
||||
def start_kex(self, _test_old_style=False):
|
||||
if self.transport.server_mode:
|
||||
self.transport._expect_packet(_MSG_KEXDH_GEX_REQUEST)
|
||||
self.transport._expect_packet(_MSG_KEXDH_GEX_REQUEST, _MSG_KEXDH_GEX_REQUEST_OLD)
|
||||
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.
|
||||
m = Message()
|
||||
if _test_old_style:
|
||||
# only used for unit tests: we shouldn't ever send this
|
||||
m.add_byte(chr(_MSG_KEXDH_GEX_REQUEST_OLD))
|
||||
m.add_int(self.preferred_bits)
|
||||
else:
|
||||
m.add_byte(chr(_MSG_KEXDH_GEX_REQUEST))
|
||||
m.add_int(self.min_bits)
|
||||
m.add_int(self.preferred_bits)
|
||||
|
@ -74,6 +81,8 @@ class KexGex (object):
|
|||
return self._parse_kexdh_gex_init(m)
|
||||
elif ptype == _MSG_KEXDH_GEX_REPLY:
|
||||
return self._parse_kexdh_gex_reply(m)
|
||||
elif ptype == _MSG_KEXDH_GEX_REQUEST_OLD:
|
||||
return self._parse_kexdh_gex_request_old(m)
|
||||
raise SSHException('KexGex asked to handle packet type %d' % ptype)
|
||||
|
||||
|
||||
|
@ -132,6 +141,28 @@ class KexGex (object):
|
|||
self.transport._send_message(m)
|
||||
self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
|
||||
|
||||
def _parse_kexdh_gex_request_old(self, m):
|
||||
# same as above, but without min_bits or max_bits (used by older clients like putty)
|
||||
self.preferred_bits = m.get_int()
|
||||
# smoosh the user's preferred size into our own limits
|
||||
if self.preferred_bits > self.max_bits:
|
||||
self.preferred_bits = self.max_bits
|
||||
if self.preferred_bits < self.min_bits:
|
||||
self.preferred_bits = self.min_bits
|
||||
# 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, 'Picking p (~ %d bits)' % (self.preferred_bits,))
|
||||
self.g, self.p = pack.get_modulus(self.min_bits, self.preferred_bits, self.max_bits)
|
||||
m = Message()
|
||||
m.add_byte(chr(_MSG_KEXDH_GEX_GROUP))
|
||||
m.add_mpint(self.p)
|
||||
m.add_mpint(self.g)
|
||||
self.transport._send_message(m)
|
||||
self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
|
||||
self.old_style = True
|
||||
|
||||
def _parse_kexdh_gex_group(self, m):
|
||||
self.p = m.get_mpint()
|
||||
self.g = m.get_mpint()
|
||||
|
@ -162,8 +193,10 @@ class KexGex (object):
|
|||
hm.add(self.transport.remote_version, self.transport.local_version,
|
||||
self.transport.remote_kex_init, self.transport.local_kex_init,
|
||||
key)
|
||||
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)
|
||||
|
|
|
@ -35,18 +35,21 @@ class FakeRandpool (object):
|
|||
def get_bytes(self, n):
|
||||
return chr(0xcc) * n
|
||||
|
||||
|
||||
class FakeKey (object):
|
||||
def __str__(self):
|
||||
return 'fake-key'
|
||||
def sign_ssh_data(self, randpool, H):
|
||||
return 'fake-sig'
|
||||
|
||||
|
||||
class FakeModulusPack (object):
|
||||
P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFFL
|
||||
G = 2
|
||||
def get_modulus(self, min, ask, max):
|
||||
return self.G, self.P
|
||||
|
||||
|
||||
class FakeTransport (object):
|
||||
randpool = FakeRandpool()
|
||||
local_version = 'SSH-2.0-paramiko_1.0'
|
||||
|
@ -56,7 +59,7 @@ class FakeTransport (object):
|
|||
|
||||
def _send_message(self, m):
|
||||
self._message = m
|
||||
def _expect_packet(self, t):
|
||||
def _expect_packet(self, *t):
|
||||
self._expect = t
|
||||
def _set_K_H(self, K, H):
|
||||
self._K = K
|
||||
|
@ -90,7 +93,7 @@ class KexTest (unittest.TestCase):
|
|||
kex.start_kex()
|
||||
x = '1E000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4'
|
||||
self.assertEquals(x, paramiko.util.hexify(str(transport._message)))
|
||||
self.assertEquals(paramiko.kex_group1._MSG_KEXDH_REPLY, transport._expect)
|
||||
self.assertEquals((paramiko.kex_group1._MSG_KEXDH_REPLY,), transport._expect)
|
||||
|
||||
# fake "reply"
|
||||
msg = Message()
|
||||
|
@ -110,7 +113,7 @@ class KexTest (unittest.TestCase):
|
|||
transport.server_mode = True
|
||||
kex = KexGroup1(transport)
|
||||
kex.start_kex()
|
||||
self.assertEquals(paramiko.kex_group1._MSG_KEXDH_INIT, transport._expect)
|
||||
self.assertEquals((paramiko.kex_group1._MSG_KEXDH_INIT,), transport._expect)
|
||||
|
||||
msg = Message()
|
||||
msg.add_mpint(69)
|
||||
|
@ -130,7 +133,7 @@ class KexTest (unittest.TestCase):
|
|||
kex.start_kex()
|
||||
x = '22000004000000080000002000'
|
||||
self.assertEquals(x, paramiko.util.hexify(str(transport._message)))
|
||||
self.assertEquals(paramiko.kex_gex._MSG_KEXDH_GEX_GROUP, transport._expect)
|
||||
self.assertEquals((paramiko.kex_gex._MSG_KEXDH_GEX_GROUP,), transport._expect)
|
||||
|
||||
msg = Message()
|
||||
msg.add_mpint(FakeModulusPack.P)
|
||||
|
@ -139,7 +142,7 @@ class KexTest (unittest.TestCase):
|
|||
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_GROUP, msg)
|
||||
x = '20000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4'
|
||||
self.assertEquals(x, paramiko.util.hexify(str(transport._message)))
|
||||
self.assertEquals(paramiko.kex_gex._MSG_KEXDH_GEX_REPLY, transport._expect)
|
||||
self.assertEquals((paramiko.kex_gex._MSG_KEXDH_GEX_REPLY,), transport._expect)
|
||||
|
||||
msg = Message()
|
||||
msg.add_string('fake-host-key')
|
||||
|
@ -153,12 +156,42 @@ class KexTest (unittest.TestCase):
|
|||
self.assertEquals(('fake-host-key', 'fake-sig'), transport._verify)
|
||||
self.assert_(transport._activated)
|
||||
|
||||
def test_4_gex_server(self):
|
||||
def test_4_gex_old_client(self):
|
||||
transport = FakeTransport()
|
||||
transport.server_mode = False
|
||||
kex = KexGex(transport)
|
||||
kex.start_kex(_test_old_style=True)
|
||||
x = '1E00000800'
|
||||
self.assertEquals(x, paramiko.util.hexify(str(transport._message)))
|
||||
self.assertEquals((paramiko.kex_gex._MSG_KEXDH_GEX_GROUP,), transport._expect)
|
||||
|
||||
msg = Message()
|
||||
msg.add_mpint(FakeModulusPack.P)
|
||||
msg.add_mpint(FakeModulusPack.G)
|
||||
msg.rewind()
|
||||
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_GROUP, msg)
|
||||
x = '20000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4'
|
||||
self.assertEquals(x, paramiko.util.hexify(str(transport._message)))
|
||||
self.assertEquals((paramiko.kex_gex._MSG_KEXDH_GEX_REPLY,), transport._expect)
|
||||
|
||||
msg = Message()
|
||||
msg.add_string('fake-host-key')
|
||||
msg.add_mpint(69)
|
||||
msg.add_string('fake-sig')
|
||||
msg.rewind()
|
||||
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_REPLY, msg)
|
||||
H = 'A265563F2FA87F1A89BF007EE90D58BE2E4A4BD0'
|
||||
self.assertEquals(self.K, transport._K)
|
||||
self.assertEquals(H, paramiko.util.hexify(transport._H))
|
||||
self.assertEquals(('fake-host-key', 'fake-sig'), transport._verify)
|
||||
self.assert_(transport._activated)
|
||||
|
||||
def test_5_gex_server(self):
|
||||
transport = FakeTransport()
|
||||
transport.server_mode = True
|
||||
kex = KexGex(transport)
|
||||
kex.start_kex()
|
||||
self.assertEquals(paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST, transport._expect)
|
||||
self.assertEquals((paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST, paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD), transport._expect)
|
||||
|
||||
msg = Message()
|
||||
msg.add_int(1024)
|
||||
|
@ -168,7 +201,7 @@ class KexTest (unittest.TestCase):
|
|||
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST, msg)
|
||||
x = '1F0000008100FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF0000000102'
|
||||
self.assertEquals(x, paramiko.util.hexify(str(transport._message)))
|
||||
self.assertEquals(paramiko.kex_gex._MSG_KEXDH_GEX_INIT, transport._expect)
|
||||
self.assertEquals((paramiko.kex_gex._MSG_KEXDH_GEX_INIT,), transport._expect)
|
||||
|
||||
msg = Message()
|
||||
msg.add_mpint(12345)
|
||||
|
@ -181,3 +214,30 @@ class KexTest (unittest.TestCase):
|
|||
self.assertEquals(H, paramiko.util.hexify(transport._H))
|
||||
self.assertEquals(x, paramiko.util.hexify(str(transport._message)))
|
||||
self.assert_(transport._activated)
|
||||
|
||||
def test_6_gex_server_with_old_client(self):
|
||||
transport = FakeTransport()
|
||||
transport.server_mode = True
|
||||
kex = KexGex(transport)
|
||||
kex.start_kex()
|
||||
self.assertEquals((paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST, paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD), transport._expect)
|
||||
|
||||
msg = Message()
|
||||
msg.add_int(2048)
|
||||
msg.rewind()
|
||||
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD, msg)
|
||||
x = '1F0000008100FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF0000000102'
|
||||
self.assertEquals(x, paramiko.util.hexify(str(transport._message)))
|
||||
self.assertEquals((paramiko.kex_gex._MSG_KEXDH_GEX_INIT,), transport._expect)
|
||||
|
||||
msg = Message()
|
||||
msg.add_mpint(12345)
|
||||
msg.rewind()
|
||||
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_INIT, msg)
|
||||
K = 67592995013596137876033460028393339951879041140378510871612128162185209509220726296697886624612526735888348020498716482757677848959420073720160491114319163078862905400020959196386947926388406687288901564192071077389283980347784184487280885335302632305026248574716290537036069329724382811853044654824945750581L
|
||||
H = 'B41A06B2E59043CEFC1AE16EC31F1E2D12EC455B'
|
||||
x = '210000000866616B652D6B6579000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D40000000866616B652D736967'
|
||||
self.assertEquals(K, transport._K)
|
||||
self.assertEquals(H, paramiko.util.hexify(transport._H))
|
||||
self.assertEquals(x, paramiko.util.hexify(str(transport._message)))
|
||||
self.assert_(transport._activated)
|
||||
|
|
Loading…
Reference in New Issue