mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 01:48:00 +00:00
cc9d920c78
It now expects its standard input to be connected to the same PuTTY its standard output is talking to, i.e. expects to be invoked as a proxy command. It conducts the same sample key exchange as it used to, but now reads the SSH greeting and first couple of packets back from PuTTY and minimally checks that they're something like what it was expecting. (In the process, I've also fixed a mistake in the Python message code enumeration, which caused one of those expect() calls to fail.)
130 lines
3.3 KiB
Python
130 lines
3.3 KiB
Python
# Python module to make it easy to manually encode SSH packets, by
|
|
# supporting the various uint32, string, mpint primitives.
|
|
#
|
|
# The idea of this is that you can use it to manually construct key
|
|
# exchange sequences of interesting kinds, for testing purposes.
|
|
|
|
import struct, random
|
|
|
|
def boolean(b):
|
|
return "\1" if b else "\0"
|
|
|
|
def byte(b):
|
|
assert 0 <= b < 0x100
|
|
return chr(b)
|
|
|
|
def uint32(u):
|
|
assert 0 <= u < 0x100000000
|
|
return struct.pack(">I", u)
|
|
|
|
def uint64(u):
|
|
assert 0 <= u < 0x10000000000000000
|
|
return struct.pack(">L", u)
|
|
|
|
def string(s):
|
|
return uint32(len(s)) + s
|
|
|
|
def mpint(m):
|
|
s = ""
|
|
lastbyte = 0
|
|
while m > 0:
|
|
lastbyte = m & 0xFF
|
|
s = chr(lastbyte) + s
|
|
m >>= 8
|
|
if lastbyte & 0x80:
|
|
s = "\0" + s
|
|
return string(s)
|
|
|
|
def name_list(ns):
|
|
s = ""
|
|
for n in ns:
|
|
assert "," not in n
|
|
if s != "":
|
|
s += ","
|
|
s += n
|
|
return string(s)
|
|
|
|
def ssh_rsa_key_blob(modulus, exponent):
|
|
return string(string("ssh-rsa") + mpint(modulus) + mpint(exponent))
|
|
|
|
def ssh_rsa_signature_blob(signature):
|
|
return string(string("ssh-rsa") + mpint(signature))
|
|
|
|
def greeting(string):
|
|
# Greeting at the start of an SSH connection.
|
|
return string + "\r\n"
|
|
|
|
# Packet types.
|
|
SSH2_MSG_DISCONNECT = 1
|
|
SSH2_MSG_IGNORE = 2
|
|
SSH2_MSG_UNIMPLEMENTED = 3
|
|
SSH2_MSG_DEBUG = 4
|
|
SSH2_MSG_SERVICE_REQUEST = 5
|
|
SSH2_MSG_SERVICE_ACCEPT = 6
|
|
SSH2_MSG_KEXINIT = 20
|
|
SSH2_MSG_NEWKEYS = 21
|
|
SSH2_MSG_KEXDH_INIT = 30
|
|
SSH2_MSG_KEXDH_REPLY = 31
|
|
SSH2_MSG_KEX_DH_GEX_REQUEST_OLD = 30
|
|
SSH2_MSG_KEX_DH_GEX_GROUP = 31
|
|
SSH2_MSG_KEX_DH_GEX_INIT = 32
|
|
SSH2_MSG_KEX_DH_GEX_REPLY = 33
|
|
SSH2_MSG_KEX_DH_GEX_REQUEST = 34
|
|
SSH2_MSG_KEXRSA_PUBKEY = 30
|
|
SSH2_MSG_KEXRSA_SECRET = 31
|
|
SSH2_MSG_KEXRSA_DONE = 32
|
|
SSH2_MSG_USERAUTH_REQUEST = 50
|
|
SSH2_MSG_USERAUTH_FAILURE = 51
|
|
SSH2_MSG_USERAUTH_SUCCESS = 52
|
|
SSH2_MSG_USERAUTH_BANNER = 53
|
|
SSH2_MSG_USERAUTH_PK_OK = 60
|
|
SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ = 60
|
|
SSH2_MSG_USERAUTH_INFO_REQUEST = 60
|
|
SSH2_MSG_USERAUTH_INFO_RESPONSE = 61
|
|
SSH2_MSG_GLOBAL_REQUEST = 80
|
|
SSH2_MSG_REQUEST_SUCCESS = 81
|
|
SSH2_MSG_REQUEST_FAILURE = 82
|
|
SSH2_MSG_CHANNEL_OPEN = 90
|
|
SSH2_MSG_CHANNEL_OPEN_CONFIRMATION = 91
|
|
SSH2_MSG_CHANNEL_OPEN_FAILURE = 92
|
|
SSH2_MSG_CHANNEL_WINDOW_ADJUST = 93
|
|
SSH2_MSG_CHANNEL_DATA = 94
|
|
SSH2_MSG_CHANNEL_EXTENDED_DATA = 95
|
|
SSH2_MSG_CHANNEL_EOF = 96
|
|
SSH2_MSG_CHANNEL_CLOSE = 97
|
|
SSH2_MSG_CHANNEL_REQUEST = 98
|
|
SSH2_MSG_CHANNEL_SUCCESS = 99
|
|
SSH2_MSG_CHANNEL_FAILURE = 100
|
|
SSH2_MSG_USERAUTH_GSSAPI_RESPONSE = 60
|
|
SSH2_MSG_USERAUTH_GSSAPI_TOKEN = 61
|
|
SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE = 63
|
|
SSH2_MSG_USERAUTH_GSSAPI_ERROR = 64
|
|
SSH2_MSG_USERAUTH_GSSAPI_ERRTOK = 65
|
|
SSH2_MSG_USERAUTH_GSSAPI_MIC = 66
|
|
|
|
def clearpkt(msgtype, *stuff):
|
|
# SSH-2 binary packet, in the cleartext format used for initial
|
|
# setup and kex.
|
|
s = byte(msgtype)
|
|
for thing in stuff:
|
|
s += thing
|
|
padlen = 0
|
|
while padlen < 4 or len(s) % 8 != 3:
|
|
padlen += 1
|
|
s += byte(random.randint(0,255))
|
|
s = byte(padlen) + s
|
|
return string(s)
|
|
|
|
def decode_uint32(s):
|
|
assert len(s) == 4
|
|
return struct.unpack(">I", s)[0]
|
|
|
|
def read_clearpkt(fh):
|
|
length_field = fh.read(4)
|
|
s = fh.read(decode_uint32(length_field))
|
|
import sys
|
|
padlen = ord(s[0])
|
|
s = s[1:-padlen]
|
|
msgtype = ord(s[0])
|
|
return msgtype, s[1:]
|