[PATCH 3/4] openconnect: Add asking cookie from agent

Jukka Rissanen jukka.rissanen at linux.intel.com
Fri Nov 2 01:50:30 PDT 2012


It is possible to ask the cookie from agent for OpenConnect
VPN client. The agent should connect to VPN gateway, get the
HTML page, show the page in window or parse the HTML, then
allow user to feed the user id and passphrase and post the result.
The server will then return cookie to agent which should pass
the cookie to openconnect plugin in connman-vpnd daemon.
---
 test/simple-agent         |  87 +++++++++++++-
 vpn/plugins/openconnect.c | 283 +++++++++++++++++++++++++++++++++++++++++++---
 vpn/plugins/vpn.c         |   2 +-
 vpn/vpn-provider.c        |  44 +++++--
 vpn/vpn-provider.h        |   8 ++
 vpn/vpn.h                 |   2 +-
 6 files changed, 402 insertions(+), 24 deletions(-)

diff --git a/test/simple-agent b/test/simple-agent
index 45437df..4a8c3a7 100755
--- a/test/simple-agent
+++ b/test/simple-agent
@@ -169,6 +169,76 @@ class Agent(dbus.service.Object):
 	def Cancel(self):
 		print "Cancel"
 
+class VpnAgent(dbus.service.Object):
+	name = None
+	host = None
+	cookie = None
+
+	@dbus.service.method("net.connman.vpn.Agent",
+					in_signature='', out_signature='')
+	def Release(self):
+		print("Release VPN agent")
+		mainloop.quit()
+
+	def input_cookie(self):
+		response = {}
+
+		if not self.cookie:
+			print "VPN credentials requested, type cancel to cancel"
+			args = raw_input('Answer: ')
+
+			for arg in args.split():
+				if arg.startswith("cancel"):
+					response["Error"] = arg
+				if arg.startswith("Cookie="):
+					cookie = arg.replace("Cookie=", "", 1)
+					response["OpenConnect.Cookie"] = cookie
+		else:
+			if self.cookie:
+				response["OpenConnect.Cookie"] = self.cookie
+
+		return response
+
+	@dbus.service.method("net.connman.vpn.Agent",
+					in_signature='oa{sv}',
+					out_signature='a{sv}')
+	def RequestInput(self, path, fields):
+		print "RequestInput (%s,%s)" % (path, fields)
+
+		response = {}
+
+		if fields.has_key("OpenConnect.Cookie"):
+			response.update(self.input_cookie())
+
+		if response.has_key("Error"):
+			if response["Error"] == "cancel":
+				raise Canceled("canceled")
+				return
+
+		print "returning (%s)" % (response)
+
+		return response
+
+	@dbus.service.method("net.connman.vpn.Agent",
+					in_signature='os',
+					out_signature='')
+	def ReportError(self, path, error):
+		print "ReportError %s, %s" % (path, error)
+		retry = raw_input("Retry service (yes/no): ")
+		if (retry == "yes"):
+			class Retry(dbus.DBusException):
+				_dbus_error_name = "net.connman.vpn.Agent.Error.Retry"
+
+			raise Retry("retry service")
+		else:
+			return
+
+
+	@dbus.service.method("net.connman.vpn.Agent",
+					in_signature='', out_signature='')
+	def Cancel(self):
+		print "Cancel"
+
 def print_usage():
 	print "Usage:"
 	print "For hidden service:"
@@ -177,6 +247,8 @@ def print_usage():
 	print "%s Identity=<identity> Passphrase=<passphrase> WPS=<wpspin>" % (sys.argv[0])
 	print "For WISPr login input:"
 	print "%s Username=<username> Password=<password>" % (sys.argv[0])
+	print "For OpenConnect:"
+	print "%s Cookie=<string>" % (sys.argv[0])
 	print "Help: %s help" % (sys.argv[0])
 	sys.exit(1)
 
@@ -193,6 +265,11 @@ if __name__ == '__main__':
 	path = "/test/agent"
 	object = Agent(bus, path)
 
