def _read_ocsp_response_cache(ocsp_response_cache_uri):
    """
    Read OCSP Response cache data from the URI, which is very likely a file.

    :param ocsp_response_cache_uri: OCSP response cache data from URI
    """
    if ocsp_response_cache_uri is not None:
        try:
            with OCSP_VALIDATION_CACHE_LOCK:
                parsed_url = urlsplit(ocsp_response_cache_uri)
                if parsed_url.scheme == 'file':
                    read_ocsp_response_cache_file(
                        path.join(parsed_url.netloc, parsed_url.path),
                        OCSP_VALIDATION_CACHE)
                else:
                    raise Exception("Unsupported OCSP URI: %s",
                                    ocsp_response_cache_uri)
        except Exception as e:
            logger.debug(
                "Failed to read OCSP response cache file %s: %s, "
                "No worry. It will validate with OCSP server. "
                "Ignoring...",
                ocsp_response_cache_uri,
                e,
                exc_info=True)
def _reset_ocsp_dynamic_cache_server_url():
    """
    Reset OCSP dynamic cache server url pattern.

    This is used only when OCSP cache server is updated.
    """
    global SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN
    global SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN_LOCK

    with SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN_LOCK:
        if SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN is None and \
                not SF_OCSP_RESPONSE_CACHE_SERVER_URL.startswith(
                    DEFAULT_OCSP_RESPONSE_CACHE_SERVER_URL):
            # only if custom OCSP cache server is used.
            parsed_url = urlsplit(SF_OCSP_RESPONSE_CACHE_SERVER_URL)
            if parsed_url.port:
                SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN = \
                    u"{0}://{1}:{2}/retry/".format(
                        parsed_url.scheme, parsed_url.hostname,
                        parsed_url.port) + u"{0}/{1}"
            else:
                SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN = \
                    u"{0}://{1}/retry/".format(
                        parsed_url.scheme, parsed_url.hostname) + u"{0}/{1}"
        logger.debug("OCSP dynamic cache server URL pattern: %s",
                     SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN)
예제 #3
0
    def _reset_ocsp_dynamic_cache_server_url():
        """
        Reset OCSP dynamic cache server url pattern.

        This is used only when OCSP cache server is updated.
        """
        with OCSPCache.RETRY_URL_PATTERN_LOCK:
            if OCSPCache.RETRY_URL_PATTERN is None:
                if not OCSPCache.CACHE_SERVER_URL.startswith(
                        OCSPCache.DEFAULT_CACHE_SERVER_URL):
                    # only if custom OCSP cache server is used.
                    parsed_url = urlsplit(OCSPCache.CACHE_SERVER_URL)
                    if parsed_url.port:
                        OCSPCache.RETRY_URL_PATTERN = \
                            u"{0}://{1}:{2}/retry/".format(
                                parsed_url.scheme, parsed_url.hostname,
                                parsed_url.port) + u"{0}/{1}"
                    else:
                        OCSPCache.RETRY_URL_PATTERN = \
                            u"{0}://{1}/retry/".format(
                                parsed_url.scheme, parsed_url.hostname) + u"{0}/{1}"
                elif OCSPCache.ACTIVATE_SSD:
                    OCSPCache.RETRY_URL_PATTERN = OCSPCache.DEFAULT_RETRY_URL

            logger.debug("OCSP dynamic cache server URL pattern: %s",
                         OCSPCache.RETRY_URL_PATTERN)
예제 #4
0
 def update_ocsp_response_cache_file(ocsp, ocsp_response_cache_uri):
     """
     Updates OCSP Response Cache
     """
     if ocsp_response_cache_uri is not None:
         try:
             parsed_url = urlsplit(ocsp_response_cache_uri)
             if parsed_url.scheme == 'file':
                 filename = path.join(parsed_url.netloc, parsed_url.path)
                 lock_dir = filename + '.lck'
                 for _ in range(100):
                     # wait until the lck file has been removed
                     # or up to 1 second (0.01 x 100)
                     if OCSPCache.lock_cache_file(lock_dir):
                         break
                     time.sleep(0.01)
                 try:
                     OCSPCache.write_ocsp_response_cache_file(
                         ocsp, filename)
                 finally:
                     OCSPCache.unlock_cache_file(lock_dir)
             else:
                 logger.debug(
                     "No OCSP response cache file is written, because the "
                     "given URI is not a file: %s. Ignoring...",
                     ocsp_response_cache_uri)
         except Exception as e:
             logger.debug(
                 "Failed to write OCSP response cache "
                 "file. file: %s, error: %s, Ignoring...",
                 ocsp_response_cache_uri,
                 e,
                 exc_info=True)
