Extend the feature 'dtls' to handle missing DTLS support in OpenSSL

OpenSSL has 'no-dtls' configure option (or can be too ancient to properly
support it), we shall respect such builds. This patch extends configure.json
with a 'dtls' test and adds protection against linkage/compile-time
issues in the QtNetwork's code.

Change-Id: I0c0dd94f5c226115cee4285b82c83aa546555aea
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Timur Pocheptsov 2018-06-25 16:30:36 +02:00
parent 58065cedf7
commit d74ced697e
13 changed files with 118 additions and 30 deletions

View File

@ -14,6 +14,7 @@
"openssl": { "type": "optionalString", "values": [ "no", "yes", "linked", "runtime" ] },
"openssl-linked": { "type": "void", "name": "openssl", "value": "linked" },
"openssl-runtime": { "type": "void", "name": "openssl", "value": "runtime" },
"dtls": "boolean",
"sctp": "boolean",
"securetransport": "boolean",
"ssl": "boolean",
@ -149,6 +150,19 @@
"type": "compile",
"test": "unix/openssl11",
"use": "openssl"
},
"dtls": {
"label": "DTLS support in OpenSSL",
"type": "compile",
"test": {
"include": "openssl/ssl.h",
"tail": [
"#if defined(OPENSSL_NO_DTLS) || !defined(DTLS1_2_VERSION)",
"# error OpenSSL without DTLS support",
"#endif"
]
},
"use": "openssl"
}
},
@ -220,7 +234,7 @@
"label": "DTLS",
"purpose": "Provides a DTLS implementation",
"section": "Networking",
"condition": "features.openssl",
"condition": "features.openssl && tests.dtls",
"output": [ "publicFeature" ]
},
"opensslv11": {
@ -353,6 +367,7 @@ For example:
"openssl",
"openssl-linked",
"opensslv11",
"dtls",
"sctp",
"system-proxies"
]

View File

@ -91,10 +91,12 @@ namespace QSsl {
TlsV1_1OrLater,
TlsV1_2OrLater,
#if QT_CONFIG(dtls)
DtlsV1_0,
DtlsV1_0OrLater,
DtlsV1_2,
DtlsV1_2OrLater,
#endif
UnknownProtocol = -1
};

View File

@ -227,7 +227,8 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint &&
d->nextAllowedProtocols == other.d->nextAllowedProtocols &&
d->nextNegotiatedProtocol == other.d->nextNegotiatedProtocol &&
d->nextProtocolNegotiationStatus == other.d->nextProtocolNegotiationStatus;
d->nextProtocolNegotiationStatus == other.d->nextProtocolNegotiationStatus &&
d->dtlsCookieEnabled == other.d->dtlsCookieEnabled;
}
/*!
@ -997,27 +998,6 @@ QSslConfiguration::NextProtocolNegotiationStatus QSslConfiguration::nextProtocol
return d->nextProtocolNegotiationStatus;
}
/*!
This function returns true if DTLS cookie verification was enabled on a
server-side socket.
\sa setDtlsCookieVerificationEnabled()
*/
bool QSslConfiguration::dtlsCookieVerificationEnabled() const
{
return d->dtlsCookieEnabled;
}
/*!
This function enables DTLS cookie verification when \a enable is true.
\sa dtlsCookieVerificationEnabled()
*/
void QSslConfiguration::setDtlsCookieVerificationEnabled(bool enable)
{
d->dtlsCookieEnabled = enable;
}
/*!
Returns the default SSL configuration to be used in new SSL
connections.
@ -1051,6 +1031,29 @@ void QSslConfiguration::setDefaultConfiguration(const QSslConfiguration &configu
QSslConfigurationPrivate::setDefaultConfiguration(configuration);
}
#if QT_CONFIG(dtls)
/*!
This function returns true if DTLS cookie verification was enabled on a
server-side socket.
\sa setDtlsCookieVerificationEnabled()
*/
bool QSslConfiguration::dtlsCookieVerificationEnabled() const
{
return d->dtlsCookieEnabled;
}
/*!
This function enables DTLS cookie verification when \a enable is true.
\sa dtlsCookieVerificationEnabled()
*/
void QSslConfiguration::setDtlsCookieVerificationEnabled(bool enable)
{
d->dtlsCookieEnabled = enable;
}
/*!
Returns the default DTLS configuration to be used in new DTLS
connections.
@ -1085,6 +1088,7 @@ void QSslConfiguration::setDefaultDtlsConfiguration(const QSslConfiguration &con
QSslConfigurationPrivate::setDefaultDtlsConfiguration(configuration);
}
#endif // dtls
/*! \internal
*/

