core/ui: add 'remote-cert-tls' option (bgo #685790)
authorJiří Klimeš <jklimes@redhat.com>
Fri, 2 Nov 2012 12:17:38 +0000 (13:17 +0100)
committerDan Williams <dcbw@redhat.com>
Wed, 7 Nov 2012 22:00:10 +0000 (16:00 -0600)
We already support 'tls-remote', i.e. verifying server's X509/common name.
However, it make sense to support 'remote-cert-tls' as well.  It's another
important security option to protect against MITM attacks. For more info see
http://openvpn.net/index.php/open-source/documentation/howto.html#mitm

properties/auth-helpers.c
properties/import-export.c
properties/nm-openvpn-dialog.ui
properties/tests/conf/tls.ovpn
properties/tests/test-import-export.c
src/nm-openvpn-service.c
src/nm-openvpn-service.h

index 8ced138..2aef793 100644 (file)
@@ -947,6 +947,7 @@ static const char *advanced_keys[] = {
        NM_OPENVPN_KEY_RENEG_SECONDS,
        NM_OPENVPN_KEY_TLS_REMOTE,
        NM_OPENVPN_KEY_REMOTE_RANDOM,
+       NM_OPENVPN_KEY_REMOTE_CERT_TLS,
        NULL
 };
 
@@ -1204,6 +1205,53 @@ populate_hmacauth_combo (GtkComboBox *box, const char *hmacauth)
        g_object_unref (store);
 }
 
+static void
+remote_tls_cert_toggled_cb (GtkWidget *widget, gpointer user_data)
+{
+       GtkBuilder *builder = (GtkBuilder *) user_data;
+       gboolean use_remote_cert_tls = FALSE;
+
+       widget = GTK_WIDGET (gtk_builder_get_object (builder, "remote_cert_tls_checkbutton"));
+       use_remote_cert_tls = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+       widget = GTK_WIDGET (gtk_builder_get_object (builder, "remote_cert_tls_label"));
+       gtk_widget_set_sensitive (widget, use_remote_cert_tls);
+
+       widget = GTK_WIDGET (gtk_builder_get_object (builder, "remote_cert_tls_combo"));
+       gtk_widget_set_sensitive (widget, use_remote_cert_tls);
+}
+
+#define REMOTE_CERT_COL_NAME 0
+#define REMOTE_CERT_COL_VALUE 1
+
+static void
+populate_remote_cert_tls_combo (GtkComboBox *box, const char *remote_cert)
+{
+       GtkListStore *store;
+       GtkTreeIter iter;
+
+       store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+       gtk_combo_box_set_model (box, GTK_TREE_MODEL (store));
+
+       gtk_list_store_append (store, &iter);
+       gtk_list_store_set (store, &iter,
+                           REMOTE_CERT_COL_NAME, _("Server"),
+                           REMOTE_CERT_COL_VALUE, NM_OPENVPN_REM_CERT_TLS_SERVER,
+                           -1);
+       gtk_list_store_append (store, &iter);
+       gtk_list_store_set (store, &iter,
+                           REMOTE_CERT_COL_NAME, _("Client"),
+                           REMOTE_CERT_COL_VALUE, NM_OPENVPN_REM_CERT_TLS_CLIENT,
+                           -1);
+
+       if (g_strcmp0 (remote_cert, NM_OPENVPN_REM_CERT_TLS_CLIENT) == 0)
+               gtk_combo_box_set_active (box, 1);
+       else
+               gtk_combo_box_set_active (box, 0);
+
+       g_object_unref (store);
+}
+
 static void
 tls_auth_toggled_cb (GtkWidget *widget, gpointer user_data)
 {
@@ -1550,6 +1598,17 @@ advanced_dialog_new (GHashTable *hash, const char *contype)
                gtk_entry_set_text (GTK_ENTRY(widget), value);
        }
 
+       widget = GTK_WIDGET (gtk_builder_get_object (builder, "remote_cert_tls_checkbutton"));
+       value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_REMOTE_CERT_TLS);
+       if (value && strlen (value))
+               gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+       g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (remote_tls_cert_toggled_cb), builder);
+       remote_tls_cert_toggled_cb (widget, builder);
+
+       widget = GTK_WIDGET (gtk_builder_get_object (builder, "remote_cert_tls_combo"));
+       value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_REMOTE_CERT_TLS);
+       populate_remote_cert_tls_combo (GTK_COMBO_BOX (widget), value);
+
        if (   !strcmp (contype, NM_OPENVPN_CONTYPE_TLS)
            || !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)
            || !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD)) {
@@ -1777,6 +1836,21 @@ advanced_dialog_new_hash_from_dialog (GtkWidget *dialog, GError **error)
                if (value && strlen (value))
                        g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_TLS_REMOTE), g_strdup (value));
 
