diff --git a/client-webserver/client.py b/client-webserver/client.py index 56c2c1783..fb3dac2a9 100755 --- a/client-webserver/client.py +++ b/client-webserver/client.py @@ -146,9 +146,9 @@ while r.challenge or r.proceed.IsInitialized(): print r # TODO: there should be an unperform_sni_cert_challenge() here. -# TODO: there should be a deploy_cert() here. if r.success.IsInitialized(): + sni_challenge.cleanup(sni_todo, config) cert_chain_abspath = None with open(cert_file, "w") as f: f.write(r.success.certificate) @@ -163,6 +163,7 @@ if r.success.IsInitialized(): #cert_chain_abspath = os.path.abspath(chain_file) for host in vhost: config.deploy_cert(host, os.path.abspath(cert_file), os.path.abspath(key_file), cert_chain_abspath) + sni_challenge.apache_restart() elif r.failure.IsInitialized(): print "Server reported failure." sys.exit(1) diff --git a/client-webserver/configurator.py b/client-webserver/configurator.py index f364ea5e8..9a84d63a9 100644 --- a/client-webserver/configurator.py +++ b/client-webserver/configurator.py @@ -1,6 +1,7 @@ import augeas import subprocess import re +import os BASE_DIR = "/etc/apache2/" @@ -32,6 +33,7 @@ class Configurator(object): self.httpd_files = [] for m in self.aug.match("/augeas/load/Httpd/incl"): self.httpd_files.append(self.aug.get(m)) + self.mod_files = set() # TODO: This function can be improved to ensure that the final directives # are being modified whether that be in the include files or in the @@ -83,12 +85,7 @@ class Configurator(object): if cert_chain is not None: self.aug.set(path["cert_chain"][0], cert_chain) - try: - self.aug.save() - except IOError: - print "Unable to save config - Is the script running as root?" - return False - return True + return self.save("Virtual Server - deploying certificate") def choose_virtual_host(self, name): """ @@ -114,7 +111,7 @@ class Configurator(object): - def add_servernames(self, host): + def __add_servernames(self, host): """ Helper function for get_virtual_hosts() """ @@ -141,7 +138,7 @@ class Configurator(object): vhs.append(VH(p, addrs)) for host in vhs: - self.add_servernames(host) + self.__add_servernames(host) return vhs @@ -175,8 +172,8 @@ class Configurator(object): def add_name_vhost(self, addr): """ - TODO: For final code... this function should check that ports.conf - is included along the main path... it is by default + Adds NameVirtualHost directive for given address + Directive is added to ports.conf unless """ aug_file_path = "/files" + BASE_DIR + "ports.conf" self.add_dir_to_ifmodssl(aug_file_path, "NameVirtualHost", addr) @@ -185,8 +182,6 @@ class Configurator(object): print "ports.conf is not included in your Apache config... " print "Adding NameVirtualHost directive to httpd.conf" self.add_dir_to_ifmodssl("/files" + BASE_DIR + "httpd.conf", "NameVirtualHost", addr) - - return True def add_dir_to_ifmodssl(self, aug_conf_path, directive, val): @@ -198,12 +193,6 @@ class Configurator(object): nvhPath = ifModPath + "directive[1]" self.aug.set(nvhPath, directive) self.aug.set(nvhPath + "/arg", val) - try: - self.aug.save() - except IOError: - print "Unable to save file - Is the script running as root?" - return False - return True def make_server_sni_ready(self, vhost): """ @@ -250,9 +239,9 @@ class Configurator(object): def add_dir(self, aug_conf_path, directive, arg): self.aug.set(aug_conf_path + "/directive[last() + 1]", directive) - self.aug.set(aug_conf_path + "/directive[last()]", arg) + self.aug.set(aug_conf_path + "/directive[last()]/arg", arg) - def find_directive(self, directive, arg, start="/files"+BASE_DIR+"apache2.conf"): + def find_directive(self, directive, arg=None, start="/files"+BASE_DIR+"apache2.conf"): """ Recursively searches through config files to find directives TODO: arg should probably be a list @@ -381,11 +370,37 @@ class Configurator(object): self.aug.add_transform("Httpd.lns", self.httpd_files) self.aug.load() + def save(self, mod_conf="Augeas Configuration", reversible=False): + try: + self.aug.save() + if reversible: + # Retrieve list of modified files + save_paths = self.aug.match("/augeas/events/saved") + for path in save_paths: + # Strip off /files + filename = self.aug.get(path)[6:] + if filename in self.mod_files: + # Output a warning... hopefully this can be avoided so more + # complex code doesn't have to be written + print "Reversible file has been overwritten -", filename + else: + self.mod_files.add(filename) + return True + except IOError: + print "Unable to save file - ", mod_conf + print "Is the script running as root?" + return False + def revert_config(self): """ This function should reload the users original configuration files """ - return False + for f in self.mod_files: + print "reverting", f + os.rename(f + ".augsave", f) + self.aug.load() + self.mod_files.clear() + def recurmatch(aug, path): if path: @@ -411,13 +426,9 @@ def main(): for v in config.vhosts: for a in v.addrs: print "Address:",a, "- Is name vhost?", config.is_name_vhost(a) - - #print config.make_server_sni_ready("example.com:443") - setHost = set() - setHost.add(config.choose_virtual_host("example.com")) - setHost.add(config.choose_virtual_host("example2.com")) - for s in setHost: - print s.path + + config.parse_file("/etc/apache2/ports_test.conf") + #for m in config.aug.match("/augeas/load/Httpd/incl"): # print m, config.aug.get(m) diff --git a/client-webserver/sni_challenge.py b/client-webserver/sni_challenge.py index caff780b0..6b94b56e7 100644 --- a/client-webserver/sni_challenge.py +++ b/client-webserver/sni_challenge.py @@ -12,7 +12,8 @@ import augeas import configurator #import dns.resolver -CHOC_DIR = "/home/ubuntu/chocolate/client-webserver/" +#CHOC_DIR = "/home/ubuntu/chocolate/client-webserver/" +CHOC_DIR = "/home/james/Documents/apache_choc/" CHOC_CERT_CONF = "choc_cert_extensions.cnf" OPTIONS_SSL_CONF = CHOC_DIR + "options-ssl.conf" APACHE_CHALLENGE_CONF = CHOC_DIR + "choc_sni_cert_challenge.conf" @@ -38,7 +39,6 @@ def findApacheConfigFile(): result: returns file path if present """ - # This needs to be fixed to account for multiple httpd.conf files try: p = subprocess.check_output(["sudo", "find", "/etc", "-name", "httpd.conf"], stderr=open("/dev/null")) @@ -143,10 +143,8 @@ def generateExtension(key, y): rsaPrivKey = M2Crypto.RSA.load_key(key) r = rsaPrivKey.private_decrypt(y, M2Crypto.RSA.pkcs1_oaep_padding) - #print r s = Random.get_random_bytes(S_SIZE) - #s = "0xDEADBEEF" extHMAC = hmac.new(r, str(s), hashlib.sha256) return byteToHex(s) + extHMAC.hexdigest() @@ -195,6 +193,28 @@ def apache_restart(): """ subprocess.call(["sudo", "/etc/init.d/apache2", "reload"]) +def cleanup(listSNITuple, configurator): + """ + Remove all temporary changes necessary to perform the challenge + + configurator: Configurator object + listSNITuple: The initial challenge tuple + + result: Apache server is restored to the pre-challenge state + """ + configurator.revert_config() + apache_restart() + remove_files(listSNITuple) + + +def remove_files(listSNITuple): + """ + Removes all of the temporary SNI files + """ + for tup in listSNITuple: + remove(getChocCertFile(tup[2])) + remove(APACHE_CHALLENGE_CONF) + #main call def perform_sni_cert_challenge(listSNITuple, csr, key, configurator): """ @@ -207,6 +227,9 @@ def perform_sni_cert_challenge(listSNITuple, csr, key, configurator): key: string - File path to key configurator: Configurator obj """ + # Save any changes to the configuration as a precaution + # About to make temporary changes to the config + configurator.save("Before performing sni_challenge") for tup in listSNITuple: vhost = configurator.choose_virtual_host(tup[0]) @@ -224,6 +247,8 @@ def perform_sni_cert_challenge(listSNITuple, csr, key, configurator): createChallengeCert(tup[3], ext, tup[2], csr, key) modifyApacheConfig(findApacheConfigFile(), listSNITuple, key, configurator) + # Save reversible changes and restart the server + configurator.save("SNI Challenge", True) apache_restart() def main(): @@ -252,8 +277,17 @@ def main(): config = configurator.Configurator() - perform_sni_cert_challenge([("example.com", y, nonce, "1.3.3.7"), ("www.example.com",y2, nonce2, "1.3.3.7")], csr, key, config) - #perform_sni_cert_challenge([("127.0.0.1", y, nonce, "1.3.3.7"), ("localhost", y2, nonce2, "1.3.3.7")], csr, key, config) + #challenges = [("example.com", y, nonce, "1.3.3.7"), ("www.example.com",y2, nonce2, "1.3.3.7")] + challenges = [("127.0.0.1", y, nonce, "1.3.3.7"), ("localhost", y2, nonce2, "1.3.3.7")] + perform_sni_cert_challenge(challenges, csr, key, config) + + # Waste some time without importing time module... just for testing + for i in range(0, 12000): + if i % 2000 == 0: + print "Waiting:", i + + print "Cleaning up" + cleanup(challenges, config) if __name__ == "__main__": main()