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