示例#1
0
def test_ocsp_single_endpoint():
    environ["SF_OCSP_ACTIVATE_NEW_ENDPOINT"] = "True"
    SnowflakeOCSP.clear_cache()
    ocsp = SFOCSP()
    ocsp.OCSP_CACHE_SERVER.NEW_DEFAULT_CACHE_SERVER_BASE_URL = "https://snowflake.preprod3.us-west-2-dev.external-zone.snowflakecomputing.com:8085/ocsp/"
    connection = _openssl_connect("snowflake.okta.com")
    assert ocsp.validate(
        "snowflake.okta.com",
        connection), "Failed to validate: {}".format("snowflake.okta.com")

    del environ["SF_OCSP_ACTIVATE_NEW_ENDPOINT"]
def test_ocsp_incomplete_chain():
    """Tests incomplete chained certificate."""
    incomplete_chain_cert = path.join(THIS_DIR, '../data', 'cert_tests',
                                      'incomplete-chain.pem')

    SnowflakeOCSP.clear_cache()  # reset the memory cache
    ocsp = SFOCSP()

    with pytest.raises(OperationalError) as ex:
        ocsp.validate_certfile(incomplete_chain_cert)
    assert 'CA certificate is NOT found' in ex.value.msg
def test_ocsp_revoked_certificate():
    """Tests revoked certificate."""
    revoked_cert = path.join(THIS_DIR, '../data', 'cert_tests',
                             'revoked_certs.pem')

    SnowflakeOCSP.clear_cache()  # reset the memory cache
    ocsp = SFOCSP()

    with pytest.raises(OperationalError) as ex:
        ocsp.validate_certfile(revoked_cert)
    assert ex.value.errno == ex.value.errno == ER_OCSP_RESPONSE_CERT_STATUS_REVOKED
示例#4
0
def test_ocsp_with_file_cache(tmpdir):
    """OCSP tests and the cache server and file."""
    tmp_dir = str(tmpdir.mkdir("ocsp_response_cache"))
    cache_file_name = path.join(tmp_dir, "cache_file.txt")

    # reset the memory cache
    SnowflakeOCSP.clear_cache()
    ocsp = SFOCSP(ocsp_response_cache_uri="file://" + cache_file_name)
    for url in TARGET_HOSTS:
        connection = _openssl_connect(url)
        assert ocsp.validate(url, connection), f"Failed to validate: {url}"
def test_ocsp():
    """
    OCSP tests
    """
    # reset the memory cache
    SnowflakeOCSP.clear_cache()
    ocsp = SFOCSP()
    for url in TARGET_HOSTS:
        connection = _openssl_connect(url)
        assert ocsp.validate(url, connection), \
            'Failed to validate: {}'.format(url)
示例#6
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 test_ocsp_bad_validity():
    SnowflakeOCSP.clear_cache()

    environ["SF_OCSP_TEST_MODE"] = "true"
    environ["SF_TEST_OCSP_FORCE_BAD_RESPONSE_VALIDITY"] = "true"

    OCSPCache.del_cache_file()

    ocsp = SFOCSP(use_ocsp_cache_server=False)
    connection = _openssl_connect("snowflake.okta.com")

    assert ocsp.validate("snowflake.okta.com", connection), "Connection should have passed with fail open"
    del environ['SF_OCSP_TEST_MODE']
    del environ['SF_TEST_OCSP_FORCE_BAD_RESPONSE_VALIDITY']
def _validate_certs_using_ocsp(url, cache_file_name):
    """Validate OCSP response. Deleting memory cache and file cache randomly."""
    logger = logging.getLogger('test')
    import time
    import random
    time.sleep(random.randint(0, 3))
    if random.random() < 0.2:
        logger.info('clearing up cache: OCSP_VALIDATION_CACHE')
        SnowflakeOCSP.clear_cache()
    if random.random() < 0.05:
        logger.info('deleting a cache file: %s', cache_file_name)
        SnowflakeOCSP.delete_cache_file()

    connection = _openssl_connect(url)
    ocsp = SFOCSP(ocsp_response_cache_uri='file://' + cache_file_name)
    ocsp.validate(url, connection)
