summaryrefslogtreecommitdiffstats
path: root/panels/network/wireless-security/eap-method.c
diff options
context:
space:
mode:
Diffstat (limited to 'panels/network/wireless-security/eap-method.c')
-rw-r--r--panels/network/wireless-security/eap-method.c594
1 files changed, 594 insertions, 0 deletions
diff --git a/panels/network/wireless-security/eap-method.c b/panels/network/wireless-security/eap-method.c
new file mode 100644
index 0000000..fdda35b
--- /dev/null
+++ b/panels/network/wireless-security/eap-method.c
@@ -0,0 +1,594 @@
+/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+
+/* NetworkManager Applet -- allow user control over networking
+ *
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ */
+
+#include <fcntl.h>
+#include <glib/gi18n.h>
+
+#include "eap-method.h"
+#include "helpers.h"
+#include "ui-helpers.h"
+
+G_DEFINE_INTERFACE (EAPMethod, eap_method, G_TYPE_OBJECT)
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+eap_method_default_init (EAPMethodInterface *iface)
+{
+ signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+GtkWidget *
+eap_method_get_default_field (EAPMethod *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+
+ return EAP_METHOD_GET_IFACE (self)->get_default_field (self);
+}
+
+const gchar *
+eap_method_get_password_flags_name (EAPMethod *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+
+ if (EAP_METHOD_GET_IFACE (self)->get_password_flags_name)
+ return EAP_METHOD_GET_IFACE (self)->get_password_flags_name (self);
+ else
+ return NULL;
+}
+
+gboolean
+eap_method_get_phase2 (EAPMethod *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+
+ if (EAP_METHOD_GET_IFACE (self)->get_phase2)
+ return EAP_METHOD_GET_IFACE (self)->get_phase2 (self);
+ else
+ return FALSE;
+}
+
+gboolean
+eap_method_validate (EAPMethod *self, GError **error)
+{
+ gboolean result;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+
+ result = (*(EAP_METHOD_GET_IFACE (self)->validate)) (self, error);
+ if (!result && error && !*error)
+ g_set_error_literal (error, NMA_ERROR, NMA_ERROR_GENERIC, _("undefined error in 802.1X security (wpa-eap)"));
+ return result;
+}
+
+void
+eap_method_update_secrets (EAPMethod *self, NMConnection *connection)
+{
+ g_return_if_fail (self != NULL);
+
+ if (EAP_METHOD_GET_IFACE (self)->update_secrets)
+ EAP_METHOD_GET_IFACE (self)->update_secrets (self, connection);
+}
+
+void
+eap_method_add_to_size_group (EAPMethod *self, GtkSizeGroup *group)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (group != NULL);
+
+ return (*(EAP_METHOD_GET_IFACE (self)->add_to_size_group)) (self, group);
+}
+
+void
+eap_method_fill_connection (EAPMethod *self,
+ NMConnection *connection,
+ NMSettingSecretFlags flags)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (connection != NULL);
+
+ return (*(EAP_METHOD_GET_IFACE (self)->fill_connection)) (self, connection, flags);
+}
+
+void
+eap_method_emit_changed (EAPMethod *self)
+{
+ g_return_if_fail (EAP_IS_METHOD (self));
+
+ g_signal_emit (self, signals[CHANGED], 0);
+}
+
+const gchar *
+eap_method_get_username (EAPMethod *self)
+{
+ g_return_val_if_fail (EAP_IS_METHOD (self), NULL);
+ return EAP_METHOD_GET_IFACE (self)->get_username (self);
+}
+
+void
+eap_method_set_username (EAPMethod *self, const gchar *username)
+{
+ g_return_if_fail (EAP_IS_METHOD (self));
+ EAP_METHOD_GET_IFACE (self)->set_username (self, username);
+}
+
+const gchar *
+eap_method_get_password (EAPMethod *self)
+{
+ g_return_val_if_fail (EAP_IS_METHOD (self), NULL);
+ return EAP_METHOD_GET_IFACE (self)->get_password (self);
+}
+
+void
+eap_method_set_password (EAPMethod *self, const gchar *password)
+{
+ g_return_if_fail (EAP_IS_METHOD (self));
+ EAP_METHOD_GET_IFACE (self)->set_password (self, password);
+}
+
+gboolean
+eap_method_get_show_password (EAPMethod *self)
+{
+ g_return_val_if_fail (EAP_IS_METHOD (self), FALSE);
+ return EAP_METHOD_GET_IFACE (self)->get_show_password (self);
+}
+
+void
+eap_method_set_show_password (EAPMethod *self, gboolean show_password)
+{
+ g_return_if_fail (EAP_IS_METHOD (self));
+ EAP_METHOD_GET_IFACE (self)->set_show_password (self, show_password);
+}
+
+gboolean
+eap_method_validate_filepicker (GtkFileChooser *chooser,
+ guint32 item_type,
+ const char *password,
+ NMSetting8021xCKFormat *out_format,
+ GError **error)
+{
+ g_autofree gchar *filename = NULL;
+ g_autoptr(NMSetting8021x) setting = NULL;
+ gboolean success = TRUE;
+
+ if (item_type == TYPE_PRIVATE_KEY) {
+ if (!password || *password == '\0')
+ success = FALSE;
+ }
+
+ filename = gtk_file_chooser_get_filename (chooser);
+ if (!filename) {
+ if (item_type != TYPE_CA_CERT) {
+ success = FALSE;
+ g_set_error_literal (error, NMA_ERROR, NMA_ERROR_GENERIC, _("no file selected"));
+ }
+ goto out;
+ }
+
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
+ success = FALSE;
+ goto out;
+ }
+
+ setting = (NMSetting8021x *) nm_setting_802_1x_new ();
+
+ success = FALSE;
+ if (item_type == TYPE_PRIVATE_KEY) {
+ if (nm_setting_802_1x_set_private_key (setting, filename, password, NM_SETTING_802_1X_CK_SCHEME_PATH, out_format, error))
+ success = TRUE;
+ } else if (item_type == TYPE_CLIENT_CERT) {
+ if (nm_setting_802_1x_set_client_cert (setting, filename, NM_SETTING_802_1X_CK_SCHEME_PATH, out_format, error))
+ success = TRUE;
+ } else if (item_type == TYPE_CA_CERT) {
+ if (nm_setting_802_1x_set_ca_cert (setting, filename, NM_SETTING_802_1X_CK_SCHEME_PATH, out_format, error))
+ success = TRUE;
+ } else
+ g_warning ("%s: invalid item type %d.", __func__, item_type);
+
+out:
+ if (!success && error && !*error)
+ g_set_error_literal (error, NMA_ERROR, NMA_ERROR_GENERIC, _("unspecified error validating eap-method file"));
+
+ if (success)
+ widget_unset_error (GTK_WIDGET (chooser));
+ else
+ widget_set_error (GTK_WIDGET (chooser));
+ return success;
+}
+
+static gboolean
+file_has_extension (const char *filename, const char *extensions[])
+{
+ char *p;
+ g_autofree gchar *ext = NULL;
+ int i = 0;
+ gboolean found = FALSE;
+
+ p = strrchr (filename, '.');
+ if (!p)
+ return FALSE;
+
+ ext = g_ascii_strdown (p, -1);
+ if (ext) {
+ while (extensions[i]) {
+ if (!strcmp (ext, extensions[i++])) {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ return found;
+}
+
+#if !LIBNM_BUILD
+static const char *
+find_tag (const char *tag, const char *buf, gsize len)
+{
+ gsize i, taglen;
+
+ taglen = strlen (tag);
+ if (len < taglen)
+ return NULL;
+
+ for (i = 0; i < len - taglen + 1; i++) {
+ if (memcmp (buf + i, tag, taglen) == 0)
+ return buf + i;
+ }
+ return NULL;
+}
+
+static const char *pem_rsa_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_dsa_key_begin = "-----BEGIN DSA PRIVATE KEY-----";
+static const char *pem_pkcs8_enc_key_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
+static const char *pem_pkcs8_dec_key_begin = "-----BEGIN PRIVATE KEY-----";
+static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
+static const char *proc_type_tag = "Proc-Type: 4,ENCRYPTED";
+static const char *dek_info_tag = "DEK-Info:";
+
+static gboolean
+pem_file_is_encrypted (const char *buffer, gsize bytes_read)
+{
+ /* Check if the private key is encrypted or not by looking for the
+ * old OpenSSL-style proc-type and dec-info tags.
+ */
+ if (find_tag (proc_type_tag, (const char *) buffer, bytes_read)) {
+ if (find_tag (dek_info_tag, (const char *) buffer, bytes_read))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+file_is_der_or_pem (const char *filename,
+ gboolean privkey,
+ gboolean *out_privkey_encrypted)
+{
+ int fd;
+ unsigned char buffer[8192];
+ ssize_t bytes_read;
+ gboolean success = FALSE;
+
+ fd = open (filename, O_RDONLY);
+ if (fd < 0)
+ return FALSE;
+
+ bytes_read = read (fd, buffer, sizeof (buffer) - 1);
+ if (bytes_read < 400) /* needs to be lower? */
+ goto out;
+ buffer[bytes_read] = '\0';
+
+ /* Check for DER signature */
+ if (bytes_read > 2 && buffer[0] == 0x30 && buffer[1] == 0x82) {
+ success = TRUE;
+ goto out;
+ }
+
+ /* Check for PEM signatures */
+ if (privkey) {
+ if (find_tag (pem_rsa_key_begin, (const char *) buffer, bytes_read)) {
+ success = TRUE;
+ if (out_privkey_encrypted)
+ *out_privkey_encrypted = pem_file_is_encrypted ((const char *) buffer, bytes_read);
+ goto out;
+ }
+
+ if (find_tag (pem_dsa_key_begin, (const char *) buffer, bytes_read)) {
+ success = TRUE;
+ if (out_privkey_encrypted)
+ *out_privkey_encrypted = pem_file_is_encrypted ((const char *) buffer, bytes_read);
+ goto out;
+ }
+
+ if (find_tag (pem_pkcs8_enc_key_begin, (const char *) buffer, bytes_read)) {
+ success = TRUE;
+ if (out_privkey_encrypted)
+ *out_privkey_encrypted = TRUE;
+ goto out;
+ }
+
+ if (find_tag (pem_pkcs8_dec_key_begin, (const char *) buffer, bytes_read)) {
+ success = TRUE;
+ if (out_privkey_encrypted)
+ *out_privkey_encrypted = FALSE;
+ goto out;
+ }
+ } else {
+ if (find_tag (pem_cert_begin, (const char *) buffer, bytes_read)) {
+ success = TRUE;
+ goto out;
+ }
+ }
+
+out:
+ close (fd);
+ return success;
+}
+#endif
+
+static gboolean
+default_filter_privkey (const GtkFileFilterInfo *filter_info, gpointer user_data)
+{
+ const char *extensions[] = { ".der", ".pem", ".p12", ".key", NULL };
+
+ if (!filter_info->filename)
+ return FALSE;
+
+ if (!file_has_extension (filter_info->filename, extensions))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+default_filter_cert (const GtkFileFilterInfo *filter_info, gpointer user_data)
+{
+ const char *extensions[] = { ".der", ".pem", ".crt", ".cer", NULL };
+
+ if (!filter_info->filename)
+ return FALSE;
+
+ if (!file_has_extension (filter_info->filename, extensions))
+ return FALSE;
+
+ return TRUE;
+}
+
+GtkFileFilter *
+eap_method_default_file_chooser_filter_new (gboolean privkey)
+{
+ GtkFileFilter *filter;
+
+ filter = gtk_file_filter_new ();
+ if (privkey) {
+ gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME, default_filter_privkey, NULL, NULL);
+ gtk_file_filter_set_name (filter, _("DER, PEM, or PKCS#12 private keys (*.der, *.pem, *.p12, *.key)"));
+ } else {
+ gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME, default_filter_cert, NULL, NULL);
+ gtk_file_filter_set_name (filter, _("DER or PEM certificates (*.der, *.pem, *.crt, *.cer)"));
+ }
+ return filter;
+}
+
+gboolean
+eap_method_is_encrypted_private_key (const char *path)
+{
+ GtkFileFilterInfo info = { .filename = path };
+ gboolean is_encrypted;
+
+ if (!default_filter_privkey (&info, NULL))
+ return FALSE;
+
+#if LIBNM_BUILD
+ is_encrypted = FALSE;
+ if (!nm_utils_file_is_private_key (path, &is_encrypted))
+ return FALSE;
+#else
+ is_encrypted = TRUE;
+ if ( !file_is_der_or_pem (path, TRUE, &is_encrypted)
+ && !nm_utils_file_is_pkcs12 (path))
+ return FALSE;
+#endif
+ return is_encrypted;
+}
+
+void
+eap_method_ca_cert_not_required_toggled (GtkToggleButton *id_ca_cert_not_required_checkbutton, GtkFileChooser *id_ca_cert_chooser)
+{
+ g_autofree gchar *filename = NULL;
+ g_autofree gchar *filename_old = NULL;
+ gboolean is_not_required;
+
+ g_assert (id_ca_cert_not_required_checkbutton && id_ca_cert_chooser);
+
+ is_not_required = gtk_toggle_button_get_active (id_ca_cert_not_required_checkbutton);
+
+ filename = gtk_file_chooser_get_filename (id_ca_cert_chooser);
+ filename_old = g_object_steal_data (G_OBJECT (id_ca_cert_chooser), "filename-old");
+ if (is_not_required) {
+ g_free (filename_old);
+ filename_old = g_steal_pointer (&filename);
+ } else {
+ g_free (filename);
+ filename = g_steal_pointer (&filename_old);
+ }
+ gtk_widget_set_sensitive (GTK_WIDGET (id_ca_cert_chooser), !is_not_required);
+ if (filename)
+ gtk_file_chooser_set_filename (id_ca_cert_chooser, filename);
+ else
+ gtk_file_chooser_unselect_all (id_ca_cert_chooser);
+ g_object_set_data_full (G_OBJECT (id_ca_cert_chooser), "filename-old", g_steal_pointer (&filename_old), g_free);
+}
+
+/* Used as both GSettings keys and GObject data tags */
+#define IGNORE_CA_CERT_TAG "ignore-ca-cert"
+#define IGNORE_PHASE2_CA_CERT_TAG "ignore-phase2-ca-cert"
+
+/**
+ * eap_method_ca_cert_ignore_set:
+ * @method: the #EAPMethod object
+ * @connection: the #NMConnection
+ * @filename: the certificate file, if any
+ * @ca_cert_error: %TRUE if an error was encountered loading the given CA
+ * certificate, %FALSE if not or if a CA certificate is not present
+ *
+ * Updates the connection's CA cert ignore value to %TRUE if the "CA certificate
+ * not required" checkbox is checked. If @ca_cert_error is %TRUE, then the
+ * connection's CA cert ignore value will always be set to %FALSE, because it
+ * means that the user selected an invalid certificate (thus he does not want to
+ * ignore the CA cert)..
+ */
+void
+eap_method_ca_cert_ignore_set (EAPMethod *self,
+ NMConnection *connection,
+ const char *filename,
+ gboolean ca_cert_error)
+{
+ NMSetting8021x *s_8021x;
+ gboolean ignore;
+
+ s_8021x = nm_connection_get_setting_802_1x (connection);
+ if (s_8021x) {
+ ignore = !ca_cert_error && filename == NULL;
+ g_object_set_data (G_OBJECT (s_8021x),
+ eap_method_get_phase2 (self) ? IGNORE_PHASE2_CA_CERT_TAG : IGNORE_CA_CERT_TAG,
+ GUINT_TO_POINTER (ignore));
+ }
+}
+
+/**
+ * eap_method_ca_cert_ignore_get:
+ * @method: the #EAPMethod object
+ * @connection: the #NMConnection
+ *
+ * Returns: %TRUE if a missing CA certificate can be ignored, %FALSE if a CA
+ * certificate should be required for the connection to be valid.
+ */
+gboolean
+eap_method_ca_cert_ignore_get (EAPMethod *self, NMConnection *connection)
+{
+ NMSetting8021x *s_8021x;
+
+ s_8021x = nm_connection_get_setting_802_1x (connection);
+ if (s_8021x) {
+ return !!g_object_get_data (G_OBJECT (s_8021x),
+ eap_method_get_phase2 (self) ? IGNORE_PHASE2_CA_CERT_TAG : IGNORE_CA_CERT_TAG);
+ }
+ return FALSE;
+}
+
+static GSettings *
+_get_ca_ignore_settings (NMConnection *connection)
+{
+ GSettings *settings;
+ g_autofree gchar *path = NULL;
+ const char *uuid;
+
+ g_return_val_if_fail (connection, NULL);
+
+ uuid = nm_connection_get_uuid (connection);
+ g_return_val_if_fail (uuid && *uuid, NULL);
+
+ path = g_strdup_printf ("/org/gnome/nm-applet/eap/%s/", uuid);
+ settings = g_settings_new_with_path ("org.gnome.nm-applet.eap", path);
+
+ return settings;
+}
+
+/**
+ * eap_method_ca_cert_ignore_save:
+ * @connection: the connection for which to save CA cert ignore values to GSettings
+ *
+ * Reads the CA cert ignore tags from the 802.1x setting GObject data and saves
+ * then to GSettings if present, using the connection UUID as the index.
+ */
+void
+eap_method_ca_cert_ignore_save (NMConnection *connection)
+{
+ NMSetting8021x *s_8021x;
+ g_autoptr(GSettings) settings = NULL;
+ gboolean ignore = FALSE, phase2_ignore = FALSE;
+
+ g_return_if_fail (connection);
+
+ s_8021x = nm_connection_get_setting_802_1x (connection);
+ if (s_8021x) {
+ ignore = !!g_object_get_data (G_OBJECT (s_8021x), IGNORE_CA_CERT_TAG);
+ phase2_ignore = !!g_object_get_data (G_OBJECT (s_8021x), IGNORE_PHASE2_CA_CERT_TAG);
+ }
+
+ settings = _get_ca_ignore_settings (connection);
+ if (!settings)
+ return;
+
+ g_settings_set_boolean (settings, IGNORE_CA_CERT_TAG, ignore);
+ g_settings_set_boolean (settings, IGNORE_PHASE2_CA_CERT_TAG, phase2_ignore);
+}
+
+/**
+ * eap_method_ca_cert_ignore_load:
+ * @connection: the connection for which to load CA cert ignore values to GSettings
+ *
+ * Reads the CA cert ignore tags from the 802.1x setting GObject data and saves
+ * then to GSettings if present, using the connection UUID as the index.
+ */
+void
+eap_method_ca_cert_ignore_load (NMConnection *connection)
+{
+ g_autoptr(GSettings) settings = NULL;
+ NMSetting8021x *s_8021x;
+ gboolean ignore, phase2_ignore;
+
+ g_return_if_fail (connection);
+
+ s_8021x = nm_connection_get_setting_802_1x (connection);
+ if (!s_8021x)
+ return;
+
+ settings = _get_ca_ignore_settings (connection);
+ if (!settings)
+ return;
+
+ ignore = g_settings_get_boolean (settings, IGNORE_CA_CERT_TAG);
+ phase2_ignore = g_settings_get_boolean (settings, IGNORE_PHASE2_CA_CERT_TAG);
+
+ g_object_set_data (G_OBJECT (s_8021x),
+ IGNORE_CA_CERT_TAG,
+ GUINT_TO_POINTER (ignore));
+ g_object_set_data (G_OBJECT (s_8021x),
+ IGNORE_PHASE2_CA_CERT_TAG,
+ GUINT_TO_POINTER (phase2_ignore));
+}
+