"""Tools for checking certificate revocation.""" import logging import re from subprocess import Popen, PIPE from certbot import errors from certbot import util logger = logging.getLogger(__name__) class RevocationChecker(object): "This class figures out OCSP checking on this system, and performs it." def __init__(self): self.broken = False if not util.exe_exists("openssl"): logger.info("openssl not installed, can't check revocation") self.broken = True return # New versions of openssl want -header var=val, old ones want -header var val test_host_format = Popen(["openssl", "ocsp", "-header", "var", "val"], stdout=PIPE, stderr=PIPE, universal_newlines=True) _out, err = test_host_format.communicate() if "Missing =" in err: self.host_args = lambda host: ["Host=" + host] else: self.host_args = lambda host: ["Host", host] def ocsp_revoked(self, cert_path, chain_path): """Get revoked status for a particular cert version. .. todo:: Make this a non-blocking call :param str cert_path: Path to certificate :param str chain_path: Path to intermediate cert :rtype bool or None: :returns: True if revoked; False if valid or the check failed """ if self.broken: return False url, host = self.determine_ocsp_server(cert_path) if not host: return False # jdkasten thanks "Bulletproof SSL and TLS - Ivan Ristic" for documenting this! cmd = ["openssl", "ocsp", "-no_nonce", "-issuer", chain_path, "-cert", cert_path, "-url", url, "-CAfile", chain_path, "-verify_other", chain_path, "-trust_other", "-header"] + self.host_args(host) logger.debug("Querying OCSP for %s", cert_path) logger.debug(" ".join(cmd)) try: output, err = util.run_script(cmd, log=logger.debug) except errors.SubprocessError: logger.info("OCSP check failed for %s (are we offline?)", cert_path) return False return _translate_ocsp_query(cert_path, output, err) def determine_ocsp_server(self, cert_path): """Extract the OCSP server host from a certificate. :param str cert_path: Path to the cert we're checking OCSP for :rtype tuple: :returns: (OCSP server URL or None, OCSP server host or None) """ try: url, _err = util.run_script( ["openssl", "x509", "-in", cert_path, "-noout", "-ocsp_uri"], log=logger.debug) except errors.SubprocessError: logger.info("Cannot extract OCSP URI from %s", cert_path) return None, None url = url.rstrip() host = url.partition("://")[2].rstrip("/") if host: return url, host else: logger.info("Cannot process OCSP host from URL (%s) in cert at %s", url, cert_path) return None, None def _translate_ocsp_query(cert_path, ocsp_output, ocsp_errors): """Parse openssl's weird output to work out what it means.""" states = ("good", "revoked", "unknown") patterns = [r"{0}: (WARNING.*)?{1}".format(cert_path, s) for s in states] good, revoked, unknown = (re.search(p, ocsp_output, flags=re.DOTALL) for p in patterns) warning = good.group(1) if good else None if (not "Response verify OK" in ocsp_errors) or (good and warning) or unknown: logger.info("Revocation status for %s is unknown", cert_path) logger.debug("Uncertain output:\n%s\nstderr:\n%s", ocsp_output, ocsp_errors) return False elif good and not warning: return False elif revoked: warning = revoked.group(1) if warning: logger.info("OCSP revocation warning: %s", warning) return True else: logger.warning("Unable to properly parse OCSP output: %s\nstderr:%s", ocsp_output, ocsp_errors) return False
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
__pycache__ | Folder | 0755 |
|
|
display | Folder | 0755 |
|
|
plugins | Folder | 0755 |
|
|
tests | Folder | 0755 |
|
|
__init__.py | File | 114 B | 0644 |
|
account.py | File | 13.98 KB | 0644 |
|
achallenges.py | File | 1.59 KB | 0644 |
|
auth_handler.py | File | 20.92 KB | 0644 |
|
cert_manager.py | File | 15.1 KB | 0644 |
|
cli.py | File | 71.49 KB | 0644 |
|
client.py | File | 28.72 KB | 0644 |
|
compat.py | File | 6.91 KB | 0644 |
|
configuration.py | File | 5.66 KB | 0644 |
|
constants.py | File | 6.54 KB | 0644 |
|
crypto_util.py | File | 15.29 KB | 0644 |
|
eff.py | File | 3.07 KB | 0644 |
|
error_handler.py | File | 5.81 KB | 0644 |
|
errors.py | File | 2.59 KB | 0644 |
|
hooks.py | File | 8.44 KB | 0644 |
|
interfaces.py | File | 22.02 KB | 0644 |
|
lock.py | File | 3.56 KB | 0644 |
|
log.py | File | 12.39 KB | 0644 |
|
main.py | File | 48.47 KB | 0644 |
|
notify.py | File | 1.04 KB | 0644 |
|
ocsp.py | File | 4.1 KB | 0644 |
|
renewal.py | File | 20.91 KB | 0644 |
|
reporter.py | File | 3.46 KB | 0644 |
|
reverter.py | File | 23.32 KB | 0644 |
|
ssl-dhparams.pem | File | 424 B | 0644 |
|
storage.py | File | 44.91 KB | 0644 |
|
updater.py | File | 3.86 KB | 0644 |
|
util.py | File | 20.35 KB | 0644 |
|