stoken: Fill in "password" fields with a generated tokencode
[platform/upstream/openconnect.git] / auth.c
1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2011 Intel Corporation.
5  * Copyright © 2008 Nick Andrew <nick@nick-andrew.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  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to:
20  *
21  *   Free Software Foundation, Inc.
22  *   51 Franklin Street, Fifth Floor,
23  *   Boston, MA 02110-1301 USA
24  */
25
26 #include <stdio.h>
27 #include <netdb.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <time.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <errno.h>
34
35 #ifdef LIBSTOKEN_HDR
36 #include LIBSTOKEN_HDR
37 #endif
38
39 #include <libxml/parser.h>
40 #include <libxml/tree.h>
41
42 #include "openconnect-internal.h"
43
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);
46
47 static int append_opt(char *body, int bodylen, char *opt, char *name)
48 {
49         int len = strlen(body);
50
51         if (len) {
52                 if (len >= bodylen - 1)
53                         return -ENOSPC;
54                 body[len++] = '&';
55         }
56
57         while (*opt) {
58                 if (isalnum((int)(unsigned char)*opt)) {
59                         if (len >= bodylen - 1)
60                                 return -ENOSPC;
61                         body[len++] = *opt;
62                 } else {
63                         if (len >= bodylen - 3)
64                                 return -ENOSPC;
65                         sprintf(body+len, "%%%02x", *opt);
66                         len += 3;
67                 }
68                 opt++;
69         }
70
71         if (len >= bodylen - 1)
72                 return -ENOSPC;
73         body[len++] = '=';
74
75         while (name && *name) {
76                 if (isalnum((int)(unsigned char)*name)) {
77                         if (len >= bodylen - 1)
78                                 return -ENOSPC;
79                         body[len++] = *name;
80                 } else {
81                         if (len >= bodylen - 3)
82                                 return -ENOSPC;
83                         sprintf(body+len, "%%%02X", *name);
84                         len += 3;
85                 }
86                 name++;
87         }
88         body[len] = 0;
89
90         return 0;
91 }
92
93 static int append_form_opts(struct openconnect_info *vpninfo,
94                             struct oc_auth_form *form, char *body, int bodylen)
95 {
96         struct oc_form_opt *opt;
97         int ret;
98
99         for (opt = form->opts; opt; opt = opt->next) {
100                 ret = append_opt(body, bodylen, opt->name, opt->value);
101                 if (ret)
102                         return ret;
103         }
104         return 0;
105 }
106
107 /*
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.
112  */
113
114 static int parse_auth_choice(struct openconnect_info *vpninfo, struct oc_auth_form *form,
115                              xmlNode *xml_node)
116 {
117         struct oc_form_opt_select *opt;
118
119         opt = calloc(1, sizeof(*opt));
120         if (!opt)
121                 return -ENOMEM;
122
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");
126
127         if (!opt->form.name) {
128                 vpn_progress(vpninfo, PRG_ERR, _("Form choice has no name\n"));
129                 free(opt);
130                 return -EINVAL;
131         }
132
133         for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
134                 char *form_id;
135                 struct oc_choice *choice;
136
137                 if (xml_node->type != XML_ELEMENT_NODE)
138                         continue;
139
140                 if (strcmp((char *)xml_node->name, "option"))
141                         continue;
142
143                 form_id = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
144                 if (!form_id)
145                         continue;
146
147                 opt->nr_choices++;
148                 opt = realloc(opt, sizeof(*opt) +
149                                    opt->nr_choices * sizeof(*choice));
150                 if (!opt)
151                         return -ENOMEM;
152
153                 choice = &opt->choices[opt->nr_choices-1];
154
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");
160         }
161
162         /* We link the choice _first_ so it's at the top of what we present
163            to the user */
164         opt->form.next = form->opts;
165         form->opts = &opt->form;
166         return 0;
167 }
168
169 /* Return value:
170  *  < 0, on error
171  *  = 0, when form was cancelled
172  *  = 1, when form was parsed
173  */
174 static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
175                       xmlNode *xml_node, char *body, int bodylen)
176 {
177         char *input_type, *input_name, *input_label;
178
179         for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
180                 struct oc_form_opt *opt, **p;
181
182                 if (xml_node->type != XML_ELEMENT_NODE)
183                         continue;
184
185                 if (!strcmp((char *)xml_node->name, "select")) {
186                         if (parse_auth_choice(vpninfo, form, xml_node))
187                                 return -EINVAL;
188                         continue;
189                 }
190                 if (strcmp((char *)xml_node->name, "input")) {
191                         vpn_progress(vpninfo, PRG_TRACE,
192                                      _("name %s not input\n"), xml_node->name);
193                         continue;
194                 }
195
196                 input_type = (char *)xmlGetProp(xml_node, (unsigned char *)"type");
197                 if (!input_type) {
198                         vpn_progress(vpninfo, PRG_INFO,
199                                      _("No input type in form\n"));
200                         continue;
201                 }
202
203                 if (!strcmp(input_type, "submit") || !strcmp(input_type, "reset")) {
204                         free(input_type);
205                         continue;
206                 }
207
208                 input_name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
209                 if (!input_name) {
210                         vpn_progress(vpninfo, PRG_INFO,
211                                      _("No input name in form\n"));
212                         free(input_type);
213                         continue;
214                 }
215                 input_label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
216
217                 opt = calloc(1, sizeof(*opt));
218                 if (!opt) {
219                         free(input_type);
220                         free(input_name);
221                         free(input_label);
222                         return -ENOMEM;
223                 }
224
225                 opt->name = input_name;
226                 opt->label = input_label;
227
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;
236                         else
237                                 opt->type = OC_FORM_OPT_PASSWORD;
238                 } else {
239                         vpn_progress(vpninfo, PRG_INFO,
240                                      _("Unknown input type %s in form\n"),
241                                      input_type);
242                         free(input_type);
243                         free(input_name);
244                         free(input_label);
245                         free(opt);
246                         continue;
247                 }
248
249                 free(input_type);
250
251                 p = &form->opts;
252                 while (*p)
253                         p = &(*p)->next;
254
255                 *p = opt;
256         }
257
258         vpn_progress(vpninfo, PRG_TRACE, _("Fixed options give %s\n"), body);
259
260         return 0;
261 }
262
263 static char *xmlnode_msg(xmlNode *xml_node)
264 {
265         char *fmt = (char *)xmlNodeGetContent(xml_node);
266         char *result, *params[2], *pct;
267         int len;
268         int nr_params = 0;
269
270         if (!fmt || !fmt[0]) {
271                 free(fmt);
272                 return NULL;
273         }
274
275         len = strlen(fmt) + 1;
276         
277         params[0] = (char *)xmlGetProp(xml_node, (unsigned char *)"param1");
278         if (params[0])
279                 len += strlen(params[0]);
280         params[1] = (char *)xmlGetProp(xml_node, (unsigned char *)"param2");
281         if (params[1])
282                 len += strlen(params[1]);
283
284         result = malloc(len);
285         if (!result) {
286                 result = fmt;
287                 goto out;
288         }
289
290         strcpy(result, fmt);
291         free (fmt);
292
293         for (pct = strchr(result, '%'); pct;
294              (pct = strchr(pct, '%'))) {
295                 int paramlen;
296
297                 /* We only cope with '%s' */
298                 if (pct[1] != 's')
299                         goto out;
300
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);
307                         pct += paramlen;
308                 } else
309                         pct++;
310
311                 if (++nr_params == 2)
312                         break;
313         }
314  out:
315         free(params[0]);
316         free(params[1]);
317         return result;
318 }
319
320 /* Return value:
321  *  < 0, on error
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
325  */
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)
329 {
330         struct oc_auth_form *form;
331         xmlDocPtr xml_doc;
332         xmlNode *xml_node;
333         int ret;
334         struct vpn_option *opt, *next;
335
336         form = calloc(1, sizeof(*form));
337         if (!form)
338                 return -ENOMEM;
339
340         xml_doc = xmlReadMemory(response, strlen(response), "noname.xml", NULL, 0);
341         if (!xml_doc) {
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);
346                 free(form);
347                 return -EINVAL;
348         }
349
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"));
354                 ret = -EINVAL;
355                 goto out;
356         }
357
358         form->auth_id = (char *)xmlGetProp(xml_node, (unsigned char *)"id");
359         if (!strcmp(form->auth_id, "success")) {
360                 ret = 2;
361                 goto out;
362         }
363
364         if (vpninfo->nopasswd) {
365                 vpn_progress(vpninfo, PRG_ERR,
366                              _("Asked for password but '--no-passwd' set\n"));
367                 ret = -EPERM;
368                 goto out;
369         }
370
371         for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
372                 if (xml_node->type != XML_ELEMENT_NODE)
373                         continue;
374
375                 if (!strcmp((char *)xml_node->name, "banner")) {
376                         free(form->banner);
377                         form->banner = xmlnode_msg(xml_node);
378                 } else if (!strcmp((char *)xml_node->name, "message")) {
379                         free(form->message);
380                         form->message = xmlnode_msg(xml_node);
381                 } else if (!strcmp((char *)xml_node->name, "error")) {
382                         free(form->error);
383                         form->error = xmlnode_msg(xml_node);
384                 } else if (!strcmp((char *)xml_node->name, "form")) {
385
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);
393                                 ret = -EINVAL;
394                                 goto out;
395                         }
396                         vpninfo->redirect_url = strdup(form->action);
397
398                         ret = parse_form(vpninfo, form, xml_node, request_body, req_len);
399                         if (ret < 0)
400                                 goto out;
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);
416                 }
417         }
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);
421
422                 /* AB: remove all cookies */
423                 for (opt = vpninfo->cookies; opt; opt = next) {
424                         next = opt->next;
425
426                         free(opt->option);
427                         free(opt->value);
428                         free(opt);
429                 }
430                 vpninfo->cookies = NULL;
431
432                 ret = 0;
433                 goto out;
434         }
435         if (!form->opts) {
436                 if (form->message)
437                         vpn_progress(vpninfo, PRG_INFO, "%s\n", form->message);
438                 if (form->error)
439                         vpn_progress(vpninfo, PRG_ERR, "%s\n", form->error);
440                 ret = -EPERM;
441                 goto out;
442         }
443
444         if (vpninfo->process_auth_form)
445                 ret = vpninfo->process_auth_form(vpninfo->cbdata, form);
446         else {
447                 vpn_progress(vpninfo, PRG_ERR, _("No form handler; cannot authenticate.\n"));
448                 ret = 1;
449         }
450         if (ret)
451                 goto out;
452
453         /* tokencode generation is deferred until after username prompts and CSD */
454         ret = do_gen_tokencode(vpninfo, form);
455         if (ret)
456                 goto out;
457
458         ret = append_form_opts(vpninfo, form, request_body, req_len);
459         if (!ret) {
460                 *method = "POST";
461                 *request_body_type = "application/x-www-form-urlencoded";
462         }
463  out:
464         xmlFreeDoc(xml_doc);
465         while (form->opts) {
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;
473                         int i;
474
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);
481                         }
482                 }
483                 free(form->opts->label);
484                 free(form->opts->name);
485                 free(form->opts);
486                 form->opts = tmp;
487         }
488         free(form->error);
489         free(form->message);
490         free(form->banner);
491         free(form->auth_id);
492         free(form->method);
493         free(form->action);
494         free(form);
495         return ret;
496 }
497
498 static void nuke_opt_values(struct oc_form_opt *opt)
499 {
500         for (; opt; opt = opt->next) {
501                 free(opt->value);
502                 opt->value = NULL;
503         }
504 }
505
506 /*
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.
510  *
511  * If the user clicks Cancel, we will abort the connection.
512  *
513  * Return value:
514  *  < 0, on error
515  *  = 0, on success (or if the user bypassed soft token init)
516  *  = 1, if the user cancelled the form submission
517  */
518 int prepare_stoken(struct openconnect_info *vpninfo)
519 {
520 #ifdef LIBSTOKEN_HDR
521         struct oc_auth_form form;
522         struct oc_form_opt opts[3], *opt = opts;
523         char **devid = NULL, **pass = NULL, **pin = NULL;
524         int ret = 0;
525
526         memset(&form, 0, sizeof(form));
527         memset(&opts, 0, sizeof(opts));
528
529         form.opts = opts;
530         form.message = _("Enter credentials to unlock software token.");
531
532         vpninfo->stoken_tries = 0;
533         vpninfo->stoken_bypassed = 0;
534
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:");
539                 devid = &opt->value;
540                 opt++;
541         }
542         if (stoken_pass_required(vpninfo->stoken_ctx)) {
543                 opt->type = OC_FORM_OPT_PASSWORD;
544                 opt->name = (char *)"password";
545                 opt->label = _("Password:");
546                 pass = &opt->value;
547                 opt++;
548         }
549         if (stoken_pin_required(vpninfo->stoken_ctx)) {
550                 opt->type = OC_FORM_OPT_PASSWORD;
551                 opt->name = (char *)"password";
552                 opt->label = _("PIN:");
553                 pin = &opt->value;
554                 opt++;
555         }
556
557         opts[0].next = opts[1].type ? &opts[1] : NULL;
558         opts[1].next = opts[2].type ? &opts[2] : NULL;
559
560         while (1) {
561                 nuke_opt_values(opts);
562
563                 if (!opts[0].type) {
564                         /* don't bug the user if there's nothing to enter */
565                         ret = 0;
566                 } else if (vpninfo->process_auth_form) {
567                         int some_empty = 0, all_empty = 1;
568
569                         /* < 0 for error; 1 if cancelled */
570                         ret = vpninfo->process_auth_form(vpninfo->cbdata, &form);
571                         if (ret)
572                                 break;
573
574                         for (opt = opts; opt; opt = opt->next) {
575                                 if (!opt->value || !strlen(opt->value))
576                                         some_empty = 1;
577                                 else
578                                         all_empty = 0;
579                         }
580                         if (all_empty) {
581                                 vpn_progress(vpninfo, PRG_INFO,
582                                              _("User bypassed soft token.\n"));
583                                 vpninfo->stoken_bypassed = 1;
584                                 ret = 0;
585                                 break;
586                         }
587                         if (some_empty) {
588                                 vpn_progress(vpninfo, PRG_INFO,
589                                              _("All fields are required; try again.\n"));
590                                 continue;
591                         }
592                 } else {
593                         vpn_progress(vpninfo, PRG_ERR,
594                                      _("No form handler; cannot authenticate.\n"));
595                         ret = -EIO;
596                         break;
597                 }
598
599                 ret = stoken_decrypt_seed(vpninfo->stoken_ctx,
600                                           pass ? *pass : NULL,
601                                           devid ? *devid : NULL);
602                 if (ret == -EIO || (ret && !devid && !pass)) {
603                         vpn_progress(vpninfo, PRG_ERR,
604                                      _("General failure in libstoken.\n"));
605                         break;
606                 } else if (ret != 0) {
607                         vpn_progress(vpninfo, PRG_INFO,
608                                      _("Incorrect device ID or password; try again.\n"));
609                         continue;
610                 }
611
612                 if (pin) {
613                         if (stoken_check_pin(vpninfo->stoken_ctx, *pin) != 0) {
614                                 vpn_progress(vpninfo, PRG_INFO,
615                                              _("Invalid PIN format; try again.\n"));
616                                 continue;
617                         }
618                         free(vpninfo->stoken_pin);
619                         vpninfo->stoken_pin = strdup(*pin);
620                         if (!vpninfo->stoken_pin) {
621                                 ret = -ENOMEM;
622                                 break;
623                         }
624                 }
625                 vpn_progress(vpninfo, PRG_DEBUG, _("Soft token init was successful.\n"));
626                 ret = 0;
627                 break;
628         }
629
630         nuke_opt_values(opts);
631         return ret;
632 #else
633         return -EOPNOTSUPP;
634 #endif
635 }
636
637 /* Return value:
638  *  < 0, if unable to generate a tokencode
639  *  = 0, on success
640  */
641 static int can_gen_tokencode(struct openconnect_info *vpninfo, struct oc_form_opt *opt)
642 {
643 #ifdef LIBSTOKEN_HDR
644         if (strcmp(opt->name, "password") || vpninfo->stoken_bypassed)
645                 return -EINVAL;
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;
654         } else {
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"));
658                 return -ENOENT;
659         }
660
661         vpninfo->stoken_tries++;
662         return 0;
663 #else
664         return -EOPNOTSUPP;
665 #endif
666 }
667
668 /* Return value:
669  *  < 0, if unable to generate a tokencode
670  *  = 0, on success
671  */
672 static int do_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form)
673 {
674 #ifdef LIBSTOKEN_HDR
675         char tokencode[STOKEN_MAX_TOKENCODE + 1];
676         struct oc_form_opt *opt;
677
678         for (opt = form->opts; ; opt = opt->next) {
679                 /* this form might not have anything for us to do */
680                 if (!opt)
681                         return 0;
682                 if (opt->type == OC_FORM_OPT_STOKEN)
683                         break;
684         }
685
686         if (!vpninfo->stoken_time)
687                 vpninfo->stoken_time = time(NULL);
688         vpn_progress(vpninfo, PRG_INFO, _("Generating tokencode\n"));
689
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"));
694                 return -EIO;
695         }
696
697         opt->value = strdup(tokencode);
698         return opt->value ? 0 : -ENOMEM;
699 #else
700         return 0;
701 #endif
702 }