Import translations from GNOME
[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 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);
48
49 static int append_opt(char *body, int bodylen, char *opt, char *name)
50 {
51         int len = strlen(body);
52
53         if (len) {
54                 if (len >= bodylen - 1)
55                         return -ENOSPC;
56                 body[len++] = '&';
57         }
58
59         while (*opt) {
60                 if (isalnum((int)(unsigned char)*opt)) {
61                         if (len >= bodylen - 1)
62                                 return -ENOSPC;
63                         body[len++] = *opt;
64                 } else {
65                         if (len >= bodylen - 3)
66                                 return -ENOSPC;
67                         sprintf(body+len, "%%%02x", *opt);
68                         len += 3;
69                 }
70                 opt++;
71         }
72
73         if (len >= bodylen - 1)
74                 return -ENOSPC;
75         body[len++] = '=';
76
77         while (name && *name) {
78                 if (isalnum((int)(unsigned char)*name)) {
79                         if (len >= bodylen - 1)
80                                 return -ENOSPC;
81                         body[len++] = *name;
82                 } else {
83                         if (len >= bodylen - 3)
84                                 return -ENOSPC;
85                         sprintf(body+len, "%%%02X", *name);
86                         len += 3;
87                 }
88                 name++;
89         }
90         body[len] = 0;
91
92         return 0;
93 }
94
95 static int append_form_opts(struct openconnect_info *vpninfo,
96                             struct oc_auth_form *form, char *body, int bodylen)
97 {
98         struct oc_form_opt *opt;
99         int ret;
100
101         for (opt = form->opts; opt; opt = opt->next) {
102                 ret = append_opt(body, bodylen, opt->name, opt->value);
103                 if (ret)
104                         return ret;
105         }
106         return 0;
107 }
108
109 /*
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.
114  */
115
116 static int parse_auth_choice(struct openconnect_info *vpninfo, struct oc_auth_form *form,
117                              xmlNode *xml_node)
118 {
119         struct oc_form_opt_select *opt;
120
121         opt = calloc(1, sizeof(*opt));
122         if (!opt)
123                 return -ENOMEM;
124
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");
128
129         if (!opt->form.name) {
130                 vpn_progress(vpninfo, PRG_ERR, _("Form choice has no name\n"));
131                 free(opt);
132                 return -EINVAL;
133         }
134
135         for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
136                 char *form_id;
137                 struct oc_choice *choice;
138
139                 if (xml_node->type != XML_ELEMENT_NODE)
140                         continue;
141
142                 if (strcmp((char *)xml_node->name, "option"))
143                         continue;
144
145                 form_id = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
146                 if (!form_id)
147                         continue;
148
149                 opt->nr_choices++;
150                 opt = realloc(opt, sizeof(*opt) +
151                                    opt->nr_choices * sizeof(*choice));
152                 if (!opt)
153                         return -ENOMEM;
154
155                 choice = &opt->choices[opt->nr_choices-1];
156
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");
162         }
163
164         /* We link the choice _first_ so it's at the top of what we present
165            to the user */
166         opt->form.next = form->opts;
167         form->opts = &opt->form;
168         return 0;
169 }
170
171 /* Return value:
172  *  < 0, on error
173  *  = 0, when form was cancelled
174  *  = 1, when form was parsed
175  */
176 static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
177                       xmlNode *xml_node)
178 {
179         char *input_type, *input_name, *input_label;
180
181         for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
182                 struct oc_form_opt *opt, **p;
183
184                 if (xml_node->type != XML_ELEMENT_NODE)
185                         continue;
186
187                 if (!strcmp((char *)xml_node->name, "select")) {
188                         if (parse_auth_choice(vpninfo, form, xml_node))
189                                 return -EINVAL;
190                         continue;
191                 }
192                 if (strcmp((char *)xml_node->name, "input")) {
193                         vpn_progress(vpninfo, PRG_TRACE,
194                                      _("name %s not input\n"), xml_node->name);
195                         continue;
196                 }
197
198                 input_type = (char *)xmlGetProp(xml_node, (unsigned char *)"type");
199                 if (!input_type) {
200                         vpn_progress(vpninfo, PRG_INFO,
201                                      _("No input type in form\n"));
202                         continue;
203                 }
204
205                 if (!strcmp(input_type, "submit") || !strcmp(input_type, "reset")) {
206                         free(input_type);
207                         continue;
208                 }
209
210                 input_name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
211                 if (!input_name) {
212                         vpn_progress(vpninfo, PRG_INFO,
213                                      _("No input name in form\n"));
214                         free(input_type);
215                         continue;
216                 }
217                 input_label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
218
219                 opt = calloc(1, sizeof(*opt));
220                 if (!opt) {
221                         free(input_type);
222                         free(input_name);
223                         free(input_label);
224                         return -ENOMEM;
225                 }
226
227                 opt->name = input_name;
228                 opt->label = input_label;
229
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;
238                         else
239                                 opt->type = OC_FORM_OPT_PASSWORD;
240                 } else {
241                         vpn_progress(vpninfo, PRG_INFO,
242                                      _("Unknown input type %s in form\n"),
243                                      input_type);
244                         free(input_type);
245                         free(input_name);
246                         free(input_label);
247                         free(opt);
248                         continue;
249                 }
250
251                 free(input_type);
252
253                 p = &form->opts;
254                 while (*p)
255                         p = &(*p)->next;
256
257                 *p = opt;
258         }
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 static int xmlnode_is_named(xmlNode *xml_node, const char *name)
321 {
322         return !strcmp((char *)xml_node->name, name);
323 }
324
325 static int xmlnode_get_prop(xmlNode *xml_node, const char *name, char **var)
326 {
327         char *str = (char *)xmlGetProp(xml_node, (unsigned char *)name);
328
329         if (!str)
330                 return -ENOENT;
331
332         free(*var);
333         *var = str;
334         return 0;
335 }
336
337 static int xmlnode_get_text(xmlNode *xml_node, const char *name, char **var)
338 {
339         char *str;
340
341         if (name && !xmlnode_is_named(xml_node, name))
342                 return -EINVAL;
343
344         str = xmlnode_msg(xml_node);
345         if (!str)
346                 return -ENOENT;
347
348         free(*var);
349         *var = str;
350         return 0;
351 }
352
353 /*
354  * Legacy server response looks like:
355  *
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 -->"
363  *   <csdMac
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 -->" />
367  *   <csdLinux
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" />
379  *   </form>
380  * </auth>
381  *
382  * New server response looks like:
383  *
384  * <config-auth>
385  *   <version><!-- whatever --></version>
386  *   <session-token><!-- if present, save to vpninfo->cookie --></session-token>
387  *   <opaque>
388  *     <!-- this could contain anything; copy to vpninfo->opaque_srvdata -->
389  *     <tunnel-group>foobar</tunnel-group>
390  *     <config-hash>1234567</config-hash>
391  *   </opaque>
392  *   <auth id="<!-- see above -->
393  *     <!-- all of our old familiar fields -->
394  *   </auth>
395  *   <host-scan>
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>
400  *   </host-scan>
401  * </config-auth>
402  *
403  * Notes:
404  *
405  * 1) The new host-scan-*-uri nodes do not map directly to the old CSD fields.
406  *
407  * 2) The new <form> tag tends to omit the method/action properties.
408  */
409
410 static int parse_auth_node(struct openconnect_info *vpninfo, xmlNode *xml_node,
411                            struct oc_auth_form *form)
412 {
413         int ret = 0;
414
415         for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
416                 if (xml_node->type != XML_ELEMENT_NODE)
417                         continue;
418
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);
422
423                 if (xmlnode_is_named(xml_node, "form")) {
424
425                         /* defaults for new XML POST */
426                         form->method = strdup("POST");
427                         form->action = strdup("/");
428
429                         xmlnode_get_prop(xml_node, "method", &form->method);
430                         xmlnode_get_prop(xml_node, "action", &form->action);
431
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);
437                                 ret = -EINVAL;
438                                 goto out;
439                         }
440
441                         ret = parse_form(vpninfo, form, xml_node);
442                         if (ret < 0)
443                                 goto out;
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);
452                 }
453         }
454
455 out:
456         return ret;
457 }
458
459 static int parse_host_scan_node(struct openconnect_info *vpninfo, xmlNode *xml_node)
460 {
461         /* ignore this whole section if the CSD trojan has already run */
462         if (vpninfo->csd_scriptname)
463                 return 0;
464
465         for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
466                 if (xml_node->type != XML_ELEMENT_NODE)
467                         continue;
468
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);
473         }
474         return 0;
475 }
476
477 /* Return value:
478  *  < 0, on error
479  *  = 0, on success; *form is populated
480  */
481 int parse_xml_response(struct openconnect_info *vpninfo, char *response, struct oc_auth_form **formp)
482 {
483         struct oc_auth_form *form;
484         xmlDocPtr xml_doc;
485         xmlNode *xml_node;
486         int ret = -EINVAL;
487
488         if (*formp) {
489                 free_auth_form(*formp);
490                 *formp = NULL;
491         }
492
493         form = calloc(1, sizeof(*form));
494         if (!form)
495                 return -ENOMEM;
496
497         xml_doc = xmlReadMemory(response, strlen(response), "noname.xml", NULL, 0);
498         if (!xml_doc) {
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);
503                 free(form);
504                 return -EINVAL;
505         }
506
507         xml_node = xmlDocGetRootElement(xml_doc);
508         while (xml_node) {
509                 int ret = 0;
510
511                 if (xml_node->type != XML_ELEMENT_NODE) {
512                         xml_node = xml_node->next;
513                         continue;
514                 }
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;
518                         continue;
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)
527                                 ret = -ENOMEM;
528                 } else if (xmlnode_is_named(xml_node, "host-scan")) {
529                         ret = parse_host_scan_node(vpninfo, xml_node);
530                 } else {
531                         xmlnode_get_text(xml_node, "session-token", &vpninfo->cookie);
532                         xmlnode_get_text(xml_node, "error", &form->error);
533                 }
534
535                 if (ret)
536                         goto out;
537                 xml_node = xml_node->next;
538         }
539
540         if (!form->auth_id) {
541                 vpn_progress(vpninfo, PRG_ERR,
542                              _("XML response has no \"auth\" node\n"));
543                 goto out;
544         }
545
546         *formp = form;
547         xmlFreeDoc(xml_doc);
548         return 0;
549
550  out:
551         xmlFreeDoc(xml_doc);
552         free_auth_form(form);
553         return ret;
554 }
555
556 /* Return value:
557  *  < 0, on error
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
561  */
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)
565 {
566         int ret;
567         struct vpn_option *opt, *next;
568
569         if (!strcmp(form->auth_id, "success"))
570                 return 2;
571
572         if (vpninfo->nopasswd) {
573                 vpn_progress(vpninfo, PRG_ERR,
574                              _("Asked for password but '--no-passwd' set\n"));
575                 return -EPERM;
576         }
577
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) {
581                         next = opt->next;
582
583                         free(opt->option);
584                         free(opt->value);
585                         free(opt);
586                 }
587                 vpninfo->cookies = NULL;
588                 return 0;
589         }
590         if (!form->opts) {
591                 if (form->message)
592                         vpn_progress(vpninfo, PRG_INFO, "%s\n", form->message);
593                 if (form->error)
594                         vpn_progress(vpninfo, PRG_ERR, "%s\n", form->error);
595                 return -EPERM;
596         }
597
598         if (vpninfo->process_auth_form)
599                 ret = vpninfo->process_auth_form(vpninfo->cbdata, form);
600         else {
601                 vpn_progress(vpninfo, PRG_ERR, _("No form handler; cannot authenticate.\n"));
602                 ret = 1;
603         }
604         if (ret)
605                 return ret;
606
607         /* tokencode generation is deferred until after username prompts and CSD */
608         ret = do_gen_tokencode(vpninfo, form);
609         if (ret)
610                 return ret;
611
612         ret = xmlpost ?
613               xmlpost_append_form_opts(vpninfo, form, request_body, req_len) :
614               append_form_opts(vpninfo, form, request_body, req_len);
615         if (!ret) {
616                 *method = "POST";
617                 *request_body_type = "application/x-www-form-urlencoded";
618         }
619         return ret;
620 }
621
622 void free_auth_form(struct oc_auth_form *form)
623 {
624         if (!form)
625                 return;
626         while (form->opts) {
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;
635                         int i;
636
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);
643                         }
644                 }
645                 free(form->opts->label);
646                 free(form->opts->name);
647                 free(form->opts);
648                 form->opts = tmp;
649         }
650         free(form->error);
651         free(form->message);
652         free(form->banner);
653         free(form->auth_id);
654         free(form->method);
655         free(form->action);
656         free(form);
657 }
658
659 /*
660  * Old submission format is just an HTTP query string:
661  *
662  * password=12345678&username=joe
663  *
664  * New XML format is more complicated:
665  *
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 -->
671  *   </opaque>
672  *
673  * For init only, add:
674  *   <group-access>https://<!-- insert hostname here --></group-access>
675  *
676  * For auth-reply only, add:
677  *   <auth>
678  *     <username><!-- same treatment as the old form options --></username>
679  *     <password><!-- ditto -->
680  *   </auth>
681  *   <host-scan-token><!-- vpninfo->csd_ticket --></host-scan-token>
682  */
683
684 #define XCAST(x) ((const xmlChar *)(x))
685
686 static xmlDocPtr xmlpost_new_query(struct openconnect_info *vpninfo, const char *type,
687                                    xmlNodePtr *rootp)
688 {
689         xmlDocPtr doc;
690         xmlNodePtr root, node;
691
692         doc = xmlNewDoc(XCAST("1.0"));
693         if (!doc)
694                 return NULL;
695
696         *rootp = root = xmlNewNode(NULL, XCAST("config-auth"));
697         if (!root)
698                 goto bad;
699         if (!xmlNewProp(root, XCAST("client"), XCAST("vpn")))
700                 goto bad;
701         if (!xmlNewProp(root, XCAST("type"), XCAST(type)))
702                 goto bad;
703         xmlDocSetRootElement(doc, root);
704
705         node = xmlNewTextChild(root, NULL, XCAST("version"), XCAST(openconnect_version_str));
706         if (!node)
707                 goto bad;
708         if (!xmlNewProp(node, XCAST("who"), XCAST("vpn")))
709                 goto bad;
710
711         if (!xmlNewTextChild(root, NULL, XCAST("device-id"), XCAST(vpninfo->platname)))
712                 goto bad;
713
714         return doc;
715
716 bad:
717         xmlFreeDoc(doc);
718         return NULL;
719 }
720
721 static int xmlpost_complete(xmlDocPtr doc, char *body, int bodylen)
722 {
723         xmlChar *mem = NULL;
724         int len, ret = 0;
725
726         if (!body) {
727                 xmlFree(doc);
728                 return 0;
729         }
730
731         xmlDocDumpMemoryEnc(doc, &mem, &len, "UTF-8");
732         if (!mem) {
733                 xmlFreeDoc(doc);
734                 return -ENOMEM;
735         }
736
737         if (len > bodylen)
738                 ret = -E2BIG;
739         else {
740                 memcpy(body, mem, len);
741                 body[len] = 0;
742         }
743
744         xmlFreeDoc(doc);
745         xmlFree(mem);
746
747         return ret;
748 }
749
750 int xmlpost_initial_req(struct openconnect_info *vpninfo, char *request_body, int req_len)
751 {
752         xmlNodePtr root, node;
753         xmlDocPtr doc = xmlpost_new_query(vpninfo, "init", &root);
754         char *url;
755
756         if (!doc)
757                 return -ENOMEM;
758
759         if (asprintf(&url, "https://%s", vpninfo->hostname) == -1)
760                 goto bad;
761         node = xmlNewTextChild(root, NULL, XCAST("group-access"), XCAST(url));
762         free(url);
763         if (!node)
764                 goto bad;
765
766         return xmlpost_complete(doc, request_body, req_len);
767
768 bad:
769         xmlpost_complete(doc, NULL, 0);
770         return -ENOMEM;
771 }
772
773 static int xmlpost_append_form_opts(struct openconnect_info *vpninfo,
774                                     struct oc_auth_form *form, char *body, int bodylen)
775 {
776         xmlNodePtr root, node;
777         xmlDocPtr doc = xmlpost_new_query(vpninfo, "auth-reply", &root);
778         struct oc_form_opt *opt;
779
780         if (!doc)
781                 return -ENOMEM;
782
783         if (vpninfo->opaque_srvdata) {
784                 node = xmlCopyNode(vpninfo->opaque_srvdata, 1);
785                 if (!node)
786                         goto bad;
787                 if (!xmlAddChild(root, node))
788                         goto bad;
789         }
790
791         node = xmlNewChild(root, NULL, XCAST("auth"), NULL);
792         if (!node)
793                 goto bad;
794
795         for (opt = form->opts; opt; opt = opt->next) {
796                 if (!xmlNewTextChild(node, NULL, XCAST(opt->name), XCAST(opt->value)))
797                         goto bad;
798         }
799
800         if (vpninfo->csd_token &&
801             !xmlNewTextChild(root, NULL, XCAST("host-scan-token"), XCAST(vpninfo->csd_token)))
802                 goto bad;
803
804         return xmlpost_complete(doc, body, bodylen);
805
806 bad:
807         xmlpost_complete(doc, NULL, 0);
808         return -ENOMEM;
809 }
810
811
812 #ifdef LIBSTOKEN_HDR
813 static void nuke_opt_values(struct oc_form_opt *opt)
814 {
815         for (; opt; opt = opt->next) {
816                 free(opt->value);
817                 opt->value = NULL;
818         }
819 }
820 #endif
821
822 /*
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.
826  *
827  * If the user clicks Cancel, we will abort the connection.
828  *
829  * Return value:
830  *  < 0, on error
831  *  = 0, on success (or if the user bypassed soft token init)
832  *  = 1, if the user cancelled the form submission
833  */
834 int prepare_stoken(struct openconnect_info *vpninfo)
835 {
836 #ifdef LIBSTOKEN_HDR
837         struct oc_auth_form form;
838         struct oc_form_opt opts[3], *opt = opts;
839         char **devid = NULL, **pass = NULL, **pin = NULL;
840         int ret = 0;
841
842         memset(&form, 0, sizeof(form));
843         memset(&opts, 0, sizeof(opts));
844
845         form.opts = opts;
846         form.message = _("Enter credentials to unlock software token.");
847
848         vpninfo->stoken_tries = 0;
849         vpninfo->stoken_bypassed = 0;
850
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:");
855                 devid = &opt->value;
856                 opt++;
857         }
858         if (stoken_pass_required(vpninfo->stoken_ctx)) {
859                 opt->type = OC_FORM_OPT_PASSWORD;
860                 opt->name = (char *)"password";
861                 opt->label = _("Password:");
862                 pass = &opt->value;
863                 opt++;
864         }
865         if (stoken_pin_required(vpninfo->stoken_ctx)) {
866                 opt->type = OC_FORM_OPT_PASSWORD;
867                 opt->name = (char *)"password";
868                 opt->label = _("PIN:");
869                 pin = &opt->value;
870                 opt++;
871         }
872
873         opts[0].next = opts[1].type ? &opts[1] : NULL;
874         opts[1].next = opts[2].type ? &opts[2] : NULL;
875
876         while (1) {
877                 nuke_opt_values(opts);
878
879                 if (!opts[0].type) {
880                         /* don't bug the user if there's nothing to enter */
881                         ret = 0;
882                 } else if (vpninfo->process_auth_form) {
883                         int some_empty = 0, all_empty = 1;
884
885                         /* < 0 for error; 1 if cancelled */
886                         ret = vpninfo->process_auth_form(vpninfo->cbdata, &form);
887                         if (ret)
888                                 break;
889
890                         for (opt = opts; opt; opt = opt->next) {
891                                 if (!opt->value || !strlen(opt->value))
892                                         some_empty = 1;
893                                 else
894                                         all_empty = 0;
895                         }
896                         if (all_empty) {
897                                 vpn_progress(vpninfo, PRG_INFO,
898                                              _("User bypassed soft token.\n"));
899                                 vpninfo->stoken_bypassed = 1;
900                                 ret = 0;
901                                 break;
902                         }
903                         if (some_empty) {
904                                 vpn_progress(vpninfo, PRG_INFO,
905                                              _("All fields are required; try again.\n"));
906                                 continue;
907                         }
908                 } else {
909                         vpn_progress(vpninfo, PRG_ERR,
910                                      _("No form handler; cannot authenticate.\n"));
911                         ret = -EIO;
912                         break;
913                 }
914
915                 ret = stoken_decrypt_seed(vpninfo->stoken_ctx,
916                                           pass ? *pass : NULL,
917                                           devid ? *devid : NULL);
918                 if (ret == -EIO || (ret && !devid && !pass)) {
919                         vpn_progress(vpninfo, PRG_ERR,
920                                      _("General failure in libstoken.\n"));
921                         break;
922                 } else if (ret != 0) {
923                         vpn_progress(vpninfo, PRG_INFO,
924                                      _("Incorrect device ID or password; try again.\n"));
925                         continue;
926                 }
927
928                 if (pin) {
929                         if (stoken_check_pin(vpninfo->stoken_ctx, *pin) != 0) {
930                                 vpn_progress(vpninfo, PRG_INFO,
931                                              _("Invalid PIN format; try again.\n"));
932                                 continue;
933                         }
934                         free(vpninfo->stoken_pin);
935                         vpninfo->stoken_pin = strdup(*pin);
936                         if (!vpninfo->stoken_pin) {
937                                 ret = -ENOMEM;
938                                 break;
939                         }
940                 }
941                 vpn_progress(vpninfo, PRG_DEBUG, _("Soft token init was successful.\n"));
942                 ret = 0;
943                 break;
944         }
945
946         nuke_opt_values(opts);
947         return ret;
948 #else
949         return -EOPNOTSUPP;
950 #endif
951 }
952
953 /* Return value:
954  *  < 0, if unable to generate a tokencode
955  *  = 0, on success
956  */
957 static int can_gen_tokencode(struct openconnect_info *vpninfo, struct oc_form_opt *opt)
958 {
959 #ifdef LIBSTOKEN_HDR
960         if (strcmp(opt->name, "password") || vpninfo->stoken_bypassed)
961                 return -EINVAL;
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;
970         } else {
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"));
974                 return -ENOENT;
975         }
976         return 0;
977 #else
978         return -EOPNOTSUPP;
979 #endif
980 }
981
982 /* Return value:
983  *  < 0, if unable to generate a tokencode
984  *  = 0, on success
985  */
986 static int do_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form)
987 {
988 #ifdef LIBSTOKEN_HDR
989         char tokencode[STOKEN_MAX_TOKENCODE + 1];
990         struct oc_form_opt *opt;
991
992         for (opt = form->opts; ; opt = opt->next) {
993                 /* this form might not have anything for us to do */
994                 if (!opt)
995                         return 0;
996                 if (opt->type == OC_FORM_OPT_STOKEN)
997                         break;
998         }
999
1000         if (!vpninfo->stoken_time)
1001                 vpninfo->stoken_time = time(NULL);
1002         vpn_progress(vpninfo, PRG_INFO, _("Generating tokencode\n"));
1003
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"));
1008                 return -EIO;
1009         }
1010
1011         vpninfo->stoken_tries++;
1012         opt->value = strdup(tokencode);
1013         return opt->value ? 0 : -ENOMEM;
1014 #else
1015         return 0;
1016 #endif
1017 }