예제 #5
0
 def generate_get_url(ocsp_url, b64data):
     if OCSPCache.RETRY_URL_PATTERN:
         parsed_url = urlsplit(ocsp_url)
         target_url = OCSPCache.RETRY_URL_PATTERN.format(
             parsed_url.hostname, b64data)
     else:
         target_url = u"{0}/{1}".format(ocsp_url, b64data)
     return target_url
예제 #6
0
 def delete_cache_file():
     """
     Delete the cache file. Used by tests only
     """
     parsed_url = urlsplit(OCSPCache.OCSP_RESPONSE_CACHE_URI)
     fname = path.join(parsed_url.netloc, parsed_url.path)
     OCSPCache.lock_cache_file(fname)
     try:
         os.unlink(fname)
     finally:
         OCSPCache.unlock_cache_file(fname)
예제 #7
0
def dump_ocsp_response(urls, output_filename):
    ocsp = SFOCSP()
    for url in urls:
        if not url.startswith('http'):
            url = 'https://' + url
        parsed_url = urlsplit(url)
        hostname = parsed_url.hostname
        port = parsed_url.port or 443
        connection = _openssl_connect(hostname, port)
        cert_data = ocsp.extract_certificate_chain(connection)
        current_time = int(time.time())
        print("Target URL: {0}".format(url))
        print("Current Time: {0}".format(
            strftime('%Y%m%d%H%M%SZ', gmtime(current_time))))
        for issuer, subject in cert_data:
            cert_id, _ = ocsp.create_ocsp_request(issuer, subject)
            _, _, _, cert_id, ocsp_response_der = \
                ocsp.validate_by_direct_connection(issuer, subject)
            ocsp_response = asn1crypto_ocsp.OCSPResponse.load(ocsp_response_der)
            print(
                "------------------------------------------------------------")
            print("Subject Name: {0}".format(subject.subject.native))
            print("Issuer Name: {0}".format(issuer.subject.native))
            print("OCSP URI: {0}".format(subject.ocsp_urls))
            print("CRL URI: {0}".format(
                subject.crl_distribution_points[0].native))
            print("Issuer Name Hash: {0}".format(subject.issuer.sha1))
            print("Issuer Key Hash: {0}".format(issuer.public_key.sha1))
            print("Serial Number: {0}".format(subject.serial_number))
            print("Response Status: {0}".format(
                ocsp_response['response_status'].native))
            basic_ocsp_response = ocsp_response.basic_ocsp_response
            tbs_response_data = basic_ocsp_response['tbs_response_data']
            print("Responder ID: {0}".format(
                tbs_response_data['responder_id'].name))
            current_time = int(time.time())
            for single_response in tbs_response_data['responses']:
                cert_status = single_response['cert_status'].name
                if cert_status == 'good':
                    dump_good_status(current_time, single_response)
                elif cert_status == 'revoked':
                    dump_revoked_status(single_response)
                else:
                    print("Unknown")
            print('')

        if output_filename:
            SFOCSP.OCSP_CACHE.write_ocsp_response_cache_file(
                ocsp,
                output_filename)
    return SFOCSP.OCSP_CACHE.CACHE
def _fetch_ocsp_response(req, cert, do_retry=True):
    """
    Fetch OCSP response using OCSPRequest
    """
    global SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN
    max_retry = 100 if do_retry else 1
    data = req.dump()  # convert to DER
    b64data = b64encode(data).decode('ascii')

    urls = cert.ocsp_urls
    ocsp_url = urls[0]
    if SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN:
        parsed_url = urlsplit(ocsp_url)
        target_url = SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN.format(
            parsed_url.hostname, b64data
        )
    else:
        target_url = u"{0}/{1}".format(ocsp_url, b64data)
    ret = None
    logger.debug('url: %s', target_url)
    with requests.Session() as session:
        session.mount('http://', adapters.HTTPAdapter(max_retries=5))
        session.mount('https://', adapters.HTTPAdapter(max_retries=5))
        global PROXIES
        for attempt in range(max_retry):
            response = session.get(
                target_url,
                proxies=PROXIES,
                timeout=30)
            if response.status_code == OK:
                logger.debug(
                    "OCSP response was successfully returned from OCSP server.")
                ret = response.content
                break
            elif max_retry > 1:
                wait_time = 2 ** attempt
                wait_time = 16 if wait_time > 16 else wait_time
                logger.debug("OCSP server returned %s. Retrying in %s(s)",
                             response.status_code, wait_time)
                time.sleep(wait_time)
        else:
            logger.error("Failed to get OCSP response after %s attempt.",
                         max_retry)
            raise OperationalError(
                msg="Failed to get OCSP response after {) attempt.".format(
                    max_retry),
                errno=ER_INVALID_OCSP_RESPONSE
            )

    return ret
