diff --git a/src/nm-openconnect-service-openconnect-helper.c b/src/nm-openconnect-service-openconnect-helper.c index 2fa6852..7ad5e75 100644 --- a/src/nm-openconnect-service-openconnect-helper.c +++ b/src/nm-openconnect-service-openconnect-helper.c @@ -620,6 +620,19 @@ main (int argc, char *argv[]) if (val) g_hash_table_insert (ip6config, NM_VPN_PLUGIN_IP6_CONFIG_DNS, val); + /* Session timeout */ + tmp = getenv ("CISCO_CSTP_OPTIONS"); + while (tmp) { + if (!strncmp(tmp, "X-CSTP-Session-Timeout=", 23)) { + val = uint_to_gvalue (strtol (tmp + 23, NULL, 10)); + g_hash_table_insert(config, "openconnect_session_timeout", val); + break; + } + tmp = strchr(tmp, '\n'); + if (tmp) + tmp++; + } + /* Routes */ val = get_ip6_routes (); if (val) { diff --git a/src/nm-openconnect-service.c b/src/nm-openconnect-service.c index 7dcf964..d23640d 100644 --- a/src/nm-openconnect-service.c +++ b/src/nm-openconnect-service.c @@ -54,6 +54,12 @@ G_DEFINE_TYPE (NMOPENCONNECTPlugin, nm_openconnect_plugin, NM_TYPE_VPN_PLUGIN) typedef struct { + char *uuid; + char *cookie; + char *gateway; + char *gwcert; + time_t cookie_start; + unsigned long cookie_validity; GPid pid; char *tun_name; } NMOPENCONNECTPluginPrivate; @@ -103,6 +109,7 @@ static ValidProperty valid_secrets[] = { { NULL, G_TYPE_NONE, 0, 0 } }; + static uid_t tun_owner; static gid_t tun_group; static gboolean debug = FALSE; @@ -114,6 +121,7 @@ typedef struct ValidateInfo { gboolean have_items; } ValidateInfo; + static void validate_one_property (const char *key, const char *value, gpointer user_data) { @@ -387,6 +395,8 @@ nm_openconnect_start_openconnect_binary (NMOPENCONNECTPlugin *plugin, /* The actual gateway to use (after redirection) comes from the auth dialog, so it's in the secrets hash not the properties */ props_vpn_gw = nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_GATEWAY); + if (!props_vpn_gw || !strlen (props_vpn_gw) ) + props_vpn_gw = priv->gateway; if (!props_vpn_gw || !strlen (props_vpn_gw) ) { g_set_error (error, NM_VPN_PLUGIN_ERROR, @@ -397,6 +407,8 @@ nm_openconnect_start_openconnect_binary (NMOPENCONNECTPlugin *plugin, } props_cookie = nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_COOKIE); + if (!props_cookie || !strlen (props_cookie) ) + props_cookie = priv->cookie; if (!props_cookie || !strlen (props_cookie)) { g_set_error (error, NM_VPN_PLUGIN_ERROR, @@ -406,6 +418,8 @@ nm_openconnect_start_openconnect_binary (NMOPENCONNECTPlugin *plugin, return -1; } props_gwcert = nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_GWCERT); + if (!props_gwcert || !strlen (props_gwcert) ) + props_gwcert = priv->gwcert; props_cacert = nm_setting_vpn_get_data_item (s_vpn, NM_OPENCONNECT_KEY_CACERT); props_mtu = nm_setting_vpn_get_data_item (s_vpn, NM_OPENCONNECT_KEY_MTU); @@ -452,6 +466,9 @@ nm_openconnect_start_openconnect_binary (NMOPENCONNECTPlugin *plugin, g_ptr_array_add (openconnect_argv, NULL); + /* Only allow the cookie reuse when connections are successful */ + priv->cookie_validity = 0; + if (!g_spawn_async_with_pipes (NULL, (char **) openconnect_argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, openconnect_drop_child_privs, priv->tun_name, @@ -507,16 +524,34 @@ real_connect (NMVPNPlugin *plugin, } static gboolean +cached_secrets_valid (NMVPNPlugin *plugin, NMSettingVPN *s_vpn, const char *uuid) +{ + NMOPENCONNECTPluginPrivate *priv = NM_OPENCONNECT_PLUGIN_GET_PRIVATE (plugin); + + if (!uuid || !priv->uuid || !priv->cookie_validity || strcmp(priv->uuid, uuid)) + return FALSE; + + /* Only use cached cookie if it's valid for at least an hour */ + if ((priv->cookie_start + priv->cookie_validity) > time(NULL) + 3600) + return TRUE; + + return FALSE; +} + +static gboolean real_need_secrets (NMVPNPlugin *plugin, NMConnection *connection, char **setting_name, GError **error) { + NMOPENCONNECTPluginPrivate *priv = NM_OPENCONNECT_PLUGIN_GET_PRIVATE (plugin); NMSettingVPN *s_vpn; + const char *gateway, *cookie, *gwcert, *uuid; g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE); g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + uuid = nm_connection_get_uuid (connection); s_vpn = NM_SETTING_VPN (nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN)); if (!s_vpn) { g_set_error (error, @@ -530,18 +565,26 @@ real_need_secrets (NMVPNPlugin *plugin, /* We just need the WebVPN cookie, and the final IP address of the gateway (after HTTP redirects, which do happen). All the certificate/SecurID nonsense can be handled for us, in the user's context, by auth-dialog */ - if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_GATEWAY)) { - *setting_name = NM_SETTING_VPN_SETTING_NAME; - return TRUE; - } - if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_COOKIE)) { - *setting_name = NM_SETTING_VPN_SETTING_NAME; - return TRUE; - } - if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_GWCERT)) { + gateway = nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_GATEWAY); + cookie = nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_COOKIE); + gwcert = nm_setting_vpn_get_secret (s_vpn, NM_OPENCONNECT_KEY_GWCERT); + if (!gateway || !cookie || !gwcert) { + if (cached_secrets_valid(plugin, s_vpn, uuid)) + return FALSE; + *setting_name = NM_SETTING_VPN_SETTING_NAME; return TRUE; } + + g_free(priv->uuid); + priv->uuid = g_strdup(uuid); + g_free(priv->cookie); + priv->cookie = g_strdup(cookie); + g_free(priv->gwcert); + priv->gwcert = g_strdup(gwcert); + g_free(priv->gateway); + priv->gateway = g_strdup(gateway); + priv->cookie_start = time(NULL); return FALSE; } @@ -629,6 +672,18 @@ quit_mainloop (NMOPENCONNECTPlugin *plugin, gpointer user_data) g_main_loop_quit ((GMainLoop *) user_data); } +static void got_config (NMOPENCONNECTPlugin *plugin, GHashTable *config, + gpointer user_data) +{ + NMOPENCONNECTPluginPrivate *priv = NM_OPENCONNECT_PLUGIN_GET_PRIVATE (plugin); + GValue *val = g_hash_table_lookup (config, "openconnect_session_timeout"); + + if (!val) + return; + + priv->cookie_validity = g_value_get_uint(val); +} + int main (int argc, char *argv[]) { NMOPENCONNECTPlugin *plugin; @@ -684,6 +739,8 @@ int main (int argc, char *argv[]) if (!persist) g_signal_connect (plugin, "quit", G_CALLBACK (quit_mainloop), loop); + g_signal_connect (plugin, "config", G_CALLBACK (got_config), plugin); + setup_signals (); g_main_loop_run (loop);