[network-manager-openvpn] core: add support for PKCS#12 certificates and keys (bgo #534219)
- From: Dan Williams <dcbw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [network-manager-openvpn] core: add support for PKCS#12 certificates and keys (bgo #534219)
- Date: Mon, 1 Mar 2010 19:26:56 +0000 (UTC)
commit 9d68010833e797aec027600fbef37696223cccdb
Author: Huzaifa S. Sidhpurwala <huzaifas redhat com>
Date: Mon Mar 1 11:21:08 2010 -0800
core: add support for PKCS#12 certificates and keys (bgo #534219)
Allow PKCS#12 files to be used. openvpn requires that all of
CA, client cert, and private key be in the same PKCS12 file, so the
UI widgets make sure that either all cert file chooser buttons
contain the selected PKCS#12 file, or none of them do.
This should also clean up handling of PEM private keys so that if
the key is unencrypted, the user is not asked for a password.
(heavily cleaned up and refactored by dcbw)
Makefile.am | 2 +-
auth-dialog/Makefile.am | 3 +-
auth-dialog/main.c | 38 +--------
common/Makefile.am | 14 +++
common/utils.c | 87 +++++++++++++++++++
common/utils.h | 32 +++++++
configure.ac | 1 +
properties/Makefile.am | 3 +-
properties/auth-helpers.c | 153 +++++++++++++++++++++++++--------
properties/auth-helpers.h | 2 +-
properties/import-export.c | 24 ++++--
properties/tests/conf/Makefile.am | 3 +-
properties/tests/conf/pkcs12.ovpn | 17 ++++
properties/tests/test-import-export.c | 114 ++++++++++++++++++++++++-
src/Makefile.am | 47 +++++-----
src/nm-openvpn-service.c | 90 +++++++++++---------
16 files changed, 483 insertions(+), 147 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index d50986a..629c15e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,6 @@
AUTOMAKE_OPTIONS = foreign
-SUBDIRS = src
+SUBDIRS = common src
if WITH_GNOME
SUBDIRS += common-gnome auth-dialog properties po
diff --git a/auth-dialog/Makefile.am b/auth-dialog/Makefile.am
index ef0e675..5a748e3 100644
--- a/auth-dialog/Makefile.am
+++ b/auth-dialog/Makefile.am
@@ -24,6 +24,7 @@ nm_openvpn_auth_dialog_SOURCES = \
nm_openvpn_auth_dialog_LDADD = \
$(GTK_LIBS) \
$(GCONF_LIBS) \
- $(top_builddir)/common-gnome/libnm-openvpn-common-gnome.la
+ $(top_builddir)/common-gnome/libnm-openvpn-common-gnome.la \
+ $(top_builddir)/common/libnm-openvpn-common.la
CLEANFILES = *~
diff --git a/auth-dialog/main.c b/auth-dialog/main.c
index 699d601..45ed544 100644
--- a/auth-dialog/main.c
+++ b/auth-dialog/main.c
@@ -36,6 +36,7 @@
#include <nm-setting-connection.h>
#include "common-gnome/keyring-helpers.h"
+#include "common/utils.h"
#include "src/nm-openvpn-service.h"
#include "gnome-two-password-dialog.h"
@@ -50,40 +51,6 @@ typedef struct {
char *certpass;
} PasswordsInfo;
-#define PROC_TYPE_TAG "Proc-Type: 4,ENCRYPTED"
-
-/** Checks if a key is encrypted
- * The key file is read and it is checked if it contains a line reading
- * Proc-Type: 4,ENCRYPTED
- * This is defined in RFC 1421 (PEM)
- * @param filename the path to the file
- * @return returns true if the key is encrypted, false otherwise
- */
-static gboolean
-pem_is_encrypted (const char *filename)
-{
- GIOChannel *pem_chan;
- char *str = NULL;
- gboolean encrypted = FALSE;
-
- pem_chan = g_io_channel_new_file (filename, "r", NULL);
- if (!pem_chan)
- return FALSE;
-
- while (g_io_channel_read_line (pem_chan, &str, NULL, NULL, NULL) != G_IO_STATUS_EOF) {
- if (strncmp (str, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG)) == 0) {
- encrypted = TRUE;
- break;
- }
- g_free (str);
- }
-
- g_io_channel_shutdown (pem_chan, FALSE, NULL);
- g_io_channel_unref (pem_chan);
- return encrypted;
-}
-
-
static void
clear_secrets (PasswordsInfo *info)
{
@@ -289,8 +256,7 @@ get_password_types (PasswordsInfo *info)
key = g_strdup_printf ("%s/%s/%s", connection_path, NM_SETTING_VPN_SETTING_NAME,
NM_OPENVPN_KEY_KEY);
str = gconf_client_get_string (gconf_client, key, NULL);
- if (str)
- info->need_certpass = pem_is_encrypted (str);
+ info->need_certpass = (is_pkcs12 (str) || is_encrypted_pem (str));
g_free (str);
g_free (key);
} else if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_STATIC_KEY)) {
diff --git a/common/Makefile.am b/common/Makefile.am
new file mode 100644
index 0000000..c16eba8
--- /dev/null
+++ b/common/Makefile.am
@@ -0,0 +1,14 @@
+noinst_LTLIBRARIES=libnm-openvpn-common.la
+
+libnm_openvpn_common_la_CPPFLAGS = \
+ $(NETWORK_MANAGER_CFLAGS) \
+ -DG_DISABLE_DEPRECATED \
+ -I$(top_srcdir)/src/
+
+libnm_openvpn_common_la_SOURCES= \
+ utils.c \
+ utils.h
+
+libnm_openvpn_common_gnome_la_LIBADD = \
+ $(NETWORK_MANAGER_LIBS)
+
diff --git a/common/utils.c b/common/utils.c
new file mode 100644
index 0000000..7574801
--- /dev/null
+++ b/common/utils.c
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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.
+ *
+ * (C) Copyright 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <nm-setting-8021x.h>
+#include "utils.h"
+
+gboolean
+is_pkcs12 (const char *filepath)
+{
+ NMSetting8021xCKFormat ck_format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+ NMSetting8021x *s_8021x;
+
+ if (!filepath || !strlen (filepath))
+ return FALSE;
+
+ if (!g_file_test (filepath, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
+ return FALSE;
+
+ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+ g_return_val_if_fail (s_8021x != NULL, FALSE);
+
+ nm_setting_802_1x_set_private_key (s_8021x,
+ filepath,
+ NULL,
+ NM_SETTING_802_1X_CK_SCHEME_PATH,
+ &ck_format,
+ NULL);
+ g_object_unref (s_8021x);
+
+ return (ck_format == NM_SETTING_802_1X_CK_FORMAT_PKCS12);
+}
+
+#define PROC_TYPE_TAG "Proc-Type: 4,ENCRYPTED"
+
+/** Checks if a key is encrypted
+ * The key file is read and it is checked if it contains a line reading
+ * Proc-Type: 4,ENCRYPTED
+ * This is defined in RFC 1421 (PEM)
+ * @param filename the path to the file
+ * @return returns true if the key is encrypted, false otherwise
+ */
+gboolean
+is_encrypted_pem (const char *filename)
+{
+ GIOChannel *pem_chan;
+ char *str = NULL;
+ gboolean encrypted = FALSE;
+
+ if (!filename || !strlen (filename))
+ return FALSE;
+
+ pem_chan = g_io_channel_new_file (filename, "r", NULL);
+ if (!pem_chan)
+ return FALSE;
+
+ while (g_io_channel_read_line (pem_chan, &str, NULL, NULL, NULL) != G_IO_STATUS_EOF) {
+ if (strncmp (str, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG)) == 0) {
+ encrypted = TRUE;
+ break;
+ }
+ g_free (str);
+ }
+
+ g_io_channel_shutdown (pem_chan, FALSE, NULL);
+ g_io_channel_unref (pem_chan);
+ return encrypted;
+}
+
diff --git a/common/utils.h b/common/utils.h
new file mode 100644
index 0000000..5e6033e
--- /dev/null
+++ b/common/utils.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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.
+ *
+ * (C) Copyright 2010 Red Hat, Inc.
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <glib.h>
+
+gboolean is_pkcs12 (const char *filepath);
+
+gboolean is_encrypted_pem (const char *filename);
+
+#endif /* UTILS_H */
+
diff --git a/configure.ac b/configure.ac
index ace7b77..9a7c55c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -118,6 +118,7 @@ esac
AC_CONFIG_FILES([
Makefile
src/Makefile
+common/Makefile
common-gnome/Makefile
auth-dialog/Makefile
properties/Makefile
diff --git a/properties/Makefile.am b/properties/Makefile.am
index 6044378..77b9e5d 100644
--- a/properties/Makefile.am
+++ b/properties/Makefile.am
@@ -35,7 +35,8 @@ libnm_openvpn_properties_la_LIBADD = \
$(GTK_LIBS) \
$(GCONF_LIBS) \
$(NETWORK_MANAGER_LIBS) \
- $(top_builddir)/common-gnome/libnm-openvpn-common-gnome.la
+ $(top_builddir)/common-gnome/libnm-openvpn-common-gnome.la \
+ $(top_builddir)/common/libnm-openvpn-common.la
libnm_openvpn_properties_la_LDFLAGS = \
-avoid-version
diff --git a/properties/auth-helpers.c b/properties/auth-helpers.c
index 7279550..6c5ad22 100644
--- a/properties/auth-helpers.c
+++ b/properties/auth-helpers.c
@@ -35,11 +35,13 @@
#include <glib/gi18n-lib.h>
#include <gnome-keyring-memory.h>
#include <nm-setting-connection.h>
+#include <nm-setting-8021x.h>
#include "auth-helpers.h"
#include "nm-openvpn.h"
#include "src/nm-openvpn-service.h"
#include "common-gnome/keyring-helpers.h"
+#include "common/utils.h"
static void
show_password (GtkToggleButton *togglebutton, GtkEntry *password_entry)
@@ -129,6 +131,47 @@ fill_vpn_passwords (GladeXML *xml,
}
}
+static void
+tls_cert_changed_cb (GtkWidget *widget, GtkWidget *next_widget)
+{
+ GtkFileChooser *this, *next;
+ char *fname, *next_fname;
+
+ /* If the just-changed file chooser is a PKCS#12 file, then all of the
+ * TLS filechoosers have to be PKCS#12. But if it just changed to something
+ * other than a PKCS#12 file, then clear out the other file choosers.
+ *
+ * Basically, all the choosers have to contain PKCS#12 files, or none of
+ * them can, because PKCS#12 files contain everything required for the TLS
+ * connection (CA, client cert, private key).
+ */
+
+ this = GTK_FILE_CHOOSER (widget);
+ next = GTK_FILE_CHOOSER (next_widget);
+
+ fname = gtk_file_chooser_get_filename (this);
+ if (is_pkcs12 (fname)) {
+ /* Make sure all choosers have this PKCS#12 file */
+ next_fname = gtk_file_chooser_get_filename (next);
+ if (!next_fname || strcmp (fname, next_fname)) {
+ /* Next chooser was different, make it the same as the first */
+ gtk_file_chooser_set_filename (next, fname);
+ }
+ g_free (fname);
+ g_free (next_fname);
+ return;
+ }
+ g_free (fname);
+
+ /* Just-chosen file isn't PKCS#12 or no file was chosen, so clear out other
+ * file selectors that have PKCS#12 files in them.
+ */
+ next_fname = gtk_file_chooser_get_filename (next);
+ if (is_pkcs12 (next_fname))
+ gtk_file_chooser_set_filename (next, NULL);
+ g_free (next_fname);
+}
+
void
tls_pw_init_auth_widget (GladeXML *xml,
GtkSizeGroup *group,
@@ -138,7 +181,7 @@ tls_pw_init_auth_widget (GladeXML *xml,
ChangedCallback changed_cb,
gpointer user_data)
{
- GtkWidget *widget;
+ GtkWidget *widget, *ca, *cert, *key;
const char *value;
char *tmp;
GtkFileFilter *filter;
@@ -149,59 +192,68 @@ tls_pw_init_auth_widget (GladeXML *xml,
g_return_if_fail (prefix != NULL);
tmp = g_strdup_printf ("%s_ca_cert_chooser", prefix);
- widget = glade_xml_get_widget (xml, tmp);
+ ca = glade_xml_get_widget (xml, tmp);
g_free (tmp);
- gtk_size_group_add_widget (group, widget);
- filter = tls_file_chooser_filter_new ();
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), filter);
- gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE);
- gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (widget),
+ gtk_size_group_add_widget (group, ca);
+ if (!strcmp (contype, NM_OPENVPN_CONTYPE_TLS) || !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS))
+ filter = tls_file_chooser_filter_new (TRUE);
+ else
+ filter = tls_file_chooser_filter_new (FALSE);
+
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (ca), filter);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (ca), TRUE);
+ gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (ca),
_("Choose a Certificate Authority certificate..."));
- g_signal_connect (G_OBJECT (widget), "selection-changed", G_CALLBACK (changed_cb), user_data);
+ g_signal_connect (G_OBJECT (ca), "selection-changed", G_CALLBACK (changed_cb), user_data);
if (s_vpn) {
value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CA);
if (value && strlen (value))
- gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), value);
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (ca), value);
}
if (!strcmp (contype, NM_OPENVPN_CONTYPE_TLS) || !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
tmp = g_strdup_printf ("%s_user_cert_chooser", prefix);
- widget = glade_xml_get_widget (xml, tmp);
+ cert = glade_xml_get_widget (xml, tmp);
g_free (tmp);
- gtk_size_group_add_widget (group, widget);
- filter = tls_file_chooser_filter_new ();
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), filter);
- gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE);
- gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (widget),
+ gtk_size_group_add_widget (group, cert);
+ filter = tls_file_chooser_filter_new (TRUE);
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (cert), filter);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (cert), TRUE);
+ gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (cert),
_("Choose your personal certificate..."));
- g_signal_connect (G_OBJECT (widget), "selection-changed", G_CALLBACK (changed_cb), user_data);
+ g_signal_connect (G_OBJECT (cert), "selection-changed", G_CALLBACK (changed_cb), user_data);
if (s_vpn) {
value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CERT);
if (value && strlen (value))
- gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), value);
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (cert), value);
}
tmp = g_strdup_printf ("%s_private_key_chooser", prefix);
- widget = glade_xml_get_widget (xml, tmp);
+ key = glade_xml_get_widget (xml, tmp);
g_free (tmp);
- gtk_size_group_add_widget (group, widget);
- filter = tls_file_chooser_filter_new ();
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), filter);
- gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE);
- gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (widget),
+ gtk_size_group_add_widget (group, key);
+ filter = tls_file_chooser_filter_new (TRUE);
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (key), filter);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (key), TRUE);
+ gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (key),
_("Choose your private key..."));
- g_signal_connect (G_OBJECT (widget), "selection-changed", G_CALLBACK (changed_cb), user_data);
+ g_signal_connect (G_OBJECT (key), "selection-changed", G_CALLBACK (changed_cb), user_data);
if (s_vpn) {
value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_KEY);
if (value && strlen (value))
- gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), value);
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (key), value);
}
+
+ /* Link choosers to the PKCS#12 changer callback */
+ g_signal_connect (ca, "selection-changed", G_CALLBACK (tls_cert_changed_cb), cert);
+ g_signal_connect (cert, "selection-changed", G_CALLBACK (tls_cert_changed_cb), key);
+ g_signal_connect (key, "selection-changed", G_CALLBACK (tls_cert_changed_cb), ca);
}
if (!strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD) || !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
@@ -317,19 +369,23 @@ validate_file_chooser (GladeXML *xml, const char *name)
{
GtkWidget *widget;
char *str;
+ gboolean valid = FALSE;
widget = glade_xml_get_widget (xml, name);
str = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
- if (!str || !strlen (str))
- return FALSE;
- return TRUE;
+ if (str && strlen (str))
+ valid = TRUE;
+ g_free (str);
+ return valid;
}
static gboolean
validate_tls (GladeXML *xml, const char *prefix, GError **error)
{
char *tmp;
- gboolean valid;
+ gboolean valid, encrypted = FALSE;
+ GtkWidget *widget;
+ char *str;
tmp = g_strdup_printf ("%s_ca_cert_chooser", prefix);
valid = validate_file_chooser (xml, tmp);
@@ -354,6 +410,7 @@ validate_tls (GladeXML *xml, const char *prefix, GError **error)
}
tmp = g_strdup_printf ("%s_private_key_chooser", prefix);
+ widget = glade_xml_get_widget (xml, tmp);
valid = validate_file_chooser (xml, tmp);
g_free (tmp);
if (!valid) {
@@ -364,6 +421,24 @@ validate_tls (GladeXML *xml, const char *prefix, GError **error)
return FALSE;
}
+ /* Encrypted certificates require a password */
+ str = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
+ encrypted = is_pkcs12 (str) || is_encrypted_pem (str);
+ g_free (str);
+ if (encrypted) {
+ tmp = g_strdup_printf ("%s_private_key_password_entry", prefix);
+ widget = glade_xml_get_widget (xml, tmp);
+ g_free (tmp);
+
+ if (!gtk_entry_get_text_length (GTK_ENTRY (widget))) {
+ g_set_error (error,
+ OPENVPN_PLUGIN_UI_ERROR,
+ OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+ NM_OPENVPN_KEY_CERTPASS);
+ return FALSE;
+ }
+ }
+
return TRUE;
}
@@ -461,12 +536,8 @@ update_from_filechooser (GladeXML *xml,
g_free (tmp);
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
- if (!filename)
- return;
-
- if (strlen (filename))
+ if (filename && strlen (filename))
nm_setting_vpn_add_data_item (s_vpn, key, filename);
-
g_free (filename);
}
@@ -630,6 +701,7 @@ tls_default_filter (const GtkFileFilterInfo *filter_info, gpointer data)
char *contents = NULL, *p, *ext;
gsize bytes_read = 0;
gboolean show = FALSE;
+ gboolean pkcs_allowed = GPOINTER_TO_UINT (data);
struct stat statbuf;
if (!filter_info->filename)
@@ -642,6 +714,12 @@ tls_default_filter (const GtkFileFilterInfo *filter_info, gpointer data)
ext = g_ascii_strdown (p, -1);
if (!ext)
return FALSE;
+
+ if (pkcs_allowed && !strcmp (ext, ".p12") && is_pkcs12 (filter_info->filename)) {
+ g_free (ext);
+ return TRUE;
+ }
+
if (strcmp (ext, ".pem") && strcmp (ext, ".crt") && strcmp (ext, ".key") && strcmp (ext, ".cer")) {
g_free (ext);
return FALSE;
@@ -682,13 +760,14 @@ out:
}
GtkFileFilter *
-tls_file_chooser_filter_new (void)
+tls_file_chooser_filter_new (gboolean pkcs_allowed)
{
GtkFileFilter *filter;
filter = gtk_file_filter_new ();
- gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME, tls_default_filter, NULL, NULL);
- gtk_file_filter_set_name (filter, _("PEM certificates (*.pem, *.crt, *.key, *.cer)"));
+ gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME, tls_default_filter, GUINT_TO_POINTER (pkcs_allowed), NULL);
+ gtk_file_filter_set_name (filter, pkcs_allowed ? _("PEM or PKCS#12 certificates (*.pem, *.crt, *.key, *.cer, *.p12)")
+ : _("PEM certificates (*.pem, *.crt, *.key, *.cer)"));
return filter;
}
diff --git a/properties/auth-helpers.h b/properties/auth-helpers.h
index b5ea395..d3af84b 100644
--- a/properties/auth-helpers.h
+++ b/properties/auth-helpers.h
@@ -63,7 +63,7 @@ gboolean auth_widget_save_secrets (GladeXML *xml,
const char *uuid,
const char *name);
-GtkFileFilter *tls_file_chooser_filter_new (void);
+GtkFileFilter *tls_file_chooser_filter_new (gboolean pkcs_allowed);
GtkFileFilter *sk_file_chooser_filter_new (void);
diff --git a/properties/import-export.c b/properties/import-export.c
index d109b9a..2c14bb2 100644
--- a/properties/import-export.c
+++ b/properties/import-export.c
@@ -65,6 +65,7 @@
#define MSSFIX_TAG "mssfix"
#define TUNMTU_TAG "tun-mtu"
#define FRAGMENT_TAG "fragment"
+#define PKCS12_TAG "pkcs12"
static char *
@@ -388,6 +389,11 @@ do_import (const char *path, char **lines, GError **error)
}
}
+ if ( handle_path_item (*line, PKCS12_TAG, NM_OPENVPN_KEY_CA, s_vpn, default_path, NULL) &&
+ handle_path_item (*line, PKCS12_TAG, NM_OPENVPN_KEY_CERT, s_vpn, default_path, NULL) &&
+ handle_path_item (*line, PKCS12_TAG, NM_OPENVPN_KEY_KEY, s_vpn, default_path, NULL))
+ continue;
+
if (handle_path_item (*line, CA_TAG, NM_OPENVPN_KEY_CA, s_vpn, default_path, NULL))
continue;
@@ -662,12 +668,18 @@ do_export (const char *path, NMConnection *connection, GError **error)
port ? " " : "",
port ? port : "");
- if (cacert)
- fprintf (f, "ca %s\n", cacert);
- if (user_cert)
- fprintf (f, "cert %s\n", user_cert);
- if (private_key)
- fprintf(f, "key %s\n", private_key);
+ /* Handle PKCS#12 (all certs are the same file) */
+ if ( cacert && user_cert && private_key
+ && !strcmp (cacert, user_cert) && !strcmp (cacert, private_key))
+ fprintf (f, "pkcs12 %s\n", cacert);
+ else {
+ if (cacert)
+ fprintf (f, "ca %s\n", cacert);
+ if (user_cert)
+ fprintf (f, "cert %s\n", user_cert);
+ if (private_key)
+ fprintf(f, "key %s\n", private_key);
+ }
if ( !strcmp(connection_type, NM_OPENVPN_CONTYPE_PASSWORD)
|| !strcmp(connection_type, NM_OPENVPN_CONTYPE_PASSWORD_TLS))
diff --git a/properties/tests/conf/Makefile.am b/properties/tests/conf/Makefile.am
index f999b76..6823fa9 100644
--- a/properties/tests/conf/Makefile.am
+++ b/properties/tests/conf/Makefile.am
@@ -6,6 +6,7 @@ EXTRA_DIST = \
static.ovpn \
port.ovpn \
rport.ovpn \
- tun-opts.conf
+ tun-opts.conf \
+ pkcs12.ovpn
diff --git a/properties/tests/conf/pkcs12.ovpn b/properties/tests/conf/pkcs12.ovpn
new file mode 100644
index 0000000..12281b0
--- /dev/null
+++ b/properties/tests/conf/pkcs12.ovpn
@@ -0,0 +1,17 @@
+remote 173.8.149.245 1194
+resolv-retry infinite
+
+dev tun
+persist-key
+persist-tun
+link-mtu 1400
+proto udp
+nobind
+pull
+tls-client
+
+pkcs12 keys/mine.p12
+
+comp-lzo
+verb 3
+
diff --git a/properties/tests/test-import-export.c b/properties/tests/test-import-export.c
index c1fcbcb..c979aad 100644
--- a/properties/tests/test-import-export.c
+++ b/properties/tests/test-import-export.c
@@ -354,6 +354,115 @@ test_tls_export (NMVpnPluginUiInterface *plugin, const char *dir)
}
static void
+test_pkcs12_import (NMVpnPluginUiInterface *plugin, const char *dir)
+{
+ NMConnection *connection;
+ NMSettingConnection *s_con;
+ NMSettingIP4Config *s_ip4;
+ NMSettingVPN *s_vpn;
+ const char *expected_id = "pkcs12";
+ char *expected_path;
+
+ connection = get_basic_connection ("pkcs12-import", plugin, dir, "pkcs12.ovpn");
+ ASSERT (connection != NULL, "pkcs12-import", "failed to import connection");
+
+ /* Connection setting */
+ s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+ ASSERT (s_con != NULL,
+ "pkcs12-import", "missing 'connection' setting");
+
+ ASSERT (strcmp (nm_setting_connection_get_id (s_con), expected_id) == 0,
+ "pkcs12-import", "unexpected connection ID");
+
+ ASSERT (nm_setting_connection_get_uuid (s_con) == NULL,
+ "pkcs12-import", "unexpected valid UUID");
+
+ /* IP4 setting */
+ s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+ ASSERT (s_ip4 == NULL,
+ "pkcs12-import", "unexpected 'ip4-config' setting");
+
+ /* VPN setting */
+ s_vpn = (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN);
+ ASSERT (s_vpn != NULL,
+ "pkcs12-import", "missing 'vpn' setting");
+
+ /* Data items */
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE, NM_OPENVPN_CONTYPE_TLS);
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_TAP_DEV, NULL);
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_PROTO_TCP, NULL);
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_COMP_LZO, "yes");
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_RENEG_SECONDS, NULL);
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_REMOTE, "173.8.149.245");
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_PORT, "1194");
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_STATIC_KEY, NULL);
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_STATIC_KEY_DIRECTION, NULL);
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_CIPHER, NULL);
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_LOCAL_IP, NULL);
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_REMOTE_IP, NULL);
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_AUTH, NULL);
+
+ expected_path = g_strdup_printf ("%s/keys/mine.p12", dir);
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_CA, expected_path);
+ g_free (expected_path);
+
+ expected_path = g_strdup_printf ("%s/keys/mine.p12", dir);
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_CERT, expected_path);
+ g_free (expected_path);
+
+ expected_path = g_strdup_printf ("%s/keys/mine.p12", dir);
+ test_item ("pkcs12-import-data", s_vpn, NM_OPENVPN_KEY_KEY, expected_path);
+ g_free (expected_path);
+
+ /* Secrets */
+ test_secret ("pkcs12-import-secrets", s_vpn, NM_OPENVPN_KEY_PASSWORD, NULL);
+ test_secret ("pkcs12-import-secrets", s_vpn, NM_OPENVPN_KEY_CERTPASS, NULL);
+
+ g_object_unref (connection);
+}
+
+#define PKCS12_EXPORTED_NAME "pkcs12.ovpntest"
+static void
+test_pkcs12_export (NMVpnPluginUiInterface *plugin, const char *dir)
+{
+ NMConnection *connection;
+ NMConnection *reimported;
+ char *path;
+ gboolean success;
+ GError *error = NULL;
+ int ret;
+
+ connection = get_basic_connection ("pkcs12-export", plugin, dir, "pkcs12.ovpn");
+ ASSERT (connection != NULL, "pkcs12-export", "failed to import connection");
+
+ path = g_build_path ("/", dir, PKCS12_EXPORTED_NAME, NULL);
+ success = nm_vpn_plugin_ui_interface_export (plugin, path, connection, &error);
+ if (!success) {
+ if (!error)
+ FAIL ("pkcs12-export", "export failed with missing error");
+ else
+ FAIL ("pkcs12-export", "export failed: %s", error->message);
+ }
+
+ /* Now re-import it and compare the connections to ensure they are the same */
+ reimported = get_basic_connection ("pkcs12-export", plugin, dir, PKCS12_EXPORTED_NAME);
+ ret = unlink (path);
+ ASSERT (connection != NULL, "pkcs12-export", "failed to re-import connection");
+
+ /* Clear secrets first, since they don't get exported, and thus would
+ * make the connection comparison below fail.
+ */
+ remove_secrets (connection);
+
+ ASSERT (nm_connection_compare (connection, reimported, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE,
+ "pkcs12-export", "original and reimported connection differ");
+
+ g_object_unref (reimported);
+ g_object_unref (connection);
+ g_free (path);
+}
+
+static void
test_non_utf8_import (NMVpnPluginUiInterface *plugin, const char *dir)
{
NMConnection *connection;
@@ -388,7 +497,7 @@ test_non_utf8_import (NMVpnPluginUiInterface *plugin, const char *dir)
"non-utf8-import", "missing 'vpn' setting");
expected_path = g_strdup_printf ("%s/%s", dir, expected_cacert);
- test_item ("tls-import-data", s_vpn, NM_OPENVPN_KEY_CA, expected_path);
+ test_item ("non-utf8-import-data", s_vpn, NM_OPENVPN_KEY_CA, expected_path);
g_free (expected_path);
g_object_unref (connection);
@@ -667,6 +776,9 @@ int main (int argc, char **argv)
test_tls_import (plugin, argv[1]);
test_tls_export (plugin, argv[1]);
+ test_pkcs12_import (plugin, argv[1]);
+ test_pkcs12_export (plugin, argv[1]);
+
test_non_utf8_import (plugin, argv[1]);
test_static_key_import (plugin, argv[1]);
diff --git a/src/Makefile.am b/src/Makefile.am
index ea40630..9557f6b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,31 +1,32 @@
-AM_CPPFLAGS = \
- $(DBUS_CFLAGS) \
- $(NETWORK_MANAGER_CFLAGS) \
- -DG_DISABLE_DEPRECATED \
- -DBINDIR=\"$(bindir)\" \
- -DPREFIX=\""$(prefix)"\" \
- -DSYSCONFDIR=\""$(sysconfdir)"\" \
- -DVERSION="\"$(VERSION)\"" \
- -DLIBDIR=\""$(libdir)"\" \
- -DLIBEXECDIR=\""$(libexecdir)"\" \
- -DLOCALSTATEDIR=\""$(localstatedir)"\" \
- -DDATADIR=\"$(datadir)\"
+AM_CPPFLAGS = \
+ $(DBUS_CFLAGS) \
+ $(NETWORK_MANAGER_CFLAGS) \
+ -DG_DISABLE_DEPRECATED \
+ -DBINDIR=\"$(bindir)\" \
+ -DPREFIX=\""$(prefix)"\" \
+ -DSYSCONFDIR=\""$(sysconfdir)"\" \
+ -DVERSION="\"$(VERSION)\"" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DLOCALSTATEDIR=\""$(localstatedir)"\" \
+ -DDATADIR=\"$(datadir)\" \
+ -I$(top_srcdir)
libexec_PROGRAMS = nm-openvpn-service nm-openvpn-service-openvpn-helper
-nm_openvpn_service_SOURCES = \
- nm-openvpn-service.c \
- nm-openvpn-service.h
+nm_openvpn_service_SOURCES = \
+ nm-openvpn-service.c \
+ nm-openvpn-service.h
+nm_openvpn_service_LDADD = \
+ $(NETWORK_MANAGER_LIBS) \
+ $(top_builddir)/common/libnm-openvpn-common.la
-nm_openvpn_service_LDADD = $(NETWORK_MANAGER_LIBS)
+nm_openvpn_service_openvpn_helper_SOURCES = \
+ nm-openvpn-service-openvpn-helper.c
-
-nm_openvpn_service_openvpn_helper_SOURCES = \
- nm-openvpn-service-openvpn-helper.c
-
-nm_openvpn_service_openvpn_helper_LDADD = \
- $(DBUS_LIBS) \
- $(NETWORK_MANAGER_LIBS)
+nm_openvpn_service_openvpn_helper_LDADD = \
+ $(DBUS_LIBS) \
+ $(NETWORK_MANAGER_LIBS)
CLEANFILES = *~
diff --git a/src/nm-openvpn-service.c b/src/nm-openvpn-service.c
index 920abcb..3a71dab 100644
--- a/src/nm-openvpn-service.c
+++ b/src/nm-openvpn-service.c
@@ -52,6 +52,7 @@
#include "nm-openvpn-service.h"
#include "nm-utils.h"
+#include "common/utils.h"
#define NM_OPENVPN_HELPER_PATH LIBEXECDIR"/nm-openvpn-service-openvpn-helper"
@@ -630,6 +631,43 @@ add_openvpn_arg_int (GPtrArray *args, const char *arg)
return TRUE;
}
+static void
+add_cert_args (GPtrArray *args, NMSettingVPN *s_vpn)
+{
+ const char *ca, *cert, *key;
+
+ g_return_if_fail (args != NULL);
+ g_return_if_fail (s_vpn != NULL);
+
+ ca = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CA);
+ cert = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CERT);
+ key = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_KEY);
+
+ if ( ca && strlen (ca)
+ && cert && strlen (cert)
+ && key && strlen (key)
+ && !strcmp (ca, cert)
+ && !strcmp (ca, key)) {
+ add_openvpn_arg (args, "--pkcs12");
+ add_openvpn_arg (args, ca);
+ } else {
+ if (ca && strlen (ca)) {
+ add_openvpn_arg (args, "--ca");
+ add_openvpn_arg (args, ca);
+ }
+
+ if (cert && strlen (cert)) {
+ add_openvpn_arg (args, "--cert");
+ add_openvpn_arg (args, cert);
+ }
+
+ if (key && strlen (key)) {
+ add_openvpn_arg (args, "--key");
+ add_openvpn_arg (args, key);
+ }
+ }
+}
+
static gboolean
nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
NMSettingVPN *s_vpn,
@@ -847,24 +885,7 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
/* Now append configuration options which are dependent on the configuration type */
if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_TLS)) {
add_openvpn_arg (args, "--client");
-
- tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CA);
- if (tmp && strlen (tmp)) {
- add_openvpn_arg (args, "--ca");
- add_openvpn_arg (args, tmp);
- }
-
- tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CERT);
- if (tmp && strlen (tmp)) {
- add_openvpn_arg (args, "--cert");
- add_openvpn_arg (args, tmp);
- }
-
- tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_KEY);
- if (tmp && strlen (tmp)) {
- add_openvpn_arg (args, "--key");
- add_openvpn_arg (args, tmp);
- }
+ add_cert_args (args, s_vpn);
} else if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_STATIC_KEY)) {
tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_STATIC_KEY);
if (tmp && strlen (tmp)) {
@@ -916,25 +937,7 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
}
} else if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
add_openvpn_arg (args, "--client");
-
- tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CA);
- if (tmp && strlen (tmp)) {
- add_openvpn_arg (args, "--ca");
- add_openvpn_arg (args, tmp);
- }
-
- tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CERT);
- if (tmp && strlen (tmp)) {
- add_openvpn_arg (args, "--cert");
- add_openvpn_arg (args, tmp);
- }
-
- tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_KEY);
- if (tmp && strlen (tmp)) {
- add_openvpn_arg (args, "--key");
- add_openvpn_arg (args, tmp);
- }
-
+ add_cert_args (args, s_vpn);
/* Use user/path authentication */
add_openvpn_arg (args, "--auth-user-pass");
} else {
@@ -1097,8 +1100,13 @@ real_need_secrets (NMVPNPlugin *plugin,
}
if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
+ const char *key;
+
/* Will require a password and maybe private key password */
- if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_CERTPASS))
+
+ key = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_KEY);
+ if ( (is_pkcs12 (key) || is_encrypted_pem (key))
+ && !nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_CERTPASS))
need_secrets = TRUE;
if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_PASSWORD))
@@ -1108,8 +1116,12 @@ real_need_secrets (NMVPNPlugin *plugin,
if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_PASSWORD))
need_secrets = TRUE;
} else if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_TLS)) {
+ const char *key;
+
/* May require private key password */
- if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_CERTPASS))
+ key = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_KEY);
+ if ( (is_pkcs12 (key) || is_encrypted_pem (key))
+ && !nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_CERTPASS))
need_secrets = TRUE;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]