mirror of
https://github.com/certbot/certbot.git
synced 2026-07-28 16:15:42 +02:00
I think this is causing failures in some of our tests so this PR reverts the change until we can fix the problem.
The 2nd commit is to keep the change using more idiomatic wording in the changelog for another change that got included in this PR.
* Revert "Use built-in support for OCSP in cryptography >= 2.5 (#6603)"
This reverts commit 2ddaf3db04.
* keep changelog correction
121 lines
4.1 KiB
Python
121 lines
4.1 KiB
Python
"""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
|
|
|