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)
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
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)
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)
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")
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.")