/
main.c
1161 lines (1043 loc) · 31.4 KB
1
/*
2
* OpenConnect (SSL + DTLS) VPN client
3
*
4
* Copyright © 2008-2012 Intel Corporation.
5
* Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
6
*
7
8
9
* Author: David Woodhouse <dwmw2@infradead.org>
*
* 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
15
16
17
18
19
20
21
22
23
* 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
24
25
*/
26
27
28
29
30
#ifdef HAVE_GETLINE
/* Various BSD systems require this for getline() to be visible */
#define _WITH_GETLINE
#endif
31
#include <stdio.h>
32
33
34
#ifdef ANDROID
#include <android/log.h>
#else
35
#include <syslog.h>
36
#endif
37
#include <stdarg.h>
38
#include <stdlib.h>
39
#include <signal.h>
40
#include <string.h>
41
42
43
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
44
45
46
47
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
48
49
#include <sys/utsname.h>
#include <sys/types.h>
50
#include <termios.h>
51
#ifdef LIBPROXY_HDR
52
#include LIBPROXY_HDR
53
#endif
54
#include <getopt.h>
55
56
#include "openconnect-internal.h"
57
58
59
60
61
62
63
64
static int write_new_config(void *_vpninfo,
char *buf, int buflen);
static void write_progress(void *_vpninfo,
int level, const char *fmt, ...);
static void syslog_progress(void *_vpninfo,
int level, const char *fmt, ...);
static int validate_peer_cert(void *_vpninfo,
65
66
OPENCONNECT_X509 *peer_cert,
const char *reason);
67
68
static int process_auth_form(void *_vpninfo,
struct oc_auth_form *form);
69
70
71
/* A sanity check that the openconnect executable is running against a
library of the same version */
72
#define openconnect_version_str openconnect_binary_version
73
#include "version.c"
74
#undef openconnect_version_str
75
76
int verbose = PRG_INFO;
77
int background;
78
int do_passphrase_from_fsid;
79
int nocertcheck;
80
int non_inter;
81
int cookieonly;
82
83
enum {
84
85
OPT_AUTHENTICATE = 0x100,
OPT_AUTHGROUP,
86
OPT_BASEMTU,
87
OPT_CAFILE,
88
OPT_CONFIGFILE,
89
90
91
OPT_COOKIEONLY,
OPT_COOKIE_ON_STDIN,
OPT_CSD_USER,
92
OPT_CSD_WRAPPER,
93
94
95
96
97
98
99
100
101
102
OPT_DISABLE_IPV6,
OPT_DTLS_CIPHERS,
OPT_FORCE_DPD,
OPT_KEY_PASSWORD_FROM_FSID,
OPT_LIBPROXY,
OPT_NO_CERT_CHECK,
OPT_NO_DTLS,
OPT_NO_HTTP_KEEPALIVE,
OPT_NO_PASSWD,
OPT_NO_PROXY,
103
OPT_PIDFILE,
104
105
106
107
108
OPT_PASSWORD_ON_STDIN,
OPT_PRINTCOOKIE,
OPT_RECONNECT_TIMEOUT,
OPT_SERVERCERT,
OPT_USERAGENT,
109
OPT_NON_INTER,
110
111
};
112
113
114
115
116
117
118
119
120
121
#ifdef __sun__
/*
* The 'name' field in Solaris 'struct option' lacks the 'const', and causes
* lots of warnings unless we cast it... https://www.illumos.org/issues/1881
*/
#define OPTION(name, arg, abbrev) {(char *)name, arg, NULL, abbrev}
#else
#define OPTION(name, arg, abbrev) {name, arg, NULL, abbrev}
#endif
122
static struct option long_options[] = {
123
124
125
126
127
128
129
130
131
132
133
134
OPTION("background", 0, 'b'),
OPTION("pid-file", 1, OPT_PIDFILE),
OPTION("certificate", 1, 'c'),
OPTION("sslkey", 1, 'k'),
OPTION("cookie", 1, 'C'),
OPTION("deflate", 0, 'd'),
OPTION("no-deflate", 0, 'D'),
OPTION("cert-expire-warning", 1, 'e'),
OPTION("usergroup", 1, 'g'),
OPTION("help", 0, 'h'),
OPTION("interface", 1, 'i'),
OPTION("mtu", 1, 'm'),
135
OPTION("base-mtu", 1, OPT_BASEMTU),
136
137
138
139
140
141
142
143
144
145
OPTION("setuid", 1, 'U'),
OPTION("script", 1, 's'),
OPTION("script-tun", 0, 'S'),
OPTION("syslog", 0, 'l'),
OPTION("key-password", 1, 'p'),
OPTION("proxy", 1, 'P'),
OPTION("user", 1, 'u'),
OPTION("verbose", 0, 'v'),
OPTION("version", 0, 'V'),
OPTION("cafile", 1, OPT_CAFILE),
146
OPTION("config", 1, OPT_CONFIGFILE),
147
OPTION("no-dtls", 0, OPT_NO_DTLS),
148
OPTION("authenticate", 0, OPT_AUTHENTICATE),
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
OPTION("cookieonly", 0, OPT_COOKIEONLY),
OPTION("printcookie", 0, OPT_PRINTCOOKIE),
OPTION("quiet", 0, 'q'),
OPTION("queue-len", 1, 'Q'),
OPTION("xmlconfig", 1, 'x'),
OPTION("cookie-on-stdin", 0, OPT_COOKIE_ON_STDIN),
OPTION("passwd-on-stdin", 0, OPT_PASSWORD_ON_STDIN),
OPTION("no-passwd", 0, OPT_NO_PASSWD),
OPTION("reconnect-timeout", 1, OPT_RECONNECT_TIMEOUT),
OPTION("dtls-ciphers", 1, OPT_DTLS_CIPHERS),
OPTION("authgroup", 1, OPT_AUTHGROUP),
OPTION("servercert", 1, OPT_SERVERCERT),
OPTION("key-password-from-fsid", 0, OPT_KEY_PASSWORD_FROM_FSID),
OPTION("useragent", 1, OPT_USERAGENT),
OPTION("csd-user", 1, OPT_CSD_USER),
OPTION("csd-wrapper", 1, OPT_CSD_WRAPPER),
OPTION("disable-ipv6", 0, OPT_DISABLE_IPV6),
OPTION("no-proxy", 0, OPT_NO_PROXY),
OPTION("libproxy", 0, OPT_LIBPROXY),
OPTION("no-http-keepalive", 0, OPT_NO_HTTP_KEEPALIVE),
OPTION("no-cert-check", 0, OPT_NO_CERT_CHECK),
OPTION("force-dpd", 1, OPT_FORCE_DPD),
OPTION("non-inter", 0, OPT_NON_INTER),
OPTION(NULL, 0, 0)
173
174
};
175
176
177
178
179
180
static void helpmessage(void)
{
printf(_("For assistance with OpenConnect, please see the web page at\n"
" http://www.infradead.org/openconnect/mail.html\n"));
}
181
182
183
static void print_build_opts(void)
{
#if defined (OPENCONNECT_OPENSSL) && defined (HAVE_ENGINE)
184
185
186
187
if (openconnect_has_tss_blob_support())
printf(_("Using OpenSSL with TPM ENGINE support. Loading TPM engine succeeded.\n"));
else
printf(_("Using OpenSSL with TPM ENGINE support, but loading TPM engine failed.\n"));
188
#elif defined (OPENCONNECT_OPENSSL)
189
printf(_("Using OpenSSL without TPM ENGINE support\n"));
190
#elif defined (OPENCONNECT_GNUTLS) && defined (HAVE_P11KIT)
191
printf(_("Using GnuTLS with PKCS#11 token support\n"));
192
#elif defined (OPENCONNECT_GNUTLS)
193
printf(_("Using GnuTLS without PKCS#11 token support\n"));
194
195
196
197
#else
#error wtf
#endif
#ifndef HAVE_DTLS
198
printf(_("No DTLS support in this binary\n"));
199
#elif defined (DTLS_OPENSSL)
200
printf(_("Using OpenSSL for DTLS support\n"));
201
#elif defined (DTLS_GNUTLS)
202
printf(_("Using GnuTLS for DTLS support\n"));
203
204
205
206
207
#else
#error wtf
#endif
}
208
static void usage(void)
209
{
210
printf(_("Usage: openconnect [options] <server>\n"));
211
printf(_("Open client for Cisco AnyConnect VPN, version %s\n\n"), openconnect_version_str);
212
print_build_opts();
213
printf(" --config=CONFIGFILE %s\n", _("Read options from config file"));
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
printf(" -b, --background %s\n", _("Continue in background after startup"));
printf(" --pid-file=PIDFILE %s\n", _("Write the daemons pid to this file"));
printf(" -c, --certificate=CERT %s\n", _("Use SSL client certificate CERT"));
printf(" -e, --cert-expire-warning=DAYS %s\n", _("Warn when certificate lifetime < DAYS"));
printf(" -k, --sslkey=KEY %s\n", _("Use SSL private key file KEY"));
printf(" -K, --key-type=TYPE %s\n", _("Private key type (PKCS#12 / TPM / PEM)"));
printf(" -C, --cookie=COOKIE %s\n", _("Use WebVPN cookie COOKIE"));
printf(" --cookie-on-stdin %s\n", _("Read cookie from standard input"));
printf(" -d, --deflate %s\n", _("Enable compression (default)"));
printf(" -D, --no-deflate %s\n", _("Disable compression"));
printf(" --force-dpd=INTERVAL %s\n", _("Set minimum Dead Peer Detection interval"));
printf(" -g, --usergroup=GROUP %s\n", _("Set login usergroup"));
printf(" -h, --help %s\n", _("Display help text"));
printf(" -i, --interface=IFNAME %s\n", _("Use IFNAME for tunnel interface"));
printf(" -l, --syslog %s\n", _("Use syslog for progress messages"));
printf(" -U, --setuid=USER %s\n", _("Drop privileges after connecting"));
printf(" --csd-user=USER %s\n", _("Drop privileges during CSD execution"));
printf(" --csd-wrapper=SCRIPT %s\n", _("Run SCRIPT instead of CSD binary"));
printf(" -m, --mtu=MTU %s\n", _("Request MTU from server"));
233
printf(" --base-mtu=MTU %s\n", _("Indicate path MTU to/from server"));
234
235
236
237
238
printf(" -p, --key-password=PASS %s\n", _("Set key passphrase or TPM SRK PIN"));
printf(" --key-password-from-fsid %s\n", _("Key passphrase is fsid of file system"));
printf(" -P, --proxy=URL %s\n", _("Set proxy server"));
printf(" --no-proxy %s\n", _("Disable proxy"));
printf(" --libproxy %s\n", _("Use libproxy to automatically configure proxy"));
239
#ifndef LIBPROXY_HDR
240
printf(" %s\n", _("(NOTE: libproxy disabled in this build)"));
241
#endif
242
243
244
printf(" -q, --quiet %s\n", _("Less output"));
printf(" -Q, --queue-len=LEN %s\n", _("Set packet queue limit to LEN pkts"));
printf(" -s, --script=SCRIPT %s\n", _("Shell command line for using a vpnc-compatible config script"));
245
printf(" %s: \"%s\"\n", _("default"), DEFAULT_VPNCSCRIPT);
246
247
248
249
250
251
printf(" -S, --script-tun %s\n", _("Pass traffic to 'script' program, not tun"));
printf(" -u, --user=NAME %s\n", _("Set login username"));
printf(" -V, --version %s\n", _("Report version number"));
printf(" -v, --verbose %s\n", _("More output"));
printf(" -x, --xmlconfig=CONFIG %s\n", _("XML config file"));
printf(" --authgroup=GROUP %s\n", _("Choose authentication login selection"));
252
printf(" --authenticate %s\n", _("Authenticate only and print login info"));
253
254
255
256
257
258
259
260
261
262
263
264
265
266
printf(" --cookieonly %s\n", _("Fetch webvpn cookie only; don't connect"));
printf(" --printcookie %s\n", _("Print webvpn cookie before connecting"));
printf(" --cafile=FILE %s\n", _("Cert file for server verification"));
printf(" --disable-ipv6 %s\n", _("Do not ask for IPv6 connectivity"));
printf(" --dtls-ciphers=LIST %s\n", _("OpenSSL ciphers to support for DTLS"));
printf(" --no-dtls %s\n", _("Disable DTLS"));
printf(" --no-http-keepalive %s\n", _("Disable HTTP connection re-use"));
printf(" --no-passwd %s\n", _("Disable password/SecurID authentication"));
printf(" --no-cert-check %s\n", _("Do not require server SSL cert to be valid"));
printf(" --non-inter %s\n", _("Do not expect user input; exit if it is required"));
printf(" --passwd-on-stdin %s\n", _("Read password from standard input"));
printf(" --reconnect-timeout %s\n", _("Connection retry timeout in seconds"));
printf(" --servercert=FINGERPRINT %s\n", _("Server's certificate SHA1 fingerprint"));
printf(" --useragent=STRING %s\n", _("HTTP header User-Agent: field"));
267
268
269
printf("\n");
helpmessage();
270
271
272
exit(1);
}
273
static void read_stdin(char **string)
274
275
276
{
char *c = malloc(100);
if (!c) {
277
fprintf(stderr, _("Allocation failure for string from stdin\n"));
278
279
280
exit(1);
}
if (!fgets(c, 100, stdin)) {
281
perror(_("fgets (stdin)"));
282
283
284
exit(1);
}
285
*string = c;
286
287
c = strchr(*string, '\n');
288
289
290
if (c)
*c = 0;
}
291
292
293
294
295
296
297
static void handle_sigusr(int sig)
{
if (sig == SIGUSR1)
verbose = PRG_TRACE;
else if (sig == SIGUSR2)
verbose = PRG_INFO;
}
298
299
300
301
static FILE *config_file = NULL;
static int config_line_num = 0;
302
303
304
305
306
307
308
309
310
311
312
313
/* There are three ways to handle config_arg:
*
* 1. We only care about it transiently and it can be lost entirely
* (e.g. vpninfo->reconnect_timeout = atoi(config_arg);
* 2. We need to kep it, but it's a static string and will never be freed
* so when it's part of argv[] we can use it in place, but when it comes
* from a file we have to strdup() because otherwise it'll be overwritten.
* For this we use the keep_config_arg() macro below.
* 3. It may be freed during normal operation, so we have to use strdup()
* even when it's an option from argv[]. (e.g. vpninfo->cert_password).
*/
#define keep_config_arg() (config_file?strdup(config_arg):config_arg)
314
315
316
static int next_option(int argc, char **argv, char **config_arg)
{
317
318
319
320
321
322
323
324
325
326
327
328
329
/* These get re-used */
static char *line_buf = NULL;
static size_t line_size = 0;
ssize_t llen;
int opt, optlen;
struct option *this;
char *line;
int ate_equals = 0;
next:
if (!config_file) {
opt = getopt_long(argc, argv,
330
"bC:c:e:Ddg:hi:k:lpP:Q:qSs:U:u:Vvx:",
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
long_options, NULL);
*config_arg = optarg;
return opt;
}
llen = getline(&line_buf, &line_size, config_file);
if (llen < 0) {
if (feof(config_file)) {
fclose(config_file);
config_file = NULL;
goto next;
}
fprintf(stderr, _("Failed to get line from config file: %s\n"),
strerror(errno));
exit(1);
}
line = line_buf;
/* Strip the trailing newline (coping with DOS newlines) */
if (llen && line[llen-1] == '\n')
line[--llen] = 0;
if (llen && line[llen-1] == '\r')
line[--llen] = 0;
/* Skip and leading whitespace */
while (line[0] == ' ' || line[0] == '\t' || line[0] == '\r')
line++;
/* Ignore comments and empty lines */
if (!line[0] || line[0] == '#') {
config_line_num++;
goto next;
}
/* Try to match on a known option... naïvely. This could be improved. */
for (this = long_options; this->name; this++) {
optlen = strlen(this->name);
/* If the option isn't followed by whitespace or NUL, or
perhaps an equals sign if the option takes an argument,
then it's not a match */
if (!strncmp(this->name, line, optlen) &&
(!line[optlen] || line[optlen] == ' ' || line[optlen] == '\t' ||
line[optlen] == '='))
break;
}
if (!this->name) {
char *l;
for (l = line; *l && *l != ' ' && *l != '\t'; l++)
;
*l = 0;
fprintf(stderr, _("Unrecognised option at line %d: '%s'\n"),
config_line_num, line);
return '?';
}
line += optlen;
while (*line == ' ' || *line == '\t' ||
(*line == '=' && this->has_arg && !ate_equals && ++ate_equals))
line++;
if (!this->has_arg && *line) {
fprintf(stderr, _("Option '%s' does not take an argument at line %d\n"),
this->name, config_line_num);
return '?';
} else if (this->has_arg && !*line) {
fprintf(stderr, _("Option '%s' requires an argument at line %d\n"),
this->name, config_line_num);
return '?';
}
config_line_num++;
*config_arg = line;
return this->val;
406
407
}
408
409
410
int main(int argc, char **argv)
{
411
struct openconnect_info *vpninfo;
412
struct utsname utsbuf;
413
struct sigaction sa;
414
int use_syslog = 0;
415
char *urlpath = NULL;
416
char *proxy = getenv("https_proxy");
417
int autoproxy = 0;
418
uid_t uid = getuid();
419
int opt;
420
421
char *pidfile = NULL;
FILE *fp = NULL;
422
char *config_arg;
423
424
#ifdef ENABLE_NLS
425
bindtextdomain("openconnect", LOCALEDIR);
426
427
428
setlocale(LC_ALL, "");
#endif
429
if (strcmp(openconnect_version_str, openconnect_binary_version)) {
430
431
fprintf(stderr, _("WARNING: This version of openconnect is %s but\n"
" the libopenconnect library is %s\n"),
432
openconnect_binary_version, openconnect_version_str);
433
434
}
435
openconnect_init_ssl();
436
437
438
vpninfo = malloc(sizeof(*vpninfo));
if (!vpninfo) {
439
fprintf(stderr, _("Failed to allocate vpninfo structure\n"));
440
441
442
exit(1);
}
memset(vpninfo, 0, sizeof(*vpninfo));
443
444
/* Set up some defaults */
445
vpninfo->tun_fd = vpninfo->ssl_fd = vpninfo->dtls_fd = vpninfo->new_dtls_fd = -1;
446
vpninfo->useragent = openconnect_create_useragent("Open AnyConnect VPN Agent");
447
vpninfo->mtu = 0;
448
vpninfo->deflate = 1;
449
vpninfo->dtls_attempt_period = 60;
450
vpninfo->max_qlen = 10;
451
vpninfo->reconnect_interval = RECONNECT_INTERVAL_MIN;
452
vpninfo->reconnect_timeout = 300;
453
454
vpninfo->uid_csd = 0;
vpninfo->uid_csd_given = 0;
455
vpninfo->validate_peer_cert = validate_peer_cert;
456
vpninfo->process_auth_form = process_auth_form;
457
vpninfo->cbdata = vpninfo;
458
vpninfo->cert_expire_warning = 60 * 86400;
459
vpninfo->vpnc_script = DEFAULT_VPNCSCRIPT;
460
vpninfo->cancel_fd = -1;
461
462
463
464
465
466
if (!uname(&utsbuf))
vpninfo->localname = utsbuf.nodename;
else
vpninfo->localname = "localhost";
467
468
while ((opt = next_option(argc, argv, &config_arg))) {
469
470
471
472
if (opt < 0)
break;
switch (opt) {
473
474
475
476
477
478
479
480
481
482
483
484
485
486
case OPT_CONFIGFILE:
if (config_file) {
fprintf(stderr, _("Cannot use 'config' option inside config file\n"));
exit(1);
}
config_file = fopen(config_arg, "r");
if (!config_file) {
fprintf(stderr, _("Cannot open config file '%s': %s\n"),
config_arg, strerror(errno));
exit(1);
}
config_line_num = 1;
/* The next option will come from the file... */
break;
487
case OPT_CAFILE:
488
vpninfo->cafile = keep_config_arg();
489
break;
490
case OPT_PIDFILE:
491
pidfile = keep_config_arg();
492
break;
493
case OPT_SERVERCERT:
494
vpninfo->servercert = keep_config_arg();
495
break;
496
case OPT_NO_DTLS:
497
vpninfo->dtls_attempt_period = 0;
498
break;
499
case OPT_COOKIEONLY:
500
501
cookieonly = 1;
break;
502
case OPT_PRINTCOOKIE:
503
504
cookieonly = 2;
break;
505
506
507
case OPT_AUTHENTICATE:
cookieonly = 3;
break;
508
case OPT_COOKIE_ON_STDIN:
509
read_stdin(&vpninfo->cookie);
510
511
512
513
/* If the cookie is empty, ignore it */
if (! *vpninfo->cookie) {
vpninfo->cookie = NULL;
}
514
break;
515
case OPT_PASSWORD_ON_STDIN:
516
read_stdin(&vpninfo->password);
517
break;
518
case OPT_NO_PASSWD:
519
520
vpninfo->nopasswd = 1;
break;
521
522
523
case OPT_NON_INTER:
non_inter = 1;
break;
524
case OPT_RECONNECT_TIMEOUT:
525
vpninfo->reconnect_timeout = atoi(config_arg);
526
break;
527
case OPT_DTLS_CIPHERS:
528
vpninfo->dtls_ciphers = keep_config_arg();
529
break;
530
case OPT_AUTHGROUP:
531
vpninfo->authgroup = keep_config_arg();
532
break;
533
534
535
case 'b':
background = 1;
break;
536
case 'C':
537
vpninfo->cookie = keep_config_arg();
538
break;
539
case 'c':
540
vpninfo->cert = strdup(config_arg);
541
break;
542
case 'e':
543
vpninfo->cert_expire_warning = 86400 * atoi(config_arg);
544
break;
545
case 'k':
546
vpninfo->sslkey = strdup(config_arg);
547
break;
548
549
550
551
552
553
case 'd':
vpninfo->deflate = 1;
break;
case 'D':
vpninfo->deflate = 0;
break;
554
case 'g':
555
556
free(urlpath);
urlpath = strdup(config_arg);
557
break;
558
case 'h':
559
560
usage();
case 'i':
561
vpninfo->ifname = keep_config_arg();
562
break;
563
564
565
case 'l':
use_syslog = 1;
break;
566
case 'm':
567
vpninfo->mtu = atol(config_arg);
568
if (vpninfo->mtu < 576) {
569
fprintf(stderr, _("MTU %d too small\n"), vpninfo->mtu);
570
vpninfo->mtu = 576;
571
572
}
break;
573
574
575
576
577
578
579
case OPT_BASEMTU:
vpninfo->basemtu = atol(config_arg);
if (vpninfo->basemtu < 576) {
fprintf(stderr, _("MTU %d too small\n"), vpninfo->basemtu);
vpninfo->basemtu = 576;
}
break;
580
case 'p':
581
vpninfo->cert_password = strdup(config_arg);
582
break;
583
case 'P':
584
proxy = keep_config_arg();
585
autoproxy = 0;
586
break;
587
case OPT_NO_PROXY:
588
autoproxy = 0;
589
proxy = NULL;
590
case OPT_LIBPROXY:
591
592
593
autoproxy = 1;
proxy = NULL;
break;
594
case OPT_NO_HTTP_KEEPALIVE:
595
596
597
fprintf(stderr,
_("Disabling all HTTP connection re-use due to --no-http-keepalive option.\n"
"If this helps, please report to <openconnect-devel@lists.infradead.org>.\n"));
598
599
vpninfo->no_http_keepalive = 1;
break;
600
case OPT_NO_CERT_CHECK:
601
602
nocertcheck = 1;
break;
603
case 's':
604
vpninfo->vpnc_script = keep_config_arg();
605
break;
606
607
608
case 'S':
vpninfo->script_tun = 1;
break;
609
case 'u':
610
vpninfo->username = keep_config_arg();
611
break;
612
613
case 'U': {
char *strend;
614
uid = strtol(config_arg, &strend, 0);
615
if (strend[0]) {
616
struct passwd *pw = getpwnam(config_arg);
617
if (!pw) {
618
fprintf(stderr, _("Invalid user \"%s\"\n"),
619
config_arg);
620
621
exit(1);
}
622
623
624
625
uid = pw->pw_uid;
}
break;
}
626
case OPT_CSD_USER: {
627
char *strend;
628
vpninfo->uid_csd = strtol(config_arg, &strend, 0);
629
if (strend[0]) {
630
struct passwd *pw = getpwnam(config_arg);
631
if (!pw) {
632
fprintf(stderr, _("Invalid user \"%s\"\n"),
633
config_arg);
634
635
636
exit(1);
}
vpninfo->uid_csd = pw->pw_uid;
637
}
638
vpninfo->uid_csd_given = 1;
639
640
break;
}
641
case OPT_CSD_WRAPPER:
642
vpninfo->csd_wrapper = keep_config_arg();
643
break;
644
case OPT_DISABLE_IPV6:
645
646
vpninfo->disable_ipv6 = 1;
break;
647
case 'Q':
648
vpninfo->max_qlen = atol(config_arg);
649
if (!vpninfo->max_qlen) {
650
fprintf(stderr, _("Queue length zero not permitted; using 1\n"));
651
652
653
vpninfo->max_qlen = 1;
}
break;
654
655
656
case 'q':
verbose = PRG_ERR;
break;
657
case 'v':
658
verbose = PRG_TRACE;
659
break;
660
case 'V':
661
printf(_("OpenConnect version %s\n"), openconnect_version_str);
662
print_build_opts();
663
exit(0);
664
case 'x':
665
vpninfo->xmlconfig = keep_config_arg();
666
vpninfo->write_new_config = write_new_config;
667
break;
668
case OPT_KEY_PASSWORD_FROM_FSID:
669
do_passphrase_from_fsid = 1;
670
break;
671
case OPT_USERAGENT:
672
free(vpninfo->useragent);
673
vpninfo->useragent = strdup(config_arg);
674
break;
675
case OPT_FORCE_DPD:
676
vpninfo->dtls_times.dpd = vpninfo->ssl_times.dpd = atoi(config_arg);
677
break;
678
679
default:
usage();
680
681
}
}
682
683
684
685
686
if (optind < argc - 1) {
fprintf(stderr, _("Too many arguments on command line\n"));
usage();
} else if (optind > argc - 1) {
687
fprintf(stderr, _("No server specified\n"));
688
usage();
689
690
}
691
692
693
if (!vpninfo->sslkey)
vpninfo->sslkey = vpninfo->cert;
694
695
vpninfo->progress = write_progress;
696
if (autoproxy) {
697
#ifdef LIBPROXY_HDR
698
vpninfo->proxy_factory = px_proxy_factory_new();
699
#else
700
fprintf(stderr, _("This version of openconnect was built without libproxy support\n"));
701
exit(1);
702
#endif
703
704
}
705
if (proxy && openconnect_set_http_proxy(vpninfo, strdup(proxy)))
706
707
exit(1);
708
if (use_syslog) {
709
#ifndef ANDROID
710
openlog("openconnect", LOG_PID, LOG_DAEMON);
711
#endif
712
713
vpninfo->progress = syslog_progress;
}
714
715
716
717
718
719
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handle_sigusr;
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
720
721
if (vpninfo->sslkey && do_passphrase_from_fsid)
722
openconnect_passphrase_from_fsid(vpninfo);
723
724
725
726
if (config_lookup_host(vpninfo, argv[optind]))
exit(1);
727
728
729
if (!vpninfo->hostname) {
char *url = strdup(argv[optind]);
730
if (openconnect_parse_url(vpninfo, url))
731
exit(1);
732
733
free(url);
734
}
735
736
737
738
739
740
741
742
743
744
/* Historically, the path in the URL superseded the one in the
* --usergroup argument, just because of the order in which they
* were processed. Preserve that behaviour. */
if (urlpath && !vpninfo->urlpath) {
vpninfo->urlpath = urlpath;
urlpath = NULL;
}
free(urlpath);
745
#ifdef SSL_UI
746
set_openssl_ui();
747
#endif
748
749
if (!vpninfo->cookie && openconnect_obtain_cookie(vpninfo)) {
750
fprintf(stderr, _("Failed to obtain WebVPN cookie\n"));
751
752
753
exit(1);
}
754
755
756
757
758
759
760
761
762
if (cookieonly == 3) {
/* --authenticate */
printf("COOKIE='%s'\n", vpninfo->cookie);
printf("HOST='%s'\n", vpninfo->hostname);
if (vpninfo->peer_cert) {
char buf[41] = {0, };
openconnect_get_cert_sha1(vpninfo, vpninfo->peer_cert, buf);
printf("FINGERPRINT='%s'\n", buf);
}
763
openconnect_vpninfo_free(vpninfo);
764
765
exit(0);
} else if (cookieonly) {
766
printf("%s\n", vpninfo->cookie);
767
if (cookieonly == 1) {
768
/* We use cookieonly=2 for 'print it and continue' */
769
openconnect_vpninfo_free(vpninfo);
770
exit(0);
771
}
772
}
773
if (make_cstp_connection(vpninfo)) {
774
fprintf(stderr, _("Creating SSL connection failed\n"));
775
776
exit(1);
}
777
778
if (setup_tun(vpninfo)) {
779
fprintf(stderr, _("Set up tun device failed\n"));
780
exit(1);
781
}
782
783
784
if (uid != getuid()) {
if (setuid(uid)) {
785
786
fprintf(stderr, _("Failed to set uid %ld\n"),
(long)uid);
787
788
789
exit(1);
}
}
790
791
if (vpninfo->dtls_attempt_period && setup_dtls(vpninfo))
792
fprintf(stderr, _("Set up DTLS failed; using SSL instead\n"));
793
794
vpn_progress(vpninfo, PRG_INFO,
795
796
797
798
799
800
801
_("Connected %s as %s%s%s, using %s\n"), vpninfo->ifname,
vpninfo->vpn_addr?:"",
(vpninfo->vpn_addr6 && vpninfo->vpn_addr)?" + ":"",
vpninfo->vpn_addr6?:"",
(vpninfo->dtls_fd == -1) ?
(vpninfo->deflate ? "SSL + deflate" : "SSL")
: "DTLS");
802
803
if (!vpninfo->vpnc_script) {
804
vpn_progress(vpninfo, PRG_INFO,
805
_("No --script argument provided; DNS and routing are not configured\n"));
806
807
808
vpn_progress(vpninfo, PRG_INFO,
_("See http://www.infradead.org/openconnect/vpnc-script.html\n"));
}
809
810
811
if (background) {
int pid;
812
813
814
815
816
817
818
/* Open the pidfile before forking, so we can report errors
more sanely. It's *possible* that we'll fail to write to
it, but very unlikely. */
if (pidfile != NULL) {
fp = fopen(pidfile, "w");
if (!fp) {
819
fprintf(stderr, _("Failed to open '%s' for write: %s\n"),
820
821
822
823
pidfile, strerror(errno));
exit(1);
}
}
824
if ((pid = fork())) {
825
826
827
828
if (fp) {
fprintf(fp, "%d\n", pid);
fclose(fp);
}
829
vpn_progress(vpninfo, PRG_INFO,
830
_("Continuing in background; pid %d\n"),
831
pid);
832
exit(0);
833
}
834
835
if (fp)
fclose(fp);
836
}
837
vpn_mainloop(vpninfo);
838
839
if (fp)
unlink(pidfile);
840
exit(1);
841
}
842
843
static int write_new_config(void *_vpninfo, char *buf, int buflen)
844
{
845
struct openconnect_info *vpninfo = _vpninfo;
846
int config_fd;
847
int err;
848
849
config_fd = open(vpninfo->xmlconfig, O_WRONLY|O_TRUNC|O_CREAT, 0644);
850
if (config_fd < 0) {
851
err = errno;
852
fprintf(stderr, _("Failed to open %s for write: %s\n"),
853
854
vpninfo->xmlconfig, strerror(err));
return -err;
855
856
857
}
/* FIXME: We should actually write to a new tempfile, then rename */
858
859
if(write(config_fd, buf, buflen) != buflen) {
err = errno;
860
fprintf(stderr, _("Failed to write config to %s: %s\n"),
861
862
863
864
865
vpninfo->xmlconfig, strerror(err));
return -err;
}
866
867
return 0;
}
868
869
void write_progress(void *_vpninfo, int level, const char *fmt, ...)
870
{
871
FILE *outf = level ? stdout : stderr;
872
873
va_list args;
874
875
876
if (cookieonly)
outf = stderr;
877
878
879
880
if (verbose >= level) {
va_start(args, fmt);
vfprintf(outf, fmt, args);
va_end(args);
881
fflush(outf);
882
883
}
}
884
885
886
887
888
889
890
891
892
893
#ifdef ANDROID
void syslog_progress(void *_vpninfo, int level, const char *fmt, ...)
{
static int l[4] = {
ANDROID_LOG_ERROR, /* PRG_ERR */
ANDROID_LOG_INFO, /* PRG_INFO */
ANDROID_LOG_DEBUG, /* PRG_DEBUG */
ANDROID_LOG_DEBUG /* PRG_TRACE */
};
894
va_list args, args2;
895
896
897
if (verbose >= level) {
va_start(args, fmt);
898
va_copy(args2, args);
899
__android_log_vprint(l[level], "openconnect", fmt, args);
900
901
902
/* Android wants it to stderr too, so the GUI can scrape
it and display it as well as going to syslog */
vfprintf(stderr, fmt, args2);
903
va_end(args);
904
va_end(args2);
905
906
907
}
}
#else /* !ANDROID */
908
void syslog_progress(void *_vpninfo, int level, const char *fmt, ...)
909
{
910
int priority = level ? LOG_INFO : LOG_NOTICE;
911
912
913
914
915
916
917
918
va_list args;
if (verbose >= level) {
va_start(args, fmt);
vsyslog(priority, fmt, args);
va_end(args);
}
}
919
#endif
920
921
922
struct accepted_cert {
struct accepted_cert *next;
923
char fingerprint[SHA1_SIZE * 2 + 1];
924
925
926
char host[0];
} *accepted_certs;
927
static int validate_peer_cert(void *_vpninfo, OPENCONNECT_X509 *peer_cert,
928
929
const char *reason)
{
930
struct openconnect_info *vpninfo = _vpninfo;
931
char fingerprint[SHA1_SIZE * 2 + 1];
932
933
934
struct accepted_cert *this;
int ret;
935
936
937
if (nocertcheck)
return 0;
938
ret = openconnect_get_cert_sha1(vpninfo, peer_cert, fingerprint);
939
940
941
942
943
944
945
946
947
948
if (ret)
return ret;
for (this = accepted_certs; this; this = this->next) {
if (!strcasecmp(this->host, vpninfo->hostname) &&
!strcasecmp(this->fingerprint, fingerprint))
return 0;
}
while (1) {
949
char buf[80];
950
char *details;
951
char *p;
952
953
fprintf(stderr, _("\nCertificate from VPN server \"%s\" failed verification.\n"
954
"Reason: %s\n"), vpninfo->hostname, reason);
955
956
957
958
if (non_inter)
return -EINVAL;
959
fprintf(stderr, _("Enter '%s' to accept, '%s' to abort; anything else to view: "),
960
961
_("yes"), _("no"));
if (!fgets(buf, sizeof(buf), stdin))
962
return -EINVAL;
963
964
965
p = strchr(buf, '\n');
if (p)
*p = 0;
966
967
if (!strcasecmp(buf, _("yes"))) {
968
969
970
971
972
973
974
975
976
977
struct accepted_cert *newcert = malloc(sizeof(*newcert) +
strlen(vpninfo->hostname) + 1);
if (newcert) {
newcert->next = accepted_certs;
accepted_certs = newcert;
strcpy(newcert->fingerprint, fingerprint);
strcpy(newcert->host, vpninfo->hostname);
}
return 0;
}
978
if (!strcasecmp(buf, _("no")))
979
980
return -EINVAL;
981
982
983
details = openconnect_get_cert_details(vpninfo, peer_cert);
fputs(details, stderr);
free(details);
984
fprintf(stderr, _("SHA1 fingerprint: %s\n"), fingerprint);
985
986
987
}
}
988
989
990
991
992
993
994
995
996
997
998
999
/* Return value:
* < 0, on error
* = 0, when form was parsed and POST required
* = 1, when response was cancelled by user
*/
static int process_auth_form(void *_vpninfo,
struct oc_auth_form *form)
{
struct openconnect_info *vpninfo = _vpninfo;
struct oc_form_opt *opt;
1000
char response[1024];