2 * OpenConnect (SSL + DTLS) VPN client
4 * Copyright © 2008-2010 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
36 #include <sys/syslog.h>
37 #include <sys/utsname.h>
38 #include <sys/types.h>
39 #include <openssl/ui.h>
40 #ifdef OPENCONNECT_LIBPROXY
47 #include "openconnect-internal.h"
49 static int write_new_config(struct openconnect_info *vpninfo, char *buf, int buflen);
50 static void write_progress(struct openconnect_info *info, int level, const char *fmt, ...);
51 static void syslog_progress(struct openconnect_info *info, int level, const char *fmt, ...);
52 static int validate_peer_cert(struct openconnect_info *info, X509 *peer_cert, const char *reason);
54 int verbose = PRG_INFO;
56 int do_passphrase_from_fsid;
60 OPT_AUTHGROUP = 0x100,
69 OPT_KEY_PASSWORD_FROM_FSID,
73 OPT_NO_HTTP_KEEPALIVE,
76 OPT_PASSWORD_ON_STDIN,
78 OPT_RECONNECT_TIMEOUT,
83 static struct option long_options[] = {
84 {"background", 0, 0, 'b'},
85 {"certificate", 1, 0, 'c'},
86 {"sslkey", 1, 0, 'k'},
87 {"cookie", 1, 0, 'C'},
88 {"deflate", 0, 0, 'd'},
89 {"no-deflate", 0, 0, 'D'},
90 {"usergroup", 1, 0, 'g'},
92 {"interface", 1, 0, 'i'},
94 {"setuid", 1, 0, 'U'},
95 {"script", 1, 0, 's'},
96 {"script-tun", 0, 0, 'S'},
97 {"syslog", 0, 0, 'l'},
98 {"key-type", 1, 0, 'K'},
99 {"key-password", 1, 0, 'p'},
100 {"proxy", 1, 0, 'P'},
102 {"verbose", 0, 0, 'v'},
103 {"version", 0, 0, 'V'},
104 {"cafile", 1, 0, OPT_CAFILE},
105 {"no-dtls", 0, 0, OPT_NO_DTLS},
106 {"cookieonly", 0, 0, OPT_COOKIEONLY},
107 {"printcookie", 0, 0, OPT_PRINTCOOKIE},
108 {"quiet", 0, 0, 'q'},
109 {"queue-len", 1, 0, 'Q'},
110 {"xmlconfig", 1, 0, 'x'},
111 {"cookie-on-stdin", 0, 0, OPT_COOKIE_ON_STDIN},
112 {"passwd-on-stdin", 0, 0, OPT_PASSWORD_ON_STDIN},
113 {"no-passwd", 0, 0, OPT_NO_PASSWD},
114 {"reconnect-timeout", 1, 0, OPT_RECONNECT_TIMEOUT},
115 {"dtls-ciphers", 1, 0, OPT_DTLS_CIPHERS},
116 {"authgroup", 1, 0, OPT_AUTHGROUP},
117 {"servercert", 1, 0, OPT_SERVERCERT},
118 {"key-password-from-fsid", 0, 0, OPT_KEY_PASSWORD_FROM_FSID},
119 {"useragent", 1, 0, OPT_USERAGENT},
120 {"csd-user", 1, 0, OPT_CSD_USER},
121 {"csd-wrapper", 1, 0, OPT_CSD_WRAPPER},
122 {"disable-ipv6", 0, 0, OPT_DISABLE_IPV6},
123 {"no-proxy", 0, 0, OPT_NO_PROXY},
124 {"libproxy", 0, 0, OPT_LIBPROXY},
125 {"no-http-keepalive", 0, 0, OPT_NO_HTTP_KEEPALIVE},
126 {"no-cert-check", 0, 0, OPT_NO_CERT_CHECK},
127 {"force-dpd", 1, 0, OPT_FORCE_DPD},
133 printf("Usage: openconnect [options] <server>\n");
134 printf("Open client for Cisco AnyConnect VPN, version %s\n\n", openconnect_version);
135 printf(" -b, --background Continue in background after startup\n");
136 printf(" -c, --certificate=CERT Use SSL client certificate CERT\n");
137 printf(" -k, --sslkey=KEY Use SSL private key file KEY\n");
138 printf(" -K, --key-type=TYPE Private key type (PKCS#12 / TPM / PEM)\n");
139 printf(" -C, --cookie=COOKIE Use WebVPN cookie COOKIE\n");
140 printf(" --cookie-on-stdin Read cookie from standard input\n");
141 printf(" -d, --deflate Enable compression (default)\n");
142 printf(" -D, --no-deflate Disable compression\n");
143 printf(" --force-dpd=INTERVAL Set minimum Dead Peer Detection interval\n");
144 printf(" -g, --usergroup=GROUP Set login usergroup\n");
145 printf(" -h, --help Display help text\n");
146 printf(" -i, --interface=IFNAME Use IFNAME for tunnel interface\n");
147 printf(" -l, --syslog Use syslog for progress messages\n");
148 printf(" -U, --setuid=USER Drop privileges after connecting\n");
149 printf(" --csd-user=USER Drop privileges during CSD execution\n");
150 printf(" --csd-wrapper=SCRIPT Run SCRIPT instead of CSD binary\n");
151 printf(" -m, --mtu=MTU Request MTU from server\n");
152 printf(" -p, --key-password=PASS Set key passphrase or TPM SRK PIN\n");
153 printf(" --key-password-from-fsid Key passphrase is fsid of file system\n");
154 printf(" -P, --proxy=URL Set proxy server\n");
155 printf(" --no-proxy Disable proxy\n");
156 printf(" --libproxy Use libproxy to automatically configure proxy\n");
157 #ifndef OPENCONNECT_LIBPROXY
158 printf(" (NOTE: libproxy disabled in this build)\n");
160 printf(" -q, --quiet Less output\n");
161 printf(" -Q, --queue-len=LEN Set packet queue limit to LEN pkts\n");
162 printf(" -s, --script=SCRIPT Use vpnc-compatible config script\n");
163 printf(" -S, --script-tun Pass traffic to 'script' program, not tun\n");
164 printf(" -u, --user=NAME Set login username\n");
165 printf(" -V, --version Report version number\n");
166 printf(" -v, --verbose More output\n");
167 printf(" -x, --xmlconfig=CONFIG XML config file\n");
168 printf(" --authgroup=GROUP Choose authentication login selection\n");
169 printf(" --cookieonly Fetch webvpn cookie only; don't connect\n");
170 printf(" --printcookie Print webvpn cookie before connecting\n");
171 printf(" --cafile=FILE Cert file for server verification\n");
172 printf(" --disable-ipv6 Do not ask for IPv6 connectivity\n");
173 printf(" --dtls-ciphers=LIST OpenSSL ciphers to support for DTLS\n");
174 printf(" --no-dtls Disable DTLS\n");
175 printf(" --no-http-keepalive Disable HTTP connection re-use\n");
176 printf(" --no-passwd Disable password/SecurID authentication\n");
177 printf(" --no-cert-check Do not require server SSL cert to be valid\n");
178 printf(" --passwd-on-stdin Read password from standard input\n");
179 printf(" --reconnect-timeout Connection retry timeout in seconds\n");
180 printf(" --servercert=FINGERPRINT Server's certificate SHA1 fingerprint\n");
181 printf(" --useragent=STRING HTTP header User-Agent: field\n");
185 static void read_stdin(char **string)
187 char *c = malloc(100);
189 fprintf(stderr, "Allocation failure for string from stdin\n");
192 if (!fgets(c, 100, stdin)) {
193 perror("fgets (stdin)");
199 c = strchr(*string, '\n');
203 static void handle_sigusr(int sig)
207 else if (sig == SIGUSR2)
211 int main(int argc, char **argv)
213 struct openconnect_info *vpninfo;
214 struct utsname utsbuf;
218 char *proxy = getenv("https_proxy");
220 uid_t uid = getuid();
223 openconnect_init_openssl();
225 vpninfo = malloc(sizeof(*vpninfo));
227 fprintf(stderr, "Failed to allocate vpninfo structure\n");
230 memset(vpninfo, 0, sizeof(*vpninfo));
232 /* Set up some defaults */
233 vpninfo->tun_fd = vpninfo->ssl_fd = vpninfo->dtls_fd = vpninfo->new_dtls_fd = -1;
234 vpninfo->useragent = openconnect_create_useragent("Open AnyConnect VPN Agent");
236 vpninfo->deflate = 1;
237 vpninfo->dtls_attempt_period = 60;
238 vpninfo->max_qlen = 10;
239 vpninfo->reconnect_interval = RECONNECT_INTERVAL_MIN;
240 vpninfo->reconnect_timeout = 300;
241 vpninfo->uid_csd = 0;
242 vpninfo->uid_csd_given = 0;
243 vpninfo->validate_peer_cert = validate_peer_cert;
246 vpninfo->localname = utsbuf.nodename;
248 vpninfo->localname = "localhost";
250 while ((opt = getopt_long(argc, argv, "bC:c:Ddg:hi:k:K:lpP:Q:qSs:U:u:Vvx:",
251 long_options, NULL))) {
257 vpninfo->cafile = optarg;
260 vpninfo->servercert = optarg;
263 vpninfo->dtls_attempt_period = 0;
268 case OPT_PRINTCOOKIE:
271 case OPT_COOKIE_ON_STDIN:
272 read_stdin(&vpninfo->cookie);
273 /* If the cookie is empty, ignore it */
274 if (! *vpninfo->cookie) {
275 vpninfo->cookie = NULL;
278 case OPT_PASSWORD_ON_STDIN:
279 read_stdin(&vpninfo->password);
282 vpninfo->nopasswd = 1;
284 case OPT_RECONNECT_TIMEOUT:
285 vpninfo->reconnect_timeout = atoi(optarg);
287 case OPT_DTLS_CIPHERS:
288 vpninfo->dtls_ciphers = optarg;
291 vpninfo->authgroup = optarg;
297 vpninfo->cookie = optarg;
300 vpninfo->cert = optarg;
303 vpninfo->sslkey = optarg;
306 if (!strcasecmp(optarg, "PKCS#12") ||
307 !strcasecmp(optarg, "PKCS12")) {
308 vpninfo->cert_type = CERT_TYPE_PKCS12;
309 } else if (!strcasecmp(optarg, "TPM")) {
310 vpninfo->cert_type = CERT_TYPE_TPM;
311 } else if (!strcasecmp(optarg, "PEM")) {
312 vpninfo->cert_type = CERT_TYPE_PEM;
314 fprintf(stderr, "Unknown certificate type '%s'\n",
319 vpninfo->deflate = 1;
322 vpninfo->deflate = 0;
325 free(vpninfo->urlpath);
326 vpninfo->urlpath = strdup(optarg);
331 vpninfo->ifname = optarg;
337 vpninfo->mtu = atol(optarg);
338 if (vpninfo->mtu < 576) {
339 fprintf(stderr, "MTU %d too small\n", vpninfo->mtu);
344 vpninfo->cert_password = optarg;
354 #ifndef OPENCONNECT_LIBPROXY
355 fprintf(stderr, "This version of openconnect was built without libproxy support\n");
361 case OPT_NO_HTTP_KEEPALIVE:
362 fprintf(stderr, "Disabling all HTTP connection re-use due to --no-http-keepalive option.\n"
363 "If this helps, please report to <openconnect-devel@lists.infradead.org>.\n");
364 vpninfo->no_http_keepalive = 1;
366 case OPT_NO_CERT_CHECK:
370 vpninfo->vpnc_script = optarg;
373 vpninfo->script_tun = 1;
376 vpninfo->username = optarg;
380 uid = strtol(optarg, &strend, 0);
382 struct passwd *pw = getpwnam(optarg);
384 fprintf(stderr, "Invalid user \"%s\"\n",
394 vpninfo->uid_csd = strtol(optarg, &strend, 0);
396 struct passwd *pw = getpwnam(optarg);
398 fprintf(stderr, "Invalid user \"%s\"\n",
402 vpninfo->uid_csd = pw->pw_uid;
404 vpninfo->uid_csd_given = 1;
407 case OPT_CSD_WRAPPER:
408 vpninfo->csd_wrapper = optarg;
410 case OPT_DISABLE_IPV6:
411 vpninfo->disable_ipv6 = 1;
414 vpninfo->max_qlen = atol(optarg);
415 if (!vpninfo->max_qlen) {
416 fprintf(stderr, "Queue length zero not permitted; using 1\n");
417 vpninfo->max_qlen = 1;
427 printf("OpenConnect version %s\n", openconnect_version);
430 vpninfo->xmlconfig = optarg;
431 vpninfo->write_new_config = write_new_config;
433 case OPT_KEY_PASSWORD_FROM_FSID:
434 do_passphrase_from_fsid = 1;
437 free(vpninfo->useragent);
438 vpninfo->useragent = optarg;
441 vpninfo->dtls_times.dpd = vpninfo->ssl_times.dpd = atoi(optarg);
448 if (optind != argc - 1) {
449 fprintf(stderr, "No server specified\n");
453 if (!vpninfo->sslkey)
454 vpninfo->sslkey = vpninfo->cert;
456 vpninfo->progress = write_progress;
458 #ifdef OPENCONNECT_LIBPROXY
460 vpninfo->proxy_factory = px_proxy_factory_new();
462 if (proxy && openconnect_set_http_proxy(vpninfo, proxy))
466 openlog("openconnect", LOG_PID, LOG_DAEMON);
467 vpninfo->progress = syslog_progress;
470 memset(&sa, 0, sizeof(sa));
471 sa.sa_handler = handle_sigusr;
473 sigaction(SIGUSR1, &sa, NULL);
474 sigaction(SIGUSR2, &sa, NULL);
476 if (vpninfo->sslkey && do_passphrase_from_fsid)
477 openconnect_passphrase_from_fsid(vpninfo);
479 if (config_lookup_host(vpninfo, argv[optind]))
482 if (!vpninfo->hostname) {
483 char *url = strdup(argv[optind]);
487 if (openconnect_parse_url(url, &scheme, &vpninfo->hostname, &vpninfo->port,
489 fprintf(stderr, "Failed to parse server URL '%s'\n",
493 if (scheme && strcmp(scheme, "https")) {
494 fprintf(stderr, "Only https:// permitted for server URL\n");
498 free(vpninfo->urlpath);
499 vpninfo->urlpath = group;
509 if (!vpninfo->cookie && openconnect_obtain_cookie(vpninfo)) {
510 fprintf(stderr, "Failed to obtain WebVPN cookie\n");
515 printf("%s\n", vpninfo->cookie);
517 /* We use cookieonly=2 for 'print it and continue' */
520 if (make_cstp_connection(vpninfo)) {
521 fprintf(stderr, "Creating SSL connection failed\n");
525 if (setup_tun(vpninfo)) {
526 fprintf(stderr, "Set up tun device failed\n");
530 if (uid != getuid()) {
532 fprintf(stderr, "Failed to set uid %d\n", uid);
537 if (vpninfo->dtls_attempt_period && setup_dtls(vpninfo))
538 fprintf(stderr, "Set up DTLS failed; using SSL instead\n");
540 vpninfo->progress(vpninfo, PRG_INFO,
541 "Connected %s as %s%s%s, using %s\n", vpninfo->ifname,
542 vpninfo->vpn_addr?:"",
543 (vpninfo->vpn_addr6 && vpninfo->vpn_addr)?" + ":"",
544 vpninfo->vpn_addr6?:"",
545 (vpninfo->dtls_fd == -1) ?
546 (vpninfo->deflate ? "SSL + deflate" : "SSL")
549 if (!vpninfo->vpnc_script)
550 vpninfo->progress(vpninfo, PRG_INFO,
551 "No --script argument provided; DNS and routing are not configured\n");
555 if ((pid = fork())) {
556 vpninfo->progress(vpninfo, PRG_INFO,
557 "Continuing in background; pid %d\n",
562 vpn_mainloop(vpninfo);
566 static int write_new_config(struct openconnect_info *vpninfo, char *buf, int buflen)
571 config_fd = open(vpninfo->xmlconfig, O_WRONLY|O_TRUNC|O_CREAT, 0644);
574 fprintf(stderr, "Failed to open %s for write: %s\n",
575 vpninfo->xmlconfig, strerror(err));
579 /* FIXME: We should actually write to a new tempfile, then rename */
580 if(write(config_fd, buf, buflen) != buflen) {
582 fprintf(stderr, "Failed to write config to %s: %s\n",
583 vpninfo->xmlconfig, strerror(err));
591 void write_progress(struct openconnect_info *info, int level, const char *fmt, ...)
593 FILE *outf = level ? stdout : stderr;
596 if (verbose >= level) {
598 vfprintf(outf, fmt, args);
603 void syslog_progress(struct openconnect_info *info, int level,
604 const char *fmt, ...)
606 int priority = level ? LOG_INFO : LOG_NOTICE;
609 if (verbose >= level) {
611 vsyslog(priority, fmt, args);
617 struct accepted_cert {
618 struct accepted_cert *next;
619 char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
623 static int validate_peer_cert(struct openconnect_info *vpninfo, X509 *peer_cert,
626 char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
627 struct accepted_cert *this;
633 ret = get_cert_sha1_fingerprint(vpninfo, peer_cert, fingerprint);
637 for (this = accepted_certs; this; this = this->next) {
638 if (!strcasecmp(this->host, vpninfo->hostname) &&
639 !strcasecmp(this->fingerprint, fingerprint))
648 fprintf(stderr, "\nCertificate from VPN server \"%s\" failed verification.\n"
649 "Reason: %s\n", vpninfo->hostname, reason);
653 UI_add_input_string(ui, "Enter 'yes' to accept, 'no' to abort; anything else to view: ",
654 UI_INPUT_FLAG_ECHO, buf, 2, 5);
655 ret = UI_process(ui);
662 if (!strcasecmp(buf, "yes")) {
663 struct accepted_cert *newcert = malloc(sizeof(*newcert) +
664 strlen(vpninfo->hostname) + 1);
666 newcert->next = accepted_certs;
667 accepted_certs = newcert;
668 strcpy(newcert->fingerprint, fingerprint);
669 strcpy(newcert->host, vpninfo->hostname);
673 if (!strcasecmp(buf, "no"))
676 X509_print_fp(stderr, peer_cert);