+	vpn_manager = dbus.Interface(bus.get_object('net.connman.vpn', "/"),
+					'net.connman.vpn.Manager')
+	path = "/test/vpn_agent"
+	vpn_object = VpnAgent(bus, path)
+
 	if len(sys.argv) >= 2:
 		for arg in sys.argv[1:]:
 			if arg.startswith("Name="):
@@ -209,10 +286,18 @@ if __name__ == '__main__':
 				object.username = arg.replace("Username=", "", 1)
 			elif arg.startswith("Password="):
 				object.password = arg.replace("Password=", "", 1)
+			elif arg.startswith("Cookie="):
+				vpn_object.cookie = arg.replace("Cookie=", "", 1)
 			else:
 				print_usage()
 
-	manager.RegisterAgent(path)
+	try:
+		manager.RegisterAgent(path)
+	except:
+		print "Cannot register connman agent."
+
+	vpn_manager.RegisterAgent(path)
+
 
 	mainloop = gobject.MainLoop()
 	mainloop.run()
diff --git a/vpn/plugins/openconnect.c b/vpn/plugins/openconnect.c
index 143e175..fa08779 100644
--- a/vpn/plugins/openconnect.c
+++ b/vpn/plugins/openconnect.c
@@ -36,11 +36,22 @@
 #include <connman/log.h>
 #include <connman/task.h>
 #include <connman/ipconfig.h>
+#include <connman/dbus.h>
+#include <connman/agent.h>
+#include <connman/setting.h>
+#include <connman/vpn-dbus.h>
 
 #include "../vpn-provider.h"
 
 #include "vpn.h"
 
+struct oc_private_data {
+	struct connman_task *task;
+	char *if_name;
+	vpn_provider_connect_cb_t cb;
+	void *user_data;
+};
+
 static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
 {
 	DBusMessageIter iter, dict;
@@ -160,27 +171,210 @@ static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
 	return VPN_STATE_CONNECT;
 }
 
