[meego-commits] 9623: Changes to MeeGo:1.1:Core:Update:Testing/openconnect
Martin Xu
no_reply at build.meego.com
Mon Nov 22 07:29:07 UTC 2010
Hi,
I have made the following changes to openconnect in project MeeGo:1.1:Core:Update:Testing. Please review and accept ASAP.
Thank You,
Martin Xu
[This message was auto-generated]
---
Request #9623:
submit: home:martin:branches:MeeGo:1.1:Core:Update:Testing/openconnect(r2)(cleanup) -> MeeGo:1.1:Core:Update:Testing/openconnect
Message:
upgrade to 2.26
State: new 2010-11-21T23:29:06 martin
Comment: None
changes files:
--------------
--- openconnect.changes
+++ openconnect.changes
@@ -0,0 +1,6 @@
+* Wed Sep 22 2010 David Woodhouse <David.Woodhouse at intel.com> - 2.26
+- Update to 2.26 to fix BMC #8449 BMC #8443 BMC #8446
+
+* Fri Aug 27 2010 David Woodhouse <David.Woodhouse at intel.com> - 2.25
+- Update to 2.25
+
old:
----
openconnect-2.22.tar.gz
new:
----
openconnect-2.26.tar.gz
spec files:
-----------
--- openconnect.spec
+++ openconnect.spec
@@ -1,5 +1,5 @@
Name: openconnect
-Version: 2.22
+Version: 2.26
Release: 1
Summary: Open client for Cisco AnyConnect VPN
other changes:
--------------
++++++ openconnect-2.22-dbus-workaround.patch
--- openconnect-2.22-dbus-workaround.patch
+++ openconnect-2.22-dbus-workaround.patch
@@ -1,5 +1,27 @@
---- openconnect-2.22/Makefile~ 2010-03-07 22:10:55.000000000 +0000
-+++ openconnect-2.22/Makefile 2010-04-20 18:41:51.000000000 +0100
+--- openconnect-2.22/nm-auth-dialog.c~ 2010-03-07 22:10:55.000000000 +0000
++++ openconnect-2.22/nm-auth-dialog.c 2010-04-20 18:39:23.000000000 +0100
+@@ -33,6 +33,7 @@
+ #include <libxml/tree.h>
+
+ #include <gconf/gconf-client.h>
++#include <dbus/dbus-glib.h>
+
+ #include <gtk/gtk.h>
+
+@@ -1468,6 +1469,9 @@ int main (int argc, char **argv)
+ }
+
+ g_thread_init (NULL);
++ /* This shouldn't be necessary; we're working around a gconf bug.
++ http://bugzilla.meego.com/show_bug.cgi?id=918 */
++ dbus_g_thread_init();
+ gtk_init(0, NULL);
+
+ ui_data = init_ui_data(vpn_name);
+diff --git a/Makefile b/Makefile
+index 4343a73..d0a0b6d 100644
+--- a/Makefile
++++ b/Makefile
@@ -42,6 +42,12 @@ ifeq ($(GCONF_LDFLAGS),)
MISSINGPKGS += gconf-2.0
endif
@@ -11,7 +33,7 @@
+endif
+
CFLAGS := $(OPT_FLAGS) $(SSL_CFLAGS) $(XML2_CFLAGS) $(EXTRA_CFLAGS)
- LDFLAGS := $(SSL_LDFLAGS) $(XML2_LDFLAGS) $(EXTRA_LDFLAGS)
+ LDFLAGS := -lz $(SSL_LDFLAGS) $(XML2_LDFLAGS) $(EXTRA_LDFLAGS)
@@ -49,7 +55,7 @@ ifdef SSL_UI
CFLAGS += -DSSL_UI
@@ -22,32 +44,12 @@
-include Make.config
-@@ -88,7 +94,7 @@ maybe-auth-dialog: $(warning Cannot buil
+@@ -91,7 +97,7 @@ maybe-auth-dialog: $(warning Cannot build NetworkManager auth-dialog:) \
endif
- nm-openconnect-auth-dialog: nm-auth-dialog.o $(AUTH_OBJECTS)
+ nm-openconnect-auth-dialog: nm-auth-dialog.o libopenconnect.a
- $(CC) -o $@ $^ $(LDFLAGS) $(GTK_LDFLAGS) $(GCONF_LDFLAGS) $(XML2_LDFLAGS)
+ $(CC) -o $@ $^ $(LDFLAGS) $(GTK_LDFLAGS) $(GCONF_LDFLAGS) $(XML2_LDFLAGS) $(DBUS_LDFLAGS)
%.o: %.c
$(CC) -c -o $@ $(CFLAGS) $(CFLAGS_$@) $< -MD -MF .$@.dep
---- openconnect-2.22/nm-auth-dialog.c~ 2010-03-07 22:10:55.000000000 +0000
-+++ openconnect-2.22/nm-auth-dialog.c 2010-04-20 18:39:23.000000000 +0100
-@@ -33,6 +33,7 @@
- #include <libxml/tree.h>
-
- #include <gconf/gconf-client.h>
-+#include <dbus/dbus-glib.h>
-
- #include <gtk/gtk.h>
-
-@@ -1468,6 +1469,9 @@ int main (int argc, char **argv)
- }
-
- g_thread_init (NULL);
-+ /* This shouldn't be necessary; we're working around a gconf bug.
-+ http://bugzilla.meego.com/show_bug.cgi?id=918 */
-+ dbus_g_thread_init();
- gtk_init(0, NULL);
-
- ui_data = init_ui_data(vpn_name);
++++++ openconnect-2.22.tar.gz -> openconnect-2.26.tar.gz
--- Makefile
+++ Makefile
@@ -15,7 +15,7 @@
# dir; there's no need to install it anywhere (we link it statically).
ifdef OPENSSL
SSL_CFLAGS += -I$(OPENSSL)/include
-SSL_LDFLAGS += -lz $(OPENSSL)/libssl.a $(OPENSSL)/libcrypto.a -ldl
+SSL_LDFLAGS += $(OPENSSL)/libssl.a $(OPENSSL)/libcrypto.a
else
ifeq ($(wildcard /usr/include/openssl),)
$(error "No OpenSSL in /usr/include/openssl. Cannot continue");
@@ -43,7 +43,7 @@
endif
CFLAGS := $(OPT_FLAGS) $(SSL_CFLAGS) $(XML2_CFLAGS) $(EXTRA_CFLAGS)
-LDFLAGS := $(SSL_LDFLAGS) $(XML2_LDFLAGS) $(EXTRA_LDFLAGS)
+LDFLAGS := -lz $(SSL_LDFLAGS) $(XML2_LDFLAGS) $(EXTRA_LDFLAGS)
ifdef SSL_UI
CFLAGS += -DSSL_UI
@@ -58,7 +58,7 @@
endif
ifneq ($(LIBPROXY_HDR),)
-CFLAGS += -DOPENCONNECT_LIBPROXY
+CFLAGS += -DOPENCONNECT_LIBPROXY -DLIBPROXY_HDR=\"$(LIBPROXY_HDR)\"
LDFLAGS += -lproxy
endif
@@ -73,11 +73,14 @@
all: openconnect maybe-auth-dialog
+libopenconnect.a: ${AUTH_OBJECTS}
+ $(AR) rcs $@ $^
+
version.c: $(patsubst %.o,%.c,$(VERSION_OBJS)) Makefile openconnect.h \
$(wildcard .git/index .git/refs/tags) version.sh
@./version.sh
-openconnect: $(OPENCONNECT_OBJS) $(CONNECTION_OBJS) $(AUTH_OBJECTS)
+openconnect: $(OPENCONNECT_OBJS) $(CONNECTION_OBJS) libopenconnect.a
$(CC) -o $@ $^ $(LDFLAGS)
ifeq ($(MISSINGPKGS),)
@@ -87,7 +90,7 @@
$(warning Missing pkg-config packages: $(MISSINGPKGS))
endif
-nm-openconnect-auth-dialog: nm-auth-dialog.o $(AUTH_OBJECTS)
+nm-openconnect-auth-dialog: nm-auth-dialog.o libopenconnect.a
$(CC) -o $@ $^ $(LDFLAGS) $(GTK_LDFLAGS) $(GCONF_LDFLAGS) $(XML2_LDFLAGS)
%.o: %.c
@@ -114,7 +117,7 @@
HDRTEST = for a in $2 ; do if echo "\#include <$$a>" | $(CC) -o/dev/null -xc - -M -MF $1 -MP -MT Make.config 2>/dev/null; then \
echo $$a; break ; fi; done
-Make.config: LIBPROXY_H = $(shell $(call HDRTEST,.libproxy.h.dep,libproxy/proxy.h))
+Make.config: LIBPROXY_H = $(shell $(call HDRTEST,.libproxy.h.dep,proxy.h libproxy/proxy.h))
Make.config: IF_TUN_H = $(shell $(call HDRTEST,.if_tun.h.dep, linux/if_tun.h net/if_tun.h net/tun/if_tun.h))
Make.config: Makefile
( echo "IF_TUN_HDR := $(IF_TUN_H)"; echo "LIBPROXY_HDR := $(LIBPROXY_H)" ) > $@
--- README.DTLS
+++ README.DTLS
@@ -1,22 +1,24 @@
Cisco's implementation of the DTLS protocol unfortunately does not
-comply with the relevant standards. We need some patches to OpenSSL to
-be compatible with it.
+comply with the relevant standards. OpenSSL 0.9.8m or newer, and
+1.0.0-beta2 or newer, contain a compatibility mode which allows
+interoperation with Cisco's servers.
-For the 0.9.8 branch of OpenSSL, the required patch is
- http://cvs.openssl.org/chngview?cn=18037
+As long as you are using a current version of OpenSSL, you have nothing
+to worry about -- everything should work optimally.
+
+Without a suitable OpenSSL, the openconnect client will fall back to
+passing packets over the HTTPS connection. This will still work OK, but
+will suffer quite a lot if your connection has packet loss. For details
+of why that happens, see http://sites.inka.de/~W1011/devel/tcp-tcp.html
+
+If you insist on using ancient buggy versions of OpenSSL, these are the
+patches you require if you want DTLS to work:
-This was included in OpenSSL CVS in April 2009 and should be in the
-next release from the 0.9.8 branch, which will presumably be 0.9.8l.
+For versions of OpenSSL earlier than 0.9.8m, you'll need the Cisco
+compatibility support:
+ http://cvs.openssl.org/chngview?cn=18037
For versions of OpenSSL earlier than 0.9.8j, a couple of other DTLS
bug-fixes are also required:
http://cvs.openssl.org/chngview?cn=17500
http://cvs.openssl.org/chngview?cn=17505
-
-OpenSSL 1.0.0-beta2 and later require no patching; all the required
-support is already present.
-
-Without a suitable OpenSSL, the openconnect client will fall back to
-passing packets over the HTTPS connection. This will work, but will
-suffer quite a lot if your connection has packet loss. For details of
-why that happens, see http://sites.inka.de/~W1011/devel/tcp-tcp.html
--- TODO
+++ TODO
@@ -1,7 +1,6 @@
openconnect:
- IPv6 support
- Port to/test on Windows/Solaris/*BSD
+ Port to/test on Windows, Symbian, etc.
Proper SecurID support
nm-auth-dialog:
--- auth-dlg-settings.h
+++ auth-dlg-settings.h
@@ -31,15 +31,11 @@
#define NM_OPENCONNECT_KEY_GATEWAY "gateway"
#define NM_OPENCONNECT_KEY_COOKIE "cookie"
#define NM_OPENCONNECT_KEY_GWCERT "gwcert"
-#define NM_OPENCONNECT_KEY_AUTHTYPE "authtype"
#define NM_OPENCONNECT_KEY_USERCERT "usercert"
#define NM_OPENCONNECT_KEY_CACERT "cacert"
#define NM_OPENCONNECT_KEY_PRIVKEY "userkey"
#define NM_OPENCONNECT_KEY_USERNAME "username"
#define NM_OPENCONNECT_KEY_XMLCONFIG "xmlconfig"
-#define NM_OPENCONNECT_AUTHTYPE_CERT "cert"
-#define NM_OPENCONNECT_AUTHTYPE_CERT_TPM "cert-tpm"
-#define NM_OPENCONNECT_AUTHTYPE_PASSWORD "password"
#endif /* __OPENCONNECT_AUTH_DLG_SETTINGS_H */
--- auth.c
+++ auth.c
@@ -523,6 +523,7 @@
if (vpninfo->password &&
!strcmp(opt->name, "password")) {
opt->value = strdup(vpninfo->password);
+ vpninfo->password = NULL;
if (!opt->value) {
ret = -ENOMEM;
goto out_ui;
--- cstp.c
+++ cstp.c
@@ -32,6 +32,7 @@
#include <openssl/ssl.h>
#include <openssl/err.h>
+#include <openssl/rand.h>
#include "openconnect.h"
@@ -68,7 +69,7 @@
{
char buf[65536];
int i;
- int retried = 0;
+ int retried = 0, sessid_found = 0;
struct vpn_option **next_dtls_option = &vpninfo->dtls_options;
struct vpn_option **next_cstp_option = &vpninfo->cstp_options;
struct vpn_option *old_cstp_opts = vpninfo->cstp_options;
@@ -84,6 +85,7 @@
vpninfo->vpn_addr6 = vpninfo->vpn_netmask6 = NULL;
vpninfo->cstp_options = vpninfo->dtls_options = NULL;
vpninfo->vpn_domain = vpninfo->vpn_proxy_pac = NULL;
+ vpninfo->banner = NULL;
for (i=0; i<3; i++)
vpninfo->vpn_dns[i] = vpninfo->vpn_nbns[i] = NULL;
@@ -99,6 +101,15 @@
inc = next;
}
vpninfo->split_includes = vpninfo->split_excludes = NULL;
+
+ /* Create (new) random master key for DTLS connection, if needed */
+ if (vpninfo->dtls_times.last_rekey + vpninfo->dtls_times.rekey <
+ time(NULL) + 300 &&
+ RAND_bytes(vpninfo->dtls_secret, sizeof(vpninfo->dtls_secret)) != 1) {
+ fprintf(stderr, "Failed to initialise DTLS secret\n");
+ exit(1);
+ }
+
retry:
openconnect_SSL_printf(vpninfo->https_ssl, "CONNECT /CSCOSSLC/tunnel HTTP/1.1\r\n");
openconnect_SSL_printf(vpninfo->https_ssl, "Host: %s\r\n", vpninfo->hostname);
@@ -196,6 +207,19 @@
if (!strncmp(buf, "X-DTLS-", 7)) {
*next_dtls_option = new_option;
next_dtls_option = &new_option->next;
+
+ if (!strcmp(buf + 7, "Session-ID")) {
+ if (strlen(colon) != 64) {
+ vpninfo->progress(vpninfo, PRG_ERR, "X-DTLS-Session-ID not 64 characters\n");
+ vpninfo->progress(vpninfo, PRG_ERR, "Is: %s\n", colon);
+ vpninfo->dtls_attempt_period = 0;
+ return -EINVAL;
+ }
+ for (i = 0; i < 64; i += 2)
+ vpninfo->dtls_session_id[i/2] = unhex(colon + i);
+ sessid_found = 1;
+ time(&vpninfo->dtls_times.last_rekey);
+ }
continue;
}
/* CSTP options... */
@@ -206,7 +230,11 @@
if (!strcmp(buf + 7, "Keepalive")) {
vpninfo->ssl_times.keepalive = atol(colon);
} else if (!strcmp(buf + 7, "DPD")) {
- vpninfo->ssl_times.dpd = atol(colon);
+ int j = atol(colon);
+ if (j && (!vpninfo->ssl_times.dpd || j < vpninfo->ssl_times.dpd))
+ vpninfo->ssl_times.dpd = j;
+ } else if (!strcmp(buf + 7, "Rekey-Time")) {
+ vpninfo->ssl_times.rekey = atol(colon);
} else if (!strcmp(buf + 7, "Content-Encoding")) {
if (!strcmp(colon, "deflate"))
vpninfo->deflate = 1;
@@ -248,6 +276,8 @@
vpninfo->vpn_domain = new_option->value;
} else if (!strcmp(buf + 7, "MSIE-Proxy-PAC-URL")) {
vpninfo->vpn_proxy_pac = new_option->value;
+ } else if (!strcmp(buf + 7, "Banner")) {
+ vpninfo->banner = new_option->value;
} else if (!strcmp(buf + 7, "Split-Include")) {
struct split_include *inc = malloc(sizeof(*inc));
if (!inc)
@@ -325,7 +355,11 @@
FD_SET(vpninfo->ssl_fd, &vpninfo->select_rfds);
FD_SET(vpninfo->ssl_fd, &vpninfo->select_efds);
- vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = time(NULL);
+ if (!sessid_found)
+ vpninfo->dtls_attempt_period = 0;
+
+ vpninfo->ssl_times.last_rekey = vpninfo->ssl_times.last_rx =
+ vpninfo->ssl_times.last_tx = time(NULL);
return 0;
}
@@ -363,12 +397,19 @@
return start_cstp_connection(vpninfo);
}
-static int cstp_reconnect(struct openconnect_info *vpninfo)
+int cstp_reconnect(struct openconnect_info *vpninfo)
{
int ret;
int timeout;
int interval;
+ openconnect_close_https(vpninfo);
+
+ /* It's already deflated in the old stream. Extremely
+ non-trivial to reconstitute it; just throw it away */
+ if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
+ vpninfo->current_ssl_pkt = NULL;
+
timeout = vpninfo->reconnect_timeout;
interval = vpninfo->reconnect_interval;
@@ -584,22 +625,14 @@
case KA_REKEY:
/* Not that this will ever happen; we don't even process
the setting when we're asked for it. */
- vpninfo->progress(vpninfo, PRG_ERR, "CSTP rekey due but we don't know how\n");
- time(&vpninfo->ssl_times.last_rekey);
- work_done = 1;
+ vpninfo->progress(vpninfo, PRG_INFO, "CSTP rekey due\n");
+ goto do_reconnect;
break;
case KA_DPD_DEAD:
peer_dead:
vpninfo->progress(vpninfo, PRG_ERR, "CSTP Dead Peer Detection detected dead peer!\n");
do_reconnect:
- openconnect_close_https(vpninfo);
-
- /* It's already deflated in the old stream. Extremely
- non-trivial to reconstitute it; just throw it away */
- if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
- vpninfo->current_ssl_pkt = NULL;
-
if (cstp_reconnect(vpninfo)) {
vpninfo->progress(vpninfo, PRG_ERR, "Reconnect failed\n");
vpninfo->quit_reason = "CSTP reconnect failed";
--- dtls.c
+++ dtls.c
@@ -35,6 +35,19 @@
#include "openconnect.h"
+static unsigned char nybble(unsigned char n)
+{
+ if (n >= '0' && n <= '9') return n - '0';
+ else if (n >= 'A' && n <= 'F') return n - ('A' - 10);
+ else if (n >= 'a' && n <= 'f') return n - ('a' - 10);
+ return 0;
+}
+
+unsigned char unhex(const char *data)
+{
+ return (nybble(data[0]) << 4) | nybble(data[1]);
+}
+
#ifdef SSL_F_DTLS1_CONNECT
#if 0
/*
@@ -90,19 +103,6 @@
* their clients use anyway.
*/
-static unsigned char nybble(unsigned char n)
-{
- if (n >= '0' && n <= '9') return n - '0';
- else if (n >= 'A' && n <= 'F') return n - ('A' - 10);
- else if (n >= 'a' && n <= 'f') return n - ('a' - 10);
- return 0;
-}
-
-static unsigned char hex(const char *data)
-{
- return (nybble(data[0]) << 4) | nybble(data[1]);
-}
-
int connect_dtls_socket(struct openconnect_info *vpninfo)
{
STACK_OF(SSL_CIPHER) *ciphers;
@@ -177,16 +177,17 @@
return -EINVAL;
}
vpninfo->dtls_session->ssl_version = 0x0100; // DTLS1_BAD_VER
-
- vpninfo->dtls_session->master_key_length = sizeof(vpninfo->dtls_secret);
- memcpy(vpninfo->dtls_session->master_key, vpninfo->dtls_secret,
- sizeof(vpninfo->dtls_secret));
-
- vpninfo->dtls_session->session_id_length = sizeof(vpninfo->dtls_session_id);
- memcpy(vpninfo->dtls_session->session_id, vpninfo->dtls_session_id,
- sizeof(vpninfo->dtls_session_id));
}
+ /* Do this every time; it may have changed due to a rekey */
+ vpninfo->dtls_session->master_key_length = sizeof(vpninfo->dtls_secret);
+ memcpy(vpninfo->dtls_session->master_key, vpninfo->dtls_secret,
+ sizeof(vpninfo->dtls_secret));
+
+ vpninfo->dtls_session->session_id_length = sizeof(vpninfo->dtls_session_id);
+ memcpy(vpninfo->dtls_session->session_id, vpninfo->dtls_session_id,
+ sizeof(vpninfo->dtls_session_id));
+
dtls_ssl = SSL_new(vpninfo->dtls_ctx);
SSL_set_connect_state(dtls_ssl);
@@ -211,7 +212,7 @@
if (!SSL_set_session(dtls_ssl, vpninfo->dtls_session)) {
vpninfo->progress(vpninfo, PRG_ERR,
"SSL_set_session() failed with old protocol version 0x%x\n"
- "Your OpenSSL may lack Cisco compatibility support\n"
+ "Are you using a version of OpenSSL older than 0.9.8m?\n"
"See http://rt.openssl.org/Ticket/Display.html?id=1751\n"
"Use the --no-dtls command line option to avoid this message\n",
vpninfo->dtls_session->ssl_version);
@@ -269,8 +270,7 @@
vpninfo->new_dtls_ssl = NULL;
vpninfo->new_dtls_fd = -1;
- vpninfo->dtls_times.last_rekey = vpninfo->dtls_times.last_rx =
- vpninfo->dtls_times.last_tx = time(NULL);
+ vpninfo->dtls_times.last_rx = vpninfo->dtls_times.last_tx = time(NULL);
return 0;
}
@@ -329,31 +329,21 @@
int setup_dtls(struct openconnect_info *vpninfo)
{
struct vpn_option *dtls_opt = vpninfo->dtls_options;
- int sessid_found = 0;
int dtls_port = 0;
- int i;
while (dtls_opt) {
vpninfo->progress(vpninfo, PRG_TRACE,
"DTLS option %s : %s\n",
dtls_opt->option, dtls_opt->value);
- if (!strcmp(dtls_opt->option, "X-DTLS-Session-ID")) {
- if (strlen(dtls_opt->value) != 64) {
- vpninfo->progress(vpninfo, PRG_ERR, "X-DTLS-Session-ID not 64 characters\n");
- vpninfo->progress(vpninfo, PRG_ERR, "Is: %s\n", dtls_opt->value);
- vpninfo->dtls_attempt_period = 0;
- return -EINVAL;
- }
- for (i = 0; i < 64; i += 2)
- vpninfo->dtls_session_id[i/2] = hex(dtls_opt->value + i);
- sessid_found = 1;
- } else if (!strcmp(dtls_opt->option + 7, "Port")) {
+ if (!strcmp(dtls_opt->option + 7, "Port")) {
dtls_port = atol(dtls_opt->value);
} else if (!strcmp(dtls_opt->option + 7, "Keepalive")) {
vpninfo->dtls_times.keepalive = atol(dtls_opt->value);
} else if (!strcmp(dtls_opt->option + 7, "DPD")) {
- vpninfo->dtls_times.dpd = atol(dtls_opt->value);
+ int j = atol(dtls_opt->value);
+ if (j && (!vpninfo->dtls_times.dpd || j < vpninfo->dtls_times.dpd))
+ vpninfo->dtls_times.dpd = j;
} else if (!strcmp(dtls_opt->option + 7, "Rekey-Time")) {
vpninfo->dtls_times.rekey = atol(dtls_opt->value);
} else if (!strcmp(dtls_opt->option + 7, "CipherSuite")) {
@@ -362,7 +352,7 @@
dtls_opt = dtls_opt->next;
}
- if (!sessid_found || !dtls_port) {
+ if (!dtls_port) {
vpninfo->dtls_attempt_period = 0;
return -EINVAL;
}
@@ -455,9 +445,17 @@
switch (keepalive_action(&vpninfo->dtls_times, timeout)) {
case KA_REKEY:
- time(&vpninfo->dtls_times.last_rekey);
- vpninfo->progress(vpninfo, PRG_TRACE, "DTLS rekey due\n");
- if (connect_dtls_socket(vpninfo)) {
+ vpninfo->progress(vpninfo, PRG_INFO, "DTLS rekey due\n");
+
+ /* There ought to be a method of rekeying DTLS without tearing down
+ the CSTP session and restarting, but we don't (yet) know it */
+ if (cstp_reconnect(vpninfo)) {
+ vpninfo->progress(vpninfo, PRG_ERR, "Reconnect failed\n");
+ vpninfo->quit_reason = "CSTP reconnect failed";
+ return 1;
+ }
+
+ if (dtls_restart(vpninfo)) {
vpninfo->progress(vpninfo, PRG_ERR, "DTLS rekey failed\n");
return 1;
}
--- http.c
+++ http.c
@@ -51,7 +51,8 @@
* provided by their caller.
*/
-int http_add_cookie(struct openconnect_info *vpninfo, const char *option, const char *value)
+static int http_add_cookie(struct openconnect_info *vpninfo,
+ const char *option, const char *value)
{
struct vpn_option *new, **this;
@@ -124,14 +125,13 @@
return -EINVAL;
}
- vpninfo->progress(vpninfo, PRG_TRACE, "Got HTTP response: %s\n", buf);
+ vpninfo->progress(vpninfo, (*result==200)?PRG_TRACE:PRG_INFO,
+ "Got HTTP response: %s\n", buf);
/* Eat headers... */
while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
char *colon;
- vpninfo->progress(vpninfo, PRG_TRACE, "%s\n", buf);
-
if (i < 0) {
vpninfo->progress(vpninfo, PRG_ERR, "Error processing HTTP response\n");
return -EINVAL;
@@ -145,6 +145,38 @@
if (*colon == ' ')
colon++;
+ /* Handle Set-Cookie first so that we can avoid printing the
+ webvpn cookie in the verbose debug output */
+ if (!strcasecmp(buf, "Set-Cookie")) {
+ char *semicolon = strchr(colon, ';');
+ char *print_equals, *equals = strchr(colon, '=');
+ int ret;
+
+ if (semicolon)
+ *semicolon = 0;
+
+ if (!equals) {
+ vpninfo->progress(vpninfo, PRG_ERR, "Invalid cookie offered: %s\n", buf);
+ return -EINVAL;
+ }
+ *(equals++) = 0;
+
+ print_equals = equals;
+ /* Don't print the webvpn cookie; we don't want people posting it
+ in public with debugging output */
+ if (!strcmp(colon, "webvpn"))
+ print_equals = "<elided>";
+ vpninfo->progress(vpninfo, PRG_TRACE, "%s: %s=%s%s%s\n",
+ buf, colon, print_equals, semicolon?";":"",
+ semicolon?(semicolon+1):"");
+
+ ret = http_add_cookie(vpninfo, colon, equals);
+ if (ret)
+ return ret;
+ } else {
+ vpninfo->progress(vpninfo, PRG_TRACE, "%s: %s\n", buf, colon);
+ }
+
if (!strcasecmp(buf, "Connection")) {
if (!strcasecmp(colon, "Close"))
closeconn = 1;
@@ -171,24 +203,6 @@
return -EINVAL;
}
}
- if (!strcasecmp(buf, "Set-Cookie")) {
- char *semicolon = strchr(colon, ';');
- char *equals = strchr(colon, '=');
- int ret;
-
- if (semicolon)
- *semicolon = 0;
-
- if (!equals) {
- vpninfo->progress(vpninfo, PRG_ERR, "Invalid cookie offered: %s\n", buf);
- return -EINVAL;
- }
- *(equals++) = 0;
-
- ret = http_add_cookie(vpninfo, colon, equals);
- if (ret)
- return ret;
- }
if (!strcasecmp(buf, "Transfer-Encoding")) {
if (!strcasecmp(colon, "chunked"))
bodylen = BODY_CHUNKED;
@@ -289,7 +303,7 @@
}
}
- if (closeconn) {
+ if (closeconn || vpninfo->no_http_keepalive) {
SSL_free(vpninfo->https_ssl);
vpninfo->https_ssl = NULL;
close(vpninfo->ssl_fd);
@@ -365,10 +379,10 @@
int fd, ret;
if (!vpninfo->uid_csd_given) {
- vpninfo->progress(vpninfo, PRG_ERR, "Error: You are trying to "
- "run insecure CSD code without specifying the CSD user.\n"
- " Use command line option \"--csd-user\"\n");
- exit(1);
+ vpninfo->progress(vpninfo, PRG_ERR,
+ "Error: Server asked us to download and run a 'Cisco Secure Desktop' trojan.\n"
+ "This facility is disabled by default for security reasons, so you may wish to enable it.");
+ return -EPERM;
}
#ifndef __linux__
@@ -427,7 +441,11 @@
"CSD code with root privileges\n"
"\t Use command line option \"--csd-user\"\n");
}
-
+ if (vpninfo->uid_csd_given == 2) {
+ /* The NM tool really needs not to get spurious output
+ on stdout, which the CSD trojan spews. */
+ dup2(2, 1);
+ }
csd_argv[i++] = fname;
csd_argv[i++] = "-ticket";
if (asprintf(&csd_argv[i++], "\"%s\"", vpninfo->csd_ticket) == -1)
@@ -532,11 +550,8 @@
}
path = strchr(host, '/');
- if (path) {
+ if (path)
*(path++) = 0;
- if (!*path)
- path = NULL;
- }
port_str = strrchr(host, ':');
if (port_str) {
@@ -556,7 +571,13 @@
if (res_port)
*res_port = port;
if (res_path)
- *res_path = path ? strdup(path) : NULL;
+ *res_path = (path && *path) ? strdup(path) : NULL;
+
+ /* Undo the damage we did to the original string */
+ if (path)
+ *(path - 1) = '/';
+ if (proto)
+ *(host - 3) = ':';
return 0;
}
@@ -688,7 +709,9 @@
vpninfo->redirect_url = NULL;
goto retry;
} else {
- char *lastslash = strrchr(vpninfo->urlpath, '/');
+ char *lastslash = NULL;
+ if (vpninfo->urlpath)
+ lastslash = strrchr(vpninfo->urlpath, '/');
if (!lastslash) {
free(vpninfo->urlpath);
vpninfo->urlpath = vpninfo->redirect_url;
@@ -712,7 +735,13 @@
goto retry;
}
}
-
+ if (!form_buf || result != 200) {
+ vpninfo->progress(vpninfo, PRG_ERR,
+ "Unexpected %d result from server\n",
+ result);
+ free(form_buf);
+ return -EINVAL;
+ }
if (vpninfo->csd_stuburl) {
/* This is the CSD stub script, which we now need to run */
result = run_csd_script(vpninfo, form_buf, buflen);
--- main.c
+++ main.c
@@ -36,9 +36,9 @@
#include <sys/syslog.h>
#include <sys/utsname.h>
#include <sys/types.h>
-#include <openssl/rand.h>
+#include <openssl/ui.h>
#ifdef OPENCONNECT_LIBPROXY
-#include <libproxy/proxy.h>
+#include LIBPROXY_HDR
#endif
#define _GNU_SOURCE
@@ -49,10 +49,35 @@
static int write_new_config(struct openconnect_info *vpninfo, char *buf, int buflen);
static void write_progress(struct openconnect_info *info, int level, const char *fmt, ...);
static void syslog_progress(struct openconnect_info *info, int level, const char *fmt, ...);
+static int validate_peer_cert(struct openconnect_info *info, X509 *peer_cert, const char *reason);
int verbose = PRG_INFO;
int background;
int do_passphrase_from_fsid;
+int nocertcheck;
+
+enum {
+ OPT_AUTHGROUP = 0x100,
+ OPT_CAFILE,
+ OPT_COOKIEONLY,
+ OPT_COOKIE_ON_STDIN,
+ OPT_CSD_USER,
+ OPT_DISABLE_IPV6,
+ OPT_DTLS_CIPHERS,
+ OPT_FORCE_DPD,
+ OPT_KEY_PASSWORD_FROM_FSID,
+ OPT_LIBPROXY,
+ OPT_NO_CERT_CHECK,
+ OPT_NO_DTLS,
+ OPT_NO_HTTP_KEEPALIVE,
+ OPT_NO_PASSWD,
+ OPT_NO_PROXY,
+ OPT_PASSWORD_ON_STDIN,
+ OPT_PRINTCOOKIE,
+ OPT_RECONNECT_TIMEOUT,
+ OPT_SERVERCERT,
+ OPT_USERAGENT,
+};
static struct option long_options[] = {
{"background", 0, 0, 'b'},
@@ -75,26 +100,29 @@
{"user", 1, 0, 'u'},
{"verbose", 0, 0, 'v'},
{"version", 0, 0, 'V'},
- {"cafile", 1, 0, '0'},
- {"no-dtls", 0, 0, '1'},
- {"cookieonly", 0, 0, '2'},
- {"printcookie", 0, 0, '3'},
+ {"cafile", 1, 0, OPT_CAFILE},
+ {"no-dtls", 0, 0, OPT_NO_DTLS},
+ {"cookieonly", 0, 0, OPT_COOKIEONLY},
+ {"printcookie", 0, 0, OPT_PRINTCOOKIE},
{"quiet", 0, 0, 'q'},
{"queue-len", 1, 0, 'Q'},
{"xmlconfig", 1, 0, 'x'},
- {"cookie-on-stdin", 0, 0, '4'},
- {"passwd-on-stdin", 0, 0, '5'},
- {"no-passwd", 0, 0, '6'},
- {"reconnect-timeout", 1, 0, '7'},
- {"dtls-ciphers", 1, 0, '8'},
- {"authgroup", 1, 0, '9'},
- {"servercert", 1, 0, 0x01},
- {"key-password-from-fsid", 0, 0, 0x02},
- {"useragent", 1, 0, 0x03},
- {"csd-user", 1, 0, 0x04},
- {"disable-ipv6", 0, 0, 0x05},
- {"no-proxy", 0, 0, 0x06},
- {"libproxy", 0, 0, 0x07},
+ {"cookie-on-stdin", 0, 0, OPT_COOKIE_ON_STDIN},
+ {"passwd-on-stdin", 0, 0, OPT_PASSWORD_ON_STDIN},
+ {"no-passwd", 0, 0, OPT_NO_PASSWD},
+ {"reconnect-timeout", 1, 0, OPT_RECONNECT_TIMEOUT},
+ {"dtls-ciphers", 1, 0, OPT_DTLS_CIPHERS},
+ {"authgroup", 1, 0, OPT_AUTHGROUP},
+ {"servercert", 1, 0, OPT_SERVERCERT},
+ {"key-password-from-fsid", 0, 0, OPT_KEY_PASSWORD_FROM_FSID},
+ {"useragent", 1, 0, OPT_USERAGENT},
+ {"csd-user", 1, 0, OPT_CSD_USER},
+ {"disable-ipv6", 0, 0, OPT_DISABLE_IPV6},
+ {"no-proxy", 0, 0, OPT_NO_PROXY},
+ {"libproxy", 0, 0, OPT_LIBPROXY},
+ {"no-http-keepalive", 0, 0, OPT_NO_HTTP_KEEPALIVE},
+ {"no-cert-check", 0, 0, OPT_NO_CERT_CHECK},
+ {"force-dpd", 1, 0, OPT_FORCE_DPD},
{NULL, 0, 0, 0},
};
@@ -110,6 +138,7 @@
printf(" --cookie-on-stdin Read cookie from standard input\n");
printf(" -d, --deflate Enable compression (default)\n");
printf(" -D, --no-deflate Disable compression\n");
+ printf(" --force-dpd=INTERVAL Set minimum Dead Peer Detection interval\n");
printf(" -g, --usergroup=GROUP Set login usergroup\n");
printf(" -h, --help Display help text\n");
printf(" -i, --interface=IFNAME Use IFNAME for tunnel interface\n");
@@ -140,7 +169,9 @@
printf(" --disable-ipv6 Do not ask for IPv6 connectivity\n");
printf(" --dtls-ciphers=LIST OpenSSL ciphers to support for DTLS\n");
printf(" --no-dtls Disable DTLS\n");
+ printf(" --no-http-keepalive Disable HTTP connection re-use\n");
printf(" --no-passwd Disable password/SecurID authentication\n");
+ printf(" --no-cert-check Do not require server SSL cert to be valid\n");
printf(" --passwd-on-stdin Read password from standard input\n");
printf(" --reconnect-timeout Connection retry timeout in seconds\n");
printf(" --servercert Server's certificate SHA1 fingerprint\n");
@@ -206,11 +237,8 @@
vpninfo->reconnect_timeout = 300;
vpninfo->uid_csd = 0;
vpninfo->uid_csd_given = 0;
+ vpninfo->validate_peer_cert = validate_peer_cert;
- if (RAND_bytes(vpninfo->dtls_secret, sizeof(vpninfo->dtls_secret)) != 1) {
- fprintf(stderr, "Failed to initialise DTLS secret\n");
- exit(1);
- }
if (!uname(&utsbuf))
vpninfo->localname = utsbuf.nodename;
else
@@ -222,41 +250,41 @@
break;
switch (opt) {
- case '0':
+ case OPT_CAFILE:
vpninfo->cafile = optarg;
break;
- case 0x01:
+ case OPT_SERVERCERT:
vpninfo->servercert = optarg;
break;
- case '1':
+ case OPT_NO_DTLS:
vpninfo->dtls_attempt_period = 0;
break;
- case '2':
+ case OPT_COOKIEONLY:
cookieonly = 1;
break;
- case '3':
+ case OPT_PRINTCOOKIE:
cookieonly = 2;
break;
- case '4':
+ case OPT_COOKIE_ON_STDIN:
read_stdin(&vpninfo->cookie);
/* If the cookie is empty, ignore it */
if (! *vpninfo->cookie) {
vpninfo->cookie = NULL;
}
break;
- case '5':
+ case OPT_PASSWORD_ON_STDIN:
read_stdin(&vpninfo->password);
break;
- case '6':
+ case OPT_NO_PASSWD:
vpninfo->nopasswd = 1;
break;
- case '7':
+ case OPT_RECONNECT_TIMEOUT:
vpninfo->reconnect_timeout = atoi(optarg);
break;
- case '8':
+ case OPT_DTLS_CIPHERS:
vpninfo->dtls_ciphers = optarg;
break;
- case '9':
+ case OPT_AUTHGROUP:
vpninfo->authgroup = optarg;
break;
case 'b':
@@ -316,10 +344,10 @@
proxy = optarg;
autoproxy = 0;
break;
- case 0x06:
+ case OPT_NO_PROXY:
autoproxy = 0;
proxy = NULL;
- case 0x07:
+ case OPT_LIBPROXY:
#ifndef OPENCONNECT_LIBPROXY
fprintf(stderr, "This version of openconnect was built without libproxy support\n");
exit(1);
@@ -327,6 +355,14 @@
autoproxy = 1;
proxy = NULL;
break;
+ case OPT_NO_HTTP_KEEPALIVE:
+ fprintf(stderr, "Disabling all HTTP connection re-use due to --no-http-keepalive option.\n"
+ "If this helps, please report to <openconnect-devel at lists.infradead.org>.\n");
+ vpninfo->no_http_keepalive = 1;
+ break;
+ case OPT_NO_CERT_CHECK:
+ nocertcheck = 1;
+ break;
case 's':
vpninfo->vpnc_script = optarg;
break;
@@ -350,7 +386,7 @@
}
break;
}
- case 0x04: {
+ case OPT_CSD_USER: {
char *strend;
vpninfo->uid_csd = strtol(optarg, &strend, 0);
if (strend[0]) {
@@ -365,7 +401,7 @@
vpninfo->uid_csd_given = 1;
break;
}
- case 0x05:
+ case OPT_DISABLE_IPV6:
vpninfo->disable_ipv6 = 1;
break;
case 'Q':
@@ -388,13 +424,16 @@
vpninfo->xmlconfig = optarg;
vpninfo->write_new_config = write_new_config;
break;
- case 0x02:
+ case OPT_KEY_PASSWORD_FROM_FSID:
do_passphrase_from_fsid = 1;
break;
- case 0x03:
+ case OPT_USERAGENT:
free(vpninfo->useragent);
vpninfo->useragent = optarg;
break;
+ case OPT_FORCE_DPD:
+ vpninfo->dtls_times.dpd = vpninfo->ssl_times.dpd = atoi(optarg);
+ break;
default:
usage();
}
@@ -501,6 +540,10 @@
(vpninfo->deflate ? "SSL + deflate" : "SSL")
: "DTLS");
+ if (!vpninfo->vpnc_script)
+ vpninfo->progress(vpninfo, PRG_INFO,
+ "No --script argument provided; DNS and routing are not configured\n");
+
if (background) {
int pid;
if ((pid = fork())) {
@@ -563,3 +606,68 @@
va_end(args);
}
}
+
+
+struct accepted_cert {
+ struct accepted_cert *next;
+ char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
+ char host[0];
+} *accepted_certs;
+
+static int validate_peer_cert(struct openconnect_info *vpninfo, X509 *peer_cert,
+ const char *reason)
+{
+ char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
+ struct accepted_cert *this;
+ int ret;
+
+ if (nocertcheck)
+ return 0;
+
+ ret = get_cert_sha1_fingerprint(vpninfo, peer_cert, fingerprint);
+ if (ret)
+ return ret;
+
+ for (this = accepted_certs; this; this = this->next) {
+ if (!strcasecmp(this->host, vpninfo->hostname) &&
+ !strcasecmp(this->fingerprint, fingerprint))
+ return 0;
+ }
+
+ while (1) {
+ UI *ui;
+ char buf[6];
+ int ret;
+
+ fprintf(stderr, "\nCertificate from VPN server \"%s\" failed verification.\n"
+ "Reason: %s\n", vpninfo->hostname, reason);
+ fflush(stderr);
+
+ ui = UI_new();
+ UI_add_input_string(ui, "Enter 'yes' to accept, 'no' to abort; anything else to view: ",
+ UI_INPUT_FLAG_ECHO, buf, 2, 5);
+ ret = UI_process(ui);
+ UI_free(ui);
+ if (ret == -2)
+ return -EINVAL;
+ if (ret == -1)
+ buf[0] = 0;
+
+ if (!strcasecmp(buf, "yes")) {
+ struct accepted_cert *newcert = malloc(sizeof(*newcert) +
+ strlen(vpninfo->hostname) + 1);
+ if (newcert) {
+ newcert->next = accepted_certs;
+ accepted_certs = newcert;
+ strcpy(newcert->fingerprint, fingerprint);
+ strcpy(newcert->host, vpninfo->hostname);
+ }
+ return 0;
+ }
+ if (!strcasecmp(buf, "no"))
+ return -EINVAL;
+
+ X509_print_fp(stderr, peer_cert);
+ }
+
+}
--- mainloop.c
+++ mainloop.c
@@ -119,7 +119,7 @@
continue;
vpninfo->progress(vpninfo, PRG_TRACE,
- "Did no work; sleeping for %d ms...\n", timeout);
+ "No work to do; sleeping for %d ms...\n", timeout);
memcpy(&rfds, &vpninfo->select_rfds, sizeof(rfds));
memcpy(&wfds, &vpninfo->select_wfds, sizeof(wfds));
memcpy(&efds, &vpninfo->select_efds, sizeof(efds));
--- nm-auth-dialog.c
+++ nm-auth-dialog.c
@@ -616,6 +616,7 @@
typedef struct cert_data {
auth_ui_data *ui_data;
X509 *peer_cert;
+ const char *reason;
} cert_data;
@@ -637,8 +638,9 @@
BIO_get_mem_ptr(bp, &certinfo);
title = get_title(data->ui_data->vpninfo->vpn_name);
- msg = g_strdup_printf("Unknown certificate from VPN server \"%s\".\n"
- "Do you want to accept it?", data->ui_data->vpninfo->hostname);
+ msg = g_strdup_printf("Certificate from VPN server \"%s\" failed verification.\n"
+ "Reason: %s\nDo you want to accept it?",
+ data->ui_data->vpninfo->hostname, data->reason);
dlg = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_QUESTION,
GTK_BUTTONS_OK_CANCEL,
@@ -683,7 +685,7 @@
/* runs in worker thread */
static int validate_peer_cert(struct openconnect_info *vpninfo,
- X509 *peer_cert)
+ X509 *peer_cert, const char *reason)
{
char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
char *certs_data;
@@ -714,6 +716,7 @@
data = g_slice_new(cert_data);
data->ui_data = ui_data; /* FIXME uses global */
data->peer_cert = peer_cert;
+ data->reason = reason;
g_mutex_lock(ui_data->form_mutex);
@@ -884,10 +887,11 @@
static int get_config(char *vpn_uuid, struct openconnect_info *vpninfo)
{
- char *authtype;
+ char *proxy;
char *xmlconfig;
char *hostname;
char *group;
+ char *csd;
char *pem_passphrase_fsid;
gcl = gconf_client_get_default();
@@ -950,37 +954,25 @@
vpninfo->cafile = get_gconf_setting(gcl, config_path, NM_OPENCONNECT_KEY_CACERT);
- authtype = get_gconf_setting(gcl, config_path, NM_OPENCONNECT_KEY_AUTHTYPE);
- if (!authtype) {
- fprintf(stderr, "No authentication type configured\n");
- return -EINVAL;
+ csd = get_gconf_setting(gcl, config_path, "enable_csd_trojan");
+ if (csd && !strcmp(csd, "yes")) {
+ /* We're not running as root; we can't setuid(). */
+ vpninfo->uid_csd = getuid();
+ vpninfo->uid_csd_given = 2;
}
+ g_free(csd);
- if (!strcmp(authtype, NM_OPENCONNECT_AUTHTYPE_PASSWORD)) {
- vpninfo->username = get_gconf_setting(gcl, config_path,
- NM_OPENCONNECT_KEY_USERNAME);
- return 0;
- }
- if (!strcmp(authtype, NM_OPENCONNECT_AUTHTYPE_CERT_TPM))
- vpninfo->cert_type = CERT_TYPE_TPM;
- else if (strcmp(authtype, NM_OPENCONNECT_AUTHTYPE_CERT)) {
- fprintf(stderr, "Unknown authentication type '%s'\n", authtype);
+ proxy = get_gconf_setting(gcl, config_path, "proxy");
+ if (proxy && proxy[0] && set_http_proxy(vpninfo, proxy))
return -EINVAL;
- }
- /* It's a certificate */
vpninfo->cert = get_gconf_setting(gcl, config_path, NM_OPENCONNECT_KEY_USERCERT);
- if (!vpninfo->cert) {
- fprintf(stderr, "No user certificate configured\n");
- return -EINVAL;
- }
-
vpninfo->sslkey = get_gconf_setting(gcl, config_path, NM_OPENCONNECT_KEY_PRIVKEY);
if (!vpninfo->sslkey)
vpninfo->sslkey = vpninfo->cert;
pem_passphrase_fsid = get_gconf_setting(gcl, config_path, "pem_passphrase_fsid");
- if (pem_passphrase_fsid && !strcmp(pem_passphrase_fsid, "yes"))
+ if (pem_passphrase_fsid && vpninfo->sslkey && !strcmp(pem_passphrase_fsid, "yes"))
passphrase_from_fsid(vpninfo);
g_free(pem_passphrase_fsid);
@@ -1183,6 +1175,8 @@
/* reset ssl context.
* TODO: this is probably not the way to go... */
if (ui_data->vpninfo->https_ssl) {
+ free(ui_data->vpninfo->peer_addr);
+ ui_data->vpninfo->peer_addr = NULL;
openconnect_close_https(ui_data->vpninfo);
}
if (ui_data->vpninfo->https_ctx) {
--- openconnect.8
+++ openconnect.8
@@ -32,6 +32,10 @@
.B -D,--no-deflate
]
[
+.B --force-dpd
+.I INTERVAL
+]
+[
.B -g,--usergroup
.I GROUP
]
@@ -93,9 +97,6 @@
.B -S,--script-tun
]
[
-.B -T,--tun-fd
-]
-[
.B -u,--user
.I NAME
]
@@ -131,9 +132,15 @@
.I LIST
]
[
+.B --no-cert-check
+]
+[
.B --no-dtls
]
[
+.B --no-http-keepalive
+]
+[
.B --no-passwd
]
[
@@ -199,6 +206,11 @@
.B -D,--no-deflate
Disable compression
.TP
+.B --force-dpd=INTERVAL
+Use
+.I INTERVAL
+as minimum Dead Peer Detection interval for CSTP and DTLS, forcing use of DPD even when the server doesn't request it.
+.TP
.B -g,--usergroup=GROUP
Use
.I GROUP
@@ -259,10 +271,10 @@
Use vpnc-compatible config script
.TP
.B -S,--script-tun
-Pass traffic to 'script' program, not tun
-.TP
-.B -T,--tun-fd
-File descriptor to use for passing traffic
+Pass traffic to 'script' program over a UNIX socket, instead of to a kernel
+tun/tap device. This allows the VPN IP traffic to be handled entirely in
+userspace, for example by a program which uses lwIP to provide SOCKS access
+into the VPN.
.TP
.B -u,--user=NAME
Set login username to
@@ -295,9 +307,38 @@
.B --dtls-ciphers=LIST
Set OpenSSL ciphers to support for DTLS
.TP
+.B --no-cert-check
+Do not require server SSL certificate to be valid. Checks will still happen
+and failures will cause a warning message, but the connection will continue
+anyway. You should not need to use this option -- if your servers have SSL
+certificates which are not signed by a trusted Certificate Authority, you can
+still add them (or your private CA) to a local file and use that file with the
+.B --cafile
+option.
+
+.TP
.B --no-dtls
Disable DTLS
.TP
+.B --no-http-keepalive
+Version 8.2.2.5 of the Cisco ASA software has a bug where it will forget
+the client's SSL certificate when HTTP connections are being re-used for
+multiple requests. So far, this has only been seen on the initial connection,
+where the server gives an HTTP/1.0 redirect response with an explicit
+.B Connection: Keep-Alive
+directive. OpenConnect as of v2.22 has an unconditional workaround for this,
+which is never to obey that directive after an HTTP/1.0 response.
+
+However, Cisco's support team has failed to give any competent
+response to the bug report and we don't know under what other
+circumstances their bug might manifest itself. So this option exists
+to disable ALL re-use of HTTP sessions and cause a new connection to be
+made for each request. If your server seems not to be recognising your
+certificate, try this option. If it makes a difference, please report
+this information to the
+.B openconnect-devel at lists.infradead.org
+mailing list.
+.TP
.B --no-passwd
Never attempt password (or SecurID) authentication
.TP
--- openconnect.h
+++ openconnect.h
@@ -35,7 +35,7 @@
#include <sys/types.h>
#include <unistd.h>
#ifdef OPENCONNECT_LIBPROXY
-#include <libproxy/proxy.h>
+#include LIBPROXY_HDR
#endif
@@ -160,6 +160,7 @@
char *urlpath;
const char *cert;
const char *sslkey;
+ X509 *cert_x509;
int cert_type;
char *cert_password;
const char *cafile;
@@ -173,6 +174,7 @@
char *dtls_ciphers;
uid_t uid_csd;
int uid_csd_given;
+ int no_http_keepalive;
char *cookie;
struct vpn_option *cookies;
@@ -210,6 +212,7 @@
char *ifname;
int mtu;
+ const char *banner;
const char *vpn_addr;
const char *vpn_netmask;
const char *vpn_addr6;
@@ -249,7 +252,7 @@
char *quit_reason;
- int (*validate_peer_cert) (struct openconnect_info *vpninfo, X509 *cert);
+ int (*validate_peer_cert) (struct openconnect_info *vpninfo, X509 *cert, const char *reason);
int (*write_new_config) (struct openconnect_info *vpninfo, char *buf, int buflen);
int (*process_auth_form) (struct openconnect_info *vpninfo, struct oc_auth_form *form);
@@ -273,7 +276,7 @@
#define AC_PKT_TERM_SERVER 9 /* Server kick */
/* Ick */
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
#define method_const const
#else
#define method_const
@@ -287,6 +290,7 @@
void shutdown_tun(struct openconnect_info *vpninfo);
/* dtls.c */
+unsigned char unhex(const char *data);
int setup_dtls(struct openconnect_info *vpninfo);
int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout);
int dtls_try_handshake(struct openconnect_info *vpninfo);
@@ -296,6 +300,7 @@
int make_cstp_connection(struct openconnect_info *vpninfo);
int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout);
int cstp_bye(struct openconnect_info *vpninfo, char *reason);
+int cstp_reconnect(struct openconnect_info *vpninfo);
/* ssl.c */
void openconnect_init_openssl(void);
--- openconnect.html
+++ openconnect.html
@@ -8,7 +8,7 @@
<body>
<h1>OpenConnect</h1>
-<P>OpenConnect is a client for Cisco's <A HREF="http://www.cisco.com/en/US/prod/collateral/iosswrel/ps6537/ps6586/ps6657/product_data_sheet0900aecd80405e25.html">AnyConnect SSL VPN</A>, which is supported by IOS 12.4(9)T or later on Cisco SR500, 870, 880, 1800, 2800, 3800, 7200 Series and Cisco 7301 Routers.</P>
+<P>OpenConnect is a client for Cisco's <A HREF="http://www.cisco.com/web/go/sslvpn">AnyConnect SSL VPN</A>, which is supported by the ASA5500 Series, by IOS 12.4(9)T or later on Cisco SR500, 870, 880, 1800, 2800, 3800, 7200 Series and Cisco 7301 Routers, and probably others.</P>
<P>OpenConnect is released under the GNU Lesser Public License, version 2.1.</P>
@@ -49,8 +49,8 @@
</LI>
<LI>Install a <TT>vpnc-script</TT>.<BR>
This script is what sets up all the addresses and routes for you; it's the
- same as <TT>vpnc</TT>'s. You can get one from <A HREF="http://git.infradead.org/users/dwmw2/vpnc-scripts.git/blob_plain/HEAD:/vpnc-script">here</A> if you don't have one — or if you need IPv6 or Solaris support, which the <TT>vpnc</TT> version lacks.</LI>
- <LI>Connect to your server:<BR>
+ same as <TT>vpnc</TT>'s. You can get one from <A HREF="http://git.infradead.org/users/dwmw2/vpnc-scripts.git/blob_plain/HEAD:/vpnc-script">here</A> if you don't have one — or if you need IPv6 or Solaris support, which the <TT>vpnc</TT> version lacks. <I>(Note that the script needs to be executable, and stored somewhere where SELinux or similar security setups won't prevent the root user from accessing it.)</I></LI>
+ <LI>Connect to your server, running as root:<BR>
<TT>openconnect --script /etc/vpnc/vpnc-script https://vpn.mycompany.com/</TT></LI>
</OL>
@@ -69,7 +69,7 @@
<H2>Supported Platforms</H2>
-OpenConnect is known to work on Linux, OpenBSD, FreeBSD, OpenSolaris
+OpenConnect is known to work on Linux, OpenBSD, FreeBSD, NetBSD, DragonFly BSD, OpenSolaris
and Mac OS X platforms, and should be trivially portable to any other platform
supporting <A HREF="http://en.wikipedia.org/wiki/TUN/TAP">TUN/TAP</a>
devices and on which <A HREF="http://www.openssl.org/">OpenSSL</a> runs.
@@ -141,7 +141,13 @@
There is a mailing list at <TT><A
HREF="mailto:openconnect-devel at lists.infradead.org">
openconnect-devel at lists.infradead.org</A></TT>. To subscribe, visit the <A
-HREF="http://lists.infradead.org/mailman/listinfo/openconnect-devel">Mailman admin page</A>.
+HREF="http://lists.infradead.org/mailman/listinfo/openconnect-devel">Mailman admin page</A>. Before posting to the list, read this:
+<P>
+<B>SECURITY WARNING:</B><BR>
+If you are posting debugging output from openconnect to the mailing list, do <em>not</em> include a line which looks like this:
+
+<BR><TT>Set-Cookie: webvpn=835278264 at 921600@1221512527 at 6B9EC24DEB2F59E242F75B424D42F223D0912984;PATH=/</TT><BR>
+That HTTP cookie is all that's needed to grant access to the VPN session you just logged in to — it's almost as bad as giving your password away. Version 2.26 or later of OpenConnect will automatically filter this out of the debugging output for you.
<H2>TODO</H2>
<UL>
@@ -178,6 +184,48 @@
<LI><I>No changelog entries yet</I></LI>
</UL><BR>
</LI>
+ <LI><B><A HREF="ftp://ftp.infradead.org/pub/openconnect/openconnect-2.26.tar.gz">OpenConnect v2.26</a></B> — 2010-09-22<BR>
+ <UL>
+ <LI>Fix potential crash on relative HTTP redirect.</LI>
+ <LI>Use correct TUN/TAP device node on Android.</LI>
+ <LI>Check client certificate expiry date.</LI>
+ <LI>Implement CSTP and DTLS rekeying <I>(both by reconnecting CSTP)</I>.</LI>
+ <LI>Add <TT>--force-dpd</TT> option to set minimum DPD interval.</LI>
+ <LI>Don't print <TT>webvpn</TT> cookie in debug output.</LI>
+ <LI>Fix host selection in NetworkManager auth dialog.</LI>
+ <LI>Use SSLv3 instead of TLSv1; some servers <I>(or their firewalls)</I>
+ don't accept any <TT>ClientHello</TT> options.</LI>
+ <LI>Never include address family prefix on <tt>script-tun</TT> connections.</LI>
+ </UL><BR>
+ </LI>
+ <LI><B><A HREF="ftp://ftp.infradead.org/pub/openconnect/openconnect-2.25.tar.gz">OpenConnect v2.25</a></B> — 2010-05-15<BR>
+ <UL>
+ <LI>Always validate server certificate, even when no extra <TT>--cafile</TT> is provided.</LI>
+ <LI>Add <TT>--no-cert-check</TT> option to avoid certificate validation.</LI>
+ <LI>Check server hostname against its certificate.</LI>
+ <LI>Provide text-mode function for reviewing and accepting "invalid" certificates.</LI>
+ <LI>Fix libproxy detection on NetBSD.</LI>
+ </UL><BR>
+ </LI>
+ <LI><B><A HREF="ftp://ftp.infradead.org/pub/openconnect/openconnect-2.24.tar.gz">OpenConnect v2.24</a></B> — 2010-05-07<BR>
+ <UL>
+ <LI>Forget preconfigured password after a single attempt; don't retry infinitely if it's failing.</LI>
+ <LI>Set <TT>$CISCO_BANNER</TT> environment variable when running script.</I></LI>
+ <LI>Better handling of passphrase failure on certificate files.</LI>
+ <LI>Fix NetBSD build (thanks to Pouya D. Tafti).</LI>
+ <LI>Fix DragonFly BSD build.</LI>
+ </UL><BR>
+ </LI>
+ <LI><B><A HREF="ftp://ftp.infradead.org/pub/openconnect/openconnect-2.23.tar.gz">OpenConnect v2.23</a></B> — 2010-04-09<BR>
+ <UL>
+ <LI>Support "Cisco Secure Desktop" trojan in NetworkManager auth-dialog.</LI>
+ <LI>Support proxy in NetworkManager auth-dialog.</LI>
+ <LI>Add <TT>--no-http-keepalive</TT> option to work around Cisco's incompetence.</LI>
+ <LI>Fix build on Debian/kFreeBSD.</LI>
+ <LI>Fix crash on receiving HTTP 404 error.</LI>
+ <LI>Improve workaround for server certificates lacking SSL_SERVER purpose, so that it also works with OpenSSL older than 0.9.8k.</LI>
+ </UL><BR>
+ </LI>
<LI><B><A HREF="ftp://ftp.infradead.org/pub/openconnect/openconnect-2.22.tar.gz">OpenConnect v2.22</a></B> — 2010-03-07<BR>
<UL>
<LI>Fix bug handling port numbers above 9999.</LI>
@@ -304,24 +352,21 @@
rather than included in the <TT>network-manager-openconnect</TT>
package. This is because it shares a lot of code with OpenConnect, but
doesn't actually share any with NetworkManager or the other parts of
-the NetworkManager support.
+the NetworkManager support.<P>
+<B>Note for KDE users:</B> knetworkmanager does not yet support OpenConnect. See <A HREF="https://bugs.kde.org/show_bug.cgi?id=226028">KDE bug #226028</A>.
<H2>ConnMan support</H2>
-Basic support for OpenConnect in <A
-HREF="http://moblin.org/projects/connection-manager">ConnMan</A> has
-been submitted, and is pending approval. It can be obtained from the
-git repository at
-<TT>git://git.infradead.org/users/dwmw2/connman-openconnect.git</TT>;
-browsable in <A HREF="
-http://git.infradead.org/users/dwmw2/connman-openconnect.git">gitweb</A>.
+<A HREF="http://connman.net/">ConnMan</A> has basic support for OpenConnect
+as from version 0.48. However, GUI support for VPNs is not yet implemented.
+
<H2>Requirements</H2>
The basic text-mode client uses the following libraries:
<UL>
- <LI><B>OpenSSL</B> — all versions from 0.9.7 onwards will work for basic connectivity, but see note on DTLS compatibility below.</LI>
+ <LI><B>OpenSSL</B> — ideally at least 0.9.8m, although all versions from 0.9.7 onwards will work for basic connectivity. See note on DTLS compatibility below.</LI>
<LI><B>libxml2</B></LI>
<LI><B>zlib</B></LI>
- <LI><B><A HREF="http://code.google.com/p/libproxy/">libproxy</A></B></LI>
+ <LI><B><A HREF="http://code.google.com/p/libproxy/">libproxy</A></B> <I>(optionally)</I></LI>
</UL>
Mac OS X users will also need to install the
<A HREF="http://tuntaposx.sourceforge.net/">Mac OS X tun/tap driver</A>, and Solaris users will need the <A HREF="http://www.whiteboard.ne.jp/~admin2/tuntap/">Solaris one</A>. Note that for IPv6 support, the Solaris tun/tap driver from 16th Nov 2009 or newer is required.<P>
@@ -365,22 +410,7 @@
implementation of DTLS.
<P>
Compatibility support for their "speshul" version of the protocol is
-in the 1.0.0-beta2 and later releases, and is in CVS for the 0.9.8 branch,
-but has not yet been part of an official 0.9.8 release.
-<P>
-It was originally committed to the 0.9.8 branch after the 0.9.8k release,
-and therefore expected to be in the 0.9.8l release — but 0.9.8l
-actually turned out to be an "emergency" release to address a <A
-HREF="http://extendedsubset.com/?p=8">serious protocol flaw</A>, and
-was identical to 0.9.8k except for a patch to disable SSL
-renegotiation. It's quite likely that 0.9.8m will have a <A
-HREF="https://svn.resiprocate.org/rep/ietf-drafts/ekr/draft-rescorla-tls-renegotiate.txt">more
-refined fix</A> for the renegotiation problem, and therefore the Cisco
-DTLS compatibility and all the other things which went into OpenSSL
-CVS after 0.9.8k should finally see the light of day in a 0.9.8n
-release.
-
-
+in the 0.9.8m and later releases of OpenSSL (and 1.0.0-beta2 and later).
<P>
If you are using an older version of OpenSSL, DTLS will
@@ -405,9 +435,13 @@
Fedora's OpenSSL packages include all required patches for DTLS compatibility.
<H3>Debian</H3>
The <TT>openconnect</TT> and <TT>network-manager-openconnect</TT> packages are available in unstable and testing.<BR>
-<A HREF="http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=524982">Debian bug #524982</A> has been filed, requesting that the required patches be included in Debian's OpenSSL package.<P>
+Debian's OpenSSL packages include all required patches for DTLS compatibility in unstable and testing.<P>
+<H3>Ubuntu</H3>
+Reasonably current versions of the required packages are finally included in Ubuntu 10.04 "Lucid". Older releases still have <A HREF="https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/516318">out of date OpenSSL</A> and <A HREF="https://bugs.launchpad.net/ubuntu/+source/openconnect/+bug/516324">out of date OpenConnect which doesn't work around the latest Cisco bugs</A>.
<H3>Gentoo</H3>
<A HREF="http://bugs.gentoo.org/show_bug.cgi?id=263097">Gentoo bug #263097</A> has been filed, asking for <TT>openconnect</TT> to be packaged.
+<H3>NetBSD, DragonFly BSD, etc. <i>(pkgsrc)</i></H3>
+There are packages for <A HREF="http://pkgsrc-wip.cvs.sourceforge.net/viewvc/pkgsrc-wip/wip/vpnc-script/">vpnc-script</A> and <A HREF="http://pkgsrc-wip.cvs.sourceforge.net/viewvc/pkgsrc-wip/wip/openconnect/">openconnect</A> in the pkgsrc-wip repository <I>(<A HREF="http://pkgsrc-wip.sourceforge.net/">pkgsrc-wip.sf.net</A>)</I>.
<H3>FreeBSD</H3>
An <TT>openconnect</TT> <A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/ports/security/openconnect/">port</A> is available for FreeBSD. FreeBSD does not yet ship a version of OpenSSL which supports Cisco's "speshul" version of DTLS.
@@ -415,6 +449,6 @@
<hr>
<address>David Woodhouse <<A HREF="mailto:dwmw2 at infradead.org">dwmw2 at infradead.org</A>></address>
<!-- hhmts start -->
-Last modified: Sun Mar 7 14:10:55 PST 2010
+Last modified: Wed Sep 22 00:05:36 BST 2010
<!-- hhmts end -->
</body> </html>
--- ssl.c
+++ ssl.c
@@ -31,12 +31,14 @@
#include <string.h>
#include <ctype.h>
#include <stdio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
#if defined(__linux__)
#include <sys/vfs.h>
-#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__APPLE__)
#include <sys/param.h>
#include <sys/mount.h>
-#elif defined (__sun__)
+#elif defined (__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
#include <sys/statvfs.h>
#endif
@@ -138,24 +140,41 @@
{
EVP_PKEY *pkey = NULL;
X509 *cert = NULL;
- STACK_OF(X509) *ca = sk_X509_new_null();
+ STACK_OF(X509) *ca;
int ret = 0;
char pass[PEM_BUFSIZE];
+ retrypass:
+ /* We do this every time round the loop, to work around a bug in
+ OpenSSL < 1.0.0-beta2 -- where the stack at *ca will be freed
+ when PKCS12_parse() returns an error, but *ca is left pointing
+ to the freed memory. */
+ ca = NULL;
if (!vpninfo->cert_password) {
if (EVP_read_pw_string(pass, PEM_BUFSIZE,
"Enter PKCS#12 pass phrase:", 0))
return -EINVAL;
}
if (!PKCS12_parse(p12, vpninfo->cert_password?:pass, &pkey, &cert, &ca)) {
- vpninfo->progress(vpninfo, PRG_ERR, "Parse PKCS#12 failed\n");
+ unsigned long err = ERR_peek_error();
+
report_ssl_errors(vpninfo);
+
+ if (ERR_GET_LIB(err) == ERR_LIB_PKCS12 &&
+ ERR_GET_FUNC(err) == PKCS12_F_PKCS12_PARSE &&
+ ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE) {
+ vpninfo->progress(vpninfo, PRG_ERR, "Parse PKCS#12 failed (wrong passphrase?)\n");
+ vpninfo->cert_password = NULL;
+ goto retrypass;
+ }
+
+ vpninfo->progress(vpninfo, PRG_ERR, "Parse PKCS#12 failed (see above errors)\n");
PKCS12_free(p12);
return -EINVAL;
}
if (cert) {
+ vpninfo->cert_x509 = cert;
SSL_CTX_use_certificate(vpninfo->https_ctx, cert);
- X509_free(cert);
} else {
vpninfo->progress(vpninfo, PRG_ERR,
"PKCS#12 contained no certificate!");
@@ -187,12 +206,13 @@
buf, sizeof(buf));
vpninfo->progress(vpninfo, PRG_DEBUG,
"Extra cert from PKCS#12: '%s'\n", buf);
+ CRYPTO_add(&cert2->references, 1, CRYPTO_LOCK_X509);
SSL_CTX_add_extra_chain_cert(vpninfo->https_ctx, cert2);
cert = cert2;
goto next;
}
}
- sk_X509_free(ca);
+ sk_X509_pop_free(ca, X509_free);
}
PKCS12_free(p12);
@@ -245,6 +265,28 @@
return 0;
}
+static int reload_pem_cert(struct openconnect_info *vpninfo)
+{
+ BIO *b = BIO_new(BIO_s_file_internal());
+
+ if (!b)
+ return -ENOMEM;
+
+ if (BIO_read_filename(b, vpninfo->cert) <= 0) {
+ err:
+ BIO_free(b);
+ vpninfo->progress(vpninfo, PRG_ERR,
+ "Failed to reload X509 cert for expiry check\n");
+ report_ssl_errors(vpninfo);
+ return -EIO;
+ }
+ vpninfo->cert_x509 = PEM_read_bio_X509_AUX(b, NULL, NULL, NULL);
+ if (!vpninfo->cert_x509)
+ goto err;
+
+ return 0;
+}
+
static int load_certificate(struct openconnect_info *vpninfo)
{
vpninfo->progress(vpninfo, PRG_TRACE,
@@ -286,6 +328,9 @@
return -EINVAL;
}
+ /* Ew, we can't get it back from the OpenSSL CTX in any sane fashion */
+ reload_pem_cert(vpninfo);
+
if (vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
FILE *f = fopen(vpninfo->sslkey, "r");
char buf[256];
@@ -333,7 +378,6 @@
SSL_FILETYPE_PEM)) {
unsigned long err = ERR_peek_error();
- vpninfo->progress(vpninfo, PRG_ERR, "Loading private key failed\n");
report_ssl_errors(vpninfo);
#ifndef EVP_F_EVP_DECRYPTFINAL_EX
@@ -342,9 +386,12 @@
/* If the user fat-fingered the passphrase, try again */
if (ERR_GET_LIB(err) == ERR_LIB_EVP &&
ERR_GET_FUNC(err) == EVP_F_EVP_DECRYPTFINAL_EX &&
- ERR_GET_REASON(err) == EVP_R_BAD_DECRYPT)
+ ERR_GET_REASON(err) == EVP_R_BAD_DECRYPT) {
+ vpninfo->progress(vpninfo, PRG_ERR, "Loading private key failed (wrong passphrase?)\n");
goto again;
+ }
+ vpninfo->progress(vpninfo, PRG_ERR, "Loading private key failed (see above errors)\n");
return -EINVAL;
}
return 0;
@@ -411,35 +458,310 @@
return 0;
}
-static int verify_peer(struct openconnect_info *vpninfo, SSL *https_ssl)
+static int match_hostname_elem(const char *hostname, int helem_len,
+ const char *match, int melem_len)
{
- X509 *peer_cert;
+ if (!helem_len && !melem_len)
+ return 0;
- if (vpninfo->cafile) {
- int vfy = SSL_get_verify_result(https_ssl);
+ if (!helem_len || !melem_len)
+ return -1;
- if (vfy != X509_V_OK) {
- vpninfo->progress(vpninfo, PRG_ERR, "Server certificate verify failed: %s\n",
- X509_verify_cert_error_string(vfy));
- return -EINVAL;
+
+ if (match[0] == '*') {
+ int i;
+
+ for (i = 1 ; i <= helem_len; i++) {
+ if (!match_hostname_elem(hostname + i, helem_len - i,
+ match + 1, melem_len - 1))
+ return 0;
}
- return 0;
+ return -1;
}
- peer_cert = SSL_get_peer_certificate(https_ssl);
+ if (toupper(hostname[0]) == toupper(match[0]))
+ return match_hostname_elem(hostname + 1, helem_len - 1,
+ match + 1, melem_len - 1);
+
+ return -1;
+}
+
+static int match_hostname(const char *hostname, const char *match)
+{
+ while (*match) {
+ const char *h_dot, *m_dot;
+ int helem_len, melem_len;
- if (vpninfo->servercert)
- return check_server_cert(vpninfo, peer_cert);
+ h_dot = strchr(hostname, '.');
+ m_dot = strchr(match, '.');
+
+ if (h_dot && m_dot) {
+ helem_len = h_dot - hostname + 1;
+ melem_len = m_dot - match + 1;
+ } else if (!h_dot && !m_dot) {
+ helem_len = strlen(hostname);
+ melem_len = strlen(match);
+ } else
+ return -1;
+
+
+ if (match_hostname_elem(hostname, helem_len,
+ match, melem_len))
+ return -1;
- if (vpninfo->validate_peer_cert)
- return vpninfo->validate_peer_cert(vpninfo, peer_cert);
+ hostname += helem_len;
+ match += melem_len;
+ }
+ if (*hostname)
+ return -1;
- /* If no validation function, just let it succeed */
return 0;
}
-void workaround_openssl_certchain_bug(struct openconnect_info *vpninfo,
- SSL *ssl)
+/* cf. RFC2818 and RFC2459 */
+int match_cert_hostname(struct openconnect_info *vpninfo, X509 *peer_cert)
+{
+ STACK_OF(GENERAL_NAME) *altnames;
+ X509_NAME *subjname;
+ ASN1_STRING *subjasn1;
+ char *subjstr = NULL;
+ int addrlen = 0;
+ int i, altdns = 0;
+ char addrbuf[sizeof(struct in6_addr)];
+ int ret;
+
+ /* Allow GEN_IP in the certificate only if we actually connected
+ by IP address rather than by name. */
+ if (inet_pton(AF_INET, vpninfo->hostname, addrbuf) > 0)
+ addrlen = 4;
+ else if (inet_pton(AF_INET6, vpninfo->hostname, addrbuf) > 0)
+ addrlen = 16;
+ else if (vpninfo->hostname[0] == '[' &&
+ vpninfo->hostname[strlen(vpninfo->hostname)-1] == ']') {
+ char *p = &vpninfo->hostname[strlen(vpninfo->hostname)-1];
+ *p = 0;
+ if (inet_pton(AF_INET6, vpninfo->hostname + 1, addrbuf) > 0)
+ addrlen = 16;
+ *p = ']';
+ }
+
+ altnames = X509_get_ext_d2i(peer_cert, NID_subject_alt_name,
+ NULL, NULL);
+ for (i = 0; i < sk_GENERAL_NAME_num(altnames); i++) {
+ const GENERAL_NAME *this = sk_GENERAL_NAME_value(altnames, i);
+
+ if (this->type == GEN_DNS) {
+ char *str;
+
+ int len = ASN1_STRING_to_UTF8((void *)&str, this->d.ia5);
+ if (len < 0)
+ continue;
+
+ altdns = 1;
+
+ /* We don't like names with embedded NUL */
+ if (strlen(str) != len)
+ continue;
+
+ if (!match_hostname(vpninfo->hostname, str)) {
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "Matched DNS altname '%s'\n",
+ str);
+ GENERAL_NAMES_free(altnames);
+ OPENSSL_free(str);
+ return 0;
+ } else {
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "No match for altname '%s'\n",
+ str);
+ }
+ OPENSSL_free(str);
+ } else if (this->type == GEN_IPADD && addrlen) {
+ char host[80];
+ int family;
+
+ if (this->d.ip->length == 4) {
+ family = AF_INET;
+ } else if (this->d.ip->length == 16) {
+ family = AF_INET6;
+ } else {
+ vpninfo->progress(vpninfo, PRG_ERR,
+ "Certificate has GEN_IPADD altname with bogus length %d\n",
+ this->d.ip->length);
+ continue;
+ }
+
+ /* We only do this for the debug messages */
+ inet_ntop(family, this->d.ip->data, host, sizeof(host));
+
+ if (this->d.ip->length == addrlen &&
+ !memcmp(addrbuf, this->d.ip->data, addrlen)) {
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "Matched IP%s address '%s'\n",
+ (family == AF_INET6)?"v6":"",
+ host);
+ GENERAL_NAMES_free(altnames);
+ return 0;
+ } else {
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "No match for IP%s address '%s'\n",
+ (family == AF_INET6)?"v6":"",
+ host);
+ }
+ } else if (this->type == GEN_URI) {
+ char *str;
+ char *url_proto, *url_host, *url_path, *url_host2;
+ int url_port;
+ int len = ASN1_STRING_to_UTF8((void *)&str, this->d.ia5);
+
+ if (len < 0)
+ continue;
+
+ /* We don't like names with embedded NUL */
+ if (strlen(str) != len)
+ continue;
+
+ if (parse_url(str, &url_proto, &url_host, &url_port, &url_path, 0)) {
+ OPENSSL_free(str);
+ continue;
+ }
+
+ if (!url_proto || strcasecmp(url_proto, "https"))
+ goto no_uri_match;
+
+ if (url_port != vpninfo->port)
+ goto no_uri_match;
+
+ /* Leave url_host as it was so that it can be freed */
+ url_host2 = url_host;
+ if (addrlen == 16 && vpninfo->hostname[0] != '[' &&
+ url_host[0] == '[' && url_host[strlen(url_host)-1] == ']') {
+ /* Cope with https://[IPv6]/ when the hostname is bare IPv6 */
+ url_host[strlen(url_host)-1] = 0;
+ url_host2++;
+ }
+
+ if (strcasecmp(vpninfo->hostname, url_host2))
+ goto no_uri_match;
+
+ if (url_path) {
+ vpninfo->progress(vpninfo, PRG_TRACE, "URI '%s' has non-empty path; ignoring\n",
+ str);
+ goto no_uri_match_silent;
+ }
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "Matched URI '%s'\n",
+ str);
+ free(url_proto);
+ free(url_host);
+ free(url_path);
+ OPENSSL_free(str);
+ GENERAL_NAMES_free(altnames);
+ return 0;
+
+ no_uri_match:
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "No match for URI '%s'\n",
+ str);
+ no_uri_match_silent:
+ free(url_proto);
+ free(url_host);
+ free(url_path);
+ OPENSSL_free(str);
+ }
+ }
+ GENERAL_NAMES_free(altnames);
+
+ /* According to RFC2818, we don't use the legacy subject name if
+ there was an altname with DNS type. */
+ if (altdns) {
+ vpninfo->progress(vpninfo, PRG_ERR, "No altname in peer cert matched '%s'\n",
+ vpninfo->hostname);
+ return -EINVAL;
+ }
+
+ subjname = X509_get_subject_name(peer_cert);
+ if (!subjname) {
+ vpninfo->progress(vpninfo, PRG_ERR, "No subject name in peer cert!\n");
+ return -EINVAL;
+ }
+
+ /* Find the _last_ (most specific) commonName */
+ i = -1;
+ while (1) {
+ int j = X509_NAME_get_index_by_NID(subjname, NID_commonName, i);
+ if (j >= 0)
+ i = j;
+ else
+ break;
+ }
+
+ subjasn1 = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subjname, i));
+
+ i = ASN1_STRING_to_UTF8((void *)&subjstr, subjasn1);
+
+ if (!subjstr || strlen(subjstr) != i) {
+ vpninfo->progress(vpninfo, PRG_ERR,
+ "Failed to parse subject name in peer cert\n");
+ return -EINVAL;
+ }
+ ret = 0;
+
+ if (match_hostname(vpninfo->hostname, subjstr)) {
+ vpninfo->progress(vpninfo, PRG_ERR, "Peer cert subject mismatch ('%s' != '%s')\n",
+ subjstr, vpninfo->hostname);
+ ret = -EINVAL;
+ } else {
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "Matched peer certificate subject name '%s'\n",
+ subjstr);
+ }
+
+ OPENSSL_free(subjstr);
+ return ret;
+}
+
+static int verify_peer(struct openconnect_info *vpninfo, SSL *https_ssl)
+{
+ X509 *peer_cert;
+ int ret;
+
+ peer_cert = SSL_get_peer_certificate(https_ssl);
+
+ if (vpninfo->servercert) {
+ /* If given a cert fingerprint on the command line, that's
+ all we look for */
+ ret = check_server_cert(vpninfo, peer_cert);
+ } else {
+ int vfy = SSL_get_verify_result(https_ssl);
+ const char *err_string = NULL;
+
+ if (vfy != X509_V_OK)
+ err_string = X509_verify_cert_error_string(vfy);
+ else if (match_cert_hostname(vpninfo, peer_cert))
+ err_string = "certificate does not match hostname";
+
+ if (err_string) {
+ vpninfo->progress(vpninfo, PRG_ERR,
+ "Server certificate verify failed: %s\n",
+ err_string);
+
+ if (vpninfo->validate_peer_cert)
+ ret = vpninfo->validate_peer_cert(vpninfo, peer_cert,
+ err_string);
+ else
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ }
+ }
+ X509_free(peer_cert);
+
+ return ret;
+}
+
+static void workaround_openssl_certchain_bug(struct openconnect_info *vpninfo,
+ SSL *ssl)
{
/* OpenSSL has problems with certificate chains -- if there are
multiple certs with the same name, it doesn't necessarily
@@ -474,6 +796,60 @@
X509_STORE_CTX_cleanup(&ctx);
}
+#if OPENSSL_VERSION_NUMBER >= 0x00908000
+static int ssl_app_verify_callback(X509_STORE_CTX *ctx, void *arg)
+{
+ /* We've seen certificates in the wild which don't have the
+ purpose fields filled in correctly */
+ X509_VERIFY_PARAM_set_purpose(ctx->param, X509_PURPOSE_ANY);
+ return X509_verify_cert(ctx);
+}
+#endif
+
+static int check_certificate_expiry(struct openconnect_info *vpninfo)
+{
+ ASN1_TIME *notAfter;
+ char *reason = NULL;
+ time_t t;
+ int i;
+ if (!vpninfo->cert_x509)
+ return 0;
+
+ t = time(NULL);
+ notAfter = X509_get_notAfter(vpninfo->cert_x509);
+ i = X509_cmp_time(notAfter, &t);
+ if (!i) {
+ vpninfo->progress(vpninfo, PRG_ERR, "Error in client cert notAfter field\n");
+ return -EINVAL;
+ } else if (i < 0) {
+ reason = "has expired";
+ } else {
+ t += 60 * 86400;
+ i = X509_cmp_time(notAfter, &t);
+ if (i < 0) {
+ reason = "expires soon";
+ }
+ }
+ if (reason) {
+ BIO *bp = BIO_new(BIO_s_mem());
+ BUF_MEM *bm;
+ char *expiry = "<error>";
+ char zero = 0;
+
+ if (bp) {
+ ASN1_TIME_print(bp, notAfter);
+ BIO_write(bp, &zero, 1);
+ BIO_get_mem_ptr(bp, &bm);
+ expiry = bm->data;
+ }
+ vpninfo->progress(vpninfo, PRG_ERR, "Client certificate %s at: %s\n",
+ reason, expiry);
+ if (bp)
+ BIO_free(bp);
+ }
+ return 0;
+}
+
int openconnect_open_https(struct openconnect_info *vpninfo)
{
method_const SSL_METHOD *ssl3_method;
@@ -581,8 +957,8 @@
free(hostname);
if (err) {
- vpninfo->progress(vpninfo, PRG_ERR, "getaddrinfo failed: %s\n",
- gai_strerror(err));
+ vpninfo->progress(vpninfo, PRG_ERR, "getaddrinfo failed for host '%s': %s\n",
+ hostname, gai_strerror(err));
return -EINVAL;
}
@@ -636,7 +1012,7 @@
}
}
- ssl3_method = TLSv1_client_method();
+ ssl3_method = SSLv3_client_method();
if (!vpninfo->https_ctx) {
vpninfo->https_ctx = SSL_CTX_new(ssl3_method);
@@ -647,11 +1023,21 @@
"Loading certificate failed. Aborting.\n");
return err;
}
+ check_certificate_expiry(vpninfo);
}
- /* We've seen certificates in the wild which don't have the
- purpose fields filled in correctly */
- SSL_CTX_set_purpose(vpninfo->https_ctx, X509_PURPOSE_ANY);
+ /* We just want to do:
+ SSL_CTX_set_purpose(vpninfo->https_ctx, X509_PURPOSE_ANY);
+ ... but it doesn't work with OpenSSL < 0.9.8k because of
+ problems with inheritance (fixed in v1.1.4.6 of
+ crypto/x509/x509_vpm.c) so we have to play silly buggers
+ instead. This trick doesn't work _either_ in < 0.9.7 but
+ I don't know of _any_ workaround which will, and can't
+ be bothered to find out either. */
+#if OPENSSL_VERSION_NUMBER >= 0x00908000
+ SSL_CTX_set_cert_verify_callback(vpninfo->https_ctx,
+ ssl_app_verify_callback, NULL);
+#endif
SSL_CTX_set_default_verify_paths(vpninfo->https_ctx);
if (vpninfo->cafile)
@@ -709,7 +1095,7 @@
OpenSSL_add_all_algorithms ();
}
-#ifdef __sun__
+#if defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
int passphrase_from_fsid(struct openconnect_info *vpninfo)
{
struct statvfs buf;
--- tun.c
+++ tun.c
@@ -37,9 +37,14 @@
#include <net/if.h>
#include <arpa/inet.h>
#include <errno.h>
+#include <ctype.h>
#if defined(__sun__)
#include <stropts.h>
#include <sys/sockio.h>
+#include <net/if_tun.h>
+#ifndef TUNNEWPPA
+#error "Install TAP driver from http://www.whiteboard.ne.jp/~admin2/tuntap/"
+#endif
#endif
#include "openconnect.h"
@@ -232,6 +237,31 @@
free(env_buf);
}
+static void set_banner(struct openconnect_info *vpninfo)
+{
+ char *banner, *q;
+ const char *p;
+
+ if (!vpninfo->banner || !(banner = malloc(strlen(vpninfo->banner)))) {
+ unsetenv("CISCO_BANNER");
+ return;
+ }
+ p = vpninfo->banner;
+ q = banner;
+
+ while (*p) {
+ if (*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) {
+ *(q++) = unhex(p + 1);
+ p += 3;
+ } else
+ *(q++) = *(p++);
+ }
+ *q = 0;
+ setenv("CISCO_BANNER", banner, 1);
+
+ free(banner);
+}
+
static void set_script_env(struct openconnect_info *vpninfo)
{
char host[80];
@@ -241,7 +271,7 @@
setenv("VPNGATEWAY", host, 1);
setenv("reason", "connect", 1);
- unsetenv("CISCO_BANNER");
+ set_banner(vpninfo);
unsetenv("CISCO_SPLIT_INC");
unsetenv("CISCO_SPLIT_EXC");
@@ -377,12 +407,25 @@
} else {
#ifdef IFF_TUN /* Linux */
struct ifreq ifr;
+ int tunerr;
tun_fd = open("/dev/net/tun", O_RDWR);
if (tun_fd < 0) {
+ /* Android has /dev/tun instead of /dev/net/tun
+ Since other systems might have too, just try it
+ as a fallback instead of using ifdef __ANDROID__ */
+ tunerr = errno;
+ tun_fd = open("/dev/tun", O_RDWR);
+ }
+ if (tun_fd < 0) {
+ /* If the error on /dev/tun is ENOENT, that's boring.
+ Use the error we got on /dev/net/tun instead */
+ if (errno != -ENOENT)
+ tunerr = errno;
+
vpninfo->progress(vpninfo, PRG_ERR,
"Failed to open tun device: %s\n",
- strerror(errno));
+ strerror(tunerr));
exit(1);
}
memset(&ifr, 0, sizeof(ifr));
@@ -529,8 +572,10 @@
while ((len = read(vpninfo->tun_fd, buf, sizeof(buf))) > 0) {
unsigned char *pkt = buf;
#ifdef TUN_HAS_AF_PREFIX
- pkt += 4;
- len -= 4;
+ if (!vpninfo->script_tun) {
+ pkt += 4;
+ len -= 4;
+ }
#endif
if (queue_new_packet(&vpninfo->outgoing_queue, pkt,
len))
@@ -556,27 +601,29 @@
int len = this->len;
#ifdef TUN_HAS_AF_PREFIX
- struct ip *iph = (void *)data;
- int type;
-
- if (iph->ip_v == 6)
- type = AF_INET6;
- else if (iph->ip_v == 4)
- type = AF_INET;
- else {
- static int complained = 0;
- if (!complained) {
- complained = 1;
- vpninfo->progress(vpninfo, PRG_ERR,
- "Unknown packet (len %d) received: %02x %02x %02x %02x...\n",
- len, data[0], data[1], data[2], data[3]);
+ if (!vpninfo->script_tun) {
+ struct ip *iph = (void *)data;
+ int type;
+
+ if (iph->ip_v == 6)
+ type = AF_INET6;
+ else if (iph->ip_v == 4)
+ type = AF_INET;
+ else {
+ static int complained = 0;
+ if (!complained) {
+ complained = 1;
+ vpninfo->progress(vpninfo, PRG_ERR,
+ "Unknown packet (len %d) received: %02x %02x %02x %02x...\n",
+ len, data[0], data[1], data[2], data[3]);
+ }
+ free(this);
+ continue;
}
- free(this);
- continue;
+ data -= 4;
+ len += 4;
+ *(int *)data = htonl(type);
}
- data -= 4;
- len += 4;
- *(int *)data = htonl(type);
#endif
vpninfo->incoming_queue = this->next;
--- version.c
+++ version.c
@@ -1 +1 @@
-char openconnect_version[] = "v2.22";
+char openconnect_version[] = "v2.26";
--- version.sh
+++ version.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-v="v2.22"
+v="v2.26"
if tag=`git describe --tags`; then
v="$tag"
More information about the MeeGo-commits
mailing list