#!/usr/bin/env python import json import os import sys from httplib import HTTPSConnection # The location were Mozilla defines the *current* TLS Security in JSON format # MOZ_TLS_CONF_SERVER = "statics.tls.security.mozilla.org" MOZ_TLS_CONF_PATH = "/server-side-tls-conf.json" MOZ_TLS_CONF_URL = "https://%s%s" % (MOZ_TLS_CONF_SERVER, MOZ_TLS_CONF_PATH) # The version we already know. Accept nothing less. # MOZ_TLS_CONF_VERSION_MIN = 4.0 # keys inside the JSON document # KEY_CONF = 'configurations' KEY_HREF = 'href' KEY_OSSL_CIPHERS = 'openssl_ciphersuites' KEY_TLS_VERSIONS = 'tls_versions' KEY_VERSION = 'version' # TLS Versions we know how to handle # TLS_VERSIONS = { 'TLSv1.3' : "SSL_PROTOCOL_TLSV1_3", # Mozilla does not list TLSv1.3 yet, but we want it in there! 'TLSv1.2' : "(SSL_PROTOCOL_TLSV1_2|SSL_PROTOCOL_TLSV1_3)", #'TLSv1.2' : "SSL_PROTOCOL_TLSV1_2", 'TLSv1.1' : "SSL_PROTOCOL_TLSV1_1", 'TLSv1' : "SSL_PROTOCOL_TLSV1", 'SSLv3' : "SSL_PROTOCOL_CONSTANTS_SSLV3", } TLS_1_X_VERSIONS = [ 'TLSv1.2', 'TLSv1.3' ] # the Security configurations to extract POLICY_NAMES = [ 'modern', 'intermediate', 'old' ] def fail(msg): sys.stderr.write(msg) sys.exit(1) def proto_string(tls_version): if tls_version in TLS_VERSIONS: return TLS_VERSIONS[tls_version] fail("Unknown TLS protocol '%s'" % tls_version) def proto_conf(tls_versions): if len(TLS_VERSIONS) < len(tls_versions): fail("more TLS versions used than we know: %s" % tls_versions) if len(tls_versions) == 1: return proto_string(tls_versions[0]) missing = [] for tls in TLS_VERSIONS: if not tls in tls_versions: missing.append(proto_string(tls)) if len(missing): return "(SSL_PROTOCOL_ALL & ~(%s))" % "|".join(missing) return "SSL_PROTOCOL_ALL" # return an #ifdef required for a policy or None # def required_ifdef(conf): for tlsv in conf[KEY_TLS_VERSIONS]: # if it has a non-1_X protocol, it works without OpenSSL 1.0.2 if not tlsv in TLS_1_X_VERSIONS: return None return "HAVE_TLSV1_X" def getPolicyDef(): c = HTTPSConnection(MOZ_TLS_CONF_SERVER) c.request('GET', MOZ_TLS_CONF_PATH) data = c.getresponse().read() c.close() return data def printPolicies(doc): print "#define SSL_POLICY_MOZILLA_VERSION %s" % doc[KEY_VERSION] print "" for pname in POLICY_NAMES: prefix = "SSL_POLICY_%s" % pname.upper() if not pname in doc[KEY_CONF]: vars[prefix] = 0 continue p = doc[KEY_CONF][pname] ifdef = required_ifdef(p) if ifdef: print "#ifdef %s" % ifdef print "#define %s 1" % prefix print "#define %s_SSL_CIPHERS \"%s\"" % (prefix, p[KEY_OSSL_CIPHERS]) # Mozilla has not specced this yet print "#define %s_TLS13_CIPHERS NULL" % (prefix) print "#define %s_PROTOCOLS %s" % (prefix, proto_conf(p[KEY_TLS_VERSIONS])) if ifdef: print "#else /* ifdef %s */" % ifdef print "#define %s 0" % prefix print "#endif /* ifdef %s, else part */" % ifdef print "" def main(argv): data = getPolicyDef() doc = json.loads(data) if MOZ_TLS_CONF_URL != doc[KEY_HREF]: fail("ERROR: Unexpected href in policy document: %s\n" % doc[KEY_HREF]) if doc[KEY_VERSION] < MOZ_TLS_CONF_VERSION_MIN: fail("ERROR: Expected at least version %s, but policy document has %s\n" \ % (MOZ_TLS_CONF_VERSION_MIN, doc[KEY_VERSION])) if 1 == len(argv): printPolicies(doc) elif 2 == len(argv): with open(argv[1]) as f: for line in f: if line == "@MOZILLA_SECURITY_POLICIES@\n": printPolicies(doc) else: sys.stdout.write(line) else: fail("usage: %s [file] \nDownload and print/replace the Mozilla TLS Security policies" % argv[0]) if __name__ == "__main__": main(sys.argv)