[glib-networking] openssl: update SSL structure when certificate is changed



commit 3f5c05e381b5c180f912a2ba2a6aae66a17de970
Author: Fredrik Ternerot <fredrikt axis com>
Date:   Mon Feb 11 15:36:12 2019 +0100

    openssl: update SSL structure when certificate is changed
    
    Set server certificate on SSL structure every time the certificate is
    changed. Previously the certificate was set during initialization only.
    
    This solves the problem when GTlsServerConnection is created without a
    certificate and g_tls_connection_set_certificate() is used to supply the
    certificate after creation time.
    
    Also modify the connection unittest to cover this use case by setting
    the server certificate after initialization when the default certificate
    is used.
    
    Fixes #55

 tls/openssl/gtlsserverconnection-openssl.c | 134 ++++++++++++++++++-----------
 tls/tests/connection.c                     |  19 ++--
 2 files changed, 91 insertions(+), 62 deletions(-)
---
diff --git a/tls/openssl/gtlsserverconnection-openssl.c b/tls/openssl/gtlsserverconnection-openssl.c
index 60f9659..ff7419e 100644
--- a/tls/openssl/gtlsserverconnection-openssl.c
+++ b/tls/openssl/gtlsserverconnection-openssl.c
@@ -74,6 +74,67 @@ g_tls_server_connection_openssl_finalize (GObject *object)
   G_OBJECT_CLASS (g_tls_server_connection_openssl_parent_class)->finalize (object);
 }
 
+static gboolean
+ssl_set_certificate (SSL              *ssl,
+                     GTlsCertificate  *cert,
+                     GError          **error)
+{
+  EVP_PKEY *key;
+  X509 *x;
+  GTlsCertificate *issuer;
+
+  key = g_tls_certificate_openssl_get_key (G_TLS_CERTIFICATE_OPENSSL (cert));
+
+  if (key == NULL)
+    {
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                           _("Certificate has no private key"));
+      return FALSE;
+    }
+
+  /* Note, order is important. If a certificate has been set previously,
+   * OpenSSL requires that the new certificate is set _before_ the new
+   * private key is set. */
+  x = g_tls_certificate_openssl_get_cert (G_TLS_CERTIFICATE_OPENSSL (cert));
+  if (SSL_use_certificate (ssl, x) <= 0)
+    {
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                   _("There is a problem with the certificate: %s"),
+                   ERR_error_string (ERR_get_error (), NULL));
+      return FALSE;
+    }
+
+  if (SSL_use_PrivateKey (ssl, key) <= 0)
+    {
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                   _("There is a problem with the certificate private key: %s"),
+                   ERR_error_string (ERR_get_error (), NULL));
+      return FALSE;
+    }
+
+  if (SSL_clear_chain_certs (ssl) == 0)
+    g_warning ("There was a problem clearing the chain certificates: %s",
+               ERR_error_string (ERR_get_error (), NULL));
+
+  /* Add all the issuers to create the full certificate chain */
+  for (issuer = g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (cert));
+       issuer != NULL;
+       issuer = g_tls_certificate_get_issuer (issuer))
+    {
+      X509 *issuer_x;
+
+      /* Be careful here and duplicate the certificate since the context
+       * will take the ownership
+       */
+      issuer_x = X509_dup (g_tls_certificate_openssl_get_cert (G_TLS_CERTIFICATE_OPENSSL (issuer)));
+      if (SSL_add0_chain_cert (ssl, issuer_x) == 0)
+        g_warning ("There was a problem adding the chain certificate: %s",
+                   ERR_error_string (ERR_get_error (), NULL));
+    }
+
+  return TRUE;
+}
+
 static void
 g_tls_server_connection_openssl_get_property (GObject    *object,
                                               guint       prop_id,
@@ -90,7 +151,7 @@ g_tls_server_connection_openssl_get_property (GObject    *object,
     case PROP_AUTHENTICATION_MODE:
       g_value_set_enum (value, priv->authentication_mode);
       break;
-      
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -168,6 +229,21 @@ g_tls_server_connection_openssl_get_ssl (GTlsConnectionOpenssl *connection)
   return priv->ssl;
 }
 
+static void
+on_certificate_changed (GObject    *object,
+                        GParamSpec *spec,
+                        gpointer    user_data)
+{
+  SSL *ssl;
+  GTlsCertificate *cert;
+
+  ssl = g_tls_server_connection_openssl_get_ssl (G_TLS_CONNECTION_OPENSSL (object));
+  cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (object));
+
+  if (ssl && cert)
+    ssl_set_certificate (ssl, cert, NULL);
+}
+
 static void
 g_tls_server_connection_openssl_class_init (GTlsServerConnectionOpensslClass *klass)
 {
@@ -313,56 +389,6 @@ g_tls_server_connection_openssl_initable_init (GInitable       *initable,
 
   SSL_CTX_set_options (priv->ssl_ctx, options);
 
-  cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (initable));
-  if (cert != NULL)
-    {
-      EVP_PKEY *key;
-      X509 *x;
-      GTlsCertificate *issuer;
-
-      key = g_tls_certificate_openssl_get_key (G_TLS_CERTIFICATE_OPENSSL (cert));
-
-      if (key == NULL)
-        {
-          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
-                               _("Certificate has no private key"));
-          return FALSE;
-        }
-
-      if (SSL_CTX_use_PrivateKey (priv->ssl_ctx, key) <= 0)
-        {
-          g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
-                       _("There is a problem with the certificate private key: %s"),
-                       ERR_error_string (ERR_get_error (), NULL));
-          return FALSE;
-        }
-
-      x = g_tls_certificate_openssl_get_cert (G_TLS_CERTIFICATE_OPENSSL (cert));
-      if (SSL_CTX_use_certificate (priv->ssl_ctx, x) <= 0)
-        {
-          g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
-                       _("There is a problem with the certificate: %s"),
-                       ERR_error_string (ERR_get_error (), NULL));
-          return FALSE;
-        }
-
-      /* Add all the issuers to create the full certificate chain */
-      for (issuer = g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (cert));
-           issuer != NULL;
-           issuer = g_tls_certificate_get_issuer (issuer))
-        {
-          X509 *issuer_x;
-
-          /* Be careful here and duplicate the certificate since the context
-           * will take the ownership
-           */
-          issuer_x = X509_dup (g_tls_certificate_openssl_get_cert (G_TLS_CERTIFICATE_OPENSSL (issuer)));
-          if (!SSL_CTX_add_extra_chain_cert (priv->ssl_ctx, issuer_x))
-            g_warning ("There was a problem adding the extra chain certificate: %s",
-                       ERR_error_string (ERR_get_error (), NULL));
-        }
-    }
-
   SSL_CTX_add_session (priv->ssl_ctx, priv->session);
 
 #ifdef SSL_CTX_set1_sigalgs_list
