Esempio n. 1
0
    def rollback_checkpoints(self, rollback=1):
        """Revert 'rollback' number of configuration checkpoints.

        :param rollback: Number of checkpoints to reverse
        :type rollback: int

        """
        try:
            rollback = int(rollback)
        except:
            logger.error("Rollback argument must be a positive integer")
        # Sanity check input
        if rollback < 1:
            logger.error("Rollback argument must be a positive integer")
            return

        backups = os.listdir(CONFIG.BACKUP_DIR)
        backups.sort()

        if len(backups) < rollback:
            logger.error(("Unable to rollback %d checkpoints, only "
                         "%d exist") % (rollback, len(backups)))

        while rollback > 0 and backups:
            cp_dir = CONFIG.BACKUP_DIR + backups.pop()
            result = self._recover_checkpoint(cp_dir)
            if result != 0:
                logger.fatal("Failed to load checkpoint during rollback")
                sys.exit(39)
            rollback -= 1

        self.aug.load()
Esempio n. 2
0
    def get_cas(self):
        DV_choices = []
        OV_choices = []
        EV_choices = []
        choices = []
        try:
            with open("/etc/letsencrypt/.ca_offerings") as f:
                for line in f:
                    choice = line.split(";", 1)
                    if 'DV' in choice[0]:
                        DV_choices.append(choice)
                    elif 'OV' in choice[0]:
                        OV_choices.append(choice)
                    else:
                        EV_choices.append(choice)

                # random.shuffle(DV_choices)
                # random.shuffle(OV_choices)
                # random.shuffle(EV_choices)
                choices = DV_choices + OV_choices + EV_choices
                choices = [(l[0], l[1]) for l in choices]

        except IOError as e:
            logger.fatal("Unable to find .ca_offerings file")
            sys.exit(1)

        return choices
Esempio n. 3
0
    def __init__(self, ca_server, cert_signing_request=None,
                 private_key=None, use_curses=True):
        global dialog
        self.curses = use_curses

        # Logger needs to be initialized before Configurator
        self.init_logger()
        # TODO:  Can probably figure out which configurator to use without
        #        special packaging based on system info
        #        Command line arg or client function to discover
        self.config = apache_configurator.ApacheConfigurator(CONFIG.SERVER_ROOT)

        self.server = ca_server

        self.csr_file = cert_signing_request
        self.key_file = private_key

        # If CSR is provided, the private key should also be provided.
        # TODO: Make sure key was actually used in CSR
        # TODO: Make sure key has proper permissions
        if self.csr_file and not self.key_file:
            logger.fatal("Please provide the private key file used in \
            generating the provided CSR")
            sys.exit(1)

        self.server_url = "https://%s/acme/" % self.server
    def rollback_checkpoints(self, rollback=1):
        """Revert 'rollback' number of configuration checkpoints."""
        try:
            rollback = int(rollback)
        except:
            logger.error("Rollback argument must be a positive integer")
        # Sanity check input
        if rollback < 1:
            logger.error("Rollback argument must be a positive integer")
            return

        backups = os.listdir(CONFIG.BACKUP_DIR)
        backups.sort()

        if len(backups) < rollback:
            logger.error(("Unable to rollback %d checkpoints, only "
                         "%d exist") % (rollback, len(backups)))

        while rollback > 0 and backups:
            cp_dir = CONFIG.BACKUP_DIR + backups.pop()
            result = self.__recover_checkpoint(cp_dir)
            if result != 0:
                logger.fatal("Failed to load checkpoint during rollback")
                sys.exit(39)
            rollback -= 1

        self.aug.load()
Esempio n. 5
0
    def acme_authorization(self, challenge_msg, chal_objs, responses):
        """Handle ACME "authorization" phase.

        :param challenge_msg: ACME "challenge" message.
        :type challenge_msg: dict

        :param chal_objs: TODO
        :type chal_objs: TODO

        :param responses: TODO
        :type responses: TODO

        :returns: ACME "authorization" message.
        :rtype: dict

        """
        auth_dict = self.send(
            acme.authorization_request(challenge_msg["sessionID"],
                                       self.names[0], challenge_msg["nonce"],
                                       responses, self.key_file))

        try:
            return self.is_expected_msg(auth_dict, "authorization")
        except:
            logger.fatal("Failed Authorization procedure - "
                         "cleaning up challenges")
            sys.exit(1)
        finally:
            self.cleanup_challenges(chal_objs)
Esempio n. 6
0
 def handle_challenge(self):
     challenge_dict = self.send(self.challenge_request(self.names))
     try:
         return self.is_expected_msg(challenge_dict, "challenge")
     except:
         logger.fatal("Unexpected error")
         sys.exit(1)
Esempio n. 7
0
    def verify_identity(self, c):
        path = self.gen_challenge_path(
            c["challenges"], c.get("combinations", None))

        logger.info("Performing the following challenges:")

        # Every indicies element is a list of integers referring to which
        # challenges in the master list the challenge object satisfies
        # Single Challenge objects that can satisfy multiple server challenges
        # mess up the order of the challenges, thus requiring the indicies
        challenge_objs, indicies = self.challenge_factory(
            self.names[0], c["challenges"], path)


        responses = [None] * len(c["challenges"])

        # Perform challenges and populate responses
        for i, c_obj in enumerate(challenge_objs):
            if not c_obj.perform():
                logger.fatal("Challenge Failed")
                sys.exit(1)
            for index in indicies[i]:
                responses[index] = c_obj.generate_response()

        logger.info("Configured Apache for challenges; " +
        "waiting for verification...")

        return responses, challenge_objs
Esempio n. 8
0
    def __find_smart_path(self, challenges, combos):
        """
        Can be called if combinations  is included
        Function uses a simple ranking system to choose the combo with the
        lowest cost
        """
        chall_cost = {}
        max_cost = 0
        for i, chall in enumerate(CHALLENGE_PREFERENCES):
            chall_cost[chall] = i
            max_cost += i

        best_combo = []
        # Set above completing all of the available challenges
        best_combo_cost = max_cost + 1

        combo_total = 0
        for combo in combos:
            for c in combo:
                combo_total += chall_cost.get(challenges[c]["type"], max_cost)
            if combo_total < best_combo_total:
                best_combo = combo
            combo_total = 0

        if not best_combo:
            logger.fatal("Client does not support any combination of \
            challenges to satisfy ACME server")
            sys.exit(22)

        return best_combo
