[PATCH 13/24] auth: Add new XML POST capability

Kevin Cernekee cernekee at gmail.com
Sat Nov 3 13:22:55 EDT 2012


Signed-off-by: Kevin Cernekee <cernekee at gmail.com>
---
 auth.c                 |  161 +++++++++++++++++++++++++++++++++++++++++++++++-
 http.c                 |    2 +-
 library.c              |    4 ++
 openconnect-internal.h |    3 +-
 4 files changed, 166 insertions(+), 4 deletions(-)

diff --git a/auth.c b/auth.c
index 79c9bc1..de02e77 100644
--- a/auth.c
+++ b/auth.c
@@ -41,6 +41,8 @@
 
 #include "openconnect-internal.h"
 
+static int xmlpost_append_form_opts(struct openconnect_info *vpninfo,
+				    struct oc_auth_form *form, char *body, int bodylen);
 static int can_gen_tokencode(struct openconnect_info *vpninfo, struct oc_form_opt *opt);
 static int do_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form);
 
@@ -560,7 +562,7 @@ int parse_xml_response(struct openconnect_info *vpninfo, char *response, struct
  */
 int handle_auth_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
 		     char *request_body, int req_len, const char **method,
-		     const char **request_body_type)
+		     const char **request_body_type, int xmlpost)
 {
 	int ret;
 	struct vpn_option *opt, *next;
@@ -611,7 +613,9 @@ int handle_auth_form(struct openconnect_info *vpninfo, struct oc_auth_form *form
 	if (ret)
 		return ret;
 
-	ret = append_form_opts(vpninfo, form, request_body, req_len);
+	ret = xmlpost ?
+	      xmlpost_append_form_opts(vpninfo, form, request_body, req_len) :
+	      append_form_opts(vpninfo, form, request_body, req_len);
 	if (!ret) {
 		*method = "POST";
 		*request_body_type = "application/x-www-form-urlencoded";
@@ -656,6 +660,159 @@ void free_auth_form(struct oc_auth_form *form)
 	free(form);
 }
 
+/*
+ * Old submission format is just an HTTP query string:
+ *
+ * password=12345678&username=joe
+ *
+ * New XML format is more complicated:
+ *
+ * <config-auth client="vpn" type="<!-- init or auth-reply -->">
+ *   <version who="vpn"><!-- currently just the OpenConnect version --></version>
+ *   <device-id><!-- linux, linux-64, mac, win --></device-id>
+ *   <opaque is-for="<!-- some name -->">
+ *     <!-- just copy this verbatim from whatever the gateway sent us -->
+ *   </opaque>
+ *
+ * For init only, add:
+ *   <group-access>https://<!-- insert hostname here --></group-access>
+ *
+ * For auth-reply only, add:
+ *   <auth>
+ *     <username><!-- same treatment as the old form options --></username>
+ *     <password><!-- ditto -->
+ *   </auth>
+ *   <host-scan-token><!-- vpninfo->csd_ticket --></host-scan-token>
+ */
+
+#define XCAST(x) ((const xmlChar *)(x))
+
+static xmlDocPtr xmlpost_new_query(struct openconnect_info *vpninfo, const char *type,
+				   xmlNodePtr *rootp)
+{
+	xmlDocPtr doc;
+	xmlNodePtr root, node;
+
+	doc = xmlNewDoc(XCAST("1.0"));
+	if (!doc)
+		return NULL;
+
+	*rootp = root = xmlNewNode(NULL, XCAST("config-auth"));
+	if (!root)
+		goto bad;
+	if (!xmlNewProp(root, XCAST("client"), XCAST("vpn")))
+		goto bad;
+	if (!xmlNewProp(root, XCAST("type"), XCAST(type)))
+		goto bad;
+	xmlDocSetRootElement(doc, root);
+
+	node = xmlNewTextChild(root, NULL, XCAST("version"), XCAST(openconnect_version_str));
+	if (!node)
+		goto bad;
+	if (!xmlNewProp(node, XCAST("who"), XCAST("vpn")))
+		goto bad;
+
+	if (!xmlNewTextChild(root, NULL, XCAST("device-id"), XCAST(vpninfo->platname)))
+		goto bad;
+
+	return doc;
+
+bad:
+	xmlFreeDoc(doc);
+	return NULL;
+}
+
+static int xmlpost_complete(xmlDocPtr doc, char *body, int bodylen)
+{
+	xmlChar *mem = NULL;
+	int len, ret = 0;
+
+	if (!body) {
+		xmlFree(doc);
+		return 0;
+	}
+
+	xmlDocDumpMemoryEnc(doc, &mem, &len, "UTF-8");
+	if (!mem) {
+		xmlFreeDoc(doc);
+		return -ENOMEM;
+	}
+
+	if (len > bodylen)
+		ret = -E2BIG;
+	else {
+		memcpy(body, mem, len);
+		body[len] = 0;
+	}
+
+	xmlFreeDoc(doc);
+	xmlFree(mem);
+
+	return ret;
+}
+
+int xmlpost_initial_req(struct openconnect_info *vpninfo, char *request_body, int req_len)
+{
+	xmlNodePtr root, node;
+	xmlDocPtr doc = xmlpost_new_query(vpninfo, "init", &root);
+	char *url;
+
+	if (!doc)
+		return -ENOMEM;
+
+	if (asprintf(&url, "https://%s", vpninfo->hostname) == -1)
+		goto bad;
+	node = xmlNewTextChild(root, NULL, XCAST("group-access"), XCAST(url));
+	free(url);
+	if (!node)
+		goto bad;
+
+	return xmlpost_complete(doc, request_body, req_len);
+
+bad:
+	xmlpost_complete(doc, NULL, 0);
+	return -ENOMEM;
+}
+
+static int xmlpost_append_form_opts(struct openconnect_info *vpninfo,
+				    struct oc_auth_form *form, char *body, int bodylen)
+{
+	xmlNodePtr root, node;
+	xmlDocPtr doc = xmlpost_new_query(vpninfo, "auth-reply", &root);
+	struct oc_form_opt *opt;
+
+	if (!doc)
+		return -ENOMEM;
+
+	if (vpninfo->opaque_srvdata) {
+		node = xmlCopyNode(vpninfo->opaque_srvdata, 1);
+		if (!node)
+			goto bad;
+		if (!xmlAddChild(root, node))
+			goto bad;
+	}
+
+	node = xmlNewChild(root, NULL, XCAST("auth"), NULL);
+	if (!node)
+		goto bad;
+
+	for (opt = form->opts; opt; opt = opt->next) {
+		if (!xmlNewTextChild(node, NULL, XCAST(opt->name), XCAST(opt->value)))
+			goto bad;
+	}
+
+	if (vpninfo->csd_token &&
+	    !xmlNewTextChild(root, NULL, XCAST("host-scan-token"), XCAST(vpninfo->csd_token)))
+		goto bad;
+
+	return xmlpost_complete(doc, body, bodylen);
+
+bad:
+	xmlpost_complete(doc, NULL, 0);
+	return -ENOMEM;
+}
+
+
 #ifdef LIBSTOKEN_HDR
 static void nuke_opt_values(struct oc_form_opt *opt)
 {
diff --git a/http.c b/http.c
index f0c0a5b..7b2c0fe 100644
--- a/http.c
+++ b/http.c
@@ -908,7 +908,7 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo)
 	}
 	request_body[0] = 0;
 	result = handle_auth_form(vpninfo, form, request_body, sizeof(request_body),
-				  &method, &request_body_type);
+				  &method, &request_body_type, 0);
 	free_auth_form(form);
 
 	if (!result)
