* < 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;
free_auth_form(*formp);
*formp = NULL;
}
+ if (cert_rq)
+ *cert_rq = 0;
if (!response) {
vpn_progress(vpninfo, PRG_TRACE,
/* 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);
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;
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);
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:
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) {
* 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;
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);
}
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 (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) {
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 */
<ul>
<li><b>OpenConnect HEAD</b>
<ul>
+ <li>Attempt to handle <client-cert-request> 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>