[glib-networking] openssl: add support for DTLS
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking] openssl: add support for DTLS
- Date: Thu, 3 Jun 2021 18:49:25 +0000 (UTC)
commit 852e5c2140f4f14275f24ff09adfd1d10014fc04
Author: Ole André Vadla Ravnås <oleavr gmail com>
Date: Wed May 26 09:39:16 2021 +0200
openssl: add support for DTLS
tls/openssl/gtlsbackend-openssl.c | 2 +
tls/openssl/gtlsbio.c | 225 ++++++++++++-----
tls/openssl/gtlsbio.h | 13 +-
tls/openssl/gtlsclientconnection-openssl.c | 13 +-
tls/openssl/gtlsconnection-openssl.c | 381 ++++++++++++++++++++---------
tls/openssl/gtlsserverconnection-openssl.c | 13 +-
tls/openssl/openssl-include.h | 11 +-
tls/tests/dtls-connection.c | 2 +
tls/tests/meson.build | 5 -
9 files changed, 466 insertions(+), 199 deletions(-)
---
diff --git a/tls/openssl/gtlsbackend-openssl.c b/tls/openssl/gtlsbackend-openssl.c
index 87248cc..23cd8de 100644
--- a/tls/openssl/gtlsbackend-openssl.c
+++ b/tls/openssl/gtlsbackend-openssl.c
@@ -239,6 +239,8 @@ g_tls_backend_openssl_interface_init (GTlsBackendInterface *iface)
iface->get_server_connection_type = g_tls_server_connection_openssl_get_type;
iface->get_file_database_type = g_tls_file_database_openssl_get_type;
iface->get_default_database = g_tls_backend_openssl_get_default_database;
+ iface->get_dtls_client_connection_type = g_tls_client_connection_openssl_get_type;
+ iface->get_dtls_server_connection_type = g_tls_server_connection_openssl_get_type;
}
void
diff --git a/tls/openssl/gtlsbio.c b/tls/openssl/gtlsbio.c
index ad1efb5..4e138e7 100644
--- a/tls/openssl/gtlsbio.c
+++ b/tls/openssl/gtlsbio.c
@@ -29,24 +29,27 @@
typedef struct {
GIOStream *io_stream;
+ GDatagramBased *socket;
GCancellable *read_cancellable;
GCancellable *write_cancellable;
- gboolean read_blocking;
- gboolean write_blocking;
GError **read_error;
GError **write_error;
- GMainContext *context;
- GMainLoop *loop;
} GTlsBio;
+typedef struct {
+ gboolean done;
+ gboolean timed_out;
+} WaitData;
+
static void
free_gbio (gpointer user_data)
{
GTlsBio *bio = (GTlsBio *)user_data;
- g_object_unref (bio->io_stream);
- g_main_context_unref (bio->context);
- g_main_loop_unref (bio->loop);
+ if (bio->io_stream)
+ g_object_unref (bio->io_stream);
+ else
+ g_object_unref (bio->socket);
g_free (bio);
}
@@ -131,6 +134,9 @@ gtls_bio_ctrl (BIO *b,
case BIO_CTRL_POP:
ret = 0;
break;
+ case BIO_CTRL_DGRAM_QUERY_MTU:
+ ret = 1400; /* Same as the GnuTLS backend */
+ break;
default:
g_debug ("Got unsupported command: %d", cmd);
ret = 0;
@@ -165,11 +171,28 @@ gtls_bio_write (BIO *bio,
#endif
BIO_clear_retry_flags (bio);
- written = g_pollable_stream_write (g_io_stream_get_output_stream (gbio->io_stream),
- in, inl,
- gbio->write_blocking,
- gbio->write_cancellable,
- &error);
+ if (gbio->io_stream)
+ {
+ written = g_pollable_stream_write (g_io_stream_get_output_stream (gbio->io_stream),
+ in, inl,
+ FALSE,
+ gbio->write_cancellable,
+ &error);
+ }
+ else
+ {
+ GOutputVector vector = { in, inl };
+ GOutputMessage message = { NULL, &vector, 1, 0, NULL, 0 };
+
+ written = g_datagram_based_send_messages (gbio->socket,
+ &message, 1, 0,
+ 0,
+ gbio->write_cancellable,
+ &error);
+
+ if (written > 0)
+ written = message.bytes_sent;
+ }
if (written == -1)
{
@@ -208,11 +231,28 @@ gtls_bio_read (BIO *bio,
#endif
BIO_clear_retry_flags (bio);
- read = g_pollable_stream_read (g_io_stream_get_input_stream (gbio->io_stream),
- out, outl,
- gbio->read_blocking,
- gbio->read_cancellable,
- &error);
+ if (gbio->io_stream)
+ {
+ read = g_pollable_stream_read (g_io_stream_get_input_stream (gbio->io_stream),
+ out, outl,
+ FALSE,
+ gbio->read_cancellable,
+ &error);
+ }
+ else
+ {
+ GInputVector vector = { out, outl };
+ GInputMessage message = { NULL, &vector, 1, 0, 0, NULL, NULL };
+
+ read = g_datagram_based_receive_messages (gbio->socket,
+ &message, 1, 0,
+ 0,
+ gbio->read_cancellable,
+ &error);
+
+ if (read > 0)
+ read = message.bytes_received;
+ }
if (read == -1)
{
@@ -284,8 +324,8 @@ BIO_s_gtls (void)
}
#endif
-BIO *
-g_tls_bio_new (GIOStream *io_stream)
+static BIO *
+g_tls_bio_alloc (GTlsBio **out_gbio)
{
BIO *ret;
GTlsBio *gbio;
@@ -295,9 +335,6 @@ g_tls_bio_new (GIOStream *io_stream)
return NULL;
gbio = g_new0 (GTlsBio, 1);
- gbio->io_stream = g_object_ref (io_stream);
- gbio->context = g_main_context_new ();
- gbio->loop = g_main_loop_new (gbio->context, FALSE);
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
ret->ptr = gbio;
@@ -307,28 +344,37 @@ g_tls_bio_new (GIOStream *io_stream)
BIO_set_init (ret, 1);
#endif
+ *out_gbio = gbio;
return ret;
}
-void
-g_tls_bio_set_read_cancellable (BIO *bio,
- GCancellable *cancellable)
+BIO *
+g_tls_bio_new_from_iostream (GIOStream *io_stream)
{
+ BIO *ret;
GTlsBio *gbio;
- g_return_if_fail (bio);
+ ret = g_tls_bio_alloc (&gbio);
+ gbio->io_stream = g_object_ref (io_stream);
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
- gbio = (GTlsBio *)bio->ptr;
-#else
- gbio = BIO_get_data (bio);
-#endif
- gbio->read_cancellable = cancellable;
+ return ret;
+}
+
+BIO *
+g_tls_bio_new_from_datagram_based (GDatagramBased *socket)
+{
+ BIO *ret;
+ GTlsBio *gbio;
+
+ ret = g_tls_bio_alloc (&gbio);
+ gbio->socket = g_object_ref (socket);
+
+ return ret;
}
void
-g_tls_bio_set_read_blocking (BIO *bio,
- gboolean blocking)
+g_tls_bio_set_read_cancellable (BIO *bio,
+ GCancellable *cancellable)
{
GTlsBio *gbio;
@@ -339,7 +385,7 @@ g_tls_bio_set_read_blocking (BIO *bio,
#else
gbio = BIO_get_data (bio);
#endif
- gbio->read_blocking = blocking;
+ gbio->read_cancellable = cancellable;
}
void
@@ -375,8 +421,8 @@ g_tls_bio_set_write_cancellable (BIO *bio,
}
void
-g_tls_bio_set_write_blocking (BIO *bio,
- gboolean blocking)
+g_tls_bio_set_write_error (BIO *bio,
+ GError **error)
{
GTlsBio *gbio;
@@ -387,45 +433,55 @@ g_tls_bio_set_write_blocking (BIO *bio,
#else
gbio = BIO_get_data (bio);
#endif
- gbio->write_blocking = blocking;
+ gbio->write_error = error;
}
-void
-g_tls_bio_set_write_error (BIO *bio,
- GError **error)
+static gboolean
+on_pollable_source_ready (GObject *pollable_stream,
+ gpointer user_data)
{
- GTlsBio *gbio;
+ WaitData *wait_data = user_data;
- g_return_if_fail (bio);
+ wait_data->done = TRUE;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
- gbio = (GTlsBio *)bio->ptr;
-#else
- gbio = BIO_get_data (bio);
-#endif
- gbio->write_error = error;
+ return G_SOURCE_REMOVE;
}
static gboolean
-on_source_ready (GObject *pollable_stream,
- gpointer user_data)
+on_datagram_source_ready (GDatagramBased *datagram_based,
+ GIOCondition condition,
+ gpointer user_data)
{
- GMainLoop *loop = user_data;
+ WaitData *wait_data = user_data;
- g_main_loop_quit (loop);
+ wait_data->done = TRUE;
return G_SOURCE_REMOVE;
}
-void
+static gboolean
+on_timeout_source_ready (gpointer user_data)
+{
+ WaitData *wait_data = user_data;
+
+ wait_data->done = TRUE;
+ wait_data->timed_out = TRUE;
+
+ return G_SOURCE_REMOVE;
+}
+
+gboolean
g_tls_bio_wait_available (BIO *bio,
GIOCondition condition,
+ gint64 timeout,
GCancellable *cancellable)
{
GTlsBio *gbio;
- GSource *source;
+ WaitData wait_data;
+ GMainContext *ctx;
+ GSource *io_source, *timeout_source;
- g_return_if_fail (bio);
+ g_return_val_if_fail (bio, FALSE);
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
gbio = (GTlsBio *)bio->ptr;
@@ -433,21 +489,54 @@ g_tls_bio_wait_available (BIO *bio,
gbio = BIO_get_data (bio);
#endif
- g_main_context_push_thread_default (gbio->context);
+ wait_data.done = FALSE;
+ wait_data.timed_out = FALSE;
+
+ ctx = g_main_context_new ();
+ g_main_context_push_thread_default (ctx);
+
+ if (gbio->io_stream)
+ {
+ if (condition & G_IO_IN)
+ io_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM
(g_io_stream_get_input_stream (gbio->io_stream)),
+ cancellable);
+ else
+ io_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM
(g_io_stream_get_output_stream (gbio->io_stream)),
+ cancellable);
+ g_source_set_callback (io_source, (GSourceFunc)on_pollable_source_ready, &wait_data, NULL);
+ }
+ else
+ {
+ io_source = g_datagram_based_create_source (gbio->socket, condition, cancellable);
+ g_source_set_callback (io_source, (GSourceFunc)on_datagram_source_ready, &wait_data, NULL);
+ }
+ g_source_attach (io_source, ctx);
- if (condition & G_IO_IN)
- source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream
(gbio->io_stream)),
- cancellable);
+ if (timeout >= 0)
+ {
+ timeout_source = g_timeout_source_new (timeout / 1000);
+ g_source_set_callback (timeout_source, (GSourceFunc)on_timeout_source_ready, &wait_data, NULL);
+ g_source_attach (timeout_source, ctx);
+ }
else
- source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (g_io_stream_get_output_stream
(gbio->io_stream)),
- cancellable);
+ {
+ timeout_source = NULL;
+ }
+
+ while (!wait_data.done)
+ g_main_context_iteration (ctx, TRUE);
+
+ if (timeout_source)
+ {
+ g_source_destroy (timeout_source);
+ g_source_unref (timeout_source);
+ }
- g_source_set_callback (source, (GSourceFunc)on_source_ready, gbio->loop, NULL);
- g_source_attach (source, gbio->context);
+ g_source_destroy (io_source);
+ g_source_unref (io_source);
- g_main_loop_run (gbio->loop);
- g_main_context_pop_thread_default (gbio->context);
+ g_main_context_pop_thread_default (ctx);
+ g_main_context_unref (ctx);
- g_source_destroy (source);
- g_source_unref (source);
+ return !wait_data.timed_out;
}
diff --git a/tls/openssl/gtlsbio.h b/tls/openssl/gtlsbio.h
index 09dccd3..e4135d1 100644
--- a/tls/openssl/gtlsbio.h
+++ b/tls/openssl/gtlsbio.h
@@ -30,28 +30,25 @@
G_BEGIN_DECLS
-BIO *g_tls_bio_new (GIOStream *io_stream);
+BIO *g_tls_bio_new_from_iostream (GIOStream *io_stream);
+
+BIO *g_tls_bio_new_from_datagram_based (GDatagramBased *socket);
void g_tls_bio_set_read_cancellable (BIO *bio,
GCancellable *cancellable);
-void g_tls_bio_set_read_blocking (BIO *bio,
- gboolean blocking);
-
void g_tls_bio_set_read_error (BIO *bio,
GError **error);
void g_tls_bio_set_write_cancellable (BIO *bio,
GCancellable *cancellable);
-void g_tls_bio_set_write_blocking (BIO *bio,
- gboolean blocking);
-
void g_tls_bio_set_write_error (BIO *bio,
GError **error);
-void g_tls_bio_wait_available (BIO *bio,
+gboolean g_tls_bio_wait_available (BIO *bio,
GIOCondition condition,
+ gint64 timeout,
GCancellable *cancellable);
G_END_DECLS
diff --git a/tls/openssl/gtlsclientconnection-openssl.c b/tls/openssl/gtlsclientconnection-openssl.c
index 3f641a5..4bbd237 100644
--- a/tls/openssl/gtlsclientconnection-openssl.c
+++ b/tls/openssl/gtlsclientconnection-openssl.c
@@ -70,7 +70,9 @@ G_DEFINE_TYPE_WITH_CODE (GTlsClientConnectionOpenssl, g_tls_client_connection_op
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
g_tls_client_connection_openssl_initable_interface_init)
G_IMPLEMENT_INTERFACE (G_TYPE_TLS_CLIENT_CONNECTION,
-
g_tls_client_connection_openssl_client_connection_interface_init))
+
g_tls_client_connection_openssl_client_connection_interface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_DTLS_CLIENT_CONNECTION,
+ NULL));
static void
g_tls_client_connection_openssl_finalize (GObject *object)
@@ -381,7 +383,14 @@ g_tls_client_connection_openssl_initable_init (GInitable *initable,
client->session = SSL_SESSION_new ();
- client->ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
+ client->ssl_ctx = SSL_CTX_new (g_tls_connection_base_is_dtls (G_TLS_CONNECTION_BASE (client))
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L || defined (LIBRESSL_VERSION_NUMBER)
+ ? DTLS_client_method ()
+ : TLS_client_method ());
+#else
+ ? DTLSv1_client_method ()
+ : SSLv23_client_method ());
+#endif
if (!client->ssl_ctx)
{
g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
index 98bbb56..29df6b7 100644
--- a/tls/openssl/gtlsconnection-openssl.c
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -39,14 +39,32 @@
#include <glib/gi18n-lib.h>
+#define DTLS_MESSAGE_MAX_SIZE 65536
+
typedef struct _GTlsConnectionOpensslPrivate
{
BIO *bio;
+ guint8 *dtls_rx;
+ guint8 *dtls_tx;
GMutex ssl_mutex;
gboolean shutting_down;
} GTlsConnectionOpensslPrivate;
+typedef int (*GTlsOpensslIOFunc) (SSL *ssl, gpointer user_data);
+
+typedef struct _ReadRequest
+{
+ void *buffer;
+ gsize count;
+} ReadRequest;
+
+typedef struct _WriteRequest
+{
+ const void *buffer;
+ gsize count;
+} WriteRequest;
+
static void g_tls_connection_openssl_initable_iface_init (GInitableIface *iface);
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionOpenssl, g_tls_connection_openssl,
G_TYPE_TLS_CONNECTION_BASE,
@@ -62,6 +80,8 @@ g_tls_connection_openssl_finalize (GObject *object)
priv = g_tls_connection_openssl_get_instance_private (openssl);
+ g_free (priv->dtls_rx);
+ g_free (priv->dtls_tx);
g_mutex_clear (&priv->ssl_mutex);
G_OBJECT_CLASS (g_tls_connection_openssl_parent_class)->finalize (object);
@@ -241,6 +261,113 @@ end_openssl_io (GTlsConnectionOpenssl *openssl,
return G_TLS_CONNECTION_BASE_ERROR;
}
+static GTlsConnectionBaseStatus
+perform_openssl_io (GTlsConnectionOpenssl *openssl,
+ GIOCondition direction,
+ GTlsOpensslIOFunc perform_func,
+ gpointer perform_data,
+ gint64 timeout,
+ GCancellable *cancellable,
+ int *out_ret,
+ GError **error,
+ const char *err_prefix)
+{
+ GTlsConnectionBaseStatus status;
+ GTlsConnectionBase *tls;
+ GTlsConnectionOpensslPrivate *priv;
+ SSL *ssl;
+ gint64 deadline;
+ int ret;
+
+ tls = G_TLS_CONNECTION_BASE (openssl);
+ priv = g_tls_connection_openssl_get_instance_private (openssl);
+ ssl = g_tls_connection_openssl_get_ssl (openssl);
+
+ if (timeout >= 0)
+ deadline = g_get_monotonic_time () + timeout;
+ else
+ deadline = -1;
+
+ while (TRUE)
+ {
+ GIOCondition io_needed;
+ char error_str[256];
+ struct timeval tv;
+ gint64 io_timeout;
+
+ g_tls_connection_base_push_io (tls, direction, 0, cancellable);
+
+ if (g_tls_connection_base_is_dtls (tls))
+ DTLSv1_handle_timeout (ssl);
+
+ ret = perform_func (ssl, perform_data);
+
+ switch (SSL_get_error (ssl, ret))
+ {
+ case SSL_ERROR_WANT_READ:
+ io_needed = G_IO_IN;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ io_needed = G_IO_OUT;
+ break;
+ default:
+ io_needed = 0;
+ break;
+ }
+
+ ERR_error_string_n (SSL_get_error (ssl, ret), error_str,
+ sizeof (error_str));
+ status = end_openssl_io (openssl, direction, ret, TRUE, error, err_prefix,
+ error_str);
+
+ if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
+ break;
+
+ if (g_tls_connection_base_is_dtls (tls) && DTLSv1_get_timeout (ssl, &tv))
+ io_timeout = (tv.tv_sec * G_USEC_PER_SEC) + tv.tv_usec;
+ else
+ io_timeout = -1;
+
+ if (deadline != -1)
+ {
+ gint64 remaining = MAX (deadline - g_get_monotonic_time (), 0);
+
+ if (io_timeout != -1)
+ io_timeout = MIN (io_timeout, remaining);
+ else
+ io_timeout = remaining;
+ }
+
+ if (io_timeout == 0)
+ break;
+
+ g_tls_bio_wait_available (priv->bio, io_needed, io_timeout, cancellable);
+ }
+
+ if (status == G_TLS_CONNECTION_BASE_TRY_AGAIN)
+ {
+ if (timeout == 0)
+ {
+ status = G_TLS_CONNECTION_BASE_WOULD_BLOCK;
+ g_clear_error (error);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
+ "Operation would block");
+ }
+ else if (timeout > 0)
+ {
+ status = G_TLS_CONNECTION_BASE_TIMED_OUT;
+ g_clear_error (error);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+ _("Socket I/O timed out"));
+ }
+ }
+
+ if (out_ret)
+ *out_ret = ret;
+
+ return status;
+}
+
#if OPENSSL_VERSION_NUMBER >= 0x10002000L || defined (LIBRESSL_VERSION_NUMBER)
static int
_openssl_alpn_select_cb (SSL *ssl,
@@ -386,41 +513,13 @@ g_tls_connection_openssl_complete_handshake (GTlsConnectionBase *tls,
}
#endif
-#define BEGIN_OPENSSL_IO(openssl, direction, timeout, cancellable) \
- do { \
- char error_str[256]; \
- g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl), \
- direction, timeout, cancellable);
-
-#define END_OPENSSL_IO(openssl, direction, ret, timeout, status, errmsg, err) \
- ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof(error_str)); \
- status = end_openssl_io (openssl, direction, ret, timeout == -1, err, errmsg, error_str); \
- } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
-
-static GTlsConnectionBaseStatus
-g_tls_connection_openssl_handshake_thread_request_rehandshake (GTlsConnectionBase *tls,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
+static int
+perform_rehandshake (SSL *ssl,
+ gpointer user_data)
{
- GTlsConnectionOpenssl *openssl;
- GTlsConnectionBaseStatus status;
- SSL *ssl;
+ GTlsConnectionBase *tls = user_data;
int ret = 1; /* always look on the bright side of life */
- /* On a client-side connection, SSL_renegotiate() itself will start
- * a rehandshake, so we only need to do something special here for
- * server-side connections.
- */
- if (!G_IS_TLS_SERVER_CONNECTION (tls))
- return G_TLS_CONNECTION_BASE_OK;
-
- openssl = G_TLS_CONNECTION_OPENSSL (tls);
-
- ssl = g_tls_connection_openssl_get_ssl (openssl);
-
- BEGIN_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, timeout, cancellable);
-
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
if (SSL_version(ssl) >= TLS1_3_VERSION)
ret = SSL_key_update (ssl, SSL_KEY_UPDATE_REQUESTED);
@@ -433,10 +532,25 @@ g_tls_connection_openssl_handshake_thread_request_rehandshake (GTlsConnectionBas
ret = SSL_renegotiate (ssl);
#endif
- END_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, ret, timeout, status,
- _("Error performing TLS handshake"), error);
+ return ret;
+}
- return status;
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_handshake_thread_request_rehandshake (GTlsConnectionBase *tls,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ /* On a client-side connection, SSL_renegotiate() itself will start
+ * a rehandshake, so we only need to do something special here for
+ * server-side connections.
+ */
+ if (!G_IS_TLS_SERVER_CONNECTION (tls))
+ return G_TLS_CONNECTION_BASE_OK;
+
+ return perform_openssl_io (G_TLS_CONNECTION_OPENSSL (tls), G_IO_IN | G_IO_OUT,
+ perform_rehandshake, tls, timeout, cancellable,
+ NULL, error, _("Error performing TLS handshake"));
}
static GTlsCertificate *
@@ -638,21 +752,18 @@ g_tls_connection_openssl_handshake_thread_handshake (GTlsConnectionBase *tls,
GCancellable *cancellable,
GError **error)
{
- GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
GTlsConnectionBaseStatus status;
- SSL *ssl;
int ret;
- ssl = g_tls_connection_openssl_get_ssl (openssl);
-
- BEGIN_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, timeout, cancellable);
- ret = SSL_do_handshake (ssl);
- END_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, ret, timeout, status,
- _("Error performing TLS handshake"), error);
+ status = perform_openssl_io (G_TLS_CONNECTION_OPENSSL (tls),
+ G_IO_IN | G_IO_OUT,
+ (GTlsOpensslIOFunc) SSL_do_handshake,
+ NULL, timeout, cancellable, &ret, error,
+ _("Error reading data from TLS socket"));
if (ret > 0)
{
- if (!g_tls_connection_base_handshake_thread_verify_certificate (G_TLS_CONNECTION_BASE (openssl)))
+ if (!g_tls_connection_base_handshake_thread_verify_certificate (tls))
{
g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
_("Unacceptable TLS certificate"));
@@ -678,14 +789,10 @@ g_tls_connection_openssl_push_io (GTlsConnectionBase *tls,
G_TLS_CONNECTION_BASE_CLASS (g_tls_connection_openssl_parent_class)->push_io (tls, direction,
timeout, cancellable);
- /* FIXME: need to support timeout > 0
- * This will require changes in GTlsBio */
-
if (direction & G_IO_IN)
{
error = g_tls_connection_base_get_read_error (tls);
g_tls_bio_set_read_cancellable (priv->bio, cancellable);
- g_tls_bio_set_read_blocking (priv->bio, timeout == -1);
g_clear_error (error);
g_tls_bio_set_read_error (priv->bio, error);
}
@@ -694,7 +801,6 @@ g_tls_connection_openssl_push_io (GTlsConnectionBase *tls,
{
error = g_tls_connection_base_get_write_error (tls);
g_tls_bio_set_write_cancellable (priv->bio, cancellable);
- g_tls_bio_set_write_blocking (priv->bio, timeout == -1);
g_clear_error (error);
g_tls_bio_set_write_error (priv->bio, error);
}
@@ -725,6 +831,15 @@ g_tls_connection_openssl_pop_io (GTlsConnectionBase *tls,
success, error);
}
+static int
+perform_read (SSL *ssl,
+ gpointer user_data)
+{
+ ReadRequest *req = user_data;
+
+ return SSL_read (ssl, req->buffer, req->count);
+}
+
static GTlsConnectionBaseStatus
g_tls_connection_openssl_read (GTlsConnectionBase *tls,
void *buffer,
@@ -733,45 +848,77 @@ g_tls_connection_openssl_read (GTlsConnectionBase *tls,
gssize *nread,
GCancellable *cancellable,
GError **error)
+{
+ GTlsConnectionBaseStatus status;
+ ReadRequest req = { buffer, count };
+ int ret;
+
+ status = perform_openssl_io (G_TLS_CONNECTION_OPENSSL (tls), G_IO_IN,
+ perform_read, &req, timeout, cancellable, &ret,
+ error, _("Error reading data from TLS socket"));
+
+ *nread = MAX (ret, 0);
+ return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_read_message (GTlsConnectionBase *tls,
+ GInputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error)
{
GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
GTlsConnectionOpensslPrivate *priv;
GTlsConnectionBaseStatus status;
- SSL *ssl;
- gssize ret;
+ gssize bytes_read;
+ gsize bytes_copied, bytes_remaining;
+ guint i;
+
+ *nread = 0;
priv = g_tls_connection_openssl_get_instance_private (openssl);
- ssl = g_tls_connection_openssl_get_ssl (openssl);
+ if (!priv->dtls_rx)
+ priv->dtls_rx = g_malloc (DTLS_MESSAGE_MAX_SIZE);
- /* FIXME: revert back to use BEGIN/END_OPENSSL_IO once we move all the ssl
- * operations into a worker thread
- */
- while (TRUE)
- {
- char error_str[256];
+ status = g_tls_connection_openssl_read (tls, priv->dtls_rx,
+ DTLS_MESSAGE_MAX_SIZE, timeout,
+ &bytes_read, cancellable, error);
+ if (status != G_TLS_CONNECTION_BASE_OK)
+ return status;
- /* We want to always be non blocking here to avoid deadlocks */
- g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),
- G_IO_IN, 0, cancellable);
+ bytes_copied = 0;
+ bytes_remaining = bytes_read;
+ for (i = 0; i < num_vectors && bytes_remaining > 0; i++)
+ {
+ GInputVector *vector = &vectors[i];
+ gsize n;
- ret = SSL_read (ssl, buffer, count);
+ n = MIN (bytes_remaining, vector->size);
- ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof (error_str));
- status = end_openssl_io (openssl, G_IO_IN, ret, timeout == -1, error,
- _("Error reading data from TLS socket"), error_str);
+ memcpy (vector->buffer, priv->dtls_rx + bytes_copied, n);
- if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
- break;
-
- /* Wait for the socket to be available again to avoid an infinite loop */
- g_tls_bio_wait_available (priv->bio, G_IO_IN, cancellable);
+ bytes_copied += n;
+ bytes_remaining -= n;
}
- *nread = MAX (ret, 0);
+ *nread = bytes_copied;
+
return status;
}
+static int
+perform_write (SSL *ssl,
+ gpointer user_data)
+{
+ WriteRequest *req = user_data;
+
+ return SSL_write (ssl, req->buffer, req->count);
+}
+
static GTlsConnectionBaseStatus
g_tls_connection_openssl_write (GTlsConnectionBase *tls,
const void *buffer,
@@ -780,40 +927,55 @@ g_tls_connection_openssl_write (GTlsConnectionBase *tls,
gssize *nwrote,
GCancellable *cancellable,
GError **error)
+{
+ GTlsConnectionBaseStatus status;
+ WriteRequest req = { buffer, count };
+ int ret;
+
+ status = perform_openssl_io (G_TLS_CONNECTION_OPENSSL (tls), G_IO_OUT,
+ perform_write, &req, timeout, cancellable, &ret,
+ error, _("Error writing data to TLS socket"));
+
+ *nwrote = MAX (ret, 0);
+ return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_write_message (GTlsConnectionBase *tls,
+ GOutputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error)
{
GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
GTlsConnectionOpensslPrivate *priv;
- GTlsConnectionBaseStatus status;
- SSL *ssl;
- gssize ret;
+ gsize bytes_copied, bytes_available;
+ guint i;
priv = g_tls_connection_openssl_get_instance_private (openssl);
- ssl = g_tls_connection_openssl_get_ssl (openssl);
+ if (!priv->dtls_tx)
+ priv->dtls_tx = g_malloc (DTLS_MESSAGE_MAX_SIZE);
- while (TRUE)
+ bytes_copied = 0;
+ bytes_available = DTLS_MESSAGE_MAX_SIZE;
+ for (i = 0; i < num_vectors && bytes_available > 0; i++)
{
- char error_str[256];
-
- /* We want to always be non blocking here to avoid deadlocks */
- g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),
- G_IO_OUT, 0, cancellable);
+ GOutputVector *vector = &vectors[i];
+ gsize n;
- ret = SSL_write (ssl, buffer, count);
+ n = MIN (vector->size, bytes_available);
- ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof (error_str));
- status = end_openssl_io (openssl, G_IO_OUT, ret, timeout == -1, error,
- _("Error writing data to TLS socket"), error_str);
+ memcpy (priv->dtls_tx + bytes_copied, vector->buffer, n);
- if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
- break;
-
- /* Wait for the socket to be available again to avoid an infinite loop */
- g_tls_bio_wait_available (priv->bio, G_IO_OUT, cancellable);
+ bytes_copied += n;
+ bytes_available -= n;
}
- *nwrote = MAX (ret, 0);
- return status;
+ return g_tls_connection_openssl_write (tls, priv->dtls_tx, bytes_copied,
+ timeout, nwrote, cancellable, error);
}
static GTlsConnectionBaseStatus
@@ -824,25 +986,16 @@ g_tls_connection_openssl_close (GTlsConnectionBase *tls,
{
GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
GTlsConnectionOpensslPrivate *priv;
- GTlsConnectionBaseStatus status;
- SSL *ssl;
- int ret;
- ssl = g_tls_connection_openssl_get_ssl (openssl);
priv = g_tls_connection_openssl_get_instance_private (openssl);
priv->shutting_down = TRUE;
- BEGIN_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, timeout, cancellable);
- ret = SSL_shutdown (ssl);
- /* Note it is documented that getting 0 is correct when shutting down since
- * it means it will close the write direction
- */
- ret = ret == 0 ? 1 : ret;
- END_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, ret, timeout, status,
- _("Error performing TLS close"), error);
-
- return status;
+ return perform_openssl_io (G_TLS_CONNECTION_OPENSSL (tls),
+ G_IO_IN | G_IO_OUT,
+ (GTlsOpensslIOFunc) SSL_shutdown,
+ NULL, timeout, cancellable, NULL, error,
+ _("Error performing TLS close"));
}
static void
@@ -865,7 +1018,9 @@ g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
base_class->push_io = g_tls_connection_openssl_push_io;
base_class->pop_io = g_tls_connection_openssl_pop_io;
base_class->read_fn = g_tls_connection_openssl_read;
+ base_class->read_message_fn = g_tls_connection_openssl_read_message;
base_class->write_fn = g_tls_connection_openssl_write;
+ base_class->write_message_fn = g_tls_connection_openssl_write_message;
base_class->close_fn = g_tls_connection_openssl_close;
}
@@ -880,12 +1035,16 @@ g_tls_connection_openssl_initable_init (GInitable *initable,
GTlsConnectionOpensslPrivate *priv;
GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (initable);
GIOStream *base_io_stream;
+ GDatagramBased *base_socket;
SSL *ssl;
g_object_get (tls,
"base-io-stream", &base_io_stream,
+ "base-socket", &base_socket,
NULL);
- g_return_val_if_fail (base_io_stream, FALSE);
+
+ /* Ensure we are in TLS mode or DTLS mode. */
+ g_return_val_if_fail (!!base_io_stream != !!base_socket, FALSE);
priv = g_tls_connection_openssl_get_instance_private (openssl);
@@ -897,11 +1056,15 @@ g_tls_connection_openssl_initable_init (GInitable *initable,
}
SSL_set_ex_data (ssl, data_index, openssl);
- priv->bio = g_tls_bio_new (base_io_stream);
+ if (base_io_stream)
+ priv->bio = g_tls_bio_new_from_iostream (base_io_stream);
+ else
+ priv->bio = g_tls_bio_new_from_datagram_based (base_socket);
SSL_set_bio (ssl, priv->bio, priv->bio);
- g_object_unref (base_io_stream);
+ g_clear_object (&base_io_stream);
+ g_clear_object (&base_socket);
return TRUE;
}
diff --git a/tls/openssl/gtlsserverconnection-openssl.c b/tls/openssl/gtlsserverconnection-openssl.c
index 4ebc4c7..69b65f2 100644
--- a/tls/openssl/gtlsserverconnection-openssl.c
+++ b/tls/openssl/gtlsserverconnection-openssl.c
@@ -57,7 +57,9 @@ G_DEFINE_TYPE_WITH_CODE (GTlsServerConnectionOpenssl, g_tls_server_connection_op
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
g_tls_server_connection_openssl_initable_interface_init)
G_IMPLEMENT_INTERFACE (G_TYPE_TLS_SERVER_CONNECTION,
-
g_tls_server_connection_openssl_server_connection_interface_init))
+
g_tls_server_connection_openssl_server_connection_interface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_DTLS_SERVER_CONNECTION,
+ NULL));
static void
g_tls_server_connection_openssl_finalize (GObject *object)
@@ -417,7 +419,14 @@ g_tls_server_connection_openssl_initable_init (GInitable *initable,
server->session = SSL_SESSION_new ();
- server->ssl_ctx = SSL_CTX_new (SSLv23_server_method ());
+ server->ssl_ctx = SSL_CTX_new (g_tls_connection_base_is_dtls (G_TLS_CONNECTION_BASE (server))
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L || defined (LIBRESSL_VERSION_NUMBER)
+ ? DTLS_server_method ()
+ : TLS_server_method ());
+#else
+ ? DTLSv1_server_method ()
+ : SSLv23_server_method ());
+#endif
if (!server->ssl_ctx)
{
g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
diff --git a/tls/openssl/openssl-include.h b/tls/openssl/openssl-include.h
index 8f57042..7d394d5 100644
--- a/tls/openssl/openssl-include.h
+++ b/tls/openssl/openssl-include.h
@@ -26,21 +26,22 @@
#pragma once
-#include "glib.h"
-
/* Due to name clashes between Windows and openssl headers we have to
* make sure windows.h is included before openssl and that we undef the
- * clashing macros.
+ * clashing macros. We also need `struct timeval` for DTLSv1_get_timeout(),
+ * and the following header also covers it for Windows.
*/
+#include <gio/gnetworking.h>
#ifdef G_OS_WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
/* These are defined by the Windows headers, but clash with openssl */
#undef X509_NAME
#undef X509_CERT_PAIR
#undef X509_EXTENSIONS
#undef OCSP_REQUEST
#undef OCSP_RESPONSE
+#else
+/* Need `struct timeval` for DTLSv1_get_timeout() */
+#include <sys/time.h>
#endif
#include <openssl/ssl.h>
diff --git a/tls/tests/dtls-connection.c b/tls/tests/dtls-connection.c
index 4f3cd3c..78b2cee 100644
--- a/tls/tests/dtls-connection.c
+++ b/tls/tests/dtls-connection.c
@@ -31,7 +31,9 @@
#include "mock-interaction.h"
#include <gio/gio.h>
+#ifdef BACKEND_IS_GNUTLS
#include <gnutls/gnutls.h>
+#endif
#include <sys/types.h>
#include <string.h>
diff --git a/tls/tests/meson.build b/tls/tests/meson.build
index a256317..e9c7d8c 100644
--- a/tls/tests/meson.build
+++ b/tls/tests/meson.build
@@ -51,11 +51,6 @@ test_programs = [
foreach backend: backends
foreach program: test_programs
- # OpenSSL does not support DTLS yet.
- if program[0] == 'dtls-connection' and backend == 'openssl'
- continue
- endif
-
program_name = program[0] + '-' + backend
test_conf = configuration_data()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]