diff --git a/library.c b/library.c
index c1cf95f..16b7bab 100644
--- a/library.c
+++ b/library.c
@@ -30,6 +30,8 @@
 #include LIBSTOKEN_HDR
 #endif
 
+#include <libxml/tree.h>
+
 #include "openconnect-internal.h"
 
 struct openconnect_info *openconnect_vpninfo_new (char *useragent,
@@ -112,6 +114,8 @@ void openconnect_vpninfo_free (struct openconnect_info *vpninfo)
 		free(vpninfo->csd_scriptname);
 	}
 	free(vpninfo->csd_stuburl);
+	if (vpninfo->opaque_srvdata)
+		xmlFreeNode(vpninfo->opaque_srvdata);
 	/* These are const in openconnect itself, but for consistency of
 	   the library API we do take ownership of the strings we're given,
 	   and thus we have to free them too. */
diff --git a/openconnect-internal.h b/openconnect-internal.h
index fd0060d..f1a3fb6 100644
--- a/openconnect-internal.h
+++ b/openconnect-internal.h
@@ -409,8 +409,9 @@ int config_lookup_host(struct openconnect_info *vpninfo, const char *host);
 int parse_xml_response(struct openconnect_info *vpninfo, char *response, struct oc_auth_form **form);
 int handle_auth_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
 		     char *request_body, int req_len, const char **method,
-		     const char **request_body_type);
+		     const char **request_body_type, int xmlpost);
 void free_auth_form(struct oc_auth_form *form);
+int xmlpost_initial_req(struct openconnect_info *vpninfo, char *request_body, int req_len);
 int prepare_stoken(struct openconnect_info *vpninfo);
 
 /* http.c */
-- 
1.7.10.4




More information about the openconnect-devel mailing list