[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Bug#590873: openconnect < 2.25 does not verify SSL server certificates



On Sat, Aug 28, 2010 at 01:16:29PM +0200, Julien Cristau wrote:
> On Sat, Aug 28, 2010 at 11:50:49 +0100, Dominic Hargreaves wrote:
> 
> > On Sun, Aug 15, 2010 at 08:56:46PM +0100, Adam D. Barratt wrote:
> > > On Sun, 2010-08-15 at 16:13 +0100, Dominic Hargreaves wrote:
> > > > To the untrained eye, the diff between
> > > > 6732c0e8ccb4d57d6a970973f994a9d2d3509def
> > > > and
> > > > 3b2738befa7fe934d0d55b77fe1fcf28aafbe424
> > > > 
> > > > in upstream git is what's required for this, but the patch would need
> > > > a bit of work to apply cleanly. Note also that there
> > > > are some memory leaks fixed in 2.25 which might be a good idea to fix
> > > > too.
> > > > 
> > > > Given all this, might the best idea be allow an exception for the 
> > > > new upstream? The full changelog is:
> > > 
> > > Most of the changes sound potentially worthy of inclusion.  What does
> > > the debdiff look like?
> > 
> > File lists identical (after any substitutions)
> > 
> > Control files: lines which differ (wdiff format)
> > ------------------------------------------------
> > Installed-Size: [-196-] {+208+}
> > Version: [-2.22-1.1-] {+2.25-0.1+}
> > 
> The debdiff between both .dscs, not between the .debs.

Ah, it wasn't clear what was required.

> > Trivial interdiff (including reverted patch included upstream)
> > attached.
> > 
> This doesn't seem to be the full story, it has no upstream changes...

The upstream changes are visible at
<http://git.infradead.org/users/dwmw2/openconnect.git>
and also in the attached debdiff.

Cheers,
Dominic.

-- 
Dominic Hargreaves | http://www.larted.org.uk/~dom/
PGP key 5178E2A5 from the.earth.li (keyserver,web,email)
diff -Nru openconnect-2.22/auth.c openconnect-2.25/auth.c
--- openconnect-2.22/auth.c	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/auth.c	2010-05-15 09:23:37.000000000 +0100
@@ -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;
diff -Nru openconnect-2.22/auth-dlg-settings.h openconnect-2.25/auth-dlg-settings.h
--- openconnect-2.22/auth-dlg-settings.h	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/auth-dlg-settings.h	2010-05-15 09:23:37.000000000 +0100
@@ -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 */
diff -Nru openconnect-2.22/cstp.c openconnect-2.25/cstp.c
--- openconnect-2.22/cstp.c	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/cstp.c	2010-05-15 09:23:37.000000000 +0100
@@ -84,6 +84,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;
@@ -248,6 +249,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)
diff -Nru openconnect-2.22/debian/changelog openconnect-2.25/debian/changelog
--- openconnect-2.22/debian/changelog	2010-08-28 12:58:22.000000000 +0100
+++ openconnect-2.25/debian/changelog	2010-08-28 12:58:23.000000000 +0100
@@ -1,3 +1,11 @@
+openconnect (2.25-0.1) unstable; urgency=low
+
+  * Non-maintainer upload.
+  * New upstream release (Closes: #566188)
+    - always verify SSL server certificates (Closes: #590873)
+
+ -- Dominic Hargreaves <dom@earth.li>  Sat, 28 Aug 2010 11:21:16 +0100
+
 openconnect (2.22-1.1) unstable; urgency=low
 
   * Non-maintainer upload.
diff -Nru openconnect-2.22/dtls.c openconnect-2.25/dtls.c
--- openconnect-2.22/dtls.c	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/dtls.c	2010-05-15 09:23:37.000000000 +0100
@@ -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;
@@ -211,7 +211,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);
@@ -346,7 +346,7 @@
 				return -EINVAL;
 			}
 			for (i = 0; i < 64; i += 2)
-				vpninfo->dtls_session_id[i/2] = hex(dtls_opt->value + i);
+				vpninfo->dtls_session_id[i/2] = unhex(dtls_opt->value + i);
 			sessid_found = 1;
 		} else if (!strcmp(dtls_opt->option + 7, "Port")) {
 			dtls_port = atol(dtls_opt->value);
diff -Nru openconnect-2.22/http.c openconnect-2.25/http.c
--- openconnect-2.22/http.c	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/http.c	2010-05-15 09:23:37.000000000 +0100
@@ -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,7 +125,8 @@
 		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)))) {
@@ -289,7 +291,7 @@
 		}
 	}
 