View File

@ -159,14 +159,16 @@ public:
void setBackendConfigurationOption(const QByteArray &name, const QVariant &value);
void setBackendConfiguration(const QMap<QByteArray, QVariant> &backendConfiguration = QMap<QByteArray, QVariant>());
bool dtlsCookieVerificationEnabled() const;
void setDtlsCookieVerificationEnabled(bool enable);
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
#if QT_CONFIG(dtls)
bool dtlsCookieVerificationEnabled() const;
void setDtlsCookieVerificationEnabled(bool enable);
static QSslConfiguration defaultDtlsConfiguration();
static void setDefaultDtlsConfiguration(const QSslConfiguration &configuration);
#endif // dtls
enum NextProtocolNegotiationStatus {
NextProtocolNegotiationNone,

View File

@ -137,7 +137,11 @@ public:
QByteArray nextNegotiatedProtocol;
QSslConfiguration::NextProtocolNegotiationStatus nextProtocolNegotiationStatus;
#if QT_CONFIG(dtls)
bool dtlsCookieEnabled = true;
#else
const bool dtlsCookieEnabled = false;
#endif // dtls
// in qsslsocket.cpp:
static QSslConfiguration defaultConfiguration();

View File

@ -59,6 +59,7 @@ QT_BEGIN_NAMESPACE
extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
extern QString getErrorsFromOpenSsl();
#if QT_CONFIG(dtls)
// defined in qdtls_openssl.cpp:
namespace dtlscallbacks
{
@ -68,6 +69,7 @@ extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst,
extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
unsigned cookieLength);
}
#endif // dtls
static inline QString msgErrorSettingEllipticCurves(const QString &why)
{
@ -95,6 +97,7 @@ init_context:
unsupportedProtocol = true;
} else {
switch (sslContext->sslConfiguration.protocol()) {
#if QT_CONFIG(dtls)
case QSsl::DtlsV1_0:
case QSsl::DtlsV1_0OrLater:
case QSsl::DtlsV1_2:
@ -102,6 +105,7 @@ init_context:
isDtls = true;
sslContext->ctx = q_SSL_CTX_new(client ? q_DTLS_client_method() : q_DTLS_server_method());
break;
#endif // dtls
default:
// The ssl options will actually control the supported methods
sslContext->ctx = q_SSL_CTX_new(client ? q_TLS_client_method() : q_TLS_server_method());
@ -124,7 +128,12 @@ init_context:
return;
}
const long anyVersion = isDtls ? DTLS_ANY_VERSION : TLS_ANY_VERSION;
const long anyVersion =
#if QT_CONFIG(dtls)
isDtls ? DTLS_ANY_VERSION : TLS_ANY_VERSION;
#else
TLS_ANY_VERSION;
#endif // dtls
long minVersion = anyVersion;
long maxVersion = anyVersion;
@ -165,6 +174,7 @@ init_context:
minVersion = TLS1_2_VERSION;
maxVersion = TLS_MAX_VERSION;
break;
#if QT_CONFIG(dtls)
case QSsl::DtlsV1_0:
minVersion = DTLS1_VERSION;
maxVersion = DTLS1_VERSION;
@ -181,6 +191,7 @@ init_context:
minVersion = DTLS1_2_VERSION;
maxVersion = DTLS_MAX_VERSION;
break;
#endif // dtls
case QSsl::SslV2:
// This protocol is not supported by OpenSSL 1.1 and we handle
// it as an error (see the code above).
@ -326,13 +337,18 @@ init_context:
q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, nullptr);
} else {
q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_PEER,
isDtls ? dtlscallbacks::q_X509DtlsCallback : q_X509Callback);
#if QT_CONFIG(dtls)
isDtls ? dtlscallbacks::q_X509DtlsCallback :
#endif // dtls
q_X509Callback);
}
#if QT_CONFIG(dtls)
if (mode == QSslSocket::SslServerMode && isDtls && configuration.dtlsCookieVerificationEnabled()) {
q_SSL_CTX_set_cookie_generate_cb(sslContext->ctx, dtlscallbacks::q_generate_cookie_callback);
q_SSL_CTX_set_cookie_verify_cb(sslContext->ctx, dtlscallbacks::q_verify_cookie_callback);
}
#endif // dtls
// Set verification depth.
if (sslContext->sslConfiguration.peerVerifyDepth() != 0)