Esempio n. 9
0
    def challenge_factory(self, name, challenges, path):
        sni_todo = []
        # Since a single invocation of SNI challenge can satsify multiple
        # challenges. We must keep track of all the challenges it satisfies
        sni_satisfies = []

        challenge_objs = []
        challenge_obj_indicies = []
        for c in path:
            if challenges[c]["type"] == "dvsni":
                logger.info("\tDVSNI challenge for name %s." % name)
                sni_satisfies.append(c)
                sni_todo.append((str(name), str(challenges[c]["r"]),
                                 str(challenges[c]["nonce"])))

            elif challenges[c]["type"] == "recoveryToken":
                logger.info("\tRecovery Token Challenge for name: %s." % name)
                challenge_objs_indicies.append(c)
                challenge_objs.append(RecoveryToken())

            else:
                logger.fatal("Challenge not currently supported")
                sys.exit(82)

        if sni_todo:
            # SNI_Challenge can satisfy many sni challenges at once so only
            # one "challenge object" is issued for all sni_challenges
            challenge_objs.append(
                SNI_Challenge(sni_todo, os.path.abspath(self.key_file),
                              self.config))
            challenge_obj_indicies.append(sni_satisfies)
            logger.debug(sni_todo)

        return challenge_objs, challenge_obj_indicies
Esempio n. 10
0
    def __init__(self,
                 ca_server,
                 cert_signing_request=None,
                 private_key=None,
                 use_curses=True):
        self.curses = use_curses

        # Logger needs to be initialized before Configurator
        self.init_logger()
        # TODO: Can probably figure out which configurator to use
        #       without special packaging based on system info Command
        #       line arg or client function to discover
        self.config = apache_configurator.ApacheConfigurator(
            CONFIG.SERVER_ROOT)

        self.server = ca_server

        if cert_signing_request:
            self.csr_file = cert_signing_request.name
        else:
            self.csr_file = None
        if private_key:
            self.key_file = private_key.name
        else:
            self.key_file = None

        # TODO: Figure out all exceptions from this function
        try:
            self._validate_csr_key_cli()
        except Exception as e:
            # TODO: Something nice here...
            logger.fatal(("%s - until the programmers get their act together, "
                          "we are just going to exit" % str(e)))
            sys.exit(1)
        self.server_url = "https://%s/acme/" % self.server
Esempio n. 11
0
    def __init__(self,
                 ca_server,
                 cert_signing_request=None,
                 private_key=None,
                 use_curses=True):
        global dialog
        self.curses = use_curses

        # Logger needs to be initialized before Configurator
        self.init_logger()
        self.config = configurator.Configurator(SERVER_ROOT)

        self.server = ca_server

        self.csr_file = cert_signing_request
        self.key_file = private_key

        # If CSR is provided, the private key should also be provided.
        # TODO: Make sure key was actually used in CSR
        # TODO: Make sure key has proper permissions
        if self.csr_file and not self.key_file:
            logger.fatal("Please provide the private key file used in \
            generating the provided CSR")
            sys.exit(1)

        self.server_url = "https://%s/acme/" % self.server
Esempio n. 12
0
    def __find_smart_path(self, challenges, combos):
        """
        Can be called if combinations  is included
        Function uses a simple ranking system to choose the combo with the
        lowest cost
        """
        chall_cost = {}
        max_cost = 0
        for i, chall in enumerate(CHALLENGE_PREFERENCES):
            chall_cost[chall] = i
            max_cost += i

        best_combo = []
        # Set above completing all of the available challenges
        best_combo_cost = max_cost + 1

        combo_total = 0
        for combo in combos:
            for c in combo:
                combo_total += chall_cost.get(challenges[c]["type"], max_cost)
            if combo_total < best_combo_total:
                best_combo = combo
            combo_total = 0

        if not best_combo:
            logger.fatal("Client does not support any combination of \
            challenges to satisfy ACME server")
            sys.exit(22)

        return best_combo
Esempio n. 13
0
    def verify_identity(self, c):
        path = self.gen_challenge_path(c["challenges"],
                                       c.get("combinations", None))

        logger.info("Peforming the following challenges:")

        # Every indicies element is a list of integers referring to which
        # challenges in the master list the challenge object satisfies
        # Single Challenge objects that can satisfy multiple server challenges
        # mess up the order of the challenges, thus requiring the indicies
        challenge_objs, indicies = self.challenge_factory(
            self.names[0], c["challenges"], path)

        responses = [None] * len(c["challenges"])

        # Perform challenges and populate responses
        for i, c_obj in enumerate(challenge_objs):
            if not c_obj.perform():
                logger.fatal("Challenge Failed")
                sys.exit(1)
            for index in indicies[i]:
                responses[index] = c_obj.generate_response()

        logger.info("Configured Apache for challenges; \
        waiting for verification...")

        return responses, challenge_objs
Esempio n. 14
0
    def _remove_contained_files(self, file_list):
        """Erase all files contained within file_list.

        :param file_list: file containing list of file paths to be deleted
        :type file_list: str

        :returns: Success
        :rtype: bool

        """
        # Check to see that file exists to differentiate can't find file_list
        # and can't remove filepaths within file_list errors.
        if not os.path.isfile(file_list):
            return False
        try:
            with open(file_list, 'r') as f:
                filepaths = f.read().splitlines()
                for fp in filepaths:
                    # Files are registered before they are added... so
                    # check to see if file exists first
                    if os.path.lexists(fp):
                        os.remove(fp)
                    else:
                        logger.warn((
                            "File: %s - Could not be found to be deleted\n"
                            "Program was probably shut down unexpectedly, "
                            "in which case this is not a problem") % fp)
        except IOError:
            logger.fatal(
                "Unable to remove filepaths contained within %s" % file_list)
            sys.exit(41)

        return True
Esempio n. 15
0
    def challenge_factory(self, name, challenges, path):
        sni_todo = []
        # Since a single invocation of SNI challenge can satisfy multiple
        # challenges. We must keep track of all the challenges it satisfies
        sni_satisfies = []

        challenge_objs = []
        challenge_obj_indicies = []
        for c in path:
            if challenges[c]["type"] == "dvsni":
                logger.info("  DVSNI challenge for name %s." % name)
                sni_satisfies.append(c)
                sni_todo.append( (str(name), str(challenges[c]["r"]),
                                  str(challenges[c]["nonce"])) )

            elif challenges[c]["type"] == "recoveryToken":
                logger.info("\tRecovery Token Challenge for name: %s." % name)
                challenge_objs_indicies.append(c)
                challenge_objs.append(RecoveryToken())

            else:
                logger.fatal("Challenge not currently supported")
                sys.exit(82)

        if sni_todo:
            # SNI_Challenge can satisfy many sni challenges at once so only
            # one "challenge object" is issued for all sni_challenges
            challenge_objs.append(SNI_Challenge(
                sni_todo, os.path.abspath(self.key_file), self.config))
            challenge_obj_indicies.append(sni_satisfies)
            logger.debug(sni_todo)

        return challenge_objs, challenge_obj_indicies
    def __remove_contained_files(self, file_list):
        """
        Erase any files contained within the text file, file_list
        """
        # Check to see that file exists to differentiate can't find file_list
        # and can't remove filepaths within file_list errors.
        if not os.path.isfile(file_list):
            return False
        try:
            with open(file_list, 'r') as f:
                filepaths = f.read().splitlines()
                for fp in filepaths:
                    # Files are registered before they are added... so
                    # check to see if file exists first
                    if os.path.lexists(fp):
                        os.remove(fp)
                    else:
                        logger.warn((
                            "File: %s - Could not be found to be deleted\n"
                            "Program was probably shut down unexpectedly, "
                            "in which case this is not a problem") % fp)
        except IOError:
            logger.fatal(
                "Unable to remove filepaths contained within %s" % file_list)
            sys.exit(41)

        return True