-	if (closeconn) {
+	if (closeconn || vpninfo->no_http_keepalive) {
 		SSL_free(vpninfo->https_ssl);
 		vpninfo->https_ssl = NULL;
 		close(vpninfo->ssl_fd);
@@ -365,10 +367,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 +429,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 +538,8 @@
 	}
 
 	path = strchr(host, '/');
-	if (path) {
+	if (path)
 		*(path++) = 0;
-		if (!*path)
-			path = NULL;
-	}
 
 	port_str = strrchr(host, ':');
 	if (port_str) {
@@ -556,7 +559,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;
 }
 
@@ -712,7 +721,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);
diff -Nru openconnect-2.22/main.c openconnect-2.25/main.c
--- openconnect-2.22/main.c	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/main.c	2010-05-15 09:23:37.000000000 +0100
@@ -37,8 +37,9 @@
 #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 +50,12 @@
 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;
 
 static struct option long_options[] = {
 	{"background", 0, 0, 'b'},
@@ -95,6 +98,8 @@
 	{"disable-ipv6", 0, 0, 0x05},
 	{"no-proxy", 0, 0, 0x06},
 	{"libproxy", 0, 0, 0x07},
+	{"no-http-keepalive", 0, 0, 0x08},
+	{"no-cert-check", 0, 0, 0x09},
 	{NULL, 0, 0, 0},
 };
 
@@ -140,7 +145,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,6 +213,7 @@
 	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");
@@ -327,6 +335,14 @@
 			autoproxy = 1;
 			proxy = NULL;
 			break;
+		case 0x08:
+			fprintf(stderr, "Disabling all HTTP connection re-use due to --no-http-keepalive option.\n"
+				"If this helps, please report to <openconnect-devel@lists.infradead.org>.\n");
+			vpninfo->no_http_keepalive = 1;
+			break;
+		case 0x09:
+			nocertcheck = 1;
+			break;
 		case 's':
 			vpninfo->vpnc_script = optarg;
 			break;
@@ -501,6 +517,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 +583,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);
+	}
+				
+}
diff -Nru openconnect-2.22/mainloop.c openconnect-2.25/mainloop.c
--- openconnect-2.22/mainloop.c	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/mainloop.c	2010-05-15 09:23:37.000000000 +0100
@@ -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));
diff -Nru openconnect-2.22/Makefile openconnect-2.25/Makefile
--- openconnect-2.22/Makefile	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/Makefile	2010-05-15 09:23:37.000000000 +0100
@@ -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)" ) > $@
diff -Nru openconnect-2.22/nm-auth-dialog.c openconnect-2.25/nm-auth-dialog.c
--- openconnect-2.22/nm-auth-dialog.c	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/nm-auth-dialog.c	2010-05-15 09:23:37.000000000 +0100
@@ -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);
 				    
diff -Nru openconnect-2.22/openconnect.8 openconnect-2.25/openconnect.8
--- openconnect-2.22/openconnect.8	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/openconnect.8	2010-05-15 09:23:37.000000000 +0100
@@ -131,9 +131,15 @@
 .I LIST
 ]
 [
+.B --no-cert-check
+]
+[
 .B --no-dtls
 ]
 [
+.B --no-http-keepalive
+]
+[
 .B --no-passwd
 ]
 [
@@ -295,9 +301,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@lists.infradead.org
+mailing list.
+.TP
 .B --no-passwd
 Never attempt password (or SecurID) authentication
 .TP
diff -Nru openconnect-2.22/openconnect.h openconnect-2.25/openconnect.h
--- openconnect-2.22/openconnect.h	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/openconnect.h	2010-05-15 09:23:37.000000000 +0100
@@ -35,7 +35,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #ifdef OPENCONNECT_LIBPROXY
-#include <libproxy/proxy.h>
+#include LIBPROXY_HDR
 #endif
 
 
@@ -173,6 +173,7 @@
 	char *dtls_ciphers;
 	uid_t uid_csd;
 	int uid_csd_given;
+	int no_http_keepalive;
 
 	char *cookie;
 	struct vpn_option *cookies;
@@ -210,6 +211,7 @@
 	char *ifname;
 
 	int mtu;
+	const char *banner;
 	const char *vpn_addr;
 	const char *vpn_netmask;
 	const char *vpn_addr6;
@@ -249,7 +251,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 +275,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 +289,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);
diff -Nru openconnect-2.22/openconnect.html openconnect-2.25/openconnect.html
--- openconnect-2.22/openconnect.html	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/openconnect.html	2010-05-15 09:23:37.000000000 +0100
@@ -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 &mdash; 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 &mdash; 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.
@@ -178,6 +178,34 @@
        <LI><I>No changelog entries yet</I></LI>
      </UL><BR>
   </LI>
