/
main.c
1170 lines (1049 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
static void print_build_opts(void)
{
183
184
185
186
187
188
189
190
191
192
193
194
const char *comma = ", ", *sep = comma + 1;
#if defined (OPENCONNECT_OPENSSL)
printf(_("Using OpenSSL. Features present:"));
#elif defined (OPENCONNECT_GNUTLS)
printf(_("Using GnuTLS. Features present:"));
#endif
if (openconnect_has_tss_blob_support()) {
printf("%sTPM", sep);
sep = comma;
}
195
#if defined (OPENCONNECT_OPENSSL) && defined (HAVE_ENGINE)
196
197
198
199
200
201
202
203
204
205
206
207
208
209
else {
printf("%sTPM (%s)", sep, _("OpenSSL ENGINE not present"));
sep = comma;
}
#endif
if (openconnect_has_pkcs11_support()) {
printf("%sPKCS#11", sep);
sep = comma;
}
#ifdef HAVE_DTLS
printf("%sDTLS", sep);
#if defined (OPENCONNECT_GNUTLS) && defined (DTLS_OPENSSL)
printf(" (%s)", _("using OpenSSL"));
210
#endif
211
printf("\n");
212
#else
213
printf(_("\nWARNING: No DTLS support in this binary. Performance will be impaired.\n"));
214
215
216
#endif
}
217
static void usage(void)
218
{
219
printf(_("Usage: openconnect [options] <server>\n"));
220
printf(_("Open client for Cisco AnyConnect VPN, version %s\n\n"), openconnect_version_str);
221
print_build_opts();
222
printf(" --config=CONFIGFILE %s\n", _("Read options from config file"));
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
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"));
242
printf(" --base-mtu=MTU %s\n", _("Indicate path MTU to/from server"));
243
244
245
246
247
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"));
248
#ifndef LIBPROXY_HDR
249
printf(" %s\n", _("(NOTE: libproxy disabled in this build)"));
250
#endif
251
252
253
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"));
254
printf(" %s: \"%s\"\n", _("default"), DEFAULT_VPNCSCRIPT);
255
256
257
258
259
260
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"));
261
printf(" --authenticate %s\n", _("Authenticate only and print login info"));
262
263
264
265
266
267
268
269
270
271
272
273
274
275
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"));
276
277
278
printf("\n");
helpmessage();
279
280
281
exit(1);
}
282
static void read_stdin(char **string)
283
284
285
{
char *c = malloc(100);
if (!c) {
286
fprintf(stderr, _("Allocation failure for string from stdin\n"));
287
288
289
exit(1);
}
if (!fgets(c, 100, stdin)) {
290
perror(_("fgets (stdin)"));
291
292
293
exit(1);
}
294
*string = c;
295
296
c = strchr(*string, '\n');
297
298
299
if (c)
*c = 0;
}
300
301
302
303
304
305
306
static void handle_sigusr(int sig)
{
if (sig == SIGUSR1)
verbose = PRG_TRACE;
else if (sig == SIGUSR2)
verbose = PRG_INFO;
}
307
308
309
310
static FILE *config_file = NULL;
static int config_line_num = 0;
311
312
313
314
315
316
317
318
319
320
321
322
/* 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)
323
324
325
static int next_option(int argc, char **argv, char **config_arg)
{
326
327
328
329
330
331
332
333
334
335
336
337
338
/* 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,
339
"bC:c:e:Ddg:hi:k:lpP:Q:qSs:U:u:Vvx:",
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
406
407
408
409
410
411
412
413
414
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;
415
416
}
417
418
419
int main(int argc, char **argv)
{
420
struct openconnect_info *vpninfo;
421
struct utsname utsbuf;
422
struct sigaction sa;
423
int use_syslog = 0;
424
char *urlpath = NULL;
425
char *proxy = getenv("https_proxy");
426
int autoproxy = 0;
427
uid_t uid = getuid();
428
int opt;
429
430
char *pidfile = NULL;
FILE *fp = NULL;
431
char *config_arg;
432
433
#ifdef ENABLE_NLS
434
bindtextdomain("openconnect", LOCALEDIR);
435
436
437
setlocale(LC_ALL, "");
#endif
438
if (strcmp(openconnect_version_str, openconnect_binary_version)) {
439
440
fprintf(stderr, _("WARNING: This version of openconnect is %s but\n"
" the libopenconnect library is %s\n"),
441
openconnect_binary_version, openconnect_version_str);
442
443
}
444
openconnect_init_ssl();
445
446
447
vpninfo = malloc(sizeof(*vpninfo));
if (!vpninfo) {
448
fprintf(stderr, _("Failed to allocate vpninfo structure\n"));
449
450
451
exit(1);
}
memset(vpninfo, 0, sizeof(*vpninfo));
452
453
/* Set up some defaults */
454
vpninfo->tun_fd = vpninfo->ssl_fd = vpninfo->dtls_fd = vpninfo->new_dtls_fd = -1;
455
vpninfo->useragent = openconnect_create_useragent("Open AnyConnect VPN Agent");
456
vpninfo->mtu = 0;
457
vpninfo->deflate = 1;
458
vpninfo->dtls_attempt_period = 60;
459
vpninfo->max_qlen = 10;
460
vpninfo->reconnect_interval = RECONNECT_INTERVAL_MIN;
461
vpninfo->reconnect_timeout = 300;
462
463
vpninfo->uid_csd = 0;
vpninfo->uid_csd_given = 0;
464
vpninfo->validate_peer_cert = validate_peer_cert;
465
vpninfo->process_auth_form = process_auth_form;
466
vpninfo->cbdata = vpninfo;
467
vpninfo->cert_expire_warning = 60 * 86400;
468
vpninfo->vpnc_script = DEFAULT_VPNCSCRIPT;
469
vpninfo->cancel_fd = -1;
470
471
472
473
474
475
if (!uname(&utsbuf))
vpninfo->localname = utsbuf.nodename;
else
vpninfo->localname = "localhost";
476
477
while ((opt = next_option(argc, argv, &config_arg))) {
478
479
480
481
if (opt < 0)
break;
switch (opt) {
482
483
484
485
486
487
488
489
490
491
492
493
494
495
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;
496
case OPT_CAFILE:
497
vpninfo->cafile = keep_config_arg();
498
break;
499
case OPT_PIDFILE:
500
pidfile = keep_config_arg();
501
break;
502
case OPT_SERVERCERT:
503
vpninfo->servercert = keep_config_arg();
504
break;
505
case OPT_NO_DTLS:
506
vpninfo->dtls_attempt_period = 0;
507
break;
508
case OPT_COOKIEONLY:
509
510
cookieonly = 1;
break;
511
case OPT_PRINTCOOKIE:
512
513
cookieonly = 2;
break;
514
515
516
case OPT_AUTHENTICATE:
cookieonly = 3;
break;
517
case OPT_COOKIE_ON_STDIN:
518
read_stdin(&vpninfo->cookie);
519
520
521
522
/* If the cookie is empty, ignore it */
if (! *vpninfo->cookie) {
vpninfo->cookie = NULL;
}
523
break;
524
case OPT_PASSWORD_ON_STDIN:
525
read_stdin(&vpninfo->password);
526
break;
527
case OPT_NO_PASSWD:
528
529
vpninfo->nopasswd = 1;
break;
530
531
532
case OPT_NON_INTER:
non_inter = 1;
break;
533
case OPT_RECONNECT_TIMEOUT:
534
vpninfo->reconnect_timeout = atoi(config_arg);
535
break;
536
case OPT_DTLS_CIPHERS:
537
vpninfo->dtls_ciphers = keep_config_arg();
538
break;
539
case OPT_AUTHGROUP:
540
vpninfo->authgroup = keep_config_arg();
541
break;
542
543
544
case 'b':
background = 1;
break;
545
case 'C':
546
vpninfo->cookie = keep_config_arg();
547
break;
548
case 'c':
549
vpninfo->cert = strdup(config_arg);
550
break;
551
case 'e':
552
vpninfo->cert_expire_warning = 86400 * atoi(config_arg);
553
break;
554
case 'k':
555
vpninfo->sslkey = strdup(config_arg);
556
break;
557
558
559
560
561
562
case 'd':
vpninfo->deflate = 1;
break;
case 'D':
vpninfo->deflate = 0;
break;
563
case 'g':
564
565
free(urlpath);
urlpath = strdup(config_arg);
566
break;
567
case 'h':
568
569
usage();
case 'i':
570
vpninfo->ifname = keep_config_arg();
571
break;
572
573
574
case 'l':
use_syslog = 1;
break;
575
case 'm':
576
vpninfo->mtu = atol(config_arg);
577
if (vpninfo->mtu < 576) {
578
fprintf(stderr, _("MTU %d too small\n"), vpninfo->mtu);
579
vpninfo->mtu = 576;
580
581
}
break;
582
583
584
585
586
587
588
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;
589
case 'p':
590
vpninfo->cert_password = strdup(config_arg);
591
break;
592
case 'P':
593
proxy = keep_config_arg();
594
autoproxy = 0;
595
break;
596
case OPT_NO_PROXY:
597
autoproxy = 0;
598
proxy = NULL;
599
case OPT_LIBPROXY:
600
601
602
autoproxy = 1;
proxy = NULL;
break;
603
case OPT_NO_HTTP_KEEPALIVE:
604
605
606
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"));
607
608
vpninfo->no_http_keepalive = 1;
break;
609
case OPT_NO_CERT_CHECK:
610
611
nocertcheck = 1;
break;
612
case 's':
613
vpninfo->vpnc_script = keep_config_arg();
614
break;
615
616
617
case 'S':
vpninfo->script_tun = 1;
break;
618
case 'u':
619
vpninfo->username = keep_config_arg();
620
break;
621
622
case 'U': {
char *strend;
623
uid = strtol(config_arg, &strend, 0);
624
if (strend[0]) {
625
struct passwd *pw = getpwnam(config_arg);
626
if (!pw) {
627
fprintf(stderr, _("Invalid user \"%s\"\n"),
628
config_arg);
629
630
exit(1);
}
631
632
633
634
uid = pw->pw_uid;
}
break;
}
635
case OPT_CSD_USER: {
636
char *strend;
637
vpninfo->uid_csd = strtol(config_arg, &strend, 0);
638
if (strend[0]) {
639
struct passwd *pw = getpwnam(config_arg);
640
if (!pw) {
641
fprintf(stderr, _("Invalid user \"%s\"\n"),
642
config_arg);
643
644
645
exit(1);
}
vpninfo->uid_csd = pw->pw_uid;
646
}
647
vpninfo->uid_csd_given = 1;
648
649
break;
}
650
case OPT_CSD_WRAPPER:
651
vpninfo->csd_wrapper = keep_config_arg();
652
break;
653
case OPT_DISABLE_IPV6:
654
655
vpninfo->disable_ipv6 = 1;
break;
656
case 'Q':
657
vpninfo->max_qlen = atol(config_arg);
658
if (!vpninfo->max_qlen) {
659
fprintf(stderr, _("Queue length zero not permitted; using 1\n"));
660
661
662
vpninfo->max_qlen = 1;
}
break;
663
664
665
case 'q':
verbose = PRG_ERR;
break;
666
case 'v':
667
verbose = PRG_TRACE;
668
break;
669
case 'V':
670
printf(_("OpenConnect version %s\n"), openconnect_version_str);
671
print_build_opts();
672
exit(0);
673
case 'x':
674
vpninfo->xmlconfig = keep_config_arg();
675
vpninfo->write_new_config = write_new_config;
676
break;
677
case OPT_KEY_PASSWORD_FROM_FSID:
678
do_passphrase_from_fsid = 1;
679
break;
680
case OPT_USERAGENT:
681
free(vpninfo->useragent);
682
vpninfo->useragent = strdup(config_arg);
683
break;
684
case OPT_FORCE_DPD:
685
vpninfo->dtls_times.dpd = vpninfo->ssl_times.dpd = atoi(config_arg);
686
break;
687
688
default:
usage();
689
690
}
}
691
692
693
694
695
if (optind < argc - 1) {
fprintf(stderr, _("Too many arguments on command line\n"));
usage();
} else if (optind > argc - 1) {
696
fprintf(stderr, _("No server specified\n"));
697
usage();
698
699
}
700
701
702
if (!vpninfo->sslkey)
vpninfo->sslkey = vpninfo->cert;
703
704
vpninfo->progress = write_progress;
705
if (autoproxy) {
706
#ifdef LIBPROXY_HDR
707
vpninfo->proxy_factory = px_proxy_factory_new();
708
#else
709
fprintf(stderr, _("This version of openconnect was built without libproxy support\n"));
710
exit(1);
711
#endif
712
713
}
714
if (proxy && openconnect_set_http_proxy(vpninfo, strdup(proxy)))
715
716
exit(1);
717
if (use_syslog) {
718
#ifndef ANDROID
719
openlog("openconnect", LOG_PID, LOG_DAEMON);
720
#endif
721
722
vpninfo->progress = syslog_progress;
}
723
724
725
726
727
728
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handle_sigusr;
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
729
730
if (vpninfo->sslkey && do_passphrase_from_fsid)
731
openconnect_passphrase_from_fsid(vpninfo);
732
733
734
735
if (config_lookup_host(vpninfo, argv[optind]))
exit(1);
736
737
738
if (!vpninfo->hostname) {
char *url = strdup(argv[optind]);
739
if (openconnect_parse_url(vpninfo, url))
740
exit(1);
741
742
free(url);
743
}
744
745
746
747
748
749
750
751
752
753
/* 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);
754
#ifdef SSL_UI
755
set_openssl_ui();
756
#endif
757
758
if (!vpninfo->cookie && openconnect_obtain_cookie(vpninfo)) {
759
fprintf(stderr, _("Failed to obtain WebVPN cookie\n"));
760
761
762
exit(1);
}
763
764
765
766
767
768
769
770
771
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);
}
772
openconnect_vpninfo_free(vpninfo);
773
774
exit(0);
} else if (cookieonly) {
775
printf("%s\n", vpninfo->cookie);
776
if (cookieonly == 1) {
777
/* We use cookieonly=2 for 'print it and continue' */
778
openconnect_vpninfo_free(vpninfo);
779
exit(0);
780
}
781
}
782
if (make_cstp_connection(vpninfo)) {
783
fprintf(stderr, _("Creating SSL connection failed\n"));
784
785
exit(1);
}
786
787
if (setup_tun(vpninfo)) {
788
fprintf(stderr, _("Set up tun device failed\n"));
789
exit(1);
790
}
791
792
793
if (uid != getuid()) {
if (setuid(uid)) {
794
795
fprintf(stderr, _("Failed to set uid %ld\n"),
(long)uid);
796
797
798
exit(1);
}
}
799
800
if (vpninfo->dtls_attempt_period && setup_dtls(vpninfo))
801
fprintf(stderr, _("Set up DTLS failed; using SSL instead\n"));
802
803
vpn_progress(vpninfo, PRG_INFO,
804
805
806
807
808
809
810
_("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");
811
812
if (!vpninfo->vpnc_script) {
813
vpn_progress(vpninfo, PRG_INFO,
814
_("No --script argument provided; DNS and routing are not configured\n"));
815
816
817
vpn_progress(vpninfo, PRG_INFO,
_("See http://www.infradead.org/openconnect/vpnc-script.html\n"));
}
818
819
820
if (background) {
int pid;
821
822
823
824
825
826
827
/* 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) {
828
fprintf(stderr, _("Failed to open '%s' for write: %s\n"),
829
830
831
832
pidfile, strerror(errno));
exit(1);
}
}
833
if ((pid = fork())) {
834
835
836
837
if (fp) {
fprintf(fp, "%d\n", pid);
fclose(fp);
}
838
vpn_progress(vpninfo, PRG_INFO,
839
_("Continuing in background; pid %d\n"),
840
pid);
841
exit(0);
842
}
843
844
if (fp)
fclose(fp);
845
}
846
vpn_mainloop(vpninfo);
847
848
if (fp)
unlink(pidfile);
849
exit(1);
850
}
851
852
static int write_new_config(void *_vpninfo, char *buf, int buflen)
853
{
854
struct openconnect_info *vpninfo = _vpninfo;
855
int config_fd;
856
int err;
857
858
config_fd = open(vpninfo->xmlconfig, O_WRONLY|O_TRUNC|O_CREAT, 0644);
859
if (config_fd < 0) {
860
err = errno;
861
fprintf(stderr, _("Failed to open %s for write: %s\n"),
862
863
vpninfo->xmlconfig, strerror(err));
return -err;
864
865
866
}
/* FIXME: We should actually write to a new tempfile, then rename */
867
868
if(write(config_fd, buf, buflen) != buflen) {
err = errno;
869
fprintf(stderr, _("Failed to write config to %s: %s\n"),
870
871
872
873
874
vpninfo->xmlconfig, strerror(err));
return -err;
}
875
876
return 0;
}
877
878
void write_progress(void *_vpninfo, int level, const char *fmt, ...)
879
{
880
FILE *outf = level ? stdout : stderr;
881
882
va_list args;
883
884
885
if (cookieonly)
outf = stderr;
886
887
888
889
if (verbose >= level) {
va_start(args, fmt);
vfprintf(outf, fmt, args);
va_end(args);
890
fflush(outf);
891
892
}
}
893
894
895
896
897
898
899
900
901
902
#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 */
};
903
va_list args, args2;
904
905
906
if (verbose >= level) {
va_start(args, fmt);
907
va_copy(args2, args);
908
__android_log_vprint(l[level], "openconnect", fmt, args);
909
910
911
/* 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);
912
va_end(args);
913
va_end(args2);
914
915
916
}
}
#else /* !ANDROID */
917
void syslog_progress(void *_vpninfo, int level, const char *fmt, ...)
918
{
919
int priority = level ? LOG_INFO : LOG_NOTICE;
920
921
922
923
924
925
926
927
va_list args;
if (verbose >= level) {
va_start(args, fmt);
vsyslog(priority, fmt, args);
va_end(args);
}
}
928
#endif
929
930
931
struct accepted_cert {
struct accepted_cert *next;
932
char fingerprint[SHA1_SIZE * 2 + 1];
933
934
935
char host[0];
} *accepted_certs;
936
static int validate_peer_cert(void *_vpninfo, OPENCONNECT_X509 *peer_cert,
937
938
const char *reason)
{
939
struct openconnect_info *vpninfo = _vpninfo;
940
char fingerprint[SHA1_SIZE * 2 + 1];
941
942
943
struct accepted_cert *this;
int ret;
944
945
946
if (nocertcheck)
return 0;
947
ret = openconnect_get_cert_sha1(vpninfo, peer_cert, fingerprint);
948
949
950
951
952
953
954
955
956
957
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) {
958
char buf[80];
959
char *details;
960
char *p;
961
962
fprintf(stderr, _("\nCertificate from VPN server \"%s\" failed verification.\n"
963
"Reason: %s\n"), vpninfo->hostname, reason);
964
965
966
967
if (non_inter)
return -EINVAL;
968
fprintf(stderr, _("Enter '%s' to accept, '%s' to abort; anything else to view: "),
969
970
_("yes"), _("no"));
if (!fgets(buf, sizeof(buf), stdin))
971
return -EINVAL;
972
973
974
p = strchr(buf, '\n');
if (p)
*p = 0;
975
976
if (!strcasecmp(buf, _("yes"))) {
977
978
979
980
981
982
983
984
985
986
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;
}
987
if (!strcasecmp(buf, _("no")))
988
989
return -EINVAL;
990
991
992
details = openconnect_get_cert_details(vpninfo, peer_cert);
fputs(details, stderr);
free(details);
993
fprintf(stderr, _("SHA1 fingerprint: %s\n"), fingerprint);
994
995
996
}
}
997
998
999
1000
/* Return value:
* < 0, on error