Exemple #1
0
def _request_ocsp(cert, issuer, uri):
    # https://cryptography.io/en/latest/x509/ocsp/#creating-requests
    builder = _OCSPRequestBuilder()
    # add_certificate returns a new instance
    builder = builder.add_certificate(cert, issuer, _SHA1())
    ocsp_request = builder.build()
    try:
        response = _post(uri,
                         data=ocsp_request.public_bytes(_Encoding.DER),
                         headers={'Content-Type': 'application/ocsp-request'},
                         timeout=5)
    except _RequestException:
        _LOGGER.debug("HTTP request failed")
        return None
    if response.status_code != 200:
        _LOGGER.debug("HTTP request returned %d", response.status_code)
        return None
    ocsp_response = _load_der_ocsp_response(response.content)
    _LOGGER.debug("OCSP response status: %r", ocsp_response.response_status)
    if ocsp_response.response_status != _OCSPResponseStatus.SUCCESSFUL:
        return None
    # RFC6960, Section 3.2, Number 1. Only relevant if we need to
    # talk to the responder directly.
    # Accessing response.serial_number raises if response status is not
    # SUCCESSFUL.
    if ocsp_response.serial_number != ocsp_request.serial_number:
        _LOGGER.debug("Response serial number does not match request")
        return None
    return ocsp_response
def _get_ocsp_response(cert, issuer, uri, ocsp_response_cache):
    ocsp_request = _build_ocsp_request(cert, issuer)
    try:
        ocsp_response = ocsp_response_cache[ocsp_request]
        _LOGGER.debug("Using cached OCSP response.")
    except KeyError:
        try:
            response = _post(
                uri,
                data=ocsp_request.public_bytes(_Encoding.DER),
                headers={"Content-Type": "application/ocsp-request"},
                timeout=5,
            )
        except _RequestException as exc:
            _LOGGER.debug("HTTP request failed: %s", exc)
            return None
        if response.status_code != 200:
            _LOGGER.debug("HTTP request returned %d", response.status_code)
            return None
        ocsp_response = _load_der_ocsp_response(response.content)
        _LOGGER.debug("OCSP response status: %r",
                      ocsp_response.response_status)
        if ocsp_response.response_status != _OCSPResponseStatus.SUCCESSFUL:
            return None
        # RFC6960, Section 3.2, Number 1. Only relevant if we need to
        # talk to the responder directly.
        # Accessing response.serial_number raises if response status is not
        # SUCCESSFUL.
        if ocsp_response.serial_number != ocsp_request.serial_number:
            _LOGGER.debug("Response serial number does not match request")
            return None
        if not _verify_response(issuer, ocsp_response):
            # The response failed verification.
            return None
        _LOGGER.debug("Caching OCSP response.")
        ocsp_response_cache[ocsp_request] = ocsp_response

    return ocsp_response
def _ocsp_callback(conn, ocsp_bytes, user_data):
    """Callback for use with OpenSSL.SSL.Context.set_ocsp_client_callback."""
    cert = conn.get_peer_certificate()
    if cert is None:
        _LOGGER.debug("No peer cert?")
        return 0
    cert = cert.to_cryptography()
    # Use the verified chain when available (pyopenssl>=20.0).
    if hasattr(conn, "get_verified_chain"):
        chain = conn.get_verified_chain()
        trusted_ca_certs = None
    else:
        chain = conn.get_peer_cert_chain()
        trusted_ca_certs = user_data.trusted_ca_certs
    if not chain:
        _LOGGER.debug("No peer cert chain?")
        return 0
    chain = [cer.to_cryptography() for cer in chain]
    issuer = _get_issuer_cert(cert, chain, trusted_ca_certs)
    must_staple = False
    # https://tools.ietf.org/html/rfc7633#section-4.2.3.1
    ext = _get_extension(cert, _TLSFeature)
    if ext is not None:
        for feature in ext.value:
            if feature == _TLSFeatureType.status_request:
                _LOGGER.debug("Peer presented a must-staple cert")
                must_staple = True
                break
    ocsp_response_cache = user_data.ocsp_response_cache

    # No stapled OCSP response
    if ocsp_bytes == b"":
        _LOGGER.debug("Peer did not staple an OCSP response")
        if must_staple:
            _LOGGER.debug(
                "Must-staple cert with no stapled response, hard fail.")
            return 0
        if not user_data.check_ocsp_endpoint:
            _LOGGER.debug("OCSP endpoint checking is disabled, soft fail.")
            # No stapled OCSP response, checking responder URI diabled, soft fail.
            return 1
        # https://tools.ietf.org/html/rfc6960#section-3.1
        ext = _get_extension(cert, _AuthorityInformationAccess)
        if ext is None:
            _LOGGER.debug("No authority access information, soft fail")
            # No stapled OCSP response, no responder URI, soft fail.
            return 1
        uris = [
            desc.access_location.value for desc in ext.value
            if desc.access_method == _AuthorityInformationAccessOID.OCSP
        ]
        if not uris:
            _LOGGER.debug("No OCSP URI, soft fail")
            # No responder URI, soft fail.
            return 1
        if issuer is None:
            _LOGGER.debug("No issuer cert?")
            return 0
        _LOGGER.debug("Requesting OCSP data")
        # When requesting data from an OCSP endpoint we only fail on
        # successful, valid responses with a certificate status of REVOKED.
        for uri in uris:
            _LOGGER.debug("Trying %s", uri)
            response = _get_ocsp_response(cert, issuer, uri,
                                          ocsp_response_cache)
            if response is None:
                # The endpoint didn't respond in time, or the response was
                # unsuccessful or didn't match the request, or the response
                # failed verification.
                continue
            _LOGGER.debug("OCSP cert status: %r", response.certificate_status)
            if response.certificate_status == _OCSPCertStatus.GOOD:
                return 1
            if response.certificate_status == _OCSPCertStatus.REVOKED:
                return 0
        # Soft fail if we couldn't get a definitive status.
        _LOGGER.debug("No definitive OCSP cert status, soft fail")
        return 1

    _LOGGER.debug("Peer stapled an OCSP response")
    if issuer is None:
        _LOGGER.debug("No issuer cert?")
        return 0
    response = _load_der_ocsp_response(ocsp_bytes)
    _LOGGER.debug("OCSP response status: %r", response.response_status)
    # This happens in _request_ocsp when there is no stapled response so
    # we know if we can compare serial numbers for the request and response.
    if response.response_status != _OCSPResponseStatus.SUCCESSFUL:
        return 0
    if not _verify_response(issuer, response):
        return 0
    # Cache the verified, stapled response.
    ocsp_response_cache[_build_ocsp_request(cert, issuer)] = response
    _LOGGER.debug("OCSP cert status: %r", response.certificate_status)
    if response.certificate_status == _OCSPCertStatus.REVOKED:
        return 0
    return 1