Update translations from Transifex
[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 #include <libxml/parser.h>
36 #include <libxml/tree.h>
37
38 #include "openconnect-internal.h"
39
40 static int append_opt(char *body, int bodylen, char *opt, char *name)
41 {
42         int len = strlen(body);
43
44         if (len) {
45                 if (len >= bodylen - 1)
46                         return -ENOSPC;
47                 body[len++] = '&';
48         }
49
50         while (*opt) {
51                 if (isalnum((int)(unsigned char)*opt)) {
52                         if (len >= bodylen - 1)
53                                 return -ENOSPC;
54                         body[len++] = *opt;
55                 } else {
56                         if (len >= bodylen - 3)
57                                 return -ENOSPC;
58                         sprintf(body+len, "%%%02x", *opt);
59                         len += 3;
60                 }
61                 opt++;
62         }
63
64         if (len >= bodylen - 1)
65                 return -ENOSPC;
66         body[len++] = '=';
67
68         while (name && *name) {
69                 if (isalnum((int)(unsigned char)*name)) {
70                         if (len >= bodylen - 1)
71                                 return -ENOSPC;
72                         body[len++] = *name;
73                 } else {
74                         if (len >= bodylen - 3)
75                                 return -ENOSPC;
76                         sprintf(body+len, "%%%02X", *name);
77                         len += 3;
78                 }
79                 name++;
80         }
81         body[len] = 0;
82
83         return 0;
84 }
85
86 static int append_form_opts(struct openconnect_info *vpninfo,
87                             struct oc_auth_form *form, char *body, int bodylen)
88 {
89         struct oc_form_opt *opt;
90         int ret;
91
92         for (opt = form->opts; opt; opt = opt->next) {
93                 ret = append_opt(body, bodylen, opt->name, opt->value);
94                 if (ret)
95                         return ret;
96         }
97         return 0;
98 }
99
100 /*
101  * Maybe we should offer this choice to the user. So far we've only
102  * ever seen it offer bogus choices though -- between certificate and
103  * password authentication, when the former has already failed.
104  * So we just accept the first option with an auth-type property.
105  */
106
107 static int parse_auth_choice(struct openconnect_info *vpninfo, struct oc_auth_form *form,
108                              xmlNode *xml_node)
109 {
110         struct oc_form_opt_select *opt;
111
112         opt = calloc(1, sizeof(*opt));
113         if (!opt)
114                 return -ENOMEM;
115
116         opt->form.type = OC_FORM_OPT_SELECT;
117         opt->form.name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
118         opt->form.label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
119
120         if (!opt->form.name) {
121                 vpn_progress(vpninfo, PRG_ERR, _("Form choice has no name\n"));
122                 free(opt);
123                 return -EINVAL;
124         }
125
126         for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
127                 char *form_id;
128                 struct oc_choice *choice;
129
130                 if (xml_node->type != XML_ELEMENT_NODE)
131                         continue;
132
133                 if (strcmp((char *)xml_node->name, "option"))
134                         continue;
135
136                 form_id = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
137                 if (!form_id)
138                         continue;
139
140                 opt->nr_choices++;
141                 opt = realloc(opt, sizeof(*opt) +
142                                    opt->nr_choices * sizeof(*choice));
143                 if (!opt)
144                         return -ENOMEM;
145
146                 choice = &opt->choices[opt->nr_choices-1];
147
148                 choice->name = form_id;
149                 choice->label = (char *)xmlNodeGetContent(xml_node);
150                 choice->auth_type = (char *)xmlGetProp(xml_node, (unsigned char *)"auth-type");
151                 choice->override_name = (char *)xmlGetProp(xml_node, (unsigned char *)"override-name");
152                 choice->override_label = (char *)xmlGetProp(xml_node, (unsigned char *)"override-label");
153         }
154
155         /* We link the choice _first_ so it's at the top of what we present
156            to the user */
157         opt->form.next = form->opts;
158         form->opts = &opt->form;
159         return 0;
160 }
161
162 /* Return value:
163  *  < 0, on error
164  *  = 0, when form was cancelled
165  *  = 1, when form was parsed
166  */
167 static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *form,
168                       xmlNode *xml_node, char *body, int bodylen)
169 {
170         char *input_type, *input_name, *input_label;
171
172         for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
173                 struct oc_form_opt *opt, **p;
174
175                 if (xml_node->type != XML_ELEMENT_NODE)
176                         continue;
177
178                 if (!strcmp((char *)xml_node->name, "select")) {
179                         if (parse_auth_choice(vpninfo, form, xml_node))
180                                 return -EINVAL;
181                         continue;
182                 }
183                 if (strcmp((char *)xml_node->name, "input")) {
184                         vpn_progress(vpninfo, PRG_TRACE,
185                                      _("name %s not input\n"), xml_node->name);
186                         continue;
187                 }
188
189                 input_type = (char *)xmlGetProp(xml_node, (unsigned char *)"type");
190                 if (!input_type) {
191                         vpn_progress(vpninfo, PRG_INFO,
192                                      _("No input type in form\n"));
193                         continue;
194                 }
195
196                 if (!strcmp(input_type, "submit") || !strcmp(input_type, "reset")) {
197                         free(input_type);
198                         continue;
199                 }
200
201                 input_name = (char *)xmlGetProp(xml_node, (unsigned char *)"name");
202                 if (!input_name) {
203                         vpn_progress(vpninfo, PRG_INFO,
204                                      _("No input name in form\n"));
205                         free(input_type);
206                         continue;
207                 }
208                 input_label = (char *)xmlGetProp(xml_node, (unsigned char *)"label");
209
210                 opt = calloc(1, sizeof(*opt));
211                 if (!opt) {
212                         free(input_type);
213                         free(input_name);
214                         free(input_label);
215                         return -ENOMEM;
216                 }
217
218                 if (!strcmp(input_type, "hidden")) {
219                         opt->type = OC_FORM_OPT_HIDDEN;
220                         opt->value = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
221                 } else if (!strcmp(input_type, "text"))
222                         opt->type = OC_FORM_OPT_TEXT;
223                 else if (!strcmp(input_type, "password"))
224                         opt->type = OC_FORM_OPT_PASSWORD;
225                 else {
226                         vpn_progress(vpninfo, PRG_INFO,
227                                      _("Unknown input type %s in form\n"),
228                                      input_type);
229                         free(input_type);
230                         free(input_name);
231                         free(input_label);
232                         free(opt);
233                         continue;
234                 }
235
236                 free(input_type);
237                 opt->name = input_name;
238                 opt->label = input_label;
239
240                 p = &form->opts;
241                 while (*p)
242                         p = &(*p)->next;
243
244                 *p = opt;
245         }
246
247         vpn_progress(vpninfo, PRG_TRACE, _("Fixed options give %s\n"), body);
248
249         return 0;
250 }
251
252 static char *xmlnode_msg(xmlNode *xml_node)
253 {
254         char *fmt = (char *)xmlNodeGetContent(xml_node);
255         char *result, *params[2], *pct;
256         int len;
257         int nr_params = 0;
258
259         if (!fmt || !fmt[0]) {
260                 free(fmt);
261                 return NULL;
262         }
263
264         len = strlen(fmt) + 1;
265         
266         params[0] = (char *)xmlGetProp(xml_node, (unsigned char *)"param1");
267         if (params[0])
268                 len += strlen(params[0]);
269         params[1] = (char *)xmlGetProp(xml_node, (unsigned char *)"param2");
270         if (params[1])
271                 len += strlen(params[1]);
272
273         result = malloc(len);
274         if (!result) {
275                 result = fmt;
276                 goto out;
277         }
278
279         strcpy(result, fmt);
280         free (fmt);
281
282         for (pct = strchr(result, '%'); pct;
283              (pct = strchr(pct, '%'))) {
284                 int paramlen;
285
286                 /* We only cope with '%s' */
287                 if (pct[1] != 's')
288                         goto out;
289
290                 if (params[nr_params]) {
291                         paramlen = strlen(params[nr_params]);
292                         /* Move rest of fmt string up... */
293                         memmove(pct - 1 + paramlen, pct + 2, strlen(pct) - 1);
294                         /* ... and put the string parameter in where the '%s' was */
295                         memcpy(pct, params[nr_params], paramlen);
296                         pct += paramlen;
297                 } else
298                         pct++;
299
300                 if (++nr_params == 2)
301                         break;
302         }
303  out:
304         free(params[0]);
305         free(params[1]);
306         return result;
307 }
308
309 /* Return value:
310  *  < 0, on error
311  *  = 0, when form parsed and POST required
312  *  = 1, when response was cancelled by user
313  *  = 2, when form indicates that login was already successful
314  */
315 int parse_xml_response(struct openconnect_info *vpninfo, char *response,
316                        char *request_body, int req_len, const char **method,
317                        const char **request_body_type)
318 {
319         struct oc_auth_form *form;
320         xmlDocPtr xml_doc;
321         xmlNode *xml_node;
322         int ret;
323         struct vpn_option *opt, *next;
324
325         form = calloc(1, sizeof(*form));
326         if (!form)
327                 return -ENOMEM;
328
329         xml_doc = xmlReadMemory(response, strlen(response), "noname.xml", NULL, 0);
330         if (!xml_doc) {
331                 vpn_progress(vpninfo, PRG_ERR,
332                              _("Failed to parse server response\n"));
333                 vpn_progress(vpninfo, PRG_TRACE,
334                              _("Response was:%s\n"), response);
335                 free(form);
336                 return -EINVAL;
337         }
338
339         xml_node = xmlDocGetRootElement(xml_doc);
340         if (xml_node->type != XML_ELEMENT_NODE || strcmp((char *)xml_node->name, "auth")) {
341                 vpn_progress(vpninfo, PRG_ERR,
342                              _("XML response has no \"auth\" root node\n"));
343                 ret = -EINVAL;
344                 goto out;
345         }
346
347         form->auth_id = (char *)xmlGetProp(xml_node, (unsigned char *)"id");
348         if (!strcmp(form->auth_id, "success")) {
349                 ret = 2;
350                 goto out;
351         }
352
353         if (vpninfo->nopasswd) {
354                 vpn_progress(vpninfo, PRG_ERR,
355                              _("Asked for password but '--no-passwd' set\n"));
356                 ret = -EPERM;
357                 goto out;
358         }
359
360         for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
361                 if (xml_node->type != XML_ELEMENT_NODE)
362                         continue;
363
364                 if (!strcmp((char *)xml_node->name, "banner")) {
365                         free(form->banner);
366                         form->banner = xmlnode_msg(xml_node);
367                 } else if (!strcmp((char *)xml_node->name, "message")) {
368                         free(form->message);
369                         form->message = xmlnode_msg(xml_node);
370                 } else if (!strcmp((char *)xml_node->name, "error")) {
371                         free(form->error);
372                         form->error = xmlnode_msg(xml_node);
373                 } else if (!strcmp((char *)xml_node->name, "form")) {
374
375                         form->method = (char *)xmlGetProp(xml_node, (unsigned char *)"method");
376                         form->action = (char *)xmlGetProp(xml_node, (unsigned char *)"action");
377                         if (!form->method || !form->action || 
378                             strcasecmp(form->method, "POST") || !form->action[0]) {
379                                 vpn_progress(vpninfo, PRG_ERR,
380                                              _("Cannot handle form method='%s', action='%s'\n"),
381                                              form->method, form->action);
382                                 ret = -EINVAL;
383                                 goto out;
384                         }
385                         vpninfo->redirect_url = strdup(form->action);
386
387                         ret = parse_form(vpninfo, form, xml_node, request_body, req_len);
388                         if (ret < 0)
389                                 goto out;
390                 } else if (!vpninfo->csd_scriptname && !strcmp((char *)xml_node->name, "csd")) {
391                         if (!vpninfo->csd_token)
392                                 vpninfo->csd_token = (char *)xmlGetProp(xml_node,
393                                                                         (unsigned char *)"token");
394                         if (!vpninfo->csd_ticket)
395                                 vpninfo->csd_ticket = (char *)xmlGetProp(xml_node,
396                                                                          (unsigned char *)"ticket");
397                 } else if (!vpninfo->csd_scriptname && !strcmp((char *)xml_node->name, vpninfo->csd_xmltag)) {
398                         vpninfo->csd_stuburl = (char *)xmlGetProp(xml_node,
399                                                                   (unsigned char *)"stuburl");
400                         vpninfo->csd_starturl = (char *)xmlGetProp(xml_node,
401                                                                    (unsigned char *)"starturl");
402                         vpninfo->csd_waiturl = (char *)xmlGetProp(xml_node,
403                                                                   (unsigned char *)"waiturl");
404                         vpninfo->csd_preurl = strdup(vpninfo->urlpath);
405                 }
406         }
407         if (vpninfo->csd_token && vpninfo->csd_ticket && vpninfo->csd_starturl && vpninfo->csd_waiturl) {
408                 /* First, redirect to the stuburl -- we'll need to fetch and run that */
409                 vpninfo->redirect_url = strdup(vpninfo->csd_stuburl);
410
411                 /* AB: remove all cookies */
412                 for (opt = vpninfo->cookies; opt; opt = next) {
413                         next = opt->next;
414
415                         free(opt->option);
416                         free(opt->value);
417                         free(opt);
418                 }
419                 vpninfo->cookies = NULL;
420
421                 ret = 0;
422                 goto out;
423         }
424         if (!form->opts) {
425                 if (form->message)
426                         vpn_progress(vpninfo, PRG_INFO, "%s\n", form->message);
427                 if (form->error)
428                         vpn_progress(vpninfo, PRG_ERR, "%s\n", form->error);
429                 ret = -EPERM;
430                 goto out;
431         }
432
433         if (vpninfo->process_auth_form)
434                 ret = vpninfo->process_auth_form(vpninfo->cbdata, form);
435         else {
436                 vpn_progress(vpninfo, PRG_ERR, _("No form handler; cannot authenticate."));
437                 ret = 1;
438         }
439         if (ret)
440                 goto out;
441
442         ret = append_form_opts(vpninfo, form, request_body, req_len);
443         if (!ret) {
444                 *method = "POST";
445                 *request_body_type = "application/x-www-form-urlencoded";
446         }
447  out:
448         xmlFreeDoc(xml_doc);
449         while (form->opts) {
450                 struct oc_form_opt *tmp = form->opts->next;
451                 if (form->opts->type == OC_FORM_OPT_TEXT ||
452                     form->opts->type == OC_FORM_OPT_PASSWORD ||
453                     form->opts->type == OC_FORM_OPT_HIDDEN)
454                         free(form->opts->value);
455                 else if (form->opts->type == OC_FORM_OPT_SELECT) {
456                         struct oc_form_opt_select *sel = (void *)form->opts;
457                         int i;
458
459                         for (i=0; i < sel->nr_choices; i++) {
460                                 free(sel->choices[i].name);
461                                 free(sel->choices[i].label);
462                                 free(sel->choices[i].auth_type);
463                                 free(sel->choices[i].override_name);
464                                 free(sel->choices[i].override_label);
465                         }
466                 }
467                 free(form->opts->label);
468                 free(form->opts->name);
469                 free(form->opts);
470                 form->opts = tmp;
471         }
472         free(form->error);
473         free(form->message);
474         free(form->banner);
475         free(form->auth_id);
476         free(form->method);
477         free(form->action);
478         free(form);
479         return ret;
480 }
481
482
483