Beispiel #1
0
def _describe_certs(config, parsed_certs, parse_failures):
    """Print information about the certs we know about"""
    out: List[str] = []

    notify = out.append

    if not parsed_certs and not parse_failures:
        notify("No certificates found.")
    else:
        if parsed_certs:
            match = "matching " if config.certname or config.domains else ""
            notify("Found the following {0}certs:".format(match))
            notify(_report_human_readable(config, parsed_certs))
        if parse_failures:
            notify("\nThe following renewal configurations "
               "were invalid:")
            notify(_report_lines(parse_failures))

    display_util.notification("\n".join(out), pause=False, wrap=False)
Beispiel #2
0
    def _perform_emailreply00(self, achall):
        response, _ = achall.challb.response_and_validation(achall.account_key)
        
        text = 'A challenge request for S/MIME certificate has been sent. In few minutes, ACME server will send a challenge e-mail to requested recipient {}. Please, copy the ENTIRE subject and paste it below. The subject starts with the label ACME: '.format(achall.domain)
        display_util.notification(text,pause=False)

        code,subject = display_util.input_text('Subject: ', force_interactive=True)
        token64 = subject.split(' ')[-1]
        token1 = jose.b64.b64decode(token64)
        full_token = token1+achall.chall.token
    
        # We reconstruct the ChallengeBody
        challt = messages.ChallengeBody.from_json({ 'type': 'email-reply-00', 'token': jose.b64.b64encode(bytes(full_token)).decode('ascii'), 'url': achall.challb.uri, 'status': achall.challb.status.to_json(), 'from': achall.challb.chall.from_addr })
        response, validation = challt.response_and_validation(achall.account_key)
        digest = hashes.Hash(hashes.SHA256())
        digest.update(validation.encode())
        thumbprint = jose.b64encode(digest.finalize()).decode()
        display_util.notification('A challenge response has been generated. Please, copy the following text, reply the e-mail you have received from ACME server and paste this text in the TOP of the message\'s body: ',pause=False)
        print('\n-----BEGIN ACME RESPONSE-----\n'
            '{}\n'
            '-----END ACME RESPONSE-----\n'.format(thumbprint))
        return response
Beispiel #3
0
def revoke_cert(args, config):
    cli_args = prepare_cli_args(args)
    if (args.reason):
        cli_args.extend(['--reason', args.reason])
    cli_args.extend(['--no-delete-after-revoke'])
    key_path, cert_path = None, None
    try:
        key_path, cert_path = try_open_p12(args.cert_path)
        cli_args.extend(['--cert-path', cert_path])
        cli_args.extend(['--key-path', key_path])
    except ValueError as e:
        if ('Invalid password' in str(e)):
            passphrase = None
            if (args.passphrase):
                passphrase = args.passphrase.encode('utf-8')
            else:
                text = 'Introduce the passphrase of the PKCS12 file.'
                display_util.notification(text, pause=False)
                pf = getpass.getpass('Enter passphrase: ')
                passphrase = pf.encode('utf-8')
            try:
                key_path, cert_path = try_open_p12(args.cert_path,
                                                   passphrase=passphrase)
                cli_args.extend(['--cert-path', cert_path])
                cli_args.extend(['--key-path', key_path])
            except ValueError as e:
                if ('Invalid password' in str(e)):
                    raise e
        elif ('Could not deserialize'):  #pem
            if (args.cert_path):
                cli_args.extend(['--cert-path', args.cert_path])
            if (args.key_path):
                cli_args.extend(['--key-path', args.key_path])
    config, plugins = prepare_config(cli_args)
    certbot_main.revoke(config, plugins)
    if (key_path):
        os.unlink(key_path)
    if (cert_path):
        os.unlink(cert_path)
