[meego-commits] 9675: Changes to Trunk/openconnect

Peter Zhu no_reply at build.meego.com
Tue Nov 23 09:48:36 UTC 2010


Hi,
I have made the following changes to openconnect in project Trunk. Please review and accept ASAP.

Thank You,
Peter Zhu

[This message was auto-generated]

---

Request #9675:

  submit:   Trunk:Testing/openconnect(r8) -> Trunk/openconnect


Message:
    Move to Trunk

State:   new          2010-11-23T01:48:36 peter
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 #8445 BMC #8448 BMC #8442 
+
+* 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