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 xmlpost_append_form_opts(struct openconnect_info *vpninfo,
45 struct oc_auth_form *form, char *body, int bodylen);
46 static int can_gen_tokencode(struct openconnect_info *vpninfo, struct oc_form_opt *opt);
47 static int do_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form);
49 static int append_opt(char *body, int bodylen, char *opt, char *name)
51 int len = strlen(body);
54 if (len >= bodylen - 1)
60 if (isalnum((int)(unsigned char)*opt)) {
61 if (len >= bodylen - 1)
65 if (len >= bodylen - 3)
67 sprintf(body+len, "%%%02x", *opt);
73 if (len >= bodylen - 1)
77 while (name && *name) {
78 if (isalnum((int)(unsigned char)*name)) {
79 if (len >= bodylen - 1)
83 if (len >= bodylen - 3)
85 sprintf(body+len, "%%%02X", *name);
95 static int append_form_opts(struct openconnect_info *vpninfo,
96 struct oc_auth_form *form, char *body, int bodylen)
98 struct oc_form_opt *opt;
101 for (opt = form->opts; opt; opt = opt->next) {
102 ret = append_opt(body, bodylen, opt->name, opt->value);
110 * Maybe we should offer this choice to the user. So far we've only
111 * ever seen it offer bogus choices though -- between certificate and
112 * password authentication, when the former has already failed.
113 * So we just accept the first option with an auth-type property.
116 static int parse_auth_choice(struct openconnect_info *vpninfo, struct oc_auth_form *form,
119 struct oc_form_opt_select *opt;
121 opt = calloc(1, sizeof(*opt));
125 opt->form.type = OC_FORM_OPT_SELECT;
126 opt->form.name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
127 opt->form.label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
129 if (!opt->form.name) {
130 vpn_progress(vpninfo, PRG_ERR, _("Form choice has no name\n"));
135 for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
137 struct oc_choice *choice;
139 if (xml_node->type != XML_ELEMENT_NODE)
142 if (strcmp((char *)xml_node->name, "option"))
145 form_id = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
150 opt = realloc(opt, sizeof(*opt) +
151 opt->nr_choices * sizeof(*choice));
155 choice = &opt->choices[opt->nr_choices-1];
157 choice->name = form_id;
158 choice->label = (char *)xmlNodeGetContent(xml_node);
159 choice->auth_type = (char *)xmlGetProp(xml_node, (unsigned char *)"auth-type");
160 choice->override_name = (char *)xmlGetProp(xml_node, (unsigned char *)"override-name");
161 choice->override_label = (char *)xmlGetProp(xml_node, (unsigned char *)"override-label");
164 /* We link the choice _first_ so it's at the top of what we present
166 opt->form.next = form->opts;
167 form->opts = &opt->form;
173 * = 0, when form was cancelled
174 * = 1, when form was parsed
176 static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
179 char *input_type, *input_name, *input_label;
181 for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
182 struct oc_form_opt *opt, **p;
184 if (xml_node->type != XML_ELEMENT_NODE)
187 if (!strcmp((char *)xml_node->name, "select")) {
188 if (parse_auth_choice(vpninfo, form, xml_node))
192 if (strcmp((char *)xml_node->name, "input")) {
193 vpn_progress(vpninfo, PRG_TRACE,
194 _("name %s not input\n"), xml_node->name);
198 input_type = (char *)xmlGetProp(xml_node, (unsigned char *)"type");
200 vpn_progress(vpninfo, PRG_INFO,
201 _("No input type in form\n"));
205 if (!strcmp(input_type, "submit") || !strcmp(input_type, "reset")) {
210 input_name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
212 vpn_progress(vpninfo, PRG_INFO,
213 _("No input name in form\n"));
217 input_label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
219 opt = calloc(1, sizeof(*opt));
227 opt->name = input_name;
228 opt->label = input_label;
230 if (!strcmp(input_type, "hidden")) {
231 opt->type = OC_FORM_OPT_HIDDEN;
232 opt->value = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
233 } else if (!strcmp(input_type, "text"))
234 opt->type = OC_FORM_OPT_TEXT;
235 else if (!strcmp(input_type, "password")) {
236 if (vpninfo->use_stoken && !can_gen_tokencode(vpninfo, opt))
237 opt->type = OC_FORM_OPT_STOKEN;
239 opt->type = OC_FORM_OPT_PASSWORD;
241 vpn_progress(vpninfo, PRG_INFO,
242 _("Unknown input type %s in form\n"),
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)
320 static int xmlnode_is_named(xmlNode *xml_node, const char *name)
322 return !strcmp((char *)xml_node->name, name);
325 static int xmlnode_get_prop(xmlNode *xml_node, const char *name, char **var)
327 char *str = (char *)xmlGetProp(xml_node, (unsigned char *)name);
337 static int xmlnode_get_text(xmlNode *xml_node, const char *name, char **var)
341 if (name && !xmlnode_is_named(xml_node, name))
344 str = xmlnode_msg(xml_node);
354 * Legacy server response looks like:
356 * <auth id="<!-- "main" for initial attempt, "success" means we have a cookie -->">
357 * <title><!-- title to display to user --></title>
358 * <csd token="<!-- save to vpninfo->csd_token -->"
359 * ticket="<!-- save to vpninfo->csd_ticket -->" />
360 * <csd stuburl="<!-- ignore -->"
361 * starturl="<!-- ignore -->"
362 * waiturl="<!-- ignore -->"
364 * stuburl="<!-- save to vpninfo->csd_stuburl on Mac only -->"
365 * starturl="<!-- save to vpninfo->csd_starturl on Mac only -->"
366 * waiturl="<!-- save to vpninfo->csd_waiturl on Mac only -->" />
368 * stuburl="<!-- same as above, for Linux -->"
369 * starturl="<!-- same as above, for Linux -->"
370 * waiturl="<!-- same as above, for Linux -->" />
371 * <banner><!-- display this to the user --></banner>
372 * <message>Please enter your username and password.</message>
373 * <form method="post" action="/+webvpn+/index.html">
374 * <input type="text" name="username" label="Username:" />
375 * <input type="password" name="password" label="Password:" />
376 * <input type="hidden" name="<!-- save these -->" value="<!-- ... -->" />
377 * <input type="submit" name="Login" value="Login" />
378 * <input type="reset" name="Clear" value="Clear" />
382 * New server response looks like:
385 * <version><!-- whatever --></version>
386 * <session-token><!-- if present, save to vpninfo->cookie --></session-token>
388 * <!-- this could contain anything; copy to vpninfo->opaque_srvdata -->
389 * <tunnel-group>foobar</tunnel-group>
390 * <config-hash>1234567</config-hash>
392 * <auth id="<!-- see above -->
393 * <!-- all of our old familiar fields -->
396 * <host-scan-ticket><!-- save to vpninfo->csd_ticket --></host-scan-ticket>
397 * <host-scan-token><!-- save to vpninfo->csd_token --></host-scan-token>
398 * <host-scan-base-uri><!-- save to vpninfo->csd_starturl --></host-scan-base-uri>
399 * <host-scan-wait-uri><!-- save to vpninfo->csd_waiturl --></host-scan-wait-uri>
405 * 1) The new host-scan-*-uri nodes do not map directly to the old CSD fields.
407 * 2) The new <form> tag tends to omit the method/action properties.
410 static int parse_auth_node(struct openconnect_info *vpninfo, xmlNode *xml_node,
411 struct oc_auth_form *form)
415 for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
416 if (xml_node->type != XML_ELEMENT_NODE)
419 xmlnode_get_text(xml_node, "banner", &form->banner);
420 xmlnode_get_text(xml_node, "message", &form->message);
421 xmlnode_get_text(xml_node, "error", &form->error);
423 if (xmlnode_is_named(xml_node, "form")) {
425 /* defaults for new XML POST */
426 form->method = strdup("POST");
427 form->action = strdup("/");
429 xmlnode_get_prop(xml_node, "method", &form->method);
430 xmlnode_get_prop(xml_node, "action", &form->action);
432 if (!form->method || !form->action ||
433 strcasecmp(form->method, "POST") || !form->action[0]) {
434 vpn_progress(vpninfo, PRG_ERR,
435 _("Cannot handle form method='%s', action='%s'\n"),
436 form->method, form->action);
441 ret = parse_form(vpninfo, form, xml_node);
444 } else if (!vpninfo->csd_scriptname && xmlnode_is_named(xml_node, "csd")) {
445 xmlnode_get_prop(xml_node, "token", &vpninfo->csd_token);
446 xmlnode_get_prop(xml_node, "ticket", &vpninfo->csd_ticket);
447 } else if (!vpninfo->csd_scriptname && xmlnode_is_named(xml_node, vpninfo->csd_xmltag)) {
448 xmlnode_get_prop(xml_node, "stuburl", &vpninfo->csd_stuburl);
449 xmlnode_get_prop(xml_node, "starturl", &vpninfo->csd_starturl);
450 xmlnode_get_prop(xml_node, "waiturl", &vpninfo->csd_waiturl);
451 vpninfo->csd_preurl = strdup(vpninfo->urlpath);
459 static int parse_host_scan_node(struct openconnect_info *vpninfo, xmlNode *xml_node)
461 /* ignore this whole section if the CSD trojan has already run */
462 if (vpninfo->csd_scriptname)
465 for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
466 if (xml_node->type != XML_ELEMENT_NODE)
469 xmlnode_get_text(xml_node, "host-scan-ticket", &vpninfo->csd_ticket);
470 xmlnode_get_text(xml_node, "host-scan-token", &vpninfo->csd_token);
471 xmlnode_get_text(xml_node, "host-scan-base-uri", &vpninfo->csd_starturl);
472 xmlnode_get_text(xml_node, "host-scan-wait-uri", &vpninfo->csd_waiturl);
479 * = 0, on success; *form is populated
481 int parse_xml_response(struct openconnect_info *vpninfo, char *response, struct oc_auth_form **formp)
483 struct oc_auth_form *form;
489 free_auth_form(*formp);
493 form = calloc(1, sizeof(*form));
497 xml_doc = xmlReadMemory(response, strlen(response), "noname.xml", NULL, 0);
499 vpn_progress(vpninfo, PRG_ERR,
500 _("Failed to parse server response\n"));
501 vpn_progress(vpninfo, PRG_TRACE,
502 _("Response was:%s\n"), response);
507 xml_node = xmlDocGetRootElement(xml_doc);
511 if (xml_node->type != XML_ELEMENT_NODE) {
512 xml_node = xml_node->next;
515 if (xmlnode_is_named(xml_node, "config-auth")) {
516 /* if we do have a config-auth node, it is the root element */
517 xml_node = xml_node->children;
519 } else if (xmlnode_is_named(xml_node, "auth")) {
520 xmlnode_get_prop(xml_node, "id", &form->auth_id);
521 ret = parse_auth_node(vpninfo, xml_node, form);
522 } else if (xmlnode_is_named(xml_node, "opaque")) {
523 if (vpninfo->opaque_srvdata)
524 xmlFreeNode(vpninfo->opaque_srvdata);
525 vpninfo->opaque_srvdata = xmlCopyNode(xml_node, 1);
526 if (!vpninfo->opaque_srvdata)
528 } else if (xmlnode_is_named(xml_node, "host-scan")) {
529 ret = parse_host_scan_node(vpninfo, xml_node);
531 xmlnode_get_text(xml_node, "session-token", &vpninfo->cookie);
532 xmlnode_get_text(xml_node, "error", &form->error);
537 xml_node = xml_node->next;
540 if (!form->auth_id) {
541 vpn_progress(vpninfo, PRG_ERR,
542 _("XML response has no \"auth\" node\n"));
552 free_auth_form(form);
558 * = 0, when form parsed and POST required
559 * = 1, when response was cancelled by user
560 * = 2, when form indicates that login was already successful
562 int handle_auth_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
563 char *request_body, int req_len, const char **method,
564 const char **request_body_type, int xmlpost)
567 struct vpn_option *opt, *next;
569 if (!strcmp(form->auth_id, "success"))
572 if (vpninfo->nopasswd) {
573 vpn_progress(vpninfo, PRG_ERR,
574 _("Asked for password but '--no-passwd' set\n"));
578 if (vpninfo->csd_token && vpninfo->csd_ticket && vpninfo->csd_starturl && vpninfo->csd_waiturl) {
579 /* AB: remove all cookies */
580 for (opt = vpninfo->cookies; opt; opt = next) {
587 vpninfo->cookies = NULL;
592 vpn_progress(vpninfo, PRG_INFO, "%s\n", form->message);
594 vpn_progress(vpninfo, PRG_ERR, "%s\n", form->error);
598 if (vpninfo->process_auth_form)
599 ret = vpninfo->process_auth_form(vpninfo->cbdata, form);
601 vpn_progress(vpninfo, PRG_ERR, _("No form handler; cannot authenticate.\n"));
607 /* tokencode generation is deferred until after username prompts and CSD */
608 ret = do_gen_tokencode(vpninfo, form);
613 xmlpost_append_form_opts(vpninfo, form, request_body, req_len) :
614 append_form_opts(vpninfo, form, request_body, req_len);
617 *request_body_type = "application/x-www-form-urlencoded";
622 void free_auth_form(struct oc_auth_form *form)
627 struct oc_form_opt *tmp = form->opts->next;
628 if (form->opts->type == OC_FORM_OPT_TEXT ||
629 form->opts->type == OC_FORM_OPT_PASSWORD ||
630 form->opts->type == OC_FORM_OPT_HIDDEN ||
631 form->opts->type == OC_FORM_OPT_STOKEN)
632 free(form->opts->value);
633 else if (form->opts->type == OC_FORM_OPT_SELECT) {
634 struct oc_form_opt_select *sel = (void *)form->opts;
637 for (i=0; i < sel->nr_choices; i++) {
638 free(sel->choices[i].name);
639 free(sel->choices[i].label);
640 free(sel->choices[i].auth_type);
641 free(sel->choices[i].override_name);
642 free(sel->choices[i].override_label);
645 free(form->opts->label);
646 free(form->opts->name);
660 * Old submission format is just an HTTP query string:
662 * password=12345678&username=joe
664 * New XML format is more complicated:
666 * <config-auth client="vpn" type="<!-- init or auth-reply -->">
667 * <version who="vpn"><!-- currently just the OpenConnect version --></version>
668 * <device-id><!-- linux, linux-64, mac, win --></device-id>
669 * <opaque is-for="<!-- some name -->">
670 * <!-- just copy this verbatim from whatever the gateway sent us -->
673 * For init only, add:
674 * <group-access>https://<!-- insert hostname here --></group-access>
676 * For auth-reply only, add:
678 * <username><!-- same treatment as the old form options --></username>
679 * <password><!-- ditto -->
681 * <host-scan-token><!-- vpninfo->csd_ticket --></host-scan-token>
684 #define XCAST(x) ((const xmlChar *)(x))
686 static xmlDocPtr xmlpost_new_query(struct openconnect_info *vpninfo, const char *type,
690 xmlNodePtr root, node;
692 doc = xmlNewDoc(XCAST("1.0"));
696 *rootp = root = xmlNewNode(NULL, XCAST("config-auth"));
699 if (!xmlNewProp(root, XCAST("client"), XCAST("vpn")))
701 if (!xmlNewProp(root, XCAST("type"), XCAST(type)))
703 xmlDocSetRootElement(doc, root);
705 node = xmlNewTextChild(root, NULL, XCAST("version"), XCAST(openconnect_version_str));
708 if (!xmlNewProp(node, XCAST("who"), XCAST("vpn")))
711 if (!xmlNewTextChild(root, NULL, XCAST("device-id"), XCAST(vpninfo->platname)))
721 static int xmlpost_complete(xmlDocPtr doc, char *body, int bodylen)
731 xmlDocDumpMemoryEnc(doc, &mem, &len, "UTF-8");
740 memcpy(body, mem, len);
750 int xmlpost_initial_req(struct openconnect_info *vpninfo, char *request_body, int req_len)
752 xmlNodePtr root, node;
753 xmlDocPtr doc = xmlpost_new_query(vpninfo, "init", &root);
759 if (asprintf(&url, "https://%s", vpninfo->hostname) == -1)
761 node = xmlNewTextChild(root, NULL, XCAST("group-access"), XCAST(url));
766 return xmlpost_complete(doc, request_body, req_len);
769 xmlpost_complete(doc, NULL, 0);
773 static int xmlpost_append_form_opts(struct openconnect_info *vpninfo,
774 struct oc_auth_form *form, char *body, int bodylen)
776 xmlNodePtr root, node;
777 xmlDocPtr doc = xmlpost_new_query(vpninfo, "auth-reply", &root);
778 struct oc_form_opt *opt;
783 if (vpninfo->opaque_srvdata) {
784 node = xmlCopyNode(vpninfo->opaque_srvdata, 1);
787 if (!xmlAddChild(root, node))
791 node = xmlNewChild(root, NULL, XCAST("auth"), NULL);
795 for (opt = form->opts; opt; opt = opt->next) {
796 if (!xmlNewTextChild(node, NULL, XCAST(opt->name), XCAST(opt->value)))
800 if (vpninfo->csd_token &&
801 !xmlNewTextChild(root, NULL, XCAST("host-scan-token"), XCAST(vpninfo->csd_token)))
804 return xmlpost_complete(doc, body, bodylen);
807 xmlpost_complete(doc, NULL, 0);
813 static void nuke_opt_values(struct oc_form_opt *opt)
815 for (; opt; opt = opt->next) {
823 * If the user clicks OK without entering any data, we will continue
824 * connecting but bypass soft token generation for the duration of
825 * this "obtain_cookie" session.
827 * If the user clicks Cancel, we will abort the connection.
831 * = 0, on success (or if the user bypassed soft token init)
832 * = 1, if the user cancelled the form submission
834 int prepare_stoken(struct openconnect_info *vpninfo)
837 struct oc_auth_form form;
838 struct oc_form_opt opts[3], *opt = opts;
839 char **devid = NULL, **pass = NULL, **pin = NULL;
842 memset(&form, 0, sizeof(form));
843 memset(&opts, 0, sizeof(opts));
846 form.message = _("Enter credentials to unlock software token.");
848 vpninfo->stoken_tries = 0;
849 vpninfo->stoken_bypassed = 0;
851 if (stoken_devid_required(vpninfo->stoken_ctx)) {
852 opt->type = OC_FORM_OPT_TEXT;
853 opt->name = (char *)"devid";
854 opt->label = _("Device ID:");
858 if (stoken_pass_required(vpninfo->stoken_ctx)) {
859 opt->type = OC_FORM_OPT_PASSWORD;
860 opt->name = (char *)"password";
861 opt->label = _("Password:");
865 if (stoken_pin_required(vpninfo->stoken_ctx)) {
866 opt->type = OC_FORM_OPT_PASSWORD;
867 opt->name = (char *)"password";
868 opt->label = _("PIN:");
873 opts[0].next = opts[1].type ? &opts[1] : NULL;
874 opts[1].next = opts[2].type ? &opts[2] : NULL;
877 nuke_opt_values(opts);
880 /* don't bug the user if there's nothing to enter */
882 } else if (vpninfo->process_auth_form) {
883 int some_empty = 0, all_empty = 1;
885 /* < 0 for error; 1 if cancelled */
886 ret = vpninfo->process_auth_form(vpninfo->cbdata, &form);
890 for (opt = opts; opt; opt = opt->next) {
891 if (!opt->value || !strlen(opt->value))
897 vpn_progress(vpninfo, PRG_INFO,
898 _("User bypassed soft token.\n"));
899 vpninfo->stoken_bypassed = 1;
904 vpn_progress(vpninfo, PRG_INFO,
905 _("All fields are required; try again.\n"));
909 vpn_progress(vpninfo, PRG_ERR,
910 _("No form handler; cannot authenticate.\n"));
915 ret = stoken_decrypt_seed(vpninfo->stoken_ctx,
917 devid ? *devid : NULL);
918 if (ret == -EIO || (ret && !devid && !pass)) {
919 vpn_progress(vpninfo, PRG_ERR,
920 _("General failure in libstoken.\n"));
922 } else if (ret != 0) {
923 vpn_progress(vpninfo, PRG_INFO,
924 _("Incorrect device ID or password; try again.\n"));
929 if (stoken_check_pin(vpninfo->stoken_ctx, *pin) != 0) {
930 vpn_progress(vpninfo, PRG_INFO,
931 _("Invalid PIN format; try again.\n"));
934 free(vpninfo->stoken_pin);
935 vpninfo->stoken_pin = strdup(*pin);
936 if (!vpninfo->stoken_pin) {
941 vpn_progress(vpninfo, PRG_DEBUG, _("Soft token init was successful.\n"));
946 nuke_opt_values(opts);
954 * < 0, if unable to generate a tokencode
957 static int can_gen_tokencode(struct openconnect_info *vpninfo, struct oc_form_opt *opt)
960 if (strcmp(opt->name, "password") || vpninfo->stoken_bypassed)
962 if (vpninfo->stoken_tries == 0) {
963 vpn_progress(vpninfo, PRG_DEBUG,
964 _("OK to generate INITIAL tokencode\n"));
965 vpninfo->stoken_time = 0;
966 } else if (vpninfo->stoken_tries == 1 && strcasestr(opt->label, "next")) {
967 vpn_progress(vpninfo, PRG_DEBUG,
968 _("OK to generate NEXT tokencode\n"));
969 vpninfo->stoken_time += 60;
971 /* limit the number of retries, to avoid account lockouts */
972 vpn_progress(vpninfo, PRG_INFO,
973 _("Server is rejecting the soft token; switching to manual entry\n"));
983 * < 0, if unable to generate a tokencode
986 static int do_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form)
989 char tokencode[STOKEN_MAX_TOKENCODE + 1];
990 struct oc_form_opt *opt;
992 for (opt = form->opts; ; opt = opt->next) {
993 /* this form might not have anything for us to do */
996 if (opt->type == OC_FORM_OPT_STOKEN)
1000 if (!vpninfo->stoken_time)
1001 vpninfo->stoken_time = time(NULL);
1002 vpn_progress(vpninfo, PRG_INFO, _("Generating tokencode\n"));
1004 /* This doesn't normally fail */
1005 if (stoken_compute_tokencode(vpninfo->stoken_ctx, vpninfo->stoken_time,
1006 vpninfo->stoken_pin, tokencode) < 0) {
1007 vpn_progress(vpninfo, PRG_ERR, _("General failure in libstoken.\n"));
1011 vpninfo->stoken_tries++;
1012 opt->value = strdup(tokencode);
1013 return opt->value ? 0 : -ENOMEM;