Beispiel #4
0
def rename_lineage(config: configuration.NamespaceConfig) -> None:
    """Rename the specified lineage to the new name.

    :param config: Configuration.
    :type config: :class:`certbot._internal.configuration.NamespaceConfig`

    """
    certname = get_certnames(config, "rename")[0]

    new_certname = config.new_certname
    if not new_certname:
        code, new_certname = display_util.input_text(
            "Enter the new name for certificate {0}".format(certname),
            force_interactive=True)
        if code != display_util.OK or not new_certname:
            raise errors.Error("User ended interaction.")

    lineage = lineage_for_certname(config, certname)
    if not lineage:
        raise errors.ConfigurationError("No existing certificate with name "
            "{0} found.".format(certname))
    storage.rename_renewal_config(certname, new_certname, config)
    display_util.notification("Successfully renamed {0} to {1}."
                                 .format(certname, new_certname), pause=False)
Beispiel #5
0
def handle_renewal_request(config):
    """Examine each lineage; renew if due and report results"""

    # This is trivially False if config.domains is empty
    if any(domain not in config.webroot_map for domain in config.domains):
        # If more plugins start using cli.add_domains,
        # we may want to only log a warning here
        raise errors.Error(
            "Currently, the renew verb is capable of either "
            "renewing all installed certificates that are due "
            "to be renewed or renewing a single certificate specified "
            "by its name. If you would like to renew specific "
            "certificates by their domains, use the certonly command "
            "instead. The renew verb may provide other options "
            "for selecting certificates to renew in the future.")

    if config.certname:
        conf_files = [
            storage.renewal_file_for_certname(config, config.certname)
        ]
    else:
        conf_files = storage.renewal_conf_files(config)

    renew_successes = []
    renew_failures = []
    renew_skipped = []
    parse_failures = []

    # Noninteractive renewals include a random delay in order to spread
    # out the load on the certificate authority servers, even if many
    # users all pick the same time for renewals.  This delay precedes
    # running any hooks, so that side effects of the hooks (such as
    # shutting down a web service) aren't prolonged unnecessarily.
    apply_random_sleep = not sys.stdin.isatty(
    ) and config.random_sleep_on_renew

    for renewal_file in conf_files:
        display_util.notification("Processing " + renewal_file, pause=False)
        lineage_config = copy.deepcopy(config)
        lineagename = storage.lineagename_for_filename(renewal_file)

        # Note that this modifies config (to add back the configuration
        # elements from within the renewal configuration file).
        try:
            renewal_candidate = _reconstitute(lineage_config, renewal_file)
        except Exception as e:  # pylint: disable=broad-except
            logger.error(
                "Renewal configuration file %s (cert: %s) "
                "produced an unexpected error: %s. Skipping.", renewal_file,
                lineagename, e)
            logger.debug("Traceback was:\n%s", traceback.format_exc())
            parse_failures.append(renewal_file)
            continue

        try:
            if renewal_candidate is None:
                parse_failures.append(renewal_file)
            else:
                # This call is done only for retro-compatibility purposes.
                # TODO: Remove this call once zope dependencies are removed from Certbot.
                zope.component.provideUtility(lineage_config,
                                              interfaces.IConfig)
                renewal_candidate.ensure_deployed()
                from certbot._internal import main
                plugins = plugins_disco.PluginsRegistry.find_all()
                if should_renew(lineage_config, renewal_candidate):
                    # Apply random sleep upon first renewal if needed
                    if apply_random_sleep:
                        sleep_time = random.uniform(1, 60 * 8)
                        logger.info(
                            "Non-interactive renewal: random delay of %s seconds",
                            sleep_time)
                        time.sleep(sleep_time)
                        # We will sleep only once this day, folks.
                        apply_random_sleep = False

                    # domains have been restored into lineage_config by reconstitute
                    # but they're unnecessary anyway because renew_cert here
                    # will just grab them from the certificate
                    # we already know it's time to renew based on should_renew
                    # and we have a lineage in renewal_candidate
                    main.renew_cert(lineage_config, plugins, renewal_candidate)
                    renew_successes.append(renewal_candidate.fullchain)
                else:
                    expiry = crypto_util.notAfter(
                        renewal_candidate.version(
                            "cert", renewal_candidate.latest_common_version()))
                    renew_skipped.append("%s expires on %s" %
                                         (renewal_candidate.fullchain,
                                          expiry.strftime("%Y-%m-%d")))
                # Run updater interface methods
                updater.run_generic_updaters(lineage_config, renewal_candidate,
                                             plugins)

        except Exception as e:  # pylint: disable=broad-except
            # obtain_cert (presumably) encountered an unanticipated problem.
            logger.error("Failed to renew certificate %s with error: %s",
                         lineagename, e)
            logger.debug("Traceback was:\n%s", traceback.format_exc())
            renew_failures.append(renewal_candidate.fullchain)

    # Describe all the results
    _renew_describe_results(config, renew_successes, renew_failures,
                            renew_skipped, parse_failures)

    if renew_failures or parse_failures:
        raise errors.Error("{0} renew failure(s), {1} parse failure(s)".format(
            len(renew_failures), len(parse_failures)))

    # Windows installer integration tests rely on handle_renewal_request behavior here.
    # If the text below changes, these tests will need to be updated accordingly.
    logger.debug("no renewal failures")