+  <LI><B><A HREF="ftp://ftp.infradead.org/pub/openconnect/openconnect-2.25.tar.gz";>OpenConnect v2.25</a></B> &mdash; 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> &mdash; 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> &mdash; 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> &mdash; 2010-03-07<BR>
      <UL>
        <LI>Fix bug handling port numbers above 9999.</LI>
@@ -318,10 +346,10 @@
 <H2>Requirements</H2>
 The basic text-mode client uses the following libraries:
 <UL>
-  <LI><B>OpenSSL</B> &mdash; all versions from 0.9.7 onwards will work for basic connectivity, but see note on DTLS compatibility below.</LI>
+  <LI><B>OpenSSL</B> &mdash; 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 +393,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 &mdash; 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
@@ -406,8 +419,12 @@
 <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>
+<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 +432,6 @@
 <hr>
 <address>David Woodhouse &lt;<A HREF="mailto:dwmw2@infradead.org";>dwmw2@infradead.org</A>&gt;</address>
 <!-- hhmts start -->
-Last modified: Sun Mar  7 14:10:55 PST 2010
+Last modified: Sat May 15 09:23:37 BST 2010
 <!-- hhmts end -->
 </body> </html>
diff -Nru openconnect-2.22/README.DTLS openconnect-2.25/README.DTLS
--- openconnect-2.22/README.DTLS	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/README.DTLS	2010-05-15 09:23:37.000000000 +0100
@@ -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
diff -Nru openconnect-2.22/ssl.c openconnect-2.25/ssl.c
--- openconnect-2.22/ssl.c	2010-08-28 12:58:22.000000000 +0100
+++ openconnect-2.25/ssl.c	2010-05-15 09:23:37.000000000 +0100
@@ -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(__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,18 +140,35 @@
 {
 	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;
 	}
@@ -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);
@@ -333,7 +353,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 +361,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 +433,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);
 
-	if (vpninfo->servercert)
-		return check_server_cert(vpninfo, peer_cert);
+	return -1;
+}
 
-	if (vpninfo->validate_peer_cert)
-		return vpninfo->validate_peer_cert(vpninfo, peer_cert);
+static int match_hostname(const char *hostname, const char *match)
+{
+	while (*match) {
+		const char *h_dot, *m_dot;
+		int helem_len, melem_len;
+
+		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;
+
+		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 +771,16 @@
 	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
+
 int openconnect_open_https(struct openconnect_info *vpninfo)
 {
 	method_const SSL_METHOD *ssl3_method;
@@ -581,8 +888,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;
 		}
 
@@ -649,9 +956,18 @@
 			}
 		}
 
-		/* 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 +1025,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;
diff -Nru openconnect-2.22/TODO openconnect-2.25/TODO
--- openconnect-2.22/TODO	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/TODO	2010-05-15 09:23:37.000000000 +0100
@@ -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:
diff -Nru openconnect-2.22/tun.c openconnect-2.25/tun.c
--- openconnect-2.22/tun.c	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/tun.c	2010-05-15 09:23:37.000000000 +0100
@@ -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");
 
diff -Nru openconnect-2.22/version.c openconnect-2.25/version.c
--- openconnect-2.22/version.c	2010-08-28 12:58:22.000000000 +0100
+++ openconnect-2.25/version.c	2010-05-15 09:23:37.000000000 +0100
@@ -1 +1 @@
-char openconnect_version[] = "v2.22-unknown";
+char openconnect_version[] = "v2.25";
diff -Nru openconnect-2.22/version.sh openconnect-2.25/version.sh
--- openconnect-2.22/version.sh	2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/version.sh	2010-05-15 09:23:37.000000000 +0100
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-v="v2.22"
+v="v2.25"
 
 if tag=`git describe --tags`; then
 	v="$tag"

Reply to: