From 9bd7b4ff7bf4d2615a7a4f59ae27906c977710f5 Mon Sep 17 00:00:00 2001 From: James Kasten Date: Sun, 19 May 2013 14:12:33 -0400 Subject: [PATCH] Final touches before case insensitive Augeas testing begins --- trustify.py | 7 +- trustify/client/client.py | 295 ++++++++++++++++---------------- trustify/client/configurator.py | 42 +++-- 3 files changed, 177 insertions(+), 167 deletions(-) diff --git a/trustify.py b/trustify.py index c12d8f78e..c1fed1b0e 100644 --- a/trustify.py +++ b/trustify.py @@ -14,7 +14,7 @@ def main(): sys.exit("\nOnly root can run trustify.\n") # Parse options try: - opts, args = getopt.getopt(sys.argv[1:], "", ["text", "view-checkpoints", "privkey=", "csr=", "server=", "rollback="]) + opts, args = getopt.getopt(sys.argv[1:], "", ["text", "test", "view-checkpoints", "privkey=", "csr=", "server=", "rollback="]) except getopt.GetoptError as err: # print help info and exit print str(err) @@ -46,7 +46,10 @@ def main(): config = configurator.Configurator() config.display_checkpoints() sys.exit(0) - + elif o == "--test": + #put any temporary tests in here + continue + if not server: if "CHOCOLATESERVER" in os.environ: server = os.environ["CHOCOLATESERVER"] diff --git a/trustify/client/client.py b/trustify/client/client.py index 3db31d36a..fd2b4256c 100644 --- a/trustify/client/client.py +++ b/trustify/client/client.py @@ -28,7 +28,6 @@ from trustify.client.CONFIG import SERVER_ROOT, KEY_DIR, CERT_DIR allow_raw_ipv6_server = False RSA_KEY_SIZE = 2048 - class Client(object): # In case of import, dialog needs scope over the class dialog = None @@ -566,34 +565,34 @@ def sha256(m): # return result[1] == "Secure" -def rsa_sign(key, data): - """ - Sign this data with this private key. For client-side use. +# def rsa_sign(key, data): +# """ +# Sign this data with this private key. For client-side use. - @type key: str - @param key: PEM-encoded string of the private key. +# @type key: str +# @param key: PEM-encoded string of the private key. - @type data: str - @param data: The data to be signed. Will be hashed (sha256) prior to - signing. +# @type data: str +# @param data: The data to be signed. Will be hashed (sha256) prior to +# signing. - @return: binary string of the signature - """ - key = str(key) - data = str(data) - privkey = M2Crypto.RSA.load_key_string(key) - return privkey.sign(hashlib.sha256(data).digest(), 'sha256') +# @return: binary string of the signature +# """ +# key = str(key) +# data = str(data) +# privkey = M2Crypto.RSA.load_key_string(key) +# return privkey.sign(hashlib.sha256(data).digest(), 'sha256') -def do(upstream, m): - u = urllib2.urlopen(upstream, m.SerializeToString()) - return u.read() +# def do(upstream, m): +# u = urllib2.urlopen(upstream, m.SerializeToString()) +# return u.read() -def decode(m): - return (chocolatemessage.FromString(m)) +# def decode(m): +# return (chocolatemessage.FromString(m)) -def init(m): - m.chocolateversion = 1 - m.session = "" +# def init(m): +# m.chocolateversion = 1 +# m.session = "" def drop_privs(): nogroup = grp.getgrnam("nogroup").gr_gid @@ -614,8 +613,8 @@ def drop_privs(): # if hashcash: m.request.clientpuzzle = hashcash -def sign(key, m): - m.request.sig = rsa_sign(key, ("(%d) (%s) (%s)" % (m.request.timestamp, m.request.recipient, m.request.csr))) +# def sign(key, m): +# m.request.sig = rsa_sign(key, ("(%d) (%s) (%s)" % (m.request.timestamp, m.request.recipient, m.request.csr))) def old_cert(cert_filename, days_left): cert = M2Crypto.X509.load_cert(cert_filename) @@ -841,154 +840,154 @@ def renew(config): # return True -def authenticate(): - """ - Main call to do DV_SNI validation and deploy the trustify certificate - TODO: This should be turned into a class... - """ - global server, names, csr, privkey +# def authenticate(): +# """ +# Main call to do DV_SNI validation and deploy the trustify certificate +# TODO: This should be turned into a class... +# """ +# global server, names, csr, privkey - # Check if root - if not os.geteuid()==0: - sys.exit("\nOnly root can run trustify\n") +# # Check if root +# if not os.geteuid()==0: +# sys.exit("\nOnly root can run trustify\n") - if "CHOCOLATESERVER" in os.environ: - server = os.environ["CHOCOLATESERVER"] - if not server: - # Global default value for Chocolate server! - server = "ca.theobroma.info" +# if "CHOCOLATESERVER" in os.environ: +# server = os.environ["CHOCOLATESERVER"] +# if not server: +# # Global default value for Chocolate server! +# server = "ca.theobroma.info" - assert is_hostname_sane(server), `server` + " is an impossible hostname" +# assert is_hostname_sane(server), `server` + " is an impossible hostname" - upstream = "https://%s/chocolate.py" % server +# upstream = "https://%s/chocolate.py" % server - if curses: - logger.setLogger(logger.NcursesLogger()) - logger.setLogLevel(logger.INFO) - else: - logger.setLogger(sys.stdout) - logger.setLogLevel(logger.INFO) +# if curses: +# logger.setLogger(logger.NcursesLogger()) +# logger.setLogLevel(logger.INFO) +# else: +# logger.setLogger(sys.stdout) +# logger.setLogLevel(logger.INFO) - # Logger should be init before config - config = configurator.Configurator() +# # Logger should be init before config +# config = configurator.Configurator() - if not names: - names = config.get_all_names() +# if not names: +# names = config.get_all_names() - if curses: - if not names: - logger.fatal("No domain names were found in your apache config") - logger.fatal("Either specify which names you would like trustify to validate or add server names to your virtual hosts") - sys.exit(1) +# if curses: +# if not names: +# logger.fatal("No domain names were found in your apache config") +# logger.fatal("Either specify which names you would like trustify to validate or add server names to your virtual hosts") +# sys.exit(1) - names = filter_names(names) - choice = choice_of_ca() - if choice[0] != 0: - sys.exit(1) +# names = filter_names(names) +# choice = choice_of_ca() +# if choice[0] != 0: +# sys.exit(1) - # Check first if mod_ssl is loaded - if not config.check_ssl_loaded(): - logger.info("Loading mod_ssl into Apache Server") - config.enable_mod("ssl") +# # Check first if mod_ssl is loaded +# if not config.check_ssl_loaded(): +# logger.info("Loading mod_ssl into Apache Server") +# config.enable_mod("ssl") - req_file = csr - key_file = privkey - if csr and privkey: - csr_pem = open(req_file).read().replace("\r", "") - key_pem = open(key_file).read().replace("\r", "") - if not csr or not privkey: - # Generate new private key and corresponding csr! - key_pem, csr_pem = make_key_and_csr(names, 2048) - key_file, req_file = save_key_csr(key_pem, csr_pem) - logger.info("Generating key: " + key_file) - logger.info("Creating CSR: " + req_file) +# req_file = csr +# key_file = privkey +# if csr and privkey: +# csr_pem = open(req_file).read().replace("\r", "") +# key_pem = open(key_file).read().replace("\r", "") +# if not csr or not privkey: +# # Generate new private key and corresponding csr! +# key_pem, csr_pem = make_key_and_csr(names, 2048) +# key_file, req_file = save_key_csr(key_pem, csr_pem) +# logger.info("Generating key: " + key_file) +# logger.info("Creating CSR: " + req_file) - r, k = send_request(key_pem, csr_pem, names) +# r, k = send_request(key_pem, csr_pem, names) - challenges, dn = challenge_factory(r, os.path.abspath(req_file), os.path.abspath(key_file), config) +# challenges, dn = challenge_factory(r, os.path.abspath(req_file), os.path.abspath(key_file), config) - # Find set of virtual hosts to deploy certificates to - vhost = set() - for name in dn: - host = config.choose_virtual_host(name) - if host is not None: - vhost.add(host) +# # Find set of virtual hosts to deploy certificates to +# vhost = set() +# for name in dn: +# host = config.choose_virtual_host(name) +# if host is not None: +# vhost.add(host) - for challenge in challenges: - if not challenge.perform(quiet=curses): - # TODO: In this case the client should probably send a failure - # to the server. - logger.fatal("challenge failed") - sys.exit(1) - logger.info("Configured Apache for challenge; waiting for verification...") +# for challenge in challenges: +# if not challenge.perform(quiet=curses): +# # TODO: In this case the client should probably send a failure +# # to the server. +# logger.fatal("challenge failed") +# sys.exit(1) +# logger.info("Configured Apache for challenge; waiting for verification...") - ############################################################# - # This whole bottom section should be reworked once the protocol - # is finalized... it is currently quite ugly - ############################################################ +# ############################################################# +# # This whole bottom section should be reworked once the protocol +# # is finalized... it is currently quite ugly +# ############################################################ - did_it = chocolatemessage() - init(did_it) - did_it.session = r.session - # This will blindly assert that all of the challenges have been - # complied with, by simply copying them from the challenge data - # structure into a new completedchallenge structure. This is - # kind of crude, because the client could instead actually build up - # a completedchallenge structure piece-by-piece as it actually - # complies with challenges (and then send that structure for the - # server to look at). In the existing client, completedchallenge - # is only ever sent once _all_ of the (assumed to be dvsni) - # challenges have been met, and client-side failure to meet any - # challenge is immediately fatal to the client. In the existing - # server, the client's assertion that the client has met any - # (assumed to be dvsni) challenge(s) will result in the server - # scheduling a test of all challenges. - did_it.completedchallenge.extend(r.challenge) +# did_it = chocolatemessage() +# init(did_it) +# did_it.session = r.session +# # This will blindly assert that all of the challenges have been +# # complied with, by simply copying them from the challenge data +# # structure into a new completedchallenge structure. This is +# # kind of crude, because the client could instead actually build up +# # a completedchallenge structure piece-by-piece as it actually +# # complies with challenges (and then send that structure for the +# # server to look at). In the existing client, completedchallenge +# # is only ever sent once _all_ of the (assumed to be dvsni) +# # challenges have been met, and client-side failure to meet any +# # challenge is immediately fatal to the client. In the existing +# # server, the client's assertion that the client has met any +# # (assumed to be dvsni) challenge(s) will result in the server +# # scheduling a test of all challenges. +# did_it.completedchallenge.extend(r.challenge) - r=decode(do(upstream, did_it)) - logger.debug(r) - delay = 5 - #while r.challenge or r.proceed.IsInitialized(): - while r.proceed.IsInitialized() or (r.challenge and not all_payment_challenge(r)): - if r.proceed.IsInitialized(): - delay = min(r.proceed.polldelay, 60) - logger.debug("waiting %d" % delay) - time.sleep(delay) - k.session = r.session - r = decode(do(upstream, k)) - logger.debug(r) +# r=decode(do(upstream, did_it)) +# logger.debug(r) +# delay = 5 +# #while r.challenge or r.proceed.IsInitialized(): +# while r.proceed.IsInitialized() or (r.challenge and not all_payment_challenge(r)): +# if r.proceed.IsInitialized(): +# delay = min(r.proceed.polldelay, 60) +# logger.debug("waiting %d" % delay) +# time.sleep(delay) +# k.session = r.session +# r = decode(do(upstream, k)) +# logger.debug(r) - # This should be invoked if a payment is necessary - # This is being tested and will have to be cleaned and organized - # once the protocol is finalized. - while r.challenge and all_payment_challenge(r): - # dont need to change domain names here - paymentChallenges, temp = challenge_factory(r, os.path.abspath(req_file), os.path.abspath(key_file), config) - for chall in paymentChallenges: - chall.perform(quiet=curses) +# # This should be invoked if a payment is necessary +# # This is being tested and will have to be cleaned and organized +# # once the protocol is finalized. +# while r.challenge and all_payment_challenge(r): +# # dont need to change domain names here +# paymentChallenges, temp = challenge_factory(r, os.path.abspath(req_file), os.path.abspath(key_file), config) +# for chall in paymentChallenges: +# chall.perform(quiet=curses) - logger.info("User has continued Trustify after submitting payment") - proceed_msg = chocolatemessage() - init(proceed_msg) - proceed_msg.session = r.session - proceed_msg.proceed.timestamp = int(time.time()) - proceed_msg.proceed.polldelay = 60 - # Send the proceed message - r = decode(do(upstream, k)) +# logger.info("User has continued Trustify after submitting payment") +# proceed_msg = chocolatemessage() +# init(proceed_msg) +# proceed_msg.session = r.session +# proceed_msg.proceed.timestamp = int(time.time()) +# proceed_msg.proceed.polldelay = 60 +# # Send the proceed message +# r = decode(do(upstream, k)) - while r.proceed.IsInitialized(): - if r.proceed.IsInitialized(): - delay = min(r.proceed.polldelay, 60) - logger.debug("waiting %d" % delay) - time.sleep(delay) - k.session = r.session - r = decode(do(upstream, k)) - logger.debug(r) +# while r.proceed.IsInitialized(): +# if r.proceed.IsInitialized(): +# delay = min(r.proceed.polldelay, 60) +# logger.debug("waiting %d" % delay) +# time.sleep(delay) +# k.session = r.session +# r = decode(do(upstream, k)) +# logger.debug(r) - handle_verification_response(r, dn, challenges, vhost, key_file, config) +# handle_verification_response(r, dn, challenges, vhost, key_file, config) # vim: set expandtab tabstop=4 shiftwidth=4 diff --git a/trustify/client/configurator.py b/trustify/client/configurator.py index 93b6c033d..ea4a91d5d 100644 --- a/trustify/client/configurator.py +++ b/trustify/client/configurator.py @@ -17,6 +17,10 @@ from trustify.client import logger # Question: Am I missing any attacks that can result from modifying CONFIG file? # Configurator should be turned into a Singleton +# Note: Apache 2.4 NameVirtualHost directive is deprecated... all vhost twins +# are considered name based vhosts by default. The use of the directive will +# emit a warning. + class VH(object): def __init__(self, filename_path, vh_path, vh_addrs, is_ssl, is_enabled): self.file = filename_path @@ -238,21 +242,22 @@ class Configurator(object): for p in paths: name_vh.append(self.aug.get(p)) - # TODO: Reread NameBasedVirtual host matching... I think it must be an - # exact match + # Mixed and matched wildcard NameVirtualHost with VirtualHost + # behavior is undefined. Make sure that an exact match exists + # Check for exact match for vh in name_vh: if vh == addr: return True - # Check for general IP_ADDR name_vh - tup = addr.partition(":") - for vh in name_vh: - if vh == tup[0]: - return True - # Check for straight wildcard name_vh - for vh in name_vh: - if vh == "*": - return True + # # Check for general IP_ADDR name_vh + # tup = addr.partition(":") + # for vh in name_vh: + # if vh == tup[0]: + # return True + # # Check for straight wildcard name_vh + # for vh in name_vh: + # if vh == "*": + # return True # NameVirtualHost directive should be added for this address return False @@ -498,12 +503,15 @@ class Configurator(object): # The configuration must also be saved before being searched # for the new directives; For these reasons... this is tacked # on after fully creating the new vhost - # TODO: Figure out what to do for vhosts with multiple addresses - if len(nonssl_vhost.addrs) == 1: - if self.is_name_vhost(nonssl_vhost.addrs[0]) and not self.is_name_vhost(ssl_addrs[0]): - self.add_name_vhost(ssl_addrs[0]) - logger.info("Enabling NameVirtualHosts on " + ssl_addrs[0]) - self.save("Added permanent NameVirtualHost for " + ssl_addrs[0]) + need_to_save = False + for i in range(len(nonssl_vhost.addrs)): + if self.is_name_vhost(nonssl_vhost.addrs[i]) and not self.is_name_vhost(ssl_addrs[i]): + self.add_name_vhost(ssl_addrs[i]) + logger.info("Enabling NameVirtualHosts on " + ssl_addrs[i]) + need_to_save = True + + if need_to_save: + self.save("Added permanent NameVirtualHost for " + ssl_addrs[i]) return ssl_vhost