[gnome-control-center/wip/feborges/new-remote-desktop-dialog: 6/14] sharing: Add TLS certificate generation implementation




commit 31efc9e16121142fb0c730703e6ad7cf1bedbcc2
Author: Jonas Ådahl <jadahl gmail com>
Date:   Mon Feb 21 18:36:33 2022 +0100

    sharing: Add TLS certificate generation implementation
    
    This copies TLS certificate generation implementation from
    https://gitlab.gnome.org/chergert/bonsai. No changes were made other
    than ones required for making it build as part of gnome-control-center's
    sharing panel. It's currently not suitable to depend on libbonsai, as it
    will likely see big changes before seeing a release.

 panels/sharing/cc-tls-certificate.c | 511 ++++++++++++++++++++++++++++++++++++
 panels/sharing/cc-tls-certificate.h |  63 +++++
 panels/sharing/meson.build          |   8 +-
 3 files changed, 581 insertions(+), 1 deletion(-)
---
diff --git a/panels/sharing/cc-tls-certificate.c b/panels/sharing/cc-tls-certificate.c
new file mode 100644
index 000000000..b2992c023
--- /dev/null
+++ b/panels/sharing/cc-tls-certificate.c
@@ -0,0 +1,511 @@
+/* cc-tls-certificate.c
+ *
+ * Copyright 2018 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <glib/gstdio.h>
+#include <gnutls/x509.h>
+
+#include "cc-tls-certificate.h"
+
+#define DEFAULT_KEY_SIZE   4096
+#define DEFAULT_EXPIRATION (60L*60L*24L*5L*365L)
+
+static void
+_gnutls_datum_clear (gnutls_datum_t *datum)
+{
+  if (datum->data != NULL)
+    gnutls_free (datum->data);
+}
+
+static void
+_gnutls_crt_free (gnutls_x509_crt_t *cert)
+{
+  if (cert != NULL)
+    gnutls_x509_crt_deinit (*cert);
+}
+
+static void
+_gnutls_privkey_free (gnutls_x509_privkey_t *privkey)
+{
+  if (privkey != NULL)
+    gnutls_x509_privkey_deinit (*privkey);
+}
+
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (gnutls_datum_t, _gnutls_datum_clear)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (gnutls_x509_crt_t, _gnutls_crt_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (gnutls_x509_privkey_t, _gnutls_privkey_free)
+
+typedef struct
+{
+  gchar *public_key_path;
+  gchar *private_key_path;
+  gchar *c;
+  gchar *cn;
+} GenerateData;
+
+static void
+generate_data_free (GenerateData *data)
+{
+  g_clear_pointer (&data->public_key_path, g_free);
+  g_clear_pointer (&data->private_key_path, g_free);
+  g_clear_pointer (&data->c, g_free);
+  g_clear_pointer (&data->cn, g_free);
+  g_slice_free (GenerateData, data);
+}
+
+static gboolean
+make_directory_parent (const gchar  *path,
+                       GError      **error)
+{
+  g_autofree gchar *dir = NULL;
+
+  g_assert (path != NULL);
+  g_assert (error != NULL);
+
+  dir = g_path_get_dirname (path);
+
+  if (g_mkdir_with_parents (dir, 0750) == -1)
+    {
+      g_set_error_literal (error,
+                           G_IO_ERROR,
+                           g_io_error_from_errno (errno),
+                           g_strerror (errno));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+bonsai_tls_certificate_generate_worker (GTask        *task,
+                                        gpointer      source_object,
+                                        gpointer      task_data,
+                                        GCancellable *cancellable)
+{
+  GenerateData *data = task_data;
+  g_autoptr(GTlsCertificate) certificate = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(gnutls_x509_crt_t) certptr = NULL;
+  g_autoptr(gnutls_x509_privkey_t) privkeyptr = NULL;
+  g_auto(gnutls_datum_t) pubkey_data = {0};
+  g_auto(gnutls_datum_t) privkey_data = {0};
+  g_autofree char *dn = NULL;
+  gnutls_x509_privkey_t privkey;
+  gnutls_x509_crt_t cert;
+  guint32 serial = 1;
+  int gtlsret = 0;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (source_object == NULL);
+  g_assert (data != NULL);
+  g_assert (data->public_key_path != NULL);
+  g_assert (data->private_key_path != NULL);
+  g_assert (data->c != NULL);
+  g_assert (data->cn != NULL);
+
+  if (!make_directory_parent (data->public_key_path, &error) ||
+      !make_directory_parent (data->private_key_path, &error))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  /*
+   * From the GnuTLS documentation:
+   *
+   * To be consistent with the X.509/PKIX specifications the provided serial
+   * should be a big-endian positive number (i.e. it's leftmost bit should be
+   * zero).
+   */
+  serial = GUINT32_TO_BE (serial);
+
+#define HANDLE_FAILURE(x)            \
+  G_STMT_START {                     \
+    gtlsret = x;                     \
+    if (gtlsret != GNUTLS_E_SUCCESS) \
+      goto failure;                  \
+  } G_STMT_END
+
+  HANDLE_FAILURE (gnutls_x509_crt_init (&cert));
+  certptr = &cert;
+  HANDLE_FAILURE (gnutls_x509_crt_set_version (cert, 3));
+  HANDLE_FAILURE (gnutls_x509_crt_set_activation_time (cert, time (NULL)));
+  dn = g_strdup_printf ("C=%s,CN=%s", data->c, data->cn);
+  HANDLE_FAILURE (gnutls_x509_crt_set_dn (cert, dn, NULL));
+  HANDLE_FAILURE (gnutls_x509_crt_set_serial (cert, &serial, sizeof serial));
+  /* 5 years. We'll figure out key rotation in that time... */
+  HANDLE_FAILURE (gnutls_x509_crt_set_expiration_time (cert, time (NULL) + DEFAULT_EXPIRATION));
+  HANDLE_FAILURE (gnutls_x509_privkey_init (&privkey));
+  privkeyptr = &privkey;
+  HANDLE_FAILURE (gnutls_x509_privkey_generate (privkey, GNUTLS_PK_RSA, DEFAULT_KEY_SIZE, 0));
+  HANDLE_FAILURE (gnutls_x509_crt_set_key (cert, privkey));
+  HANDLE_FAILURE (gnutls_x509_crt_sign (cert, cert, privkey));
+  HANDLE_FAILURE (gnutls_x509_crt_export2 (cert, GNUTLS_X509_FMT_PEM, &pubkey_data));
+  if (!g_file_set_contents (data->public_key_path, (char *)pubkey_data.data, pubkey_data.size, &error))
+    goto failure;
+
+  HANDLE_FAILURE (gnutls_x509_privkey_export2 (privkey, GNUTLS_X509_FMT_PEM, &privkey_data));
+  if (!g_file_set_contents (data->private_key_path, (char*)privkey_data.data, privkey_data.size, &error))
+    goto failure;
+
+#undef HANDLE_FAILURE
+
+  if ((certificate = g_tls_certificate_new_from_files (data->public_key_path, data->private_key_path, 
&error)))
+    {
+      g_task_return_pointer (task, g_steal_pointer (&certificate), g_object_unref);
+      return;
+    }
+
+failure:
+
+  if (error != NULL)
+    g_task_return_error (task, g_steal_pointer (&error));
+  else if (gtlsret != 0)
+    g_task_return_new_error (task,
+                             G_TLS_ERROR,
+                             G_TLS_ERROR_MISC,
+                             "GnuTLS Error: %s",
+                             gnutls_strerror (gtlsret));
+  else
+    g_task_return_new_error (task,
+                             G_IO_ERROR,
+                             G_IO_ERROR_FAILED,
+                             "Failed to generate TLS certificate pair");
+}
+
+void
+bonsai_tls_certificate_new_generate_async (const gchar         *public_key_path,
+                                           const gchar         *private_key_path,
+                                           const gchar         *c,
+                                           const gchar         *cn,
+                                           GCancellable        *cancellable,
+                                           GAsyncReadyCallback  callback,
+                                           gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  GenerateData *data;
+
+  g_return_if_fail (public_key_path != NULL);
+  g_return_if_fail (private_key_path != NULL);
+  g_return_if_fail (c != NULL);
+  g_return_if_fail (cn != NULL);
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (NULL, cancellable, callback, user_data);
+  g_task_set_source_tag (task, bonsai_tls_certificate_new_generate_async);
+
+  data = g_slice_new0 (GenerateData);
+  data->public_key_path = g_strdup (public_key_path);
+  data->private_key_path = g_strdup (private_key_path);
+  data->c = g_strdup (c);
+  data->cn = g_strdup (cn);
+  g_task_set_task_data (task, data, (GDestroyNotify)generate_data_free);
+
+  g_task_run_in_thread (task, bonsai_tls_certificate_generate_worker);
+}
+
+GTlsCertificate *
+bonsai_tls_certificate_new_generate_finish (GAsyncResult  *result,
+                                            GError       **error)
+{
+  g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+GTlsCertificate *
+bonsai_tls_certificate_new_generate (const gchar   *public_key_path,
+                                     const gchar   *private_key_path,
+                                     const gchar   *c,
+                                     const gchar   *cn,
+                                     GCancellable  *cancellable,
+                                     GError       **error)
+{
+  g_autoptr(GTask) task = NULL;
+  GenerateData *data;
+
+  g_return_val_if_fail (public_key_path != NULL, NULL);
+  g_return_val_if_fail (private_key_path != NULL, NULL);
+  g_return_val_if_fail (c != NULL, NULL);
+  g_return_val_if_fail (cn != NULL, NULL);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
+
+  task = g_task_new (NULL, cancellable, NULL, NULL);
+  g_task_set_source_tag (task, bonsai_tls_certificate_new_generate);
+
+  data = g_slice_new0 (GenerateData);
+  data->public_key_path = g_strdup (public_key_path);
+  data->private_key_path = g_strdup (private_key_path);
+  data->c = g_strdup (c);
+  data->cn = g_strdup (cn);
+  g_task_set_task_data (task, data, (GDestroyNotify)generate_data_free);
+
+  bonsai_tls_certificate_generate_worker (task, NULL, data, cancellable);
+
+  return g_task_propagate_pointer (task, error);
+}
+
+gchar *
+bonsai_tls_certificate_get_hash (GTlsCertificate *cert)
+{
+  g_autoptr(GByteArray) bytesarray = NULL;
+  g_autoptr(GChecksum) checksum = NULL;
+
+  g_return_val_if_fail (G_IS_TLS_CERTIFICATE (cert), NULL);
+
+  g_object_get (cert, "certificate", &bytesarray, NULL);
+
+  checksum = g_checksum_new (G_CHECKSUM_SHA256);
+  g_checksum_update (checksum, bytesarray->data, bytesarray->len);
+
+  return g_ascii_strdown (g_checksum_get_string (checksum), -1);
+}
+
+typedef struct
+{
+  gchar *public_key_path;
+  gchar *private_key_path;
+  gchar *c;
+  gchar *cn;
+} NewFromFilesOrGenerate;
+
+static void
+new_from_files_or_generate_free (gpointer data)
+{
+  NewFromFilesOrGenerate *state = data;
+
+  g_clear_pointer (&state->public_key_path, g_free);
+  g_clear_pointer (&state->private_key_path, g_free);
+  g_clear_pointer (&state->c, g_free);
+  g_clear_pointer (&state->cn, g_free);
+  g_free (state);
+}
+
+static void
+bonsai_tls_certificate_new_from_files_or_generate_worker (GTask        *task,
+                                                          gpointer      source_object,
+                                                          gpointer      task_data,
+                                                          GCancellable *cancellable)
+{
+  NewFromFilesOrGenerate *state = task_data;
+  g_autoptr(GTlsCertificate) certificate = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_assert (state != NULL);
+  g_assert (state->public_key_path != NULL);
+  g_assert (state->private_key_path != NULL);
+
+  /* Generate new public/private key for server if we need one.
+   * Ideally, we would generate something signed by a real CA
+   * for the user. But since this is "private cloud" oriented,
+   * we should be fine for now.
+   */
+  if (!g_file_test (state->public_key_path, G_FILE_TEST_EXISTS) ||
+      !g_file_test (state->private_key_path, G_FILE_TEST_EXISTS))
+    certificate = bonsai_tls_certificate_new_generate (state->public_key_path,
+                                                       state->private_key_path,
+                                                       state->c,
+                                                       state->cn,
+                                                       cancellable,
+                                                       &error);
+  else
+    certificate = g_tls_certificate_new_from_files (state->public_key_path,
+                                                    state->private_key_path,
+                                                    &error);
+
+  if (certificate == NULL)
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_pointer (task, g_steal_pointer (&certificate), g_object_unref);
+}
+
+/**
+ * bonsai_tls_certificate_new_from_files_or_generate_async:
+ * @public_key_path: the path to the public key file
+ * @private_key_path: the path to the private key file
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @callback: a callback to execute upon completion
+ * @user_data: closure data for @callback
+ *
+ * Asynchronously requests that a certificate is loaded, or generate one if it
+ * does not yet exist. The generated certificate is a self-signed certificate.
+ *
+ * Since: 0.2
+ */
+void
+bonsai_tls_certificate_new_from_files_or_generate_async (const gchar         *public_key_path,
+                                                         const gchar         *private_key_path,
+                                                         const gchar         *c,
+                                                         const gchar         *cn,
+                                                         GCancellable        *cancellable,
+                                                         GAsyncReadyCallback  callback,
+                                                         gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  NewFromFilesOrGenerate state;
+
+  g_assert (public_key_path != NULL);
+  g_assert (private_key_path != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  state.public_key_path = g_strdup (public_key_path);
+  state.private_key_path = g_strdup (private_key_path);
+  state.c = g_strdup (c);
+  state.cn = g_strdup (cn);
+
+  task = g_task_new (NULL, cancellable, callback, user_data);
+  g_task_set_source_tag (task, bonsai_tls_certificate_new_from_files_or_generate_async);
+  g_task_set_task_data (task, g_memdup2 (&state, sizeof state), new_from_files_or_generate_free);
+  g_task_run_in_thread (task, bonsai_tls_certificate_new_from_files_or_generate_worker);
+}
+
+/**
+ * bonsai_tls_certificate_new_from_files_or_generate_finish:
+ * @result: a #GAsyncResult provided to callback
+ * @error: a location for a #GError, or %NULL
+ *
+ * Completes a request to
+ * bonsai_tls_certificate_new_from_files_or_generate_async() which will
+ * either load a #GTlsCertificate for the files if they exist, or generate
+ * a new self-signed certificate in their place.
+ *
+ * Returns: (transfer none): a #GTlsCertificate or %NULL and @error is set.
+ *
+ * Since: 0.2
+ */
+GTlsCertificate *
+bonsai_tls_certificate_new_from_files_or_generate_finish (GAsyncResult  *result,
+                                                          GError       **error)
+{
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+/**
+ * bonsai_tls_certificate_new_from_files_or_generate:
+ * @public_key_path: the path to the public key
+ * @private_key_path: the path to the private key
+ * @c:  the C for the certificate
+ * @cn:  the CN for the certificate
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @error: the location for the error
+ *
+ * Loads a certificate or generates a new self-signed certificate in
+ * it's place.
+ *
+ * Returns: (transfer full): a #GTlsCertificate or %NULL and @error is set
+ *
+ * Since: 0.2
+ */
+GTlsCertificate *
+bonsai_tls_certificate_new_from_files_or_generate (const gchar   *public_key_path,
+                                                   const gchar   *private_key_path,
+                                                   const gchar   *c,
+                                                   const gchar   *cn,
+                                                   GCancellable  *cancellable,
+                                                   GError       **error)
+{
+  GTlsCertificate *ret;
+
+  g_return_val_if_fail (public_key_path != NULL, NULL);
+  g_return_val_if_fail (private_key_path != NULL, NULL);
+  g_return_val_if_fail (c != NULL, NULL);
+  g_return_val_if_fail (cn != NULL, NULL);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
+
+  if (!(ret = g_tls_certificate_new_from_files (public_key_path, private_key_path, NULL)))
+    ret = bonsai_tls_certificate_new_generate (public_key_path,
+                                               private_key_path,
+                                               c,
+                                               cn,
+                                               cancellable,
+                                               error);
+
+  return g_steal_pointer (&ret);
+}
+
+/**
+ * bonsai_tls_certificate_new_for_user:
+ * @public_key_path: the path to the public key
+ * @private_key_path: the path to the private key
+ *
+ * This is a simplified form to create a new certificate or load a previously
+ * created certificate for the current user.
+ *
+ * Returns: (transfer none): a #GTlsCertificate or %NULL and @error is set.
+ *
+ * Since: 0.2
+ */
+GTlsCertificate *
+bonsai_tls_certificate_new_for_user (GCancellable  *cancellable,
+                                     GError       **error)
+{
+  g_autofree gchar *public_key_path = NULL;
+  g_autofree gchar *private_key_path = NULL;
+
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
+
+  public_key_path = g_build_filename (g_get_user_config_dir (), "bonsai", "public.key", NULL);
+  private_key_path = g_build_filename (g_get_user_config_dir (), "bonsai", "private.key", NULL);
+
+  return bonsai_tls_certificate_new_from_files_or_generate (public_key_path,
+                                                            private_key_path,
+                                                            "US",
+                                                            "GNOME",
+                                                            cancellable,
+                                                            error);
+}
+
+gboolean
+bonsai_is_tls_hash (const gchar *hash)
+{
+  guint len = 0;
+
+  if (hash == NULL)
+    return FALSE;
+
+  for (; *hash; hash++)
+    {
+      if (len == 64)
+        return FALSE;
+
+      switch (*hash)
+        {
+        case '0': case '1': case '2': case '3': case '4':
+        case '5': case '6': case '7': case '8': case '9':
+        case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+        case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+          len++;
+          break;
+
+        default:
+          return FALSE;
+        }
+    }
+
+  return len == 64;
+}
+
diff --git a/panels/sharing/cc-tls-certificate.h b/panels/sharing/cc-tls-certificate.h
new file mode 100644
index 000000000..9b9a8d501
--- /dev/null
+++ b/panels/sharing/cc-tls-certificate.h
@@ -0,0 +1,63 @@
+/* cc-tls-certificate.h
+ *
+ * Copyright 2018 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+gboolean         bonsai_is_tls_hash                                       (const gchar          *hash);
+GTlsCertificate *bonsai_tls_certificate_new_generate                      (const gchar          
*public_key_path,
+                                                                           const gchar          
*private_key_path,
+                                                                           const gchar          *c,
+                                                                           const gchar          *cn,
+                                                                           GCancellable         *cancellable,
+                                                                           GError              **error);
+void             bonsai_tls_certificate_new_generate_async                (const gchar          
*public_key_path,
+                                                                           const gchar          
*private_key_path,
+                                                                           const gchar          *c,
+                                                                           const gchar          *cn,
+                                                                           GCancellable         *cancellable,
+                                                                           GAsyncReadyCallback   callback,
+                                                                           gpointer              user_data);
+GTlsCertificate *bonsai_tls_certificate_new_generate_finish               (GAsyncResult         *result,
+                                                                           GError              **error);
+gchar           *bonsai_tls_certificate_get_hash                          (GTlsCertificate      *cert);
+GTlsCertificate *bonsai_tls_certificate_new_from_files_or_generate        (const gchar          
*public_key_path,
+                                                                           const gchar          
*private_key_path,
+                                                                           const gchar          *c,
+                                                                           const gchar          *cn,
+                                                                           GCancellable         *cancellable,
+                                                                           GError              **error);
+void             bonsai_tls_certificate_new_from_files_or_generate_async  (const gchar          
*public_key_path,
+                                                                           const gchar          
*private_key_path,
+                                                                           const gchar          *c,
+                                                                           const gchar          *cn,
+                                                                           GCancellable         *cancellable,
+                                                                           GAsyncReadyCallback   callback,
+                                                                           gpointer              user_data);
+GTlsCertificate *bonsai_tls_certificate_new_from_files_or_generate_finish (GAsyncResult         *result,
+                                                                           GError              **error);
+GTlsCertificate *bonsai_tls_certificate_new_for_user                      (GCancellable         *cancellable,
+                                                                           GError              **error);
+
+G_END_DECLS
+
diff --git a/panels/sharing/meson.build b/panels/sharing/meson.build
index aef3f3d19..5d2bcba6e 100644
--- a/panels/sharing/meson.build
+++ b/panels/sharing/meson.build
@@ -41,6 +41,7 @@ sources = files(
   'cc-remote-login.c',
   'cc-sharing-networks.c',
   'cc-gnome-remote-desktop.c',
+  'cc-tls-certificate.c',
   'file-share-properties.c',
 )
 
@@ -73,12 +74,17 @@ cflags += [
 ]
 
 libsecret_dep = dependency('libsecret-1')
+gnutls_dep = dependency('gnutls')
 
 panels_libs += static_library(
   cappletname,
   sources: sources,
   include_directories: [ top_inc, common_inc ],
-  dependencies: [common_deps, libsecret_dep],
+  dependencies: [
+    common_deps,
+    libsecret_dep,
+    gnutls_dep,
+  ],
   c_args: cflags
 )
 


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