putty/contrib/kh2reg.py

129 строки
4.5 KiB
Python
Executable File

#! /usr/bin/env python
# $Id: kh2reg.py,v 1.1 2002/03/10 22:00:06 jacob Exp $
# Convert OpenSSH known_hosts and known_hosts2 files to "new format" PuTTY
# host keys in a Windows .REG file (double-click to install).
# usage: hosts2reg.py known_hosts1 2 3 4 ... > hosts.reg
# Line endings are someone else's problem as is traditional.
# Developed for Python 1.5.2.
import fileinput
import base64
import struct
import string
import re
import sys
def mungestr(s):
"Duplicate of PuTTY's mungestr() in winstore.c:1.10 for Registry keys"
candot = 0
r = ""
for c in s:
if c in ' \*?%~' or ord(c)<ord(' ') or (c == '.' and not candot):
r = r + ("%%%02X" % ord(c))
else:
r = r + c
candot = 1
return r
def strtolong(s):
"Convert arbitrary-length big-endian binary data to a Python long"
bytes = struct.unpack(">%luB" % len(s), s)
return reduce ((lambda a, b: (long(a) << 8) + long(b)), bytes)
def longtohex(n):
"""Convert long int to lower-case hex.
Ick, Python (at least in 1.5.2) doesn't appear to have a way to
turn a long int into an unadorned hex string -- % gets upset if the
number is too big, and raw hex() uses uppercase (sometimes), and
adds unwanted "0x...L" around it."""
plain=string.lower(re.match(r"0x([0-9A-Fa-f]*)l?$", hex(n), re.I).group(1))
return "0x" + plain
# Output REG file header.
sys.stdout.write("""REGEDIT4
[HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys]
""")
# Now process all known_hosts input.
for line in fileinput.input():
try:
# Remove leading/trailing whitespace (should zap CR and LF)
line = string.strip (line)
# Skip blanks and comments
if line == '' or line[0] == '#':
raise "Skipping input line"
# Split line on spaces.
fields = string.split (line, ' ')
# Common fields
hostpat = fields[0]
magicnumbers = [] # placeholder
keytype = "" # placeholder
# Grotty heuristic to distinguish known_hosts from known_hosts2:
# is second field entirely decimal digits?
if re.match (r"\d*$", fields[1]):
# Treat as SSH1-type host key.
# Format: hostpat bits10 exp10 mod10 comment...
# (PuTTY doesn't store the number of bits.)
magicnumbers = map (long, fields[2:4])
keytype = "rsa"
else:
# Treat as SSH2-type host key.
# Format: hostpat keytype keyblob64 comment...
sshkeytype, blob = fields[1], base64.decodestring (fields[2])
# 'blob' consists of a number of
# uint32 N (big-endian)
# uint8[N] field_data
subfields = []
while blob:
sizefmt = ">L"
(size,) = struct.unpack (sizefmt, blob[0:4])
size = int(size) # req'd for slicage
(data,) = struct.unpack (">%lus" % size, blob[4:size+4])
subfields.append(data)
blob = blob [struct.calcsize(sizefmt) + size : ]
# The first field is keytype again, and the rest we can treat as
# an opaque list of bignums (same numbers and order as stored
# by PuTTY). (currently embedded keytype is ignored entirely)
magicnumbers = map (strtolong, subfields[1:])
# Translate key type into something PuTTY can use.
if sshkeytype == "ssh-rsa": keytype = "rsa2"
elif sshkeytype == "ssh-dss": keytype = "dss"
else:
raise "Unknown SSH key type", sshkeytype
# Now print out one line per host pattern, discarding wildcards.
for host in string.split (hostpat, ','):
if re.search (r"[*?!]", host):
sys.stderr.write("Skipping wildcard host pattern '%s'\n"
% host)
continue
else:
# Slightly bizarre registry key format: 'type@port:hostname'
# As far as I know, the input never specifies a port.
port = 22
# XXX: does PuTTY do anything useful with literal IP[v4]s?
key = keytype + ("@%d:%s" % (port, host))
value = string.join (map (longtohex, magicnumbers), ',')
# XXX: worry about double quotes?
sys.stdout.write("\"%s\"=\"%s\"\n" % (mungestr(key), value))
except "Unknown SSH key type", k:
sys.stderr.write("Unknown SSH key type '%s', skipping\n" % k)
except "Skipping input line":
pass