def _fetch_ocsp_response(req, cert, do_retry=True):
    """
    Fetch OCSP response using OCSPRequest
    """
    urls = cert.ocsp_urls
    parsed_url = urlsplit(urls[0])  # urls is guaranteed to have OCSP URL

    max_retry = 100 if do_retry else 1
    data = req.dump()  # convert to DER
    headers = {
        'Content-Type': 'application/ocsp-request',
        'Content-Length': '{0}'.format(len(data)),
        'Host': parsed_url.hostname,
    }
    ret = None
    with requests.Session() as session:
        session.mount('http://', adapters.HTTPAdapter(max_retries=5))
        session.mount('https://', adapters.HTTPAdapter(max_retries=5))
        global PROXIES
        for attempt in range(max_retry):
            response = session.post(urls[0],
                                    headers=headers,
                                    proxies=PROXIES,
                                    data=data,
                                    timeout=30)
            if response.status_code == OK:
                logger.debug("OCSP response was successfully returned from "
                             "OCSP server.")
                ret = response.content
                break
            elif max_retry > 1:
                wait_time = 2**attempt
                wait_time = 16 if wait_time > 16 else wait_time
                logger.debug("OCSP server returned %s. Retrying in %s(s)",
                             response.status_code, wait_time)
                time.sleep(wait_time)
        else:
            logger.error("Failed to get OCSP response after %s attempt.",
                         max_retry)
            raise OperationalError(
                msg="Failed to get OCSP response after {) attempt.".format(
                    max_retry),
                errno=ER_INVALID_OCSP_RESPONSE)

    return ret
예제 #10
0
def main():
    from OpenSSL.crypto import dump_certificate, FILETYPE_PEM

    def help():
        print("Export certificate on the URL")
        print("""
    Usage: {0}  <url>
    """.format(path.basename(sys.argv[0])))
        sys.exit(2)

    if len(sys.argv) < 2:
        help()

    input_url = sys.argv[1]
    parsed_url = urlsplit(input_url)
    connection = _openssl_connect(parsed_url.hostname, parsed_url.port or 443)
    for cert_openssl in connection.get_peer_cert_chain():
        cert_pem = dump_certificate(FILETYPE_PEM, cert_openssl)
        print(cert_pem.decode('utf-8'))
def update_ocsp_response_cache_file(ocsp_response_cache_uri):
    """
    Updates OCSP Response Cache
    """
    lock_dir = None
    if ocsp_response_cache_uri is not None:
        try:
            parsed_url = urlsplit(ocsp_response_cache_uri)
            if parsed_url.scheme == 'file':
                filename = path.join(parsed_url.netloc, parsed_url.path)
                lock_dir = filename + '.lck'
                for _ in range(100):
                    # wait until the lck file has been removed
                    # or up to 1 second (0.01 x 100)
                    if _lock_cache_file(lock_dir):
                        break
                    time.sleep(0.01)
                try:
                    write_ocsp_response_cache_file(filename,
                                                   OCSP_VALIDATION_CACHE)
                finally:
                    _unlock_cache_file(lock_dir)
                    lock_dir = None
            else:
                logger.debug(
                    "No OCSP response cache file is written, because the "
                    "given URI is not a file: %s. Ignoring...",
                    ocsp_response_cache_uri)
        except Exception as e:
            logger.debug(
                "Failed to write OCSP response cache "
                "file. file: %s, error: %s, Ignoring...",
                ocsp_response_cache_uri,
                e,
                exc_info=True)

    if lock_dir is not None and os.path.exists(lock_dir):
        # final attempt to delete the lock directory
        if not _unlock_cache_file(lock_dir):
            logger.debug(
                "Failed to remove OCSP response cache lock directory. "
                "Ignoring...")