@@ -401,12 +427,18 @@ g_tls_server_connection_openssl_initable_init (GInitable       *initable,
       return FALSE;
     }
 
+  cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (initable));
+  if (cert != NULL && !ssl_set_certificate (priv->ssl, cert, error))
+    return FALSE;
+
   SSL_set_accept_state (priv->ssl);
 
   if (!g_tls_server_connection_openssl_parent_initable_iface->
       init (initable, cancellable, error))
     return FALSE;
 
+  g_signal_connect (server, "notify::certificate", G_CALLBACK (on_certificate_changed), NULL);
+
   return TRUE;
 }
 
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index c64dd35..d82d1ba 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -286,22 +286,19 @@ on_incoming_connection (GSocketService     *service,
   GTlsCertificate *cert;
   GError *error = NULL;
 
-  if (test->server_certificate)
-    {
-      cert = g_object_ref (test->server_certificate);
-    }
-  else
+  g_assert_null (test->server_connection);
+  test->server_connection = g_tls_server_connection_new (G_IO_STREAM (connection),
+                                                         test->server_certificate, &error);
+  g_assert_no_error (error);
+
+  if (!test->server_certificate)
     {
       cert = g_tls_certificate_new_from_file (tls_test_file_path ("server-and-key.pem"), &error);
       g_assert_no_error (error);
+      g_tls_connection_set_certificate ((GTlsConnection *)test->server_connection, cert);
+      g_object_unref (cert);
     }
 
-  g_assert_null (test->server_connection);
-  test->server_connection = g_tls_server_connection_new (G_IO_STREAM (connection),
-                                                         cert, &error);
-  g_assert_no_error (error);
-  g_object_unref (cert);
-
   g_object_set (test->server_connection, "authentication-mode", test->auth_mode, NULL);
   g_signal_connect (test->server_connection, "accept-certificate",
                     G_CALLBACK (on_accept_certificate), test);


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