View File

@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE
extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
extern QString getErrorsFromOpenSsl();
#if QT_CONFIG(dtls)
// defined in qdtls_openssl.cpp:
namespace dtlscallbacks
{
@ -65,6 +66,7 @@ extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst,
extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
unsigned cookieLength);
}
#endif // dtls
static inline QString msgErrorSettingEllipticCurves(const QString &why)
{
@ -86,6 +88,7 @@ void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mo
bool isDtls = false;
init_context:
switch (sslContext->sslConfiguration.protocol()) {
#if QT_CONFIG(dtls)
case QSsl::DtlsV1_0:
isDtls = true;
sslContext->ctx = q_SSL_CTX_new(client ? q_DTLSv1_client_method() : q_DTLSv1_server_method());
@ -101,6 +104,7 @@ init_context:
isDtls = true;
sslContext->ctx = q_SSL_CTX_new(client ? q_DTLS_client_method() : q_DTLS_server_method());
break;
#endif // dtls
case QSsl::SslV2:
#ifndef OPENSSL_NO_SSL2
sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv2_client_method() : q_SSLv2_server_method());
@ -313,13 +317,18 @@ init_context:
q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, 0);
} else {
q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_PEER,
isDtls ? dtlscallbacks::q_X509DtlsCallback : q_X509Callback);
#if QT_CONFIG(dtls)
isDtls ? dtlscallbacks::q_X509DtlsCallback :
#endif // dtls
q_X509Callback);
}
#if QT_CONFIG(dtls)
if (mode == QSslSocket::SslServerMode && isDtls && configuration.dtlsCookieVerificationEnabled()) {
q_SSL_CTX_set_cookie_generate_cb(sslContext->ctx, dtlscallbacks::q_generate_cookie_callback);
q_SSL_CTX_set_cookie_verify_cb(sslContext->ctx, CookieVerifyCallback(dtlscallbacks::q_verify_cookie_callback));
}
#endif // dtls
// Set verification depth.
if (sslContext->sslConfiguration.peerVerifyDepth() != 0)

View File