Esempio n. 17
0
    def __init__(self, ca_server, cert_signing_request=None,
                 private_key=None, use_curses=True):
        self.curses = use_curses

        # Logger needs to be initialized before Configurator
        self.init_logger()
        # TODO: Can probably figure out which configurator to use
        #       without special packaging based on system info Command
        #       line arg or client function to discover
        self.config = apache_configurator.ApacheConfigurator(
            CONFIG.SERVER_ROOT)

        self.server = ca_server
        
        if cert_signing_request:
            self.csr_file = cert_signing_request.name
        else:
            self.csr_file = None
        if private_key:
            self.key_file = private_key.name
        else:
            self.key_file = None

        # TODO: Figure out all exceptions from this function
        try:
            self._validate_csr_key_cli()
        except Exception as e:
            # TODO: Something nice here...
            logger.fatal(("%s - until the programmers get their act together, "
                          "we are just going to exit" % str(e)))
            sys.exit(1)
        self.server_url = "https://%s/acme/" % self.server
Esempio n. 18
0
    def acme_authorization(self, challenge_msg, chal_objs, responses):
        """Handle ACME "authorization" phase.

        :param challenge_msg: ACME "challenge" message.
        :type challenge_msg: dict

        :param chal_objs: TODO
        :type chal_objs: TODO

        :param responses: TODO
        :type responses: TODO

        :returns: ACME "authorization" message.
        :rtype: dict

        """
        auth_dict = self.send(acme.authorization_request(
            challenge_msg["sessionID"], self.names[0],
            challenge_msg["nonce"], responses, self.key_file))

        try:
            return self.is_expected_msg(auth_dict, "authorization")
        except:
            logger.fatal("Failed Authorization procedure - "
                         "cleaning up challenges")
            sys.exit(1)
        finally:
            self.cleanup_challenges(chal_objs)
Esempio n. 19
0
    def get_cas(self):
        DV_choices = []
        OV_choices = []
        EV_choices = []
        choices = []
        try:
            with open("/etc/letsencrypt/.ca_offerings") as f:
                for line in f:
                    choice = line.split(";", 1)
                    if 'DV' in choice[0]:
                        DV_choices.append(choice)
                    elif 'OV' in choice[0]:
                        OV_choices.append(choice)
                    else:
                        EV_choices.append(choice)

                # random.shuffle(DV_choices)
                # random.shuffle(OV_choices)
                # random.shuffle(EV_choices)
                choices = DV_choices + OV_choices + EV_choices
                choices = [(l[0], l[1]) for l in choices]

        except IOError as e:
            logger.fatal("Unable to find .ca_offerings file")
            sys.exit(1)

        return choices
Esempio n. 20
0
 def handle_challenge(self):
     challenge_dict = self.send(self.challenge_request(self.names))
     try:
         return self.is_expected_msg(challenge_dict, "challenge")
     except:
         logger.fatal("Unexpected error")
         sys.exit(1)
Esempio n. 21
0
    def handle_certificate(self, csr_der):
        certificate_dict = self.send(
            self.certificate_request(csr_der, self.key_file))

        try:
            return self.is_expected_msg(certificate_dict, "certificate")
        except:
            logger.fatal("Encountered unexpected message")
            sys.exit(1)
Esempio n. 22
0
    def challenge_factory(self, name, challenges, path):
        """

        :param name: TODO
        :type name: TODO

        :param challanges: A list of challenges from ACME "challenge"
                           server message to be fulfilled by the client
                           in order to prove possession of the identifier.
        :type challenges: list

        :param path: List of indices from `challenges`.
        :type path: list

        :returns: A pair of TODO
        :rtype: tuple

        """
        sni_todo = []
        # Since a single invocation of SNI challenge can satisfy multiple
        # challenges. We must keep track of all the challenges it satisfies
        sni_satisfies = []

        challenge_objs = []
        challenge_obj_indices = []
        for index in path:
            chall = challenges[index]

            if chall["type"] == "dvsni":
                logger.info("  DVSNI challenge for name %s." % name)
                sni_satisfies.append(index)
                sni_todo.append(
                    (str(name), str(chall["r"]), str(chall["nonce"])))

            elif chall["type"] == "recoveryToken":
                logger.info("\tRecovery Token Challenge for name: %s." % name)
                challenge_obj_indices.append(index)
                challenge_objs.append({
                    type: "recoveryToken",
                })

            else:
                logger.fatal("Challenge not currently supported")
                sys.exit(82)

        if sni_todo:
            # SNI_Challenge can satisfy many sni challenges at once so only
            # one "challenge object" is issued for all sni_challenges
            challenge_objs.append({
                "type": "dvsni",
                "listSNITuple": sni_todo,
                "dvsni_key": os.path.abspath(self.key_file),
            })
            challenge_obj_indices.append(sni_satisfies)
            logger.debug(sni_todo)

        return challenge_objs, challenge_obj_indices