def _store_cache_in_file(tmpdir, target_hosts=None, filename=None):
    if target_hosts is None:
        target_hosts = TARGET_HOSTS
    if filename is None:
        filename = path.join(str(tmpdir), 'cache_file.txt')

    # cache OCSP response
    SnowflakeOCSP.clear_cache()
    ocsp = SFOCSP(ocsp_response_cache_uri='file://' + filename,
                  use_ocsp_cache_server=False)
    for hostname in target_hosts:
        connection = _openssl_connect(hostname)
        assert ocsp.validate(hostname, connection), \
            'Failed to validate: {}'.format(hostname)
    assert path.exists(filename), "OCSP response cache file"
    return filename, target_hosts
示例#10
0
def extract_certificate_file(input_filename):
    ocsp = SnowflakeOCSPAsn1Crypto()
    cert_map = {}
    ocsp.read_cert_bundle(input_filename, cert_map)

    for cert in cert_map.values():
        print(f"serial #: {cert.serial_number}, name: {cert.subject.native}")
def _store_cache_in_file(tmpdir, target_hosts=None):
    if target_hosts is None:
        target_hosts = TARGET_HOSTS
    os.environ['SF_OCSP_RESPONSE_CACHE_DIR'] = str(tmpdir)
    OCSPCache.reset_cache_dir()
    filename = path.join(str(tmpdir), 'ocsp_response_cache.json')

    # cache OCSP response
    SnowflakeOCSP.clear_cache()
    ocsp = SFOCSP(ocsp_response_cache_uri='file://' + filename,
                  use_ocsp_cache_server=False)
    for hostname in target_hosts:
        connection = _openssl_connect(hostname)
        assert ocsp.validate(hostname, connection), \
            'Failed to validate: {}'.format(hostname)
    assert path.exists(filename), "OCSP response cache file"
    return filename, target_hosts
def dump_good_status(current_time, single_response):
    print("This Update: {}".format(single_response['this_update'].native))
    print("Next Update: {}".format(single_response['next_update'].native))
    this_update = (single_response['this_update'].native.replace(tzinfo=None) -
                   SFOCSP.ZERO_EPOCH).total_seconds()
    next_update = (single_response['next_update'].native.replace(tzinfo=None) -
                   SFOCSP.ZERO_EPOCH).total_seconds()

    tolerable_validity = SFOCSP._calculate_tolerable_validity(
        this_update, next_update)
    print("Tolerable Update: {}".format(
        strftime('%Y%m%d%H%M%SZ', gmtime(next_update + tolerable_validity))))
    if SFOCSP._is_validaity_range(current_time, this_update, next_update):
        print("OK")
    else:
        print(
            SFOCSP._validity_error_message(current_time, this_update,
                                           next_update))
def test_ocsp_fail_open_w_single_endpoint():
    SnowflakeOCSP.clear_cache()

    OCSPCache.del_cache_file()

    environ["SF_OCSP_TEST_MODE"] = "true"
    environ["SF_TEST_OCSP_URL"] = "http://httpbin.org/delay/10"
    environ["SF_TEST_CA_OCSP_RESPONDER_CONNECTION_TIMEOUT"] = "5"

    ocsp = SFOCSP(use_ocsp_cache_server=False)
    connection = _openssl_connect("snowflake.okta.com")

    try:
        assert ocsp.validate("snowflake.okta.com", connection), \
            'Failed to validate: {}'.format("snowflake.okta.com")
    finally:
        del environ['SF_OCSP_TEST_MODE']
        del environ['SF_TEST_OCSP_URL']
        del environ['SF_TEST_CA_OCSP_RESPONDER_CONNECTION_TIMEOUT']
def test_ocsp_wo_cache_file():
    """
    OCSP tests without File cache.
    NOTE: Use /etc as a readonly directory such that no cache file is used.
    """
    # reset the memory cache
    SnowflakeOCSP.clear_cache()
    OCSPCache.del_cache_file()
    environ['SF_OCSP_RESPONSE_CACHE_DIR'] = '/etc'
    OCSPCache.reset_cache_dir()

    try:
        ocsp = SFOCSP()
        for url in TARGET_HOSTS:
            connection = _openssl_connect(url)
            assert ocsp.validate(url, connection), \
                'Failed to validate: {}'.format(url)
    finally:
        del environ['SF_OCSP_RESPONSE_CACHE_DIR']
        OCSPCache.reset_cache_dir()