+               widget = GTK_WIDGET (gtk_builder_get_object (builder, "remote_cert_tls_checkbutton"));
+               if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
+                       widget = GTK_WIDGET (gtk_builder_get_object (builder, "remote_cert_tls_combo"));
+                       model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+                       if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter)) {
+                               char *remote_cert = NULL;
+
+                               gtk_tree_model_get (model, &iter, REMOTE_CERT_COL_VALUE, &remote_cert, -1);
+                               if (remote_cert)
+                                       g_hash_table_insert (hash,
+                                                            g_strdup (NM_OPENVPN_KEY_REMOTE_CERT_TLS),
+                                                            remote_cert);
+                       }
+               }
+
                widget = GTK_WIDGET (gtk_builder_get_object (builder, "tls_auth_checkbutton"));
                if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
                        char *filename;
index 8a451cf..9fc6e0e 100644 (file)
@@ -70,6 +70,7 @@
 #define TLS_AUTH_TAG "tls-auth "
 #define TLS_CLIENT_TAG "tls-client"
 #define TLS_REMOTE_TAG "tls-remote "
+#define REMOTE_CERT_TLS_TAG "remote-cert-tls "
 #define TUNMTU_TAG "tun-mtu "
 
 
@@ -605,6 +606,20 @@ do_import (const char *path, char **lines, GError **error)
                        continue;
                }
 
