Skip to content

Latest commit

 

History

History
483 lines (416 loc) · 12.3 KB

auth.c

File metadata and controls

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