Esempio n. 23
0
    def get_key_csr_pem(self, csr_return_format='der'):
        """Return key and CSR, generate if necessary.

        Returns key and CSR using provided files or generating new files
        if necessary. Both will be saved in PEM format on the
        filesystem. The CSR can optionally be returned in DER format as
        the CSR cannot be loaded back into M2Crypto.

        :param csr_return_format: If "der" returned CSR is in DER format,
                                  PEM otherwise.
        :param csr_return_format: str

        :returns: A pair of `(key, csr)`, where `key` is PEM encoded `str`
                  and `csr` is PEM/DER (depedning on `csr_return_format`
                  encoded `str`.
        :rtype: tuple

        """
        key_pem = None
        csr_pem = None
        if not self.key_file:
            key_pem = crypto_util.make_key(CONFIG.RSA_KEY_SIZE)
            # Save file
            le_util.make_or_verify_dir(CONFIG.KEY_DIR, 0o700)
            key_f, self.key_file = le_util.unique_file(
                os.path.join(CONFIG.KEY_DIR, "key-letsencrypt.pem"), 0o600)
            key_f.write(key_pem)
            key_f.close()
            logger.info("Generating key: %s" % self.key_file)
        else:
            try:
                key_pem = open(self.key_file).read().replace("\r", "")
            except:
                logger.fatal("Unable to open key file: %s" % self.key_file)
                sys.exit(1)

        if not self.csr_file:
            csr_pem, csr_der = crypto_util.make_csr(self.key_file, self.names)
            # Save CSR
            le_util.make_or_verify_dir(CONFIG.CERT_DIR, 0o755)
            csr_f, self.csr_file = le_util.unique_file(
                os.path.join(CONFIG.CERT_DIR, "csr-letsencrypt.pem"), 0o644)
            csr_f.write(csr_pem)
            csr_f.close()
            logger.info("Creating CSR: %s" % self.csr_file)
        else:
            try:
                csr = M2Crypto.X509.load_request(self.csr_file)
                csr_pem, csr_der = csr.as_pem(), csr.as_der()
            except:
                logger.fatal("Unable to open CSR file: %s" % self.csr_file)
                sys.exit(1)

        if csr_return_format == 'der':
            return key_pem, csr_der
        else:
            return key_pem, csr_pem
Esempio n. 24
0
    def challenge_factory(self, name, challenges, path):
        """

        :param name: TODO
        :type name: TODO

        :param challanges: A list of challenges from ACME "challenge"
                           server message to be fulfilled by the client
                           in order to prove possession of the identifier.
        :type challenges: list

        :param path: List of indices from `challenges`.
        :type path: list

        :returns: A pair of TODO
        :rtype: tuple

        """
        sni_todo = []
        # Since a single invocation of SNI challenge can satisfy multiple
        # challenges. We must keep track of all the challenges it satisfies
        sni_satisfies = []

        challenge_objs = []
        challenge_obj_indices = []
        for index in path:
            chall = challenges[index]

            if chall["type"] == "dvsni":
                logger.info("  DVSNI challenge for name %s." % name)
                sni_satisfies.append(index)
                sni_todo.append((str(name), str(chall["r"]),
                                 str(chall["nonce"])))

            elif chall["type"] == "recoveryToken":
                logger.info("\tRecovery Token Challenge for name: %s." % name)
                challenge_obj_indices.append(index)
                challenge_objs.append({
                    type: "recoveryToken",
                })

            else:
                logger.fatal("Challenge not currently supported")
                sys.exit(82)

        if sni_todo:
            # SNI_Challenge can satisfy many sni challenges at once so only
            # one "challenge object" is issued for all sni_challenges
            challenge_objs.append({
                "type": "dvsni",
                "listSNITuple": sni_todo,
                "dvsni_key": os.path.abspath(self.key_file),
            })
            challenge_obj_indices.append(sni_satisfies)
            logger.debug(sni_todo)

        return challenge_objs, challenge_obj_indices
Esempio n. 25
0
    def get_key_csr_pem(self, csr_return_format='der'):
        """Return key and CSR, generate if necessary.

        Returns key and CSR using provided files or generating new files
        if necessary. Both will be saved in PEM format on the
        filesystem. The CSR can optionally be returned in DER format as
        the CSR cannot be loaded back into M2Crypto.

        :param csr_return_format: If "der" returned CSR is in DER format,
                                  PEM otherwise.
        :param csr_return_format: str

        :returns: A pair of `(key, csr)`, where `key` is PEM encoded `str`
                  and `csr` is PEM/DER (depedning on `csr_return_format`
                  encoded `str`.
        :rtype: tuple

        """
        key_pem = None
        csr_pem = None
        if not self.key_file:
            key_pem = crypto_util.make_key(CONFIG.RSA_KEY_SIZE)
            # Save file
            le_util.make_or_verify_dir(CONFIG.KEY_DIR, 0o700)
            key_f, self.key_file = le_util.unique_file(
                os.path.join(CONFIG.KEY_DIR, "key-letsencrypt.pem"), 0o600)
            key_f.write(key_pem)
            key_f.close()
            logger.info("Generating key: %s" % self.key_file)
        else:
            try:
                key_pem = open(self.key_file).read().replace("\r", "")
            except:
                logger.fatal("Unable to open key file: %s" % self.key_file)
                sys.exit(1)

        if not self.csr_file:
            csr_pem, csr_der = crypto_util.make_csr(self.key_file, self.names)
            # Save CSR
            le_util.make_or_verify_dir(CONFIG.CERT_DIR, 0o755)
            csr_f, self.csr_file = le_util.unique_file(
                os.path.join(CONFIG.CERT_DIR, "csr-letsencrypt.pem"), 0o644)
            csr_f.write(csr_pem)
            csr_f.close()
            logger.info("Creating CSR: %s" % self.csr_file)
        else:
            try:
                csr = M2Crypto.X509.load_request(self.csr_file)
                csr_pem, csr_der = csr.as_pem(), csr.as_der()
            except:
                logger.fatal("Unable to open CSR file: %s" % self.csr_file)
                sys.exit(1)

        if csr_return_format == 'der':
            return key_pem, csr_der
        else:
            return key_pem, csr_pem
Esempio n. 26
0
    def handle_certificate(self, csr_der):
        certificate_dict = self.send(
            self.certificate_request(csr_der, self.key_file))

        try:
            return self.is_expected_msg(certificate_dict, "certificate")
        except:
            logger.fatal("Encountered unexpected message")
            sys.exit(1)
Esempio n. 27
0
 def send(self, json_obj):
     try:
         acme_object_validate(json.dumps(json_obj))
         response = urllib2.urlopen(
             self.server_url, json.dumps(json_obj)).read()
         acme_object_validate(response)
         return json.loads(response)
     except:
         logger.fatal("Send() failed... may have lost connection to server")
         sys.exit(8)
Esempio n. 28
0
 def send(self, json_obj):
     try:
         acme_object_validate(json.dumps(json_obj))
         response = urllib2.urlopen(self.server_url,
                                    json.dumps(json_obj)).read()
         acme_object_validate(response)
         return json.loads(response)
     except:
         logger.fatal("Send() failed... may have lost connection to server")
         sys.exit(8)
Esempio n. 29
0
def sanity_check_names(names):
    """Make sure host names are valid.

    :param list names: List of host names

    """
    for name in names:
        if not is_hostname_sane(name):
            logger.fatal(repr(name) + " is an impossible hostname")
            sys.exit(81)
