[network-manager-openvpn/dcbw/need-secrets] core: implement asynchronous secrets



commit 32ece3c1cb2c304a55776fcd8b6f70042c4ff7a2
Author: Dan Williams <dcbw redhat com>
Date:   Fri Jun 21 11:56:31 2013 -0500

    core: implement asynchronous secrets
    
    Request secrets from NetworkManager when openvpn asks us for them,
    instead of trying to figure out what we need before connecting.

 src/nm-openvpn-service.c |  385 +++++++++++++++++++++++++---------------------
 1 files changed, 209 insertions(+), 176 deletions(-)
---
diff --git a/src/nm-openvpn-service.c b/src/nm-openvpn-service.c
index 7b4cbd7..756b812 100644
--- a/src/nm-openvpn-service.c
+++ b/src/nm-openvpn-service.c
@@ -69,11 +69,13 @@ G_DEFINE_TYPE (NMOpenvpnPlugin, nm_openvpn_plugin, NM_TYPE_VPN_PLUGIN)
 #define NM_OPENVPN_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_OPENVPN_PLUGIN, 
NMOpenvpnPluginPrivate))
 
 typedef struct {
+       char *default_username;
        char *username;
        char *password;
        char *priv_key_pass;
        char *proxy_username;
        char *proxy_password;
+       char *pending_auth;
        GIOChannel *socket_channel;
        guint socket_channel_eventid;
 } NMOpenvpnPluginIOData;
@@ -268,17 +270,8 @@ nm_openvpn_secrets_validate (NMSettingVPN *s_vpn, GError **error)
        ValidateInfo info = { &valid_secrets[0], &validate_error, FALSE };
 
        nm_setting_vpn_foreach_secret (s_vpn, validate_one_property, &info);
-       if (!info.have_items) {
-               g_set_error (error,
-                            NM_VPN_PLUGIN_ERROR,
-                            NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
-                            "%s",
-                            _("No VPN secrets!"));
-               return FALSE;
-       }
-
        if (validate_error) {
-               *error = validate_error;
+               g_propagate_error (error, validate_error);
                return FALSE;
        }
        return TRUE;
@@ -304,6 +297,7 @@ nm_openvpn_disconnect_management_socket (NMOpenvpnPlugin *plugin)
 
        g_free (io_data->username);
        g_free (io_data->proxy_username);
+       g_free (io_data->pending_auth);
 
        if (io_data->password)
                memset (io_data->password, 0, strlen (io_data->password));
@@ -398,6 +392,92 @@ write_user_pass (GIOChannel *channel,
 }
 
 static gboolean