-static int oc_connect(struct vpn_provider *provider,
+static connman_bool_t check_reply_has_dict(DBusMessage *reply)
+{
+	const char *signature = DBUS_TYPE_ARRAY_AS_STRING
+		DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+		DBUS_TYPE_STRING_AS_STRING
+		DBUS_TYPE_VARIANT_AS_STRING
+		DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
+
+	if (dbus_message_has_signature(reply, signature) == TRUE)
+		return TRUE;
+
+	connman_warn("Reply %s to %s from %s has wrong signature %s",
+			signature,
+			dbus_message_get_interface(reply),
+			dbus_message_get_sender(reply),
+			dbus_message_get_signature(reply));
+
+	return FALSE;
+}
+
+static void request_input_append_name(DBusMessageIter *iter, void *user_data)
+{
+	struct vpn_provider *provider = user_data;
+	const char *str = "string";
+
+	connman_dbus_dict_append_basic(iter, "Type",
+				DBUS_TYPE_STRING, &str);
+	str = "informational";
+	connman_dbus_dict_append_basic(iter, "Requirement",
+				DBUS_TYPE_STRING, &str);
+
+	str = vpn_provider_get_name(provider);
+	connman_dbus_dict_append_basic(iter, "Value",
+				DBUS_TYPE_STRING, &str);
+}
+
+static void request_input_append_host(DBusMessageIter *iter, void *user_data)
+{
+	struct vpn_provider *provider = user_data;
+	const char *str = "string";
+
+	connman_dbus_dict_append_basic(iter, "Type",
+				DBUS_TYPE_STRING, &str);
+	str = "informational";
+	connman_dbus_dict_append_basic(iter, "Requirement",
+				DBUS_TYPE_STRING, &str);
+
+	str = vpn_provider_get_host(provider);
+	connman_dbus_dict_append_basic(iter, "Value",
+				DBUS_TYPE_STRING, &str);
+}
+
+static void request_input_append_cookie(DBusMessageIter *iter,
+							void *user_data)
+{
+	char *str = "string";
+
+	connman_dbus_dict_append_basic(iter, "Type",
+				DBUS_TYPE_STRING, &str);
+	str = "mandatory";
+	connman_dbus_dict_append_basic(iter, "Requirement",
+				DBUS_TYPE_STRING, &str);
+}
+
+struct request_input_reply {
+	struct vpn_provider *provider;
+	vpn_provider_auth_cb_t callback;
+	void *user_data;
+};
+
+static void request_input_cookie_reply(DBusMessage *reply, void *user_data)
+{
+	struct request_input_reply *cookie_reply = user_data;
+	const char *error = NULL;
+	char *cookie = NULL;
+	char *key;
+	DBusMessageIter iter, dict;
+
+	DBG("provider %p", cookie_reply->provider);
+
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+		error = dbus_message_get_error_name(reply);
+		goto done;
+	}
+
+	if (check_reply_has_dict(reply) == FALSE)
+		goto done;
+
+	dbus_message_iter_init(reply, &iter);
+	dbus_message_iter_recurse(&iter, &dict);
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry, value;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+			break;
+
+		dbus_message_iter_get_basic(&entry, &key);
+
+		if (g_str_equal(key, "OpenConnect.Cookie")) {
+			dbus_message_iter_next(&entry);
+			if (dbus_message_iter_get_arg_type(&entry)
+							!= DBUS_TYPE_VARIANT)
+				break;
+			dbus_message_iter_recurse(&entry, &value);
+			dbus_message_iter_get_basic(&value, &cookie);
+		}
+
+		dbus_message_iter_next(&dict);
+	}
+
+done:
+	cookie_reply->callback(cookie_reply->provider, cookie, error,
+				cookie_reply->user_data);
+	g_free(cookie_reply);
+}
+
+typedef void (* request_cb_t)(struct vpn_provider *provider,
+					const char *vpncookie,
+					const char *error, void *user_data);
+
+static int request_cookie_input(struct vpn_provider *provider,
+				request_cb_t callback, void *user_data)
+{
+	DBusMessage *message;
+	const char *path, *agent_sender, *agent_path;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	struct request_input_reply *cookie_reply;
+	int err;
+
+	connman_agent_get_info(&agent_sender, &agent_path);
+
+	if (provider == NULL || agent_path == NULL || callback == NULL)
+		return -ESRCH;
+
+	message = dbus_message_new_method_call(agent_sender, agent_path,
+					VPN_AGENT_INTERFACE,
+					"RequestInput");
+	if (message == NULL)
+		return -ENOMEM;
+
+	dbus_message_iter_init_append(message, &iter);
+
+	path = vpn_provider_get_path(provider);
+	dbus_message_iter_append_basic(&iter,
+				DBUS_TYPE_OBJECT_PATH, &path);
+
+	connman_dbus_dict_open(&iter, &dict);
+
+	connman_dbus_dict_append_dict(&dict, "OpenConnect.Cookie",
+			request_input_append_cookie, provider);
+
+	connman_dbus_dict_append_dict(&dict, "Host",
+			request_input_append_host, provider);
+
+	connman_dbus_dict_append_dict(&dict, "Name",
+			request_input_append_name, provider);
+
+	connman_dbus_dict_close(&iter, &dict);
+
+	cookie_reply = g_try_new0(struct request_input_reply, 1);
+	if (cookie_reply == NULL) {
+		dbus_message_unref(message);
+		return -ENOMEM;
+	}
+
+	cookie_reply->provider = provider;
+	cookie_reply->callback = callback;
+	cookie_reply->user_data = user_data;
+
+	err = connman_agent_queue_message(provider, message,
+			connman_timeout_input_request(),
+			request_input_cookie_reply, cookie_reply);
+	if (err < 0 && err != -EBUSY) {
+		DBG("error %d sending agent request", err);
+		dbus_message_unref(message);
+		g_free(cookie_reply);
+		return err;
+	}
+
+	dbus_message_unref(message);
+
+	return -EINPROGRESS;
+}
+
+static int run_connect(struct vpn_provider *provider,
 			struct connman_task *task, const char *if_name,
-			vpn_provider_connect_cb_t cb, void *user_data)
+			vpn_provider_connect_cb_t cb, void *user_data,
+			const char *vpncookie)
 {
-	const char *vpnhost, *vpncookie, *cafile, *certsha1, *mtu;
-	int fd, err = 0;
+	const char *vpnhost, *cafile, *certsha1, *mtu;
+	int fd, err = 0, len;
 
 	vpnhost = vpn_provider_get_string(provider, "Host");
-	if (!vpnhost) {
-		connman_error("Host not set; cannot enable VPN");
-		err = -EINVAL;
-		goto done;
-	}
 
-	vpncookie = vpn_provider_get_string(provider, "OpenConnect.Cookie");
-	if (!vpncookie) {
-		connman_error("OpenConnect.Cookie not set; cannot enable VPN");
+	if (vpncookie == NULL) {
+		DBG("Cookie missing, cannot connect!");
 		err = -EINVAL;
 		goto done;
 	}
 
+	vpn_provider_set_string(provider, "OpenConnect.Cookie", vpncookie);
+
 	certsha1 = vpn_provider_get_string(provider,
 						"OpenConnect.ServerCert");
 	if (certsha1)
@@ -214,8 +408,8 @@ static int oc_connect(struct vpn_provider *provider,
 		goto done;
 	}
 
-	if (write(fd, vpncookie, strlen(vpncookie)) !=
-			(ssize_t)strlen(vpncookie) ||
+	len = strlen(vpncookie);
+	if (write(fd, vpncookie, len) != (ssize_t)len ||
 			write(fd, "\n", 1) != 1) {
 		connman_error("openconnect failed to take cookie on stdin");
 		err = -EIO;
@@ -229,6 +423,67 @@ done:
 	return err;
 }
 
+static void free_private_data(struct oc_private_data *data)
+{
+	g_free(data->if_name);
+	g_free(data);
+}
+
+static void request_input_cb(struct vpn_provider *provider,
+			const char *vpncookie,
+			const char *error, void *user_data)
+{
+	struct oc_private_data *data = user_data;
+
+	if (vpncookie == NULL)
+		DBG("Requesting cookie failed, error %s", error);
+	else if (error != NULL)
+		DBG("error %s", error);
+
+	run_connect(provider, data->task, data->if_name, data->cb,
+		data->user_data, vpncookie);
+
+	free_private_data(data);
+}
+
+static int oc_connect(struct vpn_provider *provider,
+			struct connman_task *task, const char *if_name,
+			vpn_provider_connect_cb_t cb, void *user_data)
+{
+	const char *vpnhost, *vpncookie;
+	int err;
+
+	vpnhost = vpn_provider_get_string(provider, "Host");
+	if (vpnhost == NULL) {
+		connman_error("Host not set; cannot enable VPN");
+		return -EINVAL;
+	}
+
+	vpncookie = vpn_provider_get_string(provider, "OpenConnect.Cookie");
+	if (vpncookie == NULL) {
+		struct oc_private_data *data;
+
+		data = g_try_new0(struct oc_private_data, 1);
+		if (data == NULL)
+			return -ENOMEM;
+
+		data->task = task;
+		data->if_name = g_strdup(if_name);
+		data->cb = cb;
+		data->user_data = user_data;
+
+		err = request_cookie_input(provider, request_input_cb, data);
+		if (err != -EINPROGRESS) {
+			free_private_data(data);
+			goto done;
+		}
+		return err;
+	}
+
+done:
+	return run_connect(provider, task, if_name, cb, user_data, vpncookie);
+}
+
 static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile)
 {
 	const char *setting;
diff --git a/vpn/plugins/vpn.c b/vpn/plugins/vpn.c
index 66a75d2..a8603d9 100644
--- a/vpn/plugins/vpn.c
+++ b/vpn/plugins/vpn.c
@@ -412,7 +412,7 @@ static int vpn_connect(struct vpn_provider *provider,
 
 	ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
 						data->if_name, cb, user_data);
-	if (ret < 0) {
+	if (ret < 0 && ret != -EINPROGRESS) {
 		stop_vpn(provider);
 		connman_task_destroy(data->task);
 		data->task = NULL;
diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c
index 83741f2..386926b 100644
--- a/vpn/vpn-provider.c
+++ b/vpn/vpn-provider.c
@@ -440,11 +440,11 @@ static DBusMessage *do_connect(DBusConnection *conn, DBusMessage *msg,
 
 	DBG("conn %p provider %p", conn, provider);
 
-	err = __vpn_provider_connect(provider);
+	err = __vpn_provider_connect(provider, msg);
 	if (err < 0)
 		return __connman_error_failed(msg, -err);
 
-	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+	return NULL;
 }
 
 static DBusMessage *do_disconnect(DBusConnection *conn, DBusMessage *msg,
@@ -978,19 +978,34 @@ int __vpn_provider_disconnect(struct vpn_provider *provider)
 static void connect_cb(struct vpn_provider *provider, void *user_data,
 								int error)
 {
+	DBusMessage *pending = user_data;
+
 	DBG("provider %p user %p error %d", provider, user_data, error);
+
+	if (error != 0) {
+		DBusMessage *reply = __connman_error_failed(pending, error);
+		if (reply != NULL)
+			g_dbus_send_message(connection, reply);
+
+		vpn_provider_indicate_error(provider,
+					VPN_PROVIDER_ERROR_CONNECT_FAILED);
+		vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
+	} else
+		g_dbus_send_reply(connection, pending, DBUS_TYPE_INVALID);
+
+	dbus_message_unref(pending);
 }
 
-int __vpn_provider_connect(struct vpn_provider *provider)
+int __vpn_provider_connect(struct vpn_provider *provider, DBusMessage *msg)
 {
 	int err;
 
 	DBG("provider %p", provider);
 
-	if (provider->driver != NULL && provider->driver->connect != NULL)
-		err = provider->driver->connect(provider,
-						connect_cb, NULL);
-	else
+	if (provider->driver != NULL && provider->driver->connect != NULL) {
+		dbus_message_ref(msg);
+		err = provider->driver->connect(provider, connect_cb, msg);
+	} else
 		return -EOPNOTSUPP;
 
 	return err;
@@ -2116,6 +2131,21 @@ void vpn_provider_driver_unregister(struct vpn_provider_driver *driver)
 	driver_list = g_slist_remove(driver_list, driver);
 }
 
+const char *vpn_provider_get_name(struct vpn_provider *provider)
+{
+	return provider->name;
+}
+
+const char *vpn_provider_get_host(struct vpn_provider *provider)
+{
+	return provider->host;
+}
+
+const char *vpn_provider_get_path(struct vpn_provider *provider)
+{
+	return provider->path;
+}
+
 static gboolean check_vpn_count(gpointer data)
 {
 	if (configuration_count == 0) {
diff --git a/vpn/vpn-provider.h b/vpn/vpn-provider.h
index b462042..b290bd1 100644
--- a/vpn/vpn-provider.h
+++ b/vpn/vpn-provider.h
@@ -101,9 +101,17 @@ int vpn_provider_append_route(struct vpn_provider *provider,
 const char *vpn_provider_get_driver_name(struct vpn_provider *provider);
 const char *vpn_provider_get_save_group(struct vpn_provider *provider);
 
+const char *vpn_provider_get_name(struct vpn_provider *provider);
+const char *vpn_provider_get_host(struct vpn_provider *provider);
+const char *vpn_provider_get_path(struct vpn_provider *provider);
+
 typedef void (* vpn_provider_connect_cb_t) (struct vpn_provider *provider,
 					void *user_data, int error);
 
+typedef void (* vpn_provider_auth_cb_t) (struct vpn_provider *provider,
+					const char *authenticator,
+					const char *error, void *user_data);
+
 struct vpn_provider_driver {
 	const char *name;
 	enum vpn_provider_type type;
diff --git a/vpn/vpn.h b/vpn/vpn.h
index 2b36b30..fed66a8 100644
--- a/vpn/vpn.h
+++ b/vpn/vpn.h
@@ -83,7 +83,7 @@ int __vpn_provider_indicate_state(struct vpn_provider *provider,
 					enum vpn_provider_state state);
 int __vpn_provider_indicate_error(struct vpn_provider *provider,
 					enum vpn_provider_error error);
-int __vpn_provider_connect(struct vpn_provider *provider);
+int __vpn_provider_connect(struct vpn_provider *provider, DBusMessage *msg);
 int __vpn_provider_connect_path(const char *path);
 int __vpn_provider_disconnect(struct vpn_provider *provider);
 int __vpn_provider_remove(const char *path);
-- 
1.7.11.4




More information about the connman mailing list