forked from larryli/PuTTY
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Slightly less grotty script to convert OpenSSH known_hosts and known_…
…hosts2 host key files to .REG files for Windows. (renamed from 'hosts2reg' because of 8.3 considerations) [originally from svn r1586]
- Loading branch information
Showing
1 changed file
with
128 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
#! /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 |