+               if (!strncmp (*line, REMOTE_CERT_TLS_TAG, strlen (REMOTE_CERT_TLS_TAG))) {
+                       items = get_args (*line + strlen (REMOTE_CERT_TLS_TAG), &nitems);
+                       if (nitems == 1) {
+                               if (   !strcmp (items[0], NM_OPENVPN_REM_CERT_TLS_CLIENT)
+                                   || !strcmp (items[0], NM_OPENVPN_REM_CERT_TLS_SERVER)) {
+                                       nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_REMOTE_CERT_TLS, items[0]);
+                               } else
+                                       g_warning ("%s: unknown %s option '%s'", __func__, REMOTE_CERT_TLS_TAG, *line);
+                       }
+
+                       g_strfreev (items);
+                       continue;
+               }
+
                if (!strncmp (*line, IFCONFIG_TAG, strlen (IFCONFIG_TAG))) {
                        items = get_args (*line + strlen (IFCONFIG_TAG), &nitems);
                        if (nitems == 2) {
@@ -726,6 +741,7 @@ do_export (const char *path, NMConnection *connection, GError **error)
        const char *local_ip = NULL;
        const char *remote_ip = NULL;
        const char *tls_remote = NULL;
+       const char *remote_cert_tls = NULL;
        const char *tls_auth = NULL;
        const char *tls_auth_dir = NULL;
        gboolean success = FALSE;
@@ -841,6 +857,10 @@ do_export (const char *path, NMConnection *connection, GError **error)
        if (value && strlen (value))
                tls_auth_dir = value;
 
+       value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_REMOTE_CERT_TLS);
+       if (value && strlen (value))
+               remote_cert_tls = value;
+
        /* Advanced values end */
 
        fprintf (f, "client\n");
@@ -910,6 +930,9 @@ do_export (const char *path, NMConnection *connection, GError **error)
                if (tls_remote)
                        fprintf (f,"tls-remote \"%s\"\n", tls_remote);
 
+               if (remote_cert_tls)
+                       fprintf (f,"remote-cert-tls %s\n", remote_cert_tls);
+
                if (tls_auth) {
                        fprintf (f, "tls-auth %s%s%s\n",
                                 tls_auth,
index 35d905a..6c32e7f 100644 (file)
       </row>
     </data>
   </object>
+  <object class="GtkListStore" id="model7">
+    <columns>
+      <!-- column-name gchararray -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes"> </col>
+      </row>
+    </data>
+  </object>
   <object class="GtkDialog" id="openvpn-advanced-dialog">
     <property name="can_focus">False</property>
     <property name="border_width">5</property>
@@ -644,6 +655,80 @@ config: tls-remote</property>
                         <property name="position">0</property>
                       </packing>
                     </child>
+                    <child>
+                      <object class="GtkTable" id="table12">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="valign">start</property>
+                        <property name="n_rows">2</property>
+                        <property name="n_columns">2</property>
+                        <property name="column_spacing">12</property>
+                        <property name="row_spacing">3</property>
+                        <child>
+                          <object class="GtkCheckButton" id="remote_cert_tls_checkbutton">
+                            <property name="label" translatable="yes">_Verify peer (server) certificate usage signature</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="tooltip_text" translatable="yes">Require that peer certificate was signed with an explicit key usage and extended key usage based on RFC3280 TLS rules.</property>
+                          </object>
+                          <packing>
+                            <property name="right_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="remote_cert_tls_label">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">_Remote peer certificate TLS type:</property>
+                            <property name="use_underline">True</property>
+                            <property name="mnemonic_widget">remote_cert_tls_combo</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="right_attach">1</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="x_padding">12</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkComboBox" id="remote_cert_tls_combo">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="model">model7</property>
+                            <property name="tooltip_text" translatable="yes">Require that peer certificate was signed with an explicit key usage and extended key usage based on RFC3280 TLS rules.
+config: remote-cert-tls client|server</property>
+                            <child>
+                              <object class="GtkCellRendererText" id="renderer9"/>
+                              <attributes>
+                                <attribute name="text">0</attribute>
+                              </attributes>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="padding">6</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
                     <child>
                       <object class="GtkTable" id="table7">
                         <property name="visible">True</property>
@@ -778,7 +863,7 @@ config: tls-auth &lt;file&gt; [direction]</property>
                       <packing>
                         <property name="expand">True</property>
                         <property name="fill">True</property>
-                        <property name="position">1</property>
+                        <property name="position">2</property>
                       </packing>
                     </child>
                   </object>
index 2748519..8309c89 100644 (file)
@@ -15,6 +15,7 @@ cert keys/clee.crt
 key keys/clee.key
 
 tls-auth keys/46.key 1
+remote-cert-tls server
 tls-remote "/CN=myvpn.company.com"
 
 comp-lzo
index 025b12d..5f0c92e 100644 (file)
@@ -284,6 +284,7 @@ test_tls_import (NMVpnPluginUiInterface *plugin, const char *dir)
        test_item ("tls-import-data", s_vpn, NM_OPENVPN_KEY_REMOTE_IP, NULL);
        test_item ("tls-import-data", s_vpn, NM_OPENVPN_KEY_AUTH, NULL);
        test_item ("tls-import-data", s_vpn, NM_OPENVPN_KEY_TLS_REMOTE, "/CN=myvpn.company.com");
+       test_item ("tls-import-data", s_vpn, NM_OPENVPN_KEY_REMOTE_CERT_TLS, "server");
 
        expected_path = g_strdup_printf ("%s/keys/mg8.ca", dir);
        test_item ("tls-import-data", s_vpn, NM_OPENVPN_KEY_CA, expected_path);
index 660fd2f..5eaed74 100644 (file)
@@ -119,7 +119,8 @@ static ValidProperty valid_properties[] = {
        { NM_OPENVPN_KEY_TA,                   G_TYPE_STRING, 0, 0, FALSE },
        { NM_OPENVPN_KEY_TA_DIR,               G_TYPE_INT, 0, 1, FALSE },
        { NM_OPENVPN_KEY_TAP_DEV,              G_TYPE_BOOLEAN, 0, 0, FALSE },
-       { NM_OPENVPN_KEY_TLS_REMOTE,           G_TYPE_STRING, 0, 0, FALSE },
+       { NM_OPENVPN_KEY_TLS_REMOTE,           G_TYPE_STRING, 0, 0, FALSE },
+       { NM_OPENVPN_KEY_REMOTE_CERT_TLS,      G_TYPE_STRING, 0, 0, FALSE },
        { NM_OPENVPN_KEY_TUNNEL_MTU,           G_TYPE_INT, 0, G_MAXINT, FALSE },
        { NM_OPENVPN_KEY_USERNAME,             G_TYPE_STRING, 0, 0, FALSE },
        { NM_OPENVPN_KEY_PASSWORD"-flags",     G_TYPE_STRING, 0, 0, FALSE },
@@ -890,6 +891,13 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
                 add_openvpn_arg (args, tmp);
        }
 
+       /* remote-cert-tls */
+       tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_REMOTE_CERT_TLS);
+       if (tmp && strlen (tmp)) {
+                add_openvpn_arg (args, "--remote-cert-tls");
+                add_openvpn_arg (args, tmp);
+       }
+
        /* Reneg seconds */
        tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_RENEG_SECONDS);
        if (tmp && strlen (tmp)) {
index 9266a64..a60fa89 100644 (file)
@@ -66,6 +66,7 @@
 #define NM_OPENVPN_KEY_USERNAME "username"
 #define NM_OPENVPN_KEY_TAP_DEV "tap-dev"
 #define NM_OPENVPN_KEY_TLS_REMOTE "tls-remote"
+#define NM_OPENVPN_KEY_REMOTE_CERT_TLS "remote-cert-tls"
 
 #define NM_OPENVPN_KEY_PASSWORD "password"
 #define NM_OPENVPN_KEY_CERTPASS "cert-pass"
 #define NM_OPENVPN_CONTYPE_PASSWORD     "password"
 #define NM_OPENVPN_CONTYPE_PASSWORD_TLS "password-tls"
 
+/* arguments of "--remote-cert-tls" */
+#define NM_OPENVPN_REM_CERT_TLS_CLIENT "client"
+#define NM_OPENVPN_REM_CERT_TLS_SERVER "server"
+
 typedef struct {
        NMVPNPlugin parent;
 } NMOpenvpnPlugin;