[glib-networking] Implement ALPN handling API for OpenSSL backend



commit bd1a6db1aaec3271668c7b51cf78395e8dd3ca72
Author: Ruslan N. Marchenko <me ruff mobi>
Date:   Sat Jun 20 08:54:47 2020 +0200

    Implement ALPN handling API for OpenSSL backend
    
     * Implement prepare-handshake to push advertised-protocols to SSL for client
     * Same vfunc will setup server negotiation callback to compare client's
       proposal with locally set advertised-protocols property
     * Implement complete-handshake vfunc which returns negotiated protocol
     * Re-enable ALPN tests for OpenSSL backend

 tls/openssl/gtlsconnection-openssl.c | 146 +++++++++++++++++++++++++++++++++++
 tls/tests/connection.c               |   5 --
 2 files changed, 146 insertions(+), 5 deletions(-)
---
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
index c8e0f5b..a05c81a 100644
--- a/tls/openssl/gtlsconnection-openssl.c
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -35,6 +35,7 @@
 #include "gtlscertificate-openssl.h"
 #include "gtlsdatabase-openssl.h"
 #include "gtlsbio.h"
+#include "gtlslog.h"
 
 #include <glib/gi18n-lib.h>
 
@@ -212,6 +213,149 @@ end_openssl_io (GTlsConnectionOpenssl  *openssl,
   return G_TLS_CONNECTION_BASE_ERROR;
 }
 
+static int
+_openssl_alpn_select_cb (SSL                  *ssl,
+                         const unsigned char **out,
+                         unsigned char        *outlen,
+                         const unsigned char  *in,
+                         unsigned int          inlen,
+                         void                 *arg)
+{
+  GTlsConnectionBase *tls = arg;
+  int ret = SSL_TLSEXT_ERR_NOACK;
+  gchar **advertised_protocols = NULL;
+  gchar *logbuf;
+
+  logbuf = g_strndup ((const gchar *)in, inlen);
+  g_tls_log_debug (tls, "ALPN their protocols: %s", logbuf);
+  g_free (logbuf);
+
+  g_object_get (G_OBJECT (tls),
+                "advertised-protocols", &advertised_protocols,
+                NULL);
+
+  if (!advertised_protocols)
+    return ret;
+
+  if (g_strv_length (advertised_protocols) > 0)
+    {
+      GByteArray *protocols = g_byte_array_new ();
+      int i;
+      guint8 slen = 0;
+      guint8 *spd = NULL;
+
+      for (i = 0; advertised_protocols[i]; i++)
+        {
+          guint8 len = strlen (advertised_protocols[i]);
+          g_byte_array_append (protocols, &len, 1);
+          g_byte_array_append (protocols,
+                               (guint8 *)advertised_protocols[i],
+                               len);
+        }
+      logbuf = g_strndup ((const gchar *)protocols->data, protocols->len);
+      g_tls_log_debug (tls, "ALPN our protocols: %s", logbuf);
+      g_free (logbuf);
+
+      /* pointer to memory inside in[0..inlen] is returned on success
+       * pointer to protocols->data is returned on failure */
+      ret = SSL_select_next_proto (&spd, &slen,
+                                   in, inlen,
+                                   protocols->data, protocols->len);
+      if (ret == OPENSSL_NPN_NEGOTIATED)
+        {
+          logbuf = g_strndup ((const gchar *)spd, slen);
+          g_tls_log_debug (tls, "ALPN selected protocol %s", logbuf);
+          g_free (logbuf);
+
+          ret = SSL_TLSEXT_ERR_OK;
+          *out = spd;
+          *outlen = slen;
+        }
+      else
+        {
+          g_tls_log_debug (tls, "ALPN no matching protocol");
+          ret = SSL_TLSEXT_ERR_NOACK;
+        }
+
+      g_byte_array_unref (protocols);
+    }
+
+  g_strfreev (advertised_protocols);
+  return ret;
+}
+
+static void
+g_tls_connection_openssl_prepare_handshake (GTlsConnectionBase  *tls,
+                                            gchar              **advertised_protocols)
+{
+  SSL *ssl;
+
+  if (!advertised_protocols)
+    return;
+
+  ssl = g_tls_connection_openssl_get_ssl (G_TLS_CONNECTION_OPENSSL (tls));
+
+  if (G_IS_TLS_SERVER_CONNECTION (tls))
+    {
+      SSL_CTX *ctx = SSL_get_SSL_CTX(ssl);
+
+      g_tls_log_debug (tls, "Setting ALPN Callback on %p", ctx);
+      SSL_CTX_set_alpn_select_cb (ctx, _openssl_alpn_select_cb, tls);
+
+      return;
+    }
+
+  if (g_strv_length (advertised_protocols) > 0)
+    {
+      GByteArray *protocols = g_byte_array_new ();
+      int ret, i;
+
+      for (i = 0; advertised_protocols[i]; i++)
+        {
+          guint8 len = strlen (advertised_protocols[i]);
+          g_byte_array_append (protocols, &len, 1);
+          g_byte_array_append (protocols, (guint8 *)advertised_protocols[i], len);
+        }
+      ret = SSL_set_alpn_protos (ssl, protocols->data, protocols->len);
+      if (ret)
+        g_tls_log_debug (tls, "Error setting ALPN protocols: %d", ret);
+      else
+        {
+          gchar *logbuf = g_strndup ((const gchar *)protocols->data, protocols->len);
+
+          g_tls_log_debug (tls, "Setting ALPN protocols to %s", logbuf);
+          g_free (logbuf);
+        }
+      g_byte_array_unref (protocols);
+    }
+}
+
+static void
+g_tls_connection_openssl_complete_handshake (GTlsConnectionBase  *tls,
+                                             gboolean             handshake_succeeded,
+                                             gchar              **negotiated_protocol,
+                                             GError             **error)
+{
+  SSL *ssl;
+  unsigned int len = 0;
+  const unsigned char *data = NULL;
+
+  if (!handshake_succeeded)
+    return;
+
+  ssl = g_tls_connection_openssl_get_ssl (G_TLS_CONNECTION_OPENSSL (tls));
+
+  SSL_get0_alpn_selected (ssl, &data, &len);
+
+  g_tls_log_debug (tls, "negotiated ALPN protocols: [%d]%p", len, data);
+
+  if (data && len > 0)
+    {
+      g_assert (!*negotiated_protocol);
+      *negotiated_protocol = g_strndup ((gchar *)data, len);
+    }
+}
+
 #define BEGIN_OPENSSL_IO(openssl, direction, timeout, cancellable)          \
   do {                                                                      \
     char error_str[256];                                                    \
@@ -503,6 +647,8 @@ g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
 
   object_class->finalize                                 = g_tls_connection_openssl_finalize;
 
+  base_class->prepare_handshake                          = g_tls_connection_openssl_prepare_handshake;
+  base_class->complete_handshake                         = g_tls_connection_openssl_complete_handshake;
   base_class->handshake_thread_safe_renegotiation_status = 
g_tls_connection_openssl_handshake_thread_safe_renegotiation_status;
   base_class->handshake_thread_request_rehandshake       = 
g_tls_connection_openssl_handshake_thread_request_rehandshake;
   base_class->handshake_thread_handshake                 = 
g_tls_connection_openssl_handshake_thread_handshake;
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 7f119a9..70450d8 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -2267,11 +2267,6 @@ test_alpn (TestConnection *test,
   GIOStream *connection;
   GError *error = NULL;
 
-#ifdef BACKEND_IS_OPENSSL
-  g_test_skip ("this is not yet passing with openssl");
-  return;
-#endif
-
   test->server_protocols = server_protocols;
 
   test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]