+handle_auth (NMOpenvpnPluginIOData *io_data,
+             const char *requested_auth,
+             const char **out_message,
+             char ***out_hints)
+{
+       gboolean handled = FALSE;
+       guint i = 0;
+       char **hints = NULL;
+
+       g_return_val_if_fail (requested_auth != NULL, FALSE);
+       g_return_val_if_fail (out_message != NULL, FALSE);
+       g_return_val_if_fail (out_hints != NULL, FALSE);
+
+       if (strcmp (requested_auth, "Auth") == 0) {
+               const char *username = io_data->username;
+
+               /* Fall back to the default username if it wasn't overridden by the user */
+               if (!username)
+                       username = io_data->default_username;
+
+               if (username != NULL && io_data->password != NULL) {
+                       write_user_pass (io_data->socket_channel,
+                                        requested_auth,
+                                        username,
+                                        io_data->password);
+               } else {
+                       hints = g_new0 (char *, 3);
+                       if (!username) {
+                               hints[i++] = NM_OPENVPN_KEY_USERNAME;
+                               *out_message = _("A username is required.");
+                       }
+                       if (!io_data->password) {
+                               hints[i++] = NM_OPENVPN_KEY_PASSWORD;
+                               *out_message = _("A password is required.");
+                       }
+                       if (!username && !io_data->password)
+                               *out_message = _("A username and password are required.");
+               }
+               handled = TRUE;
+       } else if (!strcmp (requested_auth, "Private Key")) {
+               if (io_data->priv_key_pass) {
+                       char *qpass, *buf;
+
+                       /* Quote strings passed back to openvpn */
+                       qpass = ovpn_quote_string (io_data->priv_key_pass);
+                       buf = g_strdup_printf ("password \"%s\" \"%s\"\n", requested_auth, qpass);
+                       memset (qpass, 0, strlen (qpass));
+                       g_free (qpass);
+
+                       /* Will always write everything in blocking channels (on success) */
+                       g_io_channel_write_chars (io_data->socket_channel, buf, strlen (buf), NULL, NULL);
+                       g_io_channel_flush (io_data->socket_channel, NULL);
+                       g_free (buf);
+               } else {
+                       hints = g_new0 (char *, 2);
+                       hints[i++] = NM_OPENVPN_KEY_CERTPASS;
+                       *out_message = _("A private key password is required.");
+               }
+               handled = TRUE;
+       } else if (strcmp (requested_auth, "HTTP Proxy") == 0) {
+               if (io_data->proxy_username != NULL && io_data->proxy_password != NULL) {
+                       write_user_pass (io_data->socket_channel,
+                                        requested_auth,
+                                        io_data->proxy_username,
+                                        io_data->proxy_password);
+               } else {
+                       hints = g_new0 (char *, 3);
+                       if (!io_data->proxy_username) {
+                               hints[i++] = NM_OPENVPN_KEY_HTTP_PROXY_USERNAME;
+                               *out_message = _("An HTTP Proxy username is required.");
+                       }
+                       if (!io_data->proxy_password) {
+                               hints[i++] = NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD;
+                               *out_message = _("An HTTP Proxy password is required.");
+                       }
+                       if (!io_data->proxy_username && !io_data->proxy_password)
+                               *out_message = _("An HTTP Proxy username and password are required.");
+               }
+               handled = TRUE;
+       }
+
+       *out_hints = hints;
+       return handled;
+}
+
+static gboolean
 handle_management_socket (NMVPNPlugin *plugin,
                           GIOChannel *source,
                           GIOCondition condition,
@@ -405,7 +485,9 @@ handle_management_socket (NMVPNPlugin *plugin,
 {
        NMOpenvpnPluginIOData *io_data = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin)->io_data;
        gboolean again = TRUE;
-       char *str = NULL, *auth = NULL, *buf;
+       char *str = NULL, *auth = NULL;
+       const char *message = NULL;
+       char **hints = NULL;
 
        if (!(condition & G_IO_IN))
                return TRUE;
@@ -416,41 +498,33 @@ handle_management_socket (NMVPNPlugin *plugin,
        if (strlen (str) < 1)
                goto out;
 
+       if (debug)
+               g_message ("VPN request '%s'", str);
+
        auth = get_detail (str, ">PASSWORD:Need '");
        if (auth) {
-               if (strcmp (auth, "Auth") == 0) {
-                       if (io_data->username != NULL && io_data->password != NULL)
-                               write_user_pass (source, auth, io_data->username, io_data->password);
-                       else
-                               g_warning ("Auth requested but one of username or password is missing");
-               } else if (!strcmp (auth, "Private Key")) {
-                       if (io_data->priv_key_pass) {
-                               char *qpass;
-
-                               /* Quote strings passed back to openvpn */
-                               qpass = ovpn_quote_string (io_data->priv_key_pass);
-                               buf = g_strdup_printf ("password \"%s\" \"%s\"\n", auth, qpass);
-                               memset (qpass, 0, strlen (qpass));
-                               g_free (qpass);
-
-                               /* Will always write everything in blocking channels (on success) */
-                               g_io_channel_write_chars (source, buf, strlen (buf), NULL, NULL);
-                               g_io_channel_flush (source, NULL);
-                               g_free (buf);
-                       } else
-                               g_warning ("Certificate password requested but private key password == NULL");
-               } else if (strcmp (auth, "HTTP Proxy") == 0) {
-                       if (io_data->proxy_username != NULL && io_data->proxy_password != NULL)
-                               write_user_pass (source, auth, io_data->proxy_username, 
io_data->proxy_password);
-                       else
-                               g_warning ("HTTP Proxy auth requested but either proxy username or password 
is missing");
+               if (io_data->pending_auth)
+                       g_free (io_data->pending_auth);
+               io_data->pending_auth = auth;
+
+               if (handle_auth (io_data, auth, &message, &hints)) {
+                       /* Request new secrets if we need any */
+                       if (message) {
+                               if (debug) {
+                                       char *joined = hints ? g_strjoinv (",", (char **) hints) : "none";
+                                       g_message ("Requesting new secrets: '%s' (%s)", message, joined);
+                                       g_free (joined);
+                               }
+                               nm_vpn_plugin_secrets_required (plugin, message, (const char **) hints);
+                       }
+                       if (hints)
+                               g_free (hints);  /* elements are 'const' */
                } else {
-                       g_warning ("No clue what to send for username/password request for '%s'", auth);
+                       g_warning ("Unhandled management socket request '%s'", auth);
                        if (out_failure)
-                               *out_failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED;
+                                  *out_failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED;
                        again = FALSE;
                }
-               g_free (auth);
        }
 
        auth = get_detail (str, ">PASSWORD:Verification Failed: '");
@@ -727,6 +801,48 @@ add_cert_args (GPtrArray *args, NMSettingVPN *s_vpn)
        }
 }
 
+static void
+update_io_data_from_vpn_setting (NMOpenvpnPluginIOData *io_data,
+                                 NMSettingVPN *s_vpn,
+                                 const char *default_username)
+{
+       const char *tmp;
+
+       if (default_username) {
+               g_free (io_data->default_username);
+               io_data->default_username = g_strdup (default_username);
+       }
+
+       g_free (io_data->username);
+       tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_USERNAME);
+       io_data->username = tmp ? g_strdup (tmp) : NULL;
+
+       if (io_data->password) {
+               memset (io_data->password, 0, strlen (io_data->password));
+               g_free (io_data->password);
+       }
+       tmp = nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_PASSWORD);
+       io_data->password = tmp ? g_strdup (tmp) : NULL;
+
+       if (io_data->priv_key_pass) {
+               memset (io_data->priv_key_pass, 0, strlen (io_data->priv_key_pass));
+               g_free (io_data->priv_key_pass);
+       }
+       tmp = nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_CERTPASS);
+       io_data->priv_key_pass = tmp ? g_strdup (tmp) : NULL;
+
+       g_free (io_data->proxy_username);
+       tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_USERNAME);
+       io_data->proxy_username = tmp ? g_strdup (tmp) : NULL;
+
+       if (io_data->proxy_password) {
+               memset (io_data->proxy_password, 0, strlen (io_data->proxy_password));
+               g_free (io_data->proxy_password);
+       }
+       tmp = nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD);
+       io_data->proxy_password = tmp ? g_strdup (tmp) : NULL;
+}
+
 static gboolean
 nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
                                  NMSettingVPN *s_vpn,
