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