mirror of
https://github.com/gnss-sdr/gnss-sdr
synced 2026-10-13 11:11:52 +00:00
GnuTLS now verifies the leaf certificate for TLS server purpose
This commit is contained in:
@@ -18,15 +18,19 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <cerrno>
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef USE_GNUTLS_FALLBACK
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/x509.h>
|
||||
#else
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509_vfy.h>
|
||||
#endif
|
||||
@@ -124,13 +128,95 @@ void set_tls_msg(char *msg, const char *text)
|
||||
std::once_flag gnutls_init_flag;
|
||||
int gnutls_init_result = GNUTLS_E_SUCCESS;
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030603
|
||||
constexpr char GNUTLS_TLS_1_2_OR_NEWER_PRIORITY[] =
|
||||
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1";
|
||||
#endif
|
||||
|
||||
void initialize_gnutls_once()
|
||||
{
|
||||
gnutls_init_result = gnutls_global_init();
|
||||
}
|
||||
|
||||
void gnutls_set_socket_transport(gnutls_session_t session, socket_t socket)
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030200
|
||||
gnutls_transport_set_int(session, socket);
|
||||
#else
|
||||
gnutls_transport_set_ptr(session,
|
||||
reinterpret_cast<gnutls_transport_ptr_t>(
|
||||
static_cast<std::intptr_t>(socket)));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030104
|
||||
bool gnutls_leaf_allows_server_auth(gnutls_session_t session)
|
||||
{
|
||||
if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int peer_count = 0;
|
||||
const gnutls_datum_t *peers =
|
||||
gnutls_certificate_get_peers(session, &peer_count);
|
||||
if (peers == nullptr || peer_count == 0 ||
|
||||
peers[0].data == nullptr || peers[0].size == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
gnutls_x509_crt_t leaf = nullptr;
|
||||
if (gnutls_x509_crt_init(&leaf) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool allowed = false;
|
||||
if (gnutls_x509_crt_import(leaf, &peers[0], GNUTLS_X509_FMT_DER) >= 0)
|
||||
{
|
||||
bool saw_key_purpose = false;
|
||||
for (unsigned int index = 0;; ++index)
|
||||
{
|
||||
char oid[64] = {};
|
||||
size_t oid_size = sizeof(oid);
|
||||
unsigned int critical = 0;
|
||||
const int ret = gnutls_x509_crt_get_key_purpose_oid(
|
||||
leaf, index, oid, &oid_size, &critical);
|
||||
if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
|
||||
{
|
||||
// No EKU extension means unrestricted use. Once
|
||||
// an EKU was seen, reaching the end without a
|
||||
// permitted purpose is a mismatch.
|
||||
allowed = !saw_key_purpose;
|
||||
break;
|
||||
}
|
||||
if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
|
||||
{
|
||||
// Both accepted OIDs fit in this buffer, so an
|
||||
// overlong OID cannot be one of them.
|
||||
saw_key_purpose = true;
|
||||
continue;
|
||||
}
|
||||
if (ret < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
saw_key_purpose = true;
|
||||
if (std::strcmp(oid, GNUTLS_KP_TLS_WWW_SERVER) == 0 ||
|
||||
std::strcmp(oid, GNUTLS_KP_ANY) == 0)
|
||||
{
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gnutls_x509_crt_deinit(leaf);
|
||||
return allowed;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
std::once_flag openssl_init_flag;
|
||||
@@ -141,6 +227,29 @@ void initialize_openssl_once()
|
||||
SSL_load_error_strings();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool openssl_load_trust_anchor(SSL_CTX *context, const std::string &pem)
|
||||
{
|
||||
BIO *bio = BIO_new_mem_buf(const_cast<char *>(pem.data()),
|
||||
static_cast<int>(pem.size()));
|
||||
if (bio == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
X509 *certificate = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
|
||||
BIO_free(bio);
|
||||
if (certificate == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
X509_STORE *store = SSL_CTX_get_cert_store(context);
|
||||
const bool loaded = store != nullptr &&
|
||||
X509_STORE_add_cert(store, certificate) == 1;
|
||||
X509_free(certificate);
|
||||
return loaded;
|
||||
}
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
@@ -155,7 +264,7 @@ public:
|
||||
Impl(Impl &&) = delete;
|
||||
Impl &operator=(Impl &&) = delete;
|
||||
|
||||
bool initialize(char *msg)
|
||||
bool initialize(char *msg, const std::string *trust_anchor_pem = nullptr)
|
||||
{
|
||||
#ifdef USE_GNUTLS_FALLBACK
|
||||
std::call_once(gnutls_init_flag, initialize_gnutls_once);
|
||||
@@ -176,12 +285,31 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = gnutls_certificate_set_x509_system_trust(d_credentials);
|
||||
if (ret < 0)
|
||||
if (trust_anchor_pem != nullptr)
|
||||
{
|
||||
gnutls_datum_t trust_anchor;
|
||||
trust_anchor.data = reinterpret_cast<unsigned char *>(
|
||||
const_cast<char *>(trust_anchor_pem->data()));
|
||||
trust_anchor.size = static_cast<unsigned int>(trust_anchor_pem->size());
|
||||
ret = gnutls_certificate_set_x509_trust_mem(d_credentials,
|
||||
&trust_anchor, GNUTLS_X509_FMT_PEM);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = gnutls_certificate_set_x509_system_trust(d_credentials);
|
||||
}
|
||||
if (ret < 0 || (trust_anchor_pem != nullptr && ret == 0))
|
||||
{
|
||||
gnutls_certificate_free_credentials(d_credentials);
|
||||
d_credentials = nullptr;
|
||||
set_tls_msg(msg, "GnuTLS system trust failed");
|
||||
if (trust_anchor_pem == nullptr)
|
||||
{
|
||||
set_tls_msg(msg, "GnuTLS system trust failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
set_tls_msg(msg, "GnuTLS test trust failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -237,11 +365,28 @@ public:
|
||||
SSL_CTX_set_mode(d_context,
|
||||
SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
SSL_CTX_set_verify(d_context, SSL_VERIFY_PEER, nullptr);
|
||||
if (SSL_CTX_set_default_verify_paths(d_context) != 1)
|
||||
bool trust_loaded = false;
|
||||
if (trust_anchor_pem != nullptr)
|
||||
{
|
||||
trust_loaded = openssl_load_trust_anchor(d_context,
|
||||
*trust_anchor_pem);
|
||||
}
|
||||
else
|
||||
{
|
||||
trust_loaded = SSL_CTX_set_default_verify_paths(d_context) == 1;
|
||||
}
|
||||
if (!trust_loaded)
|
||||
{
|
||||
SSL_CTX_free(d_context);
|
||||
d_context = nullptr;
|
||||
set_tls_msg(msg, "OpenSSL system trust failed");
|
||||
if (trust_anchor_pem == nullptr)
|
||||
{
|
||||
set_tls_msg(msg, "OpenSSL system trust failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
set_tls_msg(msg, "OpenSSL test trust failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -345,7 +490,7 @@ public:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
gnutls_transport_set_int(d_session, sock);
|
||||
gnutls_set_socket_transport(d_session, sock);
|
||||
}
|
||||
|
||||
Tls_Sigpipe_Guard sigpipe_guard;
|
||||
@@ -367,20 +512,42 @@ public:
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030104
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030300
|
||||
unsigned int status = 0;
|
||||
ret = gnutls_certificate_verify_peers3(d_session, d_hostname.c_str(), &status);
|
||||
gnutls_typed_vdata_st verification_data[2] = {};
|
||||
verification_data[0].type = GNUTLS_DT_DNS_HOSTNAME;
|
||||
verification_data[0].data = const_cast<unsigned char *>(
|
||||
reinterpret_cast<const unsigned char *>(d_hostname.c_str()));
|
||||
verification_data[1].type = GNUTLS_DT_KEY_PURPOSE_OID;
|
||||
verification_data[1].data = const_cast<unsigned char *>(
|
||||
reinterpret_cast<const unsigned char *>(GNUTLS_KP_TLS_WWW_SERVER));
|
||||
ret = gnutls_certificate_verify_peers(d_session,
|
||||
verification_data, 2, &status);
|
||||
#elif GNUTLS_VERSION_NUMBER >= 0x030104
|
||||
unsigned int status = 0;
|
||||
ret = gnutls_certificate_verify_peers3(
|
||||
d_session, d_hostname.c_str(), &status);
|
||||
#else
|
||||
unsigned int status = GNUTLS_CERT_INVALID;
|
||||
ret = GNUTLS_E_UNIMPLEMENTED_FEATURE;
|
||||
#endif
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030104
|
||||
// GnuTLS 3.1 and 3.2 cannot receive a purpose in their peer
|
||||
// verification API. Keep the explicit leaf check on newer releases
|
||||
// as well so the compatibility path is covered by normal CI.
|
||||
if (ret >= 0 && status == 0 &&
|
||||
!gnutls_leaf_allows_server_auth(d_session))
|
||||
{
|
||||
status |= GNUTLS_CERT_INVALID |
|
||||
GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE;
|
||||
}
|
||||
#endif
|
||||
if (ret < 0 || status != 0)
|
||||
{
|
||||
reset();
|
||||
set_tls_msg(msg, "GnuTLS certificate failed");
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
reset();
|
||||
set_tls_msg(msg, "GnuTLS >= 3.1.4 required for NTRIP TLS");
|
||||
return -1;
|
||||
#endif
|
||||
#else
|
||||
if (!d_session)
|
||||
{
|
||||
@@ -624,6 +791,13 @@ bool Rtklib_Tls_Client::initialize(char *msg)
|
||||
}
|
||||
|
||||
|
||||
bool Rtklib_Tls_Client::initialize_with_trust_anchor(
|
||||
const std::string &trust_anchor_pem, char *msg)
|
||||
{
|
||||
return d_impl->initialize(msg, &trust_anchor_pem);
|
||||
}
|
||||
|
||||
|
||||
void Rtklib_Tls_Client::reset()
|
||||
{
|
||||
d_impl->reset();
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class Rtklib_Tls_Client_Test_Access;
|
||||
|
||||
class Rtklib_Tls_Client
|
||||
{
|
||||
public:
|
||||
@@ -51,6 +53,12 @@ public:
|
||||
int write(const unsigned char *buff, int n, char *msg);
|
||||
|
||||
private:
|
||||
friend class Rtklib_Tls_Client_Test_Access;
|
||||
|
||||
// Test-only trust injection. Production callers always use initialize(),
|
||||
// which loads the platform trust store.
|
||||
bool initialize_with_trust_anchor(const std::string &trust_anchor_pem, char *msg);
|
||||
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> d_impl;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include "rtklib_tls.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
@@ -39,6 +41,16 @@
|
||||
#define GNSSSDR_TLS_TEST_SKIP() return GTEST_SUCCEED() << "Test skipped: "
|
||||
#endif
|
||||
|
||||
class Rtklib_Tls_Client_Test_Access
|
||||
{
|
||||
public:
|
||||
static bool initialize_with_trust_anchor(Rtklib_Tls_Client& client,
|
||||
const std::string& trust_anchor_pem, char* msg)
|
||||
{
|
||||
return client.initialize_with_trust_anchor(trust_anchor_pem, msg);
|
||||
}
|
||||
};
|
||||
|
||||
namespace rtklib_tls_test
|
||||
{
|
||||
constexpr char TLS_TEST_CERTIFICATE[] = R"pem(-----BEGIN CERTIFICATE-----
|
||||
@@ -93,6 +105,151 @@ xdMrgAdtCksfXBKvBdxn6Xw=
|
||||
-----END PRIVATE KEY-----
|
||||
)pem";
|
||||
|
||||
// A dedicated unconstrained root and otherwise-identical localhost leaves let
|
||||
// the client tests isolate Extended Key Usage verification from chain and
|
||||
// hostname verification. The leaves share this test-only key; the root private
|
||||
// key is not part of the repository.
|
||||
constexpr char TLS_TEST_ROOT_CA[] = R"pem(-----BEGIN CERTIFICATE-----
|
||||
MIIDPDCCAiSgAwIBAgIUbyKsSquBKYhDc87+F1aln5bQS/QwDQYJKoZIhvcNAQEL
|
||||
BQAwJDEiMCAGA1UEAwwZR05TUy1TRFIgVExTIFRlc3QgUm9vdCBDQTAeFw0yNjA4
|
||||
MjIwNTM1MDNaFw0zNjA4MTkwNTM1MDNaMCQxIjAgBgNVBAMMGUdOU1MtU0RSIFRM
|
||||
UyBUZXN0IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1
|
||||
NH0lwlmFP3jKvCp4VwpLCUEPcbfIUUfgGKPBh7kSZk5RQgBv7b7er73i0k2sYBa3
|
||||
7mFzuU+76Y5VJ69SdEfIvjbFLyeLs/rcq0av2TxKfFqwGPf3vmAXfMWQT6418bHM
|
||||
vXHRGg88TuqoeCW/BipsP62RQU0zfLPF5oijsr+Kuxx/kUC9n1P1J29q9pr1PvzP
|
||||
9AwapOZ/MNXgHYLLgBQJYJcHRnd5yOCmHQrUfPiW8MYPVs9F+2tHH12f3R8DOC/+
|
||||
aH1X8+/zPtkcXOU4u9L/N21A3NSUxJAKvjAXGMJa0Lj3THZ1Lc4XPelQREjvJ7sn
|
||||
2A/iiRwDFLGDT/k6PXwZAgMBAAGjZjBkMB8GA1UdIwQYMBaAFLJNdnRMm7tDhniN
|
||||
OTeeCHyp/q7oMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0G
|
||||
A1UdDgQWBBSyTXZ0TJu7Q4Z4jTk3ngh8qf6u6DANBgkqhkiG9w0BAQsFAAOCAQEA
|
||||
Z8sJxDqWFZxSncowVl2DSTipOWiisjJNRmbAnA4WUmVA5nP4IGnmGJxELYuNLbjT
|
||||
CPx9gKQ3A0oFLW83i9Wi4aQT09b8SXgniLnP3iM0rOi/5kuqRM2p/lix7bSQ2doV
|
||||
8LhaTu3qMgwYSZsDfHqLQQ14au1znpuBiHX412Mm3+QcGJ04bpOrtPOpaKsBuNOG
|
||||
2xWyp+wNKqncQXl3TRIVAWufyOlSdt8S+JJB1WP+q3ebguLVPz8RePh6B+B6QnXg
|
||||
X8jPyH1eT6bslUucPyG78/SicgP+wyGP6GS4/LlpgA3henzonQFoJc1qz8xZPrFc
|
||||
WOnV0aJeh09viDawvtZEBw==
|
||||
-----END CERTIFICATE-----
|
||||
)pem";
|
||||
|
||||
constexpr char TLS_TEST_SERVER_AUTH_CERTIFICATE[] = R"pem(-----BEGIN CERTIFICATE-----
|
||||
MIIDQDCCAiigAwIBAgIBAjANBgkqhkiG9w0BAQsFADAkMSIwIAYDVQQDDBlHTlNT
|
||||
LVNEUiBUTFMgVGVzdCBSb290IENBMB4XDTI2MDgyMjA1MzUwM1oXDTM2MDgxOTA1
|
||||
MzUwM1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAu+6nn6H31Zx5Abw5VTQD1//UlWPoA7IKZnIRz7mMBCwkAAKM
|
||||
cXvx4i75jNXWJohFthknH7wz8AW+FdfqunTlIyiAPUhDhxGG8Yasq0kzaxJYeG1g
|
||||
BWG5pG5N2vH+4YCpIg6+z9oPPKmJXpVId3smbzf6sp7prvSlJ1ULAZkeYin16i30
|
||||
pu2hbKPSYdjjGVcbnYC5JTy+UnyqMbaccTXX5HeJZlmzrS4wBIxWi/th1O5qHJn3
|
||||
0rmmZ3WglUTYWc8sQRK+uBmxfaSHOFmFBV+iHiDwJ3Uu5UM0rSxJEdN6kHH6RMAj
|
||||
45LgCUgVqDrzn7fir+vKMuByaQnfhwzzeUd6XQIDAQABo4GMMIGJMAwGA1UdEwEB
|
||||
/wQCMAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMBQGA1Ud
|
||||
EQQNMAuCCWxvY2FsaG9zdDAdBgNVHQ4EFgQUUx7AAvieL6vi1/pKctmamlQs6yIw
|
||||
HwYDVR0jBBgwFoAUsk12dEybu0OGeI05N54IfKn+rugwDQYJKoZIhvcNAQELBQAD
|
||||
ggEBAKRXMCFk+34kL9dzn/9LZbgfv4V7F0WcUkjkll0iGdoFDrDJB4tPpeIK+eeB
|
||||
MsKvTChlzs0Qvg6ojypyuRBn7XsATwIllWq1trXiHLn+wvyXNx2ZfoYtbbae7d9/
|
||||
yyAGlOosXFIwGxi8wiy2Ajg6fzQYAXllnJMaAMWw4umuyjiL7daaFzgHL1madvbO
|
||||
Kf9osQXeMxBO3zdYout7c8aOgRCAlbIY14W5UvzBb67YqErDF3ksrVT7gQOrk3Sr
|
||||
lzXZoWDKH+j3hixVfBkc0rjE1n6E2Y7DarysJGpKaLgl7HLMH0D9yKAcN/dAcSk1
|
||||
bRpm7xfMiWfUjQsoztBPj+QJ73E=
|
||||
-----END CERTIFICATE-----
|
||||
)pem";
|
||||
|
||||
constexpr char TLS_TEST_CLIENT_AUTH_CERTIFICATE[] = R"pem(-----BEGIN CERTIFICATE-----
|
||||
MIIDQDCCAiigAwIBAgIBAzANBgkqhkiG9w0BAQsFADAkMSIwIAYDVQQDDBlHTlNT
|
||||
LVNEUiBUTFMgVGVzdCBSb290IENBMB4XDTI2MDgyMjA1MzUwM1oXDTM2MDgxOTA1
|
||||
MzUwM1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAu+6nn6H31Zx5Abw5VTQD1//UlWPoA7IKZnIRz7mMBCwkAAKM
|
||||
cXvx4i75jNXWJohFthknH7wz8AW+FdfqunTlIyiAPUhDhxGG8Yasq0kzaxJYeG1g
|
||||
BWG5pG5N2vH+4YCpIg6+z9oPPKmJXpVId3smbzf6sp7prvSlJ1ULAZkeYin16i30
|
||||
pu2hbKPSYdjjGVcbnYC5JTy+UnyqMbaccTXX5HeJZlmzrS4wBIxWi/th1O5qHJn3
|
||||
0rmmZ3WglUTYWc8sQRK+uBmxfaSHOFmFBV+iHiDwJ3Uu5UM0rSxJEdN6kHH6RMAj
|
||||
45LgCUgVqDrzn7fir+vKMuByaQnfhwzzeUd6XQIDAQABo4GMMIGJMAwGA1UdEwEB
|
||||
/wQCMAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMBQGA1Ud
|
||||
EQQNMAuCCWxvY2FsaG9zdDAdBgNVHQ4EFgQUUx7AAvieL6vi1/pKctmamlQs6yIw
|
||||
HwYDVR0jBBgwFoAUsk12dEybu0OGeI05N54IfKn+rugwDQYJKoZIhvcNAQELBQAD
|
||||
ggEBAEqfON/6faL2YqCaZVajvoUqzpkQRzGALwzrA3CYYLeYAqpUUuJgBW4X/rIF
|
||||
3I8Fkve4mzY6qfZCr3nXQmx+gbwy4UsLIcGiC/buSsb02P/90r1ug6oHswo5mP+R
|
||||
d79IIk56SWgED+SWTHVRUPltpP2Bx+mV6oiVqVv3S7w6wnvkdxoFaVyQtNlaeC+s
|
||||
GGguPxQBQJYZA9tCEwVu5gClS1gzdbFXy6968+pTxJZU+eUIIjvMA7o6XP3Qjwh+
|
||||
YaGG/U8UqWqUpA0R2hcis5EkHAADUMmK0GELAkXUiOH/L8R+dnfojsrFmdWgrDi6
|
||||
/uGxuSfjr6zvAfeaIb0RxHEG+Mc=
|
||||
-----END CERTIFICATE-----
|
||||
)pem";
|
||||
|
||||
constexpr char TLS_TEST_NO_EKU_CERTIFICATE[] = R"pem(-----BEGIN CERTIFICATE-----
|
||||
MIIDKTCCAhGgAwIBAgIBBDANBgkqhkiG9w0BAQsFADAkMSIwIAYDVQQDDBlHTlNT
|
||||
LVNEUiBUTFMgVGVzdCBSb290IENBMB4XDTI2MDgyMjA2MDMwNFoXDTM2MDgxOTA2
|
||||
MDMwNFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAu+6nn6H31Zx5Abw5VTQD1//UlWPoA7IKZnIRz7mMBCwkAAKM
|
||||
cXvx4i75jNXWJohFthknH7wz8AW+FdfqunTlIyiAPUhDhxGG8Yasq0kzaxJYeG1g
|
||||
BWG5pG5N2vH+4YCpIg6+z9oPPKmJXpVId3smbzf6sp7prvSlJ1ULAZkeYin16i30
|
||||
pu2hbKPSYdjjGVcbnYC5JTy+UnyqMbaccTXX5HeJZlmzrS4wBIxWi/th1O5qHJn3
|
||||
0rmmZ3WglUTYWc8sQRK+uBmxfaSHOFmFBV+iHiDwJ3Uu5UM0rSxJEdN6kHH6RMAj
|
||||
45LgCUgVqDrzn7fir+vKMuByaQnfhwzzeUd6XQIDAQABo3YwdDAMBgNVHRMBAf8E
|
||||
AjAAMA4GA1UdDwEB/wQEAwIFoDAUBgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0O
|
||||
BBYEFFMewAL4ni+r4tf6SnLZmppULOsiMB8GA1UdIwQYMBaAFLJNdnRMm7tDhniN
|
||||
OTeeCHyp/q7oMA0GCSqGSIb3DQEBCwUAA4IBAQApzMyj/61xlCLHrtq/7Euv61mb
|
||||
ZpYzNWx1ehglOASoixkgGxvD9+PhBgucVYc50rr6cLyVF4w92or3tMLcNbJZ4MPm
|
||||
SIdAWJnV14/YW1r+w7UzDAoC8SK4Q7XunHLC5N49ZHqSAtWOzsZNBrR7y/Gv+huZ
|
||||
0MNohi5aGf3DK8Mbcyh7XHGF4MKuGjEtleYF+ABaQOASOsHyrB3WoA3vYV1XC2Gt
|
||||
ZOfGjBkTVTGsiD6P+t6a9QKYKpE4BUE2vuAGamu6Z+bym7hCXD7qE2kNSKs5cjq/
|
||||
RgulMgtXEYkbNa9ViyouORFP67MwrXiX6ZZQHIhy8b2ZPDPDiERt5l4Gjpy+
|
||||
-----END CERTIFICATE-----
|
||||
)pem";
|
||||
|
||||
#ifdef USE_GNUTLS_FALLBACK
|
||||
constexpr char TLS_TEST_ANY_EKU_CERTIFICATE[] = R"pem(-----BEGIN CERTIFICATE-----
|
||||
MIIDPDCCAiSgAwIBAgIBBTANBgkqhkiG9w0BAQsFADAkMSIwIAYDVQQDDBlHTlNT
|
||||
LVNEUiBUTFMgVGVzdCBSb290IENBMB4XDTI2MDgyMjA2MDMwNFoXDTM2MDgxOTA2
|
||||
MDMwNFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAu+6nn6H31Zx5Abw5VTQD1//UlWPoA7IKZnIRz7mMBCwkAAKM
|
||||
cXvx4i75jNXWJohFthknH7wz8AW+FdfqunTlIyiAPUhDhxGG8Yasq0kzaxJYeG1g
|
||||
BWG5pG5N2vH+4YCpIg6+z9oPPKmJXpVId3smbzf6sp7prvSlJ1ULAZkeYin16i30
|
||||
pu2hbKPSYdjjGVcbnYC5JTy+UnyqMbaccTXX5HeJZlmzrS4wBIxWi/th1O5qHJn3
|
||||
0rmmZ3WglUTYWc8sQRK+uBmxfaSHOFmFBV+iHiDwJ3Uu5UM0rSxJEdN6kHH6RMAj
|
||||
45LgCUgVqDrzn7fir+vKMuByaQnfhwzzeUd6XQIDAQABo4GIMIGFMAwGA1UdEwEB
|
||||
/wQCMAAwDgYDVR0PAQH/BAQDAgWgMA8GA1UdJQQIMAYGBFUdJQAwFAYDVR0RBA0w
|
||||
C4IJbG9jYWxob3N0MB0GA1UdDgQWBBRTHsAC+J4vq+LX+kpy2ZqaVCzrIjAfBgNV
|
||||
HSMEGDAWgBSyTXZ0TJu7Q4Z4jTk3ngh8qf6u6DANBgkqhkiG9w0BAQsFAAOCAQEA
|
||||
J9KrKX26T9gjeVQj7J3yMldLTv+mcNdbo7LgKqY4xTgi2OJrgmNvH3BpOD5Q+5h2
|
||||
54I0wjJT4bHYY0xNRTEJadU+OgLQ9Zwo/0O//gaUMiF0dAoTYbQxsb4m3jKwZl7u
|
||||
lcGerwR0p1F12ZdbZimbLBGsEvSg3Wxxojuvmj1P14Hio91P1+xpHlv2ouBfF1lW
|
||||
D5nYPZsLrgFD/VVgHmG/b1ehUVsejuTm5hvv4QHC3MNq57w5XiZ4R64Xl5/AZAtm
|
||||
FvTN7EGftZOqImIkl+QFjsVMubDLYlIfWuL8NvRm/qoM8qJUKtoxteBDZc8z/byo
|
||||
oF+IE5tVQzmt81owsM0C3A==
|
||||
-----END CERTIFICATE-----
|
||||
)pem";
|
||||
#endif
|
||||
|
||||
constexpr char TLS_TEST_LEAF_PRIVATE_KEY[] = R"pem(-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC77qefoffVnHkB
|
||||
vDlVNAPX/9SVY+gDsgpmchHPuYwELCQAAoxxe/HiLvmM1dYmiEW2GScfvDPwBb4V
|
||||
1+q6dOUjKIA9SEOHEYbxhqyrSTNrElh4bWAFYbmkbk3a8f7hgKkiDr7P2g88qYle
|
||||
lUh3eyZvN/qynumu9KUnVQsBmR5iKfXqLfSm7aFso9Jh2OMZVxudgLklPL5SfKox
|
||||
tpxxNdfkd4lmWbOtLjAEjFaL+2HU7mocmffSuaZndaCVRNhZzyxBEr64GbF9pIc4
|
||||
WYUFX6IeIPAndS7lQzStLEkR03qQcfpEwCPjkuAJSBWoOvOft+Kv68oy4HJpCd+H
|
||||
DPN5R3pdAgMBAAECggEAIff0bOZsAzyN47onm8az1ig7/seo4XgeKJ/AJ+2ICR4I
|
||||
YMcVJxMJ6MHoYeQp2duiTBSWTYZpVd5X2h9lok+sNhNlQI5uBD0vCLt4vNoVlOoj
|
||||
Oxs78qUUFsxIPsWkdhKZH5qOM0gdHnAfcAzoJQ6Jpys1YXFXWEQIkQguXdrVEtVm
|
||||
1ekuhGb4TTwVss2SC2HwE1Op4DJ/cuBwnbWSLw9d7lBoGE6THlwci54EAl5l8/V4
|
||||
sZ7VbtiHMnjmdijvbOiw7LrhO+IsnxV5VW7pVx4YNYzfAeasdg3VxyIecdF84sBD
|
||||
t0xrFu53JpD/XCp38327qvkrnN5U7y7XuPIb3Bww0QKBgQDpqDQyi9N/SWfnmxAm
|
||||
6eA39BCFpEtQGCvoYZsh+c/twIvSZ0yekki7VyCxQvy2DeLlah2NtOzv3r4LEZ65
|
||||
aG0ZtsSxkjbGcS6akBllwAc/WcfmOMWvscUG+kAsg+RaXBXoMmkFpfusAX2OLIPr
|
||||
er0d4aSJ2isjJQ6xgaJ0sWhWkQKBgQDN5yIEEm+xWwUtdGQrx+d+t0qPn8Yq13PO
|
||||
Gdywh3nGgq2iKJbkjp0Pf/98EH02odDpPE9zj96S3Wi107WliPNBELgx97i7rqv0
|
||||
O+MRzRYN4BbgBcMmV5JYZzciBJuOex89y4SKrt06F3eAOUqd+EilfJ5oPAKGaVMZ
|
||||
7Wjg7EZFDQKBgQDV/bOYW1DesWbUycHYMbek5wS+sII9H3YPBF1kl3qj3wTvNKYK
|
||||
UCUGVjxdu+DgBwW3YCEi6AFbid75GjEI0yegNjAcBLHfukaZTcHE20KH9XzLT1FV
|
||||
oHS2+DgPzNc1HkL9/jUK9EHy/QZ1UDWdUzKijEX57nI1ZLn5PaWAQ/4v0QKBgQCh
|
||||
8jsE7ONAoZBu8e6fqLlhJxTT5hzdMVx6LEkx5zxWcA3VTwtvdOP/fvMZR3bUc213
|
||||
JOJwyCWNaeujp4JlUlPRRy84zdhUdcv1auzr4VyJSUl0pdOZ4qLOOVBZxzn6VMh7
|
||||
uAjlcye5Ox/YkDcqDFBHKL8GyLxLr6eW6u3kirGyOQKBgQCeiVm/upB77ihH7hzi
|
||||
FVX+EHI4VkWc0Fz70afDRLEhmwtNxE2Ac33DaVBx5jUMzz9IWYaTAwR4QWcz84Ay
|
||||
/J63Nh2WGhey4pcg6BLqBgMICjfA/FN2eiFgelAMLlXdd8iheKVyj/d3fzKQed3g
|
||||
hNAIqqk3Hc6v8sjdnvOyJK2XAQ==
|
||||
-----END PRIVATE KEY-----
|
||||
)pem";
|
||||
|
||||
constexpr int TLS_HANDSHAKE_TIMEOUT_S = 2;
|
||||
|
||||
|
||||
@@ -184,6 +341,18 @@ gnutls_datum_t make_datum(const char* pem)
|
||||
}
|
||||
|
||||
|
||||
void set_socket_transport(gnutls_session_t session, int socket)
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030200
|
||||
gnutls_transport_set_int(session, socket);
|
||||
#else
|
||||
gnutls_transport_set_ptr(session,
|
||||
reinterpret_cast<gnutls_transport_ptr_t>(
|
||||
static_cast<std::intptr_t>(socket)));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
class Tls_Test_Server
|
||||
{
|
||||
public:
|
||||
@@ -203,7 +372,9 @@ public:
|
||||
Tls_Test_Server& operator=(const Tls_Test_Server&) = delete;
|
||||
Tls_Test_Server() = default;
|
||||
|
||||
bool initialize(int socket, Tls_Test_Version version)
|
||||
bool initialize(int socket, Tls_Test_Version version,
|
||||
const char* certificate_pem = TLS_TEST_CERTIFICATE,
|
||||
const char* private_key_pem = TLS_TEST_PRIVATE_KEY)
|
||||
{
|
||||
std::call_once(tls_test_init_flag, initialize_tls_test_backend);
|
||||
if (tls_test_init_result < 0 ||
|
||||
@@ -212,8 +383,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
gnutls_datum_t certificate = make_datum(TLS_TEST_CERTIFICATE);
|
||||
gnutls_datum_t private_key = make_datum(TLS_TEST_PRIVATE_KEY);
|
||||
gnutls_datum_t certificate = make_datum(certificate_pem);
|
||||
gnutls_datum_t private_key = make_datum(private_key_pem);
|
||||
if (gnutls_certificate_set_x509_key_mem(d_credentials,
|
||||
&certificate, &private_key, GNUTLS_X509_FMT_PEM) < 0 ||
|
||||
gnutls_init(&d_session, GNUTLS_SERVER) < 0)
|
||||
@@ -235,7 +406,7 @@ public:
|
||||
GNUTLS_HANDSHAKE_SERVER_HELLO, GNUTLS_HOOK_POST,
|
||||
&Tls_Test_Server::server_hello_hook);
|
||||
#endif
|
||||
gnutls_transport_set_int(d_session, socket);
|
||||
set_socket_transport(d_session, socket);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -305,7 +476,7 @@ bool native_client_handshake(int socket, Tls_Test_Version version)
|
||||
credentials) >= 0;
|
||||
if (configured)
|
||||
{
|
||||
gnutls_transport_set_int(session, socket);
|
||||
set_socket_transport(session, socket);
|
||||
}
|
||||
const bool connected = configured && gnutls_handshake(session) == 0;
|
||||
gnutls_deinit(session);
|
||||
@@ -355,12 +526,13 @@ bool set_exact_protocol(SSL_CTX* context, Tls_Test_Version version)
|
||||
}
|
||||
|
||||
|
||||
bool install_test_identity(SSL_CTX* context)
|
||||
bool install_test_identity(SSL_CTX* context, const char* certificate_pem,
|
||||
const char* private_key_pem)
|
||||
{
|
||||
BIO* certificate_bio = BIO_new_mem_buf(
|
||||
const_cast<char*>(TLS_TEST_CERTIFICATE), -1);
|
||||
const_cast<char*>(certificate_pem), -1);
|
||||
BIO* private_key_bio = BIO_new_mem_buf(
|
||||
const_cast<char*>(TLS_TEST_PRIVATE_KEY), -1);
|
||||
const_cast<char*>(private_key_pem), -1);
|
||||
if (certificate_bio == nullptr || private_key_bio == nullptr)
|
||||
{
|
||||
BIO_free(certificate_bio);
|
||||
@@ -409,7 +581,9 @@ public:
|
||||
Tls_Test_Server& operator=(const Tls_Test_Server&) = delete;
|
||||
Tls_Test_Server() = default;
|
||||
|
||||
bool initialize(int socket, Tls_Test_Version version)
|
||||
bool initialize(int socket, Tls_Test_Version version,
|
||||
const char* certificate_pem = TLS_TEST_CERTIFICATE,
|
||||
const char* private_key_pem = TLS_TEST_PRIVATE_KEY)
|
||||
{
|
||||
std::call_once(tls_test_init_flag, initialize_tls_test_backend);
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
@@ -420,7 +594,8 @@ public:
|
||||
if (d_context == nullptr ||
|
||||
!set_exact_protocol(d_context, version) ||
|
||||
SSL_CTX_set_cipher_list(d_context, "ALL") != 1 ||
|
||||
!install_test_identity(d_context))
|
||||
!install_test_identity(d_context, certificate_pem,
|
||||
private_key_pem))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -552,7 +727,11 @@ bool backend_supports(Tls_Test_Version version)
|
||||
}
|
||||
|
||||
|
||||
Tls_Handshake_Result run_rtklib_handshake(Tls_Test_Version version)
|
||||
Tls_Handshake_Result run_rtklib_handshake(Tls_Test_Version version,
|
||||
const char* certificate_pem = TLS_TEST_CERTIFICATE,
|
||||
const char* private_key_pem = TLS_TEST_PRIVATE_KEY,
|
||||
const char* trust_anchor_pem = nullptr,
|
||||
const char* hostname = "localhost")
|
||||
{
|
||||
Tls_Handshake_Result result;
|
||||
Socket_Pair sockets;
|
||||
@@ -563,15 +742,27 @@ Tls_Handshake_Result run_rtklib_handshake(Tls_Test_Version version)
|
||||
}
|
||||
|
||||
Tls_Test_Server server;
|
||||
if (!server.initialize(sockets.server(), version))
|
||||
if (!server.initialize(sockets.server(), version,
|
||||
certificate_pem, private_key_pem))
|
||||
{
|
||||
result.server_error = "TLS server setup failed";
|
||||
return result;
|
||||
}
|
||||
|
||||
Rtklib_Tls_Client client("localhost");
|
||||
Rtklib_Tls_Client client(hostname);
|
||||
char message[MAXSTRMSG] = "";
|
||||
if (!client.initialize(message))
|
||||
bool initialized = false;
|
||||
if (trust_anchor_pem == nullptr)
|
||||
{
|
||||
initialized = client.initialize(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
initialized = Rtklib_Tls_Client_Test_Access::
|
||||
initialize_with_trust_anchor(
|
||||
client, trust_anchor_pem, message);
|
||||
}
|
||||
if (!initialized)
|
||||
{
|
||||
result.server_error = message;
|
||||
return result;
|
||||
@@ -579,7 +770,18 @@ Tls_Handshake_Result run_rtklib_handshake(Tls_Test_Version version)
|
||||
result.initialized = true;
|
||||
|
||||
std::thread server_thread(&Tls_Test_Server::handshake, &server);
|
||||
result.client_result = client.handshake(sockets.client(), message);
|
||||
const auto deadline = std::chrono::steady_clock::now() +
|
||||
std::chrono::seconds(TLS_HANDSHAKE_TIMEOUT_S);
|
||||
do
|
||||
{
|
||||
result.client_result = client.handshake(sockets.client(), message);
|
||||
if (result.client_result == 0)
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
while (result.client_result == 0 &&
|
||||
std::chrono::steady_clock::now() < deadline);
|
||||
shutdown(sockets.client(), SHUT_RDWR);
|
||||
server_thread.join();
|
||||
|
||||
@@ -647,3 +849,83 @@ TEST(RtklibTlsTest, AllowsTls12Negotiation)
|
||||
<< result.server_error;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
TEST(RtklibTlsTest, AcceptsTrustedServerAuthLeaf)
|
||||
{
|
||||
using namespace rtklib_tls_test;
|
||||
|
||||
const Tls_Handshake_Result result = run_rtklib_handshake(
|
||||
Tls_Test_Version::TLS_1_2, TLS_TEST_SERVER_AUTH_CERTIFICATE,
|
||||
TLS_TEST_LEAF_PRIVATE_KEY, TLS_TEST_ROOT_CA);
|
||||
|
||||
ASSERT_TRUE(result.initialized) << result.server_error;
|
||||
EXPECT_EQ(1, result.client_result) << result.client_message;
|
||||
EXPECT_TRUE(result.server_handshake_completed) << result.server_error;
|
||||
EXPECT_FALSE(result.protocol_version_rejected);
|
||||
}
|
||||
|
||||
|
||||
TEST(RtklibTlsTest, RejectsTrustedClientAuthOnlyLeaf)
|
||||
{
|
||||
using namespace rtklib_tls_test;
|
||||
|
||||
const Tls_Handshake_Result result = run_rtklib_handshake(
|
||||
Tls_Test_Version::TLS_1_2, TLS_TEST_CLIENT_AUTH_CERTIFICATE,
|
||||
TLS_TEST_LEAF_PRIVATE_KEY, TLS_TEST_ROOT_CA);
|
||||
|
||||
ASSERT_TRUE(result.initialized) << result.server_error;
|
||||
EXPECT_EQ(-1, result.client_result) << result.client_message;
|
||||
EXPECT_FALSE(result.protocol_version_rejected)
|
||||
<< "The client rejected TLS 1.2 rather than the certificate purpose";
|
||||
}
|
||||
|
||||
|
||||
TEST(RtklibTlsTest, AcceptsTrustedLeafWithoutEku)
|
||||
{
|
||||
using namespace rtklib_tls_test;
|
||||
|
||||
const Tls_Handshake_Result result = run_rtklib_handshake(
|
||||
Tls_Test_Version::TLS_1_2, TLS_TEST_NO_EKU_CERTIFICATE,
|
||||
TLS_TEST_LEAF_PRIVATE_KEY, TLS_TEST_ROOT_CA);
|
||||
|
||||
ASSERT_TRUE(result.initialized) << result.server_error;
|
||||
EXPECT_EQ(1, result.client_result) << result.client_message;
|
||||
EXPECT_TRUE(result.server_handshake_completed) << result.server_error;
|
||||
EXPECT_FALSE(result.protocol_version_rejected);
|
||||
}
|
||||
|
||||
|
||||
TEST(RtklibTlsTest, AcceptsTrustedAnyEkuLeaf)
|
||||
{
|
||||
using namespace rtklib_tls_test;
|
||||
|
||||
#ifndef USE_GNUTLS_FALLBACK
|
||||
GNSSSDR_TLS_TEST_SKIP()
|
||||
<< "OpenSSL requires an explicit serverAuth purpose";
|
||||
#else
|
||||
const Tls_Handshake_Result result = run_rtklib_handshake(
|
||||
Tls_Test_Version::TLS_1_2, TLS_TEST_ANY_EKU_CERTIFICATE,
|
||||
TLS_TEST_LEAF_PRIVATE_KEY, TLS_TEST_ROOT_CA);
|
||||
|
||||
ASSERT_TRUE(result.initialized) << result.server_error;
|
||||
EXPECT_EQ(1, result.client_result) << result.client_message;
|
||||
EXPECT_TRUE(result.server_handshake_completed) << result.server_error;
|
||||
EXPECT_FALSE(result.protocol_version_rejected);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
TEST(RtklibTlsTest, RejectsTrustedHostnameMismatch)
|
||||
{
|
||||
using namespace rtklib_tls_test;
|
||||
|
||||
const Tls_Handshake_Result result = run_rtklib_handshake(
|
||||
Tls_Test_Version::TLS_1_2, TLS_TEST_SERVER_AUTH_CERTIFICATE,
|
||||
TLS_TEST_LEAF_PRIVATE_KEY, TLS_TEST_ROOT_CA, "not-localhost");
|
||||
|
||||
ASSERT_TRUE(result.initialized) << result.server_error;
|
||||
EXPECT_EQ(-1, result.client_result) << result.client_message;
|
||||
EXPECT_FALSE(result.protocol_version_rejected)
|
||||
<< "The client rejected TLS 1.2 rather than the hostname";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user