1#ifndef CPR_SSLOPTIONS_H
2#define CPR_SSLOPTIONS_H
3
4#include <memory>
5#include <string>
6#include <vector>
7
8#include <cpr/filesystem.h>
9#include <curl/curl.h>
10
11#include "cpr/util.h"
12#include <utility>
13
14#define __LIBCURL_VERSION_GTE(major, minor) ((LIBCURL_VERSION_MAJOR > (major)) || ((LIBCURL_VERSION_MAJOR == (major)) && (LIBCURL_VERSION_MINOR >= (minor))))
15#define __LIBCURL_VERSION_LT(major, minor) ((LIBCURL_VERSION_MAJOR < (major)) || ((LIBCURL_VERSION_MAJOR == (major)) && (LIBCURL_VERSION_MINOR < (minor))))
16
17#ifndef SUPPORT_ALPN
18#define SUPPORT_ALPN __LIBCURL_VERSION_GTE(7, 36)
19#endif
20#ifndef SUPPORT_NPN
21#define SUPPORT_NPN __LIBCURL_VERSION_GTE(7, 36) && __LIBCURL_VERSION_LT(7, 86)
22#endif
23
24#ifndef SUPPORT_SSLv2
25#define SUPPORT_SSLv2 __LIBCURL_VERSION_LT(7, 19)
26#endif
27#ifndef SUPPORT_SSLv3
28#define SUPPORT_SSLv3 __LIBCURL_VERSION_LT(7, 39)
29#endif
30#ifndef SUPPORT_TLSv1_0
31#define SUPPORT_TLSv1_0 __LIBCURL_VERSION_GTE(7, 34)
32#endif
33#ifndef SUPPORT_TLSv1_1
34#define SUPPORT_TLSv1_1 __LIBCURL_VERSION_GTE(7, 34)
35#endif
36#ifndef SUPPORT_TLSv1_2
37#define SUPPORT_TLSv1_2 __LIBCURL_VERSION_GTE(7, 34)
38#endif
39#ifndef SUPPORT_TLSv1_3
40#define SUPPORT_TLSv1_3 __LIBCURL_VERSION_GTE(7, 52)
41#endif
42#ifndef SUPPORT_MAX_TLS_VERSION
43#define SUPPORT_MAX_TLS_VERSION __LIBCURL_VERSION_GTE(7, 54)
44#endif
45#ifndef SUPPORT_MAX_TLSv1_1
46#define SUPPORT_MAX_TLSv1_1 __LIBCURL_VERSION_GTE(7, 54)
47#endif
48#ifndef SUPPORT_MAX_TLSv1_2
49#define SUPPORT_MAX_TLSv1_2 __LIBCURL_VERSION_GTE(7, 54)
50#endif
51#ifndef SUPPORT_MAX_TLSv1_3
52#define SUPPORT_MAX_TLSv1_3 __LIBCURL_VERSION_GTE(7, 54)
53#endif
54#ifndef SUPPORT_TLSv13_CIPHERS
55#define SUPPORT_TLSv13_CIPHERS __LIBCURL_VERSION_GTE(7, 61)
56#endif
57#ifndef SUPPORT_SESSIONID_CACHE
58#define SUPPORT_SESSIONID_CACHE __LIBCURL_VERSION_GTE(7, 16)
59#endif
60#ifndef SUPPORT_SSL_FALSESTART
61#define SUPPORT_SSL_FALSESTART __LIBCURL_VERSION_GTE(7, 42)
62#endif
63#ifndef SUPPORT_SSL_NO_REVOKE
64#define SUPPORT_SSL_NO_REVOKE __LIBCURL_VERSION_GTE(7, 44)
65#endif
66#ifndef SUPPORT_CURLOPT_SSLKEY_BLOB
67#define SUPPORT_CURLOPT_SSLKEY_BLOB __LIBCURL_VERSION_GTE(7, 71)
68#endif
69#ifndef SUPPORT_CURLOPT_SSL_CTX_FUNCTION
70#define SUPPORT_CURLOPT_SSL_CTX_FUNCTION __LIBCURL_VERSION_GTE(7, 11)
71#endif
72
73namespace cpr {
74
75class VerifySsl {
76 public:
77 VerifySsl() = default;
78 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
79 VerifySsl(bool p_verify) : verify(p_verify) {}
80
81 explicit operator bool() const {
82 return verify;
83 }
84
85 bool verify = true;
86};
87
88namespace ssl {
89
90// set SSL client certificate
91class CertFile {
92 public:
93 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
94 CertFile(fs::path&& p_filename) : filename(std::move(p_filename)) {}
95
96 virtual ~CertFile() = default;
97
98 const fs::path filename;
99
100 virtual const char* GetCertType() const {
101 return "PEM";
102 }
103};
104
105using PemCert = CertFile;
106
107class DerCert : public CertFile {
108 public:
109 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
110 DerCert(fs::path&& p_filename) : CertFile(std::move(p_filename)) {}
111
112 virtual ~DerCert() = default;
113
114 const char* GetCertType() const override {
115 return "DER";
116 }
117};
118
119// specify private keyfile for TLS and SSL client cert
120class KeyFile {
121 public:
122 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
123 KeyFile(fs::path&& p_filename) : filename(std::move(p_filename)) {}
124
125 template <typename FileType, typename PassType>
126 KeyFile(FileType&& p_filename, PassType p_password) : filename(std::forward<FileType>(p_filename)), password(std::move(p_password)) {}
127
128 virtual ~KeyFile() {
129 util::secureStringClear(s&: password);
130 }
131
132 fs::path filename;
133 std::string password;
134
135 virtual const char* GetKeyType() const {
136 return "PEM";
137 }
138};
139
140#if SUPPORT_CURLOPT_SSLKEY_BLOB
141class KeyBlob {
142 public:
143 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
144 KeyBlob(std::string&& p_blob) : blob(std::move(p_blob)) {}
145
146 template <typename BlobType, typename PassType>
147 KeyBlob(BlobType&& p_blob, PassType p_password) : blob(std::forward<BlobType>(p_blob)), password(std::move(p_password)) {}
148
149 virtual ~KeyBlob() {
150 util::secureStringClear(s&: password);
151 }
152
153 std::string blob;
154 std::string password;
155
156 virtual const char* GetKeyType() const {
157 return "PEM";
158 }
159};
160#endif
161
162using PemKey = KeyFile;
163
164class DerKey : public KeyFile {
165 public:
166 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
167 DerKey(fs::path&& p_filename) : KeyFile(std::move(p_filename)) {}
168
169 template <typename FileType, typename PassType>
170 DerKey(FileType&& p_filename, PassType p_password) : KeyFile(std::forward<FileType>(p_filename), std::move(p_password)) {}
171
172 virtual ~DerKey() = default;
173
174 const char* GetKeyType() const override {
175 return "DER";
176 }
177};
178
179class PinnedPublicKey {
180 public:
181 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
182 PinnedPublicKey(std::string&& p_pinned_public_key) : pinned_public_key(std::move(p_pinned_public_key)) {}
183
184 const std::string pinned_public_key;
185};
186
187#if SUPPORT_ALPN
188// This option enables/disables ALPN in the SSL handshake (if the SSL backend libcurl is built to
189// use supports it), which can be used to negotiate http2.
190class ALPN {
191 public:
192 ALPN() = default;
193 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
194 ALPN(bool p_enabled) : enabled(p_enabled) {}
195
196 explicit operator bool() const {
197 return enabled;
198 }
199
200 bool enabled = true;
201};
202#endif // SUPPORT_ALPN
203
204#if SUPPORT_NPN
205// This option enables/disables NPN in the SSL handshake (if the SSL backend libcurl is built to
206// use supports it), which can be used to negotiate http2.
207class NPN {
208 public:
209 NPN() = default;
210 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
211 NPN(bool p_enabled) : enabled(p_enabled) {}
212
213 explicit operator bool() const {
214 return enabled;
215 }
216
217 bool enabled = true;
218};
219#endif // SUPPORT_NPN
220
221// This option determines whether libcurl verifies that the server cert is for the server it is
222// known as.
223class VerifyHost {
224 public:
225 VerifyHost() = default;
226 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
227 VerifyHost(bool p_enabled) : enabled(p_enabled) {}
228
229 explicit operator bool() const {
230 return enabled;
231 }
232
233 bool enabled = true;
234};
235
236// This option determines whether libcurl verifies the authenticity of the peer's certificate.
237class VerifyPeer {
238 public:
239 VerifyPeer() = default;
240 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
241 VerifyPeer(bool p_enabled) : enabled(p_enabled) {}
242
243 explicit operator bool() const {
244 return enabled;
245 }
246
247 bool enabled = true;
248};
249
250// This option determines whether libcurl verifies the status of the server cert using the
251// "Certificate Status Request" TLS extension (aka. OCSP stapling).
252class VerifyStatus {
253 public:
254 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
255 VerifyStatus(bool p_enabled) : enabled(p_enabled) {}
256
257 explicit operator bool() const {
258 return enabled;
259 }
260
261 bool enabled = false;
262};
263
264// TLS v1.0 or later
265struct TLSv1 {};
266#if SUPPORT_SSLv2
267// SSL v2 (but not SSLv3)
268struct SSLv2 {};
269#endif
270#if SUPPORT_SSLv3
271// SSL v3 (but not SSLv2)
272struct SSLv3 {};
273#endif
274#if SUPPORT_TLSv1_0
275// TLS v1.0 or later (Added in 7.34.0)
276struct TLSv1_0 {};
277#endif
278#if SUPPORT_TLSv1_1
279// TLS v1.1 or later (Added in 7.34.0)
280struct TLSv1_1 {};
281#endif
282#if SUPPORT_TLSv1_2
283// TLS v1.2 or later (Added in 7.34.0)
284struct TLSv1_2 {};
285#endif
286#if SUPPORT_TLSv1_3
287// TLS v1.3 or later (Added in 7.52.0)
288struct TLSv1_3 {};
289#endif
290#if SUPPORT_MAX_TLS_VERSION
291// The flag defines the maximum supported TLS version by libcurl, or the default value from the SSL
292// library is used.
293struct MaxTLSVersion {};
294#endif
295#if SUPPORT_MAX_TLSv1_0
296// The flag defines maximum supported TLS version as TLSv1.0. (Added in 7.54.0)
297struct MaxTLSv1_0 {};
298#endif
299#if SUPPORT_MAX_TLSv1_1
300// The flag defines maximum supported TLS version as TLSv1.1. (Added in 7.54.0)
301struct MaxTLSv1_1 {};
302#endif
303#if SUPPORT_MAX_TLSv1_2
304// The flag defines maximum supported TLS version as TLSv1.2. (Added in 7.54.0)
305struct MaxTLSv1_2 {};
306#endif
307#if SUPPORT_MAX_TLSv1_3
308// The flag defines maximum supported TLS version as TLSv1.3. (Added in 7.54.0)
309struct MaxTLSv1_3 {};
310#endif
311
312// path to Certificate Authority (CA) bundle
313class CaInfo {
314 public:
315 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
316 CaInfo(fs::path&& p_filename) : filename(std::move(p_filename)) {}
317
318 fs::path filename;
319};
320
321// specify directory holding CA certificates
322class CaPath {
323 public:
324 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
325 CaPath(fs::path&& p_filename) : filename(std::move(p_filename)) {}
326
327 fs::path filename;
328};
329
330#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
331class CaBuffer {
332 public:
333 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
334 CaBuffer(std::string&& p_buffer) : buffer(std::move(p_buffer)) {}
335
336 const std::string buffer;
337};
338#endif
339
340// specify a Certificate Revocation List file
341class Crl {
342 public:
343 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
344 Crl(fs::path&& p_filename) : filename(std::move(p_filename)) {}
345
346 fs::path filename;
347};
348
349// specify ciphers to use for TLS
350class Ciphers {
351 public:
352 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
353 Ciphers(std::string&& p_ciphers) : ciphers(std::move(p_ciphers)) {}
354
355 std::string ciphers;
356};
357
358#if SUPPORT_TLSv13_CIPHERS
359// specify ciphers suites to use for TLS 1.3
360class TLS13_Ciphers {
361 public:
362 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
363 TLS13_Ciphers(std::string&& p_ciphers) : ciphers(std::move(p_ciphers)) {}
364
365 std::string ciphers;
366};
367#endif
368
369#if SUPPORT_SESSIONID_CACHE
370// enable/disable use of the SSL session-ID cache
371class SessionIdCache {
372 public:
373 SessionIdCache() = default;
374 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
375 SessionIdCache(bool p_enabled) : enabled(p_enabled) {}
376
377 explicit operator bool() const {
378 return enabled;
379 }
380
381 bool enabled = true;
382};
383#endif
384
385#if SUPPORT_SSL_FALSESTART
386class SslFastStart {
387 public:
388 SslFastStart() = default;
389 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
390 SslFastStart(bool p_enabled) : enabled(p_enabled) {}
391
392 explicit operator bool() const {
393 return enabled;
394 }
395
396 bool enabled = false;
397};
398#endif
399
400class NoRevoke {
401 public:
402 NoRevoke() = default;
403 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
404 NoRevoke(bool p_enabled) : enabled(p_enabled) {}
405
406 explicit operator bool() const {
407 return enabled;
408 }
409
410 bool enabled = false;
411};
412
413} // namespace ssl
414
415struct SslOptions {
416 // We don't use fs::path here, as this leads to problems using windows
417 std::string cert_file;
418 std::string cert_type;
419 // We don't use fs::path here, as this leads to problems using windows
420 std::string key_file;
421#if SUPPORT_CURLOPT_SSLKEY_BLOB
422 std::string key_blob;
423#endif
424 std::string key_type;
425 std::string key_pass;
426 std::string pinned_public_key;
427#if SUPPORT_ALPN
428 bool enable_alpn = true;
429#endif // SUPPORT_ALPN
430#if SUPPORT_NPN
431 bool enable_npn = true;
432#endif // SUPPORT_ALPN
433 bool verify_host = true;
434 bool verify_peer = true;
435 bool verify_status = false;
436 int ssl_version = CURL_SSLVERSION_DEFAULT;
437#if SUPPORT_SSL_NO_REVOKE
438 bool ssl_no_revoke = false;
439#endif
440#if SUPPORT_MAX_TLS_VERSION
441 int max_version = CURL_SSLVERSION_MAX_DEFAULT;
442#endif
443 // We don't use fs::path here, as this leads to problems using windows
444 std::string ca_info;
445 // We don't use fs::path here, as this leads to problems using windows
446 std::string ca_path;
447#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
448 std::string ca_buffer;
449#endif
450 // We don't use fs::path here, as this leads to problems using windows
451 std::string crl_file;
452 std::string ciphers;
453#if SUPPORT_TLSv13_CIPHERS
454 std::string tls13_ciphers;
455#endif
456#if SUPPORT_SESSIONID_CACHE
457 bool session_id_cache = true;
458#endif
459
460 ~SslOptions() noexcept {
461#if SUPPORT_CURLOPT_SSLKEY_BLOB
462 util::secureStringClear(s&: key_blob);
463#endif
464 util::secureStringClear(s&: key_pass);
465 }
466
467 void SetOption(const ssl::CertFile& opt) {
468 cert_file = opt.filename.string();
469 cert_type = opt.GetCertType();
470 }
471 void SetOption(const ssl::KeyFile& opt) {
472 key_file = opt.filename.string();
473 key_type = opt.GetKeyType();
474 key_pass = opt.password;
475 }
476#if SUPPORT_CURLOPT_SSLKEY_BLOB
477 void SetOption(const ssl::KeyBlob& opt) {
478 key_blob = opt.blob;
479 key_type = opt.GetKeyType();
480 key_pass = opt.password;
481 }
482#endif
483 void SetOption(const ssl::PinnedPublicKey& opt) {
484 pinned_public_key = opt.pinned_public_key;
485 }
486
487#if SUPPORT_ALPN
488 void SetOption(const ssl::ALPN& opt) {
489 enable_alpn = opt.enabled;
490 }
491#endif // SUPPORT_ALPN
492#if SUPPORT_NPN
493 void SetOption(const ssl::NPN& opt) {
494 enable_npn = opt.enabled;
495 }
496#endif // SUPPORT_NPN
497 void SetOption(const ssl::VerifyHost& opt) {
498 verify_host = opt.enabled;
499 }
500 void SetOption(const ssl::VerifyPeer& opt) {
501 verify_peer = opt.enabled;
502 }
503 void SetOption(const ssl::VerifyStatus& opt) {
504 verify_status = opt.enabled;
505 }
506 void SetOption(const ssl::TLSv1& /*opt*/) {
507 ssl_version = CURL_SSLVERSION_TLSv1;
508 }
509#if SUPPORT_SSL_NO_REVOKE
510 void SetOption(const ssl::NoRevoke& opt) {
511 ssl_no_revoke = opt.enabled;
512 }
513#endif
514#if SUPPORT_SSLv2
515 void SetOption(const ssl::SSLv2& /*opt*/) {
516 ssl_version = CURL_SSLVERSION_SSLv2;
517 }
518#endif
519#if SUPPORT_SSLv3
520 void SetOption(const ssl::SSLv3& /*opt*/) {
521 ssl_version = CURL_SSLVERSION_SSLv3;
522 }
523#endif
524#if SUPPORT_TLSv1_0
525 void SetOption(const ssl::TLSv1_0& /*opt*/) {
526 ssl_version = CURL_SSLVERSION_TLSv1_0;
527 }
528#endif
529#if SUPPORT_TLSv1_1
530 void SetOption(const ssl::TLSv1_1& /*opt*/) {
531 ssl_version = CURL_SSLVERSION_TLSv1_1;
532 }
533#endif
534#if SUPPORT_TLSv1_2
535 void SetOption(const ssl::TLSv1_2& /*opt*/) {
536 ssl_version = CURL_SSLVERSION_TLSv1_2;
537 }
538#endif
539#if SUPPORT_TLSv1_3
540 void SetOption(const ssl::TLSv1_3& /*opt*/) {
541 ssl_version = CURL_SSLVERSION_TLSv1_3;
542 }
543#endif
544#if SUPPORT_MAX_TLS_VERSION
545 void SetOption(const ssl::MaxTLSVersion& /*opt*/) {
546 max_version = CURL_SSLVERSION_DEFAULT;
547 }
548#endif
549#if SUPPORT_MAX_TLSv1_0
550 void SetOption(const ssl::MaxTLSv1_0& opt) {
551 max_version = CURL_SSLVERSION_MAX_TLSv1_0;
552 }
553#endif
554#if SUPPORT_MAX_TLSv1_1
555 void SetOption(const ssl::MaxTLSv1_1& /*opt*/) {
556 max_version = CURL_SSLVERSION_MAX_TLSv1_1;
557 }
558#endif
559#if SUPPORT_MAX_TLSv1_2
560 void SetOption(const ssl::MaxTLSv1_2& /*opt*/) {
561 max_version = CURL_SSLVERSION_MAX_TLSv1_2;
562 }
563#endif
564#if SUPPORT_MAX_TLSv1_3
565 void SetOption(const ssl::MaxTLSv1_3& /*opt*/) {
566 max_version = CURL_SSLVERSION_MAX_TLSv1_3;
567 }
568#endif
569 void SetOption(const ssl::CaInfo& opt) {
570 ca_info = opt.filename.string();
571 }
572 void SetOption(const ssl::CaPath& opt) {
573 ca_path = opt.filename.string();
574 }
575#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
576 void SetOption(const ssl::CaBuffer& opt) {
577 ca_buffer = opt.buffer;
578 }
579#endif
580 void SetOption(const ssl::Crl& opt) {
581 crl_file = opt.filename.string();
582 }
583 void SetOption(const ssl::Ciphers& opt) {
584 ciphers = opt.ciphers;
585 }
586#if SUPPORT_TLSv13_CIPHERS
587 void SetOption(const ssl::TLS13_Ciphers& opt) {
588 tls13_ciphers = opt.ciphers;
589 }
590#endif
591#if SUPPORT_SESSIONID_CACHE
592 void SetOption(const ssl::SessionIdCache& opt) {
593 session_id_cache = opt.enabled;
594 }
595#endif
596};
597
598namespace priv {
599
600template <typename T>
601void set_ssl_option(SslOptions& opts, T&& t) {
602 opts.SetOption(std::forward<T>(t));
603}
604
605template <typename T, typename... Ts>
606void set_ssl_option(SslOptions& opts, T&& t, Ts&&... ts) {
607 set_ssl_option(opts, std::forward<T>(t));
608 set_ssl_option(opts, std::move(ts)...);
609}
610
611} // namespace priv
612
613template <typename... Ts>
614SslOptions Ssl(Ts&&... ts) {
615 SslOptions opts;
616 priv::set_ssl_option(opts, std::move(ts)...);
617 return opts;
618}
619
620} // namespace cpr
621
622#endif
623