2 * OpenConnect (SSL + DTLS) VPN client
4 * Copyright © 2008-2011 Intel Corporation.
5 * Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
7 * Author: David Woodhouse <dwmw2@infradead.org>
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.
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.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to:
21 * Free Software Foundation, Inc.
22 * 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301 USA
36 #include LIBSTOKEN_HDR
39 #include <libxml/parser.h>
40 #include <libxml/tree.h>
42 #include "openconnect-internal.h"
44 static int append_opt(char *body, int bodylen, char *opt, char *name)
46 int len = strlen(body);
49 if (len >= bodylen - 1)
55 if (isalnum((int)(unsigned char)*opt)) {
56 if (len >= bodylen - 1)
60 if (len >= bodylen - 3)
62 sprintf(body+len, "%%%02x", *opt);
68 if (len >= bodylen - 1)
72 while (name && *name) {
73 if (isalnum((int)(unsigned char)*name)) {
74 if (len >= bodylen - 1)
78 if (len >= bodylen - 3)
80 sprintf(body+len, "%%%02X", *name);
90 static int append_form_opts(struct openconnect_info *vpninfo,
91 struct oc_auth_form *form, char *body, int bodylen)
93 struct oc_form_opt *opt;
96 for (opt = form->opts; opt; opt = opt->next) {
97 ret = append_opt(body, bodylen, opt->name, opt->value);
105 * Maybe we should offer this choice to the user. So far we've only
106 * ever seen it offer bogus choices though -- between certificate and
107 * password authentication, when the former has already failed.
108 * So we just accept the first option with an auth-type property.
111 static int parse_auth_choice(struct openconnect_info *vpninfo, struct oc_auth_form *form,
114 struct oc_form_opt_select *opt;
116 opt = calloc(1, sizeof(*opt));
120 opt->form.type = OC_FORM_OPT_SELECT;
121 opt->form.name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
122 opt->form.label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
124 if (!opt->form.name) {
125 vpn_progress(vpninfo, PRG_ERR, _("Form choice has no name\n"));
130 for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
132 struct oc_choice *choice;
134 if (xml_node->type != XML_ELEMENT_NODE)
137 if (strcmp((char *)xml_node->name, "option"))
140 form_id = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
145 opt = realloc(opt, sizeof(*opt) +
146 opt->nr_choices * sizeof(*choice));
150 choice = &opt->choices[opt->nr_choices-1];
152 choice->name = form_id;
153 choice->label = (char *)xmlNodeGetContent(xml_node);
154 choice->auth_type = (char *)xmlGetProp(xml_node, (unsigned char *)"auth-type");
155 choice->override_name = (char *)xmlGetProp(xml_node, (unsigned char *)"override-name");
156 choice->override_label = (char *)xmlGetProp(xml_node, (unsigned char *)"override-label");
159 /* We link the choice _first_ so it's at the top of what we present
161 opt->form.next = form->opts;
162 form->opts = &opt->form;
168 * = 0, when form was cancelled
169 * = 1, when form was parsed
171 static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
172 xmlNode *xml_node, char *body, int bodylen)
174 char *input_type, *input_name, *input_label;
176 for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
177 struct oc_form_opt *opt, **p;
179 if (xml_node->type != XML_ELEMENT_NODE)
182 if (!strcmp((char *)xml_node->name, "select")) {
183 if (parse_auth_choice(vpninfo, form, xml_node))
187 if (strcmp((char *)xml_node->name, "input")) {
188 vpn_progress(vpninfo, PRG_TRACE,
189 _("name %s not input\n"), xml_node->name);
193 input_type = (char *)xmlGetProp(xml_node, (unsigned char *)"type");
195 vpn_progress(vpninfo, PRG_INFO,
196 _("No input type in form\n"));
200 if (!strcmp(input_type, "submit") || !strcmp(input_type, "reset")) {
205 input_name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
207 vpn_progress(vpninfo, PRG_INFO,
208 _("No input name in form\n"));
212 input_label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
214 opt = calloc(1, sizeof(*opt));
222 opt->name = input_name;
223 opt->label = input_label;
225 if (!strcmp(input_type, "hidden")) {
226 opt->type = OC_FORM_OPT_HIDDEN;
227 opt->value = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
228 } else if (!strcmp(input_type, "text"))
229 opt->type = OC_FORM_OPT_TEXT;
230 else if (!strcmp(input_type, "password"))
231 opt->type = OC_FORM_OPT_PASSWORD;
233 vpn_progress(vpninfo, PRG_INFO,
234 _("Unknown input type %s in form\n"),
252 vpn_progress(vpninfo, PRG_TRACE, _("Fixed options give %s\n"), body);
257 static char *xmlnode_msg(xmlNode *xml_node)
259 char *fmt = (char *)xmlNodeGetContent(xml_node);
260 char *result, *params[2], *pct;
264 if (!fmt || !fmt[0]) {
269 len = strlen(fmt) + 1;
271 params[0] = (char *)xmlGetProp(xml_node, (unsigned char *)"param1");
273 len += strlen(params[0]);
274 params[1] = (char *)xmlGetProp(xml_node, (unsigned char *)"param2");
276 len += strlen(params[1]);
278 result = malloc(len);
287 for (pct = strchr(result, '%'); pct;
288 (pct = strchr(pct, '%'))) {
291 /* We only cope with '%s' */
295 if (params[nr_params]) {
296 paramlen = strlen(params[nr_params]);
297 /* Move rest of fmt string up... */
298 memmove(pct - 1 + paramlen, pct + 2, strlen(pct) - 1);
299 /* ... and put the string parameter in where the '%s' was */
300 memcpy(pct, params[nr_params], paramlen);
305 if (++nr_params == 2)
316 * = 0, when form parsed and POST required
317 * = 1, when response was cancelled by user
318 * = 2, when form indicates that login was already successful
320 int parse_xml_response(struct openconnect_info *vpninfo, char *response,
321 char *request_body, int req_len, const char **method,
322 const char **request_body_type)
324 struct oc_auth_form *form;
328 struct vpn_option *opt, *next;
330 form = calloc(1, sizeof(*form));
334 xml_doc = xmlReadMemory(response, strlen(response), "noname.xml", NULL, 0);
336 vpn_progress(vpninfo, PRG_ERR,
337 _("Failed to parse server response\n"));
338 vpn_progress(vpninfo, PRG_TRACE,
339 _("Response was:%s\n"), response);
344 xml_node = xmlDocGetRootElement(xml_doc);
345 if (xml_node->type != XML_ELEMENT_NODE || strcmp((char *)xml_node->name, "auth")) {
346 vpn_progress(vpninfo, PRG_ERR,
347 _("XML response has no \"auth\" root node\n"));
352 form->auth_id = (char *)xmlGetProp(xml_node, (unsigned char *)"id");
353 if (!strcmp(form->auth_id, "success")) {
358 if (vpninfo->nopasswd) {
359 vpn_progress(vpninfo, PRG_ERR,
360 _("Asked for password but '--no-passwd' set\n"));
365 for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
366 if (xml_node->type != XML_ELEMENT_NODE)
369 if (!strcmp((char *)xml_node->name, "banner")) {
371 form->banner = xmlnode_msg(xml_node);
372 } else if (!strcmp((char *)xml_node->name, "message")) {
374 form->message = xmlnode_msg(xml_node);
375 } else if (!strcmp((char *)xml_node->name, "error")) {
377 form->error = xmlnode_msg(xml_node);
378 } else if (!strcmp((char *)xml_node->name, "form")) {
380 form->method = (char *)xmlGetProp(xml_node, (unsigned char *)"method");
381 form->action = (char *)xmlGetProp(xml_node, (unsigned char *)"action");
382 if (!form->method || !form->action ||
383 strcasecmp(form->method, "POST") || !form->action[0]) {
384 vpn_progress(vpninfo, PRG_ERR,
385 _("Cannot handle form method='%s', action='%s'\n"),
386 form->method, form->action);
390 vpninfo->redirect_url = strdup(form->action);
392 ret = parse_form(vpninfo, form, xml_node, request_body, req_len);
395 } else if (!vpninfo->csd_scriptname && !strcmp((char *)xml_node->name, "csd")) {
396 if (!vpninfo->csd_token)
397 vpninfo->csd_token = (char *)xmlGetProp(xml_node,
398 (unsigned char *)"token");
399 if (!vpninfo->csd_ticket)
400 vpninfo->csd_ticket = (char *)xmlGetProp(xml_node,
401 (unsigned char *)"ticket");
402 } else if (!vpninfo->csd_scriptname && !strcmp((char *)xml_node->name, vpninfo->csd_xmltag)) {
403 vpninfo->csd_stuburl = (char *)xmlGetProp(xml_node,
404 (unsigned char *)"stuburl");
405 vpninfo->csd_starturl = (char *)xmlGetProp(xml_node,
406 (unsigned char *)"starturl");
407 vpninfo->csd_waiturl = (char *)xmlGetProp(xml_node,
408 (unsigned char *)"waiturl");
409 vpninfo->csd_preurl = strdup(vpninfo->urlpath);
412 if (vpninfo->csd_token && vpninfo->csd_ticket && vpninfo->csd_starturl && vpninfo->csd_waiturl) {
413 /* First, redirect to the stuburl -- we'll need to fetch and run that */
414 vpninfo->redirect_url = strdup(vpninfo->csd_stuburl);
416 /* AB: remove all cookies */
417 for (opt = vpninfo->cookies; opt; opt = next) {
424 vpninfo->cookies = NULL;
431 vpn_progress(vpninfo, PRG_INFO, "%s\n", form->message);
433 vpn_progress(vpninfo, PRG_ERR, "%s\n", form->error);
438 if (vpninfo->process_auth_form)
439 ret = vpninfo->process_auth_form(vpninfo->cbdata, form);
441 vpn_progress(vpninfo, PRG_ERR, _("No form handler; cannot authenticate.\n"));
447 ret = append_form_opts(vpninfo, form, request_body, req_len);
450 *request_body_type = "application/x-www-form-urlencoded";
455 struct oc_form_opt *tmp = form->opts->next;
456 if (form->opts->type == OC_FORM_OPT_TEXT ||
457 form->opts->type == OC_FORM_OPT_PASSWORD ||
458 form->opts->type == OC_FORM_OPT_HIDDEN)
459 free(form->opts->value);
460 else if (form->opts->type == OC_FORM_OPT_SELECT) {
461 struct oc_form_opt_select *sel = (void *)form->opts;
464 for (i=0; i < sel->nr_choices; i++) {
465 free(sel->choices[i].name);
466 free(sel->choices[i].label);
467 free(sel->choices[i].auth_type);
468 free(sel->choices[i].override_name);
469 free(sel->choices[i].override_label);
472 free(form->opts->label);
473 free(form->opts->name);
487 static void nuke_opt_values(struct oc_form_opt *opt)
489 for (; opt; opt = opt->next) {
496 * If the user clicks OK without entering any data, we will continue
497 * connecting but bypass soft token generation for the duration of
498 * this "obtain_cookie" session.
500 * If the user clicks Cancel, we will abort the connection.
504 * = 0, on success (or if the user bypassed soft token init)
505 * = 1, if the user cancelled the form submission
507 int prepare_stoken(struct openconnect_info *vpninfo)
510 struct oc_auth_form form;
511 struct oc_form_opt opts[3], *opt = opts;
512 char **devid = NULL, **pass = NULL, **pin = NULL;
515 memset(&form, 0, sizeof(form));
516 memset(&opts, 0, sizeof(opts));
519 form.message = _("Enter credentials to unlock software token.");
521 vpninfo->stoken_tries = 0;
522 vpninfo->stoken_bypassed = 0;
524 if (stoken_devid_required(vpninfo->stoken_ctx)) {
525 opt->type = OC_FORM_OPT_TEXT;
526 opt->name = (char *)"devid";
527 opt->label = _("Device ID:");
531 if (stoken_pass_required(vpninfo->stoken_ctx)) {
532 opt->type = OC_FORM_OPT_PASSWORD;
533 opt->name = (char *)"password";
534 opt->label = _("Password:");
538 if (stoken_pin_required(vpninfo->stoken_ctx)) {
539 opt->type = OC_FORM_OPT_PASSWORD;
540 opt->name = (char *)"password";
541 opt->label = _("PIN:");
546 opts[0].next = opts[1].type ? &opts[1] : NULL;
547 opts[1].next = opts[2].type ? &opts[2] : NULL;
550 nuke_opt_values(opts);
553 /* don't bug the user if there's nothing to enter */
555 } else if (vpninfo->process_auth_form) {
556 int some_empty = 0, all_empty = 1;
558 /* < 0 for error; 1 if cancelled */
559 ret = vpninfo->process_auth_form(vpninfo->cbdata, &form);
563 for (opt = opts; opt; opt = opt->next) {
564 if (!opt->value || !strlen(opt->value))
570 vpn_progress(vpninfo, PRG_INFO,
571 _("User bypassed soft token.\n"));
572 vpninfo->stoken_bypassed = 1;
577 vpn_progress(vpninfo, PRG_INFO,
578 _("All fields are required; try again.\n"));
582 vpn_progress(vpninfo, PRG_ERR,
583 _("No form handler; cannot authenticate.\n"));
588 ret = stoken_decrypt_seed(vpninfo->stoken_ctx,
590 devid ? *devid : NULL);
591 if (ret == -EIO || (ret && !devid && !pass)) {
592 vpn_progress(vpninfo, PRG_ERR,
593 _("General failure in libstoken.\n"));
595 } else if (ret != 0) {
596 vpn_progress(vpninfo, PRG_INFO,
597 _("Incorrect device ID or password; try again.\n"));
602 if (stoken_check_pin(vpninfo->stoken_ctx, *pin) != 0) {
603 vpn_progress(vpninfo, PRG_INFO,
604 _("Invalid PIN format; try again.\n"));
607 free(vpninfo->stoken_pin);
608 vpninfo->stoken_pin = strdup(*pin);
609 if (!vpninfo->stoken_pin) {
614 vpn_progress(vpninfo, PRG_DEBUG, _("Soft token init was successful.\n"));
619 nuke_opt_values(opts);