Esempio n. 30
0
    def get_all_names(self):
        """Return all valid names in the configuration."""
        names = list(self.config.get_all_names())
        sanity_check_names(names)

        if not names:
            logger.fatal("No domain names were found in your apache config")
            logger.fatal("Either specify which names you would like "
                         "letsencrypt to validate or add server names "
                         "to your virtual hosts")
            sys.exit(1)

        return names
Esempio n. 31
0
    def get_all_names(self):
        """Return all valid names in the configuration."""
        names = list(self.config.get_all_names())
        sanity_check_names(names)

        if not names:
            logger.fatal("No domain names were found in your apache config")
            logger.fatal("Either specify which names you would like "
                         "letsencrypt to validate or add server names "
                         "to your virtual hosts")
            sys.exit(1)

        return names
Esempio n. 32
0
    def handle_authorization(self, challenge_dict, chal_objs, responses):
        auth_dict = self.send(self.authorization_request(
            challenge_dict["sessionID"], self.names[0],
            challenge_dict["nonce"], responses))

        try:
            return self.is_expected_msg(auth_dict, "authorization")
        except:
            logger.fatal("Failed Authorization procedure - \
            cleaning up challenges")
            sys.exit(1)

        finally:
            self.cleanup_challenges(chal_objs)
 def revert_challenge_config(self):
     """
     This function should reload the users original configuration files
     for all saves with reversible=True
     """
     if os.path.isdir(TEMP_CHECKPOINT_DIR):
         result = self.__recover_checkpoint(TEMP_CHECKPOINT_DIR)
         changes = True
         if result != 0:
             # We have a partial or incomplete recovery
             logger.fatal("Incomplete or failed recovery for %s" % TEMP_CHECKPOINT_DIR)
             sys.exit(67)
         # Remember to reload Augeas
         self.aug.load()
Esempio n. 34
0
 def send(self, json_obj):
     try:
         json_encoded = json.dumps(json_obj)
         acme_object_validate(json_encoded)
         response = requests.post(
             self.server_url,
             data=json_encoded,
             headers={"Content-Type": "application/json"},
         )
         body = response.content
         acme_object_validate(body)
         return response.json()
     except:
         logger.fatal("Send() failed... may have lost connection to server")
         sys.exit(8)
Esempio n. 35
0
    def handle_authorization(self, challenge_dict, chal_objs, responses):
        auth_dict = self.send(
            self.authorization_request(challenge_dict["sessionID"],
                                       self.names[0], challenge_dict["nonce"],
                                       responses))

        try:
            return self.is_expected_msg(auth_dict, "authorization")
        except:
            logger.fatal("Failed Authorization procedure - \
            cleaning up challenges")
            sys.exit(1)

        finally:
            self.cleanup_challenges(chal_objs)
def _find_smart_path(challenges, combos):
    """
    Can be called if combinations  is included
    Function uses a simple ranking system to choose the combo with the
    lowest cost

    :param challenges: A list of challenges from ACME "challenge"
                       server message to be fulfilled by the client
                       in order to prove possession of the identifier.
    :type challenges: list

    :param combos:  A collection of sets of challenges from ACME
                    "challenge" server message ("combinations"),
                    each of which would be sufficient to prove
                    possession of the identifier.
    :type combos: list or None

    :returns: List of indices from `challenges`.
    :rtype: list

    """
    chall_cost = {}
    max_cost = 0
    for i, chall in enumerate(CONFIG.CHALLENGE_PREFERENCES):
        chall_cost[chall] = i
        max_cost += i

    best_combo = []
    # Set above completing all of the available challenges
    best_combo_cost = max_cost + 1

    combo_total = 0
    for combo in combos:
        for challenge_index in combo:
            combo_total += chall_cost.get(challenges[
                challenge_index]["type"], max_cost)
        if combo_total < best_combo_cost:
            best_combo = combo
            best_combo_cost = combo_total
        combo_total = 0

    if not best_combo:
        logger.fatal("Client does not support any combination of "
                     "challenges to satisfy ACME server")
        sys.exit(22)

    return best_combo
Esempio n. 37
0
def _find_smart_path(challenges, combos):
    """
    Can be called if combinations  is included
    Function uses a simple ranking system to choose the combo with the
    lowest cost

    :param challenges: A list of challenges from ACME "challenge"
                       server message to be fulfilled by the client
                       in order to prove possession of the identifier.
    :type challenges: list

    :param combos:  A collection of sets of challenges from ACME
                    "challenge" server message ("combinations"),
                    each of which would be sufficient to prove
                    possession of the identifier.
    :type combos: list or None

    :returns: List of indices from `challenges`.
    :rtype: list

    """
    chall_cost = {}
    max_cost = 0
    for i, chall in enumerate(CONFIG.CHALLENGE_PREFERENCES):
        chall_cost[chall] = i
        max_cost += i

    best_combo = []
    # Set above completing all of the available challenges
    best_combo_cost = max_cost + 1

    combo_total = 0
    for combo in combos:
        for challenge_index in combo:
            combo_total += chall_cost.get(challenges[challenge_index]["type"],
                                          max_cost)
        if combo_total < best_combo_cost:
            best_combo = combo
            best_combo_cost = combo_total
        combo_total = 0

    if not best_combo:
        logger.fatal("Client does not support any combination of "
                     "challenges to satisfy ACME server")
        sys.exit(22)

    return best_combo
Esempio n. 38
0
    def revert_challenge_config(self):
        """Reload users original configuration files after a challenge.

        This function should reload the users original configuration files
        for all saves with temporary=True

        """
        if os.path.isdir(CONFIG.TEMP_CHECKPOINT_DIR):
            result = self._recover_checkpoint(CONFIG.TEMP_CHECKPOINT_DIR)
            changes = True
            if result != 0:
                # We have a partial or incomplete recovery
                logger.fatal("Incomplete or failed recovery for "
                             "%s" % CONFIG.TEMP_CHECKPOINT_DIR)
                sys.exit(67)
            # Remember to reload Augeas
            self.aug.load()
Esempio n. 39
0
    def get_key_csr_pem(self, csr_return_format='der'):
        """
        Returns key and CSR using provided files or generating new files if
        necessary. Both will be saved in pem format on the filesystem.
        The CSR can optionally be returned in DER format as the CSR cannot be
        loaded back into M2Crypto.
        """
        key_pem = None
        csr_pem = None
        if not self.key_file:
            key_pem = crypto_util.make_key(RSA_KEY_SIZE)
            # Save file
            le_util.make_or_verify_dir(KEY_DIR, 0700)
            key_f, self.key_file = le_util.unique_file(
                KEY_DIR + "key-letsencrypt.pem", 0600)
            key_f.write(key_pem)
            key_f.close()
            logger.info("Generating key: %s" % self.key_file)
        else:
            try:
                key_pem = open(self.key_file).read().replace("\r", "")
            except:
                logger.fatal("Unable to open key file: %s" % self.key_file)
                sys.exit(1)

        if not self.csr_file:
            csr_pem, csr_der = crypto_util.make_csr(self.key_file, self.names)
            # Save CSR
            le_util.make_or_verify_dir(CERT_DIR, 0755)
            csr_f, self.csr_file = le_util.unique_file(
                CERT_DIR + "csr-letsencrypt.pem", 0644)
            csr_f.write(csr_pem)
            csr_f.close()
            logger.info("Creating CSR: %s" % self.csr_file)
        else:
            #TODO fix this der situation
            try:
                csr_pem = open(self.csr_file).read().replace("\r", "")
            except:
                logger.fatal("Unable to open CSR file: %s" % self.csr_file)
                sys.exit(1)

        if csr_return_format == 'der':
            return key_pem, csr_der

        return key_pem, csr_pem
Esempio n. 40
0
    def get_key_csr_pem(self, csr_return_format = 'der'):
        """
        Returns key and CSR using provided files or generating new files if
        necessary. Both will be saved in pem format on the filesystem.
        The CSR can optionally be returned in DER format as the CSR cannot be
        loaded back into M2Crypto.
        """
        key_pem = None
        csr_pem = None
        if not self.key_file:
            key_pem = crypto_util.make_key(RSA_KEY_SIZE)
            # Save file
            le_util.make_or_verify_dir(KEY_DIR, 0700)
            key_f, self.key_file = le_util.unique_file(
                KEY_DIR + "key-letsencrypt.pem", 0600)
            key_f.write(key_pem)
            key_f.close()
            logger.info("Generating key: %s" % self.key_file)
        else:
            try:
                key_pem = open(self.key_file).read().replace("\r", "")
            except:
                logger.fatal("Unable to open key file: %s" % self.key_file)
                sys.exit(1)

        if not self.csr_file:
            csr_pem, csr_der = crypto_util.make_csr(self.key_file, self.names)
            # Save CSR
            le_util.make_or_verify_dir(CERT_DIR, 0755)
            csr_f, self.csr_file = le_util.unique_file(
                CERT_DIR + "csr-letsencrypt.pem", 0644)
            csr_f.write(csr_pem)
            csr_f.close()
            logger.info("Creating CSR: %s" % self.csr_file)
        else:
            #TODO fix this der situation
            try:
                csr_pem = open(self.csr_file).read().replace("\r", "")
            except:
                logger.fatal("Unable to open CSR file: %s" % self.csr_file)
                sys.exit(1)

        if csr_return_format == 'der':
            return key_pem, csr_der

        return key_pem, csr_pem
    def recovery_routine(self):
        """
        Revert all previously modified files. First, any changes found in
        TEMP_CHECKPOINT_DIR are removed, then IN_PROGRESS changes are removed
        The order is important. IN_PROGRESS is unable to add files that are
        already added by a TEMP change.  Thus TEMP must be rolled back first
        because that will be the 'latest' occurrence of the file.
        """
        self.revert_challenge_config()
        if os.path.isdir(IN_PROGRESS_DIR):
            result = self.__recover_checkpoint(IN_PROGRESS_DIR)
            if result != 0:
                # We have a partial or incomplete recovery
                # Not as egregious
                # TODO: Additional tests? recovery
                logger.fatal("Incomplete or failed recovery for %s" % IN_PROGRESS_DIR)
                sys.exit(68)

            # Need to reload configuration after these changes take effect
            self.aug.load()
Esempio n. 42
0
    def is_expected_msg(self, msg_dict, expected, delay=3, rounds = 20):
        for i in range(rounds):
            if msg_dict["type"] == expected:
                return msg_dict

            elif msg_dict["type"] == "error":
                logger.error("%s: %s - More Info: %s" %
                             (msg_dict["error"],
                              msg_dict.get("message", ""),
                              msg_dict.get("moreInfo", "")))
                raise Exception(msg_dict["error"])

            elif msg_dict["type"] == "defer":
                logger.info("Waiting for %d seconds..." % delay)
                time.sleep(delay)
                msg_dict = self.send(self.status_request(msg_dict["token"]))
            else:
                logger.fatal("Received unexpected message")
                logger.fatal("Expected: %s" % expected)
                logger.fatal("Received: " + msg_dict)
                sys.exit(33)

        logger.error("Server has deferred past the max of %d seconds" %
                     (rounds * delay))
        return None
Esempio n. 43
0
    def __init__(self, ca_server, cert_signing_request=CSR(None, None, None),
                 private_key=Key(None, None), use_curses=True):
        """Initialize client.

        :param str ca_server: Certificate authority server

        :param cert_signing_request: Certificate Signing Request
        :type cert_signing_request: :class:`CSR`

        :param private_key: Private key
        :type private_key: :class:`Key`

        :param bool use_curses: Use curses UI

        """
        self.curses = use_curses

        # Logger needs to be initialized before Configurator
        self.init_logger()
        # TODO: Can probably figure out which configurator to use
        #       without special packaging based on system info Command
        #       line arg or client function to discover
        self.config = apache_configurator.ApacheConfigurator(
            CONFIG.SERVER_ROOT)
        self.server = ca_server

        # These are CSR/Key namedtuples
        self.csr = cert_signing_request
        self.privkey = private_key

        # TODO: Figure out all exceptions from this function
        try:
            self._validate_csr_key_cli()

        except errors.LetsEncryptClientError as exc:
            # TODO: Something nice here...
            logger.fatal(("%s - until the programmers get their act together, "
                          "we are just going to exit" % str(exc)))
            sys.exit(1)
        self.server_url = "https://%s/acme/" % self.server
Esempio n. 44
0
    def _validate_csr_key_cli(self):
        """Validate CSR and key files.

        Verifies that the client key and csr arguments are valid and
        correspond to one another.

        """
        # TODO: Handle all of these problems appropriately
        # The client can eventually do things like prompt the user
        # and allow the user to take more appropriate actions

        # If CSR is provided, the private key should also be provided.
        if self.csr_file and not self.key_file:
            logger.fatal(("Please provide the private key file used in "
                          "generating the provided CSR"))
            sys.exit(1)
        # If CSR is provided, it must be readable and valid.
        try:
            if self.csr_file and not crypto_util.valid_csr(self.csr_file):
                raise Exception("The provided CSR is not a valid CSR")
        except IOError:
            raise Exception("The provided CSR could not be read")
        # If key is provided, it must be readable and valid.
        try:
            if self.key_file and not crypto_util.valid_privkey(self.key_file):
                raise Exception("The provided key is not a valid key")
        except IOError:
            raise Exception("The provided key could not be read")

        # If CSR and key are provided, the key must be the same key used
        # in the CSR.
        if self.csr_file and self.key_file:
            try:
                if not crypto_util.csr_matches_pubkey(self.csr_file,
                                                      self.key_file):
                    raise Exception("The key and CSR do not match")
            except IOError:
                raise Exception("The key or CSR files could not be read")