예제 #12
0
 def read_file(ocsp):
     """
     Read OCSP Response cache data from the URI, which is very likely a file.
     """
     try:
         parsed_url = urlsplit(OCSPCache.OCSP_RESPONSE_CACHE_URI)
         if parsed_url.scheme == 'file':
             OCSPCache.read_ocsp_response_cache_file(
                 ocsp, path.join(parsed_url.netloc, parsed_url.path))
         else:
             raise Exception("Unsupported OCSP URI: %s",
                             OCSPCache.OCSP_RESPONSE_CACHE_URI)
     except Exception as e:
         logger.debug(
             "Failed to read OCSP response cache file %s: %s, "
             "No worry. It will validate with OCSP server. "
             "Ignoring...",
             OCSPCache.OCSP_RESPONSE_CACHE_URI,
             e,
             exc_info=True)
예제 #13
0
def dump_ocsp_response(urls, output_filename):
    for url in urls:
        parsed_url = urlsplit(url)
        hostname = parsed_url.hostname
        port = parsed_url.port or 443
        connection = _openssl_connect(hostname, port)
        cert_data = _extract_certificate_chain(connection)
        current_time = int(time.time())
        print("Target URL: {0}".format(url))
        print("Current Time: {0}".format(
            strftime('%Y%m%d%H%M%SZ', gmtime(current_time))))
        for issuer, subject in cert_data:
            cert_id, _ = _create_ocsp_request(issuer, subject)
            _, cert_id, ocsp_response_der = validate_by_direct_connection(
                issuer, subject)
            ocsp_response = ocsp.OCSPResponse.load(ocsp_response_der)
            print(
                "------------------------------------------------------------")
            print("Issuer Name: {0}".format(issuer.subject.native))
            print("Subject Name: {0}".format(subject.subject.native))
            print("OCSP URI: {0}".format(subject.ocsp_urls))
            print("CRL URI: {0}".format(
                subject.crl_distribution_points[0].native))
            print("Issuer Name Hash: {0}".format(subject.issuer.sha1))
            print("Issuer Key Hash: {0}".format(issuer.public_key.sha1))
            print("Serial Number: {0}".format(subject.serial_number))
            print("Response Status: {0}".format(
                ocsp_response['response_status'].native))
            basic_ocsp_response = ocsp_response.basic_ocsp_response
            tbs_response_data = basic_ocsp_response['tbs_response_data']
            print("Responder ID: {0}".format(
                tbs_response_data['responder_id'].name))
            current_time = int(time.time())
            for single_response in tbs_response_data['responses']:
                cert_status = single_response['cert_status'].name
                if cert_status == 'good':
                    print("This Update: {0}".format(
                        single_response['this_update'].native))
                    print("Next Update: {0}".format(
                        single_response['next_update'].native))
                    this_update = (
                            single_response['this_update'].native.replace(
                                tzinfo=None) - ZERO_EPOCH).total_seconds()
                    next_update = (
                            single_response['next_update'].native.replace(
                                tzinfo=None) - ZERO_EPOCH).total_seconds()

                    tolerable_validity = _calculate_tolerable_validity(
                        this_update,
                        next_update)
                    print("Tolerable Update: {0}".format(
                        strftime('%Y%m%d%H%M%SZ', gmtime(
                            next_update + tolerable_validity))
                    ))
                    if _is_validaity_range(current_time, this_update,
                                           next_update):
                        print("OK")
                    else:
                        print(_validity_error_message(
                            current_time, this_update, next_update))
                elif cert_status == 'revoked':
                    revoked_info = single_response['cert_status']
                    revocation_time = revoked_info.native['revocation_time']
                    revocation_reason = revoked_info.native['revocation_reason']
                    print("Revoked Time: {0}".format(
                        revocation_time.strftime(OUTPUT_TIMESTAMP_FORMAT)))
                    print("Revoked Reason: {0}".format(revocation_reason))
                    print("Revoked")
                else:
                    print("Unknown")
            print('')

        if output_filename:
            write_ocsp_response_cache_file(
                output_filename, OCSP_VALIDATION_CACHE)
    return OCSP_VALIDATION_CACHE