def test_ocsp_fail_close_w_single_endpoint():
    SnowflakeOCSP.clear_cache()

    environ["SF_OCSP_TEST_MODE"] = "true"
    environ["SF_TEST_OCSP_URL"] = "http://httpbin.org/delay/10"
    environ["SF_TEST_CA_OCSP_RESPONDER_CONNECTION_TIMEOUT"] = "5"

    OCSPCache.del_cache_file()

    ocsp = SFOCSP(use_ocsp_cache_server=False, use_fail_open=False)
    connection = _openssl_connect("snowflake.okta.com")

    with pytest.raises(RevocationCheckError) as ex:
        ocsp.validate("snowflake.okta.com", connection)

    try:
        assert ex.value.errno == ER_INVALID_OCSP_RESPONSE_CODE, "Connection should have failed"
    finally:
        del environ['SF_OCSP_TEST_MODE']
        del environ['SF_TEST_OCSP_URL']
        del environ['SF_TEST_CA_OCSP_RESPONDER_CONNECTION_TIMEOUT']
def dump_ocsp_response_cache(ocsp_response_cache_file, hostname_file,
                             cert_glob_pattern):
    """Dump OCSP response cache contents.

    Show the subject name as well if the subject is included in the certificate files.
    """
    sfocsp = SFOCSP()
    s_to_n = _fetch_certs(hostname_file)
    s_to_n1 = _serial_to_name(sfocsp, cert_glob_pattern)
    s_to_n.update(s_to_n1)

    SFOCSP.OCSP_CACHE.read_ocsp_response_cache_file(sfocsp,
                                                    ocsp_response_cache_file)

    def custom_key(k):
        # third element is Serial Number for the subject
        serial_number = core.Integer.load(k[2])
        return int(serial_number.native)

    output = {}
    ocsp_validation_cache = SFOCSP.OCSP_CACHE.CACHE
    for hkey in sorted(ocsp_validation_cache, key=custom_key):
        json_key = sfocsp.encode_cert_id_base64(hkey)

        serial_number = core.Integer.load(hkey[2]).native
        if int(serial_number) in s_to_n:
            name = s_to_n[int(serial_number)]
        else:
            name = "Unknown"
        output[json_key] = {
            "serial_number": format(serial_number, "d"),
            "name": name,
        }
        value = ocsp_validation_cache[hkey]
        cache = value[1]
        ocsp_response = ocsp.OCSPResponse.load(cache)
        basic_ocsp_response = ocsp_response.basic_ocsp_response

        tbs_response_data = basic_ocsp_response["tbs_response_data"]

        current_time = int(time())
        for single_response in tbs_response_data["responses"]:
            created_on = int(value[0])
            produce_at = tbs_response_data["produced_at"].native
            this_update = single_response["this_update"].native
            next_update = single_response["next_update"].native
            if current_time - OCSP_CACHE_SERVER_INTERVAL > created_on:
                raise_old_cache_exception(current_time, created_on, name,
                                          serial_number)

            next_update_utc = (next_update.replace(tzinfo=None) -
                               ZERO_EPOCH).total_seconds()
            this_update_utc = (this_update.replace(tzinfo=None) -
                               ZERO_EPOCH).total_seconds()

            if current_time > next_update_utc or current_time < this_update_utc:
                raise_outdated_validity_exception(current_time, name,
                                                  serial_number, this_update,
                                                  next_update)

            output[json_key]["created_on"] = strftime(
                SFOCSP.OUTPUT_TIMESTAMP_FORMAT, gmtime(created_on))
            output[json_key]["produce_at"] = str(produce_at)
            output[json_key]["this_update"] = str(this_update)
            output[json_key]["next_update"] = str(next_update)
    print(json.dumps(output))