[network-manager-openvpn/dcbw/need-secrets: 1/4] core: implement asynchronous secrets
- From: Dan Williams <dcbw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [network-manager-openvpn/dcbw/need-secrets: 1/4] core: implement asynchronous secrets
- Date: Sun, 21 Jul 2013 14:41:46 +0000 (UTC)
commit b80fa832a0c6f392e2722a6cbc3d983295d4c647
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 | 386 ++++++++++++++++++++++++++++++++--------------
1 files changed, 270 insertions(+), 116 deletions(-)
---
diff --git a/src/nm-openvpn-service.c b/src/nm-openvpn-service.c
index 7b4cbd7..ad2363e 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;
@@ -83,6 +85,7 @@ typedef struct {
guint connect_timer;
guint connect_count;
NMOpenvpnPluginIOData *io_data;
+ gboolean async_allowed;
} NMOpenvpnPluginPrivate;
typedef struct {
@@ -268,17 +271,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 +298,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,14 +393,104 @@ 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,
NMVPNPluginFailure *out_failure)
{
- NMOpenvpnPluginIOData *io_data = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin)->io_data;
+ NMOpenvpnPluginPrivate *priv = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin);
gboolean again = TRUE;
- char *str = NULL, *auth = NULL, *buf;
+ char *str = NULL, *auth = NULL;
+ const char *message = NULL;
+ char **hints = NULL;
+
+ g_assert (out_failure);
if (!(condition & G_IO_IN))
return TRUE;
@@ -413,44 +498,45 @@ handle_management_socket (NMVPNPlugin *plugin,
if (g_io_channel_read_line (source, &str, NULL, NULL, NULL) != G_IO_STATUS_NORMAL)
return TRUE;
- if (strlen (str) < 1)
- goto out;
+ if (!str[0]) {
+ g_free (str);
+ return TRUE;
+ }
+
+ 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 (priv->io_data->pending_auth)
+ g_free (priv->io_data->pending_auth);
+ priv->io_data->pending_auth = auth;
+
+ if (handle_auth (priv->io_data, auth, &message, &hints)) {
+ /* Request new secrets if we need any */
+ if (priv->async_allowed) {
+ 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);
+ }
+ } else {
+ /* Async not allowed, can't ask for more secrets */
+ g_warning ("More secrets required but cannot ask asynchrously");
+ *out_failure = NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED;
+ again = FALSE;
+ }
+ if (hints)
+ g_free (hints); /* elements are 'const' */
} else {
- g_warning ("No clue what to send for username/password request for '%s'", auth);
- if (out_failure)
- *out_failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED;
+ g_warning ("Unhandled management socket request '%s'", auth);
+ *out_failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED;
again = FALSE;
}
- g_free (auth);
+ goto out;
}
auth = get_detail (str, ">PASSWORD:Verification Failed: '");
@@ -464,8 +550,7 @@ handle_management_socket (NMVPNPlugin *plugin,
g_free (auth);
- if (out_failure)
- *out_failure = NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED;
+ *out_failure = NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED;
again = FALSE;
}
@@ -727,6 +812,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 +889,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,25 +1212,7 @@ 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);
}
@@ -1167,62 +1275,43 @@ check_need_secrets (NMSettingVPN *s_vpn, gboolean *need_secrets)
}
static gboolean
-real_connect (NMVPNPlugin *plugin,
- NMConnection *connection,
- GError **error)
+_connect_common (NMVPNPlugin *plugin,
+ NMConnection *connection,
+ GHashTable *details,
+ GError **error)
{
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));
+ s_vpn = nm_connection_get_setting_vpn (connection);
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;
@@ -1230,6 +1319,27 @@ real_connect (NMVPNPlugin *plugin,
}
static gboolean
+real_connect (NMVPNPlugin *plugin,
+ NMConnection *connection,
+ GError **error)
+{
+ return _connect_common (plugin, connection, NULL, error);
+}
+
+static gboolean
+real_connect_async_allowed (NMVPNPlugin *plugin,
+ NMConnection *connection,
+ GHashTable *details,
+ GError **error)
+{
+ if (!_connect_common (plugin, connection, details, error))
+ return FALSE;
+
+ NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin)->async_allowed = TRUE;
+ return TRUE;
+}
+
+static gboolean
real_need_secrets (NMVPNPlugin *plugin,
NMConnection *connection,
char **setting_name,
@@ -1247,23 +1357,21 @@ real_need_secrets (NMVPNPlugin *plugin,
nm_connection_dump (connection);
}
- s_vpn = NM_SETTING_VPN (nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN));
+ s_vpn = nm_connection_get_setting_vpn (connection);
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."));
+ g_set_error_literal (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ _("Invalid connection type."));
return FALSE;
}
@@ -1274,6 +1382,50 @@ real_need_secrets (NMVPNPlugin *plugin,
}
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_connection_get_setting_vpn (connection);
+ if (!s_vpn) {
+ 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;
+ }
+
+ 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;
+ }
+
+ /* 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
ensure_killed (gpointer data)
{
int pid = GPOINTER_TO_INT (data);
@@ -1318,8 +1470,10 @@ nm_openvpn_plugin_class_init (NMOpenvpnPluginClass *plugin_class)
/* virtual methods */
parent_class->connect = real_connect;
+ parent_class->connect_async_allowed = real_connect_async_allowed;
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]