@@ -762,9 +878,8 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
                }
        }
 
-       tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE);
-       connection_type = validate_connection_type (tmp);
-       if (!connection_type) {
+       connection_type = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE);
+       if (!validate_connection_type (connection_type)) {
                g_set_error (error,
                             NM_VPN_PLUGIN_ERROR,
                             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
@@ -1086,86 +1201,13 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
            || nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_USERNAME)) {
 
                priv->io_data = g_malloc0 (sizeof (NMOpenvpnPluginIOData));
-
-               tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_USERNAME);
-               priv->io_data->username = tmp ? g_strdup (tmp) : NULL;
-               /* Use the default username if it wasn't overridden by the user */
-               if (!priv->io_data->username && default_username)
-                       priv->io_data->username = g_strdup (default_username);
-
-               tmp = nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_PASSWORD);
-               priv->io_data->password = tmp ? g_strdup (tmp) : NULL;
-
-               tmp = nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_CERTPASS);
-               priv->io_data->priv_key_pass = tmp ? g_strdup (tmp) : NULL;
-
-               tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_USERNAME);
-               priv->io_data->proxy_username = tmp ? g_strdup (tmp) : NULL;
-
-               tmp = nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD);
-               priv->io_data->proxy_password = tmp ? g_strdup (tmp) : NULL;
-
+               update_io_data_from_vpn_setting (priv->io_data, s_vpn, default_username);
                nm_openvpn_schedule_connect_timer (plugin);
        }
 
        return TRUE;
 }
 