Beispiel #6
0
    def handle_authorizations(
            self,
            orderr: messages.OrderResource,
            config: configuration.NamespaceConfig,
            best_effort: bool = False,
            max_retries: int = 30) -> List[messages.AuthorizationResource]:
        """
        Retrieve all authorizations, perform all challenges required to validate
        these authorizations, then poll and wait for the authorization to be checked.
        :param acme.messages.OrderResource orderr: must have authorizations filled in
        :param certbot.configuration.NamespaceConfig config: current Certbot configuration
        :param bool best_effort: if True, not all authorizations need to be validated (eg. renew)
        :param int max_retries: maximum number of retries to poll authorizations
        :returns: list of all validated authorizations
        :rtype: List

        :raises .AuthorizationError: If unable to retrieve all authorizations
        """
        authzrs = orderr.authorizations[:]
        if not authzrs:
            raise errors.AuthorizationError('No authorization to handle.')
        if not self.acme:
            raise errors.Error(
                "No ACME client defined, authorizations cannot be handled.")

        # Retrieve challenges that need to be performed to validate authorizations.
        achalls = self._choose_challenges(authzrs)
        if not achalls:
            return authzrs

        # Starting now, challenges will be cleaned at the end no matter what.
        with error_handler.ExitHandler(self._cleanup_challenges, achalls):
            # To begin, let's ask the authenticator plugin to perform all challenges.
            try:
                resps = self.auth.perform(achalls)

                # If debug is on, wait for user input before starting the verification process.
                if config.debug_challenges:
                    display_util.notification(
                        'Challenges loaded. Press continue to submit to CA.\n'
                        + self._debug_challenges_msg(achalls, config),
                        pause=True)
            except errors.AuthorizationError as error:
                logger.critical('Failure in setting up challenges.')
                logger.info('Attempting to clean up outstanding challenges...')
                raise error
            # All challenges should have been processed by the authenticator.
            assert len(resps) == len(
                achalls), 'Some challenges have not been performed.'

            # Inform the ACME CA server that challenges are available for validation.
            for achall, resp in zip(achalls, resps):
                self.acme.answer_challenge(achall.challb, resp)

            # Wait for authorizations to be checked.
            logger.info('Waiting for verification...')
            self._poll_authorizations(authzrs, max_retries, best_effort)

            # Keep validated authorizations only. If there is none, no certificate can be issued.
            authzrs_validated = [
                authzr for authzr in authzrs
                if authzr.body.status == messages.STATUS_VALID
            ]
            if not authzrs_validated:
                raise errors.AuthorizationError('All challenges have failed.')

            return authzrs_validated

        raise errors.Error(
            "An unexpected error occurred while handling the authorizations.")