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-932679: reduce delay on ocsp timeout #572

Merged
3 commits merged into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 78 additions & 17 deletions deps/curl-8.1.2/lib/vtls/sf_ocsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ typedef pthread_mutex_t SF_MUTEX_HANDLE;
#include "memdebug.h"
#include "sf_ocsp_telemetry_data.h"

#ifdef _WIN32
#include <Shellapi.h>
#define strcasecmp _stricmp
#endif

#define DEFAULT_OCSP_RESPONSE_CACHE_HOST "http://ocsp.snowflakecomputing.com"
#define OCSP_RESPONSE_CACHE_JSON "ocsp_response_cache.json"
#define OCSP_RESPONSE_CACHE_URL "%s/%s"
Expand Down Expand Up @@ -151,26 +156,30 @@ static OCSP_RESPONSE * findOCSPRespInMem(OCSP_CERTID *certid,
struct Curl_easy *data,
SF_OTD *ocsp_log_data);
static OCSP_RESPONSE * getOCSPResponse(X509 *cert, X509 *issuer,
struct connectdata *conn, struct Curl_easy *data,
SF_FAILOPEN_STATUS ocsp_fail_open, SF_OTD *ocsp_log);
struct connectdata *conn, struct Curl_easy *data,
SF_FAILOPEN_STATUS ocsp_fail_open, SF_OTD *ocsp_log,
char *last_timeout_host);
static CURLcode checkOneCert(X509 *cert, X509 *issuer,
STACK_OF(X509) *ch, X509_STORE *st,
SF_FAILOPEN_STATUS ocsp_fail_open,
struct connectdata *conn,
struct Curl_easy *data,
struct Curl_easy *data,
SF_OTD *ocsp_log_data,
bool oob_enable);
bool oob_enable,
char *last_timeout_host);
static char* ensureCacheDir(char* cache_dir, struct Curl_easy* data);
static char* mkdirIfNotExists(char* dir, struct Curl_easy* data);
static void writeOCSPCacheFile(struct Curl_easy* data);
static void readOCSPCacheFile(struct Curl_easy* data, SF_OTD *ocsp_log_data);
static OCSP_RESPONSE * queryResponderUsingCurl(char *url, OCSP_CERTID *certid,
char *hostname, OCSP_REQUEST *req,
struct Curl_easy *data,
SF_FAILOPEN_STATUS ocsp_fail_open,
SF_OTD *ocsp_log_data);
SF_FAILOPEN_STATUS ocsp_fail_open,
SF_OTD *ocsp_log_data,
char *last_timeout_host
);
static void initOCSPCacheServer(struct Curl_easy *data);
static void downloadOCSPCache(struct Curl_easy *data, SF_OTD *ocsp_log_data);
static void downloadOCSPCache(struct Curl_easy *data, SF_OTD *ocsp_log_data, char *last_timeout_host);
static char* encodeOCSPCertIDToBase64(OCSP_CERTID *certid, struct Curl_easy *data);
static char* encodeOCSPRequestToBase64(OCSP_REQUEST *reqp, struct Curl_easy *data);
static char* encodeOCSPResponseToBase64(OCSP_RESPONSE* resp, struct Curl_easy *data);
Expand Down Expand Up @@ -632,9 +641,10 @@ static char * getOCSPPostReqData(const char *hname,
}

