]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Handle <client-cert-request> in aggregate auth mode
authorDavid Woodhouse <David.Woodhouse@intel.com>
Fri, 31 May 2013 13:04:37 +0000 (14:04 +0100)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Fri, 31 May 2013 13:04:37 +0000 (14:04 +0100)
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
auth.c
http.c
openconnect-internal.h
www/changelog.xml

diff --git a/auth.c b/auth.c
index 190208964ecd9055e76ef34da3fa99d57ea74418..34975195179b4230de421bcadbb0cc75d5293dd7 100644 (file)
--- a/auth.c
+++ b/auth.c
@@ -488,7 +488,7 @@ static int parse_host_scan_node(struct openconnect_info *vpninfo, xmlNode *xml_n
  *  < 0, on error
  *  = 0, on success; *form is populated
  */
-int parse_xml_response(struct openconnect_info *vpninfo, char *response, struct oc_auth_form **formp)
+int parse_xml_response(struct openconnect_info *vpninfo, char *response, struct oc_auth_form **formp, int *cert_rq)
 {
        struct oc_auth_form *form;
        xmlDocPtr xml_doc;
@@ -499,6 +499,8 @@ int parse_xml_response(struct openconnect_info *vpninfo, char *response, struct
                free_auth_form(*formp);
                *formp = NULL;
        }
+       if (cert_rq)
+               *cert_rq = 0;
 
        if (!response) {
                vpn_progress(vpninfo, PRG_TRACE,
@@ -532,6 +534,14 @@ int parse_xml_response(struct openconnect_info *vpninfo, char *response, struct
                        /* if we do have a config-auth node, it is the root element */
                        xml_node = xml_node->children;
                        continue;
+               } else if (xmlnode_is_named(xml_node, "client-cert-request")) {
+                       if (cert_rq)
+                               *cert_rq = 1;
+                       else {
+                               vpn_progress(vpninfo, PRG_ERR,
+                                            _("Received client-cert-request> when not expected.\n"));
+                               ret = -EINVAL;
+                       }
                } else if (xmlnode_is_named(xml_node, "auth")) {
                        xmlnode_get_prop(xml_node, "id", &form->auth_id);
                        ret = parse_auth_node(vpninfo, xml_node, form);
@@ -553,7 +563,7 @@ int parse_xml_response(struct openconnect_info *vpninfo, char *response, struct
                xml_node = xml_node->next;
        }
 
-       if (!form->auth_id) {
+       if (!form->auth_id && (!cert_rq || !*cert_rq)) {
                vpn_progress(vpninfo, PRG_ERR,
                             _("XML response has no \"auth\" node\n"));
                goto out;
@@ -764,7 +774,7 @@ static int xmlpost_complete(xmlDocPtr doc, char *body, int bodylen)
        return ret;
 }
 
-int xmlpost_initial_req(struct openconnect_info *vpninfo, char *request_body, int req_len)
+int xmlpost_initial_req(struct openconnect_info *vpninfo, char *request_body, int req_len, int cert_fail)
 {
        xmlNodePtr root, node;
        xmlDocPtr doc = xmlpost_new_query(vpninfo, "init", &root);
@@ -779,7 +789,11 @@ int xmlpost_initial_req(struct openconnect_info *vpninfo, char *request_body, in
        free(url);
        if (!node)
                goto bad;
-
+       if (cert_fail) {
+               node = xmlNewTextChild(root, NULL, XCAST("client-cert-fail"), NULL);
+               if (!node)
+                       goto bad;
+       }
        return xmlpost_complete(doc, request_body, req_len);
 
 bad:
diff --git a/http.c b/http.c
index 8bf27135ef6664919a1c118c7faeab68f3786603..a61e49385f5f62a9f45db989c4d63416dff1bcd9 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1003,6 +1003,7 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo)
        const char *method = "POST";
        char *orig_host = NULL, *orig_path = NULL;
        int orig_port = 0;
+       int cert_rq;
 
        /* Step 1: Unlock software token (if applicable) */
        if (vpninfo->token_mode == OC_TOKEN_MODE_STOKEN) {
@@ -1023,7 +1024,7 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo)
         * b) Same-host redirect (e.g. Location: /foo/bar)
         * c) Three redirects without seeing a plausible login form
         */
-       result = xmlpost_initial_req(vpninfo, request_body, sizeof(request_body));
+       result = xmlpost_initial_req(vpninfo, request_body, sizeof(request_body), 0);
        if (result < 0)
                return result;
 
@@ -1069,11 +1070,33 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo)
                else if (vpninfo->redirect_type != REDIR_TYPE_NONE)
                        continue;
 
-               result = parse_xml_response(vpninfo, form_buf, &form);
+               result = parse_xml_response(vpninfo, form_buf, &form, &cert_rq);
                if (result < 0)
                        goto fail;
 
-               if (form->action) {
+               if (cert_rq) {
+                       if (vpninfo->cert) {
+                               vpn_progress(vpninfo, PRG_ERR,
+                                            _("Server requested SSL client certificate after one was provided\n"));
+                               /* Continue anyway. Authentication will probably fail but there might
+                                * be special login options which are username/password only. If there
+                                * is some special tag we're supposed to include in our 'init' XML
+                                * to make the server actually look for the client cert, and merely
+                                * having it present in the SSL negotiation isn't sufficient, then
+                                * perhaps this is a bug. And users might need too use --no-xmlpost
+                                * to make things work. */
+                               vpn_progress(vpninfo, PRG_ERR,
+                                            _("Try the --no-xmlpost option. If that works, please report as an OpenConnect bug\n"));
+                       } else {
+                               vpn_progress(vpninfo, PRG_INFO, _("Server requested SSL client certificate; none was configured\n"));
+                       }
+                       /* Try again with <client-cert-fail/> in the request */
+                       result = xmlpost_initial_req(vpninfo, request_body, sizeof(request_body), 1);
+                       if (result < 0)
+                               goto fail;
+                       continue;
+               }
+               if (form && form->action) {
                        vpninfo->redirect_url = strdup(form->action);
                        handle_redirect(vpninfo);
                }
@@ -1144,7 +1167,7 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo)
                if (result < 0)
                        goto out;
 
-               result = parse_xml_response(vpninfo, form_buf, &form);
+               result = parse_xml_response(vpninfo, form_buf, &form, NULL);
                if (result < 0)
                        goto out;
        }
@@ -1164,7 +1187,7 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo)
                if (result < 0)
                        goto out;
 
-               result = parse_xml_response(vpninfo, form_buf, &form);
+               result = parse_xml_response(vpninfo, form_buf, &form, NULL);
                if (result < 0)
                        goto out;
                if (form->action) {
index 16f4b6ddf7fe8181e475e033b5a2e954ff01169f..267f38844481ee6db55d0a4b3c7a2ae4a1b3bbc1 100644 (file)
@@ -432,12 +432,13 @@ extern int killed;
 int config_lookup_host(struct openconnect_info *vpninfo, const char *host);
 
 /* auth.c */
-int parse_xml_response(struct openconnect_info *vpninfo, char *response, struct oc_auth_form **form);
+int parse_xml_response(struct openconnect_info *vpninfo, char *response,
+                      struct oc_auth_form **form, int *cert_rq);
 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, 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 xmlpost_initial_req(struct openconnect_info *vpninfo, char *request_body, int req_len, int cert_fail);
 int prepare_stoken(struct openconnect_info *vpninfo);
 
 /* http.c */
index 8d6f76534fabb0598d7016a375d464fab658c2ca..4fc3de8748376b644f7570f0cdfb55b9cbc7d68f 100644 (file)
@@ -17,6 +17,7 @@
 <ul>
    <li><b>OpenConnect HEAD</b>
      <ul>
+       <li>Attempt to handle &lt;client-cert-request&gt; in aggregate auth mode.</li>
        <li>Don't include <tt>X-Aggregate-Auth:</tt> header in fallback mode.</li>
        <li>Enable AES256 mode for DTLS with GnuTLS <a href="https://bugzilla.redhat.com/show_bug.cgi?id=955710"><i>(RH#955710)</i></a>.</li>
        <li>Add <tt>--dump-http-traffic</tt> option for debugging.</li>