-static const char *
-check_need_secrets (NMSettingVPN *s_vpn, gboolean *need_secrets)
-{
-       const char *tmp, *key, *ctype;
-       NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-
-       g_return_val_if_fail (s_vpn != NULL, FALSE);
-       g_return_val_if_fail (need_secrets != NULL, FALSE);
-
-       *need_secrets = FALSE;
-
-       tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE);
-       ctype = validate_connection_type (tmp);
-       if (!ctype)
-               return NULL;
-
-       if (!strcmp (ctype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
-               /* Will require a password and maybe private key password */
-               key = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_KEY);
-               if (is_encrypted (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)) {
-                       *need_secrets = TRUE;
-                       if (nm_setting_get_secret_flags (NM_SETTING (s_vpn), NM_OPENVPN_KEY_PASSWORD, 
&secret_flags, NULL)) {
-                               if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
-                                       *need_secrets = FALSE;
-                       }
-               }
-       } else if (!strcmp (ctype, NM_OPENVPN_CONTYPE_PASSWORD)) {
-               /* Will require a password */
-               if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_PASSWORD)) {
-                       *need_secrets = TRUE;
-                       if (nm_setting_get_secret_flags (NM_SETTING (s_vpn), NM_OPENVPN_KEY_PASSWORD, 
&secret_flags, NULL)) {
-                               if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
-                                       *need_secrets = FALSE;
-                       }
-               }
-       } else if (!strcmp (ctype, NM_OPENVPN_CONTYPE_TLS)) {
-               /* May require private key password */
-               key = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_KEY);
-               if (is_encrypted (key) && !nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_CERTPASS))
-                       *need_secrets = TRUE;
-       } else {
-               /* Static key doesn't need passwords */
-       }
-
-       /* HTTP Proxy might require a password; assume so if there's an HTTP proxy username */
-       tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_USERNAME);
-       if (tmp && !nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD))
-               *need_secrets = TRUE;
-
-       return ctype;
-}
-
 static gboolean
 real_connect (NMVPNPlugin   *plugin,
               NMConnection  *connection,
@@ -1174,55 +1216,35 @@ real_connect (NMVPNPlugin   *plugin,
        NMSettingVPN *s_vpn;
        const char *connection_type;
        const char *user_name;
-       gboolean need_secrets;
 
        s_vpn = NM_SETTING_VPN (nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN));
        if (!s_vpn) {
-               g_set_error (error,
-                            NM_VPN_PLUGIN_ERROR,
-                            NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
-                            "%s",
-                            _("Could not process the request because the VPN connection settings were 
invalid."));
+               g_set_error_literal (error,
+                                    NM_VPN_PLUGIN_ERROR,
+                                    NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
+                                    _("Could not process the request because the VPN connection settings 
were invalid."));
                return FALSE;
        }
 
-       /* Check if we need secrets and validate the connection type */
-       connection_type = check_need_secrets (s_vpn, &need_secrets);
-       if (!connection_type) {
-               g_set_error (error,
-                            NM_VPN_PLUGIN_ERROR,
-                            NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
-                            "%s",
-                            _("Invalid connection type."));
+       connection_type = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE);
+       if (!validate_connection_type (connection_type)) {
+               g_set_error_literal (error,
+                                    NM_VPN_PLUGIN_ERROR,
+                                    NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
+                                    _("Could not process the request because the openvpn connection type was 
invalid."));
                return FALSE;
        }
 
-       user_name = nm_setting_vpn_get_user_name (s_vpn);
-
-       /* Need a username for any password-based connection types */
-       if (   !strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD_TLS)
-           || !strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD)) {
-               if (!user_name && !nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_USERNAME)) {
-                       g_set_error (error,
-                                    NM_VPN_PLUGIN_ERROR,
-                                    NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
-                                    "%s",
-                                    _("Could not process the request because no username was provided."));
-                       return FALSE;
-               }
-       }
-
        /* Validate the properties */
        if (!nm_openvpn_properties_validate (s_vpn, error))
                return FALSE;
 
        /* Validate secrets */
-       if (need_secrets) {
-               if (!nm_openvpn_secrets_validate (s_vpn, error))
-                       return FALSE;
-       }
+       if (!nm_openvpn_secrets_validate (s_vpn, error))
+               return FALSE;
 
        /* Finally try to start OpenVPN */
+       user_name = nm_setting_vpn_get_user_name (s_vpn);
        if (!nm_openvpn_start_openvpn_binary (NM_OPENVPN_PLUGIN (plugin), s_vpn, user_name, error))
                return FALSE;
 
@@ -1235,42 +1257,52 @@ real_need_secrets (NMVPNPlugin *plugin,
                    char **setting_name,
                    GError **error)
 {
-       NMSettingVPN *s_vpn;
-       const char *connection_type;
-       gboolean need_secrets = FALSE;
-
-       g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE);
-       g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+       /* Secrets are request asynchronously during Connect() */
+       return FALSE;
+}
 
-       if (debug) {
-               g_message ("%s: connection -------------------------------------", __func__);
-               nm_connection_dump (connection);
-       }
+static gboolean
+real_new_secrets (NMVPNPlugin *plugin,
+                  NMConnection *connection,
+                  GError **error)
+{
+       NMOpenvpnPluginPrivate *priv = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin);
+       NMSettingVPN *s_vpn;
+       const char *message = NULL;
+       char **hints = NULL;
 
        s_vpn = NM_SETTING_VPN (nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN));
        if (!s_vpn) {
-               g_set_error (error,
-                            NM_VPN_PLUGIN_ERROR,
-                            NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
-                            "%s",
-                            _("Could not process the request because the VPN connection settings were 
invalid."));
+               g_set_error_literal (error,
+                                    NM_VPN_PLUGIN_ERROR,
+                                    NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
+                                    _("Could not process the request because the VPN connection settings 
were invalid."));
                return FALSE;
        }
 
-       connection_type = check_need_secrets (s_vpn, &need_secrets);
-       if (!connection_type) {
-               g_set_error (error,
-                            NM_VPN_PLUGIN_ERROR,
-                            NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
-                            "%s",
-                            _("Invalid connection type."));
+       if (debug)
+               g_message ("VPN received new secrets; sending to management interface");
+
+       update_io_data_from_vpn_setting (priv->io_data, s_vpn, NULL);
+
+       g_warn_if_fail (priv->io_data->pending_auth);
+       if (!handle_auth (priv->io_data, priv->io_data->pending_auth, &message, &hints)) {
+               g_set_error_literal (error,
+                                    NM_VPN_PLUGIN_ERROR,
+                                    NM_VPN_PLUGIN_ERROR_GENERAL,
+                                    _("Unhandled pending authentication."));
                return FALSE;
        }
 
-       if (need_secrets)
-               *setting_name = NM_SETTING_VPN_SETTING_NAME;
-
-       return need_secrets;
+       /* Request new secrets if we need any */
+       if (message) {
+               if (debug)
+                       g_message ("Requesting new secrets: '%s'", message);
+               nm_vpn_plugin_secrets_required (plugin, message, (const char **) hints);
+       }
+       if (hints)
+               g_free (hints);  /* elements are 'const' */
+       return TRUE;
 }
 
 static gboolean
@@ -1320,6 +1352,7 @@ nm_openvpn_plugin_class_init (NMOpenvpnPluginClass *plugin_class)
        parent_class->connect      = real_connect;
        parent_class->need_secrets = real_need_secrets;
        parent_class->disconnect   = real_disconnect;
+       parent_class->new_secrets  = real_new_secrets;
 }
 
 static void


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