2 * OpenConnect (SSL + DTLS) VPN client
4 * Copyright © 2008-2012 Intel Corporation.
5 * Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
7 * Author: David Woodhouse <dwmw2@infradead.org>
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.
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.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to:
21 * Free Software Foundation, Inc.
22 * 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301 USA
27 /* Various BSD systems require this for getline() to be visible */
33 #include <android/log.h>
48 #include <sys/utsname.h>
49 #include <sys/types.h>
56 #include "openconnect-internal.h"
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,
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);
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
76 #undef openconnect_version_str
78 int verbose = PRG_INFO;
80 int do_passphrase_from_fsid;
86 OPT_AUTHENTICATE = 0x100,
98 OPT_KEY_PASSWORD_FROM_FSID,
102 OPT_NO_HTTP_KEEPALIVE,
106 OPT_PASSWORD_ON_STDIN,
108 OPT_RECONNECT_TIMEOUT,
119 * The 'name' field in Solaris 'struct option' lacks the 'const', and causes
120 * lots of warnings unless we cast it... https://www.illumos.org/issues/1881
122 #define OPTION(name, arg, abbrev) {(char *)name, arg, NULL, abbrev}
124 #define OPTION(name, arg, abbrev) {name, arg, NULL, abbrev}
127 static struct option long_options[] = {
128 OPTION("background", 0, 'b'),
129 OPTION("pid-file", 1, OPT_PIDFILE),
130 OPTION("certificate", 1, 'c'),
131 OPTION("sslkey", 1, 'k'),
132 OPTION("cookie", 1, 'C'),
133 OPTION("deflate", 0, 'd'),
134 OPTION("no-deflate", 0, 'D'),
135 OPTION("cert-expire-warning", 1, 'e'),
136 OPTION("usergroup", 1, 'g'),
137 OPTION("help", 0, 'h'),
138 OPTION("interface", 1, 'i'),
139 OPTION("mtu", 1, 'm'),
140 OPTION("base-mtu", 1, OPT_BASEMTU),
141 OPTION("setuid", 1, 'U'),
142 OPTION("script", 1, 's'),
143 OPTION("script-tun", 0, 'S'),
144 OPTION("syslog", 0, 'l'),
145 OPTION("key-password", 1, 'p'),
146 OPTION("proxy", 1, 'P'),
147 OPTION("user", 1, 'u'),
148 OPTION("verbose", 0, 'v'),
149 OPTION("version", 0, 'V'),
150 OPTION("cafile", 1, OPT_CAFILE),
151 OPTION("config", 1, OPT_CONFIGFILE),
152 OPTION("no-dtls", 0, OPT_NO_DTLS),
153 OPTION("authenticate", 0, OPT_AUTHENTICATE),
154 OPTION("cookieonly", 0, OPT_COOKIEONLY),
155 OPTION("printcookie", 0, OPT_PRINTCOOKIE),
156 OPTION("quiet", 0, 'q'),
157 OPTION("queue-len", 1, 'Q'),
158 OPTION("xmlconfig", 1, 'x'),
159 OPTION("cookie-on-stdin", 0, OPT_COOKIE_ON_STDIN),
160 OPTION("passwd-on-stdin", 0, OPT_PASSWORD_ON_STDIN),
161 OPTION("no-passwd", 0, OPT_NO_PASSWD),
162 OPTION("reconnect-timeout", 1, OPT_RECONNECT_TIMEOUT),
163 OPTION("dtls-ciphers", 1, OPT_DTLS_CIPHERS),
164 OPTION("authgroup", 1, OPT_AUTHGROUP),
165 OPTION("servercert", 1, OPT_SERVERCERT),
166 OPTION("key-password-from-fsid", 0, OPT_KEY_PASSWORD_FROM_FSID),
167 OPTION("useragent", 1, OPT_USERAGENT),
168 OPTION("csd-user", 1, OPT_CSD_USER),
169 OPTION("csd-wrapper", 1, OPT_CSD_WRAPPER),
170 OPTION("disable-ipv6", 0, OPT_DISABLE_IPV6),
171 OPTION("no-proxy", 0, OPT_NO_PROXY),
172 OPTION("libproxy", 0, OPT_LIBPROXY),
173 OPTION("no-http-keepalive", 0, OPT_NO_HTTP_KEEPALIVE),
174 OPTION("no-cert-check", 0, OPT_NO_CERT_CHECK),
175 OPTION("force-dpd", 1, OPT_FORCE_DPD),
176 OPTION("non-inter", 0, OPT_NON_INTER),
177 OPTION("dtls-local-port", 1, OPT_DTLS_LOCAL_PORT),
178 OPTION("stoken", 2, OPT_STOKEN),
179 OPTION("os", 1, OPT_OS),
183 static void helpmessage(void)
185 printf(_("For assistance with OpenConnect, please see the web page at\n"
186 " http://www.infradead.org/openconnect/mail.html\n"));
189 static void print_build_opts(void)
191 const char *comma = ", ", *sep = comma + 1;
193 #if defined (OPENCONNECT_OPENSSL)
194 printf(_("Using OpenSSL. Features present:"));
195 #elif defined (OPENCONNECT_GNUTLS)
196 printf(_("Using GnuTLS. Features present:"));
199 if (openconnect_has_tss_blob_support()) {
200 printf("%sTPM", sep);
203 #if defined (OPENCONNECT_OPENSSL) && defined (HAVE_ENGINE)
205 printf("%sTPM (%s)", sep, _("OpenSSL ENGINE not present"));
209 if (openconnect_has_pkcs11_support()) {
210 printf("%sPKCS#11", sep);
213 if (openconnect_has_stoken_support()) {
214 printf("%sSoftware token", sep);
219 printf("%sDTLS", sep);
220 #if defined (OPENCONNECT_GNUTLS) && defined (DTLS_OPENSSL)
221 printf(" (%s)", _("using OpenSSL"));
225 printf(_("\nWARNING: No DTLS support in this binary. Performance will be impaired.\n"));
229 static void usage(void)
231 printf(_("Usage: openconnect [options] <server>\n"));
232 printf(_("Open client for Cisco AnyConnect VPN, version %s\n\n"), openconnect_version_str);
234 printf(" --config=CONFIGFILE %s\n", _("Read options from config file"));
235 printf(" -b, --background %s\n", _("Continue in background after startup"));
236 printf(" --pid-file=PIDFILE %s\n", _("Write the daemon's PID to this file"));
237 printf(" -c, --certificate=CERT %s\n", _("Use SSL client certificate CERT"));
238 printf(" -e, --cert-expire-warning=DAYS %s\n", _("Warn when certificate lifetime < DAYS"));
239 printf(" -k, --sslkey=KEY %s\n", _("Use SSL private key file KEY"));
240 printf(" -C, --cookie=COOKIE %s\n", _("Use WebVPN cookie COOKIE"));
241 printf(" --cookie-on-stdin %s\n", _("Read cookie from standard input"));
242 printf(" -d, --deflate %s\n", _("Enable compression (default)"));
243 printf(" -D, --no-deflate %s\n", _("Disable compression"));
244 printf(" --force-dpd=INTERVAL %s\n", _("Set minimum Dead Peer Detection interval"));
245 printf(" -g, --usergroup=GROUP %s\n", _("Set login usergroup"));
246 printf(" -h, --help %s\n", _("Display help text"));
247 printf(" -i, --interface=IFNAME %s\n", _("Use IFNAME for tunnel interface"));
248 printf(" -l, --syslog %s\n", _("Use syslog for progress messages"));
249 printf(" -U, --setuid=USER %s\n", _("Drop privileges after connecting"));
250 printf(" --csd-user=USER %s\n", _("Drop privileges during CSD execution"));
251 printf(" --csd-wrapper=SCRIPT %s\n", _("Run SCRIPT instead of CSD binary"));
252 printf(" -m, --mtu=MTU %s\n", _("Request MTU from server"));
253 printf(" --base-mtu=MTU %s\n", _("Indicate path MTU to/from server"));
254 printf(" -p, --key-password=PASS %s\n", _("Set key passphrase or TPM SRK PIN"));
255 printf(" --key-password-from-fsid %s\n", _("Key passphrase is fsid of file system"));
256 printf(" -P, --proxy=URL %s\n", _("Set proxy server"));
257 printf(" --no-proxy %s\n", _("Disable proxy"));
258 printf(" --libproxy %s\n", _("Use libproxy to automatically configure proxy"));
260 printf(" %s\n", _("(NOTE: libproxy disabled in this build)"));
262 printf(" -q, --quiet %s\n", _("Less output"));
263 printf(" -Q, --queue-len=LEN %s\n", _("Set packet queue limit to LEN pkts"));
264 printf(" -s, --script=SCRIPT %s\n", _("Shell command line for using a vpnc-compatible config script"));
265 printf(" %s: \"%s\"\n", _("default"), DEFAULT_VPNCSCRIPT);
266 printf(" -S, --script-tun %s\n", _("Pass traffic to 'script' program, not tun"));
267 printf(" -u, --user=NAME %s\n", _("Set login username"));
268 printf(" -V, --version %s\n", _("Report version number"));
269 printf(" -v, --verbose %s\n", _("More output"));
270 printf(" -x, --xmlconfig=CONFIG %s\n", _("XML config file"));
271 printf(" --authgroup=GROUP %s\n", _("Choose authentication login selection"));
272 printf(" --authenticate %s\n", _("Authenticate only and print login info"));
273 printf(" --cookieonly %s\n", _("Fetch webvpn cookie only; don't connect"));
274 printf(" --printcookie %s\n", _("Print webvpn cookie before connecting"));
275 printf(" --cafile=FILE %s\n", _("Cert file for server verification"));
276 printf(" --disable-ipv6 %s\n", _("Do not ask for IPv6 connectivity"));
277 printf(" --dtls-ciphers=LIST %s\n", _("OpenSSL ciphers to support for DTLS"));
278 printf(" --no-dtls %s\n", _("Disable DTLS"));
279 printf(" --no-http-keepalive %s\n", _("Disable HTTP connection re-use"));
280 printf(" --no-passwd %s\n", _("Disable password/SecurID authentication"));
281 printf(" --no-cert-check %s\n", _("Do not require server SSL cert to be valid"));
282 printf(" --non-inter %s\n", _("Do not expect user input; exit if it is required"));
283 printf(" --passwd-on-stdin %s\n", _("Read password from standard input"));
284 printf(" --stoken[=TOKENSTRING] %s\n", _("Use software token to generate password"));
285 #ifndef LIBSTOKEN_HDR
286 printf(" %s\n", _("(NOTE: libstoken disabled in this build)"));
288 printf(" --reconnect-timeout %s\n", _("Connection retry timeout in seconds"));
289 printf(" --servercert=FINGERPRINT %s\n", _("Server's certificate SHA1 fingerprint"));
290 printf(" --useragent=STRING %s\n", _("HTTP header User-Agent: field"));
291 printf(" --os=STRING %s\n", _("OS type (linux,linux-64,mac,win) to report"));
292 printf(" --dtls-local-port=PORT %s\n", _("Set local port for DTLS datagrams"));
299 static void read_stdin(char **string)
301 char *c = malloc(100);
303 fprintf(stderr, _("Allocation failure for string from stdin\n"));
306 if (!fgets(c, 100, stdin)) {
307 perror(_("fgets (stdin)"));
313 c = strchr(*string, '\n');
317 static void handle_sigusr(int sig)
321 else if (sig == SIGUSR2)
325 static FILE *config_file = NULL;
326 static int config_line_num = 0;
328 /* There are three ways to handle config_arg:
330 * 1. We only care about it transiently and it can be lost entirely
331 * (e.g. vpninfo->reconnect_timeout = atoi(config_arg);
332 * 2. We need to keep it, but it's a static string and will never be freed
333 * so when it's part of argv[] we can use it in place, but when it comes
334 * from a file we have to strdup() because otherwise it'll be overwritten.
335 * For this we use the keep_config_arg() macro below.
336 * 3. It may be freed during normal operation, so we have to use strdup()
337 * even when it's an option from argv[]. (e.g. vpninfo->cert_password).
339 #define keep_config_arg() (config_file && config_arg ? strdup(config_arg) : config_arg)
341 static int next_option(int argc, char **argv, char **config_arg)
343 /* These get re-used */
344 static char *line_buf = NULL;
345 static size_t line_size = 0;
355 opt = getopt_long(argc, argv,
356 "bC:c:e:Ddg:hi:k:lp:P:Q:qSs:U:u:Vvx:",
359 *config_arg = optarg;
363 llen = getline(&line_buf, &line_size, config_file);
365 if (feof(config_file)) {
370 fprintf(stderr, _("Failed to get line from config file: %s\n"),
376 /* Strip the trailing newline (coping with DOS newlines) */
377 if (llen && line[llen-1] == '\n')
379 if (llen && line[llen-1] == '\r')
382 /* Skip and leading whitespace */
383 while (line[0] == ' ' || line[0] == '\t' || line[0] == '\r')
386 /* Ignore comments and empty lines */
387 if (!line[0] || line[0] == '#') {
392 /* Try to match on a known option... naïvely. This could be improved. */
393 for (this = long_options; this->name; this++) {
394 optlen = strlen(this->name);
395 /* If the option isn't followed by whitespace or NUL, or
396 perhaps an equals sign if the option takes an argument,
397 then it's not a match */
398 if (!strncmp(this->name, line, optlen) &&
399 (!line[optlen] || line[optlen] == ' ' || line[optlen] == '\t' ||
400 line[optlen] == '='))
406 for (l = line; *l && *l != ' ' && *l != '\t'; l++)
410 fprintf(stderr, _("Unrecognised option at line %d: '%s'\n"),
411 config_line_num, line);
415 while (*line == ' ' || *line == '\t' ||
416 (*line == '=' && this->has_arg && !ate_equals && ++ate_equals))
419 if (!this->has_arg && *line) {
420 fprintf(stderr, _("Option '%s' does not take an argument at line %d\n"),
421 this->name, config_line_num);
423 } else if (this->has_arg == 1 && !*line) {
424 fprintf(stderr, _("Option '%s' requires an argument at line %d\n"),
425 this->name, config_line_num);
427 } else if (this->has_arg == 2 && !*line) {
437 int main(int argc, char **argv)
439 struct openconnect_info *vpninfo;
440 struct utsname utsbuf;
443 char *urlpath = NULL;
444 char *proxy = getenv("https_proxy");
446 uid_t uid = getuid();
448 char *pidfile = NULL;
452 char *token_str = NULL;
455 bindtextdomain("openconnect", LOCALEDIR);
456 setlocale(LC_ALL, "");
459 if (strcmp(openconnect_version_str, openconnect_binary_version)) {
460 fprintf(stderr, _("WARNING: This version of openconnect is %s but\n"
461 " the libopenconnect library is %s\n"),
462 openconnect_binary_version, openconnect_version_str);
465 openconnect_init_ssl();
467 vpninfo = malloc(sizeof(*vpninfo));
469 fprintf(stderr, _("Failed to allocate vpninfo structure\n"));
472 memset(vpninfo, 0, sizeof(*vpninfo));
474 /* Set up some defaults */
475 vpninfo->tun_fd = vpninfo->ssl_fd = vpninfo->dtls_fd = vpninfo->new_dtls_fd = -1;
476 vpninfo->useragent = openconnect_create_useragent("Open AnyConnect VPN Agent");
478 vpninfo->deflate = 1;
479 vpninfo->dtls_attempt_period = 60;
480 vpninfo->max_qlen = 10;
481 vpninfo->reconnect_interval = RECONNECT_INTERVAL_MIN;
482 vpninfo->reconnect_timeout = 300;
483 vpninfo->uid_csd = 0;
484 /* We could let them override this on the command line some day, perhaps */
485 openconnect_set_reported_os(vpninfo, NULL);
486 vpninfo->uid_csd = 0;
487 vpninfo->uid_csd_given = 0;
488 vpninfo->validate_peer_cert = validate_peer_cert;
489 vpninfo->process_auth_form = process_auth_form;
490 vpninfo->cbdata = vpninfo;
491 vpninfo->cert_expire_warning = 60 * 86400;
492 vpninfo->vpnc_script = DEFAULT_VPNCSCRIPT;
493 vpninfo->cancel_fd = -1;
496 vpninfo->localname = utsbuf.nodename;
498 vpninfo->localname = "localhost";
500 while ((opt = next_option(argc, argv, &config_arg))) {
508 fprintf(stderr, _("Cannot use 'config' option inside config file\n"));
511 config_file = fopen(config_arg, "r");
513 fprintf(stderr, _("Cannot open config file '%s': %s\n"),
514 config_arg, strerror(errno));
518 /* The next option will come from the file... */
521 vpninfo->cafile = keep_config_arg();
524 pidfile = keep_config_arg();
527 vpninfo->servercert = keep_config_arg();
530 vpninfo->dtls_attempt_period = 0;
535 case OPT_PRINTCOOKIE:
538 case OPT_AUTHENTICATE:
541 case OPT_COOKIE_ON_STDIN:
542 read_stdin(&vpninfo->cookie);
543 /* If the cookie is empty, ignore it */
544 if (! *vpninfo->cookie) {
545 vpninfo->cookie = NULL;
548 case OPT_PASSWORD_ON_STDIN:
549 read_stdin(&vpninfo->password);
552 vpninfo->nopasswd = 1;
557 case OPT_RECONNECT_TIMEOUT:
558 vpninfo->reconnect_timeout = atoi(config_arg);
560 case OPT_DTLS_CIPHERS:
561 vpninfo->dtls_ciphers = keep_config_arg();
564 vpninfo->authgroup = keep_config_arg();
570 vpninfo->cookie = keep_config_arg();
573 vpninfo->cert = strdup(config_arg);
576 vpninfo->cert_expire_warning = 86400 * atoi(config_arg);
579 vpninfo->sslkey = strdup(config_arg);
582 vpninfo->deflate = 1;
585 vpninfo->deflate = 0;
589 urlpath = strdup(config_arg);
594 vpninfo->ifname = keep_config_arg();
600 vpninfo->reqmtu = atol(config_arg);
601 if (vpninfo->reqmtu < 576) {
602 fprintf(stderr, _("MTU %d too small\n"), vpninfo->reqmtu);
603 vpninfo->reqmtu = 576;
607 vpninfo->basemtu = atol(config_arg);
608 if (vpninfo->basemtu < 576) {
609 fprintf(stderr, _("MTU %d too small\n"), vpninfo->basemtu);
610 vpninfo->basemtu = 576;
614 vpninfo->cert_password = strdup(config_arg);
617 proxy = keep_config_arg();
628 case OPT_NO_HTTP_KEEPALIVE:
630 _("Disabling all HTTP connection re-use due to --no-http-keepalive option.\n"
631 "If this helps, please report to <openconnect-devel@lists.infradead.org>.\n"));
632 vpninfo->no_http_keepalive = 1;
634 case OPT_NO_CERT_CHECK:
638 vpninfo->vpnc_script = keep_config_arg();
641 vpninfo->script_tun = 1;
644 vpninfo->username = keep_config_arg();
648 uid = strtol(config_arg, &strend, 0);
650 struct passwd *pw = getpwnam(config_arg);
652 fprintf(stderr, _("Invalid user \"%s\"\n"),
662 vpninfo->uid_csd = strtol(config_arg, &strend, 0);
664 struct passwd *pw = getpwnam(config_arg);
666 fprintf(stderr, _("Invalid user \"%s\"\n"),
670 vpninfo->uid_csd = pw->pw_uid;
672 vpninfo->uid_csd_given = 1;
675 case OPT_CSD_WRAPPER:
676 vpninfo->csd_wrapper = keep_config_arg();
678 case OPT_DISABLE_IPV6:
679 vpninfo->disable_ipv6 = 1;
682 vpninfo->max_qlen = atol(config_arg);
683 if (!vpninfo->max_qlen) {
684 fprintf(stderr, _("Queue length zero not permitted; using 1\n"));
685 vpninfo->max_qlen = 1;
695 printf(_("OpenConnect version %s\n"), openconnect_version_str);
699 vpninfo->xmlconfig = keep_config_arg();
700 vpninfo->write_new_config = write_new_config;
702 case OPT_KEY_PASSWORD_FROM_FSID:
703 do_passphrase_from_fsid = 1;
706 free(vpninfo->useragent);
707 vpninfo->useragent = strdup(config_arg);
710 vpninfo->dtls_times.dpd = vpninfo->ssl_times.dpd = atoi(config_arg);
712 case OPT_DTLS_LOCAL_PORT:
713 vpninfo->dtls_local_port = atoi(config_arg);
717 token_str = keep_config_arg();
720 if (openconnect_set_reported_os(vpninfo, config_arg)) {
721 fprintf(stderr, _("Invalid OS identity \"%s\"\n"),
731 if (optind < argc - 1) {
732 fprintf(stderr, _("Too many arguments on command line\n"));
734 } else if (optind > argc - 1) {
735 fprintf(stderr, _("No server specified\n"));
739 if (!vpninfo->sslkey)
740 vpninfo->sslkey = vpninfo->cert;
742 vpninfo->progress = write_progress;
746 vpninfo->proxy_factory = px_proxy_factory_new();
748 fprintf(stderr, _("This version of openconnect was built without libproxy support\n"));
754 init_stoken(vpninfo, token_str);
756 if (proxy && openconnect_set_http_proxy(vpninfo, strdup(proxy)))
761 openlog("openconnect", LOG_PID, LOG_DAEMON);
763 vpninfo->progress = syslog_progress;
766 memset(&sa, 0, sizeof(sa));
767 sa.sa_handler = handle_sigusr;
769 sigaction(SIGUSR1, &sa, NULL);
770 sigaction(SIGUSR2, &sa, NULL);
772 if (vpninfo->sslkey && do_passphrase_from_fsid)
773 openconnect_passphrase_from_fsid(vpninfo);
775 if (config_lookup_host(vpninfo, argv[optind]))
778 if (!vpninfo->hostname) {
779 char *url = strdup(argv[optind]);
781 if (openconnect_parse_url(vpninfo, url))
787 /* Historically, the path in the URL superseded the one in the
788 * --usergroup argument, just because of the order in which they
789 * were processed. Preserve that behaviour. */
790 if (urlpath && !vpninfo->urlpath) {
791 vpninfo->urlpath = urlpath;
800 if (!vpninfo->cookie && openconnect_obtain_cookie(vpninfo)) {
801 if (vpninfo->csd_scriptname) {
802 unlink(vpninfo->csd_scriptname);
803 vpninfo->csd_scriptname = NULL;
805 fprintf(stderr, _("Failed to obtain WebVPN cookie\n"));
809 if (cookieonly == 3) {
811 printf("COOKIE='%s'\n", vpninfo->cookie);
812 printf("HOST='%s'\n", vpninfo->hostname);
813 if (vpninfo->peer_cert) {
814 char buf[41] = {0, };
815 openconnect_get_cert_sha1(vpninfo, vpninfo->peer_cert, buf);
816 printf("FINGERPRINT='%s'\n", buf);
818 openconnect_vpninfo_free(vpninfo);
820 } else if (cookieonly) {
821 printf("%s\n", vpninfo->cookie);
822 if (cookieonly == 1) {
823 /* We use cookieonly=2 for 'print it and continue' */
824 openconnect_vpninfo_free(vpninfo);
828 if (make_cstp_connection(vpninfo)) {
829 fprintf(stderr, _("Creating SSL connection failed\n"));
833 if (setup_tun(vpninfo)) {
834 fprintf(stderr, _("Set up tun device failed\n"));
838 if (uid != getuid()) {
840 fprintf(stderr, _("Failed to set uid %ld\n"),
846 if (vpninfo->dtls_attempt_period && setup_dtls(vpninfo))
847 fprintf(stderr, _("Set up DTLS failed; using SSL instead\n"));
849 vpn_progress(vpninfo, PRG_INFO,
850 _("Connected %s as %s%s%s, using %s\n"), vpninfo->ifname,
851 vpninfo->vpn_addr?:"",
852 (vpninfo->vpn_addr6 && vpninfo->vpn_addr)?" + ":"",
853 vpninfo->vpn_addr6?:"",
854 (vpninfo->dtls_fd == -1) ?
855 (vpninfo->deflate ? "SSL + deflate" : "SSL")
858 if (!vpninfo->vpnc_script) {
859 vpn_progress(vpninfo, PRG_INFO,
860 _("No --script argument provided; DNS and routing are not configured\n"));
861 vpn_progress(vpninfo, PRG_INFO,
862 _("See http://www.infradead.org/openconnect/vpnc-script.html\n"));
868 /* Open the pidfile before forking, so we can report errors
869 more sanely. It's *possible* that we'll fail to write to
870 it, but very unlikely. */
871 if (pidfile != NULL) {
872 fp = fopen(pidfile, "w");
874 fprintf(stderr, _("Failed to open '%s' for write: %s\n"),
875 pidfile, strerror(errno));
879 if ((pid = fork())) {
881 fprintf(fp, "%d\n", pid);
884 vpn_progress(vpninfo, PRG_INFO,
885 _("Continuing in background; pid %d\n"),
892 vpn_mainloop(vpninfo);
898 static int write_new_config(void *_vpninfo, char *buf, int buflen)
900 struct openconnect_info *vpninfo = _vpninfo;
904 config_fd = open(vpninfo->xmlconfig, O_WRONLY|O_TRUNC|O_CREAT, 0644);
907 fprintf(stderr, _("Failed to open %s for write: %s\n"),
908 vpninfo->xmlconfig, strerror(err));
912 /* FIXME: We should actually write to a new tempfile, then rename */
913 if(write(config_fd, buf, buflen) != buflen) {
915 fprintf(stderr, _("Failed to write config to %s: %s\n"),
916 vpninfo->xmlconfig, strerror(err));
925 void write_progress(void *_vpninfo, int level, const char *fmt, ...)
927 FILE *outf = level ? stdout : stderr;
933 if (verbose >= level) {
935 vfprintf(outf, fmt, args);
942 void syslog_progress(void *_vpninfo, int level, const char *fmt, ...)
945 ANDROID_LOG_ERROR, /* PRG_ERR */
946 ANDROID_LOG_INFO, /* PRG_INFO */
947 ANDROID_LOG_DEBUG, /* PRG_DEBUG */
948 ANDROID_LOG_DEBUG /* PRG_TRACE */
952 if (verbose >= level) {
954 va_copy(args2, args);
955 __android_log_vprint(l[level], "openconnect", fmt, args);
956 /* Android wants it to stderr too, so the GUI can scrape
957 it and display it as well as going to syslog */
958 vfprintf(stderr, fmt, args2);
964 void syslog_progress(void *_vpninfo, int level, const char *fmt, ...)
966 int priority = level ? LOG_INFO : LOG_NOTICE;
969 if (verbose >= level) {
971 vsyslog(priority, fmt, args);
977 struct accepted_cert {
978 struct accepted_cert *next;
979 char fingerprint[SHA1_SIZE * 2 + 1];
983 static int validate_peer_cert(void *_vpninfo, OPENCONNECT_X509 *peer_cert,
986 struct openconnect_info *vpninfo = _vpninfo;
987 char fingerprint[SHA1_SIZE * 2 + 1];
988 struct accepted_cert *this;
994 ret = openconnect_get_cert_sha1(vpninfo, peer_cert, fingerprint);
998 for (this = accepted_certs; this; this = this->next) {
999 if (!strcasecmp(this->host, vpninfo->hostname) &&
1000 !strcasecmp(this->fingerprint, fingerprint))
1009 fprintf(stderr, _("\nCertificate from VPN server \"%s\" failed verification.\n"
1010 "Reason: %s\n"), vpninfo->hostname, reason);
1015 fprintf(stderr, _("Enter '%s' to accept, '%s' to abort; anything else to view: "),
1017 if (!fgets(buf, sizeof(buf), stdin))
1019 p = strchr(buf, '\n');
1023 if (!strcasecmp(buf, _("yes"))) {
1024 struct accepted_cert *newcert = malloc(sizeof(*newcert) +
1025 strlen(vpninfo->hostname) + 1);
1027 newcert->next = accepted_certs;
1028 accepted_certs = newcert;
1029 strcpy(newcert->fingerprint, fingerprint);
1030 strcpy(newcert->host, vpninfo->hostname);
1034 if (!strcasecmp(buf, _("no")))
1037 details = openconnect_get_cert_details(vpninfo, peer_cert);
1038 fputs(details, stderr);
1040 fprintf(stderr, _("SHA1 fingerprint: %s\n"), fingerprint);
1047 * = 0, when form was parsed and POST required
1048 * = 1, when response was cancelled by user
1050 static int process_auth_form(void *_vpninfo,
1051 struct oc_auth_form *form)
1053 struct openconnect_info *vpninfo = _vpninfo;
1054 struct oc_form_opt *opt;
1055 char response[1024];
1059 fprintf(stderr, "%s\n", form->banner);
1062 fprintf(stderr, "%s\n", form->error);
1065 fprintf(stderr, "%s\n", form->message);
1067 /* scan for select options first so they are displayed first */
1068 for (opt = form->opts; opt; opt = opt->next) {
1069 if (opt->type == OC_FORM_OPT_SELECT) {
1070 struct oc_form_opt_select *select_opt = (void *)opt;
1071 struct oc_choice *choice = NULL;
1074 if (!select_opt->nr_choices)
1077 if (vpninfo->authgroup &&
1078 !strcmp(opt->name, "group_list")) {
1079 for (i = 0; i < select_opt->nr_choices; i++) {
1080 choice = &select_opt->choices[i];
1082 if (!strcmp(vpninfo->authgroup,
1084 opt->value = choice->name;
1089 vpn_progress(vpninfo, PRG_ERR,
1090 _("Auth choice \"%s\" not available\n"),
1091 vpninfo->authgroup);
1093 if (!opt->value && select_opt->nr_choices == 1) {
1094 choice = &select_opt->choices[0];
1095 opt->value = choice->name;
1102 vpn_progress(vpninfo, PRG_ERR,
1103 _("User input required in non-interactive mode\n"));
1106 fprintf(stderr, "%s [", opt->label);
1107 for (i = 0; i < select_opt->nr_choices; i++) {
1108 choice = &select_opt->choices[i];
1110 fprintf(stderr, "|");
1112 fprintf(stderr, "%s", choice->label);
1114 fprintf(stderr, "]:");
1117 if (!fgets(response, sizeof(response), stdin) || !strlen(response))
1120 p = strchr(response, '\n');
1124 for (i = 0; i < select_opt->nr_choices; i++) {
1125 choice = &select_opt->choices[i];
1127 if (!strcmp(response, choice->label)) {
1128 select_opt->form.value = choice->name;
1132 if (!select_opt->form.value) {
1133 vpn_progress(vpninfo, PRG_ERR,
1134 _("Auth choice \"%s\" not valid\n"),
1141 for (opt = form->opts; opt; opt = opt->next) {
1143 if (opt->type == OC_FORM_OPT_TEXT) {
1144 if (vpninfo->username &&
1145 !strcmp(opt->name, "username")) {
1146 opt->value = strdup(vpninfo->username);
1149 } else if (non_inter) {
1150 vpn_progress(vpninfo, PRG_ERR,
1151 _("User input required in non-interactive mode\n"));
1154 opt->value=malloc(80);
1158 fprintf(stderr, "%s", opt->label);
1161 if (!fgets(opt->value, 80, stdin) || !strlen(opt->value))
1164 p = strchr(opt->value, '\n');
1169 } else if (opt->type == OC_FORM_OPT_PASSWORD) {
1170 if (vpninfo->password &&
1171 !strcmp(opt->name, "password")) {
1172 opt->value = vpninfo->password;
1173 vpninfo->password = NULL;
1176 } else if (non_inter) {
1177 vpn_progress(vpninfo, PRG_ERR,
1178 _("User input required in non-interactive mode\n"));
1182 opt->value=malloc(80);
1186 fprintf(stderr, "%s", opt->label);
1191 tcsetattr(0, TCSANOW, &t);
1193 p = fgets(opt->value, 80, stdin);
1196 tcsetattr(0, TCSANOW, &t);
1197 fprintf(stderr, "\n");
1199 if (!p || !strlen(opt->value))
1202 p = strchr(opt->value, '\n');
1210 if (vpninfo->password) {
1211 free(vpninfo->password);
1212 vpninfo->password = NULL;
1218 for (opt = form->opts; opt; opt = opt->next) {
1219 if (opt->value && (opt->type == OC_FORM_OPT_TEXT ||
1220 opt->type == OC_FORM_OPT_PASSWORD)) {
1228 static void init_stoken(struct openconnect_info *vpninfo,
1229 const char *token_str)
1231 int ret = openconnect_set_stoken_mode(vpninfo, 1, token_str);
1237 fprintf(stderr, _("Soft token string is invalid\n"));
1240 fprintf(stderr, _("Can't open ~/.stokenrc file\n"));
1243 fprintf(stderr, _("OpenConnect was not built with soft token support\n"));
1246 fprintf(stderr, _("General failure in libstoken\n"));