static OCSP_RESPONSE * queryResponderUsingCurl(char *url, OCSP_CERTID *certid, char *hostname,
OCSP_REQUEST *req, struct Curl_easy *data,
SF_FAILOPEN_STATUS ocsp_fail_open,
SF_OTD *ocsp_log_data)
OCSP_REQUEST *req, struct Curl_easy *data,
SF_FAILOPEN_STATUS ocsp_fail_open,
SF_OTD *ocsp_log_data,
char *last_timeout_host)
{
unsigned char *ocsp_req_der = NULL;
int len_ocsp_req_der = 0;
Expand Down Expand Up @@ -753,6 +763,21 @@ static OCSP_RESPONSE * queryResponderUsingCurl(char *url, OCSP_CERTID *certid, c

infof(data, "OCSP fetch URL: %s", urlbuf);

int url_parse_result;

url_parse_result = OCSP_parse_url(
urlbuf, &host, &port, &path, &use_ssl);
if (!url_parse_result)
{
failf(data, "Invalid OCSP fetch URL: %s", urlbuf);
goto end;
}
if (strcasecmp(host, last_timeout_host) == 0)
{
// skip if we got timeout on the same host for previous certificate entry
goto end;
This conversation was marked as resolved.
Show resolved Hide resolved
}

/* allocate another curl handle for ocsp checking */
ocsp_curl = curl_easy_init();

Expand Down Expand Up @@ -805,6 +830,11 @@ static OCSP_RESPONSE * queryResponderUsingCurl(char *url, OCSP_CERTID *certid, c
curl_easy_strerror(res));
if (ocsp_retry_cnt == max_retry -1)
{
if (CURLE_OPERATION_TIMEDOUT == res)
{
failf(data, "Timeout reached when fetching OCSP response.");
strcpy(last_timeout_host, host);
}
snprintf(error_msg, OCSP_TELEMETRY_ERROR_MSG_MAX_LEN,
"OCSP checking curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
Expand Down Expand Up @@ -863,6 +893,9 @@ static OCSP_RESPONSE * queryResponderUsingCurl(char *url, OCSP_CERTID *certid, c
if (cert_id_b64) curl_free(cert_id_b64);
if (ocsp_req_der) OPENSSL_free(ocsp_req_der);
if (ocsp_curl) curl_easy_cleanup(ocsp_curl);
if (host) OPENSSL_free(host);
if (port) OPENSSL_free(port);
if (path) OPENSSL_free(path);

free(ocsp_response_raw.memory_ptr);

Expand Down Expand Up @@ -1403,7 +1436,7 @@ void updateCacheWithBulkEntries(cJSON* tmp_cache, struct Curl_easy *data)
* Download a OCSP cache content from OCSP cache server.
* @param data curl handle
*/
void downloadOCSPCache(struct Curl_easy *data, SF_OTD *ocsp_log_data)
void downloadOCSPCache(struct Curl_easy *data, SF_OTD *ocsp_log_data, char *last_timeout_host)
{
struct curl_memory_write ocsp_response_cache_json_mem;
CURL *curlh = NULL;
Expand All @@ -1417,6 +1450,22 @@ void downloadOCSPCache(struct Curl_easy *data, SF_OTD *ocsp_log_data)
ocsp_response_cache_json_mem.memory_ptr = malloc(1); /* will be grown as needed by the realloc above */
ocsp_response_cache_json_mem.size = 0;

int use_ssl;
int url_parse_result;
char *host = NULL, *port = NULL, *path = NULL;

url_parse_result = OCSP_parse_url(
ocsp_cache_server_url, &host, &port, &path, &use_ssl);
if (!url_parse_result)
{
failf(data, "Invalid OCSP cache server URL: %s", ocsp_cache_server_url);
goto end;
}
if (strcasecmp(host, last_timeout_host) == 0)
{
// skip if we got timeout on the same host for previous certificate entry
goto end;
}

/* allocate another curl handle for ocsp checking */
curlh = curl_easy_init();
Expand Down Expand Up @@ -1483,6 +1532,11 @@ void downloadOCSPCache(struct Curl_easy *data, SF_OTD *ocsp_log_data)
else
{
failf(data, "CURL Response code is not CURLE_OK.");
if (CURLE_OPERATION_TIMEDOUT == res)
{
failf(data, "Timeout reached when downloading OCSP cache.");
strcpy(last_timeout_host, host);
}
sf_otd_set_event_sub_type(OCSP_RESPONSE_CURL_FAILURE, ocsp_log_data);
goto end;
}
Expand All @@ -1504,6 +1558,9 @@ void downloadOCSPCache(struct Curl_easy *data, SF_OTD *ocsp_log_data)
if (tmp_cache) cJSON_Delete(tmp_cache);
if (curlh) curl_easy_cleanup(curlh);
if (headers) curl_slist_free_all(headers);
if (host) OPENSSL_free(host);
if (port) OPENSSL_free(port);
if (path) OPENSSL_free(path);

free(ocsp_response_cache_json_mem.memory_ptr);
}
Expand All @@ -1521,7 +1578,8 @@ OCSP_RESPONSE * getOCSPResponse(X509 *cert, X509 *issuer,
struct connectdata *conn,
struct Curl_easy *data,
SF_FAILOPEN_STATUS ocsp_fail_open,
SF_OTD *ocsp_log_data)
SF_OTD *ocsp_log_data,
char *last_timeout_host)
{
int i;
bool ocsp_url_missing = true;
Expand All @@ -1546,7 +1604,7 @@ OCSP_RESPONSE * getOCSPResponse(X509 *cert, X509 *issuer,
{
sf_otd_set_cache_enabled(1, ocsp_log_data);
/* download OCSP Cache from the server*/
downloadOCSPCache(data, ocsp_log_data);
downloadOCSPCache(data, ocsp_log_data, last_timeout_host);

/* try hitting cache again */
resp = findOCSPRespInMem(certid, data, ocsp_log_data);
Expand Down Expand Up @@ -1603,7 +1661,7 @@ OCSP_RESPONSE * getOCSPResponse(X509 *cert, X509 *issuer,
}

ocsp_url_invalid = false;
resp = queryResponderUsingCurl(ocsp_url, certid, conn->host.name, req, data, ocsp_fail_open, ocsp_log_data);
resp = queryResponderUsingCurl(ocsp_url, certid, conn->host.name, req, data, ocsp_fail_open, ocsp_log_data, last_timeout_host);
/* update local cache */
OPENSSL_free(host);
OPENSSL_free(path);
Expand Down Expand Up @@ -1690,7 +1748,8 @@ CURLcode checkOneCert(X509 *cert, X509 *issuer,
struct connectdata *conn,
struct Curl_easy *data,
SF_OTD *ocsp_log_data,
bool oob_enable)
bool oob_enable,
char *last_timeout_host)
{
CURLcode result;
SF_CERT_STATUS sf_cert_status = CERT_STATUS_INVALID;
Expand All @@ -1707,7 +1766,7 @@ CURLcode checkOneCert(X509 *cert, X509 *issuer,
{
OCSP_CERTID *certid = NULL;
int ocsp_status = 0;
resp = getOCSPResponse(cert, issuer, conn, data, ocsp_fail_open, ocsp_log_data);
resp = getOCSPResponse(cert, issuer, conn, data, ocsp_fail_open, ocsp_log_data, last_timeout_host);
infof(data, "Starting checking cert for %s...",
X509_NAME_oneline(X509_get_subject_name(cert), X509_cert_name,
MAX_CERT_NAME_LEN));
Expand Down Expand Up @@ -2298,6 +2357,8 @@ SF_PUBLIC(CURLcode) checkCertOCSP(struct connectdata *conn,
CURLcode rs = CURLE_OK;
char *ocsp_fail_open_env = getenv("SF_OCSP_FAIL_OPEN"); // Testing Only
SF_FAILOPEN_STATUS ocsp_fail_open = ENABLED;
char last_timeout_host[MAX_BUFFER_LENGTH];
last_timeout_host[0] = '\0';


// These end points are Out of band telemetry end points.
Expand Down Expand Up @@ -2351,7 +2412,7 @@ SF_PUBLIC(CURLcode) checkCertOCSP(struct connectdata *conn,
X509* cert = sk_X509_value(ch, i);
X509* issuer = sk_X509_value(ch, i+1);

rs = checkOneCert(cert, issuer, ch, st, ocsp_fail_open, conn, data, &ocsp_log_data, oob_enable);
rs = checkOneCert(cert, issuer, ch, st, ocsp_fail_open, conn, data, &ocsp_log_data, oob_enable, last_timeout_host);
if (rs != CURLE_OK)
{
goto end;
Expand Down
2 changes: 1 addition & 1 deletion scripts/build_curl.bat
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

@echo off
set CURL_SRC_VERSION=8.1.2
set CURL_BUILD_VERSION=3
set CURL_BUILD_VERSION=4
set CURL_VERSION=%CURL_SRC_VERSION%.%CURL_BUILD_VERSION%
call %*
goto :EOF
Expand Down
2 changes: 1 addition & 1 deletion scripts/build_curl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function usage() {
set -o pipefail

CURL_SRC_VERSION=8.1.2
CURL_BUILD_VERSION=3
CURL_BUILD_VERSION=4
CURL_DIR=$CURL_SRC_VERSION
CURL_VERSION=${CURL_DIR}.${CURL_BUILD_VERSION}

Expand Down
15 changes: 14 additions & 1 deletion tests/unit_test_ocsp/test_ocsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,25 @@ int main(int argc, char **argv)
unlink(cache_file);
checkCertificateRevocationStatus(host, port, cacert, NULL, NULL, 1, 1);

printf("===> Case 10: Delete file cache with invalid cache server URL to test OOB disabled\n");
printf("===> Case 10: Delete file cache with invalid cache server URL to test delay on failure and OOB disabled\n");
setenv("SF_OCSP_RESPONSE_CACHE_SERVER_ENABLED", "false", 1);
// use random IP address so it will get connection timeout
setenv("SF_OCSP_RESPONSE_CACHE_SERVER_URL", "http://10.24.123.89/ocsp_response_cache.json", 1);
unlink(cache_file);

time_t start_time = time(NULL);
checkCertificateRevocationStatus(host, port, cacert, NULL, NULL, 0, 1);
time_t end_time = time(NULL);
// should be around 5 seconds but no longer than 10.
if ((end_time - start_time) > 10)
{
fprintf(stderr, "Delay check FAILED! Delayed %d seconds\n", end_time - start_time);
exit(1);
}
else
{
fprintf(stderr, "Delay check OK\n");
}

unsetenv("SF_OCSP_RESPONSE_CACHE_SERVER_ENABLED");
unsetenv("SF_OCSP_RESPONSE_CACHE_SERVER_URL");
Expand Down