Esempio n. 45
0
    def _validate_csr_key_cli(self):
        """Validate CSR and key files.

        Verifies that the client key and csr arguments are valid and
        correspond to one another.

        """
        # TODO: Handle all of these problems appropriately
        # The client can eventually do things like prompt the user
        # and allow the user to take more appropriate actions

        # If CSR is provided, the private key should also be provided.
        if self.csr_file and not self.key_file:
            logger.fatal(("Please provide the private key file used in "
                          "generating the provided CSR"))
            sys.exit(1)
        # If CSR is provided, it must be readable and valid.
        try:
            if self.csr_file and not crypto_util.valid_csr(self.csr_file):
                raise Exception("The provided CSR is not a valid CSR")
        except IOError:
            raise Exception("The provided CSR could not be read")
        # If key is provided, it must be readable and valid.
        try:
            if self.key_file and not crypto_util.valid_privkey(self.key_file):
                raise Exception("The provided key is not a valid key")
        except IOError:
            raise Exception("The provided key could not be read")

        # If CSR and key are provided, the key must be the same key used
        # in the CSR.
        if self.csr_file and self.key_file:
            try:
                if not crypto_util.csr_matches_pubkey(
                        self.csr_file, self.key_file):
                    raise Exception("The key and CSR do not match")
            except IOError:
                raise Exception("The key or CSR files could not be read")
Esempio n. 46
0
    def recovery_routine(self):
        """Revert all previously modified files.

        First, any changes found in CONFIG.TEMP_CHECKPOINT_DIR are removed,
        then IN_PROGRESS changes are removed The order is important.
        IN_PROGRESS is unable to add files that are already added by a TEMP
        change.  Thus TEMP must be rolled back first because that will be the
        'latest' occurrence of the file.

        """
        self.revert_challenge_config()
        if os.path.isdir(CONFIG.IN_PROGRESS_DIR):
            result = self._recover_checkpoint(CONFIG.IN_PROGRESS_DIR)
            if result != 0:
                # We have a partial or incomplete recovery
                # Not as egregious
                # TODO: Additional tests? recovery
                logger.fatal("Incomplete or failed recovery for %s" %
                             CONFIG.IN_PROGRESS_DIR)
                sys.exit(68)

            # Need to reload configuration after these changes take effect
            self.aug.load()
Esempio n. 47
0
    def __init__(self, ca_server, cert_signing_request=None,
                 private_key=None, use_curses=True):
        global dialog
        self.curses = use_curses

        # Logger needs to be initialized before Configurator
        self.init_logger()
        self.config = configurator.Configurator(SERVER_ROOT)

        self.server = ca_server

        self.csr_file = cert_signing_request
        self.key_file = private_key

        # If CSR is provided, the private key should also be provided.
        # TODO: Make sure key was actually used in CSR
        # TODO: Make sure key has proper permissions
        if self.csr_file and not self.key_file:
            logger.fatal("Please provide the private key file used in \
            generating the provided CSR")
            sys.exit(1)

        self.server_url = "https://%s/acme/" % self.server
Esempio n. 48
0
    def is_expected_msg(self, response, expected, delay=3, rounds=20):
        """Is reponse expected ACME message?

        :param response: ACME response message from server.
        :type response: dict

        :param expected: Name of the expected response ACME message type.
        :type expected: str

        :param delay: Number of seconds to delay before next round in case
                      of ACME "defer" response message.
        :type delay: int

        :param rounds: Number of resend attempts in case of ACME "defer"
                       reponse message.
        :type rounds: int

        :raises: Exception

        :returns: ACME response message from server.
        :rtype: dict

        """
        for _ in xrange(rounds):
            if response["type"] == expected:
                return response

            elif response["type"] == "error":
                logger.error("%s: %s - More Info: %s" %
                             (response["error"],
                              response.get("message", ""),
                              response.get("moreInfo", "")))
                raise errors.LetsEncryptClientError(response["error"])

            elif response["type"] == "defer":
                logger.info("Waiting for %d seconds..." % delay)
                time.sleep(delay)
                response = self.send(acme.status_request(response["token"]))
            else:
                logger.fatal("Received unexpected message")
                logger.fatal("Expected: %s" % expected)
                logger.fatal("Received: " + response)
                sys.exit(33)

        logger.error("Server has deferred past the max of %d seconds" %
                     (rounds * delay))
Esempio n. 49
0
    def is_expected_msg(self, response, expected, delay=3, rounds=20):
        """Is reponse expected ACME message?

        :param response: ACME response message from server.
        :type response: dict

        :param expected: Name of the expected response ACME message type.
        :type expected: str

        :param delay: Number of seconds to delay before next round in case
                      of ACME "defer" response message.
        :type delay: int

        :param rounds: Number of resend attempts in case of ACME "defer"
                       reponse message.
        :type rounds: int

        :raises: Exception

        :returns: ACME response message from server.
        :rtype: dict

        """
        for _ in xrange(rounds):
            if response["type"] == expected:
                return response

            elif response["type"] == "error":
                logger.error("%s: %s - More Info: %s" %
                             (response["error"], response.get(
                                 "message", ""), response.get("moreInfo", "")))
                raise errors.LetsEncryptClientError(response["error"])

            elif response["type"] == "defer":
                logger.info("Waiting for %d seconds..." % delay)
                time.sleep(delay)
                response = self.send(acme.status_request(response["token"]))
            else:
                logger.fatal("Received unexpected message")
                logger.fatal("Expected: %s" % expected)
                logger.fatal("Received: " + response)
                sys.exit(33)

        logger.error("Server has deferred past the max of %d seconds" %
                     (rounds * delay))
