Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNOW-1526335: support OCSP FAIL_OPEN #740

Merged
merged 8 commits into from
Oct 16, 2024
2 changes: 2 additions & 0 deletions include/snowflake/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ typedef enum SF_ATTRIBUTE {
SF_CON_MAX_VARCHAR_SIZE,
SF_CON_MAX_BINARY_SIZE,
SF_CON_MAX_VARIANT_SIZE,
SF_CON_OCSP_FAIL_OPEN,
SF_DIR_QUERY_URL,
SF_DIR_QUERY_URL_PARAM,
SF_DIR_QUERY_TOKEN,
Expand Down Expand Up @@ -318,6 +319,7 @@ typedef struct SF_CONNECT {
char *passcode;
sf_bool passcode_in_password;
sf_bool insecure_mode;
sf_bool ocsp_fail_open;
sf_bool autocommit;
char *timezone;
char *service_name;
Expand Down
7 changes: 5 additions & 2 deletions lib/chunk_downloader.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ sf_bool STDCALL download_chunk(char *url, SF_HEADER *headers,
NON_JSON_RESP* non_json_resp,
SF_ERROR_STRUCT *error,
sf_bool insecure_mode,
sf_bool fail_open,
const char *proxy,
const char *no_proxy,
int64 network_timeout,
Expand All @@ -218,7 +219,7 @@ sf_bool STDCALL download_chunk(char *url, SF_HEADER *headers,
if (!curl ||
!http_perform(curl, GET_REQUEST_TYPE, url, headers, NULL, chunk,
non_json_resp, network_timeout,
SF_BOOLEAN_TRUE, error, insecure_mode, 0,
SF_BOOLEAN_TRUE, error, insecure_mode, fail_open, 0,
0, retry_max_count, NULL, NULL, NULL, SF_BOOLEAN_FALSE,
proxy, no_proxy, SF_BOOLEAN_FALSE, SF_BOOLEAN_FALSE)) {
// Error set in perform function
Expand All @@ -240,6 +241,7 @@ SF_CHUNK_DOWNLOADER *STDCALL chunk_downloader_init(const char *qrmk,
uint64 fetch_slots,
SF_ERROR_STRUCT *sf_error,
sf_bool insecure_mode,
sf_bool fail_open,
NON_JSON_RESP* (*callback_create_resp)(void),
const char *proxy,
const char *no_proxy,
Expand Down Expand Up @@ -277,6 +279,7 @@ SF_CHUNK_DOWNLOADER *STDCALL chunk_downloader_init(const char *qrmk,
chunk_downloader->has_error = SF_BOOLEAN_FALSE;
chunk_downloader->sf_error = sf_error;
chunk_downloader->insecure_mode = insecure_mode;
chunk_downloader->fail_open = fail_open,
chunk_downloader->callback_create_resp = callback_create_resp;
chunk_downloader->proxy = NULL;
chunk_downloader->no_proxy = NULL;
Expand Down Expand Up @@ -484,7 +487,7 @@ static void * chunk_downloader_thread(void *downloader) {
non_json_resp = chunk_downloader->callback_create_resp();
}
if (!download_chunk(chunk_downloader->queue[index].url, chunk_downloader->chunk_headers,
chunk_ptr, non_json_resp, &err, chunk_downloader->insecure_mode,
chunk_ptr, non_json_resp, &err, chunk_downloader->insecure_mode, chunk_downloader->fail_open,
chunk_downloader->proxy, chunk_downloader->no_proxy,
chunk_downloader->network_timeout, chunk_downloader->retry_max_count)) {
_rwlock_wrlock(&chunk_downloader->attr_lock);
Expand Down
4 changes: 4 additions & 0 deletions lib/chunk_downloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ struct SF_CHUNK_DOWNLOADER {
// Snowflake connection insecure mode flag
sf_bool insecure_mode;

// OCSP fail open flag
sf_bool fail_open;

// callback function to create non-json response buffer. Json format will be used if this is set to NULL.
NON_JSON_RESP* (*callback_create_resp)(void);

Expand All @@ -82,6 +85,7 @@ SF_CHUNK_DOWNLOADER *STDCALL chunk_downloader_init(const char *qrmk,
uint64 fetch_slots,
SF_ERROR_STRUCT *sf_error,
sf_bool insecure_mode,
sf_bool fail_open,
NON_JSON_RESP* (*callback_create_resp)(void),
const char *proxy,
const char *no_proxy,
Expand Down
9 changes: 9 additions & 0 deletions lib/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ _snowflake_check_connection_parameters(SF_CONNECT *sf) {
log_debug("protocol: %s", sf->protocol);
log_debug("autocommit: %s", sf->autocommit ? "true": "false");
log_debug("insecure_mode: %s", sf->insecure_mode ? "true" : "false");
log_debug("ocsp_fail_open: %s", sf->ocsp_fail_open ? "true" : "false");
sfc-gh-dprzybysz marked this conversation as resolved.
Show resolved Hide resolved
log_debug("timezone: %s", sf->timezone);
log_debug("login_timeout: %d", sf->login_timeout);
log_debug("network_timeout: %d", sf->network_timeout);
Expand Down Expand Up @@ -677,6 +678,7 @@ SF_CONNECT *STDCALL snowflake_init() {
sf->passcode = NULL;
sf->passcode_in_password = SF_BOOLEAN_FALSE;
sf->insecure_mode = SF_BOOLEAN_FALSE;
sf->ocsp_fail_open = SF_BOOLEAN_TRUE;
sf->autocommit = SF_BOOLEAN_TRUE;
sf->qcc_disable = SF_BOOLEAN_FALSE;
sf->include_retry_reason = SF_BOOLEAN_TRUE;
Expand Down Expand Up @@ -1075,6 +1077,9 @@ SF_STATUS STDCALL snowflake_set_attribute(
case SF_CON_INSECURE_MODE:
sf->insecure_mode = value ? *((sf_bool *) value) : SF_BOOLEAN_FALSE;
break;
case SF_CON_OCSP_FAIL_OPEN:
sf->ocsp_fail_open = value ? *((sf_bool*)value) : SF_BOOLEAN_TRUE;
break;
case SF_CON_LOGIN_TIMEOUT:
sf->login_timeout = value ? *((int64 *) value) : SF_LOGIN_TIMEOUT;
break;
Expand Down Expand Up @@ -1211,6 +1216,9 @@ SF_STATUS STDCALL snowflake_get_attribute(
case SF_CON_INSECURE_MODE:
*value = &sf->insecure_mode;
break;
case SF_CON_OCSP_FAIL_OPEN:
*value = &sf->ocsp_fail_open;
break;
case SF_CON_LOGIN_TIMEOUT:
*value = &sf->login_timeout;
break;
Expand Down Expand Up @@ -2274,6 +2282,7 @@ SF_STATUS STDCALL _snowflake_execute_ex(SF_STMT *sfstmt,
4, // fetch slot
&sfstmt->error,
sfstmt->connection->insecure_mode,
sfstmt->connection->ocsp_fail_open,
callback_create_resp,
sfstmt->connection->proxy,
sfstmt->connection->no_proxy,
Expand Down
4 changes: 2 additions & 2 deletions lib/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ sf_bool STDCALL curl_post_call(SF_CONNECT *sf,
do {
if (!http_perform(curl, POST_REQUEST_TYPE, url, header, body, json, NULL,
retry_timeout, SF_BOOLEAN_FALSE, error,
sf->insecure_mode,
sf->insecure_mode, sf->ocsp_fail_open,
sf->retry_on_curle_couldnt_connect_count,
renew_timeout, retry_max_count, elapsed_time,
retried_count, is_renew, renew_injection,
Expand Down Expand Up @@ -484,7 +484,7 @@ sf_bool STDCALL curl_get_call(SF_CONNECT *sf,
do {
if (!http_perform(curl, GET_REQUEST_TYPE, url, header, NULL, json, NULL,
get_retry_timeout(sf), SF_BOOLEAN_FALSE, error,
sf->insecure_mode,
sf->insecure_mode, sf->ocsp_fail_open,
sf->retry_on_curle_couldnt_connect_count,
0, sf->retry_count, NULL, NULL, NULL, SF_BOOLEAN_FALSE,
sf->proxy, sf->no_proxy, SF_BOOLEAN_FALSE, SF_BOOLEAN_FALSE) ||
Expand Down
3 changes: 2 additions & 1 deletion lib/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ size_t json_resp_cb(char *data, size_t size, size_t nmemb, RAW_JSON_BUFFER *raw_
* at the end of the text buffer.
* @param error Reference to the Snowflake Error object to set an error if one occurs.
* @param insecure_mode Insecure mode disable OCSP check when set to true
* @param fail_open OCSP FAIL_OPEN mode when set to true
* @param retry_on_curle_couldnt_connect_count number of times retrying server connection on CURLE_COULDNT_CONNECT error
* @param renew_timeout For key pair authentication. Credentials could expire
* during the connection retry. Set renew timeout in such
Expand All @@ -445,7 +446,7 @@ size_t json_resp_cb(char *data, size_t size, size_t nmemb, RAW_JSON_BUFFER *raw_
*/
sf_bool STDCALL http_perform(CURL *curl, SF_REQUEST_TYPE request_type, char *url, SF_HEADER *header,
char *body, cJSON **json, NON_JSON_RESP* non_json_resp, int64 network_timeout, sf_bool chunk_downloader,
SF_ERROR_STRUCT *error, sf_bool insecure_mode,
SF_ERROR_STRUCT *error, sf_bool insecure_mode, sf_bool fail_open,
int8 retry_on_curle_couldnt_connect_count,
int64 renew_timeout, int8 retry_max_count,
int64 *elapsed_time, int8 *retried_count,
Expand Down
15 changes: 10 additions & 5 deletions lib/http_perform.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ sf_bool STDCALL http_perform(CURL *curl,
sf_bool chunk_downloader,
SF_ERROR_STRUCT *error,
sf_bool insecure_mode,
sf_bool fail_open,
int8 retry_on_curle_couldnt_connect_count,
int64 renew_timeout,
int8 retry_max_count,
Expand Down Expand Up @@ -320,8 +321,6 @@ sf_bool STDCALL http_perform(CURL *curl,
break;
}

#ifndef _WIN32
sfc-gh-ext-simba-hx marked this conversation as resolved.
Show resolved Hide resolved
#ifndef LIBSFCLI_FOR_XP
// If insecure mode is set to true, skip OCSP check not matter the value of SF_OCSP_CHECK (global OCSP variable)
sf_bool ocsp_check;
if (insecure_mode) {
Expand All @@ -335,8 +334,13 @@ sf_bool STDCALL http_perform(CURL *curl,
curl_easy_strerror(res));
break;
}
#endif
#endif

res = curl_easy_setopt(curl, CURLOPT_SSL_SF_OCSP_FAIL_OPEN, fail_open);
if (res != CURLE_OK) {
log_error("Unable to set OCSP FAIL_OPEN [%s]",
curl_easy_strerror(res));
break;
}

// Set chunk downloader specific stuff here
if (chunk_downloader) {
Expand Down Expand Up @@ -512,7 +516,8 @@ sf_bool STDCALL __wrap_http_perform(CURL *curl,
int64 network_timeout,
sf_bool chunk_downloader,
SF_ERROR_STRUCT *error,
sf_bool insecure_mode) {
sf_bool insecure_mode,
sf_bool fail_open) {
char *resp;
const char *request_type_str = request_type == POST_REQUEST_TYPE ? "POST" : "GET";

Expand Down
2 changes: 1 addition & 1 deletion lib/mock_http_perform.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extern "C" {
// This is just the mock interface
sf_bool STDCALL __wrap_http_perform(CURL *curl, SF_REQUEST_TYPE request_type, char *url, SF_HEADER *header,
char *body, cJSON **json, NON_JSON_RESP *non_json_resp, int64 network_timeout, sf_bool chunk_downloader,
SF_ERROR_STRUCT *error, sf_bool insecure_mode);
SF_ERROR_STRUCT *error, sf_bool insecure_mode, sf_bool fail_open);

#endif

Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ SET(TESTS_C
test_get_describe_only_query_result
test_stmt_functions
test_unit_mfa_auth
test_ocsp_fail_open
# FEATURE_INCREASED_MAX_LOB_SIZE_IN_MEMORY is internal switch
# will enable lob test when the change on server side will be published
# test_lob
Expand Down
113 changes: 113 additions & 0 deletions tests/test_ocsp_fail_open.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2024 Snowflake Computing, Inc. All rights reserved.
*/

#include "utils/test_setup.h"

void setCacheFile(char *cache_file)
{
#ifdef __linux__
char *home_env = getenv("HOME");
strcpy(cache_file, (home_env == NULL ? (char*)"/tmp" : home_env));
strcat(cache_file, "/.cache");
strcat(cache_file, "/snowflake");
strcat(cache_file, "/ocsp_response_cache.json");
#elif defined(__APPLE__)
char *home_env = getenv("HOME");
strcpy(cache_file, (home_env == NULL ? (char*)"/tmp" : home_env));
strcat(cache_file, "/Library");
strcat(cache_file, "/Caches");
strcat(cache_file, "/Snowflake");
strcat(cache_file, "/ocsp_response_cache.json");
#elif defined(_WIN32)
char *home_env = getenv("USERPROFILE");
if (home_env == NULL)
{
home_env = getenv("TMP");
}
if (home_env == NULL)
{
home_env = getenv("TEMP");
}
strcpy(cache_file, (home_env == NULL ? (char*)"c:\\temp" : home_env));
strcat(cache_file, "\\AppData");
strcat(cache_file, "\\Local");
strcat(cache_file, "\\Snowflake");
strcat(cache_file, "\\Caches");
strcat(cache_file, "\\ocsp_response_cache.json");
#endif
}

void test_fail_open_revoked(void **unused) {
char cache_file[4096];
setCacheFile(cache_file);
remove(cache_file);
sf_setenv("SF_OCSP_TEST_MODE", "true");
sf_setenv("SF_TEST_OCSP_CERT_STATUS_REVOKED", "true");
sf_setenv("SF_OCSP_RESPONSE_CACHE_SERVER_ENABLED", "false");

SF_CONNECT *sf = setup_snowflake_connection();

SF_STATUS ret = snowflake_connect(sf);
assert_int_not_equal(ret, SF_STATUS_SUCCESS); // must fail
SF_ERROR_STRUCT *sferr = snowflake_error(sf);
if (sferr->error_code != SF_STATUS_ERROR_CURL) {
dump_error(sferr);
}
assert_int_equal(sferr->error_code, SF_STATUS_ERROR_CURL);
snowflake_term(sf);
}

void test_fail_close_timeout(void** unused) {
char cache_file[4096];
setCacheFile(cache_file);
remove(cache_file);
sf_setenv("SF_OCSP_TEST_MODE", "true");
sf_setenv("SF_TEST_CA_OCSP_RESPONDER_CONNECTION_TIMEOUT", "5");
sf_setenv("SF_TEST_OCSP_URL", "http://httpbin.org/delay/10");
sf_setenv("SF_OCSP_RESPONSE_CACHE_SERVER_ENABLED", "false");

SF_CONNECT* sf = setup_snowflake_connection();
sf_bool value = SF_BOOLEAN_FALSE;
snowflake_set_attribute(sf, SF_CON_OCSP_FAIL_OPEN, &value);

SF_STATUS ret = snowflake_connect(sf);
assert_int_not_equal(ret, SF_STATUS_SUCCESS); // must fail
SF_ERROR_STRUCT* sferr = snowflake_error(sf);
if (sferr->error_code != SF_STATUS_ERROR_CURL) {
dump_error(sferr);
}
assert_int_equal(sferr->error_code, SF_STATUS_ERROR_CURL);
snowflake_term(sf);
}

void test_fail_open_timeout(void** unused) {
char cache_file[4096];
setCacheFile(cache_file);
remove(cache_file);
sf_setenv("SF_OCSP_TEST_MODE", "true");
sf_setenv("SF_TEST_CA_OCSP_RESPONDER_CONNECTION_TIMEOUT", "5");
sf_setenv("SF_TEST_OCSP_URL", "http://httpbin.org/delay/10");
sf_setenv("SF_OCSP_RESPONSE_CACHE_SERVER_ENABLED", "false");

SF_CONNECT* sf = setup_snowflake_connection();

SF_STATUS ret = snowflake_connect(sf);
if (ret != SF_STATUS_SUCCESS) {
dump_error(&(sf->error));
}
assert_int_equal(ret, SF_STATUS_SUCCESS);
snowflake_term(sf);
}

int main(void) {
initialize_test(SF_BOOLEAN_FALSE);
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_fail_open_revoked),
cmocka_unit_test(test_fail_close_timeout),
cmocka_unit_test(test_fail_open_timeout),
};
int ret = cmocka_run_group_tests(tests, NULL, NULL);
snowflake_global_term();
return ret;
}
1 change: 1 addition & 0 deletions tests/test_unit_set_get_attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ typedef struct sf_bool_attributes {
std::vector<sf_bool_attributes> boolAttributes = {
{ SF_CON_PASSCODE_IN_PASSWORD, true },
{ SF_CON_INSECURE_MODE, false },
{ SF_CON_OCSP_FAIL_OPEN, true },
{ SF_CON_AUTOCOMMIT, true },
};

Expand Down
Loading