Add sshwot-verify
This commit is contained in:
parent
aee9cec62e
commit
973ba3c63a
|
@ -6,3 +6,4 @@ __pycache__
|
||||||
build
|
build
|
||||||
sshwot-export-known-hosts
|
sshwot-export-known-hosts
|
||||||
sshwot-filter
|
sshwot-filter
|
||||||
|
sshwot-verify
|
||||||
|
|
14
Makefile
14
Makefile
|
@ -1,4 +1,4 @@
|
||||||
BINS:=sshwot-export-known-hosts sshwot-filter
|
BINS:=sshwot-export-known-hosts sshwot-filter sshwot-verify
|
||||||
|
|
||||||
SSHWOT_EXPORT_KNOWN_HOSTS_MAIN:=src/main-export-known-hosts.py
|
SSHWOT_EXPORT_KNOWN_HOSTS_MAIN:=src/main-export-known-hosts.py
|
||||||
SSHWOT_EXPORT_KNOWN_HOSTS_DEPS:=src/entry.py src/hashing.py src/process_known_hosts.py src/write_file.py
|
SSHWOT_EXPORT_KNOWN_HOSTS_DEPS:=src/entry.py src/hashing.py src/process_known_hosts.py src/write_file.py
|
||||||
|
@ -6,6 +6,9 @@ SSHWOT_EXPORT_KNOWN_HOSTS_DEPS:=src/entry.py src/hashing.py src/process_known_ho
|
||||||
SSHWOT_FILTER_MAIN:=src/main-filter.py
|
SSHWOT_FILTER_MAIN:=src/main-filter.py
|
||||||
SSHWOT_FILTER_DEPS:=src/entry.py src/hashing.py src/default_files.py src/read_file.py src/write_file.py
|
SSHWOT_FILTER_DEPS:=src/entry.py src/hashing.py src/default_files.py src/read_file.py src/write_file.py
|
||||||
|
|
||||||
|
SSHWOT_VERIFY_MAIN:=src/main-verify.py
|
||||||
|
SSHWOT_VERIFY_DEPS:=src/check_fingerprint.py src/default_files.py src/entry.py src/hashing.py src/read_file.py
|
||||||
|
|
||||||
all: $(BINS)
|
all: $(BINS)
|
||||||
|
|
||||||
sshwot-export-known-hosts: $(SSHWOT_EXPORT_KNOWN_HOSTS_MAIN) $(SSHWOT_EXPORT_KNOWN_HOSTS_DEPS)
|
sshwot-export-known-hosts: $(SSHWOT_EXPORT_KNOWN_HOSTS_MAIN) $(SSHWOT_EXPORT_KNOWN_HOSTS_DEPS)
|
||||||
|
@ -26,6 +29,15 @@ sshwot-filter: $(SSHWOT_FILTER_MAIN) $(SSHWOT_FILTER_DEPS)
|
||||||
cat build/$@.zip >> $@
|
cat build/$@.zip >> $@
|
||||||
chmod +x $@
|
chmod +x $@
|
||||||
|
|
||||||
|
sshwot-verify: $(SSHWOT_VERIFY_MAIN) $(SSHWOT_VERIFY_DEPS)
|
||||||
|
mkdir -p build/$@
|
||||||
|
cp $(SSHWOT_VERIFY_DEPS) build/$@/
|
||||||
|
cp $(SSHWOT_VERIFY_MAIN) build/$@/__main__.py
|
||||||
|
zip --quiet --junk-paths build/$@.zip build/$@/*.py
|
||||||
|
echo '#!/usr/bin/env python3' > $@
|
||||||
|
cat build/$@.zip >> $@
|
||||||
|
chmod +x $@
|
||||||
|
|
||||||
.PHONY: all clean distclean buildclean
|
.PHONY: all clean distclean buildclean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
def open_all():
|
def list_all():
|
||||||
"""open_all() → [file(rb)]
|
"""list_all() → [str]
|
||||||
Open the default sshwot files"""
|
List the default sshwot files"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
homedir = os.environ['HOME']
|
homedir = os.environ['HOME']
|
||||||
|
@ -17,10 +17,20 @@ def open_all():
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Read all the .sshwot files from /.sshwot by default
|
# Read all the .sshwot files from /.sshwot by default
|
||||||
files = []
|
paths = []
|
||||||
for dir_entry in sshwot_dir:
|
for dir_entry in sshwot_dir:
|
||||||
if dir_entry.split('.')[-1] == 'sshwot':
|
if dir_entry.split('.')[-1] == 'sshwot':
|
||||||
path = os.path.join(sshwot_dir_path, dir_entry)
|
path = os.path.join(sshwot_dir_path, dir_entry)
|
||||||
files.append(open(path, 'rb'))
|
paths.append(path)
|
||||||
|
|
||||||
|
return paths
|
||||||
|
|
||||||
|
def open_all():
|
||||||
|
"""open_all() → [file(rb)]
|
||||||
|
Open the default sshwot files"""
|
||||||
|
|
||||||
|
files = []
|
||||||
|
for path in list_all():
|
||||||
|
files.append(open(path, 'rb'))
|
||||||
|
|
||||||
return files
|
return files
|
||||||
|
|
|
@ -90,6 +90,11 @@ def main():
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
# We encode this, because hashing.base64dec expects bytes
|
# We encode this, because hashing.base64dec expects bytes
|
||||||
fingerprint = hashing.base64dec(args.fingerprint[7:].encode())
|
fingerprint = hashing.base64dec(args.fingerprint[7:].encode())
|
||||||
|
# A valid sha256 fingerprint is 32 bytes
|
||||||
|
if len(fingerprint) < 32:
|
||||||
|
raise Exception('Fingerprint too short')
|
||||||
|
elif len(fingerprint) > 32:
|
||||||
|
raise Exception('Fingerprint too long')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
fingerprint = None
|
fingerprint = None
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import check_fingerprint
|
||||||
|
import default_files
|
||||||
|
import entry
|
||||||
|
import hashing
|
||||||
|
import read_file
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# TODO: Do known_hosts files too
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description = """Search sshwot files for matching fingerprints.""",
|
||||||
|
# We want to provide help on --help, but the default thing
|
||||||
|
# also adds -h, which we don't want
|
||||||
|
add_help = False
|
||||||
|
)
|
||||||
|
|
||||||
|
# --help to get help
|
||||||
|
parser.add_argument('--help',
|
||||||
|
action = 'help',
|
||||||
|
help = 'show this help message and exit'
|
||||||
|
)
|
||||||
|
|
||||||
|
# -p/--port for port, but host is a positional argument
|
||||||
|
parser.add_argument('-p', '--port',
|
||||||
|
action = 'store',
|
||||||
|
dest = 'port',
|
||||||
|
# Automatically convert to integer
|
||||||
|
type = int,
|
||||||
|
help = 'the port associated with the given host'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Host and fingerprint are required
|
||||||
|
parser.add_argument('host',
|
||||||
|
help = 'the domain to check'
|
||||||
|
)
|
||||||
|
parser.add_argument('fingerprint',
|
||||||
|
help = 'the fingerprint to check'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Input file(s)
|
||||||
|
# Don't use argparse.FileType('rb'), since we want to know the names
|
||||||
|
parser.add_argument('infiles',
|
||||||
|
nargs = '*',
|
||||||
|
# The text shown for these in the usage
|
||||||
|
metavar = 'sshwot-file',
|
||||||
|
help = 'a sshwot file to search'
|
||||||
|
)
|
||||||
|
|
||||||
|
# This automatically parses the command line args for us. If it
|
||||||
|
# returns, we have correct arguments
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Default to port 22
|
||||||
|
port = 22 if args.port is None else args.port
|
||||||
|
|
||||||
|
# Check the validity of the fingerprint and de-base64 it
|
||||||
|
if args.fingerprint[0:7].upper() != 'SHA256:':
|
||||||
|
print('We can only handle sha256 fingerprints (starts with SHA256:)')
|
||||||
|
sys.exit(1)
|
||||||
|
# We encode this, because hashing.base64dec expects bytes
|
||||||
|
fingerprint = hashing.base64dec(args.fingerprint[7:].encode())
|
||||||
|
# A valid sha256 fingerprint is 32 bytes
|
||||||
|
if len(fingerprint) < 32:
|
||||||
|
raise Exception('Fingerprint too short')
|
||||||
|
elif len(fingerprint) > 32:
|
||||||
|
raise Exception('Fingerprint too long')
|
||||||
|
|
||||||
|
# Use the default files if no input files were specified
|
||||||
|
if len(args.infiles) == 0:
|
||||||
|
infiles = default_files.list_all()
|
||||||
|
else:
|
||||||
|
infiles = args.infiles
|
||||||
|
|
||||||
|
# Check
|
||||||
|
for path in infiles:
|
||||||
|
# Remove the directory and the extension from the file
|
||||||
|
name = os.path.basename(path)
|
||||||
|
if name.split('.')[-1] == 'sshwot':
|
||||||
|
name = '.'.join(name.split('.')[:-1])
|
||||||
|
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
entries, file_comment = read_file.read(f)
|
||||||
|
|
||||||
|
success, fail, same_fingerprint = check_fingerprint.check(entries, args.host, port, fingerprint)
|
||||||
|
|
||||||
|
for match_host, match_port, match_comment in success:
|
||||||
|
# Use for display the same normalzed format as internally
|
||||||
|
# We do .decode() here, as it produces bytes
|
||||||
|
host_display = entry.normalize_host(match_host, match_port).decode()
|
||||||
|
print('[\x1b[32mok\x1b[0m] %s: %s: %s' % (name, host_display, match_comment))
|
||||||
|
|
||||||
|
for fail_host, fail_port, fail_comment in fail:
|
||||||
|
host_display = entry.normalize_host(fail_host, fail_port).decode()
|
||||||
|
print('[\x1b[31mfail\x1b[0m] %s: %s: %s' % (name, host_display, fail_comment))
|
||||||
|
|
||||||
|
for _, _, same_fingerprint_comment in same_fingerprint:
|
||||||
|
print('[same fingerprint] %s: (unknown host): %s' % (name, same_fingerprint_comment))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except Exception as err:
|
||||||
|
print('Error: %s' % err, file=sys.stderr)
|
||||||
|
sys.exit(1)
|
Loading…
Reference in New Issue