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