Compare commits

...

3 Commits

Author SHA1 Message Date
Juhani Krekelä 7cd275fada Start writing documentation 2018-09-01 22:38:01 +03:00
Juhani Krekelä 973ba3c63a Add sshwot-verify 2018-09-01 22:33:41 +03:00
Juhani Krekelä aee9cec62e Rename src/open_default_files.py → src/default_files.py 2018-09-01 22:05:45 +03:00
7 changed files with 161 additions and 7 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ __pycache__
build
sshwot-export-known-hosts
sshwot-filter
sshwot-verify

View File

@ -1,10 +1,13 @@
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
SSHWOT_FILTER_MAIN:=src/main-filter.py
SSHWOT_FILTER_DEPS:=src/entry.py src/hashing.py src/open_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)
@ -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:

18
README.md Normal file
View File

@ -0,0 +1,18 @@
Name
----
*SSH* *W*eb *o*f *T*rust
TODO
----
* `egrep 'TODO|FIXME|XXX' src/*.py`
* Convert format description to mandoc
* Write manpages
* Add `install` and `uninstall` makefile targets
License
-------
Both the sshwot specification and the reference implementation contained
here are licensed under Creative Commons Zero v1.0 Universal.

View File

@ -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

View File

@ -6,6 +6,7 @@ import process_known_hosts
import write_file
def main():
# TODO: Provide a way to add comments
parser = argparse.ArgumentParser(
description = """Export .ssh/known_hosts as a sshwot file.
By default the output is written to stdout.""",

View File

@ -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
View 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)