From 97dd7c5d57257cc2d62cf2e9c82585be21f3f537 Mon Sep 17 00:00:00 2001 From: deepend-tildeclub Date: Mon, 30 Mar 2026 11:46:08 -0600 Subject: [PATCH 1/2] Add client SSL cert import action --- src/fe-gtk/servlistgui.c | 92 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/src/fe-gtk/servlistgui.c b/src/fe-gtk/servlistgui.c index 67e4aca2..5b178993 100644 --- a/src/fe-gtk/servlistgui.c +++ b/src/fe-gtk/servlistgui.c @@ -91,6 +91,7 @@ static GtkWidget *edit_label_real; static GtkWidget *edit_label_user; static GtkWidget *edit_trees[N_TREES]; static GtkWidget *edit_button_cert_generate; +static GtkWidget *edit_button_cert_import; static GtkWidget *edit_button_cert_info; static GtkWidget *edit_button_cert_delete; @@ -135,12 +136,98 @@ servlist_update_cert_buttons (ircnet *net) if (edit_button_cert_generate) gtk_widget_set_visible (edit_button_cert_generate, !has_cert); + if (edit_button_cert_import) + gtk_widget_set_visible (edit_button_cert_import, !has_cert); if (edit_button_cert_info) gtk_widget_set_visible (edit_button_cert_info, has_cert); if (edit_button_cert_delete) gtk_widget_set_visible (edit_button_cert_delete, has_cert); } +static void +servlist_import_client_cert_cb (GtkWidget *button, gpointer userdata) +{ + ircnet *net = (ircnet *)userdata; + GtkWidget *dialog; + GtkWidget *message; + GtkFileFilter *filter; + char *cert_dir; + char *cert_file; + char *source_file; + char *contents; + gsize length; + + if (!net || !net->name || !net->name[0]) + return; + + dialog = gtk_file_chooser_dialog_new (_("Import Client Certificate"), + GTK_WINDOW (edit_win), + GTK_FILE_CHOOSER_ACTION_OPEN, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Open"), GTK_RESPONSE_ACCEPT, + NULL); + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Certificate files")); + gtk_file_filter_add_pattern (filter, "*.pem"); + gtk_file_filter_add_pattern (filter, "*.crt"); + gtk_file_filter_add_pattern (filter, "*.cer"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All files")); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + theme_manager_attach_window (dialog); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT) + { + gtk_widget_destroy (dialog); + return; + } + + source_file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + gtk_widget_destroy (dialog); + if (!source_file) + return; + + cert_dir = g_build_filename (get_xdir (), "certs", NULL); + cert_file = servlist_get_cert_file (net); + contents = NULL; + length = 0; + + if (cert_file && + g_mkdir_with_parents (cert_dir, 0700) == 0 && + g_file_get_contents (source_file, &contents, &length, NULL) && + g_file_set_contents (cert_file, contents, length, NULL)) + { + chmod (cert_file, 0600); + servlist_update_cert_buttons (net); + message = gtk_message_dialog_new (GTK_WINDOW (edit_win), + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, + _("Client certificate imported for \"%s\"."), + net->name); + } + else + { + message = gtk_message_dialog_new (GTK_WINDOW (edit_win), + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Failed to import client certificate for \"%s\"."), + net->name); + } + + theme_manager_attach_window (message); + g_signal_connect_swapped (message, "response", G_CALLBACK (gtk_widget_destroy), message); + gtk_widget_show (message); + + g_free (contents); + g_free (cert_file); + g_free (cert_dir); + g_free (source_file); +} + static void servlist_generate_client_cert_cb (GtkWidget *button, gpointer userdata) { @@ -2285,6 +2372,11 @@ servlist_open_edit (GtkWidget *parent, ircnet *net) G_CALLBACK (servlist_generate_client_cert_cb), net); gtk_box_pack_start (GTK_BOX (hbox_cert_buttons), edit_button_cert_generate, FALSE, FALSE, 0); + edit_button_cert_import = gtk_button_new_with_mnemonic (_("Import client SSL cert")); + g_signal_connect (G_OBJECT (edit_button_cert_import), "clicked", + G_CALLBACK (servlist_import_client_cert_cb), net); + gtk_box_pack_start (GTK_BOX (hbox_cert_buttons), edit_button_cert_import, FALSE, FALSE, 0); + edit_button_cert_info = gtk_button_new_with_mnemonic (_("Client SSL cert info")); g_signal_connect (G_OBJECT (edit_button_cert_info), "clicked", G_CALLBACK (servlist_cert_info_cb), net); From 0f508bf4389e7e1a24df1c1aae398d499e069c53 Mon Sep 17 00:00:00 2001 From: deepend-tildeclub Date: Mon, 30 Mar 2026 11:57:58 -0600 Subject: [PATCH 2/2] Sanitize OpenSSL env for client cert actions --- src/fe-gtk/servlistgui.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/fe-gtk/servlistgui.c b/src/fe-gtk/servlistgui.c index 5b178993..c2a8d62e 100644 --- a/src/fe-gtk/servlistgui.c +++ b/src/fe-gtk/servlistgui.c @@ -252,6 +252,7 @@ servlist_generate_client_cert_cb (GtkWidget *button, gpointer userdata) gboolean success; gint status; char *argv[20]; + char **envp; if (!net || !net->name || !net->name[0]) return; @@ -274,6 +275,7 @@ servlist_generate_client_cert_cb (GtkWidget *button, gpointer userdata) crt_len = 0; success = FALSE; status = 0; + envp = g_environ_unsetenv (g_get_environ (), "LD_LIBRARY_PATH"); if (g_mkdir_with_parents (cert_dir, 0700) == 0 && g_file_set_contents (openssl_conf, conf_data, -1, NULL)) @@ -299,7 +301,7 @@ servlist_generate_client_cert_cb (GtkWidget *button, gpointer userdata) argv[18] = subject; argv[19] = NULL; - spawned = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, + spawned = g_spawn_sync (NULL, argv, envp, G_SPAWN_SEARCH_PATH, NULL, NULL, &stdout_data, &stderr_data, &status, NULL); if (spawned && g_spawn_check_exit_status (status, NULL) && g_file_get_contents (key_file, &key_data, &key_len, NULL) && @@ -354,6 +356,7 @@ servlist_generate_client_cert_cb (GtkWidget *button, gpointer userdata) g_free (openssl_conf); g_free (cert_file); g_free (cert_dir); + g_strfreev (envp); #else return; #endif @@ -371,6 +374,7 @@ servlist_cert_info_cb (GtkWidget *button, gpointer userdata) gboolean spawned; gint status; char *argv[12]; + char **envp; cert_file = servlist_get_cert_file (net); if (!cert_file) @@ -379,6 +383,7 @@ servlist_cert_info_cb (GtkWidget *button, gpointer userdata) stdout_data = NULL; stderr_data = NULL; status = 0; + envp = g_environ_unsetenv (g_get_environ (), "LD_LIBRARY_PATH"); argv[0] = "openssl"; argv[1] = "x509"; argv[2] = "-in"; @@ -392,7 +397,7 @@ servlist_cert_info_cb (GtkWidget *button, gpointer userdata) argv[10] = "-sha256"; argv[11] = NULL; - spawned = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, + spawned = g_spawn_sync (NULL, argv, envp, G_SPAWN_SEARCH_PATH, NULL, NULL, &stdout_data, &stderr_data, &status, NULL); if (spawned && g_spawn_check_exit_status (status, NULL) && stdout_data && stdout_data[0]) @@ -423,6 +428,7 @@ servlist_cert_info_cb (GtkWidget *button, gpointer userdata) g_free (stdout_data); g_free (stderr_data); g_free (cert_file); + g_strfreev (envp); #else return; #endif