openconnect-unknown/auth.c

1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2015 Intel Corporation.
5  * Copyright © 2013 John Morrissey <jwm@horde.net>
6  *
7  * Author: David Woodhouse <dwmw2@infradead.org>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * version 2.1, as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  */
18 
19 #include <config.h>
20 
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <time.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #ifndef _WIN32
31 #include <sys/wait.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #endif
35 
36 #include <libxml/parser.h>
37 #include <libxml/tree.h>
38 
39 #include "openconnect-internal.h"
40 
41 static int xmlpost_append_form_opts(struct openconnect_info *vpninfo,
42 				    struct oc_auth_form *form, struct oc_text_buf *body);
43 static int cstp_can_gen_tokencode(struct openconnect_info *vpninfo,
44 				  struct oc_auth_form *form,
45 				  struct oc_form_opt *opt);
46 
47 int openconnect_set_option_value(struct oc_form_opt *opt, const char *value)
48 {
49 	if (opt->type == OC_FORM_OPT_SELECT) {
50 		struct oc_form_opt_select *sopt = (void *)opt;
51 		int i;
52 
53 		for (i=0; i<sopt->nr_choices; i++) {
54 			if (!strcmp(value, sopt->choices[i]->name)) {
55 				opt->_value = sopt->choices[i]->name;
56 				return 0;
57 			}
58 		}
59 		return -EINVAL;
60 	}
61 
62 	opt->_value = strdup(value);
63 	if (!opt->_value)
64 		return -ENOMEM;
65 
66 	return 0;
67 }
68 
69 static int prop_equals(xmlNode *xml_node, const char *name, const char *value)
70 {
71 	char *tmp = (char *)xmlGetProp(xml_node, (unsigned char *)name);
72 	int ret = 0;
73 
74 	if (tmp && !strcasecmp(tmp, value))
75 		ret = 1;
76 	free(tmp);
77 	return ret;
78 }
79 
80 static int parse_auth_choice(struct openconnect_info *vpninfo, struct oc_auth_form *form,
81 			     xmlNode *xml_node)
82 {
83 	struct oc_form_opt_select *opt;
84 	xmlNode *opt_node;
85 	int max_choices = 0, selection = 0;
86 
87 	opt = calloc(1, sizeof(*opt));
88 	if (!opt)
89 		return -ENOMEM;
90 
91 	opt->form.type = OC_FORM_OPT_SELECT;
92 	opt->form.name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
93 	opt->form.label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
94 
95 	if (!opt->form.name) {
96 		vpn_progress(vpninfo, PRG_ERR, _("Form choice has no name\n"));
97 		free_opt((struct oc_form_opt *)opt);
98 		return -EINVAL;
99 	}
100 
101 	for (opt_node = xml_node->children; opt_node; opt_node = opt_node->next)
102 		max_choices++;
103 
104 	opt->choices = calloc(1, max_choices * sizeof(struct oc_choice *));
105 	if (!opt->choices) {
106 		free_opt((struct oc_form_opt *)opt);
107 		return -ENOMEM;
108 	}
109 
110 	for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
111 		char *form_id;
112 		struct oc_choice *choice;
113 
114 		if (xml_node->type != XML_ELEMENT_NODE)
115 			continue;
116 
117 		if (strcmp((char *)xml_node->name, "option"))
118 			continue;
119 
120 		form_id = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
121 		if (!form_id)
122 			form_id = (char *)xmlNodeGetContent(xml_node);
123 		if (!form_id)
124 			continue;
125 
126 		choice = calloc(1, sizeof(*choice));
127 		if (!choice) {
128 			free_opt((struct oc_form_opt *)opt);
129 			return -ENOMEM;
130 		}
131 
132 		choice->name = form_id;
133 		choice->label = (char *)xmlNodeGetContent(xml_node);
134 		choice->auth_type = (char *)xmlGetProp(xml_node, (unsigned char *)"auth-type");
135 		choice->override_name = (char *)xmlGetProp(xml_node, (unsigned char *)"override-name");
136 		choice->override_label = (char *)xmlGetProp(xml_node, (unsigned char *)"override-label");
137 
138 		choice->second_auth = prop_equals(xml_node, "second-auth", "1");
139 		choice->secondary_username = (char *)xmlGetProp(xml_node,
140 			(unsigned char *)"secondary_username");
141 		choice->secondary_username_editable = prop_equals(xml_node,
142 			"secondary_username_editable", "true");
143 		choice->noaaa = prop_equals(xml_node, "noaaa", "1");
144 
145 		if (prop_equals(xml_node, "selected", "true"))
146 			selection = opt->nr_choices;
147 
148 		opt->choices[opt->nr_choices++] = choice;
149 	}
150 
151 	if (!strcmp(opt->form.name, "group_list")) {
152 		form->authgroup_opt = opt;
153 		form->authgroup_selection = selection;
154 	}
155 
156 	/* We link the choice _first_ so it's at the top of what we present
157 	   to the user */
158 	opt->form.next = form->opts;
159 	form->opts = &opt->form;
160 	return 0;
161 }
162 
163 static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
164 		      xmlNode *xml_node)
165 {
166 	char *input_type, *input_name, *input_label;
167 
168 	for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
169 		struct oc_form_opt *opt, **p;
170 
171 		if (xml_node->type != XML_ELEMENT_NODE)
172 			continue;
173 
174 		if (!strcmp((char *)xml_node->name, "select")) {
175 			if (parse_auth_choice(vpninfo, form, xml_node))
176 				return -EINVAL;
177 			continue;
178 		}
179 		if (strcmp((char *)xml_node->name, "input")) {
180 			vpn_progress(vpninfo, PRG_DEBUG,
181 				     _("name %s not input\n"), xml_node->name);
182 			continue;
183 		}
184 
185 		input_type = (char *)xmlGetProp(xml_node, (unsigned char *)"type");
186 		if (!input_type) {
187 			vpn_progress(vpninfo, PRG_INFO,
188 				     _("No input type in form\n"));
189 			continue;
190 		}
191 
192 		if (!strcmp(input_type, "submit") || !strcmp(input_type, "reset")) {
193 			free(input_type);
194 			continue;
195 		}
196 
197 		input_name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
198 		if (!input_name) {
199 			vpn_progress(vpninfo, PRG_INFO,
200 				     _("No input name in form\n"));
201 			free(input_type);
202 			continue;
203 		}
204 		input_label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
205 
206 		opt = calloc(1, sizeof(*opt));
207 		if (!opt) {
208 			free(input_type);
209 			free(input_name);
210 			free(input_label);
211 			return -ENOMEM;
212 		}
213 
214 		opt->name = input_name;
215 		opt->label = input_label;
216 		opt->flags = prop_equals(xml_node, "second-auth", "1") ? OC_FORM_OPT_SECOND_AUTH : 0;
217 
218 		if (!strcmp(input_type, "hidden")) {
219 			opt->type = OC_FORM_OPT_HIDDEN;
220 			opt->_value = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
221 		} else if (!strcmp(input_type, "text")) {
222 			opt->type = OC_FORM_OPT_TEXT;
223 		} else if (!strcmp(input_type, "password")) {
224 			if (!cstp_can_gen_tokencode(vpninfo, form, opt))
225 				opt->type = OC_FORM_OPT_TOKEN;
226 			else
227 				opt->type = OC_FORM_OPT_PASSWORD;
228 		} else {
229 			vpn_progress(vpninfo, PRG_INFO,
230 				     _("Unknown input type %s in form\n"),
231 				     input_type);
232 			free(input_type);
233 			free(input_name);
234 			free(input_label);
235 			free(opt);
236 			continue;
237 		}
238 
239 		free(input_type);
240 
241 		p = &form->opts;
242 		while (*p)
243 			p = &(*p)->next;
244 
245 		*p = opt;
246 	}
247 
248 	return 0;
249 }
250 
251 static char *xmlnode_msg(xmlNode *xml_node)
252 {
253 	char *fmt = (char *)xmlNodeGetContent(xml_node);
254 	char *result, *params[2], *pct;
255 	int len;
256 	int nr_params = 0;
257 
258 	if (!fmt || !fmt[0]) {
259 		free(fmt);
260 		return NULL;
261 	}
262 
263 	len = strlen(fmt) + 1;
264 
265 	params[0] = (char *)xmlGetProp(xml_node, (unsigned char *)"param1");
266 	if (params[0])
267 		len += strlen(params[0]);
268 	params[1] = (char *)xmlGetProp(xml_node, (unsigned char *)"param2");
269 	if (params[1])
270 		len += strlen(params[1]);
271 
272 	result = malloc(len);
273 	if (!result) {
274 		result = fmt;
275 		goto out;
276 	}
277 
278 	strcpy(result, fmt);
279 	free(fmt);
280 
281 	for (pct = strchr(result, '%'); pct;
282 	     (pct = strchr(pct, '%'))) {
283 		int paramlen;
284 
285 		/* We only cope with '%s' */
286 		if (pct[1] != 's')
287 			goto out;
288 
289 		if (params[nr_params]) {
290 			paramlen = strlen(params[nr_params]);
291 			/* Move rest of fmt string up... */
292 			memmove(pct + paramlen, pct + 2, strlen(pct + 2) + 1);
293 			/* ... and put the string parameter in where the '%s' was */
294 			memcpy(pct, params[nr_params], paramlen);
295 			pct += paramlen;
296 		} else
297 			pct++;
298 
299 		if (++nr_params == 2)
300 			break;
301 	}
302  out:
303 	free(params[0]);
304 	free(params[1]);
305 	return result;
306 }
307 
308 static int xmlnode_get_text(xmlNode *xml_node, const char *name, char **var)
309 {
310 	char *str;
311 
312 	if (name && !xmlnode_is_named(xml_node, name))
313 		return -EINVAL;
314 
315 	str = xmlnode_msg(xml_node);
316 	if (!str)
317 		return -ENOENT;
318 
319 	free(*var);
320 	*var = str;
321 	return 0;
322 }
323 
324 /*
325  * Legacy server response looks like:
326  *
327  * <auth id="<!-- "main" for initial attempt, "success" means we have a cookie -->">
328  *   <title><!-- title to display to user --></title>
329  *   <csd
330  *        token="<!-- save to vpninfo->csd_token -->"
331  *        ticket="<!-- save to vpninfo->csd_ticket -->" />
332  *   <csd
333  *        stuburl="<!-- save to vpninfo->csd_stuburl if --os=win -->"
334  *        starturl="<!-- save to vpninfo->csd_starturl if --os=win -->"
335  *        waiturl="<!-- save to vpninfo->csd_starturl if --os=win -->"
336  *   <csdMac
337  *        stuburl="<!-- save to vpninfo->csd_stuburl if --os=mac-intel -->"
338  *        starturl="<!-- save to vpninfo->csd_starturl if --os=mac-intel -->"
339  *        waiturl="<!-- save to vpninfo->csd_waiturl if --os=mac-intel -->" />
340  *   <csdLinux
341  *        stuburl="<!-- same as above, for Linux -->"
342  *        starturl="<!-- same as above, for Linux -->"
343  *        waiturl="<!-- same as above, for Linux -->" />
344  *   <banner><!-- display this to the user --></banner>
345  *   <message>Please enter your username and password.</message>
346  *   <form method="post" action="/+webvpn+/index.html">
347  *     <input type="text" name="username" label="Username:" />
348  *     <input type="password" name="password" label="Password:" />
349  *     <input type="hidden" name="<!-- save these -->" value="<!-- ... -->" />
350  *     <input type="submit" name="Login" value="Login" />
351  *     <input type="reset" name="Clear" value="Clear" />
352  *   </form>
353  * </auth>
354  *
355  * New server response looks like:
356  *
357  * <config-auth>
358  *   <version><!-- whatever --></version>
359  *   <session-token><!-- if present, save to vpninfo->cookie --></session-token>
360  *   <opaque>
361  *     <!-- this could contain anything; copy to vpninfo->opaque_srvdata -->
362  *     <tunnel-group>foobar</tunnel-group>
363  *     <config-hash>1234567</config-hash>
364  *   </opaque>
365  *   <auth id="<!-- see above -->
366  *     <!-- all of our old familiar fields -->
367  *   </auth>
368  *   <host-scan>
369  *     <host-scan-ticket><!-- save to vpninfo->csd_ticket --></host-scan-ticket>
370  *     <host-scan-token><!-- save to vpninfo->csd_token --></host-scan-token>
371  *     <host-scan-base-uri><!-- save to vpninfo->csd_starturl --></host-scan-base-uri>
372  *     <host-scan-wait-uri><!-- save to vpninfo->csd_waiturl --></host-scan-wait-uri>
373  *   </host-scan>
374  * </config-auth>
375  *
376  * Notes:
377  *
378  * 1) The new host-scan-*-uri nodes do not map directly to the old CSD fields.
379  *
380  * 2) The new <form> tag tends to omit the method/action properties.
381  */
382 
383 static int parse_auth_node(struct openconnect_info *vpninfo, xmlNode *xml_node,
384 			   struct oc_auth_form *form)
385 {
386 	int ret = 0;
387 
388 	for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
389 		if (xml_node->type != XML_ELEMENT_NODE)
390 			continue;
391 
392 		xmlnode_get_text(xml_node, "banner", &form->banner);
393 		xmlnode_get_text(xml_node, "message", &form->message);
394 		xmlnode_get_text(xml_node, "error", &form->error);
395 
396 		if (xmlnode_is_named(xml_node, "form")) {
397 
398 			/* defaults for new XML POST */
399 			form->method = strdup("POST");
400 			form->action = strdup("/");
401 
402 			xmlnode_get_prop(xml_node, "method", &form->method);
403 			xmlnode_get_prop(xml_node, "action", &form->action);
404 
405 			if (!form->method || !form->action ||
406 			    strcasecmp(form->method, "POST") || !form->action[0]) {
407 				vpn_progress(vpninfo, PRG_ERR,
408 					     _("Cannot handle form method='%s', action='%s'\n"),
409 					     form->method, form->action);
410 				ret = -EINVAL;
411 				goto out;
412 			}
413 
414 			ret = parse_form(vpninfo, form, xml_node);
415 			if (ret < 0)
416 				goto out;
417 		} else if (!vpninfo->csd_scriptname && xmlnode_is_named(xml_node, "csd")) {
418 			xmlnode_get_prop(xml_node, "token", &vpninfo->csd_token);
419 			xmlnode_get_prop(xml_node, "ticket", &vpninfo->csd_ticket);
420 		}
421 		/* For Windows, vpninfo->csd_xmltag will be "csd" and there are *two* <csd>
422 		   nodes; one with token/ticket and one with the URLs. Process them both
423 		   the same and rely on the fact that xmlnode_get_prop() will not *clear*
424 		   the variable if no such property is found. */
425 		if (!vpninfo->csd_scriptname && xmlnode_is_named(xml_node, vpninfo->csd_xmltag)) {
426 			/* ignore the CSD trojan binary on mobile platforms */
427 			if (!vpninfo->csd_nostub)
428 				xmlnode_get_prop(xml_node, "stuburl", &vpninfo->csd_stuburl);
429 			xmlnode_get_prop(xml_node, "starturl", &vpninfo->csd_starturl);
430 			xmlnode_get_prop(xml_node, "waiturl", &vpninfo->csd_waiturl);
431 			vpninfo->csd_preurl = strdup(vpninfo->urlpath);
432 		}
433 	}
434 
435 out:
436 	return ret;
437 }
438 
439 static int parse_host_scan_node(struct openconnect_info *vpninfo, xmlNode *xml_node)
440 {
441 	/* ignore this whole section if the CSD trojan has already run */
442 	if (vpninfo->csd_scriptname)
443 		return 0;
444 
445 	for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
446 		if (xml_node->type != XML_ELEMENT_NODE)
447 			continue;
448 
449 		xmlnode_get_text(xml_node, "host-scan-ticket", &vpninfo->csd_ticket);
450 		xmlnode_get_text(xml_node, "host-scan-token", &vpninfo->csd_token);
451 		xmlnode_get_text(xml_node, "host-scan-base-uri", &vpninfo->csd_starturl);
452 		xmlnode_get_text(xml_node, "host-scan-wait-uri", &vpninfo->csd_waiturl);
453 	}
454 	return 0;
455 }
456 
457 static void parse_profile_node(struct openconnect_info *vpninfo, xmlNode *xml_node)
458 {
459 	/* ignore this whole section if we already have a URL */
460 	if (vpninfo->profile_url && vpninfo->profile_sha1)
461 		return;
462 
463 	/* Find <vpn rev="1.0"> child... */
464 	xml_node = xml_node->children;
465 	while (1) {
466 		if (!xml_node)
467 			return;
468 
469 		if (xml_node->type == XML_ELEMENT_NODE &&
470 		    xmlnode_is_named(xml_node, "vpn") &&
471 		    !xmlnode_match_prop(xml_node, "rev", "1.0"))
472 			break;
473 
474 		xml_node = xml_node->next;
475 	}
476 
477 	/* Find <file type="profile" service-type="user"> */
478 	xml_node = xml_node->children;
479 	while (1) {
480 		if (!xml_node)
481 			return;
482 
483 		if (xml_node->type == XML_ELEMENT_NODE &&
484 		    xmlnode_is_named(xml_node, "file") &&
485 		    !xmlnode_match_prop(xml_node, "type", "profile") &&
486 		    !xmlnode_match_prop(xml_node, "service-type", "user"))
487 			break;
488 
489 		xml_node = xml_node->next;
490 	}
491 
492 	for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
493 		if (xml_node->type != XML_ELEMENT_NODE)
494 			continue;
495 
496 		xmlnode_get_text(xml_node, "uri", &vpninfo->profile_url);
497 		/* FIXME: Check for <hash type="sha1"> */
498 		xmlnode_get_text(xml_node, "hash", &vpninfo->profile_sha1);
499 	}
500 }
501 
502 static void parse_config_node(struct openconnect_info *vpninfo, xmlNode *xml_node)
503 {
504 	for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
505 		if (xml_node->type != XML_ELEMENT_NODE)
506 			continue;
507 
508 		if (xmlnode_is_named(xml_node, "vpn-profile-manifest"))
509 			parse_profile_node(vpninfo, xml_node);
510 	}
511 }
512 
513 /* Return value:
514  *  < 0, on error
515  *  = 0, on success; *form is populated
516  */
517 static int parse_xml_response(struct openconnect_info *vpninfo, char *response,
518 			      struct oc_auth_form **formp, int *cert_rq)
519 {
520 	struct oc_auth_form *form;
521 	xmlDocPtr xml_doc;
522 	xmlNode *xml_node;
523 	int ret;
524 
525 	if (*formp) {
526 		free_auth_form(*formp);
527 		*formp = NULL;
528 	}
529 	if (cert_rq)
530 		*cert_rq = 0;
531 
532 	if (!response) {
533 		vpn_progress(vpninfo, PRG_DEBUG,
534 			     _("Empty response from server\n"));
535 		return -EINVAL;
536 	}
537 
538 	form = calloc(1, sizeof(*form));
539 	if (!form)
540 		return -ENOMEM;
541 	xml_doc = xmlReadMemory(response, strlen(response), "noname.xml", NULL,
542 				XML_PARSE_NOERROR|XML_PARSE_RECOVER);
543 	if (!xml_doc) {
544 		vpn_progress(vpninfo, PRG_ERR,
545 			     _("Failed to parse server response\n"));
546 		vpn_progress(vpninfo, PRG_DEBUG,
547 			     _("Response was:%s\n"), response);
548 		free(form);
549 		return -EINVAL;
550 	}
551 
552 	xml_node = xmlDocGetRootElement(xml_doc);
553 	while (xml_node) {
554 		ret = 0;
555 
556 		if (xml_node->type != XML_ELEMENT_NODE) {
557 			xml_node = xml_node->next;
558 			continue;
559 		}
560 		if (xmlnode_is_named(xml_node, "config-auth")) {
561 			/* if we do have a config-auth node, it is the root element */
562 			xml_node = xml_node->children;
563 			continue;
564 		} else if (xmlnode_is_named(xml_node, "client-cert-request")) {
565 			if (cert_rq)
566 				*cert_rq = 1;
567 			else {
568 				vpn_progress(vpninfo, PRG_ERR,
569 					     _("Received <client-cert-request> when not expected.\n"));
570 				ret = -EINVAL;
571 			}
572 		} else if (xmlnode_is_named(xml_node, "auth")) {
573 			xmlnode_get_prop(xml_node, "id", &form->auth_id);
574 			ret = parse_auth_node(vpninfo, xml_node, form);
575 		} else if (xmlnode_is_named(xml_node, "opaque")) {
576 			if (vpninfo->opaque_srvdata)
577 				xmlFreeNode(vpninfo->opaque_srvdata);
578 			vpninfo->opaque_srvdata = xmlCopyNode(xml_node, 1);
579 			if (!vpninfo->opaque_srvdata)
580 				ret = -ENOMEM;
581 		} else if (xmlnode_is_named(xml_node, "host-scan")) {
582 			ret = parse_host_scan_node(vpninfo, xml_node);
583 		} else if (xmlnode_is_named(xml_node, "config")) {
584 			parse_config_node(vpninfo, xml_node);
585 		} else {
586 			xmlnode_get_text(xml_node, "session-token", &vpninfo->cookie);
587 			xmlnode_get_text(xml_node, "error", &form->error);
588 		}
589 
590 		if (ret)
591 			goto out;
592 		xml_node = xml_node->next;
593 	}
594 
595 	if (!form->auth_id && (!cert_rq || !*cert_rq)) {
596 		vpn_progress(vpninfo, PRG_ERR,
597 			     _("XML response has no \"auth\" node\n"));
598 		ret = -EINVAL;
599 		goto out;
600 	}
601 
602 	*formp = form;
603 	xmlFreeDoc(xml_doc);
604 	return 0;
605 
606  out:
607 	xmlFreeDoc(xml_doc);
608 	free_auth_form(form);
609 	return ret;
610 }
611 
612 /* Return value:
613  *  < 0, on error
614  *  = OC_FORM_RESULT_OK (0), when form parsed and POST required
615  *  = OC_FORM_RESULT_CANCELLED, when response was cancelled by user
616  *  = OC_FORM_RESULT_LOGGEDIN, when form indicates that login was already successful
617  */
618 static int handle_auth_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
619 			    struct oc_text_buf *request_body, const char **method,
620 			    const char **request_body_type)
621 {
622 	int ret;
623 	struct oc_vpn_option *opt, *next;
624 
625 	if (!strcmp(form->auth_id, "success"))
626 		return OC_FORM_RESULT_LOGGEDIN;
627 
628 	if (vpninfo->nopasswd) {
629 		vpn_progress(vpninfo, PRG_ERR,
630 			     _("Asked for password but '--no-passwd' set\n"));
631 		return -EPERM;
632 	}
633 
634 	if (vpninfo->csd_token && vpninfo->csd_ticket && vpninfo->csd_starturl && vpninfo->csd_waiturl) {
635 		/* AB: remove all cookies */
636 		for (opt = vpninfo->cookies; opt; opt = next) {
637 			next = opt->next;
638 
639 			free(opt->option);
640 			free(opt->value);
641 			free(opt);
642 		}
643 		vpninfo->cookies = NULL;
644 		return OC_FORM_RESULT_OK;
645 	}
646 	if (!form->opts) {
647 		if (form->message)
648 			vpn_progress(vpninfo, PRG_INFO, "%s\n", form->message);
649 		if (form->error)
650 			vpn_progress(vpninfo, PRG_ERR, "%s\n", form->error);
651 		return -EPERM;
652 	}
653 
654 	ret = process_auth_form(vpninfo, form);
655 	if (ret)
656 		return ret;
657 
658 	/* tokencode generation is deferred until after username prompts and CSD */
659 	ret = do_gen_tokencode(vpninfo, form);
660 	if (ret) {
661 		vpn_progress(vpninfo, PRG_ERR, _("Failed to generate OTP tokencode; disabling token\n"));
662 		vpninfo->token_bypassed = 1;
663 		return ret;
664 	}
665 
666 	ret = vpninfo->xmlpost ?
667 	      xmlpost_append_form_opts(vpninfo, form, request_body) :
668 	      append_form_opts(vpninfo, form, request_body);
669 	if (!ret) {
670 		*method = "POST";
671 		*request_body_type = "application/x-www-form-urlencoded";
672 	}
673 	return ret;
674 }
675 
676 /*
677  * Old submission format is just an HTTP query string:
678  *
679  * password=12345678&username=joe
680  *
681  * New XML format is more complicated:
682  *
683  * <config-auth client="vpn" type="<!-- init or auth-reply -->">
684  *   <version who="vpn"><!-- currently just the OpenConnect version --></version>
685  *   <device-id><!-- linux, linux-64, win, ... --></device-id>
686  *   <opaque is-for="<!-- some name -->">
687  *     <!-- just copy this verbatim from whatever the gateway sent us -->
688  *   </opaque>
689  *
690  * For init only, add:
691  *   <group-access>https://<!-- insert hostname here --></group-access>
692  *
693  * For auth-reply only, add:
694  *   <auth>
695  *     <username><!-- same treatment as the old form options --></username>
696  *     <password><!-- ditto -->
697  *   </auth>
698  *   <group-select><!-- name of selected authgroup --></group-select>
699  *   <host-scan-token><!-- vpninfo->csd_ticket --></host-scan-token>
700  */
701 
702 #define XCAST(x) ((const xmlChar *)(x))
703 
704 static xmlDocPtr xmlpost_new_query(struct openconnect_info *vpninfo, const char *type,
705 				   xmlNodePtr *rootp)
706 {
707 	xmlDocPtr doc;
708 	xmlNodePtr root, node;
709 
710 	doc = xmlNewDoc(XCAST("1.0"));
711 	if (!doc)
712 		return NULL;
713 
714 	*rootp = root = xmlNewNode(NULL, XCAST("config-auth"));
715 	if (!root)
716 		goto bad;
717 	if (!xmlNewProp(root, XCAST("client"), XCAST("vpn")))
718 		goto bad;
719 	if (!xmlNewProp(root, XCAST("type"), XCAST(type)))
720 		goto bad;
721 	xmlDocSetRootElement(doc, root);
722 
723 	node = xmlNewTextChild(root, NULL, XCAST("version"), XCAST(openconnect_version_str));
724 	if (!node)
725 		goto bad;
726 	if (!xmlNewProp(node, XCAST("who"), XCAST("vpn")))
727 		goto bad;
728 
729 	node = xmlNewTextChild(root, NULL, XCAST("device-id"), XCAST(vpninfo->platname));
730 	if (!node)
731 		goto bad;
732 	if (vpninfo->mobile_platform_version) {
733 		if (!xmlNewProp(node, XCAST("platform-version"), XCAST(vpninfo->mobile_platform_version)) ||
734 		    !xmlNewProp(node, XCAST("device-type"), XCAST(vpninfo->mobile_device_type)) ||
735 		    !xmlNewProp(node, XCAST("unique-id"), XCAST(vpninfo->mobile_device_uniqueid)))
736 			goto bad;
737 	}
738 
739 	return doc;
740 
741 bad:
742 	xmlFreeDoc(doc);
743 	return NULL;
744 }
745 
746 static int xmlpost_complete(xmlDocPtr doc, struct oc_text_buf *body)
747 {
748 	xmlChar *mem = NULL;
749 	int len, ret = 0;
750 
751 	if (!body) {
752 		xmlFree(doc);
753 		return 0;
754 	}
755 
756 	xmlDocDumpMemoryEnc(doc, &mem, &len, "UTF-8");
757 	if (!mem) {
758 		xmlFreeDoc(doc);
759 		return -ENOMEM;
760 	}
761 
762 	buf_append_bytes(body, mem, len);
763 
764 	xmlFreeDoc(doc);
765 	xmlFree(mem);
766 
767 	return ret;
768 }
769 
770 static int xmlpost_initial_req(struct openconnect_info *vpninfo,
771 			       struct oc_text_buf *request_body, int cert_fail)
772 {
773 	xmlNodePtr root, node;
774 	xmlDocPtr doc = xmlpost_new_query(vpninfo, "init", &root);
775 	struct oc_text_buf *url_buf;
776 
777 	if (!doc)
778 		return -ENOMEM;
779 
780 	url_buf = buf_alloc();
781 	buf_append(url_buf, "https://%s", vpninfo->hostname);
782 	if (vpninfo->port != 443)
783 		buf_append(url_buf, ":%d", vpninfo->port);
784 	/* Do we *need* to omit the trailing / here when no path? */
785 	if (vpninfo->urlpath)
786 		buf_append(url_buf, "/%s", vpninfo->urlpath);
787 
788 	if (buf_error(url_buf)) {
789 		buf_free(url_buf);
790 		goto bad;
791 	}
792 	node = xmlNewTextChild(root, NULL, XCAST("group-access"), XCAST(url_buf->data));
793 	buf_free(url_buf);
794 	if (!node)
795 		goto bad;
796 	if (cert_fail) {
797 		node = xmlNewTextChild(root, NULL, XCAST("client-cert-fail"), NULL);
798 		if (!node)
799 			goto bad;
800 	}
801 	if (vpninfo->authgroup) {
802 		node = xmlNewTextChild(root, NULL, XCAST("group-select"), XCAST(vpninfo->authgroup));
803 		if (!node)
804 			goto bad;
805 	}
806 	return xmlpost_complete(doc, request_body);
807 
808 bad:
809 	buf_free(url_buf);
810 	xmlpost_complete(doc, NULL);
811 	return -ENOMEM;
812 }
813 
814 static int xmlpost_append_form_opts(struct openconnect_info *vpninfo,
815 				    struct oc_auth_form *form, struct oc_text_buf *body)
816 {
817 	xmlNodePtr root, node;
818 	xmlDocPtr doc = xmlpost_new_query(vpninfo, "auth-reply", &root);
819 	struct oc_form_opt *opt;
820 
821 	if (!doc)
822 		return -ENOMEM;
823 
824 	if (vpninfo->opaque_srvdata) {
825 		node = xmlCopyNode(vpninfo->opaque_srvdata, 1);
826 		if (!node)
827 			goto bad;
828 		if (!xmlAddChild(root, node))
829 			goto bad;
830 	}
831 
832 	node = xmlNewChild(root, NULL, XCAST("auth"), NULL);
833 	if (!node)
834 		goto bad;
835 
836 	for (opt = form->opts; opt; opt = opt->next) {
837 		/* group_list: create a new <group-select> node under <config-auth> */
838 		if (!strcmp(opt->name, "group_list")) {
839 			if (!xmlNewTextChild(root, NULL, XCAST("group-select"), XCAST(opt->_value)))
840 				goto bad;
841 			continue;
842 		}
843 
844 		/* answer,whichpin,new_password: rename to "password" */
845 		if (!strcmp(opt->name, "answer") ||
846 		    !strcmp(opt->name, "whichpin") ||
847 		    !strcmp(opt->name, "new_password")) {
848 			if (!xmlNewTextChild(node, NULL, XCAST("password"), XCAST(opt->_value)))
849 				goto bad;
850 			continue;
851 		}
852 
853 		/* verify_pin,verify_password: ignore */
854 		if (!strcmp(opt->name, "verify_pin") ||
855 		    !strcmp(opt->name, "verify_password")) {
856 			continue;
857 		}
858 
859 		/* everything else: create <foo>user_input</foo> under <auth> */
860 		if (!xmlNewTextChild(node, NULL, XCAST(opt->name), XCAST(opt->_value)))
861 			goto bad;
862 	}
863 
864 	if (vpninfo->csd_token &&
865 	    !xmlNewTextChild(root, NULL, XCAST("host-scan-token"), XCAST(vpninfo->csd_token)))
866 		goto bad;
867 
868 	return xmlpost_complete(doc, body);
869 
870 bad:
871 	xmlpost_complete(doc, NULL);
872 	return -ENOMEM;
873 }
874 
875 /* Return value:
876  *  < 0, if unable to generate a tokencode
877  *  = 0, on success
878  */
879 static int cstp_can_gen_tokencode(struct openconnect_info *vpninfo,
880 				  struct oc_auth_form *form,
881 				  struct oc_form_opt *opt)
882 {
883 	if (vpninfo->token_mode == OC_TOKEN_MODE_NONE ||
884 	    vpninfo->token_bypassed)
885 		return -EINVAL;
886 
887 #ifdef HAVE_LIBSTOKEN
888 	if (vpninfo->token_mode == OC_TOKEN_MODE_STOKEN) {
889 		if (strcmp(opt->name, "password") &&
890 		    strcmp(opt->name, "answer"))
891 			return -EINVAL;
892 		return can_gen_stoken_code(vpninfo, form, opt);
893 	}
894 #endif
895 	/* Otherwise it's an OATH token of some kind. */
896 	if (strcmp(opt->name, "secondary_password"))
897 		return -EINVAL;
898 
899 	return can_gen_tokencode(vpninfo, form, opt);
900 }
901 
902 static int fetch_config(struct openconnect_info *vpninfo)
903 {
904 	struct oc_text_buf *buf;
905 	int result;
906 	unsigned char local_sha1_bin[SHA1_SIZE];
907 	char local_sha1_ascii[(SHA1_SIZE * 2)+1];
908 	int i;
909 
910 	if (!vpninfo->profile_url || !vpninfo->profile_sha1 || !vpninfo->write_new_config)
911 		return -ENOENT;
912 
913 	if (!strncasecmp(vpninfo->xmlsha1, vpninfo->profile_sha1, SHA1_SIZE * 2)) {
914 		vpn_progress(vpninfo, PRG_TRACE,
915 			     _("Not downloading XML profile because SHA1 already matches\n"));
916 		return 0;
917 	}
918 
919 	if ((result = openconnect_open_https(vpninfo))) {
920 		vpn_progress(vpninfo, PRG_ERR,
921 			     _("Failed to open HTTPS connection to %s\n"),
922 			     vpninfo->hostname);
923 		return result;
924 	}
925 
926 	buf = buf_alloc();
927 
928 	if (vpninfo->port != 443)
929 		buf_append(buf, "GET %s:%d HTTP/1.1\r\n", vpninfo->profile_url, vpninfo->port);
930 	else
931 		buf_append(buf, "GET %s HTTP/1.1\r\n", vpninfo->profile_url);
932 	cstp_common_headers(vpninfo, buf);
933 	if (vpninfo->xmlpost)
934 		buf_append(buf, "Cookie: webvpn=%s\r\n", vpninfo->cookie);
935 	buf_append(buf, "\r\n");
936 
937 	if (buf_error(buf))
938 		return buf_free(buf);
939 
940 	if (vpninfo->ssl_write(vpninfo, buf->data, buf->pos) != buf->pos) {
941 		vpn_progress(vpninfo, PRG_ERR,
942 			     _("Failed to send GET request for new config\n"));
943 		buf_free(buf);
944 		return -EIO;
945 	}
946 
947 	result = process_http_response(vpninfo, 0, NULL, buf);
948 	if (result < 0) {
949 		/* We'll already have complained about whatever offended us */
950 		buf_free(buf);
951 		return -EINVAL;
952 	}
953 
954 	if (result != 200) {
955 		buf_free(buf);
956 		return -EINVAL;
957 	}
958 
959 	openconnect_sha1(local_sha1_bin, buf->data, buf->pos);
960 
961 	for (i = 0; i < SHA1_SIZE; i++)
962 		sprintf(&local_sha1_ascii[i*2], "%02x", local_sha1_bin[i]);
963 
964 	if (strcasecmp(vpninfo->profile_sha1, local_sha1_ascii)) {
965 		vpn_progress(vpninfo, PRG_ERR,
966 			     _("Downloaded config file did not match intended SHA1\n"));
967 		buf_free(buf);
968 		return -EINVAL;
969 	}
970 
971 	vpn_progress(vpninfo, PRG_DEBUG, _("Downloaded new XML profile\n"));
972 
973 	result = vpninfo->write_new_config(vpninfo->cbdata, buf->data, buf->pos);
974 	buf_free(buf);
975 	return result;
976 }
977 
978 static int run_csd_script(struct openconnect_info *vpninfo, char *buf, int buflen)
979 {
980 #if defined(_WIN32) || defined(__native_client__)
981 	vpn_progress(vpninfo, PRG_ERR,
982 		     _("Error: Running the 'Cisco Secure Desktop' trojan on this platform is not yet implemented.\n"));
983 	return -EPERM;
984 #else
985 	char fname[64];
986 	int fd, ret;
987 	pid_t child;
988 
989 	if (!vpninfo->csd_wrapper && !buflen) {
990 		vpn_progress(vpninfo, PRG_ERR,
991 			     _("Error: Server asked us to run CSD hostscan.\n"
992 			       "You need to provide a suitable --csd-wrapper argument.\n"));
993 		return -EINVAL;
994 	}
995 
996 	if (!vpninfo->uid_csd_given && !vpninfo->csd_wrapper) {
997 		vpn_progress(vpninfo, PRG_ERR,
998 			     _("Error: Server asked us to download and run a 'Cisco Secure Desktop' trojan.\n"
999 			       "This facility is disabled by default for security reasons, so you may wish to enable it.\n"));
1000 		return -EPERM;
1001 	}
1002 
1003 #ifndef __linux__
1004 	vpn_progress(vpninfo, PRG_INFO,
1005 		     _("Trying to run Linux CSD trojan script.\n"));
1006 #endif
1007 
1008 	fname[0] = 0;
1009 	if (buflen) {
1010 		struct oc_vpn_option *opt;
1011 		const char *tmpdir = NULL;
1012 
1013 		/* If the caller wanted $TMPDIR set for the CSD script, that
1014 		   means for us too; look through the csd_env for a TMPDIR
1015 		   override. */
1016 		for (opt = vpninfo->csd_env; opt; opt = opt->next) {
1017 			if (!strcmp(opt->option, "TMPDIR")) {
1018 				tmpdir = opt->value;
1019 				break;
1020 			}
1021 		}
1022 		if (!opt)
1023 			tmpdir = getenv("TMPDIR");
1024 
1025 		if (!tmpdir && !access("/var/tmp", W_OK))
1026 			tmpdir = "/var/tmp";
1027 		if (!tmpdir)
1028 			tmpdir = "/tmp";
1029 
1030 		if (access(tmpdir, W_OK))
1031 			vpn_progress(vpninfo, PRG_ERR,
1032 				     _("Temporary directory '%s' is not writable: %s\n"),
1033 				     tmpdir, strerror(errno));
1034 
1035 		snprintf(fname, 64, "%s/csdXXXXXX", tmpdir);
1036 		fd = mkstemp(fname);
1037 		if (fd < 0) {
1038 			int err = -errno;
1039 			vpn_progress(vpninfo, PRG_ERR,
1040 				     _("Failed to open temporary CSD script file: %s\n"),
1041 				     strerror(errno));
1042 			return err;
1043 		}
1044 
1045 		ret = write(fd, (void *)buf, buflen);
1046 		if (ret != buflen) {
1047 			int err = -errno;
1048 			vpn_progress(vpninfo, PRG_ERR,
1049 				     _("Failed to write temporary CSD script file: %s\n"),
1050 				     strerror(errno));
1051 			return err;
1052 		}
1053 		fchmod(fd, 0755);
1054 		close(fd);
1055 	}
1056 
1057 	child = fork();
1058 	if (child == -1) {
1059 		goto out;
1060 	} else if (child > 0) {
1061 		/* in parent: must reap child process */
1062 		int status;
1063 		waitpid(child, &status, 0);
1064 	} else {
1065 		/* in child: run CSD script as daemon */
1066 		if (fork()) {
1067 			/* child must use _exit(2) */
1068 			_exit(0);
1069 		} else {
1070 			/* in grandchild: will be reaped by init */
1071 			char scertbuf[MD5_SIZE * 2 + 1];
1072 			char ccertbuf[MD5_SIZE * 2 + 1];
1073 			char *csd_argv[32];
1074 			int i = 0;
1075 
1076 			setsid();
1077 
1078 			if (vpninfo->uid_csd_given && vpninfo->uid_csd != getuid()) {
1079 				struct passwd *pw;
1080 				int e;
1081 
1082 				if (setgid(vpninfo->gid_csd)) {
1083 					e = errno;
1084 					fprintf(stderr, _("Failed to set gid %ld: %s\n"),
1085 						(long)vpninfo->uid_csd, strerror(e));
1086 					exit(1);
1087 				}
1088 
1089 				if (setgroups(1, &vpninfo->gid_csd)) {
1090 					e = errno;
1091 					fprintf(stderr, _("Failed to set groups to %ld: %s\n"),
1092 						(long)vpninfo->uid_csd, strerror(e));
1093 					exit(1);
1094 				}
1095 
1096 				if (setuid(vpninfo->uid_csd)) {
1097 					e = errno;
1098 					fprintf(stderr, _("Failed to set uid %ld: %s\n"),
1099 						(long)vpninfo->uid_csd, strerror(e));
1100 					exit(1);
1101 				}
1102 
1103 				if (!(pw = getpwuid(vpninfo->uid_csd))) {
1104 					e = errno;
1105 					fprintf(stderr, _("Invalid user uid=%ld: %s\n"),
1106 						(long)vpninfo->uid_csd, strerror(e));
1107 					exit(1);
1108 				}
1109 				setenv("HOME", pw->pw_dir, 1);
1110 				if (chdir(pw->pw_dir)) {
1111 					e = errno;
1112 					fprintf(stderr, _("Failed to change to CSD home directory '%s': %s\n"),
1113 						pw->pw_dir, strerror(e));
1114 					exit(1);
1115 				}
1116 			}
1117 			if (getuid() == 0 && !vpninfo->csd_wrapper) {
1118 				fprintf(stderr, _("Warning: you are running insecure "
1119 						  "CSD code with root privileges\n"
1120 						  "\t Use command line option \"--csd-user\"\n"));
1121 			}
1122 			/* Spurious stdout output from the CSD trojan will break both
1123 			   the NM tool and the various cookieonly modes. */
1124 			dup2(2, 1);
1125 			if (vpninfo->csd_wrapper)
1126 				csd_argv[i++] = openconnect_utf8_to_legacy(vpninfo,
1127 									   vpninfo->csd_wrapper);
1128 			csd_argv[i++] = fname;
1129 			csd_argv[i++] = (char *)"-ticket";
1130 			if (asprintf(&csd_argv[i++], "\"%s\"", vpninfo->csd_ticket) == -1)
1131 				goto out;
1132 			csd_argv[i++] = (char *)"-stub";
1133 			csd_argv[i++] = (char *)"\"0\"";
1134 			csd_argv[i++] = (char *)"-group";
1135 			if (asprintf(&csd_argv[i++], "\"%s\"", vpninfo->authgroup?:"") == -1)
1136 				goto out;
1137 
1138 			openconnect_local_cert_md5(vpninfo, ccertbuf);
1139 			scertbuf[0] = 0;
1140 			get_cert_md5_fingerprint(vpninfo, vpninfo->peer_cert, scertbuf);
1141 			csd_argv[i++] = (char *)"-certhash";
1142 			if (asprintf(&csd_argv[i++], "\"%s:%s\"", scertbuf, ccertbuf) == -1)
1143 				goto out;
1144 
1145 			csd_argv[i++] = (char *)"-url";
1146 			if (asprintf(&csd_argv[i++], "\"https://%s%s\"", vpninfo->hostname, vpninfo->csd_starturl) == -1)
1147 				goto out;
1148 
1149 			csd_argv[i++] = (char *)"-langselen";
1150 			csd_argv[i++] = NULL;
1151 
1152 			if (setenv("CSD_TOKEN", vpninfo->csd_token, 1))
1153 				goto out;
1154 			if (setenv("CSD_HOSTNAME", vpninfo->hostname, 1))
1155 				goto out;
1156 
1157 			apply_script_env(vpninfo->csd_env);
1158 
1159 			execv(csd_argv[0], csd_argv);
1160 
1161 		out:
1162 			vpn_progress(vpninfo, PRG_ERR,
1163 				     _("Failed to exec CSD script %s\n"), csd_argv[0]);
1164 			exit(1);
1165 		}
1166 	}
1167 
1168 	free(vpninfo->csd_stuburl);
1169 	vpninfo->csd_stuburl = NULL;
1170 	free(vpninfo->urlpath);
1171 	vpninfo->urlpath = strdup(vpninfo->csd_waiturl +
1172 				  (vpninfo->csd_waiturl[0] == '/' ? 1 : 0));
1173 	free(vpninfo->csd_waiturl);
1174 	vpninfo->csd_waiturl = NULL;
1175 	vpninfo->csd_scriptname = strdup(fname);
1176 
1177 	http_add_cookie(vpninfo, "sdesktop", vpninfo->csd_token, 1);
1178 	return 0;
1179 #endif /* !_WIN32 && !__native_client__ */
1180 }
1181 
1182 
1183 /* Return value:
1184  *  < 0, if the data is unrecognized
1185  *  = 0, if the page contains an XML document
1186  *  = 1, if the page is a wait/refresh HTML page
1187  */
1188 static int check_response_type(struct openconnect_info *vpninfo, char *form_buf)
1189 {
1190 	if (strncmp(form_buf, "<?xml", 5)) {
1191 		/* Not XML? Perhaps it's HTML with a refresh... */
1192 		if (strcasestr(form_buf, "http-equiv=\"refresh\""))
1193 			return 1;
1194 		vpn_progress(vpninfo, PRG_ERR,
1195 			     _("Unknown response from server\n"));
1196 		return -EINVAL;
1197 	}
1198 	return 0;
1199 }
1200 
1201 /* Return value:
1202  *  < 0, on error
1203  *  > 0, no cookie (user cancel)
1204  *  = 0, obtained cookie
1205  */
1206 int cstp_obtain_cookie(struct openconnect_info *vpninfo)
1207 {
1208 	struct oc_vpn_option *opt;
1209 	char *form_buf = NULL;
1210 	struct oc_auth_form *form = NULL;
1211 	int result, buflen, tries;
1212 	struct oc_text_buf *request_body = buf_alloc();
1213 	const char *request_body_type = "application/x-www-form-urlencoded";
1214 	const char *method = "POST";
1215 	char *orig_host = NULL, *orig_path = NULL, *form_path = NULL;
1216 	int orig_port = 0;
1217 	int cert_rq, cert_sent = !vpninfo->cert;
1218 	int newgroup_attempts = 5;
1219 
1220 #ifdef HAVE_LIBSTOKEN
1221 	/* Step 1: Unlock software token (if applicable) */
1222 	if (vpninfo->token_mode == OC_TOKEN_MODE_STOKEN) {
1223 		result = prepare_stoken(vpninfo);
1224 		if (result)
1225 			goto out;
1226 	}
1227 #endif
1228 
1229 	if (!vpninfo->xmlpost)
1230 		goto no_xmlpost;
1231 
1232 	/*
1233 	 * Step 2: Probe for XML POST compatibility
1234 	 *
1235 	 * This can get stuck in a redirect loop, so give up after any of:
1236 	 *
1237 	 * a) HTTP error (e.g. 400 Bad Request)
1238 	 * b) Same-host redirect (e.g. Location: /foo/bar)
1239 	 * c) Three redirects without seeing a plausible login form
1240 	 */
1241 newgroup:
1242 	if (newgroup_attempts-- <= 0) {
1243 		result = -1;
1244 		goto out;
1245 	}
1246 
1247 	buf_truncate(request_body);
1248 	result = xmlpost_initial_req(vpninfo, request_body, 0);
1249 	if (result < 0)
1250 		goto out;
1251 
1252 	free(orig_host);
1253 	free(orig_path);
1254 	orig_host = strdup(vpninfo->hostname);
1255 	orig_path = vpninfo->urlpath ? strdup(vpninfo->urlpath) : NULL;
1256 	orig_port = vpninfo->port;
1257 
1258 	for (tries = 0; ; tries++) {
1259 		if (tries == 3) {
1260 		fail:
1261 			if (vpninfo->xmlpost) {
1262 			no_xmlpost:
1263 				/* Try without XML POST this time... */
1264 				tries = 0;
1265 				vpninfo->xmlpost = 0;
1266 				request_body_type = NULL;
1267 				buf_truncate(request_body);
1268 				method = "GET";
1269 				if (orig_host) {
1270 					openconnect_set_hostname(vpninfo, orig_host);
1271 					free(orig_host);
1272 					orig_host = NULL;
1273 					free(vpninfo->urlpath);
1274 					vpninfo->urlpath = orig_path;
1275 					orig_path = NULL;
1276 					vpninfo->port = orig_port;
1277 				}
1278 				openconnect_close_https(vpninfo, 0);
1279 			} else {
1280 				result = -EIO;
1281 				goto out;
1282 			}
1283 		}
1284 
1285 		result = do_https_request(vpninfo, method, request_body_type, request_body,
1286 					  &form_buf, 0);
1287 		if (vpninfo->got_cancel_cmd) {
1288 			result = 1;
1289 			goto out;
1290 		}
1291 		if (result == -EINVAL)
1292 			goto fail;
1293 		if (result < 0)
1294 			goto out;
1295 
1296 		/* Some ASAs forget to send the TLS cert request on the initial connection.
1297 		 * If we have a client cert, disable HTTP keepalive until we get a real
1298 		 * login form (not a redirect). */
1299 		if (!cert_sent)
1300 			openconnect_close_https(vpninfo, 0);
1301 
1302 		/* XML POST does not allow local redirects, but GET does. */
1303 		if (vpninfo->xmlpost &&
1304 		    vpninfo->redirect_type == REDIR_TYPE_LOCAL)
1305 			goto fail;
1306 		else if (vpninfo->redirect_type != REDIR_TYPE_NONE)
1307 			continue;
1308 
1309 		result = parse_xml_response(vpninfo, form_buf, &form, &cert_rq);
1310 		if (result < 0)
1311 			goto fail;
1312 
1313 		if (cert_rq) {
1314 			int cert_failed = 0;
1315 
1316 			free_auth_form(form);
1317 			form = NULL;
1318 
1319 			if (!cert_sent && vpninfo->cert) {
1320 				/* Try again on a fresh connection. */
1321 				cert_sent = 1;
1322 			} else if (cert_sent && vpninfo->cert) {
1323 				/* Try again with <client-cert-fail/> in the request */
1324 				vpn_progress(vpninfo, PRG_ERR,
1325 					     _("Server requested SSL client certificate after one was provided\n"));
1326 				cert_failed = 1;
1327 			} else {
1328 				vpn_progress(vpninfo, PRG_INFO,
1329 					     _("Server requested SSL client certificate; none was configured\n"));
1330 				cert_failed = 1;
1331 			}
1332 			buf_truncate(request_body);
1333 			result = xmlpost_initial_req(vpninfo, request_body, cert_failed);
1334 			if (result < 0)
1335 				goto fail;
1336 			continue;
1337 		}
1338 		if (form && form->action) {
1339 			vpninfo->redirect_url = strdup(form->action);
1340 			handle_redirect(vpninfo);
1341 		}
1342 		break;
1343 	}
1344 	if (vpninfo->xmlpost)
1345 		vpn_progress(vpninfo, PRG_INFO, _("XML POST enabled\n"));
1346 
1347 	/* Step 4: Run the CSD trojan, if applicable */
1348 	if (vpninfo->csd_starturl && vpninfo->csd_waiturl) {
1349 		buflen = 0;
1350 
1351 		if (vpninfo->urlpath) {
1352 			form_path = strdup(vpninfo->urlpath);
1353 			if (!form_path) {
1354 				result = -ENOMEM;
1355 				goto out;
1356 			}
1357 		}
1358 
1359 		/* fetch the CSD program, if available */
1360 		if (vpninfo->csd_stuburl) {
1361 			vpninfo->redirect_url = vpninfo->csd_stuburl;
1362 			vpninfo->csd_stuburl = NULL;
1363 			handle_redirect(vpninfo);
1364 
1365 			buflen = do_https_request(vpninfo, "GET", NULL, NULL, &form_buf, 0);
1366 			if (buflen <= 0) {
1367 				result = -EINVAL;
1368 				goto out;
1369 			}
1370 		}
1371 
1372 		/* This is the CSD stub script, which we now need to run */
1373 		result = run_csd_script(vpninfo, form_buf, buflen);
1374 		if (result)
1375 			goto out;
1376 
1377 		/* vpninfo->urlpath now points to the wait page */
1378 		while (1) {
1379 			result = do_https_request(vpninfo, "GET", NULL, NULL, &form_buf, 0);
1380 			if (result <= 0)
1381 				break;
1382 
1383 			result = check_response_type(vpninfo, form_buf);
1384 			if (result <= 0)
1385 				break;
1386 
1387 			vpn_progress(vpninfo, PRG_INFO,
1388 				     _("Refreshing %s after 1 second...\n"),
1389 				     vpninfo->urlpath);
1390 			sleep(1);
1391 		}
1392 		if (result < 0)
1393 			goto out;
1394 
1395 		/* refresh the form page, to see if we're authorized now */
1396 		free(vpninfo->urlpath);
1397 		vpninfo->urlpath = form_path;
1398 		form_path = NULL;
1399 
1400 		result = do_https_request(vpninfo,
1401 					  vpninfo->xmlpost ? "POST" : "GET",
1402 					  request_body_type, request_body, &form_buf, 1);
1403 		if (result < 0)
1404 			goto out;
1405 
1406 		result = parse_xml_response(vpninfo, form_buf, &form, NULL);
1407 		if (result < 0)
1408 			goto out;
1409 	}
1410 
1411 	/* Step 5: Ask the user to fill in the auth form; repeat as necessary */
1412 	while (1) {
1413 		buf_truncate(request_body);
1414 		result = handle_auth_form(vpninfo, form, request_body,
1415 					  &method, &request_body_type);
1416 		if (result < 0 || result == OC_FORM_RESULT_CANCELLED)
1417 			goto out;
1418 		if (result == OC_FORM_RESULT_LOGGEDIN)
1419 			break;
1420 		if (result == OC_FORM_RESULT_NEWGROUP) {
1421 			free(form_buf);
1422 			form_buf = NULL;
1423 			free_auth_form(form);
1424 			form = NULL;
1425 			goto newgroup;
1426 		}
1427 
1428 		result = do_https_request(vpninfo, method, request_body_type, request_body,
1429 					  &form_buf, 1);
1430 		if (result < 0)
1431 			goto out;
1432 
1433 		result = parse_xml_response(vpninfo, form_buf, &form, NULL);
1434 		if (result < 0)
1435 			goto out;
1436 		if (form->action) {
1437 			vpninfo->redirect_url = strdup(form->action);
1438 			handle_redirect(vpninfo);
1439 		}
1440 	}
1441 
1442 	/* A return value of 2 means the XML form indicated
1443 	   success. We _should_ have a cookie... */
1444 
1445 	for (opt = vpninfo->cookies; opt; opt = opt->next) {
1446 
1447 		if (!strcmp(opt->option, "webvpn")) {
1448 			free(vpninfo->cookie);
1449 			vpninfo->cookie = strdup(opt->value);
1450 		} else if (vpninfo->write_new_config && !strcmp(opt->option, "webvpnc")) {
1451 			char *tok = opt->value;
1452 			char *bu = NULL, *fu = NULL, *sha = NULL;
1453 
1454 			do {
1455 				if (tok != opt->value)
1456 					*(tok++) = 0;
1457 
1458 				if (!strncmp(tok, "bu:", 3))
1459 					bu = tok + 3;
1460 				else if (!strncmp(tok, "fu:", 3))
1461 					fu = tok + 3;
1462 				else if (!strncmp(tok, "fh:", 3))
1463 					sha = tok + 3;
1464 			} while ((tok = strchr(tok, '&')));
1465 
1466 			if (bu && fu && sha) {
1467 				if (asprintf(&vpninfo->profile_url, "%s%s", bu, fu) == -1) {
1468 					result = -ENOMEM;
1469 					goto out;
1470 				}
1471 				vpninfo->profile_sha1 = strdup(sha);
1472 			}
1473 		}
1474 	}
1475 	result = 0;
1476 
1477 	fetch_config(vpninfo);
1478 
1479 out:
1480 	buf_free(request_body);
1481 
1482 	free (orig_host);
1483 	free (orig_path);
1484 
1485 	free(form_path);
1486 	free(form_buf);
1487 	free_auth_form(form);
1488 
1489 	if (vpninfo->csd_scriptname) {
1490 		unlink(vpninfo->csd_scriptname);
1491 		free(vpninfo->csd_scriptname);
1492 		vpninfo->csd_scriptname = NULL;
1493 	}
1494 
1495 	return result;
1496 }