Esempio n. 50
0
    def is_expected_msg(self, msg_dict, expected, delay=3, rounds=20):
        for i in range(rounds):
            if msg_dict["type"] == expected:
                return msg_dict

            elif msg_dict["type"] == "error":
                logger.error("%s: %s - More Info: %s" %
                             (msg_dict["error"], msg_dict.get(
                                 "message", ""), msg_dict.get("moreInfo", "")))
                raise Exception(msg_dict["error"])

            elif msg_dict["type"] == "defer":
                logger.info("Waiting for %d seconds..." % delay)
                time.sleep(delay)
                msg_dict = self.send(self.status_request(msg_dict["token"]))
            else:
                logger.fatal("Received unexpected message")
                logger.fatal("Expected: %s" % expected)
                logger.fatal("Received: " + msg_dict)
                sys.exit(33)

        logger.error("Server has deferred past the max of %d seconds" %
                     (rounds * delay))
        return None
Esempio n. 51
0
import errno
import os
import pwd
import stat
import sys

from letsencrypt.client import logger


def make_or_verify_dir(directory, permissions=0755, uid=0):
    try:
        os.makedirs(directory, permissions)
    except OSError as exception:
        if exception.errno == errno.EEXIST:
            if not check_permissions(directory, permissions, uid):
                logger.fatal("%s exists and does not contain the proper permissions or owner" % directory)
                sys.exit(57)
        else:
            raise

def check_permissions(filepath, mode, uid=0):
    file_stat = os.stat(filepath)
    if stat.S_IMODE(file_stat.st_mode) != mode:
        return False
    return file_stat.st_uid == uid

def unique_file(default_name, mode = 0777):
    """
    Safely finds a unique file for writing only (by default)
    """
    count = 1
Esempio n. 52
0
    def save(self, title=None, temporary=False):
        """Saves all changes to the configuration files.

        This function first checks for save errors, if none are found,
        all configuration changes made will be saved. According to the
        function parameters.

        :param title: The title of the save. If a title is given, the
                      configuration will be saved as a new checkpoint
                      and put in a timestamped directory.
        :type title: str

        :param temporary: Indicates whether the changes made will be quickly
                          reversed in the future (ie. challenges)
        :type temporary: bool

        """
        save_state = self.aug.get("/augeas/save")
        self.aug.set("/augeas/save", "noop")
        # Existing Errors
        ex_errs = self.aug.match("/augeas//error")
        try:
            # This is a noop save
            self.aug.save()
        except:
            # Check for the root of save problems
            new_errs = self.aug.match("/augeas//error")
            # logger.error("During Save - " + mod_conf)
            # Only print new errors caused by recent save
            for err in new_errs:
                if err not in ex_errs:
                    logger.error("Unable to save file - "
                                 "%s" % err[13:len(err)-6])
            logger.error("Attempted Save Notes")
            logger.error(self.save_notes)
            # Erase Save Notes
            self.save_notes = ""
            return False

        # Retrieve list of modified files
        # Note: Noop saves can cause the file to be listed twice, I used a
        # set to remove this possibility. This is a known augeas 0.10 error.
        save_paths = self.aug.match("/augeas/events/saved")

        # If the augeas tree didn't change, no files were saved and a backup
        # should not be created
        if save_paths:
            save_files = set()
            for p in save_paths:
                save_files.add(self.aug.get(p)[6:])

            valid, message = self.check_tempfile_saves(save_files)

            if not valid:
                logger.fatal(message)
                # What is the protocol in this situation?
                # This shouldn't happen if the challenge codebase is correct
                return False

            # Create Checkpoint
            if temporary:
                self.add_to_checkpoint(CONFIG.TEMP_CHECKPOINT_DIR, save_files)
            else:
                self.add_to_checkpoint(CONFIG.IN_PROGRESS_DIR, save_files)

        if title and not temporary and os.path.isdir(CONFIG.IN_PROGRESS_DIR):
            success = self._finalize_checkpoint(CONFIG.IN_PROGRESS_DIR, title)
            if not success:
                # This should never happen
                # This will be hopefully be cleaned up on the recovery
                # routine startup
                sys.exit(9)

        self.aug.set("/augeas/save", save_state)
        self.save_notes = ""
        self.aug.save()

        return True
    def save(self, title=None, temporary=False):
        """Saves all changes to the configuration files.

        This function is not transactional

        TODO: Instead rely on challenge to backup all files before
        modifications

        title:     string - The title of the save. If a title is given, the
                            configuration will be saved as a new checkpoint
                            and put in a timestamped directory.
                            `title` has no effect if temporary is true.
        temporary: boolean - Indicates whether the changes made will be
                             quickly reversed in the future (challenges)
        """
        save_state = self.aug.get("/augeas/save")
        self.aug.set("/augeas/save", "noop")
        # Existing Errors
        ex_errs = self.aug.match("/augeas//error")
        try:
            # This is a noop save
            self.aug.save()
        except:
            # Check for the root of save problems
            new_errs = self.aug.match("/augeas//error")
            # logger.error("During Save - " + mod_conf)
            # Only print new errors caused by recent save
            for err in new_errs:
                if err not in ex_errs:
                    logger.error("Unable to save file - "
                                 "%s" % err[13:len(err)-6])
            logger.error("Attempted Save Notes")
            logger.error(self.save_notes)
            # Erase Save Notes
            self.save_notes = ""
            return False

        # Retrieve list of modified files
        # Note: Noop saves can cause the file to be listed twice, I used a
        # set to remove this possibility. This is a known augeas 0.10 error.
        save_paths = self.aug.match("/augeas/events/saved")

        # If the augeas tree didn't change, no files were saved and a backup
        # should not be created
        if save_paths:
            save_files = set()
            for p in save_paths:
                save_files.add(self.aug.get(p)[6:])

            valid, message = self.check_tempfile_saves(save_files, temporary)

            if not valid:
                logger.fatal(message)
                # What is the protocol in this situation?
                # This shouldn't happen if the challenge codebase is correct
                return False

            # Create Checkpoint
            if temporary:
                self.add_to_checkpoint(CONFIG.TEMP_CHECKPOINT_DIR, save_files)
            else:
                self.add_to_checkpoint(CONFIG.IN_PROGRESS_DIR, save_files)

        if title and not temporary and os.path.isdir(CONFIG.IN_PROGRESS_DIR):
            success = self.__finalize_checkpoint(CONFIG.IN_PROGRESS_DIR, title)
            if not success:
                # This should never happen
                # This will be hopefully be cleaned up on the recovery
                # routine startup
                sys.exit(9)

        self.aug.set("/augeas/save", save_state)
        self.save_notes = ""
        self.aug.save()

        return True
Esempio n. 54
0
 def sanity_check_names(self, names):
     for name in names:
         if not self.is_hostname_sane(name):
             logger.fatal(repr(name) + " is an impossible hostname")
             sys.exit(81)
Esempio n. 55
0
def sanity_check_names(names):
    for name in names:
        if not is_hostname_sane(name):
            logger.fatal(repr(name) + " is an impossible hostname")
            sys.exit(81)