@ -340,7 +340,9 @@ public:
: config(new QSslConfigurationPrivate),
dtlsConfig(new QSslConfigurationPrivate)
{
#if QT_CONFIG(dtls)
dtlsConfig->protocol = QSsl::DtlsV1_2OrLater;
#endif // dtls
}
QMutex mutex;
@ -2316,6 +2318,9 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
ptr->sslOptions = global->sslOptions;
ptr->ellipticCurves = global->ellipticCurves;
ptr->backendConfig = global->backendConfig;
#if QT_CONFIG(dtls)
ptr->dtlsCookieEnabled = global->dtlsCookieEnabled;
#endif
}
/*!

View File

@ -487,9 +487,9 @@ void QSslSocketPrivate::resetDefaultCiphers()
setDefaultSupportedCiphers(ciphers);
setDefaultCiphers(defaultCiphers);
#if QT_CONFIG(dtls)
ciphers.clear();
defaultCiphers.clear();
myCtx = q_SSL_CTX_new(q_DTLS_client_method());
if (myCtx) {
mySsl = q_SSL_new(myCtx);
@ -500,6 +500,7 @@ void QSslSocketPrivate::resetDefaultCiphers()
}
q_SSL_CTX_free(myCtx);
}
#endif // dtls
}
void QSslSocketPrivate::resetDefaultEllipticCurves()

View File

@ -130,6 +130,7 @@ const char *q_OpenSSL_version(int type);
unsigned long q_SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session);
unsigned long q_SSL_set_options(SSL *s, unsigned long op);
#if QT_CONFIG(dtls)
// Functions and types required for DTLS support:
extern "C"
{
@ -149,6 +150,7 @@ BIO_ADDR *q_BIO_ADDR_new();
void q_BIO_ADDR_free(BIO_ADDR *ap);
// API we need for a custom dgram BIO:
BIO_METHOD *q_BIO_meth_new(int type, const char *name);
void q_BIO_meth_free(BIO_METHOD *biom);
int q_BIO_meth_set_write(BIO_METHOD *biom, DgramWriteCallback);
@ -157,6 +159,9 @@ int q_BIO_meth_set_puts(BIO_METHOD *biom, DgramPutsCallback);
int q_BIO_meth_set_ctrl(BIO_METHOD *biom, DgramCtrlCallback);
int q_BIO_meth_set_create(BIO_METHOD *biom, DgramCreateCallback);
int q_BIO_meth_set_destroy(BIO_METHOD *biom, DgramDestroyCallback);
#endif // dtls
void q_BIO_set_data(BIO *a, void *ptr);
void *q_BIO_get_data(BIO *a);
void q_BIO_set_init(BIO *a, int init);

View File

@ -179,6 +179,8 @@ DEFINEFUNC(const char *, OpenSSL_version, int a, a, return 0, return)
DEFINEFUNC(unsigned long, SSL_SESSION_get_ticket_lifetime_hint, const SSL_SESSION *session, session, return 0, return)
DEFINEFUNC4(void, DH_get0_pqg, const DH *dh, dh, const BIGNUM **p, p, const BIGNUM **q, q, const BIGNUM **g, g, return, DUMMYARG)
DEFINEFUNC(int, DH_bits, DH *dh, dh, return 0, return)
#if QT_CONFIG(dtls)
DEFINEFUNC2(int, DTLSv1_listen, SSL *s, s, BIO_ADDR *c, c, return -1, return)
DEFINEFUNC(BIO_ADDR *, BIO_ADDR_new, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(void, BIO_ADDR_free, BIO_ADDR *ap, ap, return, DUMMYARG)
@ -190,6 +192,8 @@ DEFINEFUNC2(int, BIO_meth_set_puts, BIO_METHOD *biom, biom, DgramPutsCallback pu
DEFINEFUNC2(int, BIO_meth_set_ctrl, BIO_METHOD *biom, biom, DgramCtrlCallback ctrl, ctrl, return 0, return)
DEFINEFUNC2(int, BIO_meth_set_create, BIO_METHOD *biom, biom, DgramCreateCallback crt, crt, return 0, return)
DEFINEFUNC2(int, BIO_meth_set_destroy, BIO_METHOD *biom, biom, DgramDestroyCallback dtr, dtr, return 0, return)
#endif // dtls
DEFINEFUNC2(void, BIO_set_data, BIO *a, a, void *ptr, ptr, return, DUMMYARG)
DEFINEFUNC(void *, BIO_get_data, BIO *a, a, return nullptr, return)
DEFINEFUNC2(void, BIO_set_init, BIO *a, a, int init, init, return, DUMMYARG)
@ -304,10 +308,12 @@ DEFINEFUNC3(EC_KEY *, d2i_ECPrivateKey, EC_KEY **a, a, unsigned char **b, b, lon
#endif
#endif
#if QT_CONFIG(dtls)
DEFINEFUNC(const SSL_METHOD *, DTLSv1_server_method, void, DUMMYARG, return nullptr, return)
DEFINEFUNC(const SSL_METHOD *, DTLSv1_client_method, void, DUMMYARG, return nullptr, return)
DEFINEFUNC(const SSL_METHOD *, DTLSv1_2_server_method, void, DUMMYARG, return nullptr, return)
DEFINEFUNC(const SSL_METHOD *, DTLSv1_2_client_method, void, DUMMYARG, return nullptr, return)
#endif // dtls
DEFINEFUNC(char *, CONF_get1_default_config_file, DUMMYARG, DUMMYARG, return 0, return)
DEFINEFUNC(void, OPENSSL_add_all_algorithms_noconf, void, DUMMYARG, return, DUMMYARG)
@ -555,10 +561,12 @@ DEFINEFUNC3(void, SSL_get0_alpn_selected, const SSL *s, s, const unsigned char *
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
// DTLS:
#if QT_CONFIG(dtls)
DEFINEFUNC2(void, SSL_CTX_set_cookie_generate_cb, SSL_CTX *ctx, ctx, CookieGenerateCallback cb, cb, return, DUMMYARG)
DEFINEFUNC2(void, SSL_CTX_set_cookie_verify_cb, SSL_CTX *ctx, ctx, CookieVerifyCallback cb, cb, return, DUMMYARG)
DEFINEFUNC(const SSL_METHOD *, DTLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(const SSL_METHOD *, DTLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif // dtls
DEFINEFUNC2(void, BIO_set_flags, BIO *b, b, int flags, flags, return, DUMMYARG)
DEFINEFUNC2(void, BIO_clear_flags, BIO *b, b, int flags, flags, return, DUMMYARG)
DEFINEFUNC2(void *, BIO_get_ex_data, BIO *b, b, int idx, idx, return nullptr, return)
@ -963,6 +971,8 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_SESSION_get_ticket_lifetime_hint)
RESOLVEFUNC(DH_bits)
RESOLVEFUNC(DSA_bits)
#if QT_CONFIG(dtls)
RESOLVEFUNC(DTLSv1_listen)
RESOLVEFUNC(BIO_ADDR_new)
RESOLVEFUNC(BIO_ADDR_free)
@ -974,6 +984,8 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(BIO_meth_set_ctrl)
RESOLVEFUNC(BIO_meth_set_create)
RESOLVEFUNC(BIO_meth_set_destroy)
#endif // dtls
RESOLVEFUNC(BIO_set_data)
RESOLVEFUNC(BIO_get_data)
RESOLVEFUNC(BIO_set_init)
@ -1044,10 +1056,12 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(d2i_RSAPrivateKey)
#endif
#if QT_CONFIG(dtls)
RESOLVEFUNC(DTLSv1_server_method)
RESOLVEFUNC(DTLSv1_client_method)
RESOLVEFUNC(DTLSv1_2_server_method)
RESOLVEFUNC(DTLSv1_2_client_method)
#endif // dtls
RESOLVEFUNC(CONF_get1_default_config_file)
RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf)
@ -1290,10 +1304,12 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_CTX_set_alpn_select_cb)
RESOLVEFUNC(SSL_get0_alpn_selected)
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ...
#if QT_CONFIG(dtls)
RESOLVEFUNC(SSL_CTX_set_cookie_generate_cb)
RESOLVEFUNC(SSL_CTX_set_cookie_verify_cb)
RESOLVEFUNC(DTLS_server_method)
RESOLVEFUNC(DTLS_client_method)
#endif // dtls
RESOLVEFUNC(DH_new)
RESOLVEFUNC(DH_free)
RESOLVEFUNC(d2i_DHparams)

View File

@ -533,6 +533,8 @@ void q_SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data,
#endif
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
#if QT_CONFIG(dtls)
extern "C"
{
typedef int (*CookieGenerateCallback)(SSL *, unsigned char *, unsigned *);
@ -543,12 +545,16 @@ void q_SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx, CookieVerifyCallback cb);
const SSL_METHOD *q_DTLS_server_method();
const SSL_METHOD *q_DTLS_client_method();
#endif // dtls
void *q_X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx, int idx);
int q_SSL_get_ex_data_X509_STORE_CTX_idx();
#if QT_CONFIG(dtls)
#define q_DTLS_set_link_mtu(ssl, mtu) q_SSL_ctrl((ssl), DTLS_CTRL_SET_LINK_MTU, (mtu), nullptr)
#define q_DTLSv1_get_timeout(ssl, arg) q_SSL_ctrl(ssl, DTLS_CTRL_GET_TIMEOUT, 0, arg)
#define q_DTLSv1_handle_timeout(ssl) q_SSL_ctrl(ssl, DTLS_CTRL_HANDLE_TIMEOUT, 0, nullptr)
#endif // dtls
void q_BIO_set_flags(BIO *b, int flags);
void q_BIO_clear_flags(BIO *b, int flags);

View File

@ -227,16 +227,19 @@ void q_OPENSSL_add_all_algorithms_conf();
long q_SSLeay();
const char *q_SSLeay_version(int type);
#if QT_CONFIG(dtls)
// DTLS:
extern "C"
{
typedef int (*CookieVerifyCallback)(SSL *, unsigned char *, unsigned);
}
#define q_DTLSv1_listen(ssl, peer) q_SSL_ctrl(ssl, DTLS_CTRL_LISTEN, 0, (void *)peer)
const SSL_METHOD *q_DTLSv1_server_method();
const SSL_METHOD *q_DTLSv1_client_method();
const SSL_METHOD *q_DTLSv1_2_server_method();
const SSL_METHOD *q_DTLSv1_2_client_method();
#endif // dtls
#endif // QSSLSOCKET_OPENSSL_PRE11_SYMBOLS_P_H