From c0d8aceccb0961a25ee58a163441bbcbe6d6ea3d Mon Sep 17 00:00:00 2001 From: Magnus Ahltorp Date: Fri, 27 Jan 2017 16:11:11 +0100 Subject: Verify config file signature Read log key from config file in more places. Check STH signature in storagegc.py --- tools/compileconfig.py | 10 ++++++---- tools/loginfo.py | 5 ++--- tools/merge_sth.py | 3 ++- tools/mergetools.py | 6 +++--- tools/readconfig.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/storagegc.py | 7 ++++--- 6 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 tools/readconfig.py (limited to 'tools') diff --git a/tools/compileconfig.py b/tools/compileconfig.py index 0ee3fab..b5e5053 100755 --- a/tools/compileconfig.py +++ b/tools/compileconfig.py @@ -5,7 +5,7 @@ import argparse import sys -import yaml +import readconfig import re import base64 @@ -405,7 +405,6 @@ def gen_testmakefile(config, testmakefile, machines, shellvars=False): configfile.close() - def main(): parser = argparse.ArgumentParser(description="") parser.add_argument('--config', help="System configuration", required=True) @@ -415,13 +414,16 @@ def main(): parser.add_argument("--machines", type=int, metavar="n", help="Number of machines") args = parser.parse_args() - config = yaml.load(open(args.config)) if args.testmakefile and args.machines: + config = readconfig.read_config(args.config) gen_testmakefile(config, args.testmakefile, args.machines) elif args.testshellvars and args.machines: + config = readconfig.read_config(args.config) gen_testmakefile(config, args.testshellvars, args.machines, shellvars=True) elif args.localconfig: - localconfig = yaml.load(open(args.localconfig)) + localconfig = readconfig.read_config(args.localconfig) + config = readconfig.verify_and_read_config(args.config, localconfig["logadminkey"]) + localnodes = localconfig["localnodes"] for localnode in localnodes: gen_config(localnode, config, localconfig) diff --git a/tools/loginfo.py b/tools/loginfo.py index c61ad1b..1537c5e 100755 --- a/tools/loginfo.py +++ b/tools/loginfo.py @@ -6,7 +6,7 @@ import sys import argparse -import yaml +import readconfig from certtools import create_ssl_context, get_sth def main(): @@ -26,8 +26,7 @@ def main(): required=True) parser.add_argument('baseurl', help="Log base URL") args = parser.parse_args() - #config = yaml.load(open(args.config)) - localconfig = yaml.load(open(args.localconfig)) + localconfig = readconfig.read_config(args.localconfig) paths = localconfig["paths"] create_ssl_context(cafile=paths["https_cacertfile"]) diff --git a/tools/merge_sth.py b/tools/merge_sth.py index 6b1bb60..2bc19dd 100755 --- a/tools/merge_sth.py +++ b/tools/merge_sth.py @@ -12,6 +12,7 @@ import json import urllib2 import time import requests +import base64 from base64 import b64encode from mergetools import parse_args, get_nfetched, hexencode, hexdecode, \ get_logorder, get_sth @@ -31,7 +32,7 @@ def merge_sth(args, config, localconfig): sthfile = mergedb + "/sth" logorderfile = mergedb + "/logorder" currentsizefile = mergedb + "/fetched" - logpublickey = get_public_key_from_file(paths["logpublickey"]) + logpublickey = base64.decodestring(config["logpublickey"]) backupquorum = config.get("backup-quorum-size", 0) assert backupquorum <= len(mergenodes) - 1 create_ssl_context(cafile=paths["https_cacertfile"]) diff --git a/tools/mergetools.py b/tools/mergetools.py index f49e789..0afec24 100644 --- a/tools/mergetools.py +++ b/tools/mergetools.py @@ -7,7 +7,7 @@ import hashlib import sys import struct import json -import yaml +import readconfig import argparse import requests try: @@ -430,8 +430,8 @@ def parse_args(): help="Print timing information") args = parser.parse_args() - config = yaml.load(open(args.config)) - localconfig = yaml.load(open(args.localconfig)) + localconfig = readconfig.read_config(args.localconfig) + config = readconfig.verify_and_read_config(args.config, localconfig["logadminkey"]) set_api_keys(config) diff --git a/tools/readconfig.py b/tools/readconfig.py new file mode 100644 index 0000000..5079691 --- /dev/null +++ b/tools/readconfig.py @@ -0,0 +1,54 @@ +import io +import ecdsa +import hashlib +import yaml +import base64 +import sys + +class ErrorHandlingDict(dict): + def __init__(self, filename, path): + self._filename = filename + self._path = path + dict.__init__({}) + def __missing__(self, key): + if self._path: + path = ", ".join(self._path) + else: + path = "the top level" + print >>sys.stderr, "error: could not find configuration key '%s' at %s in %s" % (key, path, self._filename) + sys.exit(1) + +def errorhandlify(term, filename, path=[]): + if isinstance(term, basestring): + return term + elif isinstance(term, int): + return term + elif isinstance(term, dict): + result = ErrorHandlingDict(filename, path) + for k, v in term.items(): + result[k] = errorhandlify(v, filename, path + [k]) + return result + elif isinstance(term, list): + return [errorhandlify(e, filename, path + ["item %d" % i]) for i, e in enumerate(term, start=1)] + else: + print "unknown type", type(term) + sys.exit(1) + +def verify_and_read_config(filename, publickey_base64): + rawconfig = open(filename).read() + signature = open(filename + ".sig").read() + + publickey = base64.decodestring(publickey_base64) + + try: + vk = ecdsa.VerifyingKey.from_der(publickey) + vk.verify(signature, rawconfig, hashfunc=hashlib.sha256, + sigdecode=ecdsa.util.sigdecode_der) + except ecdsa.keys.BadSignatureError: + print >>sys.stderr, "error: configuration file %s did not have a correct signature" % (filename,) + sys.exit(1) + + return errorhandlify(yaml.load(io.BytesIO(rawconfig), yaml.SafeLoader), filename) + +def read_config(filename): + return errorhandlify(yaml.load(open(filename), yaml.SafeLoader), filename) diff --git a/tools/storagegc.py b/tools/storagegc.py index 38b5379..6360495 100755 --- a/tools/storagegc.py +++ b/tools/storagegc.py @@ -9,7 +9,7 @@ import urllib import json import base64 import sys -import yaml +import readconfig from certtools import * parser = argparse.ArgumentParser(description='') @@ -17,8 +17,8 @@ parser.add_argument('--config', help="System configuration", required=True) parser.add_argument('--localconfig', help="Local configuration", required=True) args = parser.parse_args() -config = yaml.load(open(args.config)) -localconfig = yaml.load(open(args.localconfig)) +localconfig = readconfig.read_config(args.localconfig) +config = readconfig.verify_and_read_config(args.config, localconfig["logadminkey"]) paths = localconfig["paths"] db_path = paths["db"] @@ -27,6 +27,7 @@ create_ssl_context(cafile=paths.get("public_cacertfile", None)) baseurl = config["baseurl"] sth = get_sth(baseurl) +check_sth_signature(baseurl, sth, base64.decodestring(config["logpublickey"])) def verifyleafhash(leaf_hash): try: -- cgit v1.1