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 can_gen_tokencode(struct openconnect_info *vpninfo, struct oc_form_opt *opt);
45 static int do_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form);
47 static int append_opt(char *body, int bodylen, char *opt, char *name)
49 int len = strlen(body);
52 if (len >= bodylen - 1)
58 if (isalnum((int)(unsigned char)*opt)) {
59 if (len >= bodylen - 1)
63 if (len >= bodylen - 3)
65 sprintf(body+len, "%%%02x", *opt);
71 if (len >= bodylen - 1)
75 while (name && *name) {
76 if (isalnum((int)(unsigned char)*name)) {
77 if (len >= bodylen - 1)
81 if (len >= bodylen - 3)
83 sprintf(body+len, "%%%02X", *name);
93 static int append_form_opts(struct openconnect_info *vpninfo,
94 struct oc_auth_form *form, char *body, int bodylen)
96 struct oc_form_opt *opt;
99 for (opt = form->opts; opt; opt = opt->next) {
100 ret = append_opt(body, bodylen, opt->name, opt->value);
108 * Maybe we should offer this choice to the user. So far we've only
109 * ever seen it offer bogus choices though -- between certificate and
110 * password authentication, when the former has already failed.
111 * So we just accept the first option with an auth-type property.
114 static int parse_auth_choice(struct openconnect_info *vpninfo, struct oc_auth_form *form,
117 struct oc_form_opt_select *opt;
119 opt = calloc(1, sizeof(*opt));
123 opt->form.type = OC_FORM_OPT_SELECT;
124 opt->form.name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
125 opt->form.label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
127 if (!opt->form.name) {
128 vpn_progress(vpninfo, PRG_ERR, _("Form choice has no name\n"));
133 for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
135 struct oc_choice *choice;
137 if (xml_node->type != XML_ELEMENT_NODE)
140 if (strcmp((char *)xml_node->name, "option"))
143 form_id = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
148 opt = realloc(opt, sizeof(*opt) +
149 opt->nr_choices * sizeof(*choice));
153 choice = &opt->choices[opt->nr_choices-1];
155 choice->name = form_id;
156 choice->label = (char *)xmlNodeGetContent(xml_node);
157 choice->auth_type = (char *)xmlGetProp(xml_node, (unsigned char *)"auth-type");
158 choice->override_name = (char *)xmlGetProp(xml_node, (unsigned char *)"override-name");
159 choice->override_label = (char *)xmlGetProp(xml_node, (unsigned char *)"override-label");
162 /* We link the choice _first_ so it's at the top of what we present
164 opt->form.next = form->opts;
165 form->opts = &opt->form;
171 * = 0, when form was cancelled
172 * = 1, when form was parsed
174 static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
175 xmlNode *xml_node, char *body, int bodylen)
177 char *input_type, *input_name, *input_label;
179 for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
180 struct oc_form_opt *opt, **p;
182 if (xml_node->type != XML_ELEMENT_NODE)
185 if (!strcmp((char *)xml_node->name, "select")) {
186 if (parse_auth_choice(vpninfo, form, xml_node))
190 if (strcmp((char *)xml_node->name, "input")) {
191 vpn_progress(vpninfo, PRG_TRACE,
192 _("name %s not input\n"), xml_node->name);
196 input_type = (char *)xmlGetProp(xml_node, (unsigned char *)"type");
198 vpn_progress(vpninfo, PRG_INFO,
199 _("No input type in form\n"));
203 if (!strcmp(input_type, "submit") || !strcmp(input_type, "reset")) {
208 input_name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
210 vpn_progress(vpninfo, PRG_INFO,
211 _("No input name in form\n"));
215 input_label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
217 opt = calloc(1, sizeof(*opt));
225 opt->name = input_name;
226 opt->label = input_label;
228 if (!strcmp(input_type, "hidden")) {
229 opt->type = OC_FORM_OPT_HIDDEN;
230 opt->value = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
231 } else if (!strcmp(input_type, "text"))
232 opt->type = OC_FORM_OPT_TEXT;
233 else if (!strcmp(input_type, "password")) {
234 if (vpninfo->use_stoken && !can_gen_tokencode(vpninfo, opt))
235 opt->type = OC_FORM_OPT_STOKEN;
237 opt->type = OC_FORM_OPT_PASSWORD;
239 vpn_progress(vpninfo, PRG_INFO,
240 _("Unknown input type %s in form\n"),
258 vpn_progress(vpninfo, PRG_TRACE, _("Fixed options give %s\n"), body);
263 static char *xmlnode_msg(xmlNode *xml_node)
265 char *fmt = (char *)xmlNodeGetContent(xml_node);
266 char *result, *params[2], *pct;
270 if (!fmt || !fmt[0]) {
275 len = strlen(fmt) + 1;
277 params[0] = (char *)xmlGetProp(xml_node, (unsigned char *)"param1");
279 len += strlen(params[0]);
280 params[1] = (char *)xmlGetProp(xml_node, (unsigned char *)"param2");
282 len += strlen(params[1]);
284 result = malloc(len);
293 for (pct = strchr(result, '%'); pct;
294 (pct = strchr(pct, '%'))) {
297 /* We only cope with '%s' */
301 if (params[nr_params]) {
302 paramlen = strlen(params[nr_params]);
303 /* Move rest of fmt string up... */
304 memmove(pct - 1 + paramlen, pct + 2, strlen(pct) - 1);
305 /* ... and put the string parameter in where the '%s' was */
306 memcpy(pct, params[nr_params], paramlen);
311 if (++nr_params == 2)
322 * = 0, when form parsed and POST required
323 * = 1, when response was cancelled by user
324 * = 2, when form indicates that login was already successful
326 int parse_xml_response(struct openconnect_info *vpninfo, char *response,
327 char *request_body, int req_len, const char **method,
328 const char **request_body_type)
330 struct oc_auth_form *form;
334 struct vpn_option *opt, *next;
336 form = calloc(1, sizeof(*form));
340 xml_doc = xmlReadMemory(response, strlen(response), "noname.xml", NULL, 0);
342 vpn_progress(vpninfo, PRG_ERR,
343 _("Failed to parse server response\n"));
344 vpn_progress(vpninfo, PRG_TRACE,
345 _("Response was:%s\n"), response);
350 xml_node = xmlDocGetRootElement(xml_doc);
351 if (xml_node->type != XML_ELEMENT_NODE || strcmp((char *)xml_node->name, "auth")) {
352 vpn_progress(vpninfo, PRG_ERR,
353 _("XML response has no \"auth\" root node\n"));
358 form->auth_id = (char *)xmlGetProp(xml_node, (unsigned char *)"id");
359 if (!strcmp(form->auth_id, "success")) {
364 if (vpninfo->nopasswd) {
365 vpn_progress(vpninfo, PRG_ERR,
366 _("Asked for password but '--no-passwd' set\n"));
371 for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
372 if (xml_node->type != XML_ELEMENT_NODE)
375 if (!strcmp((char *)xml_node->name, "banner")) {
377 form->banner = xmlnode_msg(xml_node);
378 } else if (!strcmp((char *)xml_node->name, "message")) {
380 form->message = xmlnode_msg(xml_node);
381 } else if (!strcmp((char *)xml_node->name, "error")) {
383 form->error = xmlnode_msg(xml_node);
384 } else if (!strcmp((char *)xml_node->name, "form")) {
386 form->method = (char *)xmlGetProp(xml_node, (unsigned char *)"method");
387 form->action = (char *)xmlGetProp(xml_node, (unsigned char *)"action");
388 if (!form->method || !form->action ||
389 strcasecmp(form->method, "POST") || !form->action[0]) {
390 vpn_progress(vpninfo, PRG_ERR,
391 _("Cannot handle form method='%s', action='%s'\n"),
392 form->method, form->action);
396 vpninfo->redirect_url = strdup(form->action);
398 ret = parse_form(vpninfo, form, xml_node, request_body, req_len);
401 } else if (!vpninfo->csd_scriptname && !strcmp((char *)xml_node->name, "csd")) {
402 if (!vpninfo->csd_token)
403 vpninfo->csd_token = (char *)xmlGetProp(xml_node,
404 (unsigned char *)"token");
405 if (!vpninfo->csd_ticket)
406 vpninfo->csd_ticket = (char *)xmlGetProp(xml_node,
407 (unsigned char *)"ticket");
408 } else if (!vpninfo->csd_scriptname && !strcmp((char *)xml_node->name, vpninfo->csd_xmltag)) {
409 vpninfo->csd_stuburl = (char *)xmlGetProp(xml_node,
410 (unsigned char *)"stuburl");
411 vpninfo->csd_starturl = (char *)xmlGetProp(xml_node,
412 (unsigned char *)"starturl");
413 vpninfo->csd_waiturl = (char *)xmlGetProp(xml_node,
414 (unsigned char *)"waiturl");
415 vpninfo->csd_preurl = strdup(vpninfo->urlpath);
418 if (vpninfo->csd_token && vpninfo->csd_ticket && vpninfo->csd_starturl && vpninfo->csd_waiturl) {
419 /* First, redirect to the stuburl -- we'll need to fetch and run that */
420 vpninfo->redirect_url = strdup(vpninfo->csd_stuburl);
422 /* AB: remove all cookies */
423 for (opt = vpninfo->cookies; opt; opt = next) {
430 vpninfo->cookies = NULL;
437 vpn_progress(vpninfo, PRG_INFO, "%s\n", form->message);
439 vpn_progress(vpninfo, PRG_ERR, "%s\n", form->error);
444 if (vpninfo->process_auth_form)
445 ret = vpninfo->process_auth_form(vpninfo->cbdata, form);
447 vpn_progress(vpninfo, PRG_ERR, _("No form handler; cannot authenticate.\n"));
453 /* tokencode generation is deferred until after username prompts and CSD */
454 ret = do_gen_tokencode(vpninfo, form);
458 ret = append_form_opts(vpninfo, form, request_body, req_len);
461 *request_body_type = "application/x-www-form-urlencoded";
466 struct oc_form_opt *tmp = form->opts->next;
467 if (form->opts->type == OC_FORM_OPT_TEXT ||
468 form->opts->type == OC_FORM_OPT_PASSWORD ||
469 form->opts->type == OC_FORM_OPT_HIDDEN)
470 free(form->opts->value);
471 else if (form->opts->type == OC_FORM_OPT_SELECT) {
472 struct oc_form_opt_select *sel = (void *)form->opts;
475 for (i=0; i < sel->nr_choices; i++) {
476 free(sel->choices[i].name);
477 free(sel->choices[i].label);
478 free(sel->choices[i].auth_type);
479 free(sel->choices[i].override_name);
480 free(sel->choices[i].override_label);
483 free(form->opts->label);
484 free(form->opts->name);
498 static void nuke_opt_values(struct oc_form_opt *opt)
500 for (; opt; opt = opt->next) {
507 * If the user clicks OK without entering any data, we will continue
508 * connecting but bypass soft token generation for the duration of
509 * this "obtain_cookie" session.
511 * If the user clicks Cancel, we will abort the connection.
515 * = 0, on success (or if the user bypassed soft token init)
516 * = 1, if the user cancelled the form submission
518 int prepare_stoken(struct openconnect_info *vpninfo)
521 struct oc_auth_form form;
522 struct oc_form_opt opts[3], *opt = opts;
523 char **devid = NULL, **pass = NULL, **pin = NULL;
526 memset(&form, 0, sizeof(form));
527 memset(&opts, 0, sizeof(opts));
530 form.message = _("Enter credentials to unlock software token.");
532 vpninfo->stoken_tries = 0;
533 vpninfo->stoken_bypassed = 0;
535 if (stoken_devid_required(vpninfo->stoken_ctx)) {
536 opt->type = OC_FORM_OPT_TEXT;
537 opt->name = (char *)"devid";
538 opt->label = _("Device ID:");
542 if (stoken_pass_required(vpninfo->stoken_ctx)) {
543 opt->type = OC_FORM_OPT_PASSWORD;
544 opt->name = (char *)"password";
545 opt->label = _("Password:");
549 if (stoken_pin_required(vpninfo->stoken_ctx)) {
550 opt->type = OC_FORM_OPT_PASSWORD;
551 opt->name = (char *)"password";
552 opt->label = _("PIN:");
557 opts[0].next = opts[1].type ? &opts[1] : NULL;
558 opts[1].next = opts[2].type ? &opts[2] : NULL;
561 nuke_opt_values(opts);
564 /* don't bug the user if there's nothing to enter */
566 } else if (vpninfo->process_auth_form) {
567 int some_empty = 0, all_empty = 1;
569 /* < 0 for error; 1 if cancelled */
570 ret = vpninfo->process_auth_form(vpninfo->cbdata, &form);
574 for (opt = opts; opt; opt = opt->next) {
575 if (!opt->value || !strlen(opt->value))
581 vpn_progress(vpninfo, PRG_INFO,
582 _("User bypassed soft token.\n"));
583 vpninfo->stoken_bypassed = 1;
588 vpn_progress(vpninfo, PRG_INFO,
589 _("All fields are required; try again.\n"));
593 vpn_progress(vpninfo, PRG_ERR,
594 _("No form handler; cannot authenticate.\n"));
599 ret = stoken_decrypt_seed(vpninfo->stoken_ctx,
601 devid ? *devid : NULL);
602 if (ret == -EIO || (ret && !devid && !pass)) {
603 vpn_progress(vpninfo, PRG_ERR,
604 _("General failure in libstoken.\n"));
606 } else if (ret != 0) {
607 vpn_progress(vpninfo, PRG_INFO,
608 _("Incorrect device ID or password; try again.\n"));
613 if (stoken_check_pin(vpninfo->stoken_ctx, *pin) != 0) {
614 vpn_progress(vpninfo, PRG_INFO,
615 _("Invalid PIN format; try again.\n"));
618 free(vpninfo->stoken_pin);
619 vpninfo->stoken_pin = strdup(*pin);
620 if (!vpninfo->stoken_pin) {
625 vpn_progress(vpninfo, PRG_DEBUG, _("Soft token init was successful.\n"));
630 nuke_opt_values(opts);
638 * < 0, if unable to generate a tokencode
641 static int can_gen_tokencode(struct openconnect_info *vpninfo, struct oc_form_opt *opt)
644 if (strcmp(opt->name, "password") || vpninfo->stoken_bypassed)
646 if (vpninfo->stoken_tries == 0) {
647 vpn_progress(vpninfo, PRG_DEBUG,
648 _("OK to generate INITIAL tokencode\n"));
649 vpninfo->stoken_time = 0;
650 } else if (vpninfo->stoken_tries == 1 && strcasestr(opt->label, "next")) {
651 vpn_progress(vpninfo, PRG_DEBUG,
652 _("OK to generate NEXT tokencode\n"));
653 vpninfo->stoken_time += 60;
655 /* limit the number of retries, to avoid account lockouts */
656 vpn_progress(vpninfo, PRG_INFO,
657 _("Server is rejecting the soft token; switching to manual entry\n"));
661 vpninfo->stoken_tries++;
669 * < 0, if unable to generate a tokencode
672 static int do_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form)
675 char tokencode[STOKEN_MAX_TOKENCODE + 1];
676 struct oc_form_opt *opt;
678 for (opt = form->opts; ; opt = opt->next) {
679 /* this form might not have anything for us to do */
682 if (opt->type == OC_FORM_OPT_STOKEN)
686 if (!vpninfo->stoken_time)
687 vpninfo->stoken_time = time(NULL);
688 vpn_progress(vpninfo, PRG_INFO, _("Generating tokencode\n"));
690 /* This doesn't normally fail */
691 if (stoken_compute_tokencode(vpninfo->stoken_ctx, vpninfo->stoken_time,
692 vpninfo->stoken_pin, tokencode) < 0) {
693 vpn_progress(vpninfo, PRG_ERR, _("General failure in libstoken.\n"));
697 opt->value = strdup(tokencode);
698 return opt->value ? 0 : -ENOMEM;