Add sshwot-verify
This commit is contained in:
parent
aee9cec62e
commit
973ba3c63a
5 changed files with 141 additions and 6 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,3 +6,4 @@ __pycache__
|
|||
build
|
||||
sshwot-export-known-hosts
|
||||
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_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_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)
|
||||
|
||||
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 >> $@
|
||||
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
|
||||
|
||||
clean:
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import os
|
||||
|
||||
def open_all():
|
||||
"""open_all() → [file(rb)]
|
||||
Open the default sshwot files"""
|
||||
def list_all():
|
||||
"""list_all() → [str]
|
||||
List the default sshwot files"""
|
||||
|
||||
try:
|
||||
homedir = os.environ['HOME']
|
||||
|
@ -17,10 +17,20 @@ def open_all():
|
|||
return []
|
||||
|
||||
# Read all the .sshwot files from /.sshwot by default
|
||||
files = []
|
||||
paths = []
|
||||
for dir_entry in sshwot_dir:
|
||||
if dir_entry.split('.')[-1] == 'sshwot':
|
||||
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
|
||||
|
|
|
@ -90,6 +90,11 @@ def main():
|
|||
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')
|
||||
|
||||
else:
|
||||
fingerprint = None
|
||||
|
|
107
src/main-verify.py
Normal file
107
src/main-verify.py
Normal file
|
@ -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 a new issue