Skip to content

Commit cccdbca

Browse files
Merge pull request #321 from donoghuc/pick-crl
(maint) Force full cert chain in curl when validating CRLs
2 parents 6b85e1b + e7338b3 commit cccdbca

File tree

5 files changed

+61
-2
lines changed

5 files changed

+61
-2
lines changed

curl/inc/leatherman/curl/client.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,13 @@ namespace leatherman { namespace curl {
350350
*/
351351
void set_client_cert(std::string const& client_cert, std::string const& client_key);
352352

353+
/**
354+
* Set client SSL certificate revocation list.
355+
* @param client_crl The path to the client's CRL file.
356+
* (see more: https://curl.haxx.se/libcurl/c/CURLOPT_CRLFILE.html)
357+
*/
358+
void set_client_crl(std::string const& client_crl);
359+
353360
/**
354361
* Set proxy information.
355362
* @param proxy String with following components [scheme]://[hostname]:[port].
@@ -394,6 +401,7 @@ namespace leatherman { namespace curl {
394401
std::string _ca_cert;
395402
std::string _client_cert;
396403
std::string _client_key;
404+
std::string _client_crl;
397405
std::string _proxy;
398406
long _client_protocols = CURLPROTO_ALL;
399407

@@ -414,6 +422,7 @@ namespace leatherman { namespace curl {
414422
LEATHERMAN_CURL_NO_EXPORT void set_write_callbacks(context& ctx, FILE* fp);
415423
LEATHERMAN_CURL_NO_EXPORT void set_client_info(context &ctx);
416424
LEATHERMAN_CURL_NO_EXPORT void set_ca_info(context& ctx);
425+
LEATHERMAN_CURL_NO_EXPORT void set_crl_info(context& ctx);
417426
LEATHERMAN_CURL_NO_EXPORT void set_client_protocols(context& ctx);
418427
LEATHERMAN_CURL_NO_EXPORT void set_proxy_info(context& ctx);
419428

curl/src/client.cc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ namespace leatherman { namespace curl {
229229
set_timeouts(ctx);
230230
set_write_callbacks(ctx);
231231
set_ca_info(ctx);
232+
set_crl_info(ctx);
232233
set_client_info(ctx);
233234
set_client_protocols(ctx);
234235
set_proxy_info(ctx);
@@ -274,6 +275,7 @@ namespace leatherman { namespace curl {
274275
set_timeouts(ctx);
275276
set_write_callbacks(ctx, temp_file.get_fp());
276277
set_ca_info(ctx);
278+
set_crl_info(ctx);
277279
set_client_info(ctx);
278280
set_client_protocols(ctx);
279281
set_proxy_info(ctx);
@@ -307,6 +309,11 @@ namespace leatherman { namespace curl {
307309
_ca_cert = cert_file;
308310
}
309311

312+
void client::set_client_crl(string const& client_crl)
313+
{
314+
_client_crl = client_crl;
315+
}
316+
310317
void client::set_proxy(string const& proxy)
311318
{
312319
_proxy = proxy;
@@ -437,6 +444,25 @@ namespace leatherman { namespace curl {
437444
curl_easy_setopt_maybe(ctx, CURLOPT_SSLKEY, _client_key.c_str());
438445
}
439446

447+
void client::set_crl_info(context& ctx){
448+
if (_client_crl == "") {
449+
return;
450+
}
451+
452+
curl_easy_setopt_maybe(ctx, CURLOPT_CRLFILE, _client_crl.c_str());
453+
454+
#ifdef CURLSSLOPT_NO_PARTIALCHAIN
455+
// Curl 7.68 has a bug where it defaults to passing
456+
// X509_V_FLAG_PARTIAL_CHAIN to openssl. This breaks CRL
457+
// chains, since the crl logic passes
458+
// X509_V_FLAG_CRL_CHECK_ALL, which requires a full chain.
459+
//
460+
// We disable partial chains explicitly here to work around
461+
// this.
462+
curl_easy_setopt_maybe(ctx, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_PARTIALCHAIN);
463+
#endif
464+
}
465+
440466
void client::set_proxy_info(context &ctx) {
441467
if (_proxy == "") {
442468
return;

curl/tests/client_test.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,21 @@ TEST_CASE("curl::client CA bundle and SSL setup") {
251251
REQUIRE(test_impl->proxy == "proxy");
252252
}
253253

254+
SECTION("CRL should be unspecified by default") {
255+
auto resp = test_client.get(test_request);
256+
CURL* const& handle = test_client.get_handle();
257+
auto test_impl = reinterpret_cast<curl_impl* const>(handle);
258+
REQUIRE(test_impl->client_crl == "");
259+
}
260+
261+
SECTION("cURL should receive the CRL specified in the request") {
262+
test_client.set_client_crl("/tmp/foo.pem");
263+
auto resp = test_client.get(test_request);
264+
CURL* const& handle = test_client.get_handle();
265+
auto test_impl = reinterpret_cast<curl_impl* const>(handle);
266+
REQUIRE(test_impl->client_crl == "/tmp/foo.pem");
267+
}
268+
254269
SECTION("Client cert name should be unspecified by default") {
255270
auto resp = test_client.get(test_request);
256271
CURL* const& handle = test_client.get_handle();

curl/tests/mock_curl.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,14 @@ CURLcode curl_easy_setopt(CURL *handle, CURLoption option, ...)
217217
}
218218
h->client_cert = va_arg(vl, char*);
219219
break;
220+
case CURLOPT_CRLFILE:
221+
// Set the mock curl SSL CRL to that which was passed in the request.
222+
if (h->test_failure_mode == curl_impl::error_mode::client_crl_error) {
223+
va_end(vl);
224+
return CURLE_OUT_OF_MEMORY;
225+
}
226+
h->client_crl = va_arg(vl, char*);
227+
break;
220228
case CURLOPT_PROXY:
221229
// Set the mock curl proxy to that which was passed in the request.
222230
if (h->test_failure_mode == curl_impl::error_mode::proxy_error) {

curl/tests/mock_curl.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ struct curl_impl
4040
ssl_cert_error,
4141
ssl_key_error,
4242
protocol_error,
43-
proxy_error
43+
proxy_error,
44+
client_crl_error
4445
};
4546

4647
error_mode test_failure_mode = error_mode::success;
@@ -57,7 +58,7 @@ struct curl_impl
5758
std::function<size_t(char*, size_t, size_t, void*)> read_function;
5859
void* read_data; // Where to read the request body from
5960

60-
std::string request_url, cookie, cacert, client_cert, client_key, proxy;
61+
std::string request_url, cookie, cacert, client_cert, client_key, client_crl, proxy;
6162
long protocols;
6263
long connect_timeout;
6364
http_method method = http_method::get;

0 commit comments

Comments
 (0)