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