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