auth: Add new XML POST capability
authorKevin Cernekee <cernekee@gmail.com>
Fri, 26 Oct 2012 05:04:05 +0000 (22:04 -0700)
committerKevin Cernekee <cernekee@gmail.com>
Sun, 28 Oct 2012 08:22:56 +0000 (01:22 -0700)
Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
auth.c
http.c
library.c
openconnect-internal.h

diff --git a/auth.c b/auth.c
index 79c9bc1..de02e77 100644 (file)
--- 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 (file)
--- 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)
index c1cf95f..16b7bab 100644 (file)
--- 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. */
index fd0060d..f1a3fb6 100644 (file)
@@ -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 */