2 * OpenConnect (SSL + DTLS) VPN client
4 * Copyright © 2008 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
31 #include <openssl/ssl.h>
32 #include <openssl/err.h>
34 #include "openconnect.h"
37 * Data packets are encapsulated in the SSL stream as follows:
39 * 0000: Magic "STF\x1"
40 * 0004: Big-endian 16-bit length (not including 8-byte header)
41 * 0006: Byte packet type (see openconnect.h)
45 static char data_hdr[8] = {
48 AC_PKT_DATA, /* Type */
52 static struct pkt keepalive_pkt = {
53 .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_KEEPALIVE, 0 },
56 static struct pkt dpd_pkt = {
57 .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_OUT, 0 },
60 static struct pkt dpd_resp_pkt = {
61 .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_RESP, 0 },
65 static int start_cstp_connection(struct openconnect_info *vpninfo)
70 struct vpn_option **next_dtls_option = &vpninfo->dtls_options;
71 struct vpn_option **next_cstp_option = &vpninfo->cstp_options;
72 struct vpn_option *old_cstp_opts = vpninfo->cstp_options;
73 struct vpn_option *old_dtls_opts = vpninfo->dtls_options;
74 const char *old_addr = vpninfo->vpn_addr;
75 const char *old_netmask = vpninfo->vpn_netmask;
76 struct split_include *inc;
78 /* Clear old options which will be overwritten */
79 vpninfo->vpn_addr = vpninfo->vpn_netmask = NULL;
80 vpninfo->cstp_options = vpninfo->dtls_options = NULL;
81 vpninfo->vpn_domain = vpninfo->vpn_proxy_pac = NULL;
82 vpninfo->dtls_cipher = NULL;
85 vpninfo->vpn_dns[i] = vpninfo->vpn_nbns[i] = NULL;
87 for (inc = vpninfo->split_includes; inc; ) {
88 struct split_include *next = inc->next;
92 for (inc = vpninfo->split_excludes; inc; ) {
93 struct split_include *next = inc->next;
97 vpninfo->split_includes = vpninfo->split_excludes = NULL;
99 openconnect_SSL_printf(vpninfo->https_ssl, "CONNECT /CSCOSSLC/tunnel HTTP/1.1\r\n");
100 openconnect_SSL_printf(vpninfo->https_ssl, "Host: %s\r\n", vpninfo->hostname);
101 openconnect_SSL_printf(vpninfo->https_ssl, "User-Agent: %s\r\n", vpninfo->useragent);
102 openconnect_SSL_printf(vpninfo->https_ssl, "Cookie: webvpn=%s\r\n", vpninfo->cookie);
103 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Version: 1\r\n");
104 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Hostname: %s\r\n", vpninfo->localname);
105 if (vpninfo->deflate)
106 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Accept-Encoding: deflate;q=1.0\r\n");
107 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-MTU: %d\r\n", vpninfo->mtu);
108 /* To enable IPv6, send 'IPv6,IPv4'.
109 We don't know how most of that works yet though. */
110 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Address-Type: IPv4\r\n");
111 openconnect_SSL_printf(vpninfo->https_ssl, "X-DTLS-Master-Secret: ");
112 for (i = 0; i < sizeof(vpninfo->dtls_secret); i++)
113 openconnect_SSL_printf(vpninfo->https_ssl, "%02X", vpninfo->dtls_secret[i]);
114 openconnect_SSL_printf(vpninfo->https_ssl, "\r\nX-DTLS-CipherSuite: %s\r\n\r\n",
115 vpninfo->dtls_ciphers?:"AES256-SHA:AES128-SHA:DES-CBC3-SHA:DES-CBC-SHA");
117 if (openconnect_SSL_gets(vpninfo->https_ssl, buf, 65536) < 0) {
118 vpninfo->progress(vpninfo, PRG_ERR, "Error fetching HTTPS response\n");
121 openconnect_close_https(vpninfo);
123 if (openconnect_open_https(vpninfo)) {
124 vpninfo->progress(vpninfo, PRG_ERR,
125 "Failed to open HTTPS connection to %s\n",
134 if (strncmp(buf, "HTTP/1.1 200 ", 13)) {
135 if (!strncmp(buf, "HTTP/1.1 503 ", 13)) {
136 /* "Service Unavailable. Why? */
137 char *reason = "<unknown>";
138 while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
139 if (!strncmp(buf, "X-Reason: ", 10)) {
144 vpninfo->progress(vpninfo, PRG_ERR, "VPN service unavailable; reason: %s\n",
148 vpninfo->progress(vpninfo, PRG_ERR,
149 "Got inappropriate HTTP CONNECT response: %s\n",
151 if (!strncmp(buf, "HTTP/1.1 401 ", 13))
156 vpninfo->progress(vpninfo, PRG_INFO,
157 "Got CONNECT response: %s\n", buf);
159 /* We may have advertised it, but we only do it if the server agrees */
160 vpninfo->deflate = 0;
162 while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
163 struct vpn_option *new_option;
164 char *colon = strchr(buf, ':');
173 if (strncmp(buf, "X-DTLS-", 7) &&
174 strncmp(buf, "X-CSTP-", 7))
177 new_option = malloc(sizeof(*new_option));
179 vpninfo->progress(vpninfo, PRG_ERR, "No memory for options\n");
182 new_option->option = strdup(buf);
183 new_option->value = strdup(colon);
184 new_option->next = NULL;
186 if (!new_option->option || !new_option->value) {
187 vpninfo->progress(vpninfo, PRG_ERR, "No memory for options\n");
191 vpninfo->progress(vpninfo, PRG_TRACE, "%s: %s\n", buf, colon);
193 if (!strncmp(buf, "X-DTLS-", 7)) {
194 *next_dtls_option = new_option;
195 next_dtls_option = &new_option->next;
198 /* CSTP options... */
199 *next_cstp_option = new_option;
200 next_cstp_option = &new_option->next;
203 if (!strcmp(buf + 7, "Keepalive")) {
204 vpninfo->ssl_times.keepalive = atol(colon);
205 } else if (!strcmp(buf + 7, "DPD")) {
206 vpninfo->ssl_times.dpd = atol(colon);
207 } else if (!strcmp(buf + 7, "Content-Encoding")) {
208 if (!strcmp(colon, "deflate"))
209 vpninfo->deflate = 1;
211 vpninfo->progress(vpninfo, PRG_ERR,
212 "Unknown CSTP-Content-Encoding %s\n",
216 } else if (!strcmp(buf + 7, "MTU")) {
217 vpninfo->mtu = atol(colon);
218 } else if (!strcmp(buf + 7, "Address")) {
219 vpninfo->vpn_addr = new_option->value;
220 } else if (!strcmp(buf + 7, "Netmask")) {
221 vpninfo->vpn_netmask = new_option->value;
222 } else if (!strcmp(buf + 7, "DNS")) {
224 for (j = 0; j < 3; j++) {
225 if (!vpninfo->vpn_dns[j]) {
226 vpninfo->vpn_dns[j] = new_option->value;
230 } else if (!strcmp(buf + 7, "NBNS")) {
232 for (j = 0; j < 3; j++) {
233 if (!vpninfo->vpn_nbns[j]) {
234 vpninfo->vpn_nbns[j] = new_option->value;
238 } else if (!strcmp(buf + 7, "Default-Domain")) {
239 vpninfo->vpn_domain = new_option->value;
240 } else if (!strcmp(buf + 7, "MSIE-Proxy-PAC-URL")) {
241 vpninfo->vpn_proxy_pac = new_option->value;
242 } else if (!strcmp(buf + 7, "Split-Include")) {
243 struct split_include *inc = malloc(sizeof(*inc));
246 inc->route = new_option->value;
247 inc->next = vpninfo->split_includes;
248 vpninfo->split_includes = inc;
249 } else if (!strcmp(buf + 7, "Split-Exclude")) {
250 struct split_include *exc = malloc(sizeof(*exc));
253 exc->route = new_option->value;
254 exc->next = vpninfo->split_excludes;
255 vpninfo->split_excludes = exc;
259 if (!vpninfo->vpn_addr) {
260 vpninfo->progress(vpninfo, PRG_ERR, "No IP address received. Aborting\n");
263 if (!vpninfo->vpn_netmask)
264 vpninfo->vpn_netmask = "255.255.255.255";
266 if (strcmp(old_addr, vpninfo->vpn_addr)) {
267 vpninfo->progress(vpninfo, PRG_ERR, "Reconnect gave different IP address (%s != %s)\n",
268 vpninfo->vpn_addr, old_addr);
273 if (strcmp(old_netmask, vpninfo->vpn_netmask)) {
274 vpninfo->progress(vpninfo, PRG_ERR, "Reconnect gave different netmask (%s != %s)\n",
275 vpninfo->vpn_netmask, old_netmask);
280 while (old_dtls_opts) {
281 struct vpn_option *tmp = old_dtls_opts;
282 old_dtls_opts = old_dtls_opts->next;
287 while (old_cstp_opts) {
288 struct vpn_option *tmp = old_cstp_opts;
289 old_cstp_opts = old_cstp_opts->next;
294 vpninfo->progress(vpninfo, PRG_INFO, "CSTP connected. DPD %d, Keepalive %d\n",
295 vpninfo->ssl_times.dpd, vpninfo->ssl_times.keepalive);
297 BIO_set_nbio(SSL_get_rbio(vpninfo->https_ssl), 1);
298 BIO_set_nbio(SSL_get_wbio(vpninfo->https_ssl), 1);
300 fcntl(vpninfo->ssl_fd, F_SETFL, fcntl(vpninfo->ssl_fd, F_GETFL) | O_NONBLOCK);
301 if (vpninfo->select_nfds <= vpninfo->ssl_fd)
302 vpninfo->select_nfds = vpninfo->ssl_fd + 1;
304 FD_SET(vpninfo->ssl_fd, &vpninfo->select_rfds);
305 FD_SET(vpninfo->ssl_fd, &vpninfo->select_efds);
307 vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = time(NULL);
312 int make_cstp_connection(struct openconnect_info *vpninfo)
316 if (!vpninfo->https_ssl && (ret = openconnect_open_https(vpninfo)))
319 if (vpninfo->deflate) {
320 vpninfo->deflate_adler32 = 1;
321 vpninfo->inflate_adler32 = 1;
323 if (inflateInit2(&vpninfo->inflate_strm, -12) ||
324 deflateInit2(&vpninfo->deflate_strm, Z_DEFAULT_COMPRESSION,
325 Z_DEFLATED, -12, 9, Z_DEFAULT_STRATEGY)) {
326 vpninfo->progress(vpninfo, PRG_ERR, "Compression setup failed\n");
327 vpninfo->deflate = 0;
330 if (!vpninfo->deflate_pkt) {
331 vpninfo->deflate_pkt = malloc(sizeof(struct pkt) + 2048);
332 if (!vpninfo->deflate_pkt) {
333 vpninfo->progress(vpninfo, PRG_ERR, "Allocation of deflate buffer failed\n");
334 vpninfo->deflate = 0;
336 memset(vpninfo->deflate_pkt, 0, sizeof(struct pkt));
337 memcpy(vpninfo->deflate_pkt->hdr, data_hdr, 8);
338 vpninfo->deflate_pkt->hdr[6] = AC_PKT_COMPRESSED;
342 return start_cstp_connection(vpninfo);
345 static int cstp_reconnect(struct openconnect_info *vpninfo)
351 timeout = vpninfo->reconnect_timeout;
352 interval = vpninfo->reconnect_interval;
354 while ((ret = make_cstp_connection(vpninfo))) {
357 vpninfo->progress(vpninfo, PRG_INFO,
358 "sleep %ds, remaining timeout %ds\n",
364 interval += vpninfo->reconnect_interval;
365 if (interval > RECONNECT_INTERVAL_MAX)
366 interval = RECONNECT_INTERVAL_MAX;
371 static int inflate_and_queue_packet(struct openconnect_info *vpninfo, int type, void *buf, int len)
373 struct pkt *new = malloc(sizeof(struct pkt) + vpninfo->mtu);
381 vpninfo->inflate_strm.next_in = buf;
382 vpninfo->inflate_strm.avail_in = len - 4;
384 vpninfo->inflate_strm.next_out = new->data;
385 vpninfo->inflate_strm.avail_out = vpninfo->mtu;
386 vpninfo->inflate_strm.total_out = 0;
388 if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
389 vpninfo->progress(vpninfo, PRG_ERR, "inflate failed\n");
394 new->len = vpninfo->inflate_strm.total_out;
396 vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
397 new->data, new->len);
399 if (vpninfo->inflate_adler32 != ntohl( *(uint32_t *) (buf + len - 4) )) {
400 vpninfo->quit_reason = "Compression (inflate) adler32 failure";
403 vpninfo->progress(vpninfo, PRG_TRACE,
404 "Received compressed data packet of %ld bytes\n",
405 vpninfo->inflate_strm.total_out);
407 queue_packet(&vpninfo->incoming_queue, new);
411 int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
413 unsigned char buf[16384];
417 /* FIXME: The poll() handling here is fairly simplistic. Actually,
418 if the SSL connection stalls it could return a WANT_WRITE error
419 on _either_ of the SSL_read() or SSL_write() calls. In that case,
420 we should probably remove POLLIN from the events we're looking for,
421 and add POLLOUT. As it is, though, it'll just chew CPU time in that
422 fairly unlikely situation, until the write backlog clears. */
423 while ( (len = SSL_read(vpninfo->https_ssl, buf, sizeof(buf))) > 0) {
426 if (buf[0] != 'S' || buf[1] != 'T' ||
427 buf[2] != 'F' || buf[3] != 1 || buf[7])
430 payload_len = (buf[4] << 8) + buf[5];
431 if (len != 8 + payload_len) {
432 vpninfo->progress(vpninfo, PRG_ERR,
433 "Unexpected packet length. SSL_read returned %d but packet is\n",
435 vpninfo->progress(vpninfo, PRG_ERR,
436 "%02x %02x %02x %02x %02x %02x %02x %02x\n",
437 buf[0], buf[1], buf[2], buf[3],
438 buf[4], buf[5], buf[6], buf[7]);
441 vpninfo->ssl_times.last_rx = time(NULL);
444 vpninfo->progress(vpninfo, PRG_TRACE,
445 "Got CSTP DPD request\n");
446 vpninfo->owe_ssl_dpd_response = 1;
449 case AC_PKT_DPD_RESP:
450 vpninfo->progress(vpninfo, PRG_TRACE,
451 "Got CSTP DPD response\n");
454 case AC_PKT_KEEPALIVE:
455 vpninfo->progress(vpninfo, PRG_TRACE,
456 "Got CSTP Keepalive\n");
460 vpninfo->progress(vpninfo, PRG_TRACE,
461 "Received uncompressed data packet of %d bytes\n",
463 queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf + 8,
468 case AC_PKT_DISCONN: {
470 for (i = 0; i < payload_len; i++) {
471 if (!isprint(buf[payload_len + 8 + i]))
472 buf[payload_len + 8 + i] = '.';
474 buf[payload_len + 8] = 0;
475 vpninfo->progress(vpninfo, PRG_ERR,
476 "Received server disconnect: %02x '%s'\n", buf[8], buf + 9);
477 vpninfo->quit_reason = "Server request";
480 case AC_PKT_COMPRESSED:
481 if (!vpninfo->deflate) {
482 vpninfo->progress(vpninfo, PRG_ERR, "Compressed packet received in !deflate mode\n");
485 inflate_and_queue_packet(vpninfo, AF_INET, buf + 8, payload_len);
489 case AC_PKT_TERM_SERVER:
490 vpninfo->progress(vpninfo, PRG_ERR, "received server terminate packet\n");
491 vpninfo->quit_reason = "Server request";
496 vpninfo->progress(vpninfo, PRG_ERR,
497 "Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n",
498 buf[0], buf[1], buf[2], buf[3],
499 buf[4], buf[5], buf[6], buf[7]);
500 vpninfo->quit_reason = "Unknown packet received";
505 /* If SSL_write() fails we are expected to try again. With exactly
506 the same data, at exactly the same location. So we keep the
507 packet we had before.... */
508 if (vpninfo->current_ssl_pkt) {
510 vpninfo->ssl_times.last_tx = time(NULL);
511 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
512 ret = SSL_write(vpninfo->https_ssl,
513 vpninfo->current_ssl_pkt->hdr,
514 vpninfo->current_ssl_pkt->len + 8);
516 ret = SSL_get_error(vpninfo->https_ssl, ret);
518 case SSL_ERROR_WANT_WRITE:
519 /* Waiting for the socket to become writable -- it's
520 probably stalled, and/or the buffers are full */
521 FD_SET(vpninfo->ssl_fd, &vpninfo->select_wfds);
523 case SSL_ERROR_WANT_READ:
524 if (ka_stalled_dpd_time(&vpninfo->ssl_times, timeout))
528 vpninfo->progress(vpninfo, PRG_ERR, "SSL_write failed: %d\n", ret);
529 ERR_print_errors_fp(stderr);
533 if (ret != vpninfo->current_ssl_pkt->len + 8) {
534 vpninfo->progress(vpninfo, PRG_ERR, "SSL wrote too few bytes! Asked for %d, sent %d\n",
535 vpninfo->current_ssl_pkt->len + 8, ret);
536 vpninfo->quit_reason = "Internal error";
539 /* Don't free the 'special' packets */
540 if (vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
541 vpninfo->current_ssl_pkt != &dpd_pkt &&
542 vpninfo->current_ssl_pkt != &dpd_resp_pkt &&
543 vpninfo->current_ssl_pkt != &keepalive_pkt)
544 free(vpninfo->current_ssl_pkt);
546 vpninfo->current_ssl_pkt = NULL;
549 if (vpninfo->owe_ssl_dpd_response) {
550 vpninfo->owe_ssl_dpd_response = 0;
551 vpninfo->current_ssl_pkt = &dpd_resp_pkt;
552 goto handle_outgoing;
555 switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
557 /* Not that this will ever happen; we don't even process
558 the setting when we're asked for it. */
559 vpninfo->progress(vpninfo, PRG_ERR, "CSTP rekey due but we don't know how\n");
560 time(&vpninfo->ssl_times.last_rekey);
566 vpninfo->progress(vpninfo, PRG_ERR, "CSTP Dead Peer Detection detected dead peer!\n");
568 openconnect_close_https(vpninfo);
570 /* It's already deflated in the old stream. Extremely
571 non-trivial to reconstitute it; just throw it away */
572 if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
573 vpninfo->current_ssl_pkt = NULL;
575 if (cstp_reconnect(vpninfo)) {
576 vpninfo->progress(vpninfo, PRG_ERR, "Reconnect failed\n");
577 vpninfo->quit_reason = "CSTP reconnect failed";
580 /* I think we can leave DTLS to its own devices; when we reconnect
581 with the same master secret, we do seem to get the same sessid */
585 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP DPD\n");
587 vpninfo->current_ssl_pkt = &dpd_pkt;
588 goto handle_outgoing;
591 /* No need to send an explicit keepalive
592 if we have real data to send */
593 if (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue)
596 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP Keepalive\n");
598 vpninfo->current_ssl_pkt = &keepalive_pkt;
599 goto handle_outgoing;
605 /* Service outgoing packet queue, if no DTLS */
606 while (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue) {
607 struct pkt *this = vpninfo->outgoing_queue;
608 vpninfo->outgoing_queue = this->next;
609 vpninfo->outgoing_qlen--;
611 if (vpninfo->deflate) {
612 unsigned char *adler;
615 vpninfo->deflate_strm.next_in = this->data;
616 vpninfo->deflate_strm.avail_in = this->len;
617 vpninfo->deflate_strm.next_out = (void *)vpninfo->deflate_pkt->data;
618 vpninfo->deflate_strm.avail_out = 2040;
619 vpninfo->deflate_strm.total_out = 0;
621 ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
623 vpninfo->progress(vpninfo, PRG_ERR, "deflate failed %d\n", ret);
627 vpninfo->deflate_pkt->hdr[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
628 vpninfo->deflate_pkt->hdr[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
630 /* Add ongoing adler32 to tail of compressed packet */
631 vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
632 this->data, this->len);
634 adler = &vpninfo->deflate_pkt->data[vpninfo->deflate_strm.total_out];
635 *(adler++) = vpninfo->deflate_adler32 >> 24;
636 *(adler++) = (vpninfo->deflate_adler32 >> 16) & 0xff;
637 *(adler++) = (vpninfo->deflate_adler32 >> 8) & 0xff;
638 *(adler) = vpninfo->deflate_adler32 & 0xff;
640 vpninfo->deflate_pkt->len = vpninfo->deflate_strm.total_out + 4;
642 vpninfo->progress(vpninfo, PRG_TRACE,
643 "Sending compressed data packet of %d bytes\n",
646 vpninfo->current_ssl_pkt = vpninfo->deflate_pkt;
649 memcpy(this->hdr, data_hdr, 8);
650 this->hdr[4] = this->len >> 8;
651 this->hdr[5] = this->len & 0xff;
653 vpninfo->progress(vpninfo, PRG_TRACE,
654 "Sending uncompressed data packet of %d bytes\n",
657 vpninfo->current_ssl_pkt = this;
659 goto handle_outgoing;
662 /* Work is not done if we just got rid of packets off the queue */
666 int cstp_bye(struct openconnect_info *vpninfo, char *reason)
668 unsigned char *bye_pkt;
671 /* already lost connection? */
672 if (!vpninfo->https_ssl)
675 reason_len = strlen(reason);
676 bye_pkt = malloc(reason_len + 8);
680 memcpy(bye_pkt, data_hdr, 8);
681 memcpy(bye_pkt + 8, reason, reason_len);
683 bye_pkt[4] = reason_len >> 8;
684 bye_pkt[5] = reason_len & 0xff;
685 bye_pkt[6] = AC_PKT_DISCONN;
687 SSL_write(vpninfo->https_ssl, bye_pkt, reason_len + 8);
690 vpninfo->progress(vpninfo, PRG_INFO,
691 "Send BYE packet: %s\n", reason);